Coverage for python/lsst/cp/verify/mergeResults.py: 38%
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',
28 'CpVerifyVisitExpMergeConfig', 'CpVerifyVisitExpMergeTask',
29 'CpVerifyVisitRunMergeConfig', 'CpVerifyVisitRunMergeTask']
32class CpVerifyExpMergeConnections(pipeBase.PipelineTaskConnections,
33 dimensions={"instrument", "exposure"},
34 defaultTemplates={}):
35 inputStats = cT.Input(
36 name="detectorStats",
37 doc="Input statistics to merge.",
38 storageClass="StructuredDataDict",
39 dimensions=["instrument", "exposure", "detector"],
40 multiple=True,
41 )
42 camera = cT.PrerequisiteInput(
43 name="camera",
44 storageClass="Camera",
45 doc="Input camera.",
46 dimensions=["instrument", ],
47 isCalibration=True,
48 )
50 outputStats = cT.Output(
51 name="exposureStats",
52 doc="Output statistics.",
53 storageClass="StructuredDataDict",
54 dimensions=["instrument", "exposure"],
55 )
58class CpVerifyExpMergeConfig(pipeBase.PipelineTaskConfig,
59 pipelineConnections=CpVerifyExpMergeConnections):
60 """Configuration parameters for exposure stats merging.
61 """
62 exposureStatKeywords = pexConfig.DictField(
63 keytype=str,
64 itemtype=str,
65 doc="Dictionary of statistics to run on the set of detector values. The key should be the test "
66 "name to record in the output, and the value should be the `lsst.afw.math` statistic name string.",
67 default={},
68 )
71class CpVerifyExpMergeTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
72 """Merge statistics from detectors together.
73 """
74 ConfigClass = CpVerifyExpMergeConfig
75 _DefaultName = 'cpVerifyExpMerge'
77 def runQuantum(self, butlerQC, inputRefs, outputRefs):
78 inputs = butlerQC.get(inputRefs)
80 dimensions = [exp.dataId.byName() for exp in inputRefs.inputStats]
81 inputs['inputDims'] = dimensions
83 outputs = self.run(**inputs)
84 butlerQC.put(outputs, outputRefs)
86 def run(self, inputStats, camera, inputDims):
87 """Merge statistics.
89 Parameters
90 ----------
91 inputStats : `list` [`dict`]
92 Measured statistics for a detector (from
93 CpVerifyStatsTask).
94 camera : `lsst.afw.cameraGeom.Camera`
95 The camera geometry for this exposure.
96 inputDims : `list` [`dict`]
97 List of dictionaries of input data dimensions/values.
98 Each list entry should contain:
100 ``"exposure"``
101 exposure id value (`int`)
102 ``"detector"``
103 detector id value (`int`)
105 Returns
106 -------
107 outputStats : `dict`
108 Merged full exposure statistics.
110 See Also
111 --------
112 lsst.cp.verify.CpVerifyStatsTask
114 Notes
115 -----
116 The outputStats should have a yaml representation of the form:
118 DET:
119 DetName1:
120 FAILURES:
121 - TEST_NAME
122 STAT: value
123 STAT2: value2
124 DetName2:
125 VERIFY:
126 TEST: boolean
127 TEST2: boolean
128 SUCCESS: boolean
129 """
130 outputStats = {}
131 success = True
133 mergedStats = {}
134 for detStats, dimensions in zip(inputStats, inputDims):
135 detId = dimensions['detector']
136 detName = camera[detId].getName()
137 calcStats = {}
139 mergedStats[detName] = detStats
141 if detStats['SUCCESS'] is True:
142 calcStats['SUCCESS'] = True
143 else:
144 calcStats['SUCCESS'] = False
145 calcStats['FAILURES'] = list()
146 success = False
147 # See if the detector failed
148 if 'DET' in detStats['VERIFY']:
149 detSuccess = detStats['VERIFY']['DET'].pop('SUCCESS', False)
150 if not detSuccess:
151 for testName, testResult in detStats['VERIFY']['DET'].items():
152 if testResult is False:
153 calcStats['FAILURES'].append(testName)
154 # See if the catalog failed
155 if 'CATALOG' in detStats['VERIFY']:
156 for testName, testResult in detStats['VERIFY']['CATALOG'].items():
157 if testResult is False:
158 calcStats['FAILURES'].append(testName)
159 # See if an amplifier failed
160 for ampName, ampStats in detStats['VERIFY']['AMP'].items():
161 ampSuccess = ampStats.pop('SUCCESS')
162 if not ampSuccess:
163 for testName, testResult in ampStats.items():
164 if testResult is False:
165 calcStats['FAILURES'].append(ampName + " " + testName)
167 outputStats[detName] = calcStats
169 exposureSuccess = True
170 if len(self.config.exposureStatKeywords):
171 outputStats['EXP'] = self.exposureStatistics(mergedStats)
172 outputStats['VERIFY'], exposureSuccess = self.verify(mergedStats, outputStats)
174 outputStats['SUCCESS'] = success & exposureSuccess
176 return pipeBase.Struct(
177 outputStats=outputStats,
178 )
180 def exposureStatistics(self, statisticsDict):
181 """Calculate exposure level statistics based on the existing
182 per-amplifier and per-detector measurements.
184 Parameters
185 ----------
186 statisticsDictionary : `dict [`str`, `dict` [`str`, scalar]],
187 Dictionary of measured statistics. The top level
188 dictionary is keyed on the detector names, and contains
189 the measured statistics from the per-detector
190 measurements.
192 Returns
193 -------
194 outputStatistics : `dict` [`str, scalar]
195 A dictionary of the statistics measured and their values.
196 """
197 raise NotImplementedError("Subclasses must implement verification criteria.")
199 def verify(self, detectorStatistics, statisticsDictionary):
201 """Verify if the measured statistics meet the verification criteria.
203 Parameters
204 ----------
205 detectorStatistics : `dict` [`str`, `dict` [`str`, scalar]]
206 Merged set of input detector level statistics.
207 statisticsDictionary : `dict` [`str`, `dict` [`str`, scalar]]
208 Dictionary of measured statistics. The inner dictionary
209 should have keys that are statistic names (`str`) with
210 values that are some sort of scalar (`int` or `float` are
211 the mostly likely types).
213 Returns
214 -------
215 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]]
216 A dictionary indexed by the amplifier name, containing
217 dictionaries of the verification criteria.
218 success : `bool`
219 A boolean indicating if all tests have passed.
221 Raises
222 ------
223 NotImplementedError :
224 This method must be implemented by the calibration-type
225 subclass.
226 """
227 raise NotImplementedError("Subclasses must implement verification criteria.")
230class CpVerifyRunMergeConnections(pipeBase.PipelineTaskConnections,
231 dimensions={"instrument"},
232 defaultTemplates={}):
233 inputStats = cT.Input(
234 name="exposureStats",
235 doc="Input statistics to merge.",
236 storageClass="StructuredDataDict",
237 dimensions=["instrument", "exposure"],
238 multiple=True,
239 )
241 outputStats = cT.Output(
242 name="runStats",
243 doc="Output statistics.",
244 storageClass="StructuredDataDict",
245 dimensions=["instrument"],
246 )
249class CpVerifyRunMergeConfig(pipeBase.PipelineTaskConfig,
250 pipelineConnections=CpVerifyRunMergeConnections):
251 """Configuration paramters for exposure stats merging.
252 """
253 runStatKeywords = pexConfig.DictField(
254 keytype=str,
255 itemtype=str,
256 doc="Dictionary of statistics to run on the set of exposure values. The key should be the test "
257 "name to record in the output, and the value should be the `lsst.afw.math` statistic name string.",
258 default={},
259 )
262class CpVerifyRunMergeTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
263 """Merge statistics from detectors together.
264 """
265 ConfigClass = CpVerifyRunMergeConfig
266 _DefaultName = 'cpVerifyRunMerge'
268 def runQuantum(self, butlerQC, inputRefs, outputRefs):
269 inputs = butlerQC.get(inputRefs)
271 dimensions = [exp.dataId.byName() for exp in inputRefs.inputStats]
272 inputs['inputDims'] = dimensions
274 outputs = self.run(**inputs)
275 butlerQC.put(outputs, outputRefs)
277 def run(self, inputStats, inputDims):
278 """Merge statistics.
280 Parameters
281 ----------
282 inputStats : `list` [`dict`]
283 Measured statistics for a detector.
284 inputDims : `list` [`dict`]
285 List of dictionaries of input data dimensions/values.
286 Each list entry should contain:
288 ``"exposure"``
289 exposure id value (`int`)
291 Returns
292 -------
293 outputStats : `dict`
294 Merged full exposure statistics.
296 Notes
297 -----
298 The outputStats should have a yaml representation as follows.
300 VERIFY:
301 ExposureId1:
302 VERIFY_MEAN: boolean
303 VERIFY_SIGMA: boolean
304 ExposureId2:
305 [...]
306 MEAN_UNIMODAL: boolean
307 SIGMA_UNIMODAL: boolean
308 """
309 outputStats = {}
310 success = True
311 for expStats, dimensions in zip(inputStats, inputDims):
312 expId = dimensions.get('exposure', dimensions.get('visit', None))
313 if expId is None:
314 raise RuntimeError("Could not identify the exposure from %s", dimensions)
316 calcStats = {}
318 expSuccess = expStats.pop('SUCCESS')
319 if expSuccess:
320 calcStats['SUCCESS'] = True
321 else:
322 calcStats['FAILURES'] = list()
323 success = False
324 for detName, detStats in expStats.items():
325 detSuccess = detStats.pop('SUCCESS')
326 if not detSuccess:
327 for testName in expStats[detName]['FAILURES']:
328 calcStats['FAILURES'].append(detName + " " + testName)
330 outputStats[expId] = calcStats
332 runSuccess = True
333 if len(self.config.runStatKeywords):
334 outputStats['VERIFY'], runSuccess = self.verify(outputStats)
336 outputStats['SUCCESS'] = success & runSuccess
338 return pipeBase.Struct(
339 outputStats=outputStats,
340 )
342 def verify(self, statisticsDictionary):
343 """Verify if the measured statistics meet the verification criteria.
345 Parameters
346 ----------
347 statisticsDictionary : `dict` [`str`, `dict`],
348 Dictionary of measured statistics. The inner dictionary
349 should have keys that are statistic names (`str`) with
350 values that are some sort of scalar (`int` or `float` are
351 the mostly likely types).
353 Returns
354 -------
355 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]]
356 A dictionary indexed by the amplifier name, containing
357 dictionaries of the verification criteria.
358 success : `bool`
359 A boolean indicating if all tests have passed.
361 Raises
362 ------
363 NotImplementedError :
364 This method must be implemented by the calibration-type
365 subclass.
367 """
368 raise NotImplementedError("Subclasses must implement verification criteria.")
371class CpVerifyVisitExpMergeConnections(pipeBase.PipelineTaskConnections,
372 dimensions={"instrument", "visit"},
373 defaultTemplates={}):
374 inputStats = cT.Input(
375 name="detectorStats",
376 doc="Input statistics to merge.",
377 storageClass="StructuredDataDict",
378 dimensions=["instrument", "visit", "detector"],
379 multiple=True,
380 )
381 camera = cT.PrerequisiteInput(
382 name="camera",
383 storageClass="Camera",
384 doc="Input camera.",
385 dimensions=["instrument", ],
386 isCalibration=True,
387 )
389 outputStats = cT.Output(
390 name="exposureStats",
391 doc="Output statistics.",
392 storageClass="StructuredDataDict",
393 dimensions=["instrument", "visit"],
394 )
397class CpVerifyVisitExpMergeConfig(CpVerifyExpMergeConfig,
398 pipelineConnections=CpVerifyVisitExpMergeConnections):
399 pass
402class CpVerifyVisitExpMergeTask(CpVerifyExpMergeTask):
403 """Merge visit based data."""
405 ConfigClass = CpVerifyVisitExpMergeConfig
406 _DefaultName = 'cpVerifyVisitExpMerge'
408 pass
411class CpVerifyVisitRunMergeConnections(pipeBase.PipelineTaskConnections,
412 dimensions={"instrument"},
413 defaultTemplates={}):
414 inputStats = cT.Input(
415 name="exposureStats",
416 doc="Input statistics to merge.",
417 storageClass="StructuredDataDict",
418 dimensions=["instrument", "visit"],
419 multiple=True,
420 )
422 outputStats = cT.Output(
423 name="runStats",
424 doc="Output statistics.",
425 storageClass="StructuredDataDict",
426 dimensions=["instrument"],
427 )
430class CpVerifyVisitRunMergeConfig(CpVerifyRunMergeConfig,
431 pipelineConnections=CpVerifyVisitRunMergeConnections):
432 pass
435class CpVerifyVisitRunMergeTask(CpVerifyRunMergeTask):
436 """Merge visit based data."""
438 ConfigClass = CpVerifyVisitRunMergeConfig
439 _DefaultName = 'cpVerifyVisitRunMerge'
441 pass