Coverage for python/lsst/ip/isr/overscanAmpConfig.py: 32%

107 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-02 04:05 -0700

1import lsst.pex.config as pexConfig 

2import hashlib 

3 

4from .overscan import SerialOverscanCorrectionTaskConfig, ParallelOverscanCorrectionTaskConfig 

5 

6 

7__all__ = [ 

8 "OverscanAmpConfig", 

9 "OverscanDetectorConfig", 

10 "OverscanCameraConfig", 

11] 

12 

13 

14class OverscanAmpConfig(pexConfig.Config): 

15 """Overscan configurations applicable to a single amplifier.""" 

16 doSerialOverscan = pexConfig.Field( 

17 dtype=bool, 

18 doc="Do serial overscan subtraction?", 

19 default=True, 

20 ) 

21 serialOverscanConfig = pexConfig.ConfigField( 

22 dtype=SerialOverscanCorrectionTaskConfig, 

23 doc="Serial overscan configuration.", 

24 ) 

25 doParallelOverscanCrosstalk = pexConfig.Field( 

26 dtype=bool, 

27 doc="Apply crosstalk correction in parallel overscan region?", 

28 default=True, 

29 ) 

30 doParallelOverscan = pexConfig.Field( 

31 dtype=bool, 

32 doc="Do parallel overscan subtraction?", 

33 default=True, 

34 ) 

35 parallelOverscanConfig = pexConfig.ConfigField( 

36 dtype=ParallelOverscanCorrectionTaskConfig, 

37 doc="Parallel overscan configuration.", 

38 ) 

39 

40 def setDefaults(self): 

41 super().setDefaults() 

42 

43 self.serialOverscanConfig.fitType = "MEDIAN_PER_ROW" 

44 self.serialOverscanConfig.leadingToSkip = 3 

45 self.serialOverscanConfig.trailingToSkip = 3 

46 self.parallelOverscanConfig.fitType = "MEDIAN_PER_ROW" 

47 self.parallelOverscanConfig.leadingToSkip = 3 

48 self.parallelOverscanConfig.trailingToSkip = 3 

49 

50 @property 

51 def _stringForHash(self): 

52 """Turn this config into a simple string for hashing. 

53 

54 Only essential data for tracking is returned. 

55 

56 Returns 

57 ------- 

58 stringForHash : `str` 

59 """ 

60 stringForHash = (f"doSerial={self.doSerialOverscan} " 

61 f"serialFitType={self.serialOverscanConfig.fitType} " 

62 f"doParallelCrosstalk={self.doParallelOverscanCrosstalk} " 

63 f"doParallel={self.doParallelOverscan} " 

64 f"parallelFitType={self.parallelOverscanConfig.fitType}") 

65 return stringForHash 

66 

67 

68class OverscanDetectorConfig(pexConfig.Config): 

69 """Overscan configurations applicable to multiple amplifiers in 

70 a single detector. 

71 """ 

72 ampRules = pexConfig.ConfigDictField( 

73 doc="Amplifier level rules for overscan, keyed by amp name.", 

74 keytype=str, 

75 itemtype=OverscanAmpConfig, 

76 default={}, 

77 ) 

78 defaultAmpConfig = pexConfig.ConfigField( 

79 dtype=OverscanAmpConfig, 

80 doc="Default configuration for amplifiers.", 

81 ) 

82 

83 @property 

84 def doAnySerialOverscan(self): 

85 """Check if any of the amp configs have doSerialOverscan. 

86 

87 Returns 

88 ------- 

89 doAnySerialOverscan : `bool` 

90 """ 

91 if self.defaultAmpConfig.doSerialOverscan: 

92 return True 

93 

94 for _, ampRule in self.ampRules.items(): 

95 if ampRule.doSerialOverscan: 

96 return True 

97 

98 return False 

99 

100 @property 

101 def doAnyParallelOverscan(self): 

102 """Check if any of the amp configs have doParallelOverscan. 

103 

104 Returns 

105 ------- 

106 doAnyParallelOverscan : `bool` 

107 """ 

108 if self.defaultAmpConfig.doParallelOverscan: 

109 return True 

110 

111 for _, ampRule in self.ampRules.items(): 

112 if ampRule.doParallelOverscan: 

113 return True 

114 

115 return False 

116 

117 @property 

118 def doAnyParallelOverscanCrosstalk(self): 

119 """Check if any of the amp configs have doParallelOverscanCrosstalk. 

120 

121 Returns 

122 ------- 

123 doAnyParallelOverscanCrosstalk : `bool` 

124 """ 

125 if self.defaultAmpConfig.doParallelOverscanCrosstalk: 

126 return True 

127 

128 for _, ampRule in self.ampRules.items(): 

129 if ampRule.doParallelOverscanCrosstalk: 

130 return True 

131 

132 return False 

133 

134 def getOverscanAmpConfig(self, amplifier): 

135 """Get the OverscanAmpConfig for a specific amplifier. 

136 

137 Parameters 

138 ---------- 

139 amplifier : `lsst.afw.cameraGeom.Amplifier` 

140 

141 Returns 

142 ------- 

143 overscanAmpConfig : `lsst.ip.isr.overscanAmpConfig.OverscanAmpConfig` 

144 """ 

145 ampKey = amplifier.getName() 

146 

147 if ampKey in self.ampRules.keys(): 

148 overscanAmpConfig = self.ampRules[ampKey] 

149 else: 

150 overscanAmpConfig = self.defaultAmpConfig 

151 

152 return overscanAmpConfig 

153 

154 @property 

155 def _stringForHash(self): 

156 """Turn this config into a simple string for hashing. 

157 

158 Only the default and amps that are different than the 

159 default are used in the string representation. 

160 

161 Returns 

162 ------- 

163 stringForHash : `str` 

164 """ 

165 defaultString = self.defaultAmpConfig._stringForHash 

166 

167 stringForHash = f"default: {defaultString}" 

168 for ampName in self.ampRules: 

169 ampString = self.ampRules[ampName]._stringForHash 

170 if ampString != defaultString: 

171 stringForHash += f" {ampName}: {ampString}" 

172 

173 return stringForHash 

174 

175 @property 

176 def md5(self): 

177 """Compute the MD5 hash of this config (detector + amps). 

178 

179 This can be used to ensure overscan configs are consistent. 

180 

181 Returns 

182 ------- 

183 md5Hash : `str` 

184 """ 

185 return hashlib.md5(self._stringForHash.encode("UTF-8")).hexdigest() 

186 

187 

188class OverscanCameraConfig(pexConfig.Config): 

189 """Overscan configurations applicable to multiple detectors in 

190 a single camera. 

191 """ 

192 detectorRules = pexConfig.ConfigDictField( 

193 doc="Detector level rules for overscan", 

194 keytype=str, 

195 itemtype=OverscanDetectorConfig, 

196 default={}, 

197 ) 

198 defaultDetectorConfig = pexConfig.ConfigField( 

199 dtype=OverscanDetectorConfig, 

200 doc="Default configuration for detectors.", 

201 ) 

202 detectorRuleKeyType = pexConfig.ChoiceField( 

203 doc="Detector rule key type.", 

204 dtype=str, 

205 default="NAME", 

206 allowed={ 

207 "NAME": "DetectorRules has a key that is the detector name.", 

208 "SERIAL": "DetectorRules has a key that is the detector serial number.", 

209 "ID": "DetectorRules has a key that is the detector id number.", 

210 }, 

211 ) 

212 

213 @property 

214 def doAnySerialOverscan(self): 

215 """Check if any of the detector/amp configs have doSerialOverscan. 

216 

217 Returns 

218 ------- 

219 doAnySerialOverscan : `bool` 

220 """ 

221 if self.defaultDetectorConfig.doAnySerialOverscan: 

222 return True 

223 

224 for _, detectorRule in self.detectorRules.items(): 

225 if detectorRule.doAnySerialOverscan: 

226 return True 

227 

228 return False 

229 

230 @property 

231 def doAnyParallelOverscan(self): 

232 """Check if any of the detector/amp configs have 

233 doParallelOverscan. 

234 

235 Returns 

236 ------- 

237 doAnyParallelOverscan : `bool` 

238 """ 

239 

240 if self.defaultDetectorConfig.doAnyParallelOverscan: 

241 return True 

242 

243 for _, detectorRule in self.detectorRules.items(): 

244 if detectorRule.doAnyParallelOverscan: 

245 return True 

246 

247 return False 

248 

249 @property 

250 def doAnyParallelOverscanCrosstalk(self): 

251 """Check if any of the detector/amp configs have 

252 doParallelOverscanCrosstalk. 

253 

254 Returns 

255 ------- 

256 doAnyParallelOverscanCrosstalk : `bool` 

257 """ 

258 

259 if self.defaultDetectorConfig.doAnyParallelOverscanCrosstalk: 

260 return True 

261 

262 for _, detectorRule in self.detectorRules.items(): 

263 if detectorRule.doAnyParallelOverscanCrosstalk: 

264 return True 

265 

266 return False 

267 

268 def getOverscanDetectorConfig(self, detector): 

269 """Get the OverscanDetectorConfig for a specific detector. 

270 

271 Parameters 

272 ---------- 

273 detector : `lsst.afw.cameraGeom.Detector` 

274 

275 Returns 

276 ------- 

277 overscanDetectorConfig : `OverscanDetectorConfig` 

278 """ 

279 match self.detectorRuleKeyType: 

280 case "NAME": 

281 key = detector.getName() 

282 case "SERIAL": 

283 key = detector.getSerial() 

284 case "ID": 

285 key = str(detector.getId()) 

286 

287 if key in self.detectorRules.keys(): 

288 overscanDetectorConfig = self.detectorRules[key] 

289 else: 

290 overscanDetectorConfig = self.defaultDetectorConfig 

291 

292 return overscanDetectorConfig