Coverage for tests/test_assembleCoadd.py: 35%

159 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-19 13:00 -0700

1# This file is part of pipe_tasks. 

2# 

3# LSST Data Management System 

4# This product includes software developed by the 

5# LSST Project (http://www.lsst.org/). 

6# See COPYRIGHT file at the top of the source tree. 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <https://www.lsstcorp.org/LegalNotices/>. 

21# 

22"""Test AssembleCoaddTask and its variants. 

23 

24This uses 

25""" 

26import unittest 

27import numpy as np 

28 

29import lsst.utils.tests 

30 

31import lsst.pipe.base as pipeBase 

32from lsst.pipe.tasks.assembleCoadd import (AssembleCoaddTask, AssembleCoaddConfig, 

33 CompareWarpAssembleCoaddTask, CompareWarpAssembleCoaddConfig) 

34from lsst.pipe.tasks.dcrAssembleCoadd import DcrAssembleCoaddTask, DcrAssembleCoaddConfig 

35from assembleCoaddTestUtils import makeMockSkyInfo, MockCoaddTestData 

36 

37__all__ = ["MockAssembleCoaddConfig", "MockAssembleCoaddTask", 

38 "MockCompareWarpAssembleCoaddConfig", "MockCompareWarpAssembleCoaddTask"] 

39 

40 

41class MockAssembleCoaddConfig(AssembleCoaddConfig): 

42 

43 def setDefaults(self): 

44 super().setDefaults() 

45 self.doWrite = False 

46 

47 

48class MockAssembleCoaddTask(AssembleCoaddTask): 

49 """Lightly modified version of `AssembleCoaddTask` for use with unit tests. 

50 

51 The modifications bypass the usual middleware for loading data and setting 

52 up the Task, and instead supply in-memory mock data references to the `run` 

53 method so that the coaddition algorithms can be tested without a Butler. 

54 """ 

55 ConfigClass = MockAssembleCoaddConfig 

56 

57 def __init__(self, **kwargs): 

58 super().__init__(**kwargs) 

59 self.warpType = self.config.warpType 

60 self.makeSubtask("interpImage") 

61 self.makeSubtask("scaleZeroPoint") 

62 

63 def processResults(self, *args, **kwargs): 

64 "This should be tested separately." 

65 pass 

66 

67 def runQuantum(self, mockSkyInfo, warpRefList, *args): 

68 """Modified interface for testing coaddition algorithms without a Butler. 

69 

70 Parameters 

71 ---------- 

72 mockSkyInfo : `lsst.pipe.base.Struct` 

73 A simple container that supplies a bounding box and WCS in the 

74 same format as the output of 

75 `lsst.pipe.tasks.CoaddBaseTask.getSkyInfo` 

76 warpRefList : `list` of `lsst.pipe.tasks.MockExposureReference` 

77 Data references to the test exposures that will be coadded, 

78 using the Gen 3 API. 

79 

80 Returns 

81 ------- 

82 retStruct : `lsst.pipe.base.Struct` 

83 The coadded exposure and associated metadata. 

84 """ 

85 inputs = self.prepareInputs(warpRefList) 

86 

87 retStruct = self.run(mockSkyInfo, inputs.tempExpRefList, inputs.imageScalerList, 

88 inputs.weightList, supplementaryData=pipeBase.Struct()) 

89 return retStruct 

90 

91 

92class MockCompareWarpAssembleCoaddConfig(CompareWarpAssembleCoaddConfig): 

93 

94 def setDefaults(self): 

95 super().setDefaults() 

96 self.assembleStaticSkyModel.retarget(MockAssembleCoaddTask) 

97 self.assembleStaticSkyModel.doWrite = False 

98 self.doWrite = False 

99 

100 

101class MockCompareWarpAssembleCoaddTask(MockAssembleCoaddTask, CompareWarpAssembleCoaddTask): 

102 """Lightly modified version of `CompareWarpAssembleCoaddTask` 

103 for use with unit tests. 

104 

105 The modifications bypass the usual middleware for loading data and setting 

106 up the Task, and instead supply in-memory mock data references to the `run` 

107 method so that the coaddition algorithms can be tested without a Butler. 

108 """ 

109 ConfigClass = MockCompareWarpAssembleCoaddConfig 

110 _DefaultName = "compareWarpAssembleCoadd" 

111 

112 def __init__(self, *args, **kwargs): 

113 CompareWarpAssembleCoaddTask.__init__(self, *args, **kwargs) 

114 

115 def runQuantum(self, mockSkyInfo, warpRefList, *args): 

116 inputs = self.prepareInputs(warpRefList) 

117 

118 assembleStaticSkyModel = MockAssembleCoaddTask(config=self.config.assembleStaticSkyModel) 

119 templateCoadd = assembleStaticSkyModel.runQuantum(mockSkyInfo, warpRefList) 

120 

121 supplementaryData = pipeBase.Struct( 

122 templateCoadd=templateCoadd.coaddExposure, 

123 nImage=templateCoadd.nImage, 

124 warpRefList=templateCoadd.warpRefList, 

125 imageScalerList=templateCoadd.imageScalerList, 

126 weightList=templateCoadd.weightList) 

127 

128 retStruct = self.run(mockSkyInfo, inputs.tempExpRefList, inputs.imageScalerList, 

129 inputs.weightList, supplementaryData=supplementaryData) 

130 return retStruct 

131 

132 

133class MockDcrAssembleCoaddConfig(DcrAssembleCoaddConfig): 

134 

135 def setDefaults(self): 

136 super().setDefaults() 

137 self.assembleStaticSkyModel.retarget(MockCompareWarpAssembleCoaddTask) 

138 self.assembleStaticSkyModel.doWrite = False 

139 self.doWrite = False 

140 self.effectiveWavelength = 476.31 # Use LSST g band values for the test. 

141 self.bandwidth = 552. - 405. 

142 

143 

144class MockDcrAssembleCoaddTask(MockCompareWarpAssembleCoaddTask, DcrAssembleCoaddTask): 

145 """Lightly modified version of `DcrAssembleCoaddTask` 

146 for use with unit tests. 

147 

148 The modifications bypass the usual middleware for loading data and setting 

149 up the Task, and instead supply in-memory mock data references to the `run` 

150 method so that the coaddition algorithms can be tested without a Butler. 

151 """ 

152 ConfigClass = MockDcrAssembleCoaddConfig 

153 _DefaultName = "dcrAssembleCoadd" 

154 

155 def __init__(self, *args, **kwargs): 

156 DcrAssembleCoaddTask.__init__(self, *args, **kwargs) 

157 

158 

159class MockInputMapAssembleCoaddConfig(MockCompareWarpAssembleCoaddConfig): 

160 

161 def setDefaults(self): 

162 super().setDefaults() 

163 self.doInputMap = True 

164 

165 

166class MockInputMapAssembleCoaddTask(MockCompareWarpAssembleCoaddTask): 

167 """Lightly modified version of `CompareWarpAssembleCoaddTask` 

168 for use with unit tests. 

169 

170 The modifications bypass the usual middleware for loading data and setting 

171 up the Task, and instead supply in-memory mock data references to the `run` 

172 method so that the coaddition algorithms can be tested without a Butler. 

173 """ 

174 ConfigClass = MockInputMapAssembleCoaddConfig 

175 _DefaultName = "inputMapAssembleCoadd" 

176 

177 def __init__(self, *args, **kwargs): 

178 CompareWarpAssembleCoaddTask.__init__(self, *args, **kwargs) 

179 

180 

181class AssembleCoaddTestCase(lsst.utils.tests.TestCase): 

182 """Tests of AssembleCoaddTask and its derived classes. 

183 

184 These tests bypass the middleware used for accessing data and managing Task 

185 execution. 

186 """ 

187 

188 def setUp(self): 

189 patch = 42 

190 tract = 0 

191 testData = MockCoaddTestData(fluxRange=1e4) 

192 exposures = {} 

193 matchedExposures = {} 

194 for expId in range(100, 110): 

195 exposures[expId], matchedExposures[expId] = testData.makeTestImage(expId) 

196 self.dataRefList = testData.makeDataRefList(exposures, matchedExposures, 

197 'direct', patch=patch, tract=tract) 

198 self.dataRefListPsfMatched = testData.makeDataRefList(exposures, matchedExposures, 

199 'psfMatched', patch=patch, tract=tract) 

200 self.skyInfo = makeMockSkyInfo(testData.bbox, testData.wcs, patch=patch) 

201 

202 def checkRun(self, assembleTask, warpType="direct"): 

203 """Check that the task runs successfully.""" 

204 dataRefList = self.dataRefListPsfMatched if warpType == "psfMatched" else self.dataRefList 

205 result = assembleTask.runQuantum(self.skyInfo, dataRefList) 

206 

207 # Check that we produced an exposure. 

208 self.assertTrue(result.coaddExposure is not None) 

209 

210 def testAssembleBasic(self): 

211 config = MockAssembleCoaddConfig() 

212 config.validate() 

213 assembleTask = MockAssembleCoaddTask(config=config) 

214 self.checkRun(assembleTask) 

215 

216 def testAssemblePsfMatched(self): 

217 config = MockAssembleCoaddConfig(warpType="psfMatched") 

218 config.validate() 

219 assembleTask = MockAssembleCoaddTask(config=config) 

220 self.checkRun(assembleTask, warpType="psfMatched") 

221 

222 def testAssembleCompareWarp(self): 

223 config = MockCompareWarpAssembleCoaddConfig() 

224 config.validate() 

225 assembleTask = MockCompareWarpAssembleCoaddTask(config=config) 

226 self.checkRun(assembleTask) 

227 

228 def testAssembleDCR(self): 

229 config = MockDcrAssembleCoaddConfig() 

230 config.validate() 

231 assembleTask = MockDcrAssembleCoaddTask(config=config) 

232 self.checkRun(assembleTask) 

233 

234 def testOnlineCoadd(self): 

235 config = MockInputMapAssembleCoaddConfig() 

236 config.statistic = "MEAN" 

237 config.validate() 

238 assembleTask = MockInputMapAssembleCoaddTask(config=config) 

239 

240 dataRefList = self.dataRefList 

241 results = assembleTask.runQuantum(self.skyInfo, dataRefList) 

242 coadd = results.coaddExposure 

243 

244 configOnline = MockInputMapAssembleCoaddConfig() 

245 configOnline.statistic = "MEAN" 

246 configOnline.doOnlineForMean = True 

247 configOnline.validate() 

248 assembleTaskOnline = MockInputMapAssembleCoaddTask(config=configOnline) 

249 

250 resultsOnline = assembleTaskOnline.runQuantum(self.skyInfo, dataRefList) 

251 coaddOnline = resultsOnline.coaddExposure 

252 

253 self.assertFloatsAlmostEqual(coaddOnline.image.array, 

254 coadd.image.array, rtol=1e-3) 

255 self.assertFloatsAlmostEqual(coaddOnline.variance.array, 

256 coadd.variance.array, rtol=1e-6) 

257 self.assertMasksEqual(coaddOnline.mask, coadd.mask) 

258 

259 def testInputMap(self): 

260 config = MockInputMapAssembleCoaddConfig() 

261 config.validate() 

262 assembleTask = MockInputMapAssembleCoaddTask(config=config) 

263 

264 # Make exposures where one of them has a bad region. 

265 patch = 42 

266 tract = 0 

267 testData = MockCoaddTestData(fluxRange=1e4) 

268 exposures = {} 

269 matchedExposures = {} 

270 for expId in range(100, 110): 

271 if expId == 105: 

272 badBox = lsst.geom.Box2I(lsst.geom.Point2I(testData.bbox.beginX + 10, 

273 testData.bbox.beginY + 10), 

274 lsst.geom.Extent2I(100, 100)) 

275 else: 

276 badBox = None 

277 exposures[expId], matchedExposures[expId] = testData.makeTestImage(expId, 

278 badRegionBox=badBox) 

279 dataRefList = testData.makeDataRefList(exposures, matchedExposures, 

280 'direct', patch=patch, tract=tract) 

281 

282 results = assembleTask.runQuantum(self.skyInfo, dataRefList) 

283 

284 inputMap = results.inputMap 

285 validPix, raPix, decPix = inputMap.valid_pixels_pos(return_pixels=True) 

286 

287 # Confirm that all the map pixels are in the bounding box 

288 # Exposure 100 is the first one and they all have the same WCS in the tests. 

289 xPix, yPix = exposures[100].getWcs().skyToPixelArray(raPix, decPix, degrees=True) 

290 self.assertGreater(xPix.min(), testData.bbox.beginX) 

291 self.assertGreater(yPix.min(), testData.bbox.beginY) 

292 self.assertLess(xPix.max(), testData.bbox.endX) 

293 self.assertLess(xPix.max(), testData.bbox.endY) 

294 

295 # Confirm that all exposures except 105 are completely covered 

296 # This assumes we have one input per visit in the mock data. 

297 metadata = inputMap.metadata 

298 visitBitDict = {} 

299 for bit in range(inputMap.wide_mask_maxbits): 

300 if f'B{bit:04d}VIS' in metadata: 

301 visitBitDict[metadata[f'B{bit:04d}VIS']] = bit 

302 for expId in range(100, 110): 

303 if expId == 105: 

304 self.assertFalse(np.all(inputMap.check_bits_pix(validPix, [visitBitDict[expId]]))) 

305 else: 

306 self.assertTrue(np.all(inputMap.check_bits_pix(validPix, [visitBitDict[expId]]))) 

307 

308 

309class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

310 pass 

311 

312 

313def setup_module(module): 

314 lsst.utils.tests.init() 

315 

316 

317if __name__ == "__main__": 317 ↛ 318line 317 didn't jump to line 318, because the condition on line 317 was never true

318 lsst.utils.tests.init() 

319 unittest.main()