Coverage for python/lsst/analysis/tools/atools/calexpMetrics.py: 38%
54 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-23 09:34 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-23 09:34 +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 "CalexpSummaryMetrics",
25 "CalexpMetricHists",
26 "AggregateVisitSummaryStats",
27 "AggregateCornerSeparations",
28)
30from lsst.pex.config import DictField
32from ..actions.plot import HistPanel, HistPlot
33from ..actions.scalar import (
34 MaxAction,
35 MedianAction,
36 MinAction,
37 SigmaMadAction,
38)
39from ..actions.vector import (
40 AngularSeparation,
41 BandSelector,
42 DivideVector,
43 LoadVector,
44)
45from ..interfaces import AnalysisTool
47# Common to both CalexpSummaryMetrics and AggregateVisitSummaryStats.
48# Units in comments indicate compound units, which are unsupported.
49_SUMMARY_STATS_UNITS = {
50 "psfSigma": "pixel",
51 "psfArea": "", # pixel**2
52 "psfIxx": "", # pixel**2
53 "psfIyy": "", # pixel**2
54 "psfIxy": "", # pixel**2
55 "ra": "degree",
56 "dec": "degree",
57 "pixelScale": "", # arcsec/pixel.
58 "zenithDistance": "degree",
59 "expTime": "s",
60 "zeroPoint": "mag",
61 "skyBg": "electron",
62 "skyNoise": "electron",
63 "meanVar": "", # electron**2
64 "astromOffsetMean": "arcsec",
65 "astromOffsetStd": "arcsec",
66 "nPsfStar": "ct",
67 "psfStarDeltaE1Median": "",
68 "psfStarDeltaE2Median": "",
69 "psfStarDeltaE1Scatter": "",
70 "psfStarDeltaE2Scatter": "",
71 "psfStarDeltaSizeMedian": "pixel",
72 "psfStarDeltaSizeScatter": "pixel",
73 "psfStarScaledDeltaSizeScatter": "",
74 "psfTraceRadiusDelta": "pixel",
75 "psfApFluxDelta": "",
76 "psfApCorrSigmaScaledDelta": "",
77 "maxDistToNearestPsf": "pixel",
78 "starEMedian": "",
79 "starUnNormalizedEMedian": "", # pixel**2
80 "effTime": "s",
81 "effTimePsfSigmaScale": "",
82 "effTimeSkyBgScale": "",
83 "effTimeZeroPointScale": "",
84 "magLim": "mag",
85}
88class CalexpSummaryMetrics(AnalysisTool):
89 """
90 Class to load statistics from the summary stats contained with a calexp's
91 metadata and write them to metrics.
92 """
94 propagateData: bool = True
96 _units = _SUMMARY_STATS_UNITS
98 def setDefaults(self):
99 super().setDefaults()
101 self.prep.keysToLoad = list(self._units.keys())
102 self.produce.metric.units = self._units
105class CalexpMetricHists(AnalysisTool):
106 """
107 Class to generate histograms of metrics extracted from a Metrics Table.
108 One plot per band.
109 """
111 parameterizedBand: bool = False
112 metrics = DictField[str, str](doc="The metrics to plot and their respective labels.")
114 def setDefaults(self):
115 super().setDefaults()
117 # Band is passed as a kwarg from the calling task.
118 self.prep.selectors.bandSelector = BandSelector()
119 self.produce.plot = HistPlot()
121 def finalize(self):
123 for metric, label in self.metrics.items():
124 setattr(self.process.buildActions, metric, LoadVector(vectorKey=metric))
125 self.produce.plot.panels[metric] = HistPanel(hists={metric: "Number of calexps"}, label=label)
128class AggregateVisitSummaryStats(AnalysisTool):
129 """
130 Class to calculate per-stat aggregates (min, max, median, sigmaMad) of
131 vectors of summary stats.
132 """
134 _units = _SUMMARY_STATS_UNITS
136 aggregators = {
137 "min": MinAction,
138 "max": MaxAction,
139 "median": MedianAction,
140 "sigmaMad": SigmaMadAction,
141 }
143 def finalize(self):
144 units = {}
145 for key in self._units.keys():
146 for agg_name, agg_cls in self.aggregators.items():
147 action = agg_cls()
148 action.vectorKey = key
149 setattr(self.process.calculateActions, f"{key}_{agg_name}", action)
150 units[f"{key}_{agg_name}"] = self._units[key]
151 self.produce.metric.units = units
154class AggregateCornerSeparations(AnalysisTool):
155 """
156 Class to calculate aggregate corner-to-corner separations
157 from vectors of on-sky corner coordinates.
158 """
160 def setDefaults(self):
161 super().setDefaults()
163 self.process.buildActions.cornersep = AngularSeparation(
164 raKey_A="raCorners_0",
165 decKey_A="decCorners_0",
166 raKey_B="raCorners_2",
167 decKey_B="decCorners_2",
168 outputUnit="arcminute",
169 )
170 self.process.buildActions.ratio = DivideVector()
171 self.process.buildActions.ratio.actionA = AngularSeparation(
172 raKey_A="raCorners_0",
173 decKey_A="decCorners_0",
174 raKey_B="raCorners_2",
175 decKey_B="decCorners_2",
176 outputUnit="arcminute",
177 )
178 self.process.buildActions.ratio.actionB = AngularSeparation(
179 raKey_A="raCorners_1",
180 decKey_A="decCorners_1",
181 raKey_B="raCorners_3",
182 decKey_B="decCorners_3",
183 outputUnit="arcminute",
184 )
186 self.process.calculateActions.cornerSeparation_min = MinAction(vectorKey="cornersep")
187 self.process.calculateActions.cornerSeparation_max = MaxAction(vectorKey="cornersep")
188 self.process.calculateActions.cornerSeparation_median = MedianAction(vectorKey="cornersep")
189 self.process.calculateActions.cornerSeparation_sigmaMad = SigmaMadAction(vectorKey="cornersep")
191 self.process.calculateActions.cornerSeparationRatio_min = MinAction(vectorKey="ratio")
192 self.process.calculateActions.cornerSeparationRatio_max = MaxAction(vectorKey="ratio")
193 self.process.calculateActions.cornerSeparationRatio_median = MedianAction(vectorKey="ratio")
194 self.process.calculateActions.cornerSeparationRatio_sigmaMad = SigmaMadAction(vectorKey="ratio")
196 self.produce.metric.units = {
197 "cornerSeparation_min": "arcmin",
198 "cornerSeparation_max": "arcmin",
199 "cornerSeparation_median": "arcmin",
200 "cornerSeparation_sigmaMad": "arcmin",
201 "cornerSeparationRatio_min": "",
202 "cornerSeparationRatio_max": "",
203 "cornerSeparationRatio_median": "",
204 "cornerSeparationRatio_sigmaMad": "",
205 }