Coverage for tests/test_assembleCoadd.py: 29%

153 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-12-04 02:22 -0800

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 assembleTask = MockAssembleCoaddTask(config=config) 

213 self.checkRun(assembleTask) 

214 

215 def testAssemblePsfMatched(self): 

216 config = MockAssembleCoaddConfig(warpType="psfMatched") 

217 assembleTask = MockAssembleCoaddTask(config=config) 

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

219 

220 def testAssembleCompareWarp(self): 

221 config = MockCompareWarpAssembleCoaddConfig() 

222 assembleTask = MockCompareWarpAssembleCoaddTask(config=config) 

223 self.checkRun(assembleTask) 

224 

225 def testAssembleDCR(self): 

226 config = MockDcrAssembleCoaddConfig() 

227 assembleTask = MockDcrAssembleCoaddTask(config=config) 

228 self.checkRun(assembleTask) 

229 

230 def testOnlineCoadd(self): 

231 config = MockInputMapAssembleCoaddConfig() 

232 config.statistic = "MEAN" 

233 assembleTask = MockInputMapAssembleCoaddTask(config=config) 

234 

235 dataRefList = self.dataRefList 

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

237 coadd = results.coaddExposure 

238 

239 configOnline = MockInputMapAssembleCoaddConfig() 

240 configOnline.statistic = "MEAN" 

241 configOnline.doOnlineForMean = True 

242 configOnline.validate() 

243 assembleTaskOnline = MockInputMapAssembleCoaddTask(config=configOnline) 

244 

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

246 coaddOnline = resultsOnline.coaddExposure 

247 

248 self.assertFloatsAlmostEqual(coaddOnline.image.array, 

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

250 self.assertFloatsAlmostEqual(coaddOnline.variance.array, 

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

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

253 

254 def testInputMap(self): 

255 config = MockInputMapAssembleCoaddConfig() 

256 assembleTask = MockInputMapAssembleCoaddTask(config=config) 

257 

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

259 patch = 42 

260 tract = 0 

261 testData = MockCoaddTestData(fluxRange=1e4) 

262 exposures = {} 

263 matchedExposures = {} 

264 for expId in range(100, 110): 

265 if expId == 105: 

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

267 testData.bbox.beginY + 10), 

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

269 else: 

270 badBox = None 

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

272 badRegionBox=badBox) 

273 dataRefList = testData.makeDataRefList(exposures, matchedExposures, 

274 'direct', patch=patch, tract=tract) 

275 

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

277 

278 inputMap = results.inputMap 

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

280 

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

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

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

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

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

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

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

288 

289 # Confirm that all exposures except 105 are completely covered 

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

291 metadata = inputMap.metadata 

292 visitBitDict = {} 

293 for bit in range(inputMap.wide_mask_maxbits): 

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

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

296 for expId in range(100, 110): 

297 if expId == 105: 

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

299 else: 

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

301 

302 

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

304 pass 

305 

306 

307def setup_module(module): 

308 lsst.utils.tests.init() 

309 

310 

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

312 lsst.utils.tests.init() 

313 unittest.main()