Coverage for tests / test_overscanAmpConfig.py: 12%

148 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-06 08:42 +0000

1# 

2# LSST Data Management System 

3# Copyright 2023 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23import copy 

24import math 

25import tempfile 

26import unittest 

27 

28import lsst.utils.tests 

29import lsst.afw.cameraGeom as cameraGeom 

30 

31from lsst.ip.isr.overscanAmpConfig import ( 

32 OverscanAmpConfig, 

33 OverscanDetectorConfig, 

34 OverscanCameraConfig, 

35) 

36 

37 

38class OverscanAmpConfigTestCase(lsst.utils.tests.TestCase): 

39 def _makeCamera(self): 

40 self.detectors = { 

41 "detector0": (0, "0010"), 

42 "detector1": (1, "0011"), 

43 "detector2": (2, "0012"), 

44 } 

45 self.amps = ["amp0", "amp1", "amp2", "amp3"] 

46 

47 cameraBuilder = cameraGeom.Camera.Builder("Fake Camera") 

48 for detector in self.detectors: 

49 detectorBuilder = cameraBuilder.add(detector, self.detectors[detector][0]) 

50 detectorBuilder.setSerial(self.detectors[detector][1]) 

51 

52 for amp in self.amps: 

53 ampBuilder = cameraGeom.Amplifier.Builder() 

54 ampBuilder.setName(amp) 

55 detectorBuilder.append(ampBuilder) 

56 

57 return cameraBuilder.finish() 

58 

59 def _serializeAndReadConfig(self, configIn): 

60 # This bit of code serializes and reads the config 

61 # to ensure that everything here works as expected 

62 # with the nested dictionaries of configs. 

63 

64 with tempfile.NamedTemporaryFile(suffix=".py") as f: 

65 configIn.save(f.name) 

66 configOut = OverscanCameraConfig() 

67 configOut.load(f.name) 

68 

69 return configOut 

70 

71 def _checkOverscanConfig( 

72 self, 

73 overscanAmpConfig, 

74 doSerialOverscan=True, 

75 doParallelOverscan=True, 

76 serialFitType="MEDIAN_PER_ROW", 

77 parallelFitType="MEDIAN_PER_ROW", 

78 saturation=float("NaN"), 

79 gain=float("NaN"), 

80 suspectLevel=float("NaN"), 

81 ): 

82 self.assertEqual(overscanAmpConfig.doSerialOverscan, doSerialOverscan) 

83 self.assertEqual(overscanAmpConfig.doParallelOverscan, doParallelOverscan) 

84 self.assertEqual(overscanAmpConfig.serialOverscanConfig.fitType, serialFitType) 

85 self.assertEqual(overscanAmpConfig.parallelOverscanConfig.fitType, parallelFitType) 

86 if math.isnan(saturation): 

87 self.assertTrue(math.isnan(overscanAmpConfig.saturation)) 

88 else: 

89 self.assertEqual(overscanAmpConfig.saturation, saturation) 

90 if math.isnan(gain): 

91 self.assertTrue(math.isnan(overscanAmpConfig.gain)) 

92 else: 

93 self.assertEqual(overscanAmpConfig.gain, gain) 

94 if math.isnan(suspectLevel): 

95 self.assertTrue(math.isnan(overscanAmpConfig.suspectLevel)) 

96 else: 

97 self.assertEqual(overscanAmpConfig.suspectLevel, suspectLevel) 

98 

99 def _checkAnyOverscanConfig( 

100 self, 

101 config, 

102 doSerialOverscan=True, 

103 doParallelOverscan=True, 

104 ): 

105 self.assertEqual(config.doAnySerialOverscan, doSerialOverscan) 

106 self.assertEqual(config.doAnyParallelOverscan, doParallelOverscan) 

107 

108 def _checkDetectorOverscanConfig( 

109 self, 

110 overscanDetectorConfig, 

111 integerDitherMode="SYMMETRIC", 

112 ): 

113 self.assertEqual(overscanDetectorConfig.integerDitherMode, integerDitherMode) 

114 

115 def testAmpConfigNoOverrides(self): 

116 camera = self._makeCamera() 

117 

118 config = OverscanCameraConfig() 

119 

120 config = self._serializeAndReadConfig(config) 

121 

122 for detector in camera: 

123 for amp in detector: 

124 detectorConfig = config.getOverscanDetectorConfig(detector) 

125 ampConfig = detectorConfig.getOverscanAmpConfig(amp) 

126 self._checkOverscanConfig(ampConfig) 

127 

128 self._checkAnyOverscanConfig(config) 

129 

130 def testAmpConfigOverrideDetectorDefault(self): 

131 camera = self._makeCamera() 

132 

133 overscanAmpConfig = OverscanAmpConfig( 

134 doSerialOverscan=False, 

135 doParallelOverscan=False, 

136 saturation=100_000.0, 

137 gain=1.7, 

138 suspectLevel=90_000.0, 

139 ) 

140 

141 overscanDetectorConfig = OverscanDetectorConfig(defaultAmpConfig=overscanAmpConfig) 

142 

143 config = OverscanCameraConfig(defaultDetectorConfig=overscanDetectorConfig) 

144 

145 config = self._serializeAndReadConfig(config) 

146 

147 for detector in camera: 

148 for amp in detector: 

149 detectorConfig = config.getOverscanDetectorConfig(detector) 

150 overscanAmpConfig = detectorConfig.getOverscanAmpConfig(amp) 

151 self._checkOverscanConfig( 

152 overscanAmpConfig, 

153 doSerialOverscan=False, 

154 doParallelOverscan=False, 

155 saturation=100_000.0, 

156 gain=1.7, 

157 suspectLevel=90_000.0, 

158 ) 

159 

160 self._checkAnyOverscanConfig( 

161 config, 

162 doSerialOverscan=False, 

163 doParallelOverscan=False, 

164 ) 

165 

166 def testAmpConfigOverrideDetectorDefaultWithOneAmp(self): 

167 camera = self._makeCamera() 

168 

169 overscanAmpConfigOverride = OverscanAmpConfig() 

170 overscanAmpConfigOverride.parallelOverscanConfig.fitType = "MEDIAN" 

171 

172 overscanDetectorConfig = OverscanDetectorConfig() 

173 overscanDetectorConfig.ampRules["amp2"] = overscanAmpConfigOverride 

174 

175 config = OverscanCameraConfig(defaultDetectorConfig=overscanDetectorConfig) 

176 

177 config = self._serializeAndReadConfig(config) 

178 

179 for detector in camera: 

180 for amp in detector: 

181 detectorConfig = config.getOverscanDetectorConfig(detector) 

182 ampConfig = detectorConfig.getOverscanAmpConfig(amp) 

183 if amp.getName() == "amp2": 

184 self._checkOverscanConfig(ampConfig, parallelFitType="MEDIAN") 

185 else: 

186 self._checkOverscanConfig(ampConfig) 

187 

188 self._checkAnyOverscanConfig(config) 

189 

190 def testAmpConfigOverrideOneDetector(self): 

191 camera = self._makeCamera() 

192 

193 overscanAmpConfigOverride = OverscanAmpConfig(doParallelOverscan=False) 

194 overscanDetectorConfigOverride = OverscanDetectorConfig( 

195 defaultAmpConfig=overscanAmpConfigOverride, 

196 integerDitherMode="NONE", 

197 ) 

198 

199 for keyType in ["NAME", "SERIAL", "ID"]: 

200 config = OverscanCameraConfig() 

201 

202 match keyType: 

203 case "NAME": 

204 key = camera[1].getName() 

205 case "SERIAL": 

206 key = camera[1].getSerial() 

207 case "ID": 

208 key = str(camera[1].getId()) 

209 

210 config.detectorRuleKeyType = keyType 

211 config.detectorRules[key] = overscanDetectorConfigOverride 

212 

213 config = self._serializeAndReadConfig(config) 

214 

215 for detector in camera: 

216 detectorConfig = config.getOverscanDetectorConfig(detector) 

217 if detector.getName() == camera[1].getName(): 

218 self._checkDetectorOverscanConfig(detectorConfig, integerDitherMode="NONE") 

219 else: 

220 self._checkDetectorOverscanConfig(detectorConfig) 

221 

222 for amp in detector: 

223 ampConfig = detectorConfig.getOverscanAmpConfig(amp) 

224 if detector.getName() == camera[1].getName(): 

225 self._checkOverscanConfig(ampConfig, doParallelOverscan=False) 

226 else: 

227 self._checkOverscanConfig(ampConfig) 

228 

229 self._checkAnyOverscanConfig(config) 

230 

231 def testAmpConfigOverrideOneDetectorOneAmp(self): 

232 camera = self._makeCamera() 

233 

234 overscanAmpConfigOverride = OverscanAmpConfig() 

235 overscanAmpConfigOverride.serialOverscanConfig.fitType = "MEDIAN" 

236 overscanDetectorConfigOverride = OverscanDetectorConfig() 

237 overscanDetectorConfigOverride.ampRules["amp3"] = overscanAmpConfigOverride 

238 

239 config = OverscanCameraConfig() 

240 config.detectorRules["detector0"] = overscanDetectorConfigOverride 

241 

242 config = self._serializeAndReadConfig(config) 

243 

244 for detector in camera: 

245 for amp in detector: 

246 detectorConfig = config.getOverscanDetectorConfig(detector) 

247 ampConfig = detectorConfig.getOverscanAmpConfig(amp) 

248 if detector.getName() == "detector0" and amp.getName() == "amp3": 

249 self._checkOverscanConfig(ampConfig, serialFitType="MEDIAN") 

250 else: 

251 self._checkOverscanConfig(ampConfig) 

252 

253 self._checkAnyOverscanConfig(config) 

254 

255 def testAmpConfigMd5(self): 

256 # Check a default detectorConfig 

257 detectorConfig1 = OverscanDetectorConfig() 

258 configMd51 = detectorConfig1.md5 

259 

260 # Make sure copying it has the same hash. 

261 detectorConfig2 = copy.copy(detectorConfig1) 

262 configMd52 = detectorConfig2.md5 

263 

264 self.assertEqual(configMd51, configMd52) 

265 

266 # Make a new one with an amp override. 

267 overscanAmpConfigOverride = OverscanAmpConfig() 

268 overscanAmpConfigOverride.parallelOverscanConfig.fitType = "MEDIAN" 

269 

270 detectorConfig3 = OverscanDetectorConfig() 

271 detectorConfig3.ampRules["amp2"] = overscanAmpConfigOverride 

272 

273 self.assertNotEqual(detectorConfig3.md5, detectorConfig1.md5) 

274 

275 # Override another amp but with the default. This should 

276 # give the same answer because amp overrides that match the default 

277 # are not hashed. 

278 detectorConfig4 = copy.copy(detectorConfig3) 

279 detectorConfig4.ampRules["amp3"] = OverscanAmpConfig() 

280 

281 self.assertEqual(detectorConfig4.md5, detectorConfig3.md5) 

282 

283 

284class MemoryTester(lsst.utils.tests.MemoryTestCase): 

285 pass 

286 

287 

288def setup_module(module): 

289 lsst.utils.tests.init() 

290 

291 

292if __name__ == "__main__": 292 ↛ 293line 292 didn't jump to line 293 because the condition on line 292 was never true

293 lsst.utils.tests.init() 

294 unittest.main()