Coverage for python/lsst/analysis/tools/atools/calibQuantityProfile.py: 62%
140 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 09:31 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 09:31 +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
23from lsst.analysis.tools.interfaces._interfaces import KeyedDataSchema
25__all__ = (
26 "CalibQuantityBaseTool",
27 "CalibQuantityAmpProfileScatterTool",
28 "CalibQuantityAmpProfileHistTool",
29 "CalibAmpScatterTool",
30 "CalibDivisaderoScatterTool",
31 "CalibPtcCovarScatterTool",
32)
34from typing import cast
36import numpy as np
38from lsst.pex.config import Field
39from lsst.pex.config.configurableActions import ConfigurableActionField
41from ..actions.plot.elements import HistElement, ScatterElement
42from ..actions.plot.gridPlot import GridPanelConfig, GridPlot
43from ..interfaces import AnalysisTool, KeyedData, KeyedDataAction, PlotElement, Vector
46class PrepRepacker(KeyedDataAction):
47 """Prep action to repack data."""
49 panelKey = Field[str](
50 doc="Panel selector. Data will be separated into multiple panels based on this key.",
51 )
52 dataKey = Field[str](
53 doc="Data selector. Data will be separated into multiple groups in a single panel based on this key.",
54 )
55 quantityKey = Field[str](
56 doc="Quantity selector. The actual data quantities to be plotted.",
57 )
59 def __call__(self, data: KeyedData, **kwargs) -> KeyedData:
60 repackedData = {}
61 # Loop over the length of the data vector and repack row by row
62 for i in range(len(cast(Vector, data[self.panelKey]))):
63 panelVec = cast(Vector, data[self.panelKey])
64 dataVec = cast(Vector, data[self.dataKey])
65 quantityVec = cast(Vector, data[self.quantityKey])
66 repackedData[f"{panelVec[i]}_{dataVec[i]}_{self.quantityKey}"] = quantityVec[i]
67 return repackedData
69 def getInputSchema(self) -> KeyedDataSchema:
70 return (
71 (self.panelKey, Vector),
72 (self.dataKey, Vector),
73 (self.quantityKey, Vector),
74 )
76 def addInputSchema(self, inputSchema: KeyedDataSchema) -> None:
77 pass
80class SingleValueRepacker(KeyedDataAction):
81 """Prep action to repack data."""
83 panelKey = Field[str](
84 doc="Panel selector. Data will be separated into multiple panels based on this key.",
85 )
86 dataKey = Field[str](
87 doc="Data selector. Data will be separated into multiple groups in a single panel based on this key.",
88 )
89 quantityKey = Field[str](
90 doc="Quantity selector. The actual data quantities to be plotted.",
91 )
93 def __call__(self, data: KeyedData, **kwargs) -> KeyedData:
94 repackedData: dict[str, list] = {}
96 panelVec = cast(Vector, data[self.panelKey])
97 dataVec = cast(Vector, data[self.dataKey])
98 quantityVec = cast(Vector, data[self.quantityKey])
99 # cp_verify's results tables also hold per-detector and per-exposure
100 # rows, which have no amplifier and so belong in no panel.
101 panelMask = np.ma.getmaskarray(panelVec)
103 for i in range(len(panelVec)):
104 if panelMask[i]:
105 continue
106 repackedData.setdefault(f"{panelVec[i]}_x", []).append(dataVec[i])
107 repackedData.setdefault(f"{panelVec[i]}", []).append(quantityVec[i])
109 return repackedData
111 def getInputSchema(self) -> KeyedDataSchema:
112 return (
113 (self.panelKey, Vector),
114 (self.dataKey, Vector),
115 (self.quantityKey, Vector),
116 )
118 def addInputSchema(self, inputSchema: KeyedDataSchema) -> None:
119 pass
122class PassThrough(KeyedDataAction):
123 def __call__(self, data: KeyedData, **kwargs) -> KeyedData:
124 return data
126 def getInputSchema(self) -> KeyedDataSchema:
127 # In general this method should be implemented, but here we are ALSO
128 # implementing the prep step. We therefore know the method results are
129 # not needed because it is doing something special with the inputs.
130 return ()
133class CalibQuantityBaseTool(AnalysisTool):
134 parameterizedBand: bool = False
136 plotElement = ConfigurableActionField[PlotElement](
137 doc="Plot element.",
138 )
140 def setDefaults(self):
141 super().setDefaults()
143 # Repack the input data into a usable format
144 self.prep = PrepRepacker()
146 # A simple pass-through process action to keep the data unchanged
147 self.process = PassThrough()
149 # Plot the repacked data in a 4x4 grid
150 self.produce.plot = GridPlot()
151 self.produce.plot.panels = {}
152 self.produce.plot.numRows = 4
153 self.produce.plot.numCols = 4
155 # Values to group by to distinguish between data in differing panels
156 self.produce.plot.valsGroupBy = {
157 0: "C00",
158 1: "C01",
159 2: "C02",
160 3: "C03",
161 4: "C04",
162 5: "C05",
163 6: "C06",
164 7: "C07",
165 8: "C10",
166 9: "C11",
167 10: "C12",
168 11: "C13",
169 12: "C14",
170 13: "C15",
171 14: "C16",
172 15: "C17",
173 }
175 def finalize(self):
176 super().finalize()
178 # Configure each panel
179 for key, value in self.produce.plot.valsGroupBy.items():
180 gridPanelConfig = GridPanelConfig(
181 plotElement=self.plotElement,
182 title={"label": str(value), "fontsize": "10"},
183 titleY=0.85,
184 )
185 self.produce.plot.panels[key] = gridPanelConfig
188class CalibQuantityAmpProfileScatterTool(CalibQuantityBaseTool):
189 def setDefaults(self):
190 super().setDefaults()
191 self.plotElement = ScatterElement()
192 self.prep.panelKey = "amplifier"
193 self.prep.dataKey = "mjd"
196class CalibQuantityAmpProfileHistTool(CalibQuantityBaseTool):
197 def setDefaults(self):
198 super().setDefaults()
199 self.plotElement = HistElement()
200 self.prep.panelKey = "amplifier"
201 self.prep.dataKey = "mjd"
204class CalibAmpScatterTool(CalibQuantityBaseTool):
205 def setDefaults(self):
206 super().setDefaults()
207 self.plotElement = ScatterElement()
209 # Repack the input data into a usable format
210 self.prep = SingleValueRepacker()
212 self.produce.plot = GridPlot()
213 self.produce.plot.panels = {}
214 self.produce.plot.numRows = 4
215 self.produce.plot.numCols = 4
217 # Values to use for x-axis data
218 self.produce.plot.xDataKeys = {
219 0: "C00_x",
220 1: "C01_x",
221 2: "C02_x",
222 3: "C03_x",
223 4: "C04_x",
224 5: "C05_x",
225 6: "C06_x",
226 7: "C07_x",
227 8: "C10_x",
228 9: "C11_x",
229 10: "C12_x",
230 11: "C13_x",
231 12: "C14_x",
232 13: "C15_x",
233 14: "C16_x",
234 15: "C17_x",
235 }
237 # Values to group by to distinguish between data in differing panels
238 self.produce.plot.valsGroupBy = {
239 0: "C00",
240 1: "C01",
241 2: "C02",
242 3: "C03",
243 4: "C04",
244 5: "C05",
245 6: "C06",
246 7: "C07",
247 8: "C10",
248 9: "C11",
249 10: "C12",
250 11: "C13",
251 12: "C14",
252 13: "C15",
253 14: "C16",
254 15: "C17",
255 }
257 self.prep.panelKey = "amplifier"
258 self.prep.dataKey = "mjd"
260 def finalize(self):
261 super().finalize()
263 # Configure each panel
264 for key, value in self.produce.plot.valsGroupBy.items():
265 gridPanelConfig = GridPanelConfig(
266 plotElement=self.plotElement,
267 title={"label": str(value), "fontsize": "10"},
268 titleY=0.85,
269 )
270 self.produce.plot.panels[key] = gridPanelConfig
271 self.produce.plot.panels[key].plotElement.xKey = f"{value}_x"
272 self.produce.plot.panels[key].plotElement.valsKey = value
275class CalibDivisaderoScatterTool(CalibQuantityBaseTool):
276 def setDefaults(self):
277 super().setDefaults()
278 self.plotElement = ScatterElement()
280 # Repack the input data into a usable format
281 self.prep = SingleValueRepacker()
283 self.produce.plot = GridPlot()
284 self.produce.plot.panels = {}
285 self.produce.plot.numRows = 2
286 self.produce.plot.numCols = 1
288 # Values to group by to distinguish between data in differing panels
289 self.produce.plot.valsGroupBy = {0: "bank1", 1: "bank0"}
291 self.produce.plot.xDataKeys = {
292 0: "bank1_x",
293 1: "bank0_x",
294 }
295 self.prep.panelKey = "amplifier"
296 self.prep.dataKey = "mjd"
298 def finalize(self):
299 super().finalize()
301 # Configure each panel
302 for key, value in self.produce.plot.valsGroupBy.items():
303 gridPanelConfig = GridPanelConfig(
304 plotElement=self.plotElement,
305 title={"label": str(value), "fontsize": "10"},
306 titleY=0.85,
307 )
308 self.produce.plot.panels[key] = gridPanelConfig
309 self.produce.plot.panels[key].plotElement.xKey = f"{value}_x"
310 self.produce.plot.panels[key].plotElement.valsKey = value
313class CalibPtcCovarScatterTool(CalibQuantityBaseTool):
314 def setDefaults(self):
315 super().setDefaults()
316 self.plotElement = ScatterElement()
318 # Repack the input data into a usable format
319 self.prep = PrepRepacker()
321 self.produce.plot = GridPlot()
322 self.produce.plot.panels = {}
323 self.produce.plot.numRows = 3
324 self.produce.plot.numCols = 2
326 # Values to group by to distinguish between data in differing panels
327 self.produce.plot.valsGroupBy = {
328 0: "PTC_COV_10",
329 1: "PTC_COV_20",
330 2: "PTC_COV_01",
331 3: "PTC_COV_02",
332 4: "PTC_COV_11",
333 5: None,
334 }
336 self.produce.plot.xDataKeys = {
337 0: "PTC_PTC_RAW_MEANS",
338 1: "PTC_PTC_RAW_MEANS",
339 2: "PTC_PTC_RAW_MEANS",
340 3: "PTC_PTC_RAW_MEANS",
341 4: "PTC_PTC_RAW_MEANS",
342 5: None,
343 }
344 self.prep.panelKey = "amplifier"
345 self.prep.dataKey = "mjd"
347 def finalize(self):
348 super().finalize()
350 # Configure each panel
351 for key, value in self.produce.plot.valsGroupBy.items():
352 gridPanelConfig = GridPanelConfig(
353 plotElement=self.plotElement,
354 title={"label": str(value), "fontsize": "10"},
355 titleY=0.85,
356 )
357 self.produce.plot.panels[key] = gridPanelConfig
358 self.produce.plot.panels[key].plotElement.xKey = "PTC_PTC_RAW_MEANS"
359 self.produce.plot.panels[key].plotElement.valsKey = f"PTC_{value}"