Coverage for python / lsst / analysis / tools / atools / refCatMatchPlots.py: 23%
353 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-23 09:00 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-23 09:00 +0000
1# This file is part of analysis_tools.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21from __future__ import annotations
23__all__ = (
24 "TargetRefCatDeltaRAScatterPlot",
25 "TargetRefCatDeltaDecScatterPlot",
26 "TargetRefCatDeltaRASkyPlot",
27 "TargetRefCatDeltaDecSkyPlot",
28 "TargetRefCatDeltaScatterPhotom",
29 "TargetRefCatDeltaScatterAstrom",
30 "TargetRefCatDeltaSkyPlotPhotom",
31 "TargetRefCatDeltaPsfScatterPlot",
32 "TargetRefCatDeltaCModelScatterPlot",
33 "TargetRefCatDeltaCModelSkyPlot",
34 "TargetRefCatDeltaPsfSkyPlot",
35 "TargetRefCatDeltaRASkyVisitPlot",
36 "TargetRefCatDeltaAp09ScatterVisitPlot",
37 "TargetRefCatDeltaPsfScatterVisitPlot",
38 "TargetRefCatDeltaAp09SkyVisitPlot",
39 "TargetRefCatDeltaPsfSkyVisitPlot",
40 "TargetRefCatDeltaDecSkyVisitPlot",
41 "TargetRefCatDeltaRAScatterVisitPlot",
42 "TargetRefCatDeltaDecScatterVisitPlot",
43 "TargetRefCatDeltaMetrics",
44 "TargetRefCatDeltaColorMetrics",
45 "TargetRefCatDeltaPhotomMetrics",
46)
48from lsst.pex.config import Field
50from ..actions.plot import HistPanel, HistPlot, HistStatsPanel
51from ..actions.plot.scatterplotWithTwoHists import ScatterPlotStatsAction, ScatterPlotWithTwoHists
52from ..actions.plot.skyPlot import SkyPlot
53from ..actions.scalar.scalarActions import (
54 CountAction,
55 FracThreshold,
56 MeanAction,
57 MedianAction,
58 MedianGradientAction,
59 RmsAction,
60 SigmaMadAction,
61 StdevAction,
62)
63from ..actions.vector import (
64 AngularSeparation,
65 CoaddPlotFlagSelector,
66 ConvertFluxToMag,
67 ConvertUnits,
68 DownselectVector,
69 LoadVector,
70 MagDiff,
71 RAcosDec,
72 RangeSelector,
73 SnSelector,
74 StarSelector,
75 SubtractVector,
76 VisitPlotFlagSelector,
77)
78from ..contexts import CoaddContext, RefMatchContext, VisitContext
79from ..interfaces import AnalysisTool
82class TargetRefCatDelta(AnalysisTool):
83 """Plot the difference between a target catalog and a
84 reference catalog for the quantity set in `setDefaults`.
85 """
87 parameterizedBand = Field[bool](
88 doc="Does this AnalysisTool support band as a name parameter", default=True
89 )
91 def coaddContext(self) -> None:
92 """Apply coadd options for the ref cat plots.
93 Applies the coadd plot flag selector and sets
94 flux types.
95 """
96 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
97 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux_target"
98 self.prep.selectors.snSelector.threshold = 200
99 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target"
100 self.process.buildActions.starStatMask = SnSelector()
101 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux_target"
102 self.process.buildActions.patch = LoadVector()
103 self.process.buildActions.patch.vectorKey = "patch_target"
105 def visitContext(self) -> None:
106 """Apply visit options for the ref cat plots.
107 Applies the visit plot flag selector and sets
108 the flux types.
109 """
110 self.parameterizedBand = False
111 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
112 self.prep.selectors.starSelector.vectorKey = "extendedness_target"
113 self.prep.selectors.snSelector.fluxType = "psfFlux_target"
114 self.prep.selectors.snSelector.threshold = 50
116 self.process.buildActions.starStatMask = SnSelector()
117 self.process.buildActions.starStatMask.fluxType = "psfFlux_target"
118 self.process.buildActions.starStatMask.threshold = 200
120 def setDefaults(self):
121 super().setDefaults()
123 self.prep.selectors.snSelector = SnSelector()
124 self.prep.selectors.starSelector = StarSelector()
127class TargetRefCatDeltaScatterAstrom(TargetRefCatDelta):
128 """Plot the difference in milliseconds between a target catalog and a
129 reference catalog for the coordinate set in `setDefaults`. Plot it on
130 a scatter plot.
131 """
133 def setDefaults(self):
134 super().setDefaults()
136 self.process.buildActions.yStars = ConvertUnits(
137 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
138 )
139 self.process.buildActions.xStars = ConvertFluxToMag()
140 self.process.buildActions.xStars.vectorKey = "{band}_psfFlux_target"
141 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
142 self.process.calculateActions.stars.lowSNSelector.fluxType = "{band}_psfFlux_target"
143 self.process.calculateActions.stars.highSNSelector.fluxType = "{band}_psfFlux_target"
144 self.process.calculateActions.stars.fluxType = "{band}_psfFlux_target"
146 self.produce = ScatterPlotWithTwoHists()
147 self.produce.plotTypes = ["stars"]
148 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
149 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
150 self.applyContext(CoaddContext)
151 self.applyContext(RefMatchContext)
154class TargetRefCatDeltaScatterAstromVisit(TargetRefCatDelta):
155 """Plot the difference in milliseconds between a target catalog and a
156 reference catalog for the coordinate set in `setDefaults`. Plot it on
157 a scatter plot.
158 """
160 def setDefaults(self):
161 super().setDefaults()
163 self.process.buildActions.yStars = ConvertUnits(
164 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
165 )
166 self.process.buildActions.xStars = ConvertFluxToMag()
167 self.process.buildActions.xStars.vectorKey = "psfFlux_target"
168 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
169 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux_target"
170 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux_target"
171 self.process.calculateActions.stars.fluxType = "psfFlux_target"
173 self.produce = ScatterPlotWithTwoHists()
174 self.produce.addSummaryPlot = False
175 self.produce.plotTypes = ["stars"]
176 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
177 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
178 self.applyContext(VisitContext)
179 self.applyContext(RefMatchContext)
182class TargetRefCatDeltaScatterPhotom(TargetRefCatDelta):
183 """Plot the difference in millimags between a target catalog and a
184 reference catalog for the flux type set in `setDefaults`.
185 """
187 def setDefaults(self):
188 super().setDefaults()
190 self.process.buildActions.yStars = MagDiff()
191 self.process.buildActions.yStars.col2 = "{band}_mag_ref"
192 self.process.buildActions.yStars.fluxUnits2 = "mag(AB)"
194 self.process.buildActions.xStars = ConvertFluxToMag()
195 self.process.buildActions.xStars.vectorKey = "{band}_psfFlux_target"
197 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
198 self.process.calculateActions.stars.lowSNSelector.fluxType = "{band}_psfFlux_target"
199 self.process.calculateActions.stars.highSNSelector.fluxType = "{band}_psfFlux_target"
200 self.process.calculateActions.stars.fluxType = "{band}_psfFlux_target"
202 self.produce = ScatterPlotWithTwoHists()
203 self.produce.plotTypes = ["stars"]
204 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
205 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
206 self.produce.yAxisLabel = "Output Mag - Ref Mag (mmag)"
207 self.applyContext(CoaddContext)
208 self.applyContext(RefMatchContext)
211class TargetRefCatDeltaScatterPhotomVisit(TargetRefCatDelta):
212 """Plot the difference in millimags between a target catalog and a
213 reference catalog for the flux type set in `setDefaults`.
214 """
216 def setDefaults(self):
217 super().setDefaults()
219 self.process.buildActions.yStars = MagDiff()
220 self.process.buildActions.yStars.col2 = "mag_ref"
221 self.process.buildActions.yStars.fluxUnits2 = "mag(AB)"
223 self.process.buildActions.xStars = ConvertFluxToMag()
224 self.process.buildActions.xStars.vectorKey = "psfFlux_target"
226 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
227 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux_target"
228 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux_target"
229 self.process.calculateActions.stars.fluxType = "psfFlux_target"
231 self.produce = ScatterPlotWithTwoHists()
232 self.produce.addSummaryPlot = False
233 self.produce.plotTypes = ["stars"]
234 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
235 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
236 self.produce.yAxisLabel = "Output Mag - Ref Mag (mmag)"
237 self.applyContext(VisitContext)
238 self.applyContext(RefMatchContext)
241class TargetRefCatDeltaPsfScatterPlot(TargetRefCatDeltaScatterPhotom):
242 """Plot the difference in millimags between the PSF flux
243 of a target catalog and a reference catalog
244 """
246 def setDefaults(self):
247 super().setDefaults()
249 self.process.buildActions.yStars.col1 = "{band}_psfFlux_target"
252class TargetRefCatDeltaCModelScatterPlot(TargetRefCatDeltaScatterPhotom):
253 """Plot the difference in millimags between the CModel flux
254 of a target catalog and a reference catalog.
255 """
257 def setDefaults(self):
258 super().setDefaults()
260 self.process.buildActions.yStars.col1 = "{band}_cModelFlux_target"
263class TargetRefCatDeltaPsfScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit):
264 """Plot the difference in millimags between the PSF flux
265 of a target catalog and a reference catalog
266 """
268 def setDefaults(self):
269 super().setDefaults()
271 self.process.buildActions.yStars.col1 = "psfFlux_target"
274class TargetRefCatDeltaAp09ScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit):
275 """Plot the difference in millimags between the aper 09 flux
276 of a target catalog and a reference catalog.
277 """
279 def setDefaults(self):
280 super().setDefaults()
282 self.process.buildActions.yStars.col1 = "ap09Flux_target"
285class TargetRefCatDeltaRAScatterPlot(TargetRefCatDeltaScatterAstrom):
286 """Plot the difference in milliseconds between the RA of a target catalog
287 and a reference catalog
288 """
290 def setDefaults(self):
291 super().setDefaults()
292 self.process.buildActions.yStars.buildAction.actionA = RAcosDec(
293 raKey="coord_ra_target", decKey="coord_dec_target"
294 )
295 self.process.buildActions.yStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
297 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
300class TargetRefCatDeltaRAScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit):
301 """Plot the difference in milliseconds between the RA of a target catalog
302 and a reference catalog
303 """
305 def setDefaults(self):
306 super().setDefaults()
307 self.process.buildActions.yStars.buildAction.actionA = RAcosDec(
308 raKey="coord_ra_target", decKey="coord_dec_target"
309 )
310 self.process.buildActions.yStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
312 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
315class TargetRefCatDeltaDecScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit):
316 """Plot the difference in milliseconds between the Decs of a target catalog
317 and a reference catalog
318 """
320 def setDefaults(self):
321 super().setDefaults()
322 self.process.buildActions.yStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
323 self.process.buildActions.yStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
325 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
328class TargetRefCatDeltaDecScatterPlot(TargetRefCatDeltaScatterAstrom):
329 """Plot the difference in milliseconds between the Dec of a target catalog
330 and a reference catalog
331 """
333 def setDefaults(self):
334 super().setDefaults()
335 self.process.buildActions.yStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
336 self.process.buildActions.yStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
338 self.produce.yAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)"
341class TargetRefCatDeltaSkyPlot(TargetRefCatDelta):
342 """Base class for plotting the RA/Dec distribution of stars, with the
343 difference of the quantity defined in the vector key parameter between
344 the target and reference catalog as the color.
345 """
347 parameterizedBand = Field[bool](
348 doc="Does this AnalysisTool support band as a name parameter", default=True
349 )
351 def setDefaults(self):
352 super().setDefaults()
354 self.process.buildActions.xStars = LoadVector()
355 self.process.buildActions.xStars.vectorKey = "coord_ra_target"
356 self.process.buildActions.yStars = LoadVector()
357 self.process.buildActions.yStars.vectorKey = "coord_dec_target"
359 self.produce = SkyPlot()
360 self.produce.plotTypes = ["stars"]
361 self.produce.xAxisLabel = "R.A. (deg)"
362 self.produce.yAxisLabel = "Dec. (deg)"
363 self.produce.plotOutlines = False
366class TargetRefCatDeltaSkyPlotAstrom(TargetRefCatDeltaSkyPlot):
367 """Base class for plotting the RA/Dec distribution of stars, with the
368 difference between the RA or Dec of the target and reference catalog as
369 the color.
370 """
372 def setDefaults(self):
373 super().setDefaults()
375 self.process.buildActions.zStars = ConvertUnits(
376 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
377 )
379 self.applyContext(CoaddContext)
380 self.applyContext(RefMatchContext)
382 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux_target"
385class TargetRefCatDeltaSkyPlotAstromVisit(TargetRefCatDeltaSkyPlot):
386 """Base class for plotting the RA/Dec distribution of stars at
387 the visit level, with the difference between the RA or Dec of
388 the target and reference catalog as the color.
389 """
391 def setDefaults(self):
392 super().setDefaults()
394 self.process.buildActions.zStars = ConvertUnits(
395 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
396 )
398 self.applyContext(VisitContext)
399 self.applyContext(RefMatchContext)
402class TargetRefCatDeltaSkyPlotPhotomVisit(TargetRefCatDeltaSkyPlot):
403 """Base class for plotting the RA/Dec distribution of stars, with the
404 difference between the photometry of the target and reference catalog as
405 the color.
406 """
408 def setDefaults(self):
409 super().setDefaults()
411 self.process.buildActions.zStars = MagDiff()
412 self.process.buildActions.zStars.col2 = "mag_ref"
413 self.process.buildActions.zStars.fluxUnits2 = "mag(AB)"
415 self.produce.plotName = "photomDiffSky"
416 self.produce.zAxisLabel = "Output Mag - Ref Mag (mmag)"
417 self.applyContext(VisitContext)
418 self.applyContext(RefMatchContext)
421class TargetRefCatDeltaSkyPlotPhotom(TargetRefCatDeltaSkyPlot):
422 """Base class for plotting the RA/Dec distribution of stars, with the
423 difference between the photometry of the target and reference catalog as
424 the color.
425 """
427 def setDefaults(self):
428 super().setDefaults()
430 self.process.buildActions.zStars = MagDiff()
431 self.process.buildActions.zStars.col2 = "{band}_mag_ref"
432 self.process.buildActions.zStars.fluxUnits2 = "mag(AB)"
434 self.produce.plotName = "photomDiffSky_{band}"
435 self.produce.zAxisLabel = "Output Mag - Ref Mag (mmag)"
436 self.applyContext(CoaddContext)
437 self.applyContext(RefMatchContext)
440class TargetRefCatDeltaPsfSkyPlot(TargetRefCatDeltaSkyPlotPhotom):
441 """Plot the RA/Dec distribution of stars, with the
442 difference between the PSF photometry of the target and reference
443 catalog as the color.
444 """
446 def setDefaults(self):
447 super().setDefaults()
449 self.process.buildActions.zStars.col1 = "{band}_psfFlux_target"
452class TargetRefCatDeltaPsfSkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit):
453 """Plot the RA/Dec distribution of stars, with the
454 difference between the PSF photometry of the target and reference
455 catalog as the color.
456 """
458 def setDefaults(self):
459 super().setDefaults()
461 self.process.buildActions.zStars.col1 = "psfFlux_target"
464class TargetRefCatDeltaAp09SkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit):
465 """Plot the RA/Dec distribution of stars, with the
466 difference between the Ap09 photometry of the target and reference
467 catalog as the color.
468 """
470 def setDefaults(self):
471 super().setDefaults()
473 self.process.buildActions.zStars.col1 = "ap09Flux_target"
476class TargetRefCatDeltaCModelSkyPlot(TargetRefCatDeltaSkyPlotPhotom):
477 """Plot the RA/Dec distribution of stars, with the
478 difference between the CModel photometry of the target and reference
479 catalog as the color.
480 """
482 def setDefaults(self):
483 super().setDefaults()
485 self.process.buildActions.zStars.col1 = "{band}_cModelFlux_target"
488class TargetRefCatDeltaRASkyPlot(TargetRefCatDeltaSkyPlotAstrom):
489 """Plot the RA/Dec distribution of stars, with the
490 difference between the RA of the target and reference catalog as
491 the color.
492 """
494 def setDefaults(self):
495 super().setDefaults()
496 self.process.buildActions.zStars.buildAction.actionA = RAcosDec(
497 raKey="coord_ra_target", decKey="coord_dec_target"
498 )
499 self.process.buildActions.zStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
501 self.produce.plotName = "astromDiffSky_RA"
502 self.produce.zAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
505class TargetRefCatDeltaRASkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit):
506 """Plot the RA/Dec distribution of stars, with the
507 difference between the RA of the target and reference catalog as
508 the color.
509 """
511 def setDefaults(self):
512 super().setDefaults()
513 self.process.buildActions.zStars.buildAction.actionA = RAcosDec(
514 raKey="coord_ra_target", decKey="coord_dec_target"
515 )
516 self.process.buildActions.zStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
517 self.produce.plotName = "astromDiffSky_RA"
518 self.produce.zAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
521class TargetRefCatDeltaDecSkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit):
522 """Plot the RA/Dec distribution of stars, with the
523 difference between the RA of the target and reference catalog as
524 the color.
525 """
527 def setDefaults(self):
528 super().setDefaults()
529 self.process.buildActions.zStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
530 self.process.buildActions.zStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
532 self.produce.plotName = "astromDiffSky_Dec"
533 self.produce.zAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)"
536class TargetRefCatDeltaDecSkyPlot(TargetRefCatDeltaSkyPlotAstrom):
537 """Plot the RA/Dec distribution of stars, with the
538 difference between the Dec of the target and reference catalog as
539 the color.
540 """
542 def setDefaults(self):
543 super().setDefaults()
544 self.process.buildActions.zStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
545 self.process.buildActions.zStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
547 self.produce.plotName = "astromDiffSky_Dec"
548 self.produce.zAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)"
551class TargetRefCatDeltaMetrics(AnalysisTool):
552 """Calculate the AA1 metric and the sigma MAD from the difference between
553 the target and reference catalog coordinates.
554 """
556 parameterizedBand = Field[bool](
557 doc="Does this AnalysisTool support band as a name parameter", default=True
558 )
560 def coaddContext(self) -> None:
561 """Apply coadd options for the metrics. Applies the coadd plot flag
562 selector and sets flux types.
563 """
564 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
565 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target"
567 self.applyContext(RefMatchContext)
569 self.process.buildActions.mags.vectorKey = "{band}_psfFlux_target"
571 self.produce.metric.newNames = {
572 "AA1_RA": "{band}_AA1_RA_coadd",
573 "AA1_sigmaMad_RA": "{band}_AA1_sigmaMad_RA_coadd",
574 "AA1_Dec": "{band}_AA1_Dec_coadd",
575 "AA1_sigmaMad_Dec": "{band}_AA1_sigmaMad_Dec_coadd",
576 "AA1_tot": "{band}_AA1_tot_coadd",
577 "AA1_sigmaMad_tot": "{band}_AA1_sigmaMad_tot_coadd",
578 }
580 def visitContext(self) -> None:
581 """Apply visit options for the metrics. Applies the visit plot flag
582 selector and sets flux types.
583 """
584 self.parameterizedBand = False
585 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
586 self.prep.selectors.starSelector.vectorKey = "extendedness_target"
588 self.applyContext(RefMatchContext)
590 self.process.buildActions.mags.vectorKey = "psfFlux_target"
592 def setDefaults(self):
593 super().setDefaults()
595 self.prep.selectors.starSelector = StarSelector()
597 # Calculate difference in RA
598 self.process.buildActions.astromDiffRA = ConvertUnits(
599 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
600 )
601 self.process.buildActions.astromDiffRA.buildAction.actionA = RAcosDec(
602 raKey="coord_ra_target", decKey="dec_ref"
603 )
604 self.process.buildActions.astromDiffRA.buildAction.actionB = RAcosDec(
605 raKey="ra_ref", decKey="dec_ref"
606 )
607 # Calculate difference in Dec
608 self.process.buildActions.astromDiffDec = ConvertUnits(
609 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
610 )
611 self.process.buildActions.astromDiffDec.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
612 self.process.buildActions.astromDiffDec.buildAction.actionB = LoadVector(vectorKey="dec_ref")
613 # Calculate total difference (using astropy)
614 self.process.buildActions.astromDiffTot = AngularSeparation(
615 raKey_A="coord_ra_target",
616 decKey_A="coord_dec_target",
617 raKey_B="ra_ref",
618 decKey_B="dec_ref",
619 )
621 self.process.buildActions.mags = ConvertFluxToMag()
623 # Filter down to only objects with mag 17-21.5
624 self.process.filterActions.brightStarsRA = DownselectVector(vectorKey="astromDiffRA")
625 self.process.filterActions.brightStarsRA.selector = RangeSelector(
626 vectorKey="mags", minimum=17, maximum=21.5
627 )
629 self.process.filterActions.brightStarsDec = DownselectVector(vectorKey="astromDiffDec")
630 self.process.filterActions.brightStarsDec.selector = RangeSelector(
631 vectorKey="mags", minimum=17, maximum=21.5
632 )
634 self.process.filterActions.brightStarsTot = DownselectVector(vectorKey="astromDiffTot")
635 self.process.filterActions.brightStarsTot.selector = RangeSelector(
636 vectorKey="mags", minimum=17, maximum=21.5
637 )
639 # Calculate median and sigmaMad
640 self.process.calculateActions.AA1_RA = MedianAction(vectorKey="brightStarsRA")
641 self.process.calculateActions.AA1_sigmaMad_RA = SigmaMadAction(vectorKey="brightStarsRA")
643 self.process.calculateActions.AA1_Dec = MedianAction(vectorKey="brightStarsDec")
644 self.process.calculateActions.AA1_sigmaMad_Dec = SigmaMadAction(vectorKey="brightStarsDec")
646 self.process.calculateActions.AA1_tot = MedianAction(vectorKey="brightStarsTot")
647 self.process.calculateActions.AA1_sigmaMad_tot = SigmaMadAction(vectorKey="brightStarsTot")
649 self.produce.metric.units = {
650 "AA1_RA": "mas",
651 "AA1_sigmaMad_RA": "mas",
652 "AA1_Dec": "mas",
653 "AA1_sigmaMad_Dec": "mas",
654 "AA1_tot": "mas",
655 "AA1_sigmaMad_tot": "mas",
656 }
659class TargetRefCatDeltaColorMetrics(AnalysisTool):
660 """Calculate the AB1 and ABF1 metrics from the difference between the
661 target and reference catalog coordinates.
662 """
664 parameterizedBand: bool = False
666 AB2 = Field[float](
667 doc=(
668 "Separation in milliarcseconds used to calculate the ABF1 metric. ABF1 is the percentage of"
669 " sources whose distance from the reference is greater than AB2 mas from the mean."
670 ),
671 default=20,
672 )
674 def setDefaults(self):
675 super().setDefaults()
677 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
678 self.prep.selectors.starSelector = StarSelector()
679 self.prep.selectors.starSelector.vectorKey = "extendedness"
681 # Calculate difference in RA
682 self.process.buildActions.astromDiffRA = ConvertUnits(
683 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
684 )
685 self.process.buildActions.astromDiffRA.buildAction.actionA = RAcosDec(
686 raKey="coord_ra", decKey="coord_dec"
687 )
688 self.process.buildActions.astromDiffRA.buildAction.actionB = RAcosDec(raKey="r_ra", decKey="r_dec")
689 # Calculate difference in Dec
690 self.process.buildActions.astromDiffDec = ConvertUnits(
691 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
692 )
693 self.process.buildActions.astromDiffDec.buildAction.actionA = LoadVector(vectorKey="dec")
694 self.process.buildActions.astromDiffDec.buildAction.actionB = LoadVector(vectorKey="r_dec")
696 # Calculate total difference (using astropy)
697 self.process.buildActions.astromDiffTot = AngularSeparation(
698 raKey_A="coord_ra",
699 decKey_A="coord_dec",
700 raKey_B="r_ra",
701 decKey_B="r_dec",
702 )
704 self.process.buildActions.mags = ConvertFluxToMag()
705 self.process.buildActions.mags.vectorKey = "psfFlux"
707 # Filter down to only objects with mag 17-21.5
708 self.process.filterActions.brightStarsRA = DownselectVector(vectorKey="astromDiffRA")
709 self.process.filterActions.brightStarsRA.selector = RangeSelector(
710 vectorKey="mags", minimum=17, maximum=21.5
711 )
713 self.process.filterActions.brightStarsDec = DownselectVector(vectorKey="astromDiffDec")
714 self.process.filterActions.brightStarsDec.selector = RangeSelector(
715 vectorKey="mags", minimum=17, maximum=21.5
716 )
718 self.process.filterActions.brightStarsTot = DownselectVector(vectorKey="astromDiffTot")
719 self.process.filterActions.brightStarsTot.selector = RangeSelector(
720 vectorKey="mags", minimum=17, maximum=21.5
721 )
723 # Calculate RMS annd number of sources
724 self.process.calculateActions.AB1_RA = RmsAction(vectorKey="brightStarsRA")
725 self.process.calculateActions.AB1_Dec = RmsAction(vectorKey="brightStarsDec")
726 self.process.calculateActions.AB1_tot = RmsAction(vectorKey="brightStarsTot")
727 self.process.calculateActions.nSources = CountAction(vectorKey="brightStarsTot")
729 self.produce.metric.units = {
730 "AB1_RA": "mas",
731 "ABF1_RA": "percent",
732 "AB1_Dec": "mas",
733 "ABF1_Dec": "percent",
734 "AB1_tot": "mas",
735 "ABF1_tot": "percent",
736 }
738 self.produce.plot = HistPlot()
740 self.produce.plot.panels["panel_sep"] = HistPanel()
741 self.produce.plot.panels["panel_sep"].hists = dict(
742 brightStarsRA="Separations in RA",
743 brightStarsDec="Separations in Dec",
744 brightStarsTot="Total separations",
745 )
746 self.produce.plot.panels["panel_sep"].label = "Separation Distances (mas)"
748 self.produce.plot.panels["panel_sep"].statsPanel = HistStatsPanel()
749 self.produce.plot.panels["panel_sep"].statsPanel.statsLabels = ["N", "AB1", "ABF1 %"]
750 self.produce.plot.panels["panel_sep"].statsPanel.stat1 = ["nSources", "nSources", "nSources"]
751 self.produce.plot.panels["panel_sep"].statsPanel.stat2 = ["AB1_RA", "AB1_Dec", "AB1_tot"]
752 self.produce.plot.panels["panel_sep"].statsPanel.stat3 = ["ABF1_RA", "ABF1_Dec", "ABF1_tot"]
754 def finalize(self):
755 super().finalize()
757 # Calculate the percentage of outliers above the AB2 threshold
758 self.process.calculateActions.ABF1_RA = FracThreshold(
759 vectorKey="brightStarsRA",
760 op="gt",
761 threshold=self.AB2,
762 relative_to_median=True,
763 percent=True,
764 use_absolute_value=True,
765 )
766 self.process.calculateActions.ABF1_Dec = FracThreshold(
767 vectorKey="brightStarsDec",
768 threshold=self.AB2,
769 op="gt",
770 relative_to_median=True,
771 percent=True,
772 use_absolute_value=True,
773 )
774 self.process.calculateActions.ABF1_tot = FracThreshold(
775 vectorKey="brightStarsTot", threshold=self.AB2, op="gt", percent=True
776 )
779class TargetRefCatDeltaPhotomMetrics(TargetRefCatDeltaMetrics):
780 """Calculate statistics from the differences between
781 the target and reference catalog magnitudes.
782 """
784 parameterizedBand = Field[bool](
785 doc="Does this AnalysisTool support band as a name parameter", default=True
786 )
788 def coaddContext(self) -> None:
789 """Apply coadd options for the metrics. Applies the coadd plot flag
790 selector and sets flux types.
791 """
792 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
793 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target"
794 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux_target"
796 # Calculate magnitude difference
797 self.process.buildActions.srcRefMagdiffStars.col1 = "{band}_psfFlux_target"
798 self.process.buildActions.srcRefMagdiffStars.col2 = "{band}_mag_ref"
799 self.process.buildActions.psfMag = ConvertFluxToMag(
800 vectorKey="{band}_psfFlux_target", returnMillimags=True
801 )
803 self.applyContext(RefMatchContext)
805 self.process.buildActions.mags.vectorKey = "{band}_psfFlux_target"
807 self.produce.metric.newNames = {
808 "ref_photom_offset": "{band}_ref_photom_offset_coadd",
809 "ref_photom_offset_mean": "{band}_ref_photom_offset_mean_coadd",
810 "ref_photom_offset_stdev": "{band}_ref_photom_offset_stdev_coadd",
811 "ref_photom_offset_sigmaMad": "{band}_ref_photom_offset_sigmaMad_coadd",
812 "ref_photom_offset_rms": "{band}_ref_photom_offset_rms_coadd",
813 "ref_photom_offset_nstars": "{band}_ref_photom_offset_nstars_coadd",
814 "ref_photom_offset_gradient": "{band}_ref_photom_offset_gradient_coadd",
815 }
817 def visitContext(self) -> None:
818 """Apply visit options for the metrics. Applies the visit plot flag
819 selector and sets flux types.
820 """
821 self.parameterizedBand = False
822 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
823 self.prep.selectors.starSelector.vectorKey = "extendedness_target"
825 self.applyContext(RefMatchContext)
827 self.process.buildActions.mags.vectorKey = "psfFlux_target"
829 def setDefaults(self):
830 super().setDefaults()
832 self.prep.selectors.starSelector = StarSelector()
833 self.prep.selectors.snSelector = SnSelector()
834 self.prep.selectors.snSelector.fluxType = "psfFlux_target"
835 self.prep.selectors.snSelector.threshold = 200
837 # Calculate magnitude difference
838 self.process.buildActions.srcRefMagdiffStars = MagDiff()
839 self.process.buildActions.srcRefMagdiffStars.col1 = "psfFlux_target"
840 self.process.buildActions.srcRefMagdiffStars.col2 = "mag_ref"
841 self.process.buildActions.srcRefMagdiffStars.fluxUnits2 = "mag(AB)"
843 self.process.buildActions.psfMag = ConvertFluxToMag(vectorKey="psfFlux_target", returnMillimags=True)
845 # Calculate median, std, sigmaMad, RMS, and number counts:
846 self.process.calculateActions.ref_photom_offset = MedianAction(vectorKey="srcRefMagdiffStars")
847 self.process.calculateActions.ref_photom_offset_mean = MeanAction(vectorKey="srcRefMagdiffStars")
848 self.process.calculateActions.ref_photom_offset_stdev = StdevAction(vectorKey="srcRefMagdiffStars")
849 self.process.calculateActions.ref_photom_offset_sigmaMad = SigmaMadAction(
850 vectorKey="srcRefMagdiffStars"
851 )
852 self.process.calculateActions.ref_photom_offset_rms = RmsAction(vectorKey="srcRefMagdiffStars")
853 self.process.calculateActions.ref_photom_offset_nstars = CountAction(vectorKey="srcRefMagdiffStars")
854 self.process.calculateActions.ref_photom_offset_gradient = MedianGradientAction(
855 xsVectorKey="psfMag", ysVectorKey="srcRefMagdiffStars"
856 )
858 self.produce.metric.units = {
859 "ref_photom_offset": "mmag",
860 "ref_photom_offset_mean": "mmag",
861 "ref_photom_offset_stdev": "mmag",
862 "ref_photom_offset_sigmaMad": "mmag",
863 "ref_photom_offset_rms": "mmag",
864 "ref_photom_offset_nstars": "ct",
865 "ref_photom_offset_gradient": "",
866 }