Coverage for python/lsst/analysis/tools/analysisMetrics/diffMatchedMetrics.py: 29%
77 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-15 03:39 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-15 03:39 -0700
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__ = ("MatchedRefCoaddMetric", "MatchedRefCoaddCModelFluxMetric", "MatchedRefCoaddPositionMetric")
25from typing import Optional
27import lsst.pex.config as pexConfig
29from ..actions.vector.calcBinnedStats import CalcBinnedStatsAction
30from ..actions.vector.selectors import RangeSelector
31from ..analysisParts.diffMatched import (
32 MatchedRefCoaddDiffMagTool,
33 MatchedRefCoaddDiffPositionTool,
34 MatchedRefCoaddTool,
35)
36from ..interfaces import AnalysisMetric, KeyedData
39class MatchedRefCoaddMetric(MatchedRefCoaddTool, AnalysisMetric):
40 """Base tool for matched-to-reference metrics on coadds."""
42 mag_low_min: int = 15
43 mag_low_max: int = 27
44 mag_interval: int = 1
46 name_prefix = pexConfig.Field[str](default=None, doc="Prefix for metric key")
48 names = ("stars", "galaxies", "all")
49 types = ("unresolved", "resolved", "all")
51 unit = pexConfig.Field[str](default=None, doc="Astropy unit of y-axis values")
53 def _validate(self):
54 if self.name_prefix is None or self.unit is None:
55 raise ValueError(
56 f"{self.name_prefix=} and {self.unit=} must not be None;"
57 f" did you forget to set a valid context?"
58 )
60 def configureMetrics(
61 self,
62 unit: Optional[str] = None,
63 name_prefix: Optional[str] = None,
64 name_suffix: str = "_mad_ref_mag{minimum}",
65 unit_select: str = "mag",
66 ):
67 """Configure metric actions and return units.
69 Parameters
70 ----------
71 unit : `str`
72 The (astropy) unit of the summary statistic metrics.
73 name_prefix : `str`
74 The prefix for the action (column) name.
75 name_suffix : `str`
76 The sufffix for the action (column) name.
77 unit_select : `str`
78 The (astropy) unit of the selection (x-axis) column. Default "mag".
80 Returns
81 -------
82 units : `dict` [`str`, `str`]
83 A dict of the unit (value) for each metric name (key)
84 """
85 unit_is_none = unit is None
86 name_prefix_is_none = name_prefix is None
88 if unit_is_none or name_prefix_is_none:
89 if unit_is_none:
90 unit = self.unit
91 if name_prefix_is_none:
92 name_prefix = self.name_prefix
93 self._validate()
94 if unit_select is None:
95 unit_select = "mag"
97 assert name_prefix is not None
98 units = {}
99 for name, name_class in zip(self.names, self.types):
100 name_capital = name.capitalize()
101 x_key = f"x{name_capital}"
103 for minimum in range(self.mag_low_min, self.mag_low_max + 1):
104 action = getattr(self.process.calculateActions, f"{name}{minimum}")
105 action.selector_range = RangeSelector(
106 key=x_key,
107 minimum=minimum,
108 maximum=minimum + self.mag_interval,
109 )
111 action.name_prefix = name_prefix.format(name_class=name_class)
112 action.name_suffix = name_suffix.format(minimum=minimum)
114 units.update(
115 {
116 action.name_median: unit,
117 action.name_sigmaMad: unit,
118 action.name_count: "count",
119 action.name_select_minimum: unit_select,
120 action.name_select_median: unit_select,
121 action.name_select_maximum: unit_select,
122 }
123 )
124 return units
126 def setDefaults(self):
127 super().setDefaults()
129 for name in self.names:
130 name_capital = name.capitalize()
131 for minimum in range(self.mag_low_min, self.mag_low_max + 1):
132 setattr(
133 self.process.calculateActions,
134 f"{name}{minimum}",
135 CalcBinnedStatsAction(key_vector=f"y{name_capital}"),
136 )
138 def __call__(self, data: KeyedData, **kwargs):
139 self._validate()
140 return super().__call__(data=data, **kwargs)
143# The diamond inheritance on MatchedRefCoaddTool seems ok
144class MatchedRefCoaddCModelFluxMetric(MatchedRefCoaddDiffMagTool, MatchedRefCoaddMetric):
145 """Metric for diffs between reference and CModel coadd mags."""
147 def matchedRefDiffContext(self):
148 super().matchedRefDiffContext()
149 self.unit = "mmag"
150 self.name_prefix = "photom_mag_cModelFlux_{name_class}_diff_"
151 self.produce.units = self.configureMetrics()
153 def matchedRefChiContext(self):
154 super().matchedRefChiContext()
155 self.unit = ""
156 self.name_prefix = "photom_mag_cModelFlux_{name_class}_chi_"
157 self.produce.units = self.configureMetrics()
159 def setDefaults(self):
160 super().setDefaults()
163class MatchedRefCoaddPositionMetric(MatchedRefCoaddDiffPositionTool, MatchedRefCoaddMetric):
164 """Metric for diffs between reference and base coadd centroids."""
166 def matchedRefDiffContext(self):
167 super().matchedRefDiffContext()
168 self.unit = "pix"
169 self.name_prefix = f"astrom_{self.variable}_{{name_class}}_diff_"
170 self.produce.units = self.configureMetrics()
172 def matchedRefChiContext(self):
173 super().matchedRefChiContext()
174 self.unit = ""
175 self.name_prefix = f"astrom_{self.variable}_{{name_class}}_diff_"
176 self.produce.units = self.configureMetrics()
178 def setDefaults(self):
179 super().setDefaults()