Coverage for tests/test_assemble_coadd.py: 29%
153 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-27 12:15 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-27 12:15 +0000
1# This file is part of drp_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"""
24import unittest
25import numpy as np
27import lsst.utils.tests
29import lsst.pipe.base as pipeBase
30from lsst.drp.tasks.assemble_coadd import (AssembleCoaddTask, AssembleCoaddConfig,
31 CompareWarpAssembleCoaddTask, CompareWarpAssembleCoaddConfig,
32 )
33from lsst.drp.tasks.dcr_assemble_coadd import DcrAssembleCoaddTask, DcrAssembleCoaddConfig
34from assemble_coadd_test_utils import makeMockSkyInfo, MockCoaddTestData
36__all__ = ["MockAssembleCoaddConfig", "MockAssembleCoaddTask",
37 "MockCompareWarpAssembleCoaddConfig", "MockCompareWarpAssembleCoaddTask"]
40class MockAssembleCoaddConfig(AssembleCoaddConfig):
42 def setDefaults(self):
43 super().setDefaults()
44 self.doWrite = False
47class MockAssembleCoaddTask(AssembleCoaddTask):
48 """Lightly modified version of `AssembleCoaddTask` for use with unit tests.
50 The modifications bypass the usual middleware for loading data and setting
51 up the Task, and instead supply in-memory mock data references to the `run`
52 method so that the coaddition algorithms can be tested without a Butler.
53 """
54 ConfigClass = MockAssembleCoaddConfig
56 def __init__(self, **kwargs):
57 super().__init__(**kwargs)
58 self.warpType = self.config.warpType
59 self.makeSubtask("interpImage")
60 self.makeSubtask("scaleZeroPoint")
62 def processResults(self, *args, **kwargs):
63 "This should be tested separately."
64 pass
66 def runQuantum(self, mockSkyInfo, warpRefList, *args):
67 """Modified interface for testing coaddition algorithms without a
68 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
283 # tests.
284 xPix, yPix = exposures[100].getWcs().skyToPixelArray(raPix, decPix, degrees=True)
285 self.assertGreater(xPix.min(), testData.bbox.beginX)
286 self.assertGreater(yPix.min(), testData.bbox.beginY)
287 self.assertLess(xPix.max(), testData.bbox.endX)
288 self.assertLess(xPix.max(), testData.bbox.endY)
290 # Confirm that all exposures except 105 are completely covered
291 # This assumes we have one input per visit in the mock data.
292 metadata = inputMap.metadata
293 visitBitDict = {}
294 for bit in range(inputMap.wide_mask_maxbits):
295 if f'B{bit:04d}VIS' in metadata:
296 visitBitDict[metadata[f'B{bit:04d}VIS']] = bit
297 for expId in range(100, 110):
298 if expId == 105:
299 self.assertFalse(np.all(inputMap.check_bits_pix(validPix, [visitBitDict[expId]])))
300 else:
301 self.assertTrue(np.all(inputMap.check_bits_pix(validPix, [visitBitDict[expId]])))
304class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
305 pass
308def setup_module(module):
309 lsst.utils.tests.init()
312if __name__ == "__main__": 312 ↛ 313line 312 didn't jump to line 313, because the condition on line 312 was never true
313 lsst.utils.tests.init()
314 unittest.main()