Coverage for tests/test_assembleCoadd.py: 35%
159 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-18 12:37 -0700
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-18 12:37 -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.
24This uses
25"""
26import unittest
27import numpy as np
29import lsst.utils.tests
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
37__all__ = ["MockAssembleCoaddConfig", "MockAssembleCoaddTask",
38 "MockCompareWarpAssembleCoaddConfig", "MockCompareWarpAssembleCoaddTask"]
41class MockAssembleCoaddConfig(AssembleCoaddConfig):
43 def setDefaults(self):
44 super().setDefaults()
45 self.doWrite = False
48class MockAssembleCoaddTask(AssembleCoaddTask):
49 """Lightly modified version of `AssembleCoaddTask` for use with unit tests.
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
57 def __init__(self, **kwargs):
58 super().__init__(**kwargs)
59 self.warpType = self.config.warpType
60 self.makeSubtask("interpImage")
61 self.makeSubtask("scaleZeroPoint")
63 def processResults(self, *args, **kwargs):
64 "This should be tested separately."
65 pass
67 def runQuantum(self, mockSkyInfo, warpRefList, *args):
68 """Modified interface for testing coaddition algorithms without a Butler.
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.
80 Returns
81 -------
82 retStruct : `lsst.pipe.base.Struct`
83 The coadded exposure and associated metadata.
84 """
85 inputs = self.prepareInputs(warpRefList)
87 retStruct = self.run(mockSkyInfo, inputs.tempExpRefList, inputs.imageScalerList,
88 inputs.weightList, supplementaryData=pipeBase.Struct())
89 return retStruct
92class MockCompareWarpAssembleCoaddConfig(CompareWarpAssembleCoaddConfig):
94 def setDefaults(self):
95 super().setDefaults()
96 self.assembleStaticSkyModel.retarget(MockAssembleCoaddTask)
97 self.assembleStaticSkyModel.doWrite = False
98 self.doWrite = False
101class MockCompareWarpAssembleCoaddTask(MockAssembleCoaddTask, CompareWarpAssembleCoaddTask):
102 """Lightly modified version of `CompareWarpAssembleCoaddTask`
103 for use with unit tests.
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"
112 def __init__(self, *args, **kwargs):
113 CompareWarpAssembleCoaddTask.__init__(self, *args, **kwargs)
115 def runQuantum(self, mockSkyInfo, warpRefList, *args):
116 inputs = self.prepareInputs(warpRefList)
118 assembleStaticSkyModel = MockAssembleCoaddTask(config=self.config.assembleStaticSkyModel)
119 templateCoadd = assembleStaticSkyModel.runQuantum(mockSkyInfo, warpRefList)
121 supplementaryData = pipeBase.Struct(
122 templateCoadd=templateCoadd.coaddExposure,
123 nImage=templateCoadd.nImage,
124 warpRefList=templateCoadd.warpRefList,
125 imageScalerList=templateCoadd.imageScalerList,
126 weightList=templateCoadd.weightList)
128 retStruct = self.run(mockSkyInfo, inputs.tempExpRefList, inputs.imageScalerList,
129 inputs.weightList, supplementaryData=supplementaryData)
130 return retStruct
133class MockDcrAssembleCoaddConfig(DcrAssembleCoaddConfig):
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.
144class MockDcrAssembleCoaddTask(MockCompareWarpAssembleCoaddTask, DcrAssembleCoaddTask):
145 """Lightly modified version of `DcrAssembleCoaddTask`
146 for use with unit tests.
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"
155 def __init__(self, *args, **kwargs):
156 DcrAssembleCoaddTask.__init__(self, *args, **kwargs)
159class MockInputMapAssembleCoaddConfig(MockCompareWarpAssembleCoaddConfig):
161 def setDefaults(self):
162 super().setDefaults()
163 self.doInputMap = True
166class MockInputMapAssembleCoaddTask(MockCompareWarpAssembleCoaddTask):
167 """Lightly modified version of `CompareWarpAssembleCoaddTask`
168 for use with unit tests.
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"
177 def __init__(self, *args, **kwargs):
178 CompareWarpAssembleCoaddTask.__init__(self, *args, **kwargs)
181class AssembleCoaddTestCase(lsst.utils.tests.TestCase):
182 """Tests of AssembleCoaddTask and its derived classes.
184 These tests bypass the middleware used for accessing data and managing Task
185 execution.
186 """
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)
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)
207 # Check that we produced an exposure.
208 self.assertTrue(result.coaddExposure is not None)
210 def testAssembleBasic(self):
211 config = MockAssembleCoaddConfig()
212 config.validate()
213 assembleTask = MockAssembleCoaddTask(config=config)
214 self.checkRun(assembleTask)
216 def testAssemblePsfMatched(self):
217 config = MockAssembleCoaddConfig(warpType="psfMatched")
218 config.validate()
219 assembleTask = MockAssembleCoaddTask(config=config)
220 self.checkRun(assembleTask, warpType="psfMatched")
222 def testAssembleCompareWarp(self):
223 config = MockCompareWarpAssembleCoaddConfig()
224 config.validate()
225 assembleTask = MockCompareWarpAssembleCoaddTask(config=config)
226 self.checkRun(assembleTask)
228 def testAssembleDCR(self):
229 config = MockDcrAssembleCoaddConfig()
230 config.validate()
231 assembleTask = MockDcrAssembleCoaddTask(config=config)
232 self.checkRun(assembleTask)
234 def testOnlineCoadd(self):
235 config = MockInputMapAssembleCoaddConfig()
236 config.statistic = "MEAN"
237 config.validate()
238 assembleTask = MockInputMapAssembleCoaddTask(config=config)
240 dataRefList = self.dataRefList
241 results = assembleTask.runQuantum(self.skyInfo, dataRefList)
242 coadd = results.coaddExposure
244 configOnline = MockInputMapAssembleCoaddConfig()
245 configOnline.statistic = "MEAN"
246 configOnline.doOnlineForMean = True
247 configOnline.validate()
248 assembleTaskOnline = MockInputMapAssembleCoaddTask(config=configOnline)
250 resultsOnline = assembleTaskOnline.runQuantum(self.skyInfo, dataRefList)
251 coaddOnline = resultsOnline.coaddExposure
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)
259 def testInputMap(self):
260 config = MockInputMapAssembleCoaddConfig()
261 config.validate()
262 assembleTask = MockInputMapAssembleCoaddTask(config=config)
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)
282 results = assembleTask.runQuantum(self.skyInfo, dataRefList)
284 inputMap = results.inputMap
285 validPix, raPix, decPix = inputMap.valid_pixels_pos(return_pixels=True)
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)
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]])))
309class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
310 pass
313def setup_module(module):
314 lsst.utils.tests.init()
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()