Coverage for python/lsst/cp/verify/mergeResults.py: 20%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of cp_verify.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://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 <http://www.gnu.org/licenses/>.
21import lsst.pipe.base as pipeBase
22import lsst.pipe.base.connectionTypes as cT
23import lsst.pex.config as pexConfig
26__all__ = ['CpVerifyExpMergeConfig', 'CpVerifyExpMergeTask',
27 'CpVerifyRunMergeConfig', 'CpVerifyRunMergeTask']
30class CpVerifyExpMergeConnections(pipeBase.PipelineTaskConnections,
31 dimensions={"instrument", "exposure"},
32 defaultTemplates={}):
33 inputStats = cT.Input(
34 name="detectorStats",
35 doc="Input statistics to merge.",
36 storageClass="StructuredDataDict",
37 dimensions=["instrument", "exposure", "detector"],
38 multiple=True,
39 )
40 camera = cT.PrerequisiteInput(
41 name="camera",
42 storageClass="Camera",
43 doc="Input camera.",
44 dimensions=["instrument", ],
45 isCalibration=True,
46 )
48 outputStats = cT.Output(
49 name="exposureStats",
50 doc="Output statistics.",
51 storageClass="StructuredDataDict",
52 dimensions=["instrument", "exposure"],
53 )
56class CpVerifyExpMergeConfig(pipeBase.PipelineTaskConfig,
57 pipelineConnections=CpVerifyExpMergeConnections):
58 """Configuration parameters for exposure stats merging.
59 """
60 exposureStatKeywords = pexConfig.DictField(
61 keytype=str,
62 itemtype=str,
63 doc="Dictionary of statistics to run on the set of detector values. The key should be the test "
64 "name to record in the output, and the value should be the `lsst.afw.math` statistic name string.",
65 default={},
66 )
69class CpVerifyExpMergeTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
70 """Merge statistics from detectors together.
71 """
72 ConfigClass = CpVerifyExpMergeConfig
73 _DefaultName = 'cpVerifyExpMerge'
75 def runQuantum(self, butlerQC, inputRefs, outputRefs):
76 inputs = butlerQC.get(inputRefs)
78 dimensions = [exp.dataId.byName() for exp in inputRefs.inputStats]
79 inputs['inputDims'] = dimensions
81 outputs = self.run(**inputs)
82 butlerQC.put(outputs, outputRefs)
84 def run(self, inputStats, camera, inputDims):
85 """Merge statistics.
87 Parameters
88 ----------
89 inputStats : `list` [`dict`]
90 Measured statistics for a detector (from
91 CpVerifyStatsTask).
92 camera : `lsst.afw.cameraGeom.Camera`
93 The camera geometry for this exposure.
94 inputDims : `list` [`dict`]
95 List of dictionaries of input data dimensions/values.
96 Each list entry should contain:
98 ``"exposure"``
99 exposure id value (`int`)
100 ``"detector"``
101 detector id value (`int`)
103 Returns
104 -------
105 outputStats : `dict`
106 Merged full exposure statistics.
108 See Also
109 --------
110 lsst.cp.verify.CpVerifyStatsTask
112 Notes
113 -----
114 The outputStats should have a yaml representation of the form:
116 DET:
117 DetName1:
118 FAILURES:
119 - TEST_NAME
120 STAT: value
121 STAT2: value2
122 DetName2:
123 VERIFY:
124 TEST: boolean
125 TEST2: boolean
126 SUCCESS: boolean
127 """
128 outputStats = {}
129 success = True
131 mergedStats = {}
132 for detStats, dimensions in zip(inputStats, inputDims):
133 detId = dimensions['detector']
134 detName = camera[detId].getName()
135 calcStats = {}
137 mergedStats[detName] = detStats
139 if detStats['SUCCESS'] is True:
140 calcStats['SUCCESS'] = True
141 else:
142 calcStats['SUCCESS'] = False
143 calcStats['FAILURES'] = list()
144 success = False
145 # See if the detector failed
146 if 'DET' in detStats['VERIFY']:
147 for testName, testResult in detStats['VERIFY']['DET'].items():
148 if testResult is False:
149 calcStats['FAILURES'].append(testName)
150 # See if the catalog failed
151 if 'CATALOG' in detStats['VERIFY']:
152 for testName, testResult in detStats['VERIFY']['CATALOG'].items():
153 if testResult is False:
154 calcStats['FAILURES'].append(testName)
155 # See if an amplifier failed
156 for ampName, ampStats in detStats['VERIFY']['AMP'].items():
157 ampSuccess = ampStats.pop('SUCCESS')
158 if not ampSuccess:
159 for testName, testResult in ampStats.items():
160 if testResult is False:
161 calcStats['FAILURES'].append(ampName + " " + testName)
163 outputStats[detName] = calcStats
165 exposureSuccess = True
166 if len(self.config.exposureStatKeywords):
167 outputStats['EXP'] = self.exposureStatistics(mergedStats)
168 outputStats['VERIFY'], exposureSuccess = self.verify(mergedStats, outputStats)
170 outputStats['SUCCESS'] = success & exposureSuccess
172 return pipeBase.Struct(
173 outputStats=outputStats,
174 )
176 def exposureStatistics(self, statisticsDict):
177 """Calculate exposure level statistics based on the existing
178 per-amplifier and per-detector measurements.
180 Parameters
181 ----------
182 statisticsDictionary : `dict [`str`, `dict` [`str`, scalar]],
183 Dictionary of measured statistics. The top level
184 dictionary is keyed on the detector names, and contains
185 the measured statistics from the per-detector
186 measurements.
188 Returns
189 -------
190 outputStatistics : `dict` [`str, scalar]
191 A dictionary of the statistics measured and their values.
192 """
193 raise NotImplementedError("Subclasses must implement verification criteria.")
195 def verify(self, detectorStatistics, statisticsDictionary):
197 """Verify if the measured statistics meet the verification criteria.
199 Parameters
200 ----------
201 detectorStatistics : `dict` [`str`, `dict` [`str`, scalar]]
202 Merged set of input detector level statistics.
203 statisticsDictionary : `dict` [`str`, `dict` [`str`, scalar]]
204 Dictionary of measured statistics. The inner dictionary
205 should have keys that are statistic names (`str`) with
206 values that are some sort of scalar (`int` or `float` are
207 the mostly likely types).
209 Returns
210 -------
211 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]]
212 A dictionary indexed by the amplifier name, containing
213 dictionaries of the verification criteria.
214 success : `bool`
215 A boolean indicating if all tests have passed.
217 Raises
218 ------
219 NotImplementedError :
220 This method must be implemented by the calibration-type
221 subclass.
222 """
223 raise NotImplementedError("Subclasses must implement verification criteria.")
226class CpVerifyRunMergeConnections(pipeBase.PipelineTaskConnections,
227 dimensions={"instrument"},
228 defaultTemplates={}):
229 inputStats = cT.Input(
230 name="exposureStats",
231 doc="Input statistics to merge.",
232 storageClass="StructuredDataDict",
233 dimensions=["instrument", "exposure"],
234 multiple=True,
235 )
237 outputStats = cT.Output(
238 name="runStats",
239 doc="Output statistics.",
240 storageClass="StructuredDataDict",
241 dimensions=["instrument"],
242 )
245class CpVerifyRunMergeConfig(pipeBase.PipelineTaskConfig,
246 pipelineConnections=CpVerifyRunMergeConnections):
247 """Configuration paramters for exposure stats merging.
248 """
249 runStatKeywords = pexConfig.DictField(
250 keytype=str,
251 itemtype=str,
252 doc="Dictionary of statistics to run on the set of exposure values. The key should be the test "
253 "name to record in the output, and the value should be the `lsst.afw.math` statistic name string.",
254 default={},
255 )
258class CpVerifyRunMergeTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
259 """Merge statistics from detectors together.
260 """
261 ConfigClass = CpVerifyRunMergeConfig
262 _DefaultName = 'cpVerifyRunMerge'
264 def runQuantum(self, butlerQC, inputRefs, outputRefs):
265 inputs = butlerQC.get(inputRefs)
267 dimensions = [exp.dataId.byName() for exp in inputRefs.inputStats]
268 inputs['inputDims'] = dimensions
270 outputs = self.run(**inputs)
271 butlerQC.put(outputs, outputRefs)
273 def run(self, inputStats, inputDims):
274 """Merge statistics.
276 Parameters
277 ----------
278 inputStats : `list` [`dict`]
279 Measured statistics for a detector.
280 inputDims : `list` [`dict`]
281 List of dictionaries of input data dimensions/values.
282 Each list entry should contain:
284 ``"exposure"``
285 exposure id value (`int`)
287 Returns
288 -------
289 outputStats : `dict`
290 Merged full exposure statistics.
292 Notes
293 -----
294 The outputStats should have a yaml representation as follows.
296 VERIFY:
297 ExposureId1:
298 VERIFY_MEAN: boolean
299 VERIFY_SIGMA: boolean
300 ExposureId2:
301 [...]
302 MEAN_UNIMODAL: boolean
303 SIGMA_UNIMODAL: boolean
304 """
305 outputStats = {}
306 success = True
307 for expStats, dimensions in zip(inputStats, inputDims):
308 expId = dimensions['exposure']
309 calcStats = {}
311 expSuccess = expStats.pop('SUCCESS')
312 if expSuccess:
313 calcStats['SUCCESS'] = True
314 else:
315 calcStats['FAILURES'] = list()
316 success = False
317 for detName, detStats in expStats.items():
318 detSuccess = detStats.pop('SUCCESS')
319 if not detSuccess:
320 for testName in expStats[detName]['FAILURES']:
321 calcStats['FAILURES'].append(detName + " " + testName)
323 outputStats[expId] = calcStats
325 runSuccess = True
326 if len(self.config.runStatKeywords):
327 outputStats['VERIFY'], runSuccess = self.verify(outputStats)
329 outputStats['SUCCESS'] = success & runSuccess
331 return pipeBase.Struct(
332 outputStats=outputStats,
333 )
335 def verify(self, statisticsDictionary):
336 """Verify if the measured statistics meet the verification criteria.
338 Parameters
339 ----------
340 statisticsDictionary : `dict` [`str`, `dict`],
341 Dictionary of measured statistics. The inner dictionary
342 should have keys that are statistic names (`str`) with
343 values that are some sort of scalar (`int` or `float` are
344 the mostly likely types).
346 Returns
347 -------
348 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]]
349 A dictionary indexed by the amplifier name, containing
350 dictionaries of the verification criteria.
351 success : `bool`
352 A boolean indicating if all tests have passed.
354 Raises
355 ------
356 NotImplementedError :
357 This method must be implemented by the calibration-type
358 subclass.
360 """
361 raise NotImplementedError("Subclasses must implement verification criteria.")