Coverage for tests/test_quickLook.py: 16%

159 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-03 04:44 -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 contextlib 

23import tempfile 

24import unittest 

25 

26import numpy as np 

27 

28import lsst.afw.cameraGeom.testUtils as afwTestUtils 

29import lsst.afw.image as afwImage 

30import lsst.daf.butler.tests as butlerTests 

31import lsst.ip.isr as ipIsr 

32import lsst.ip.isr.isrMock as isrMock 

33import lsst.pex.exceptions 

34import lsst.pipe.base as pipeBase 

35import lsst.pipe.base.testUtils 

36from lsst.afw.image import TransmissionCurve 

37from lsst.summit.utils.quickLook import QuickLookIsrTask, QuickLookIsrTaskConfig 

38 

39 

40class QuickLookIsrTaskTestCase(unittest.TestCase): 

41 """Tests of the run method with fake data.""" 

42 

43 def setUp(self): 

44 self.mockConfig = isrMock.IsrMockConfig() 

45 self.dataContainer = isrMock.MockDataContainer(config=self.mockConfig) 

46 self.camera = isrMock.IsrMock(config=self.mockConfig).getCamera() 

47 

48 self.ccdExposure = isrMock.RawMock(config=self.mockConfig).run() 

49 self.detector = self.ccdExposure.getDetector() 

50 amps = self.detector.getAmplifiers() 

51 ampNames = [amp.getName() for amp in amps] 

52 

53 # # Mock other optional parameters 

54 self.bias = self.dataContainer.get("bias") 

55 self.dark = self.dataContainer.get("dark") 

56 self.flat = self.dataContainer.get("flat") 

57 self.defects = self.dataContainer.get("defects") 

58 self.ptc = ipIsr.PhotonTransferCurveDataset(ampNames=ampNames) # Mock PTC dataset 

59 self.bfKernel = self.dataContainer.get("bfKernel") 

60 self.newBFKernel = pipeBase.Struct(gain={}) 

61 for amp_i, amp in enumerate(ampNames): 

62 self.newBFKernel.gain[amp] = 0.9 + 0.1 * amp_i 

63 self.task = QuickLookIsrTask(config=QuickLookIsrTaskConfig()) 

64 

65 def test_runQuickLook(self): 

66 # Execute the run method with the mock data 

67 result = self.task.run( 

68 self.ccdExposure, 

69 camera=self.camera, 

70 bias=self.bias, 

71 dark=self.dark, 

72 flat=self.flat, 

73 defects=self.defects, 

74 linearizer=None, 

75 crosstalk=None, 

76 bfKernel=self.bfKernel, 

77 newBFKernel=self.newBFKernel, 

78 ptc=self.ptc, 

79 crosstalkSources=None, 

80 ) 

81 self.assertIsNotNone(result, "Result of run method should not be None") 

82 self.assertIsInstance(result, pipeBase.Struct, "Result should be of type lsst.pipe.base.Struct") 

83 self.assertIsInstance( 

84 result.exposure, 

85 afwImage.Exposure, 

86 "Resulting exposure should be an instance of lsst.afw.image.Exposure", 

87 ) 

88 

89 def test_runQuickLookMissingData(self): 

90 # Test without any inputs other than the exposure 

91 result = self.task.run(self.ccdExposure) 

92 self.assertIsInstance(result.exposure, afwImage.Exposure) 

93 

94 def test_runQuickLookBadDark(self): 

95 # Test with an incorrect dark frame 

96 bbox = self.ccdExposure.getBBox() 

97 bbox.grow(-20) 

98 with self.assertRaises(lsst.pex.exceptions.wrappers.LengthError): 

99 self.task.run( 

100 self.ccdExposure, 

101 camera=self.camera, 

102 bias=self.bias, 

103 dark=self.dark[bbox], 

104 flat=self.flat, 

105 defects=self.defects, 

106 ) 

107 

108 

109class QuickLookIsrTaskRunQuantumTests(lsst.utils.tests.TestCase): 

110 """Tests of ``QuickLookIsrTask.runQuantum``, which need a test butler, 

111 but do not need real images. 

112 

113 Adapted from the unit tests of ``CalibrateImageTask.runQuantum`` 

114 """ 

115 

116 def setUp(self): 

117 instrument = "testCam" 

118 exposureId = 100 

119 visit = 100101 

120 detector = 0 

121 physical_filter = "testCam_filter" 

122 band = "X" 

123 

124 # Map the isrTask connection names to the names of the Butler dataset 

125 # inputs 

126 ccdExposure = "raw" 

127 camera = "camera" 

128 bias = "bias" 

129 dark = "dark" 

130 flat = "flat" 

131 defects = "defects" 

132 bfKernel = "bfKernel" 

133 newBFKernel = "brighterFatterKernel" 

134 ptc = "ptc" 

135 filterTransmission = "transmission_filter" 

136 deferredChargeCalib = "cpCtiCalib" 

137 opticsTransmission = "transmission_optics" 

138 strayLightData = "yBackground" 

139 atmosphereTransmission = "transmission_atmosphere" 

140 crosstalk = "crosstalk" 

141 illumMaskedImage = "illum" 

142 linearizer = "linearizer" 

143 fringes = "fringe" 

144 sensorTransmission = "transmission_sensor" 

145 crosstalkSources = "isrOverscanCorrected" 

146 

147 # outputs 

148 outputExposure = "postISRCCD" 

149 

150 # quickLook-only outputs 

151 exposure = "quickLookExp" 

152 

153 # Create a and populate a test butler for runQuantum tests. 

154 self.repo_path = tempfile.TemporaryDirectory(ignore_cleanup_errors=True) 

155 self.repo = butlerTests.makeTestRepo(self.repo_path.name) 

156 

157 # dataIds for fake data 

158 butlerTests.addDataIdValue(self.repo, "instrument", instrument) 

159 butlerTests.addDataIdValue(self.repo, "physical_filter", physical_filter, band=band) 

160 butlerTests.addDataIdValue(self.repo, "detector", detector) 

161 butlerTests.addDataIdValue(self.repo, "exposure", exposureId, physical_filter=physical_filter) 

162 butlerTests.addDataIdValue(self.repo, "visit", visit) 

163 

164 # inputs 

165 butlerTests.addDatasetType(self.repo, ccdExposure, {"instrument", "exposure", "detector"}, "Exposure") 

166 butlerTests.addDatasetType(self.repo, camera, {"instrument"}, "Camera") 

167 butlerTests.addDatasetType(self.repo, bias, {"instrument", "detector"}, "Exposure") 

168 butlerTests.addDatasetType(self.repo, dark, {"instrument", "detector"}, "Exposure") 

169 butlerTests.addDatasetType(self.repo, flat, {"instrument", "physical_filter", "detector"}, "Exposure") 

170 butlerTests.addDatasetType(self.repo, defects, {"instrument", "detector"}, "Defects") 

171 butlerTests.addDatasetType(self.repo, linearizer, {"instrument", "detector"}, "Linearizer") 

172 butlerTests.addDatasetType(self.repo, crosstalk, {"instrument", "detector"}, "CrosstalkCalib") 

173 butlerTests.addDatasetType(self.repo, bfKernel, {"instrument"}, "NumpyArray") 

174 butlerTests.addDatasetType(self.repo, newBFKernel, {"instrument", "detector"}, "BrighterFatterKernel") 

175 butlerTests.addDatasetType(self.repo, ptc, {"instrument", "detector"}, "PhotonTransferCurveDataset") 

176 butlerTests.addDatasetType( 

177 self.repo, filterTransmission, {"instrument", "physical_filter"}, "TransmissionCurve" 

178 ) 

179 butlerTests.addDatasetType(self.repo, opticsTransmission, {"instrument"}, "TransmissionCurve") 

180 butlerTests.addDatasetType(self.repo, deferredChargeCalib, {"instrument", "detector"}, "IsrCalib") 

181 butlerTests.addDatasetType( 

182 self.repo, strayLightData, {"instrument", "physical_filter", "detector"}, "Exposure" 

183 ) 

184 butlerTests.addDatasetType(self.repo, atmosphereTransmission, {"instrument"}, "TransmissionCurve") 

185 butlerTests.addDatasetType( 

186 self.repo, illumMaskedImage, {"instrument", "physical_filter", "detector"}, "MaskedImage" 

187 ) 

188 butlerTests.addDatasetType( 

189 self.repo, fringes, {"instrument", "physical_filter", "detector"}, "Exposure" 

190 ) 

191 butlerTests.addDatasetType( 

192 self.repo, sensorTransmission, {"instrument", "detector"}, "TransmissionCurve" 

193 ) 

194 butlerTests.addDatasetType( 

195 self.repo, crosstalkSources, {"instrument", "exposure", "detector"}, "Exposure" 

196 ) 

197 

198 # outputs 

199 butlerTests.addDatasetType( 

200 self.repo, outputExposure, {"instrument", "exposure", "detector"}, "Exposure" 

201 ) 

202 butlerTests.addDatasetType(self.repo, exposure, {"instrument", "exposure", "detector"}, "Exposure") 

203 

204 # dataIds 

205 self.exposure_id = self.repo.registry.expandDataId( 

206 { 

207 "instrument": instrument, 

208 "exposure": exposureId, 

209 "detector": detector, 

210 "physical_filter": physical_filter, 

211 } 

212 ) 

213 self.instrument_id = self.repo.registry.expandDataId({"instrument": instrument}) 

214 self.flat_id = self.repo.registry.expandDataId( 

215 {"instrument": instrument, "physical_filter": physical_filter, "detector": detector} 

216 ) 

217 self.detector_id = self.repo.registry.expandDataId({"instrument": instrument, "detector": detector}) 

218 self.filter_id = self.repo.registry.expandDataId( 

219 {"instrument": instrument, "physical_filter": physical_filter} 

220 ) 

221 

222 # put empty data 

223 transmissionCurve = TransmissionCurve.makeSpatiallyConstant( 

224 np.ones(2), np.linspace(0, 1, 2), 0.0, 0.0 

225 ) 

226 self.butler = butlerTests.makeTestCollection(self.repo) 

227 self.butler.put(afwImage.ExposureF(), ccdExposure, self.exposure_id) 

228 self.butler.put(afwTestUtils.CameraWrapper().camera, camera, self.instrument_id) 

229 self.butler.put(afwImage.ExposureF(), bias, self.detector_id) 

230 self.butler.put(afwImage.ExposureF(), dark, self.detector_id) 

231 self.butler.put(afwImage.ExposureF(), flat, self.flat_id) 

232 self.butler.put(lsst.ip.isr.Defects(), defects, self.detector_id) 

233 self.butler.put(np.zeros(2), bfKernel, self.instrument_id) 

234 self.butler.put( 

235 lsst.ip.isr.brighterFatterKernel.BrighterFatterKernel(), newBFKernel, self.detector_id 

236 ) 

237 self.butler.put(ipIsr.PhotonTransferCurveDataset(), ptc, self.detector_id) 

238 self.butler.put(transmissionCurve, filterTransmission, self.filter_id) 

239 self.butler.put(lsst.ip.isr.calibType.IsrCalib(), deferredChargeCalib, self.detector_id) 

240 self.butler.put(transmissionCurve, opticsTransmission, self.instrument_id) 

241 self.butler.put(afwImage.ExposureF(), strayLightData, self.flat_id) 

242 self.butler.put(transmissionCurve, atmosphereTransmission, self.instrument_id) 

243 self.butler.put(lsst.ip.isr.crosstalk.CrosstalkCalib(), crosstalk, self.detector_id) 

244 self.butler.put(afwImage.ExposureF().maskedImage, illumMaskedImage, self.flat_id) 

245 self.butler.put(lsst.ip.isr.linearize.Linearizer(), linearizer, self.detector_id) 

246 self.butler.put(afwImage.ExposureF(), fringes, self.flat_id) 

247 self.butler.put(transmissionCurve, sensorTransmission, self.detector_id) 

248 self.butler.put(afwImage.ExposureF(), crosstalkSources, self.exposure_id) 

249 

250 def tearDown(self): 

251 del self.repo_path # this removes the temporary directory 

252 

253 def test_runQuantum(self): 

254 config = ipIsr.IsrTaskConfig() 

255 # Remove some outputs 

256 config.doBinnedExposures = False 

257 config.doSaveInterpPixels = False 

258 config.qa.doThumbnailOss = False 

259 config.qa.doThumbnailFlattened = False 

260 config.doCalculateStatistics = False 

261 

262 # Turn on all optional inputs 

263 config.doAttachTransmissionCurve = True 

264 config.doIlluminationCorrection = True 

265 config.doStrayLight = True 

266 config.doDeferredCharge = True 

267 config.usePtcReadNoise = True 

268 config.doCrosstalk = True 

269 config.doBrighterFatter = True 

270 

271 # Override a method in IsrTask that is executed early, to instead raise 

272 # a custom exception called ExitMock that we can catch and ignore. 

273 isrTask = ipIsr.IsrTask 

274 isrTask.ensureExposure = raiseExitMockError 

275 task = QuickLookIsrTask(isrTask=isrTask) 

276 lsst.pipe.base.testUtils.assertValidInitOutput(task) 

277 

278 # Use the names of the connections here, not the Butler dataset name 

279 quantum = lsst.pipe.base.testUtils.makeQuantum( 

280 task, 

281 self.butler, 

282 self.exposure_id, 

283 { 

284 "ccdExposure": self.exposure_id, 

285 "camera": self.instrument_id, 

286 "bias": self.detector_id, 

287 "dark": self.detector_id, 

288 "flat": self.flat_id, 

289 "defects": self.detector_id, 

290 "bfKernel": self.instrument_id, 

291 "newBFKernel": self.detector_id, 

292 "ptc": self.detector_id, 

293 "filterTransmission": self.filter_id, 

294 "deferredChargeCalib": self.detector_id, 

295 "opticsTransmission": self.instrument_id, 

296 "strayLightData": self.flat_id, 

297 "atmosphereTransmission": self.instrument_id, 

298 "crosstalk": self.detector_id, 

299 "illumMaskedImage": self.flat_id, 

300 "linearizer": self.detector_id, 

301 "fringes": self.flat_id, 

302 "sensorTransmission": self.detector_id, 

303 "crosstalkSources": [self.exposure_id, self.exposure_id], 

304 # outputs 

305 "outputExposure": self.exposure_id, 

306 "exposure": self.exposure_id, 

307 }, 

308 ) 

309 # Check that the proper kwargs are passed to run(). 

310 with contextlib.suppress(ExitMockError): 

311 lsst.pipe.base.testUtils.runTestQuantum(task, self.butler, quantum, mockRun=False) 

312 

313 

314def raiseExitMockError(*args): 

315 """Raise a custom exception.""" 

316 raise ExitMockError 

317 

318 

319class ExitMockError(Exception): 

320 """A custom exception to catch during a unit test.""" 

321 

322 pass