Coverage for python/lsst/cp/verify/verifyFlat.py: 15%
102 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-14 03:02 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-14 03:02 -0700
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 numpy as np
22import lsst.afw.math as afwMath
23from .verifyStats import CpVerifyStatsConfig, CpVerifyStatsTask, CpVerifyStatsConnections
24from .mergeResults import CpVerifyExpMergeByFilterConfig, CpVerifyExpMergeByFilterTask
26__all__ = ['CpVerifyFlatConfig', 'CpVerifyFlatTask',
27 'CpVerifyFlatExpMergeConfig', 'CpVerifyFlatExpMergeTask']
28# 'CpVerifyFlatRunMergeConfig', 'CpVerifyFlatRunMergeTask',]
31class CpVerifyFlatConfig(CpVerifyStatsConfig,
32 pipelineConnections=CpVerifyStatsConnections):
33 """Inherits from base CpVerifyStatsConfig.
34 """
36 def setDefaults(self):
37 super().setDefaults()
38 self.stageName = 'FLAT'
39 self.imageStatKeywords = {'MEAN': 'MEAN', # noqa F841
40 'NOISE': 'STDEVCLIP', }
41 self.detectorStatKeywords = {'MEAN': 'MEAN', # noqa F841
42 'SCATTER': 'STDEV', }
45class CpVerifyFlatTask(CpVerifyStatsTask):
46 """Flat verification sub-class, implementing the verify method.
47 """
48 ConfigClass = CpVerifyFlatConfig
49 _DefaultName = 'cpVerifyFlat'
51 def detectorStatistics(self, statisticsDict, statControl, exposure=None, uncorrectedExposure=None):
52 """Calculate detector level statistics based on the existing
53 per-amplifier measurements.
55 Parameters
56 ----------
57 statisticsDict : `dict` [`str`, `dict` [`str`, scalar]],
58 Dictionary of measured statistics. The inner dictionary
59 should have keys that are statistic names (`str`) with
60 values that are some sort of scalar (`int` or `float` are
61 the mostly likely types).
62 statControl : `lsst.afw.math.StatControl`
63 Statistics control object with parameters defined by
64 the config.
65 exposure : `lsst.afw.image.Exposure`, optional
66 Exposure containing the ISR-processed data to measure.
67 uncorrectedExposure : `lsst.afw.image.Exposure`, optional
68 uncorrected esposure (no defects) containing the
69 ISR-processed data to measure.
71 Returns
72 -------
73 outputStatistics : `dict` [`str`, scalar]
74 A dictionary of the statistics measured and their values.
76 """
77 outputStatistics = {}
79 ampStats = statisticsDict['AMP']
80 amplifierMeans = [stats['MEAN'] for stats in ampStats.values()]
82 statisticToRun, statAccessor = self._configHelper(self.config.detectorStatKeywords)
83 stats = afwMath.makeStatistics(amplifierMeans, statisticToRun, statControl)
85 for k, v in statAccessor.items():
86 outputStatistics[k] = stats.getValue(v)
88 return outputStatistics
90 def verify(self, exposure, statisticsDict):
91 """Verify that the measured statistics meet the verification criteria.
93 Parameters
94 ----------
95 exposure : `lsst.afw.image.Exposure`
96 The exposure the statistics are from.
97 statisticsDict : `dict` [`str`, `dict` [`str`, scalar]],
98 Dictionary of measured statistics. The inner dictionary
99 should have keys that are statistic names (`str`) with
100 values that are some sort of scalar (`int` or `float` are
101 the mostly likely types).
103 Returns
104 -------
105 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]]
106 A dictionary indexed by the amplifier name, containing
107 dictionaries of the verification criteria.
108 success : `bool`
109 A boolean indicating if all tests have passed.
110 """
111 ampStats = statisticsDict['AMP']
112 verifyStats = {}
113 success = True
114 for ampName, stats in ampStats.items():
115 verify = {}
117 # DMTN-101 Test 10.X: confirm that per-amplifier scatter is
118 # consistent with Poissonian
119 verify['NOISE'] = bool(stats['NOISE'] <= np.sqrt(stats['MEAN']))
121 verify['SUCCESS'] = bool(np.all(list(verify.values())))
122 if verify['SUCCESS'] is False:
123 success = False
125 verifyStats[ampName] = verify
127 verifyDet = {}
128 detStats = statisticsDict['DET']
130 # DMTN-101 Test 10.Y: confirm intra-chip scatter is small.
131 verifyDet['SCATTER'] = bool(detStats['SCATTER']/detStats['MEAN'] <= 0.05)
133 verifyDet['SUCCESS'] = bool(np.all(list(verifyDet.values())))
134 if verifyDet['SUCCESS'] is False:
135 success = False
137 return {'AMP': verifyStats, 'DET': verifyDet}, bool(success)
139 def repackStats(self, statisticsDict, dimensions):
140 # docstring inherited
141 rows = {}
142 rowList = []
143 matrixRowList = None
145 if self.config.useIsrStatistics:
146 mjd = statisticsDict["ISR"]["MJD"]
147 else:
148 mjd = np.nan
150 print(dimensions)
151 rowBase = {
152 "instrument": dimensions["instrument"],
153 "exposure": dimensions["exposure"],
154 "detector": dimensions["detector"],
155 "physical_filter": dimensions["physical_filter"],
156 "mjd": mjd,
157 }
159 # AMP results:
160 for ampName, stats in statisticsDict["AMP"].items():
161 rows[ampName] = {}
162 rows[ampName].update(rowBase)
163 rows[ampName]["amplifier"] = ampName
164 for key, value in stats.items():
165 rows[ampName][f"{self.config.stageName}_{key}"] = value
167 # VERIFY results
168 for ampName, stats in statisticsDict["VERIFY"]["AMP"].items():
169 for key, value in stats.items():
170 rows[ampName][f"{self.config.stageName}_VERIFY_{key}"] = value
172 # METADATA results
173 # DET results
174 rows['detector'] = rowBase
175 for testName, value in statisticsDict["DET"].items():
176 verifyDict = statisticsDict["VERIFY"]["DET"]
177 rows['detector'][f"{self.config.stageName}_DET_{testName}"] = value
178 if testName in verifyDict:
179 rows['detector'][f"{self.config.stageName}_DET_VERIFY_{testName}"] = verifyDict[testName]
181 # ISR results
182 if self.config.useIsrStatistics and "ISR" in statisticsDict:
183 for ampName, stats in statisticsDict["ISR"]["CALIBDIST"].items():
184 for level in self.config.expectedDistributionLevels:
185 key = f"LSST CALIB {self.config.stageName.upper()} {ampName} DISTRIBUTION {level}-PCT"
186 rows[ampName][f"{self.config.stageName}_FLAT_DIST_{level}_PCT"] = stats[key]
188 # pack final list
189 for ampName, stats in rows.items():
190 rowList.append(stats)
192 return rowList, matrixRowList
195class CpVerifyFlatExpMergeConfig(CpVerifyExpMergeByFilterConfig):
196 """Inherits from base CpVerifyExpMergeConfig
197 """
199 def setDefaults(self):
200 super().setDefaults()
201 self.statKeywords = {
202 'EXPOSURE_SCATTER': 'STDEV', # noqa F841
203 }
206class CpVerifyFlatExpMergeTask(CpVerifyExpMergeByFilterTask):
207 """Inherits from base CpVerifyExpMergeTask
208 """
209 ConfigClass = CpVerifyFlatExpMergeConfig
210 _DefaultName = 'cpVerifyFlatExpMerge'
212 def calcStatistics(self, statisticsDictionary):
213 """Calculate exposure level statistics based on the existing
214 per-amplifier and per-detector measurements.
216 Parameters
217 ----------
218 statisticsDictionary : `dict [`str`, `dict` [`str`, scalar]],
219 Dictionary of measured statistics. The top level
220 dictionary is keyed on the detector names, and contains
221 the measured statistics from the per-detector
222 measurements.
224 Returns
225 -------
226 outputStatistics : `dict` [`str, scalar]
227 A dictionary of the statistics measured and their values.
228 """
229 detectorMeans = []
230 for detName, stats in statisticsDictionary.items():
231 # Get detector stats:
232 detectorMeans.append(stats['DET']['MEAN'])
234 return {'SCATTER': float(np.std(detectorMeans))}
236 def verify(self, detectorStatistics, statisticsDictionary):
237 """Verify if the measured statistics meet the verification criteria.
239 Parameters
240 ----------
241 detectorStatistics : `dict` [`str`, `dict` [`str`, scalar]]
242 Merged set of input detector level statistics.
243 statisticsDictionary : `dict` [`str`, `dict` [`str`, scalar]],
244 Dictionary of measured statistics. The inner dictionary
245 should have keys that are statistic names (`str`) with
246 values that are some sort of scalar (`int` or `float` are
247 the mostly likely types).
249 Returns
250 -------
251 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]]
252 A dictionary indexed by the amplifier name, containing
253 dictionaries of the verification criteria.
254 success : `bool`
255 A boolean indicating if all tests have passed.
257 """
258 verifyStats = {}
259 success = True
261 # DMTN-101 Test 10.Z: confirm inter-chip scatter is small.
262 verifyStats['SCATTER'] = bool(statisticsDictionary['EXP']['SCATTER'] <= 0.05)
264 success = bool(np.all(list(verifyStats.values())))
266 return {'EXP': verifyStats}, bool(success)
268 def pack(self, statisticsDict, dimensions, outKey):
269 """Repack information into flat tables.
271 Parameters
272 ----------
273 statisticsDictionary : `dict` [`str`, `dict` [`str`, scalar]],
274 Dictionary of measured statistics. The inner dictionary
275 should have keys that are statistic names (`str`) with
276 values that are some sort of scalar (`int` or `float` are
277 the mostly likely types).
278 dimensions : `dict`
279 Dictionary of input dimensions.
280 outKey : `str`
281 Key to use to access the data to pack.
283 Returns
284 -------
285 outputResults : `list` [`dict`]
286 A list of rows to add to the output table.
287 outputMatrix : `list` [`dict`]
288 A list of rows to add to the output matrix.
289 """
290 rowList = []
291 matrixRowList = None
293 # We can only do stats if we only have one thing.
294 rowBase = {
295 "instrument": dimensions[0]["instrument"],
296 "exposure": dimensions[0]["exposure"],
297 "detector": dimensions[0]["detector"],
298 }
300 # This only needs to add the new results.
301 stats = statisticsDict[outKey]
302 verify = statisticsDict["VERIFY"][outKey]
304 for test, value in stats.items():
305 rowBase[f"{self.config.stageName}_{test}"] = value
306 rowBase[f"{self.config.stageName}_VERIFY_{test}"] = verify[test]
308 # pack final list
309 rowList.append(rowBase)
311 return rowList, matrixRowList