Coverage for tests/test_assemble_cell_coadd.py: 33%

68 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-07 11:54 +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 

23from __future__ import annotations 

24 

25import unittest 

26from typing import TYPE_CHECKING, Iterable 

27 

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 

33 

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 

36 

37__all__ = ( 

38 "MockAssembleCellCoaddConfig", 

39 "MockAssembleCellCoaddTask", 

40) 

41 

42 

43class MockAssembleCellCoaddConfig(AssembleCellCoaddConfig): 

44 pass 

45 

46 

47class MockAssembleCellCoaddTask(AssembleCellCoaddTask): 

48 """Lightly modified version of `AssembleCellCoaddTask` for unit tests. 

49 

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 

55 ConfigClass = MockAssembleCellCoaddConfig 

56 

57 def runQuantum(self, mockSkyInfo, warpRefList): 

58 """Modified interface for testing coaddition algorithms without a 

59 Butler. 

60 

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. 

70 

71 Returns 

72 ------- 

73 retStruct : `lsst.pipe.base.Struct` 

74 The coadded exposure and associated metadata. 

75 """ 

76 

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 ) 

83 

84 retStruct = self.run( 

85 warpRefList, 

86 mockSkyInfo, 

87 ) 

88 

89 return retStruct 

90 

91 

92class AssembleCellCoaddTestCase(lsst.utils.tests.TestCase): 

93 """Tests of AssembleCellCoaddTask. 

94 

95 These tests bypass the middleware used for accessing data and managing Task 

96 execution. 

97 """ 

98 

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) 

112 

113 config = MockAssembleCellCoaddConfig() 

114 assembleTask = MockAssembleCellCoaddTask(config=config) 

115 cls.result = assembleTask.runQuantum(cls.skyInfo, cls.dataRefList) 

116 

117 def checkSortOrder(self, inputs: Iterable[ObservationIdentifiers]) -> None: 

118 """Check that the inputs are sorted. 

119 

120 The inputs must be sorted first by visit, and within the same visit, 

121 by detector. 

122 

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) 

135 

136 visit, detector = obsId.visit, obsId.detector 

137 

138 def checkRun(self, assembleTask): 

139 """Check that the task runs successfully.""" 

140 result = assembleTask.runQuantum(self.skyInfo, self.dataRefList) 

141 

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) 

152 

153 def test_assemble_basic(self): 

154 """Test that AssembleCellCoaddTask runs successfully without errors. 

155 

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) 

161 

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) 

170 

171 def test_inputs_sorted(self): 

172 """Check that the inputs are sorted. 

173 

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) 

179 

180 

181class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

182 pass 

183 

184 

185def setup_module(module): 

186 lsst.utils.tests.init() 

187 

188 

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()