Coverage for tests/test_assembleCoadd.py : 31%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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
31from lsst.pipe.tasks.assembleCoadd import (AssembleCoaddTask, AssembleCoaddConfig,
32 SafeClipAssembleCoaddTask, SafeClipAssembleCoaddConfig,
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 _dataRef2DebugPath(self, *args, **kwargs):
68 raise NotImplementedError("This lightweight version of the task is not "
69 "meant to test debugging options.")
71 def runQuantum(self, mockSkyInfo, warpRefList, *args):
72 """Modified interface for testing coaddition algorithms without a Butler.
74 Parameters
75 ----------
76 mockSkyInfo : `lsst.pipe.base.Struct`
77 A simple container that supplies a bounding box and WCS in the
78 same format as the output of
79 `lsst.pipe.tasks.CoaddBaseTask.getSkyInfo`
80 warpRefList : `list` of `lsst.pipe.tasks.MockExposureReference`
81 Data references to the test exposures that will be coadded,
82 using the Gen 3 API.
84 Returns
85 -------
86 retStruct : `lsst.pipe.base.Struct`
87 The coadded exposure and associated metadata.
88 """
89 inputs = self.prepareInputs(warpRefList)
90 supplementaryData = self.makeSupplementaryData(mockSkyInfo, warpRefList=inputs.tempExpRefList)
91 retStruct = self.run(mockSkyInfo, inputs.tempExpRefList, inputs.imageScalerList,
92 inputs.weightList, supplementaryData=supplementaryData)
93 return retStruct
95 def runDataRef(self, mockSkyInfo, selectDataList=None, warpRefList=None):
96 """Modified interface for testing coaddition algorithms without a Butler.
98 Notes
99 -----
100 This tests the coaddition algorithms using Gen 2 Butler data references,
101 and can be removed once that is fully deprecated.
103 Both `runDataRef` and `runQuantum` are needed even those their
104 implementation here is identical, because the Gen 2 and Gen 3 versions
105 of `makeSupplementaryData` call `runDataRef` and `runQuantum` to build
106 initial templates, respectively.
108 Parameters
109 ----------
110 mockSkyInfo : `lsst.pipe.base.Struct`
111 A simple container that supplies a bounding box and WCS in the
112 same format as the output of
113 `lsst.pipe.tasks.CoaddBaseTask.getSkyInfo`
114 warpRefList : `list` of `lsst.pipe.tasks.MockGen2ExposureReference`
115 Data references to the test exposures that will be coadded,
116 using the Gen 2 API.
118 Returns
119 -------
120 retStruct : `lsst.pipe.base.Struct`
121 The coadded exposure and associated metadata.
122 """
123 inputData = self.prepareInputs(warpRefList)
124 supplementaryData = self.makeSupplementaryData(mockSkyInfo, warpRefList=inputData.tempExpRefList)
125 retStruct = self.run(mockSkyInfo, inputData.tempExpRefList, inputData.imageScalerList,
126 inputData.weightList, supplementaryData=supplementaryData)
127 return retStruct
130class MockSafeClipAssembleCoaddConfig(SafeClipAssembleCoaddConfig):
132 def setDefaults(self):
133 super().setDefaults()
134 self.doWrite = False
137class MockSafeClipAssembleCoaddTask(MockAssembleCoaddTask, SafeClipAssembleCoaddTask):
138 """Lightly modified version of `SafeClipAssembleCoaddTask`
139 for use with unit tests.
141 The modifications bypass the usual middleware for loading data and setting
142 up the Task, and instead supply in-memory mock data references to the `run`
143 method so that the coaddition algorithms can be tested without a Butler.
144 """
145 ConfigClass = MockSafeClipAssembleCoaddConfig
146 _DefaultName = "safeClipAssembleCoadd"
148 def __init__(self, *args, **kwargs):
149 SafeClipAssembleCoaddTask.__init__(self, *args, **kwargs)
152class MockCompareWarpAssembleCoaddConfig(CompareWarpAssembleCoaddConfig):
154 def setDefaults(self):
155 super().setDefaults()
156 self.assembleStaticSkyModel.retarget(MockAssembleCoaddTask)
157 self.assembleStaticSkyModel.doWrite = False
158 self.doWrite = False
161class MockCompareWarpAssembleCoaddTask(MockAssembleCoaddTask, CompareWarpAssembleCoaddTask):
162 """Lightly modified version of `CompareWarpAssembleCoaddTask`
163 for use with unit tests.
165 The modifications bypass the usual middleware for loading data and setting
166 up the Task, and instead supply in-memory mock data references to the `run`
167 method so that the coaddition algorithms can be tested without a Butler.
168 """
169 ConfigClass = MockCompareWarpAssembleCoaddConfig
170 _DefaultName = "compareWarpAssembleCoadd"
172 def __init__(self, *args, **kwargs):
173 CompareWarpAssembleCoaddTask.__init__(self, *args, **kwargs)
176class MockDcrAssembleCoaddConfig(DcrAssembleCoaddConfig):
178 def setDefaults(self):
179 super().setDefaults()
180 self.assembleStaticSkyModel.retarget(MockCompareWarpAssembleCoaddTask)
181 self.assembleStaticSkyModel.doWrite = False
182 self.doWrite = False
183 self.effectiveWavelength = 476.31 # Use LSST g band values for the test.
184 self.bandwidth = 552. - 405.
187class MockDcrAssembleCoaddTask(MockAssembleCoaddTask, DcrAssembleCoaddTask):
188 """Lightly modified version of `DcrAssembleCoaddTask`
189 for use with unit tests.
191 The modifications bypass the usual middleware for loading data and setting
192 up the Task, and instead supply in-memory mock data references to the `run`
193 method so that the coaddition algorithms can be tested without a Butler.
194 """
195 ConfigClass = MockDcrAssembleCoaddConfig
196 _DefaultName = "dcrAssembleCoadd"
198 def __init__(self, *args, **kwargs):
199 DcrAssembleCoaddTask.__init__(self, *args, **kwargs)
202class MockInputMapAssembleCoaddConfig(MockCompareWarpAssembleCoaddConfig):
204 def setDefaults(self):
205 super().setDefaults()
206 self.doInputMap = True
209class MockInputMapAssembleCoaddTask(MockCompareWarpAssembleCoaddTask):
210 """Lightly modified version of `CompareWarpAssembleCoaddTask`
211 for use with unit tests.
213 The modifications bypass the usual middleware for loading data and setting
214 up the Task, and instead supply in-memory mock data references to the `run`
215 method so that the coaddition algorithms can be tested without a Butler.
216 """
217 ConfigClass = MockInputMapAssembleCoaddConfig
218 _DefaultName = "inputMapAssembleCoadd"
220 def __init__(self, *args, **kwargs):
221 CompareWarpAssembleCoaddTask.__init__(self, *args, **kwargs)
224class AssembleCoaddTestCase(lsst.utils.tests.TestCase):
225 """Tests of AssembleCoaddTask and its derived classes.
227 These tests bypass the middleware used for accessing data and managing Task
228 execution.
229 """
231 def setUp(self):
232 patch = 42
233 patchGen2 = "2,3"
234 tract = 0
235 testData = MockCoaddTestData(fluxRange=1e4)
236 exposures = {}
237 matchedExposures = {}
238 for expId in range(100, 110):
239 exposures[expId], matchedExposures[expId] = testData.makeTestImage(expId)
240 self.gen2DataRefList = testData.makeGen2DataRefList(exposures, matchedExposures,
241 patch=patchGen2, tract=tract)
242 self.dataRefList = testData.makeDataRefList(exposures, matchedExposures,
243 'direct', patch=patch, tract=tract)
244 self.dataRefListPsfMatched = testData.makeDataRefList(exposures, matchedExposures,
245 'psfMatched', patch=patch, tract=tract)
246 self.skyInfoGen2 = makeMockSkyInfo(testData.bbox, testData.wcs, patch=patchGen2)
247 self.skyInfo = makeMockSkyInfo(testData.bbox, testData.wcs, patch=patch)
249 def checkGen2Gen3Compatibility(self, assembleTask, warpType="direct"):
250 dataRefList = self.dataRefListPsfMatched if warpType == "psfMatched" else self.dataRefList
251 resultsGen3 = assembleTask.runQuantum(self.skyInfo, dataRefList)
252 resultsGen2 = assembleTask.runDataRef(self.skyInfoGen2, warpRefList=self.gen2DataRefList)
253 coaddGen2 = resultsGen2.coaddExposure
254 coaddGen3 = resultsGen3.coaddExposure
255 self.assertFloatsEqual(coaddGen2.image.array, coaddGen3.image.array)
257 def testGen2Gen3Compatibility(self):
258 config = MockAssembleCoaddConfig()
259 config.validate()
260 assembleTask = MockAssembleCoaddTask(config=config)
261 self.checkGen2Gen3Compatibility(assembleTask)
263 def testPsfMatchedGen2Gen3Compatibility(self):
264 config = MockAssembleCoaddConfig(warpType="psfMatched")
265 config.validate()
266 assembleTask = MockAssembleCoaddTask(config=config)
267 self.checkGen2Gen3Compatibility(assembleTask, warpType="psfMatched")
269 def testSafeClipGen2Gen3Compatibility(self):
270 config = MockSafeClipAssembleCoaddConfig()
271 config.validate()
272 assembleTask = MockSafeClipAssembleCoaddTask(config=config)
273 self.checkGen2Gen3Compatibility(assembleTask)
275 def testCompareWarpGen2Gen3Compatibility(self):
276 config = MockCompareWarpAssembleCoaddConfig()
277 config.validate()
278 assembleTask = MockCompareWarpAssembleCoaddTask(config=config)
279 self.checkGen2Gen3Compatibility(assembleTask)
281 def testDcrGen2Gen3Compatibility(self):
282 config = MockDcrAssembleCoaddConfig()
283 config.validate()
284 assembleTask = MockDcrAssembleCoaddTask(config=config)
285 self.checkGen2Gen3Compatibility(assembleTask)
287 def testInputMapGen3(self):
288 config = MockInputMapAssembleCoaddConfig()
289 config.validate()
290 assembleTask = MockInputMapAssembleCoaddTask(config=config)
292 # Make exposures where one of them has a bad region.
293 patch = 42
294 tract = 0
295 testData = MockCoaddTestData(fluxRange=1e4)
296 exposures = {}
297 matchedExposures = {}
298 for expId in range(100, 110):
299 if expId == 105:
300 badBox = lsst.geom.Box2I(lsst.geom.Point2I(testData.bbox.beginX + 10,
301 testData.bbox.beginY + 10),
302 lsst.geom.Extent2I(100, 100))
303 else:
304 badBox = None
305 exposures[expId], matchedExposures[expId] = testData.makeTestImage(expId,
306 badRegionBox=badBox)
307 dataRefList = testData.makeDataRefList(exposures, matchedExposures,
308 'direct', patch=patch, tract=tract)
310 results = assembleTask.runQuantum(self.skyInfo, dataRefList)
312 inputMap = results.inputMap
313 validPix, raPix, decPix = inputMap.valid_pixels_pos(return_pixels=True)
315 # Confirm that all the map pixels are in the bounding box
316 # Exposure 100 is the first one and they all have the same WCS in the tests.
317 xPix, yPix = exposures[100].getWcs().skyToPixelArray(raPix, decPix, degrees=True)
318 self.assertGreater(xPix.min(), testData.bbox.beginX)
319 self.assertGreater(yPix.min(), testData.bbox.beginY)
320 self.assertLess(xPix.max(), testData.bbox.endX)
321 self.assertLess(xPix.max(), testData.bbox.endY)
323 # Confirm that all exposures except 105 are completely covered
324 # This assumes we have one input per visit in the mock data.
325 metadata = inputMap.metadata
326 visitBitDict = {}
327 for bit in range(inputMap.wide_mask_maxbits):
328 if f'B{bit:04d}VIS' in metadata:
329 visitBitDict[metadata[f'B{bit:04d}VIS']] = bit
330 for expId in range(100, 110):
331 if expId == 105:
332 self.assertFalse(np.all(inputMap.check_bits_pix(validPix, [visitBitDict[expId]])))
333 else:
334 self.assertTrue(np.all(inputMap.check_bits_pix(validPix, [visitBitDict[expId]])))
337class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
338 pass
341def setup_module(module):
342 lsst.utils.tests.init()
345if __name__ == "__main__": 345 ↛ 346line 345 didn't jump to line 346, because the condition on line 345 was never true
346 lsst.utils.tests.init()
347 unittest.main()