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

96 statements  

« prev     ^ index     » next       coverage.py v7.2.6, created at 2023-05-24 03:00 -0700

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 os 

23from lsst.ip.isr import IsrTask 

24from lsst.ip.isr.isrTask import IsrTaskConnections 

25import lsst.pipe.base.connectionTypes as cT 

26from lsst.utils import getPackageDir 

27 

28import lsst.pex.config as pexConfig 

29import lsst.pipe.base as pipeBase 

30from lsst.pipe.tasks.characterizeImage import CharacterizeImageTask 

31from lsst.meas.algorithms.installGaussianPsf import InstallGaussianPsfTask 

32 

33__all__ = ['QuickLookIsrTask', 'QuickLookIsrTaskConfig'] 

34 

35 

36def _getArgs(connection): 

37 """Get the all required args from a connection in order to reconstruct it. 

38 """ 

39 newArgs = {} 

40 for attr in ("name", "storageClass", "multiple", "doc", "dimensions", "isCalibration", "deferLoad", 

41 "minimum", "lookupFunction"): 

42 if hasattr(connection, attr): 

43 newArgs[attr] = getattr(connection, attr) 

44 return newArgs 

45 

46 

47class QuickLookIsrTaskConnections(IsrTaskConnections): 

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

49 

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

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

52 minimum values to zero. 

53 """ 

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

55 # programatically clone all of the connections from isrTask 

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

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

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

59 if hasattr(connection, 'minimum'): 

60 args = _getArgs(connection) 

61 if name != "ccdExposure": # need one input image always 

62 args['minimum'] = 0 

63 newConnection = type(connection)(**args) 

64 self.allConnections[name] = newConnection 

65 setattr(self, name, newConnection) 

66 

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

68 name="quickLookExp", 

69 doc="The quickLook output exposure.", 

70 storageClass="ExposureF", 

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

72 ) 

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

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

75 # API compatibility. 

76 self.outputExposure = exposure 

77 

78 

79class QuickLookIsrTaskConfig(pipeBase.PipelineTaskConfig, 

80 pipelineConnections=QuickLookIsrTaskConnections): 

81 """Configuration parameters for QuickLookIsrTask.""" 

82 

83 doRepairCosmics = pexConfig.Field( 

84 dtype=bool, 

85 doc="Interpolate over cosmic rays?", 

86 default=True, 

87 ) 

88 

89 

90class QuickLookIsrTask(pipeBase.PipelineTask): 

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

92 

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

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

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

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

97 interpolated over. 

98 """ 

99 

100 ConfigClass = QuickLookIsrTaskConfig 

101 _DefaultName = "quickLook" 

102 

103 def __init__(self, **kwargs): 

104 super().__init__(**kwargs) 

105 

106 def run(self, ccdExposure, *, 

107 camera=None, 

108 bias=None, 

109 dark=None, 

110 flat=None, 

111 defects=None, 

112 linearizer=None, 

113 crosstalk=None, 

114 bfKernel=None, 

115 bfGains=None, 

116 ptc=None, 

117 crosstalkSources=None, 

118 isrBaseConfig=None 

119 ): 

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

121 

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

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

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

125 

126 Parameters 

127 ---------- 

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

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

130 exposure is modified by this method. 

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

132 The camera geometry for this exposure. Required if 

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

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

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

136 Bias calibration frame. 

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

138 Functor for linearization. 

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

140 Calibration for crosstalk. 

141 crosstalkSources : `list`, optional 

142 List of possible crosstalk sources. 

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

144 Dark calibration frame. 

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

146 Flat calibration frame. 

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

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

149 and read noise. 

150 bfKernel : `numpy.ndarray`, optional 

151 Brighter-fatter kernel. 

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

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

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

155 the detector in question. 

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

157 List of defects. 

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

159 Struct containing the fringe correction data, with 

160 elements: 

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

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

163 number generator (`uint32`) 

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

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

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

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

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

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

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

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

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

173 coordinates. 

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

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

176 atmosphere, assumed to be spatially constant. 

177 detectorNum : `int`, optional 

178 The integer number for the detector to process. 

179 strayLightData : `object`, optional 

180 Opaque object containing calibration information for stray-light 

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

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

183 Illumination correction image. 

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

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

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

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

188 

189 Returns 

190 ------- 

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

192 Result struct with component: 

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

194 The ISRed and cosmic-ray-repaired exposure. 

195 """ 

196 if not isrBaseConfig: 

197 isrConfig = IsrTask.ConfigClass() 

198 packageDir = getPackageDir("summit_utils") 

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

200 else: 

201 isrConfig = isrBaseConfig 

202 

203 isrConfig.doBias = False 

204 isrConfig.doDark = False 

205 isrConfig.doFlat = False 

206 isrConfig.doFringe = False 

207 isrConfig.doDefect = False 

208 isrConfig.doLinearize = False 

209 isrConfig.doCrosstalk = False 

210 isrConfig.doBrighterFatter = False 

211 isrConfig.usePtcGains = False 

212 

213 if bias: 

214 isrConfig.doBias = True 

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

216 

217 if dark: 

218 isrConfig.doDark = True 

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

220 

221 if flat: 

222 isrConfig.doFlat = True 

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

224 

225 # TODO: deal with fringes here 

226 if defects: 

227 isrConfig.doDefect = True 

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

229 

230 if linearizer: 

231 isrConfig.doLinearize = True 

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

233 

234 if crosstalk: 

235 isrConfig.doCrosstalk = True 

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

237 

238 if bfKernel: 

239 isrConfig.doBrighterFatter = True 

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

241 

242 if ptc: 

243 isrConfig.usePtcGains = True 

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

245 

246 isrConfig.doWrite = False 

247 isrTask = IsrTask(config=isrConfig) 

248 result = isrTask.run(ccdExposure, 

249 camera=camera, 

250 bias=bias, 

251 dark=dark, 

252 flat=flat, 

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

254 defects=defects, 

255 linearizer=linearizer, 

256 crosstalk=crosstalk, 

257 bfKernel=bfKernel, 

258 bfGains=bfGains, 

259 ptc=ptc, 

260 crosstalkSources=crosstalkSources,) 

261 

262 postIsr = result.exposure 

263 

264 if self.config.doRepairCosmics: 

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

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

267 if postIsr.getPsf() is None: 

268 installPsfTask = InstallGaussianPsfTask() 

269 installPsfTask.run(postIsr) 

270 

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

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

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

274 repairConfig = CharacterizeImageTask.ConfigClass() 

275 repairConfig.doMeasurePsf = False 

276 repairConfig.doApCorr = False 

277 repairConfig.doDeblend = False 

278 repairConfig.doWrite = False 

279 repairConfig.repair.cosmicray.nCrPixelMax = 200000 

280 repairTask = CharacterizeImageTask(config=repairConfig) 

281 

282 repairTask.repair.run(postIsr) 

283 except Exception as e: 

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

285 

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

287 return pipeBase.Struct(exposure=postIsr, 

288 outputExposure=postIsr)