Coverage for python/lsst/summit/utils/quickLook.py: 16%

97 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-28 14:27 +0000

1# This file is part of summit_utils. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 <https://www.gnu.org/licenses/>. 

21 

22import dataclasses 

23import os 

24 

25import lsst.pex.config as pexConfig 

26import lsst.pipe.base as pipeBase 

27import lsst.pipe.base.connectionTypes as cT 

28from lsst.ip.isr import IsrTask 

29from lsst.ip.isr.isrTask import IsrTaskConnections 

30from lsst.meas.algorithms.installGaussianPsf import InstallGaussianPsfTask 

31from lsst.pipe.tasks.characterizeImage import CharacterizeImageTask 

32from lsst.utils import getPackageDir 

33 

34__all__ = ["QuickLookIsrTask", "QuickLookIsrTaskConfig"] 

35 

36 

37class QuickLookIsrTaskConnections(IsrTaskConnections): 

38 """Copy isrTask's connections, changing prereq min values to zero. 

39 

40 Copy all the connections directly for IsrTask, keeping ccdExposure as 

41 required as non-zero, but changing all the other PrerequisiteInputs' 

42 minimum values to zero. 

43 """ 

44 

45 def __init__(self, *, config=None): 

46 # programatically clone all of the connections from isrTask 

47 # setting minimum values to zero for everything except the ccdExposure 

48 super().__init__(config=IsrTask.ConfigClass()) # need a dummy config, isn't used other than for ctor 

49 for name, connection in self.allConnections.items(): 

50 if hasattr(connection, "minimum"): 

51 setattr( 

52 self, 

53 name, 

54 dataclasses.replace(connection, minimum=(0 if name != "ccdExposure" else 1)), 

55 ) 

56 

57 exposure = cT.Output( # called just "exposure" to mimic isrTask's return struct 

58 name="quickLookExp", 

59 doc="The quickLook output exposure.", 

60 storageClass="ExposureF", 

61 dimensions=("instrument", "exposure", "detector"), 

62 ) 

63 # set like this to make it explicit that the outputExposure 

64 # and the exposure are identical. The only reason there are two is for 

65 # API compatibility. 

66 self.outputExposure = exposure 

67 

68 

69class QuickLookIsrTaskConfig(pipeBase.PipelineTaskConfig, pipelineConnections=QuickLookIsrTaskConnections): 

70 """Configuration parameters for QuickLookIsrTask.""" 

71 

72 doRepairCosmics = pexConfig.Field( 

73 dtype=bool, 

74 doc="Interpolate over cosmic rays?", 

75 default=True, 

76 ) 

77 

78 

79class QuickLookIsrTask(pipeBase.PipelineTask): 

80 """Task automatically performing as much isr as possible. Should never fail 

81 

82 Automatically performs as much isr as is possible, depending on the 

83 calibration products available. All calibration products that can be found 

84 are applied, and if none are found, the image is assembled, the overscan is 

85 subtracted and the assembled image is returned. Optionally, cosmic rays are 

86 interpolated over. 

87 """ 

88 

89 ConfigClass = QuickLookIsrTaskConfig 

90 _DefaultName = "quickLook" 

91 

92 def __init__(self, isrTask=IsrTask, **kwargs): 

93 super().__init__(**kwargs) 

94 # Pass in IsrTask so that we can modify it slightly for unit tests. 

95 # Note that this is not an instance of the IsrTask class, but the class 

96 # itself, which is then instantiated later on, in the run() method, 

97 # with the dynamically generated config. 

98 self.isrTask = IsrTask 

99 

100 def run( 

101 self, 

102 ccdExposure, 

103 *, 

104 camera=None, 

105 bias=None, 

106 dark=None, 

107 flat=None, 

108 fringes=None, 

109 defects=None, 

110 linearizer=None, 

111 crosstalk=None, 

112 bfKernel=None, 

113 newBFKernel=None, 

114 ptc=None, 

115 crosstalkSources=None, 

116 isrBaseConfig=None, 

117 filterTransmission=None, 

118 opticsTransmission=None, 

119 strayLightData=None, 

120 sensorTransmission=None, 

121 atmosphereTransmission=None, 

122 deferredChargeCalib=None, 

123 illumMaskedImage=None, 

124 ): 

125 """Run isr and cosmic ray repair using, doing as much isr as possible. 

126 

127 Retrieves as many calibration products as are available, and runs isr 

128 with those settings enabled, but always returns an assembled image at 

129 a minimum. Then performs cosmic ray repair if configured to. 

130 

131 Parameters 

132 ---------- 

133 ccdExposure : `lsst.afw.image.Exposure` 

134 The raw exposure that is to be run through ISR. The 

135 exposure is modified by this method. 

136 camera : `lsst.afw.cameraGeom.Camera`, optional 

137 The camera geometry for this exposure. Required if 

138 one or more of ``ccdExposure``, ``bias``, ``dark``, or 

139 ``flat`` does not have an associated detector. 

140 bias : `lsst.afw.image.Exposure`, optional 

141 Bias calibration frame. 

142 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 

143 Functor for linearization. 

144 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional 

145 Calibration for crosstalk. 

146 crosstalkSources : `list`, optional 

147 List of possible crosstalk sources. 

148 dark : `lsst.afw.image.Exposure`, optional 

149 Dark calibration frame. 

150 flat : `lsst.afw.image.Exposure`, optional 

151 Flat calibration frame. 

152 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional 

153 Photon transfer curve dataset, with, e.g., gains 

154 and read noise. 

155 bfKernel : `numpy.ndarray`, optional 

156 Brighter-fatter kernel. 

157 bfGains : `dict` of `float`, optional 

158 Gains used to override the detector's nominal gains for the 

159 brighter-fatter correction. A dict keyed by amplifier name for 

160 the detector in question. 

161 defects : `lsst.ip.isr.Defects`, optional 

162 List of defects. 

163 fringes : `afw.image.Exposure`, optional 

164 The fringe correction data. 

165 This input is slightly different than the `fringes` keyword to 

166 `lsst.ip.isr.IsrTask`, since the processing done in that task's 

167 `runQuantum` method is instead done here. 

168 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 

169 A ``TransmissionCurve`` that represents the throughput of the, 

170 optics, to be evaluated in focal-plane coordinates. 

171 filterTransmission : `lsst.afw.image.TransmissionCurve` 

172 A ``TransmissionCurve`` that represents the throughput of the 

173 filter itself, to be evaluated in focal-plane coordinates. 

174 sensorTransmission : `lsst.afw.image.TransmissionCurve` 

175 A ``TransmissionCurve`` that represents the throughput of the 

176 sensor itself, to be evaluated in post-assembly trimmed detector 

177 coordinates. 

178 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 

179 A ``TransmissionCurve`` that represents the throughput of the 

180 atmosphere, assumed to be spatially constant. 

181 detectorNum : `int`, optional 

182 The integer number for the detector to process. 

183 strayLightData : `object`, optional 

184 Opaque object containing calibration information for stray-light 

185 correction. If `None`, no correction will be performed. 

186 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 

187 Illumination correction image. 

188 isrBaseConfig : `lsst.ip.isr.IsrTaskConfig`, optional 

189 An isrTask config to act as the base configuration. Options which 

190 involve applying a calibration product are ignored, but this allows 

191 for the configuration of e.g. the number of overscan columns. 

192 

193 Returns 

194 ------- 

195 result : `lsst.pipe.base.Struct` 

196 Result struct with component: 

197 - ``exposure`` : `afw.image.Exposure` 

198 The ISRed and cosmic-ray-repaired exposure. 

199 """ 

200 if not isrBaseConfig: 

201 isrConfig = IsrTask.ConfigClass() 

202 packageDir = getPackageDir("summit_utils") 

203 isrConfig.load(os.path.join(packageDir, "config", "quickLookIsr.py")) 

204 else: 

205 isrConfig = isrBaseConfig 

206 

207 isrConfig.doBias = False 

208 isrConfig.doDark = False 

209 isrConfig.doFlat = False 

210 isrConfig.doFringe = False 

211 isrConfig.doDefect = False 

212 isrConfig.doLinearize = False 

213 isrConfig.doCrosstalk = False 

214 isrConfig.doBrighterFatter = False 

215 isrConfig.usePtcGains = False 

216 

217 if bias: 

218 isrConfig.doBias = True 

219 self.log.info("Running with bias correction") 

220 

221 if dark: 

222 isrConfig.doDark = True 

223 self.log.info("Running with dark correction") 

224 

225 if flat: 

226 isrConfig.doFlat = True 

227 self.log.info("Running with flat correction") 

228 

229 if fringes: 

230 isrConfig.doFringe = True 

231 self.log.info("Running with fringe correction") 

232 

233 if defects: 

234 isrConfig.doDefect = True 

235 self.log.info("Running with defect correction") 

236 

237 if linearizer: 

238 isrConfig.doLinearize = True 

239 self.log.info("Running with linearity correction") 

240 

241 if crosstalk: 

242 isrConfig.doCrosstalk = True 

243 self.log.info("Running with crosstalk correction") 

244 

245 if newBFKernel is not None: 

246 bfGains = newBFKernel.gain 

247 isrConfig.doBrighterFatter = True 

248 self.log.info("Running with new brighter-fatter correction") 

249 else: 

250 bfGains = None 

251 

252 if bfKernel is not None and bfGains is None: 

253 isrConfig.doBrighterFatter = True 

254 self.log.info("Running with brighter-fatter correction") 

255 

256 if ptc: 

257 isrConfig.usePtcGains = True 

258 self.log.info("Running with ptc correction") 

259 

260 isrConfig.doWrite = False 

261 isrTask = self.isrTask(config=isrConfig) 

262 

263 if fringes: 

264 # Must be run after isrTask is instantiated. 

265 isrTask.fringe.loadFringes( 

266 fringes, 

267 expId=ccdExposure.info.id, 

268 assembler=isrTask.assembleCcd if isrConfig.doAssembleIsrExposures else None, 

269 ) 

270 

271 result = isrTask.run( 

272 ccdExposure, 

273 camera=camera, 

274 bias=bias, 

275 dark=dark, 

276 flat=flat, 

277 fringes=fringes, 

278 defects=defects, 

279 linearizer=linearizer, 

280 crosstalk=crosstalk, 

281 bfKernel=bfKernel, 

282 bfGains=bfGains, 

283 ptc=ptc, 

284 crosstalkSources=crosstalkSources, 

285 filterTransmission=filterTransmission, 

286 opticsTransmission=opticsTransmission, 

287 sensorTransmission=sensorTransmission, 

288 atmosphereTransmission=atmosphereTransmission, 

289 strayLightData=strayLightData, 

290 deferredChargeCalib=deferredChargeCalib, 

291 illumMaskedImage=illumMaskedImage, 

292 ) 

293 

294 postIsr = result.exposure 

295 

296 if self.config.doRepairCosmics: 

297 try: # can fail due to too many CRs detected, and we always want an exposure back 

298 self.log.info("Repairing cosmics...") 

299 if postIsr.getPsf() is None: 

300 installPsfTask = InstallGaussianPsfTask() 

301 installPsfTask.run(postIsr) 

302 

303 # TODO: try adding a reasonably wide Gaussian as a temp PSF 

304 # and then just running repairTask on its own without any 

305 # imChar. It should work, and be faster. 

306 repairConfig = CharacterizeImageTask.ConfigClass() 

307 repairConfig.doMeasurePsf = False 

308 repairConfig.doApCorr = False 

309 repairConfig.doDeblend = False 

310 repairConfig.doWrite = False 

311 repairConfig.repair.cosmicray.nCrPixelMax = 200000 

312 repairTask = CharacterizeImageTask(config=repairConfig) 

313 

314 repairTask.repair.run(postIsr) 

315 except Exception as e: 

316 self.log.warning(f"During CR repair caught: {e}") 

317 

318 # exposure is returned for convenience to mimic isrTask's API. 

319 return pipeBase.Struct(exposure=postIsr, outputExposure=postIsr)