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

97 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-09 14:32 +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 

24from lsst.ip.isr import IsrTask 

25from lsst.ip.isr.isrTask import IsrTaskConnections 

26import lsst.pipe.base.connectionTypes as cT 

27from lsst.utils import getPackageDir 

28 

29import lsst.pex.config as pexConfig 

30import lsst.pipe.base as pipeBase 

31from lsst.pipe.tasks.characterizeImage import CharacterizeImageTask 

32from lsst.meas.algorithms.installGaussianPsf import InstallGaussianPsfTask 

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 def __init__(self, *, config=None): 

45 # programatically clone all of the connections from isrTask 

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

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

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

49 if hasattr(connection, 'minimum'): 

50 setattr( 

51 self, 

52 name, 

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

54 ) 

55 

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

57 name="quickLookExp", 

58 doc="The quickLook output exposure.", 

59 storageClass="ExposureF", 

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

61 ) 

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

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

64 # API compatibility. 

65 self.outputExposure = exposure 

66 

67 

68class QuickLookIsrTaskConfig(pipeBase.PipelineTaskConfig, 

69 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(self, ccdExposure, *, 

101 camera=None, 

102 bias=None, 

103 dark=None, 

104 flat=None, 

105 fringes=None, 

106 defects=None, 

107 linearizer=None, 

108 crosstalk=None, 

109 bfKernel=None, 

110 newBFKernel=None, 

111 ptc=None, 

112 crosstalkSources=None, 

113 isrBaseConfig=None, 

114 filterTransmission=None, 

115 opticsTransmission=None, 

116 strayLightData=None, 

117 sensorTransmission=None, 

118 atmosphereTransmission=None, 

119 deferredChargeCalib=None, 

120 illumMaskedImage=None, 

121 ): 

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

123 

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

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

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

127 

128 Parameters 

129 ---------- 

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

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

132 exposure is modified by this method. 

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

134 The camera geometry for this exposure. Required if 

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

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

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

138 Bias calibration frame. 

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

140 Functor for linearization. 

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

142 Calibration for crosstalk. 

143 crosstalkSources : `list`, optional 

144 List of possible crosstalk sources. 

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

146 Dark calibration frame. 

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

148 Flat calibration frame. 

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

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

151 and read noise. 

152 bfKernel : `numpy.ndarray`, optional 

153 Brighter-fatter kernel. 

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

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

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

157 the detector in question. 

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

159 List of defects. 

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

161 The fringe correction data. 

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

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

164 `runQuantum` method is instead done here. 

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

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

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

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

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

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

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

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

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

174 coordinates. 

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

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

177 atmosphere, assumed to be spatially constant. 

178 detectorNum : `int`, optional 

179 The integer number for the detector to process. 

180 strayLightData : `object`, optional 

181 Opaque object containing calibration information for stray-light 

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

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

184 Illumination correction image. 

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

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

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

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

189 

190 Returns 

191 ------- 

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

193 Result struct with component: 

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

195 The ISRed and cosmic-ray-repaired exposure. 

196 """ 

197 if not isrBaseConfig: 

198 isrConfig = IsrTask.ConfigClass() 

199 packageDir = getPackageDir("summit_utils") 

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

201 else: 

202 isrConfig = isrBaseConfig 

203 

204 isrConfig.doBias = False 

205 isrConfig.doDark = False 

206 isrConfig.doFlat = False 

207 isrConfig.doFringe = False 

208 isrConfig.doDefect = False 

209 isrConfig.doLinearize = False 

210 isrConfig.doCrosstalk = False 

211 isrConfig.doBrighterFatter = False 

212 isrConfig.usePtcGains = False 

213 

214 if bias: 

215 isrConfig.doBias = True 

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

217 

218 if dark: 

219 isrConfig.doDark = True 

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

221 

222 if flat: 

223 isrConfig.doFlat = True 

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

225 

226 if fringes: 

227 isrConfig.doFringe = True 

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

229 

230 if defects: 

231 isrConfig.doDefect = True 

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

233 

234 if linearizer: 

235 isrConfig.doLinearize = True 

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

237 

238 if crosstalk: 

239 isrConfig.doCrosstalk = True 

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

241 

242 if newBFKernel is not None: 

243 bfGains = newBFKernel.gain 

244 isrConfig.doBrighterFatter = True 

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

246 else: 

247 bfGains = None 

248 

249 if bfKernel is not None and bfGains is None: 

250 isrConfig.doBrighterFatter = True 

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

252 

253 if ptc: 

254 isrConfig.usePtcGains = True 

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

256 

257 isrConfig.doWrite = False 

258 isrTask = self.isrTask(config=isrConfig) 

259 

260 if fringes: 

261 # Must be run after isrTask is instantiated. 

262 isrTask.fringe.loadFringes(fringes, 

263 expId=ccdExposure.info.id, 

264 assembler=isrTask.assembleCcd 

265 if isrConfig.doAssembleIsrExposures else None) 

266 

267 result = isrTask.run(ccdExposure, 

268 camera=camera, 

269 bias=bias, 

270 dark=dark, 

271 flat=flat, 

272 fringes=fringes, 

273 defects=defects, 

274 linearizer=linearizer, 

275 crosstalk=crosstalk, 

276 bfKernel=bfKernel, 

277 bfGains=bfGains, 

278 ptc=ptc, 

279 crosstalkSources=crosstalkSources, 

280 filterTransmission=filterTransmission, 

281 opticsTransmission=opticsTransmission, 

282 sensorTransmission=sensorTransmission, 

283 atmosphereTransmission=atmosphereTransmission, 

284 strayLightData=strayLightData, 

285 deferredChargeCalib=deferredChargeCalib, 

286 illumMaskedImage=illumMaskedImage, 

287 ) 

288 

289 postIsr = result.exposure 

290 

291 if self.config.doRepairCosmics: 

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

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

294 if postIsr.getPsf() is None: 

295 installPsfTask = InstallGaussianPsfTask() 

296 installPsfTask.run(postIsr) 

297 

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

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

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

301 repairConfig = CharacterizeImageTask.ConfigClass() 

302 repairConfig.doMeasurePsf = False 

303 repairConfig.doApCorr = False 

304 repairConfig.doDeblend = False 

305 repairConfig.doWrite = False 

306 repairConfig.repair.cosmicray.nCrPixelMax = 200000 

307 repairTask = CharacterizeImageTask(config=repairConfig) 

308 

309 repairTask.repair.run(postIsr) 

310 except Exception as e: 

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

312 

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

314 return pipeBase.Struct(exposure=postIsr, 

315 outputExposure=postIsr)