Coverage for python / lsst / analysis / tools / atools / refCatMatchPlots.py: 23%
350 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-15 00:23 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-15 00:23 +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 RmsAction,
59 SigmaMadAction,
60 StdevAction,
61)
62from ..actions.vector import (
63 AngularSeparation,
64 CoaddPlotFlagSelector,
65 ConvertFluxToMag,
66 ConvertUnits,
67 DownselectVector,
68 LoadVector,
69 MagDiff,
70 RAcosDec,
71 RangeSelector,
72 SnSelector,
73 StarSelector,
74 SubtractVector,
75 VisitPlotFlagSelector,
76)
77from ..contexts import CoaddContext, RefMatchContext, VisitContext
78from ..interfaces import AnalysisTool
81class TargetRefCatDelta(AnalysisTool):
82 """Plot the difference between a target catalog and a
83 reference catalog for the quantity set in `setDefaults`.
84 """
86 parameterizedBand = Field[bool](
87 doc="Does this AnalysisTool support band as a name parameter", default=True
88 )
90 def coaddContext(self) -> None:
91 """Apply coadd options for the ref cat plots.
92 Applies the coadd plot flag selector and sets
93 flux types.
94 """
95 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
96 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux_target"
97 self.prep.selectors.snSelector.threshold = 200
98 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target"
99 self.process.buildActions.starStatMask = SnSelector()
100 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux_target"
101 self.process.buildActions.patch = LoadVector()
102 self.process.buildActions.patch.vectorKey = "patch_target"
104 def visitContext(self) -> None:
105 """Apply visit options for the ref cat plots.
106 Applies the visit plot flag selector and sets
107 the flux types.
108 """
109 self.parameterizedBand = False
110 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
111 self.prep.selectors.starSelector.vectorKey = "extendedness_target"
112 self.prep.selectors.snSelector.fluxType = "psfFlux_target"
113 self.prep.selectors.snSelector.threshold = 50
115 self.process.buildActions.starStatMask = SnSelector()
116 self.process.buildActions.starStatMask.fluxType = "psfFlux_target"
117 self.process.buildActions.starStatMask.threshold = 200
119 def setDefaults(self):
120 super().setDefaults()
122 self.prep.selectors.snSelector = SnSelector()
123 self.prep.selectors.starSelector = StarSelector()
126class TargetRefCatDeltaScatterAstrom(TargetRefCatDelta):
127 """Plot the difference in milliseconds between a target catalog and a
128 reference catalog for the coordinate set in `setDefaults`. Plot it on
129 a scatter plot.
130 """
132 def setDefaults(self):
133 super().setDefaults()
135 self.process.buildActions.yStars = ConvertUnits(
136 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
137 )
138 self.process.buildActions.xStars = ConvertFluxToMag()
139 self.process.buildActions.xStars.vectorKey = "{band}_psfFlux_target"
140 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
141 self.process.calculateActions.stars.lowSNSelector.fluxType = "{band}_psfFlux_target"
142 self.process.calculateActions.stars.highSNSelector.fluxType = "{band}_psfFlux_target"
143 self.process.calculateActions.stars.fluxType = "{band}_psfFlux_target"
145 self.produce = ScatterPlotWithTwoHists()
146 self.produce.plotTypes = ["stars"]
147 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
148 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
149 self.applyContext(CoaddContext)
150 self.applyContext(RefMatchContext)
153class TargetRefCatDeltaScatterAstromVisit(TargetRefCatDelta):
154 """Plot the difference in milliseconds between a target catalog and a
155 reference catalog for the coordinate set in `setDefaults`. Plot it on
156 a scatter plot.
157 """
159 def setDefaults(self):
160 super().setDefaults()
162 self.process.buildActions.yStars = ConvertUnits(
163 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
164 )
165 self.process.buildActions.xStars = ConvertFluxToMag()
166 self.process.buildActions.xStars.vectorKey = "psfFlux_target"
167 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
168 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux_target"
169 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux_target"
170 self.process.calculateActions.stars.fluxType = "psfFlux_target"
172 self.produce = ScatterPlotWithTwoHists()
173 self.produce.addSummaryPlot = False
174 self.produce.plotTypes = ["stars"]
175 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
176 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
177 self.applyContext(VisitContext)
178 self.applyContext(RefMatchContext)
181class TargetRefCatDeltaScatterPhotom(TargetRefCatDelta):
182 """Plot the difference in millimags between a target catalog and a
183 reference catalog for the flux type set in `setDefaults`.
184 """
186 def setDefaults(self):
187 super().setDefaults()
189 self.process.buildActions.yStars = MagDiff()
190 self.process.buildActions.yStars.col2 = "{band}_mag_ref"
191 self.process.buildActions.yStars.fluxUnits2 = "mag(AB)"
193 self.process.buildActions.xStars = ConvertFluxToMag()
194 self.process.buildActions.xStars.vectorKey = "{band}_psfFlux_target"
196 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
197 self.process.calculateActions.stars.lowSNSelector.fluxType = "{band}_psfFlux_target"
198 self.process.calculateActions.stars.highSNSelector.fluxType = "{band}_psfFlux_target"
199 self.process.calculateActions.stars.fluxType = "{band}_psfFlux_target"
201 self.produce = ScatterPlotWithTwoHists()
202 self.produce.plotTypes = ["stars"]
203 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
204 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
205 self.produce.yAxisLabel = "Output Mag - Ref Mag (mmag)"
206 self.applyContext(CoaddContext)
207 self.applyContext(RefMatchContext)
210class TargetRefCatDeltaScatterPhotomVisit(TargetRefCatDelta):
211 """Plot the difference in millimags between a target catalog and a
212 reference catalog for the flux type set in `setDefaults`.
213 """
215 def setDefaults(self):
216 super().setDefaults()
218 self.process.buildActions.yStars = MagDiff()
219 self.process.buildActions.yStars.col2 = "mag_ref"
220 self.process.buildActions.yStars.fluxUnits2 = "mag(AB)"
222 self.process.buildActions.xStars = ConvertFluxToMag()
223 self.process.buildActions.xStars.vectorKey = "psfFlux_target"
225 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars")
226 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux_target"
227 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux_target"
228 self.process.calculateActions.stars.fluxType = "psfFlux_target"
230 self.produce = ScatterPlotWithTwoHists()
231 self.produce.addSummaryPlot = False
232 self.produce.plotTypes = ["stars"]
233 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)"
234 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)"
235 self.produce.yAxisLabel = "Output Mag - Ref Mag (mmag)"
236 self.applyContext(VisitContext)
237 self.applyContext(RefMatchContext)
240class TargetRefCatDeltaPsfScatterPlot(TargetRefCatDeltaScatterPhotom):
241 """Plot the difference in millimags between the PSF flux
242 of a target catalog and a reference catalog
243 """
245 def setDefaults(self):
246 super().setDefaults()
248 self.process.buildActions.yStars.col1 = "{band}_psfFlux_target"
251class TargetRefCatDeltaCModelScatterPlot(TargetRefCatDeltaScatterPhotom):
252 """Plot the difference in millimags between the CModel flux
253 of a target catalog and a reference catalog.
254 """
256 def setDefaults(self):
257 super().setDefaults()
259 self.process.buildActions.yStars.col1 = "{band}_cModelFlux_target"
262class TargetRefCatDeltaPsfScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit):
263 """Plot the difference in millimags between the PSF flux
264 of a target catalog and a reference catalog
265 """
267 def setDefaults(self):
268 super().setDefaults()
270 self.process.buildActions.yStars.col1 = "psfFlux_target"
273class TargetRefCatDeltaAp09ScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit):
274 """Plot the difference in millimags between the aper 09 flux
275 of a target catalog and a reference catalog.
276 """
278 def setDefaults(self):
279 super().setDefaults()
281 self.process.buildActions.yStars.col1 = "ap09Flux_target"
284class TargetRefCatDeltaRAScatterPlot(TargetRefCatDeltaScatterAstrom):
285 """Plot the difference in milliseconds between the RA of a target catalog
286 and a reference catalog
287 """
289 def setDefaults(self):
290 super().setDefaults()
291 self.process.buildActions.yStars.buildAction.actionA = RAcosDec(
292 raKey="coord_ra_target", decKey="coord_dec_target"
293 )
294 self.process.buildActions.yStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
296 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
299class TargetRefCatDeltaRAScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit):
300 """Plot the difference in milliseconds between the RA of a target catalog
301 and a reference catalog
302 """
304 def setDefaults(self):
305 super().setDefaults()
306 self.process.buildActions.yStars.buildAction.actionA = RAcosDec(
307 raKey="coord_ra_target", decKey="coord_dec_target"
308 )
309 self.process.buildActions.yStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
311 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
314class TargetRefCatDeltaDecScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit):
315 """Plot the difference in milliseconds between the Decs of a target catalog
316 and a reference catalog
317 """
319 def setDefaults(self):
320 super().setDefaults()
321 self.process.buildActions.yStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
322 self.process.buildActions.yStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
324 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
327class TargetRefCatDeltaDecScatterPlot(TargetRefCatDeltaScatterAstrom):
328 """Plot the difference in milliseconds between the Dec of a target catalog
329 and a reference catalog
330 """
332 def setDefaults(self):
333 super().setDefaults()
334 self.process.buildActions.yStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
335 self.process.buildActions.yStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
337 self.produce.yAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)"
340class TargetRefCatDeltaSkyPlot(TargetRefCatDelta):
341 """Base class for plotting the RA/Dec distribution of stars, with the
342 difference of the quantity defined in the vector key parameter between
343 the target and reference catalog as the color.
344 """
346 parameterizedBand = Field[bool](
347 doc="Does this AnalysisTool support band as a name parameter", default=True
348 )
350 def setDefaults(self):
351 super().setDefaults()
353 self.process.buildActions.xStars = LoadVector()
354 self.process.buildActions.xStars.vectorKey = "coord_ra_target"
355 self.process.buildActions.yStars = LoadVector()
356 self.process.buildActions.yStars.vectorKey = "coord_dec_target"
358 self.produce = SkyPlot()
359 self.produce.plotTypes = ["stars"]
360 self.produce.xAxisLabel = "R.A. (deg)"
361 self.produce.yAxisLabel = "Dec. (deg)"
362 self.produce.plotOutlines = False
365class TargetRefCatDeltaSkyPlotAstrom(TargetRefCatDeltaSkyPlot):
366 """Base class for plotting the RA/Dec distribution of stars, with the
367 difference between the RA or Dec of the target and reference catalog as
368 the color.
369 """
371 def setDefaults(self):
372 super().setDefaults()
374 self.process.buildActions.zStars = ConvertUnits(
375 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
376 )
378 self.applyContext(CoaddContext)
379 self.applyContext(RefMatchContext)
381 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux_target"
384class TargetRefCatDeltaSkyPlotAstromVisit(TargetRefCatDeltaSkyPlot):
385 """Base class for plotting the RA/Dec distribution of stars at
386 the visit level, with the difference between the RA or Dec of
387 the target and reference catalog as the color.
388 """
390 def setDefaults(self):
391 super().setDefaults()
393 self.process.buildActions.zStars = ConvertUnits(
394 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
395 )
397 self.applyContext(VisitContext)
398 self.applyContext(RefMatchContext)
401class TargetRefCatDeltaSkyPlotPhotomVisit(TargetRefCatDeltaSkyPlot):
402 """Base class for plotting the RA/Dec distribution of stars, with the
403 difference between the photometry of the target and reference catalog as
404 the color.
405 """
407 def setDefaults(self):
408 super().setDefaults()
410 self.process.buildActions.zStars = MagDiff()
411 self.process.buildActions.zStars.col2 = "mag_ref"
412 self.process.buildActions.zStars.fluxUnits2 = "mag(AB)"
414 self.produce.plotName = "photomDiffSky"
415 self.produce.zAxisLabel = "Output Mag - Ref Mag (mmag)"
416 self.applyContext(VisitContext)
417 self.applyContext(RefMatchContext)
420class TargetRefCatDeltaSkyPlotPhotom(TargetRefCatDeltaSkyPlot):
421 """Base class for plotting the RA/Dec distribution of stars, with the
422 difference between the photometry of the target and reference catalog as
423 the color.
424 """
426 def setDefaults(self):
427 super().setDefaults()
429 self.process.buildActions.zStars = MagDiff()
430 self.process.buildActions.zStars.col2 = "{band}_mag_ref"
431 self.process.buildActions.zStars.fluxUnits2 = "mag(AB)"
433 self.produce.plotName = "photomDiffSky_{band}"
434 self.produce.zAxisLabel = "Output Mag - Ref Mag (mmag)"
435 self.applyContext(CoaddContext)
436 self.applyContext(RefMatchContext)
439class TargetRefCatDeltaPsfSkyPlot(TargetRefCatDeltaSkyPlotPhotom):
440 """Plot the RA/Dec distribution of stars, with the
441 difference between the PSF photometry of the target and reference
442 catalog as the color.
443 """
445 def setDefaults(self):
446 super().setDefaults()
448 self.process.buildActions.zStars.col1 = "{band}_psfFlux_target"
451class TargetRefCatDeltaPsfSkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit):
452 """Plot the RA/Dec distribution of stars, with the
453 difference between the PSF photometry of the target and reference
454 catalog as the color.
455 """
457 def setDefaults(self):
458 super().setDefaults()
460 self.process.buildActions.zStars.col1 = "psfFlux_target"
463class TargetRefCatDeltaAp09SkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit):
464 """Plot the RA/Dec distribution of stars, with the
465 difference between the Ap09 photometry of the target and reference
466 catalog as the color.
467 """
469 def setDefaults(self):
470 super().setDefaults()
472 self.process.buildActions.zStars.col1 = "ap09Flux_target"
475class TargetRefCatDeltaCModelSkyPlot(TargetRefCatDeltaSkyPlotPhotom):
476 """Plot the RA/Dec distribution of stars, with the
477 difference between the CModel photometry of the target and reference
478 catalog as the color.
479 """
481 def setDefaults(self):
482 super().setDefaults()
484 self.process.buildActions.zStars.col1 = "{band}_cModelFlux_target"
487class TargetRefCatDeltaRASkyPlot(TargetRefCatDeltaSkyPlotAstrom):
488 """Plot the RA/Dec distribution of stars, with the
489 difference between the RA of the target and reference catalog as
490 the color.
491 """
493 def setDefaults(self):
494 super().setDefaults()
495 self.process.buildActions.zStars.buildAction.actionA = RAcosDec(
496 raKey="coord_ra_target", decKey="coord_dec_target"
497 )
498 self.process.buildActions.zStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
500 self.produce.plotName = "astromDiffSky_RA"
501 self.produce.zAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
504class TargetRefCatDeltaRASkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit):
505 """Plot the RA/Dec distribution of stars, with the
506 difference between the RA of the target and reference catalog as
507 the color.
508 """
510 def setDefaults(self):
511 super().setDefaults()
512 self.process.buildActions.zStars.buildAction.actionA = RAcosDec(
513 raKey="coord_ra_target", decKey="coord_dec_target"
514 )
515 self.process.buildActions.zStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref")
516 self.produce.plotName = "astromDiffSky_RA"
517 self.produce.zAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)"
520class TargetRefCatDeltaDecSkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit):
521 """Plot the RA/Dec distribution of stars, with the
522 difference between the RA of the target and reference catalog as
523 the color.
524 """
526 def setDefaults(self):
527 super().setDefaults()
528 self.process.buildActions.zStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
529 self.process.buildActions.zStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
531 self.produce.plotName = "astromDiffSky_Dec"
532 self.produce.zAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)"
535class TargetRefCatDeltaDecSkyPlot(TargetRefCatDeltaSkyPlotAstrom):
536 """Plot the RA/Dec distribution of stars, with the
537 difference between the Dec of the target and reference catalog as
538 the color.
539 """
541 def setDefaults(self):
542 super().setDefaults()
543 self.process.buildActions.zStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
544 self.process.buildActions.zStars.buildAction.actionB = LoadVector(vectorKey="dec_ref")
546 self.produce.plotName = "astromDiffSky_Dec"
547 self.produce.zAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)"
550class TargetRefCatDeltaMetrics(AnalysisTool):
551 """Calculate the AA1 metric and the sigma MAD from the difference between
552 the target and reference catalog coordinates.
553 """
555 parameterizedBand = Field[bool](
556 doc="Does this AnalysisTool support band as a name parameter", default=True
557 )
559 def coaddContext(self) -> None:
560 """Apply coadd options for the metrics. Applies the coadd plot flag
561 selector and sets flux types.
562 """
563 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
564 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target"
566 self.applyContext(RefMatchContext)
568 self.process.buildActions.mags.vectorKey = "{band}_psfFlux_target"
570 self.produce.metric.newNames = {
571 "AA1_RA": "{band}_AA1_RA_coadd",
572 "AA1_sigmaMad_RA": "{band}_AA1_sigmaMad_RA_coadd",
573 "AA1_Dec": "{band}_AA1_Dec_coadd",
574 "AA1_sigmaMad_Dec": "{band}_AA1_sigmaMad_Dec_coadd",
575 "AA1_tot": "{band}_AA1_tot_coadd",
576 "AA1_sigmaMad_tot": "{band}_AA1_sigmaMad_tot_coadd",
577 }
579 def visitContext(self) -> None:
580 """Apply visit options for the metrics. Applies the visit plot flag
581 selector and sets flux types.
582 """
583 self.parameterizedBand = False
584 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
585 self.prep.selectors.starSelector.vectorKey = "extendedness_target"
587 self.applyContext(RefMatchContext)
589 self.process.buildActions.mags.vectorKey = "psfFlux_target"
591 def setDefaults(self):
592 super().setDefaults()
594 self.prep.selectors.starSelector = StarSelector()
596 # Calculate difference in RA
597 self.process.buildActions.astromDiffRA = ConvertUnits(
598 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
599 )
600 self.process.buildActions.astromDiffRA.buildAction.actionA = RAcosDec(
601 raKey="coord_ra_target", decKey="dec_ref"
602 )
603 self.process.buildActions.astromDiffRA.buildAction.actionB = RAcosDec(
604 raKey="ra_ref", decKey="dec_ref"
605 )
606 # Calculate difference in Dec
607 self.process.buildActions.astromDiffDec = ConvertUnits(
608 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
609 )
610 self.process.buildActions.astromDiffDec.buildAction.actionA = LoadVector(vectorKey="coord_dec_target")
611 self.process.buildActions.astromDiffDec.buildAction.actionB = LoadVector(vectorKey="dec_ref")
612 # Calculate total difference (using astropy)
613 self.process.buildActions.astromDiffTot = AngularSeparation(
614 raKey_A="coord_ra_target",
615 decKey_A="coord_dec_target",
616 raKey_B="ra_ref",
617 decKey_B="dec_ref",
618 )
620 self.process.buildActions.mags = ConvertFluxToMag()
622 # Filter down to only objects with mag 17-21.5
623 self.process.filterActions.brightStarsRA = DownselectVector(vectorKey="astromDiffRA")
624 self.process.filterActions.brightStarsRA.selector = RangeSelector(
625 vectorKey="mags", minimum=17, maximum=21.5
626 )
628 self.process.filterActions.brightStarsDec = DownselectVector(vectorKey="astromDiffDec")
629 self.process.filterActions.brightStarsDec.selector = RangeSelector(
630 vectorKey="mags", minimum=17, maximum=21.5
631 )
633 self.process.filterActions.brightStarsTot = DownselectVector(vectorKey="astromDiffTot")
634 self.process.filterActions.brightStarsTot.selector = RangeSelector(
635 vectorKey="mags", minimum=17, maximum=21.5
636 )
638 # Calculate median and sigmaMad
639 self.process.calculateActions.AA1_RA = MedianAction(vectorKey="brightStarsRA")
640 self.process.calculateActions.AA1_sigmaMad_RA = SigmaMadAction(vectorKey="brightStarsRA")
642 self.process.calculateActions.AA1_Dec = MedianAction(vectorKey="brightStarsDec")
643 self.process.calculateActions.AA1_sigmaMad_Dec = SigmaMadAction(vectorKey="brightStarsDec")
645 self.process.calculateActions.AA1_tot = MedianAction(vectorKey="brightStarsTot")
646 self.process.calculateActions.AA1_sigmaMad_tot = SigmaMadAction(vectorKey="brightStarsTot")
648 self.produce.metric.units = {
649 "AA1_RA": "mas",
650 "AA1_sigmaMad_RA": "mas",
651 "AA1_Dec": "mas",
652 "AA1_sigmaMad_Dec": "mas",
653 "AA1_tot": "mas",
654 "AA1_sigmaMad_tot": "mas",
655 }
658class TargetRefCatDeltaColorMetrics(AnalysisTool):
659 """Calculate the AB1 and ABF1 metrics from the difference between the
660 target and reference catalog coordinates.
661 """
663 parameterizedBand: bool = False
665 AB2 = Field[float](
666 doc=(
667 "Separation in milliarcseconds used to calculate the ABF1 metric. ABF1 is the percentage of"
668 " sources whose distance from the reference is greater than AB2 mas from the mean."
669 ),
670 default=20,
671 )
673 def setDefaults(self):
674 super().setDefaults()
676 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
677 self.prep.selectors.starSelector = StarSelector()
678 self.prep.selectors.starSelector.vectorKey = "extendedness"
680 # Calculate difference in RA
681 self.process.buildActions.astromDiffRA = ConvertUnits(
682 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
683 )
684 self.process.buildActions.astromDiffRA.buildAction.actionA = RAcosDec(
685 raKey="coord_ra", decKey="coord_dec"
686 )
687 self.process.buildActions.astromDiffRA.buildAction.actionB = RAcosDec(raKey="r_ra", decKey="r_dec")
688 # Calculate difference in Dec
689 self.process.buildActions.astromDiffDec = ConvertUnits(
690 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond"
691 )
692 self.process.buildActions.astromDiffDec.buildAction.actionA = LoadVector(vectorKey="dec")
693 self.process.buildActions.astromDiffDec.buildAction.actionB = LoadVector(vectorKey="r_dec")
695 # Calculate total difference (using astropy)
696 self.process.buildActions.astromDiffTot = AngularSeparation(
697 raKey_A="coord_ra",
698 decKey_A="coord_dec",
699 raKey_B="r_ra",
700 decKey_B="r_dec",
701 )
703 self.process.buildActions.mags = ConvertFluxToMag()
704 self.process.buildActions.mags.vectorKey = "psfFlux"
706 # Filter down to only objects with mag 17-21.5
707 self.process.filterActions.brightStarsRA = DownselectVector(vectorKey="astromDiffRA")
708 self.process.filterActions.brightStarsRA.selector = RangeSelector(
709 vectorKey="mags", minimum=17, maximum=21.5
710 )
712 self.process.filterActions.brightStarsDec = DownselectVector(vectorKey="astromDiffDec")
713 self.process.filterActions.brightStarsDec.selector = RangeSelector(
714 vectorKey="mags", minimum=17, maximum=21.5
715 )
717 self.process.filterActions.brightStarsTot = DownselectVector(vectorKey="astromDiffTot")
718 self.process.filterActions.brightStarsTot.selector = RangeSelector(
719 vectorKey="mags", minimum=17, maximum=21.5
720 )
722 # Calculate RMS annd number of sources
723 self.process.calculateActions.AB1_RA = RmsAction(vectorKey="brightStarsRA")
724 self.process.calculateActions.AB1_Dec = RmsAction(vectorKey="brightStarsDec")
725 self.process.calculateActions.AB1_tot = RmsAction(vectorKey="brightStarsTot")
726 self.process.calculateActions.nSources = CountAction(vectorKey="brightStarsTot")
728 self.produce.metric.units = {
729 "AB1_RA": "mas",
730 "ABF1_RA": "percent",
731 "AB1_Dec": "mas",
732 "ABF1_Dec": "percent",
733 "AB1_tot": "mas",
734 "ABF1_tot": "percent",
735 }
737 self.produce.plot = HistPlot()
739 self.produce.plot.panels["panel_sep"] = HistPanel()
740 self.produce.plot.panels["panel_sep"].hists = dict(
741 brightStarsRA="Separations in RA",
742 brightStarsDec="Separations in Dec",
743 brightStarsTot="Total separations",
744 )
745 self.produce.plot.panels["panel_sep"].label = "Separation Distances (mas)"
747 self.produce.plot.panels["panel_sep"].statsPanel = HistStatsPanel()
748 self.produce.plot.panels["panel_sep"].statsPanel.statsLabels = ["N", "AB1", "ABF1 %"]
749 self.produce.plot.panels["panel_sep"].statsPanel.stat1 = ["nSources", "nSources", "nSources"]
750 self.produce.plot.panels["panel_sep"].statsPanel.stat2 = ["AB1_RA", "AB1_Dec", "AB1_tot"]
751 self.produce.plot.panels["panel_sep"].statsPanel.stat3 = ["ABF1_RA", "ABF1_Dec", "ABF1_tot"]
753 def finalize(self):
754 super().finalize()
756 # Calculate the percentage of outliers above the AB2 threshold
757 self.process.calculateActions.ABF1_RA = FracThreshold(
758 vectorKey="brightStarsRA",
759 op="gt",
760 threshold=self.AB2,
761 relative_to_median=True,
762 percent=True,
763 use_absolute_value=True,
764 )
765 self.process.calculateActions.ABF1_Dec = FracThreshold(
766 vectorKey="brightStarsDec",
767 threshold=self.AB2,
768 op="gt",
769 relative_to_median=True,
770 percent=True,
771 use_absolute_value=True,
772 )
773 self.process.calculateActions.ABF1_tot = FracThreshold(
774 vectorKey="brightStarsTot", threshold=self.AB2, op="gt", percent=True
775 )
778class TargetRefCatDeltaPhotomMetrics(TargetRefCatDeltaMetrics):
779 """Calculate statistics from the differences between
780 the target and reference catalog magnitudes.
781 """
783 parameterizedBand = Field[bool](
784 doc="Does this AnalysisTool support band as a name parameter", default=True
785 )
787 def coaddContext(self) -> None:
788 """Apply coadd options for the metrics. Applies the coadd plot flag
789 selector and sets flux types.
790 """
791 self.prep.selectors.flagSelector = CoaddPlotFlagSelector()
792 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target"
793 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux_target"
795 # Calculate magnitude difference
796 self.process.buildActions.srcRefMagdiffStars.col1 = "{band}_psfFlux_target"
797 self.process.buildActions.srcRefMagdiffStars.col2 = "{band}_mag_ref"
799 self.applyContext(RefMatchContext)
801 self.process.buildActions.mags.vectorKey = "{band}_psfFlux_target"
803 self.produce.metric.newNames = {
804 "ref_photom_offset": "{band}_ref_photom_offset_coadd",
805 "ref_photom_offset_mean": "{band}_ref_photom_offset_mean_coadd",
806 "ref_photom_offset_stdev": "{band}_ref_photom_offset_stdev_coadd",
807 "ref_photom_offset_sigmaMad": "{band}_ref_photom_offset_sigmaMad_coadd",
808 "ref_photom_offset_rms": "{band}_ref_photom_offset_rms_coadd",
809 "ref_photom_offset_nstars": "{band}_ref_photom_offset_nstars_coadd",
810 }
812 def visitContext(self) -> None:
813 """Apply visit options for the metrics. Applies the visit plot flag
814 selector and sets flux types.
815 """
816 self.parameterizedBand = False
817 self.prep.selectors.flagSelector = VisitPlotFlagSelector()
818 self.prep.selectors.starSelector.vectorKey = "extendedness_target"
820 self.applyContext(RefMatchContext)
822 self.process.buildActions.mags.vectorKey = "psfFlux_target"
824 def setDefaults(self):
825 super().setDefaults()
827 self.prep.selectors.starSelector = StarSelector()
828 self.prep.selectors.snSelector = SnSelector()
829 self.prep.selectors.snSelector.fluxType = "psfFlux_target"
830 self.prep.selectors.snSelector.threshold = 200
832 # Calculate magnitude difference
833 self.process.buildActions.srcRefMagdiffStars = MagDiff()
834 self.process.buildActions.srcRefMagdiffStars.col1 = "psfFlux_target"
835 self.process.buildActions.srcRefMagdiffStars.col2 = "mag_ref"
836 self.process.buildActions.srcRefMagdiffStars.fluxUnits2 = "mag(AB)"
838 # Calculate median, std, sigmaMad, RMS, and number counts:
839 self.process.calculateActions.ref_photom_offset = MedianAction(vectorKey="srcRefMagdiffStars")
840 self.process.calculateActions.ref_photom_offset_mean = MeanAction(vectorKey="srcRefMagdiffStars")
841 self.process.calculateActions.ref_photom_offset_stdev = StdevAction(vectorKey="srcRefMagdiffStars")
842 self.process.calculateActions.ref_photom_offset_sigmaMad = SigmaMadAction(
843 vectorKey="srcRefMagdiffStars"
844 )
845 self.process.calculateActions.ref_photom_offset_rms = RmsAction(vectorKey="srcRefMagdiffStars")
846 self.process.calculateActions.ref_photom_offset_nstars = CountAction(vectorKey="srcRefMagdiffStars")
848 self.produce.metric.units = {
849 "ref_photom_offset": "mmag",
850 "ref_photom_offset_mean": "mmag",
851 "ref_photom_offset_stdev": "mmag",
852 "ref_photom_offset_sigmaMad": "mmag",
853 "ref_photom_offset_rms": "mmag",
854 "ref_photom_offset_nstars": "ct",
855 }