Coverage for tests/test_assemble_cell_coadd.py: 33%
68 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-12 02:32 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-12 02:32 -0700
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#
23from __future__ import annotations
25import unittest
26from typing import TYPE_CHECKING, Iterable
28import lsst.pipe.base as pipeBase
29import lsst.utils.tests
30import numpy as np
31from assemble_coadd_test_utils import MockCoaddTestData, makeMockSkyInfo
32from lsst.drp.tasks.assemble_cell_coadd import AssembleCellCoaddConfig, AssembleCellCoaddTask
34if TYPE_CHECKING: 34 ↛ 35line 34 didn't jump to line 35, because the condition on line 34 was never true
35 from lsst.cell_coadds import ObservationIdentifiers
37__all__ = (
38 "MockAssembleCellCoaddConfig",
39 "MockAssembleCellCoaddTask",
40)
43class MockAssembleCellCoaddConfig(AssembleCellCoaddConfig):
44 pass
47class MockAssembleCellCoaddTask(AssembleCellCoaddTask):
48 """Lightly modified version of `AssembleCellCoaddTask` for 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 """
55 ConfigClass = MockAssembleCellCoaddConfig
57 def runQuantum(self, mockSkyInfo, warpRefList):
58 """Modified interface for testing coaddition algorithms without a
59 Butler.
61 Parameters
62 ----------
63 mockSkyInfo : `lsst.pipe.base.Struct`
64 A simple container that supplies a bounding box and WCS in the
65 same format as the output of
66 `lsst.pipe.tasks.CoaddBaseTask.getSkyInfo`
67 warpRefList : `list` of `lsst.pipe.tasks.MockExposureReference`
68 Data references to the test exposures that will be coadded,
69 using the Gen 3 API.
71 Returns
72 -------
73 retStruct : `lsst.pipe.base.Struct`
74 The coadded exposure and associated metadata.
75 """
77 self.common = pipeBase.Struct(
78 units=None,
79 wcs=mockSkyInfo.wcs,
80 band="i",
81 identifiers=pipeBase.Struct(skymap=None, tract=0, patch=42, band="i"),
82 )
84 retStruct = self.run(
85 warpRefList,
86 mockSkyInfo,
87 )
89 return retStruct
92class AssembleCellCoaddTestCase(lsst.utils.tests.TestCase):
93 """Tests of AssembleCellCoaddTask.
95 These tests bypass the middleware used for accessing data and managing Task
96 execution.
97 """
99 @classmethod
100 def setUpClass(cls) -> None:
101 patch = 42
102 tract = 0
103 testData = MockCoaddTestData(fluxRange=1e4)
104 exposures = {}
105 matchedExposures = {}
106 for expId in range(100, 110):
107 exposures[expId], matchedExposures[expId] = testData.makeTestImage(expId)
108 cls.dataRefList = testData.makeDataRefList(
109 exposures, matchedExposures, "direct", patch=patch, tract=tract
110 )
111 cls.skyInfo = makeMockSkyInfo(testData.bbox, testData.wcs, patch=patch)
113 config = MockAssembleCellCoaddConfig()
114 assembleTask = MockAssembleCellCoaddTask(config=config)
115 cls.result = assembleTask.runQuantum(cls.skyInfo, cls.dataRefList)
117 def checkSortOrder(self, inputs: Iterable[ObservationIdentifiers]) -> None:
118 """Check that the inputs are sorted.
120 The inputs must be sorted first by visit, and within the same visit,
121 by detector.
123 Parameters
124 ----------
125 inputs : `Iterable` [`ObservationIdentifiers`]
126 The inputs to be checked.
127 """
128 visit, detector = -np.inf, -np.inf # Previous visit, detector IDs.
129 for _, obsId in enumerate(inputs):
130 with self.subTest(input_number=obsId):
131 self.assertGreaterEqual(obsId.visit, visit)
132 if visit == obsId.visit:
133 with self.subTest(detector_number=obsId.detector):
134 self.assertGreaterEqual(obsId.detector, detector)
136 visit, detector = obsId.visit, obsId.detector
138 def checkRun(self, assembleTask):
139 """Check that the task runs successfully."""
140 result = assembleTask.runQuantum(self.skyInfo, self.dataRefList)
142 # Check that we produced an exposure.
143 self.assertTrue(result.multipleCellCoadd is not None)
144 # Check that the visit_count method returns a number less than or equal
145 # to the total number of input exposures available.
146 max_visit_count = len(self.dataRefList)
147 for cellId, singleCellCoadd in result.multipleCellCoadd.cells.items():
148 with self.subTest(x=cellId.x, y=cellId.y):
149 self.assertLessEqual(singleCellCoadd.visit_count, max_visit_count)
150 # Check that the inputs are sorted.
151 self.checkSortOrder(singleCellCoadd.inputs)
153 def test_assemble_basic(self):
154 """Test that AssembleCellCoaddTask runs successfully without errors.
156 This test does not check the correctness of the coaddition algorithms.
157 This is intended to prevent the code from bit rotting.
158 """
159 # Check that we produced an exposure.
160 self.assertTrue(self.result.multipleCellCoadd is not None)
162 def test_visit_count(self):
163 """Check that the visit_count method returns a number less than or
164 equal to the total number of input exposures available.
165 """
166 max_visit_count = len(self.dataRefList)
167 for cellId, singleCellCoadd in self.result.multipleCellCoadd.cells.items():
168 with self.subTest(x=cellId.x, y=cellId.y):
169 self.assertLessEqual(singleCellCoadd.visit_count, max_visit_count)
171 def test_inputs_sorted(self):
172 """Check that the inputs are sorted.
174 The ordering is that inputs are sorted first by visit, and within the
175 same visit, they are ordered by detector.
176 """
177 for _, singleCellCoadd in self.result.multipleCellCoadd.cells.items():
178 self.checkSortOrder(singleCellCoadd.inputs)
181class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
182 pass
185def setup_module(module):
186 lsst.utils.tests.init()
189if __name__ == "__main__": 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true
190 lsst.utils.tests.init()
191 unittest.main()