@@ -116,6 +116,44 @@ def pgc_resolver(
116116 return PreliminaryCrossmatchStatusColliding (previous_result .pgcs | {pgc }), PendingReason .MATCHED_PGC_OUTSIDE_CIRCLE
117117
118118
119+ def redshift_resolver (
120+ evidence : RecordEvidence ,
121+ previous_result : PreliminaryCrossmatchStatus ,
122+ redshift_tolerance : float ,
123+ ) -> tuple [PreliminaryCrossmatchStatus , PendingReason | None ]:
124+ if evidence .record_redshift is None :
125+ return previous_result , None
126+
127+ record_z = evidence .record_redshift
128+
129+ if isinstance (previous_result , PreliminaryCrossmatchStatusNew ):
130+ return previous_result , None
131+
132+ if isinstance (previous_result , PreliminaryCrossmatchStatusExisting ):
133+ # across neigbours, find me a neighbour with the matched PGC
134+ neighbor = next ((n for n in evidence .neighbors if n .pgc == previous_result .pgc ), None )
135+
136+ if neighbor is None or neighbor .redshift is None :
137+ return previous_result , None
138+
139+ if abs (neighbor .redshift - record_z ) < redshift_tolerance :
140+ return previous_result , None
141+
142+ return previous_result , PendingReason .REDSHIFT_MISMATCH
143+
144+ neighbors_involved = [n for n in evidence .neighbors if n .pgc in previous_result .pgcs ]
145+ if any (n .redshift is None for n in neighbors_involved ):
146+ return previous_result , None
147+
148+ close = [
149+ n for n in neighbors_involved if n .redshift is not None and abs (n .redshift - record_z ) < redshift_tolerance
150+ ]
151+ if len (close ) == 1 :
152+ return PreliminaryCrossmatchStatusExisting (close [0 ].pgc ), None
153+
154+ return previous_result , None
155+
156+
119157def _preliminary_to_final (results : PreliminaryCrossmatchStatus , pending_reason : PendingReason ) -> CrossmatchResult :
120158 if isinstance (results , PreliminaryCrossmatchStatusNew ):
121159 return CrossmatchResult (
@@ -136,7 +174,7 @@ def _preliminary_to_final(results: PreliminaryCrossmatchStatus, pending_reason:
136174 )
137175
138176
139- def resolver (evidence : RecordEvidence ) -> CrossmatchResult :
177+ def resolver (evidence : RecordEvidence , redshift_tolerance : float | None = None ) -> CrossmatchResult :
140178 icrs_result , pending_reason = icrs_simple_resolver (evidence )
141179 if pending_reason is not None :
142180 return _preliminary_to_final (icrs_result , pending_reason )
@@ -149,7 +187,13 @@ def resolver(evidence: RecordEvidence) -> CrossmatchResult:
149187 if pending_reason is not None :
150188 return _preliminary_to_final (pgc_result , pending_reason )
151189
152- final_result = pgc_result
190+ if redshift_tolerance is None :
191+ final_result = pgc_result
192+ else :
193+ redshift_result , pending_reason = redshift_resolver (evidence , pgc_result , redshift_tolerance )
194+ if pending_reason is not None :
195+ return _preliminary_to_final (redshift_result , pending_reason )
196+ final_result = redshift_result
153197
154198 if isinstance (final_result , PreliminaryCrossmatchStatusNew ):
155199 return CrossmatchResult (status = CrossmatchStatus .NEW , triage_status = TriageStatus .RESOLVED )
@@ -165,3 +209,26 @@ def resolver(evidence: RecordEvidence) -> CrossmatchResult:
165209 colliding_pgcs = list (final_result .pgcs ),
166210 pending_reason = PendingReason .MULTIPLE_OBJECTS_MATCHED ,
167211 )
212+
213+
214+ class LayeredResolver :
215+ def __init__ (
216+ self ,
217+ radius_deg : float ,
218+ pgc_column : str | None = None ,
219+ redshift_tolerance : float | None = None ,
220+ ) -> None :
221+ self ._radius_deg = radius_deg
222+ self ._pgc_column = pgc_column
223+ self ._redshift_tolerance = redshift_tolerance
224+
225+ @property
226+ def search_radius_deg (self ) -> float :
227+ return self ._radius_deg
228+
229+ @property
230+ def pgc_column (self ) -> str | None :
231+ return self ._pgc_column
232+
233+ def resolve (self , evidence : RecordEvidence ) -> CrossmatchResult :
234+ return resolver (evidence , self ._redshift_tolerance )
0 commit comments