Coverage for python / lsst / cp / verify / verifyGain.py: 18%
75 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:33 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:33 +0000
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.pex.config as pexConfig
24import lsst.pipe.base.connectionTypes as cT
25from .verifyCalib import CpVerifyCalibConfig, CpVerifyCalibTask, CpVerifyCalibConnections
27__all__ = ['CpVerifyGainConnections', 'CpVerifyGainConfig', 'CpVerifyGainTask']
30class CpVerifyGainConnections(CpVerifyCalibConnections,
31 dimensions={"instrument", "detector"},
32 defaultTemplates={}):
33 exposure = cT.Input(
34 name="raw",
35 doc="Exposure ID of first flat from flat pair.",
36 storageClass='Exposure',
37 dimensions=("instrument", "detector", "exposure"),
38 multiple=True,
39 deferLoad=True,
40 )
42 inputCalib = cT.Input(
43 name="calib",
44 doc="Input calib to calculate statistics for.",
45 storageClass="PhotonTransferCurveDataset",
46 dimensions=("instrument", "detector", "exposure"),
47 isCalibration=True
48 )
51class CpVerifyGainConfig(CpVerifyCalibConfig,
52 pipelineConnections=CpVerifyGainConnections):
53 """Inherits from base CpVerifyCalibConfig."""
55 gainThreshold = pexConfig.Field(
56 dtype=float,
57 doc="Maximum percentage difference between gain from flat pairs and nominal amplifier gain.",
58 default=5.0,
59 )
61 noiseThreshold = pexConfig.Field(
62 dtype=float,
63 doc="Maximum percentage difference between overscan readout noise and nominal "
64 "amplifier readout noise.",
65 default=5.0,
66 )
68 def setDefaults(self):
69 super().setDefaults()
70 self.stageName = 'GAIN'
73class CpVerifyGainTask(CpVerifyCalibTask):
74 """Gain from flat pairs verification sub-class. Implements verify method.
75 """
76 ConfigClass = CpVerifyGainConfig
77 _DefaultName = 'cpVerifyGain'
79 def detectorStatistics(self, inputCalib, camera=None, exposure=None):
80 """Calculate detector level statistics from the calibration.
82 Parameters
83 ----------
84 inputCalib : `lsst.ip.isr.IsrCalib`
85 The calibration to verify.
86 camera : `lsst.afw.cameraGeom.Camera`, optional
87 Input camera to get detectors from.
88 exposure : `lsst.afw.image.exposure.ExposureF`, optional
89 First flat-field image from pair of flats used to
90 estimate the gain.
92 Returns
93 -------
94 outputStatistics : `dict` [`str`, scalar]
95 A dictionary of the statistics measured and their values.
96 """
97 return {}
99 def amplifierStatistics(self, inputCalib, camera=None, exposure=None):
100 """Calculate detector level statistics from the calibration.
102 Parameters
103 ----------
104 inputCalib : `lsst.ip.isr.IsrCalib`
105 The calibration to verify.
106 camera : `lsst.afw.cameraGeom.Camera`, optional
107 Input camera to get detectors from.
108 exposure : `lsst.afw.image.exposure.ExposureF`, optional
109 First flat-field image from pair of flats used to
110 estimate the gain.
112 Returns
113 -------
114 outputStatistics : `dict` [`str`, scalar]
115 A dictionary of the statistics measured and their values.
116 """
117 calibMetadata = inputCalib.getMetadata().toDict()
118 detId = calibMetadata['DETECTOR']
119 detector = camera[detId]
120 # 'DET_SER' is of the form 'ITL-3800C-229'
121 detVendor = calibMetadata['DET_SER'].split('-')[0]
122 # Adjust gain estimated from flat pair for flux bias, see DM-35790
123 if detVendor == 'ITL':
124 slope = 0.00027 # %/ADU
125 elif detVendor == 'e2V':
126 slope = 0.00046 # %/ADU
127 outputStatistics = {amp.getName(): {} for amp in detector}
129 for amp in detector:
130 ampName = amp.getName()
131 # Flux correction to gain-per-flat-pair method, see DM-35790
132 correction = 1. - slope*inputCalib.rawMeans[ampName][0]/100
133 outputStatistics[ampName]['MEAN_FLUX_FLAT_PAIR'] = inputCalib.rawMeans[ampName][0]
134 outputStatistics[ampName]['GAIN_FROM_FLAT_PAIR'] = inputCalib.gain[ampName]
135 outputStatistics[ampName]['GAIN_CORRECTION_FACTOR'] = correction
136 outputStatistics[ampName]['AMP_GAIN'] = amp.getGain()
137 outputStatistics[ampName]['ISR_NOISE'] = inputCalib.noise[ampName]
138 outputStatistics[ampName]['AMP_NOISE'] = amp.getReadNoise()
140 return outputStatistics
142 def verify(self, calib, statisticsDict, camera=None, exposure=None):
143 """Verify that the calibration meets the verification criteria.
145 Parameters
146 ----------
147 inputCalib : `lsst.ip.isr.IsrCalib`
148 The calibration to verify.
149 statisticsDictionary : `dict` [`str`, `dict` [`str`, scalar]],
150 Dictionary of measured statistics. The inner dictionary
151 should have keys that are statistic names (`str`) with
152 values that are some sort of scalar (`int` or `float` are
153 the mostly likely types).
154 camera : `lsst.afw.cameraGeom.Camera`, optional
155 Input camera to get detectors from.
156 exposure : `lsst.afw.image.exposure.ExposureF`, optional
157 First flat-field image from pair of flats used to
158 estimate the gain.
160 Returns
161 -------
162 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]]
163 A dictionary indexed by the amplifier name, containing
164 dictionaries of the verification criteria.
165 success : `bool`
166 A boolean indicating whether all tests have passed.
167 """
168 verifyStats = {}
169 success = True
170 calibMetadata = calib.getMetadata().toDict()
171 detId = calibMetadata['DETECTOR']
172 detector = camera[detId]
173 # 'DET_SER' is of the form 'ITL-3800C-229'
174 detVendor = calibMetadata['DET_SER'].split('-')[0]
175 # Adjust gain estimated from flat pair for flux bias, see DM-35790
176 if detVendor == 'ITL':
177 slope = 0.00027 # %/ADU
178 elif detVendor == 'e2V':
179 slope = 0.00046 # %/ADU
181 for amp in detector:
182 verify = {}
183 ampName = amp.getName()
185 # Gain from a pair of flats and noise from overscan after ISR.
186 # See DM-35790.
187 correction = 1. - slope*np.array(calib.rawMeans[ampName][0])/100
188 gain = correction*calib.gain[ampName]
190 diffGain = (np.abs(gain - amp.getGain()) / amp.getGain())*100
191 diffNoise = (np.abs(calib.noise[ampName] - amp.getReadNoise()) / amp.getReadNoise())*100
193 # DMTN-101: 6.1 and 6.2
194 # Estimate gain from a pair of flats and compare it with the value
195 # in the amplifiers.
196 verify['GAIN_FROM_FLAT_PAIR'] = bool(diffGain < self.config.gainThreshold)
197 # Check the empirical noise (as oppossed to fitted noise
198 # from the PTC) calculated from the overscan after ISR.
199 verify['ISR_NOISE'] = bool(diffNoise < self.config.noiseThreshold)
201 # Overall success among all tests for this amp.
202 verify['SUCCESS'] = bool(np.all(list(verify.values())))
203 if verify['SUCCESS'] is False:
204 success = False
206 verifyStats[ampName] = verify
208 # Loop over amps to make a detector summary.
209 verifyDetStats = {'GAIN_FROM_FLAT_PAIR': [], 'ISR_NOISE': []}
210 for amp in verifyStats:
211 for testName in verifyStats[amp]:
212 if testName == 'SUCCESS':
213 continue
214 verifyDetStats[testName].append(verifyStats[amp][testName])
216 # VerifyDetStatsFinal has final boolean test over all amps
217 verifyDetStatsFinal = {}
218 for testName in verifyDetStats:
219 testBool = bool(np.all(list(verifyDetStats[testName])))
220 # Save the tests that failed
221 if not testBool:
222 verifyDetStatsFinal[testName] = bool(np.all(list(verifyDetStats[testName])))
224 return verifyDetStatsFinal, bool(success)