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

90 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-18 12:17 +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 

22from lsst.ip.isr import IsrTask 

23from lsst.ip.isr.isrTask import IsrTaskConnections 

24import lsst.pipe.base.connectionTypes as cT 

25 

26import lsst.pex.config as pexConfig 

27import lsst.pipe.base as pipeBase 

28from lsst.pipe.tasks.characterizeImage import CharacterizeImageTask 

29from lsst.meas.algorithms.installGaussianPsf import InstallGaussianPsfTask 

30 

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

32 

33 

34def _getArgs(connection): 

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

36 """ 

37 newArgs = {} 

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

39 "minimum", "lookupFunction"): 

40 if hasattr(connection, attr): 

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

42 return newArgs 

43 

44 

45class QuickLookIsrTaskConnections(IsrTaskConnections): 

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

47 

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

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

50 minimum values to zero. 

51 """ 

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

53 # programatically clone all of the connections from isrTask 

54 # settin minimum values to zero for everything except the ccdExposure 

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

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

57 if hasattr(connection, 'minimum'): 

58 args = _getArgs(connection) 

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

60 args['minimum'] = 0 

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

62 self.allConnections[name] = newConnection 

63 setattr(self, name, newConnection) 

64 

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

66 name="quickLookExp", 

67 doc="The quickLook output exposure.", 

68 storageClass="ExposureF", 

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

70 ) 

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

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

73 # API compatibility. 

74 self.outputExposure = exposure 

75 

76 

77class QuickLookIsrTaskConfig(pipeBase.PipelineTaskConfig, 

78 pipelineConnections=QuickLookIsrTaskConnections): 

79 """Configuration parameters for QuickLookIsrTask.""" 

80 

81 doRepairCosmics = pexConfig.Field( 

82 dtype=bool, 

83 doc="Interpolate over cosmic rays?", 

84 default=True, 

85 ) 

86 

87 

88class QuickLookIsrTask(pipeBase.PipelineTask): 

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

90 

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

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

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

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

95 interpolated over. 

96 """ 

97 

98 ConfigClass = QuickLookIsrTaskConfig 

99 _DefaultName = "quickLook" 

100 

101 def __init__(self, **kwargs): 

102 super().__init__(**kwargs) 

103 

104 def run(self, ccdExposure, *, 

105 camera=None, 

106 bias=None, 

107 dark=None, 

108 flat=None, 

109 defects=None, 

110 linearizer=None, 

111 crosstalk=None, 

112 bfKernel=None, 

113 bfGains=None, 

114 ptc=None, 

115 crosstalkSources=None, 

116 isrBaseConfig=None 

117 ): 

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

119 

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

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

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

123 

124 Parameters 

125 ---------- 

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

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

128 exposure is modified by this method. 

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

130 The camera geometry for this exposure. Required if 

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

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

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

134 Bias calibration frame. 

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

136 Functor for linearization. 

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

138 Calibration for crosstalk. 

139 crosstalkSources : `list`, optional 

140 List of possible crosstalk sources. 

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

142 Dark calibration frame. 

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

144 Flat calibration frame. 

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

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

147 and read noise. 

148 bfKernel : `numpy.ndarray`, optional 

149 Brighter-fatter kernel. 

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

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

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

153 the detector in question. 

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

155 List of defects. 

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

157 Struct containing the fringe correction data, with 

158 elements: 

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

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

161 number generator (`uint32`) 

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

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

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

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

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

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

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

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

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

171 coordinates. 

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

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

174 atmosphere, assumed to be spatially constant. 

175 detectorNum : `int`, optional 

176 The integer number for the detector to process. 

177 strayLightData : `object`, optional 

178 Opaque object containing calibration information for stray-light 

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

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

181 Illumination correction image. 

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

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

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

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

186 

187 Returns 

188 ------- 

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

190 Result struct with component: 

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

192 The ISRed and cosmic-ray-repaired exposure. 

193 """ 

194 isrConfig = isrBaseConfig if isrBaseConfig else IsrTask.ConfigClass() 

195 isrConfig.doBias = False 

196 isrConfig.doDark = False 

197 isrConfig.doFlat = False 

198 isrConfig.doFringe = False 

199 isrConfig.doDefect = False 

200 isrConfig.doLinearize = False 

201 isrConfig.doCrosstalk = False 

202 isrConfig.doBrighterFatter = False 

203 isrConfig.usePtcGains = False 

204 

205 if bias: 

206 isrConfig.doBias = True 

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

208 

209 if dark: 

210 isrConfig.doDark = True 

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

212 

213 if flat: 

214 isrConfig.doFlat = True 

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

216 

217 # TODO: deal with fringes here 

218 if defects: 

219 isrConfig.doDefect = True 

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

221 

222 if linearizer: 

223 isrConfig.doLinearize = True 

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

225 

226 if crosstalk: 

227 isrConfig.doCrosstalk = True 

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

229 

230 if bfKernel: 

231 isrConfig.doBrighterFatter = True 

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

233 

234 if ptc: 

235 isrConfig.usePtcGains = True 

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

237 

238 isrConfig.doWrite = False 

239 isrTask = IsrTask(config=isrConfig) 

240 result = isrTask.run(ccdExposure, 

241 camera=camera, 

242 bias=bias, 

243 dark=dark, 

244 flat=flat, 

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

246 defects=defects, 

247 linearizer=linearizer, 

248 crosstalk=crosstalk, 

249 bfKernel=bfKernel, 

250 bfGains=bfGains, 

251 ptc=ptc, 

252 crosstalkSources=crosstalkSources,) 

253 

254 postIsr = result.exposure 

255 

256 if self.config.doRepairCosmics: 

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

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

259 if postIsr.getPsf() is None: 

260 installPsfTask = InstallGaussianPsfTask() 

261 installPsfTask.run(postIsr) 

262 

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

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

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

266 repairConfig = CharacterizeImageTask.ConfigClass() 

267 repairConfig.doMeasurePsf = False 

268 repairConfig.doApCorr = False 

269 repairConfig.doDeblend = False 

270 repairConfig.doWrite = False 

271 repairConfig.repair.cosmicray.nCrPixelMax = 200000 

272 repairTask = CharacterizeImageTask(config=repairConfig) 

273 

274 repairTask.repair.run(postIsr) 

275 except Exception as e: 

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

277 

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

279 return pipeBase.Struct(exposure=postIsr, 

280 outputExposure=postIsr)