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

86 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-22 12:13 +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, **kwargs): 

93 super().__init__(**kwargs) 

94 

95 def run(self, ccdExposure, *, 

96 camera=None, 

97 bias=None, 

98 dark=None, 

99 flat=None, 

100 defects=None, 

101 linearizer=None, 

102 crosstalk=None, 

103 bfKernel=None, 

104 bfGains=None, 

105 ptc=None, 

106 crosstalkSources=None, 

107 isrBaseConfig=None 

108 ): 

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

110 

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

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

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

114 

115 Parameters 

116 ---------- 

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

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

119 exposure is modified by this method. 

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

121 The camera geometry for this exposure. Required if 

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

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

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

125 Bias calibration frame. 

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

127 Functor for linearization. 

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

129 Calibration for crosstalk. 

130 crosstalkSources : `list`, optional 

131 List of possible crosstalk sources. 

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

133 Dark calibration frame. 

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

135 Flat calibration frame. 

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

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

138 and read noise. 

139 bfKernel : `numpy.ndarray`, optional 

140 Brighter-fatter kernel. 

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

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

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

144 the detector in question. 

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

146 List of defects. 

147 fringes : `lsst.pipe.base.Struct`, optional 

148 Struct containing the fringe correction data, with 

149 elements: 

150 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 

151 - ``seed``: random seed derived from the ccdExposureId for random 

152 number generator (`uint32`) 

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

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

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

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

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

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

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

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

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

162 coordinates. 

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

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

165 atmosphere, assumed to be spatially constant. 

166 detectorNum : `int`, optional 

167 The integer number for the detector to process. 

168 strayLightData : `object`, optional 

169 Opaque object containing calibration information for stray-light 

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

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

172 Illumination correction image. 

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

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

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

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

177 

178 Returns 

179 ------- 

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

181 Result struct with component: 

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

183 The ISRed and cosmic-ray-repaired exposure. 

184 """ 

185 if not isrBaseConfig: 

186 isrConfig = IsrTask.ConfigClass() 

187 packageDir = getPackageDir("summit_utils") 

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

189 else: 

190 isrConfig = isrBaseConfig 

191 

192 isrConfig.doBias = False 

193 isrConfig.doDark = False 

194 isrConfig.doFlat = False 

195 isrConfig.doFringe = False 

196 isrConfig.doDefect = False 

197 isrConfig.doLinearize = False 

198 isrConfig.doCrosstalk = False 

199 isrConfig.doBrighterFatter = False 

200 isrConfig.usePtcGains = False 

201 

202 if bias: 

203 isrConfig.doBias = True 

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

205 

206 if dark: 

207 isrConfig.doDark = True 

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

209 

210 if flat: 

211 isrConfig.doFlat = True 

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

213 

214 # TODO: deal with fringes here 

215 if defects: 

216 isrConfig.doDefect = True 

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

218 

219 if linearizer: 

220 isrConfig.doLinearize = True 

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

222 

223 if crosstalk: 

224 isrConfig.doCrosstalk = True 

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

226 

227 if bfKernel: 

228 isrConfig.doBrighterFatter = True 

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

230 

231 if ptc: 

232 isrConfig.usePtcGains = True 

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

234 

235 isrConfig.doWrite = False 

236 isrTask = IsrTask(config=isrConfig) 

237 result = isrTask.run(ccdExposure, 

238 camera=camera, 

239 bias=bias, 

240 dark=dark, 

241 flat=flat, 

242 # fringes=pipeBase.Struct(fringes=None), 

243 defects=defects, 

244 linearizer=linearizer, 

245 crosstalk=crosstalk, 

246 bfKernel=bfKernel, 

247 bfGains=bfGains, 

248 ptc=ptc, 

249 crosstalkSources=crosstalkSources,) 

250 

251 postIsr = result.exposure 

252 

253 if self.config.doRepairCosmics: 

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

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

256 if postIsr.getPsf() is None: 

257 installPsfTask = InstallGaussianPsfTask() 

258 installPsfTask.run(postIsr) 

259 

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

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

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

263 repairConfig = CharacterizeImageTask.ConfigClass() 

264 repairConfig.doMeasurePsf = False 

265 repairConfig.doApCorr = False 

266 repairConfig.doDeblend = False 

267 repairConfig.doWrite = False 

268 repairConfig.repair.cosmicray.nCrPixelMax = 200000 

269 repairTask = CharacterizeImageTask(config=repairConfig) 

270 

271 repairTask.repair.run(postIsr) 

272 except Exception as e: 

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

274 

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

276 return pipeBase.Struct(exposure=postIsr, 

277 outputExposure=postIsr)