Coverage for tests/test_assembleCoadd.py: 29%
153 statements
« prev ^ index » next coverage.py v7.2.4, created at 2023-04-30 02:43 -0700
« prev ^ index » next coverage.py v7.2.4, created at 2023-04-30 02:43 -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 assembleTask = MockAssembleCoaddTask(config=config)
213 self.checkRun(assembleTask)
215 def testAssemblePsfMatched(self):
216 config = MockAssembleCoaddConfig(warpType="psfMatched")
217 assembleTask = MockAssembleCoaddTask(config=config)
218 self.checkRun(assembleTask, warpType="psfMatched")
220 def testAssembleCompareWarp(self):
221 config = MockCompareWarpAssembleCoaddConfig()
222 assembleTask = MockCompareWarpAssembleCoaddTask(config=config)
223 self.checkRun(assembleTask)
225 def testAssembleDCR(self):
226 config = MockDcrAssembleCoaddConfig()
227 assembleTask = MockDcrAssembleCoaddTask(config=config)
228 self.checkRun(assembleTask)
230 def testOnlineCoadd(self):
231 config = MockInputMapAssembleCoaddConfig()
232 config.statistic = "MEAN"
233 assembleTask = MockInputMapAssembleCoaddTask(config=config)
235 dataRefList = self.dataRefList
236 results = assembleTask.runQuantum(self.skyInfo, dataRefList)
237 coadd = results.coaddExposure
239 configOnline = MockInputMapAssembleCoaddConfig()
240 configOnline.statistic = "MEAN"
241 configOnline.doOnlineForMean = True
242 configOnline.validate()
243 assembleTaskOnline = MockInputMapAssembleCoaddTask(config=configOnline)
245 resultsOnline = assembleTaskOnline.runQuantum(self.skyInfo, dataRefList)
246 coaddOnline = resultsOnline.coaddExposure
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)
254 def testInputMap(self):
255 config = MockInputMapAssembleCoaddConfig()
256 assembleTask = MockInputMapAssembleCoaddTask(config=config)
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)
276 results = assembleTask.runQuantum(self.skyInfo, dataRefList)
278 inputMap = results.inputMap
279 validPix, raPix, decPix = inputMap.valid_pixels_pos(return_pixels=True)
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)
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]])))
303class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
304 pass
307def setup_module(module):
308 lsst.utils.tests.init()
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()