Skip to content

Commit e8ce6f8

Browse files
committed
add redshift resolver layer
1 parent 30a7ab7 commit e8ce6f8

3 files changed

Lines changed: 82 additions & 18 deletions

File tree

app/crossmatch/layered.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
119157
def _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)

app/crossmatch/resolver.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -321,18 +321,4 @@ def resolve(self, evidence: RecordEvidence) -> CrossmatchResult:
321321
)
322322

323323

324-
class LayeredResolver:
325-
def __init__(self, radius_deg: float, pgc_column: str | None = None) -> None:
326-
self._radius_deg = radius_deg
327-
self._pgc_column = pgc_column
328-
329-
@property
330-
def search_radius_deg(self) -> float:
331-
return self._radius_deg
332-
333-
@property
334-
def pgc_column(self) -> str | None:
335-
return self._pgc_column
336-
337-
def resolve(self, evidence: RecordEvidence) -> CrossmatchResult:
338-
return layered.resolver(evidence)
324+
LayeredResolver = layered.LayeredResolver

main.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,25 @@ def crossmatch_two_radii(
369369
default=None,
370370
help="Column in the raw data table containing the claimed PGC; if omitted, PGC matching is disabled",
371371
)
372+
@click.option(
373+
"--redshift-tolerance",
374+
type=float,
375+
default=None,
376+
help="Redshift z tolerance for crossmatching; if omitted, redshift resolution is skipped",
377+
)
372378
@click.pass_context
373379
def crossmatch_layered(
374380
ctx: click.Context,
375381
radius: float,
376382
pgc_column: str | None,
383+
redshift_tolerance: float | None,
377384
) -> None:
378385
common = ctx.obj.crossmatch_common
379-
resolver = LayeredResolver(radius_deg=radius / 3600.0, pgc_column=pgc_column)
386+
resolver = LayeredResolver(
387+
radius_deg=radius / 3600.0,
388+
pgc_column=pgc_column,
389+
redshift_tolerance=redshift_tolerance,
390+
)
380391
with connect(common["dsn"]) as conn:
381392
storage = PgStorage(conn)
382393
run_crossmatch_cmd(

0 commit comments

Comments
 (0)