Coverage for tests / test_coadd_ap_corr_map.py: 17%
102 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 09:22 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 09:22 +0000
1# This file is part of cell_coadds.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22import unittest
24import numpy as np
26import lsst.afw.image as afwImage
27import lsst.afw.math as afwMath
28import lsst.geom as geom
29import lsst.utils.tests
30from lsst.cell_coadds import CoaddApCorrMapStacker
33class TestCoaddApCorrMapStacker(lsst.utils.tests.TestCase):
34 """Test the CoaddApCorrMap stacker."""
36 @classmethod
37 def setUpClass(cls): # noqa: D102
38 cls.bbox = geom.Box2I(
39 minimum=geom.Point2I(-10, -10),
40 maximum=geom.Point2I(10, 10),
41 )
42 cls.evaluation_point = geom.Point2D(0, 0)
43 cls.visit_count = 5
44 cls.ap_corr_map_list = [afwImage.ApCorrMap() for _ in range(cls.visit_count)]
45 # Set them up to the spatially constant BoundedFields.
46 for idx, ap_corr_map in enumerate(cls.ap_corr_map_list, start=2):
47 ap_corr_map.set(
48 "base_PsfFlux_instFlux", afwMath.ChebyshevBoundedField(cls.bbox, np.array([[1 / idx]]))
49 )
50 ap_corr_map.set(
51 "base_PsfFlux_instFluxErr", afwMath.ChebyshevBoundedField(cls.bbox, np.array([[0.05 / idx]]))
52 )
53 ap_corr_map.set(
54 "base_GaussianFlux_instFlux", afwMath.ChebyshevBoundedField(cls.bbox, np.array([[1.5 / idx]]))
55 )
56 ap_corr_map.set(
57 "base_GaussianFlux_instFluxErr",
58 afwMath.ChebyshevBoundedField(cls.bbox, np.array([[0.6 / idx]])),
59 )
61 def test_add_direct_ap_corr(self):
62 """Test that the values are what we expect when adding directly."""
63 stacker = CoaddApCorrMapStacker(
64 evaluation_point=self.evaluation_point,
65 do_coadd_inverse_ap_corr=False,
66 )
67 for idx in range(self.visit_count):
68 stacker.add(
69 self.ap_corr_map_list[idx],
70 weight=1.0,
71 )
73 final_ap_corr_map = stacker.final_ap_corr_map
75 self.assertFloatsAlmostEqual(
76 final_ap_corr_map["base_PsfFlux_instFlux"],
77 0.29, # mean of 1/2, 1/3, 1/4, 1/5, 1/6
78 atol=1e-8,
79 )
80 self.assertFloatsAlmostEqual(
81 final_ap_corr_map["base_PsfFlux_instFluxErr"],
82 0.05 * 0.14019827229875395,
83 atol=1e-8,
84 )
85 self.assertFloatsAlmostEqual(
86 final_ap_corr_map["base_GaussianFlux_instFlux"],
87 1.5 * 0.29,
88 atol=1e-8,
89 )
90 self.assertFloatsAlmostEqual(
91 final_ap_corr_map["base_GaussianFlux_instFluxErr"],
92 0.6 * 0.14019827229875395,
93 atol=1e-8,
94 )
96 def test_add_inverse_ap_corr(self):
97 """Test that the values are what we expect when adding inverse."""
98 stacker = CoaddApCorrMapStacker(
99 evaluation_point=self.evaluation_point,
100 do_coadd_inverse_ap_corr=True,
101 )
102 for idx in range(self.visit_count):
103 stacker.add(
104 self.ap_corr_map_list[idx],
105 weight=1.0,
106 )
108 final_ap_corr_map = stacker.final_ap_corr_map
110 self.assertFloatsAlmostEqual(
111 final_ap_corr_map["base_PsfFlux_instFlux"],
112 0.25,
113 atol=1e-8,
114 )
116 self.assertFloatsAlmostEqual(
117 final_ap_corr_map["base_GaussianFlux_instFlux"],
118 1.5 * 0.25,
119 atol=1e-8,
120 )
122 self.assertFloatsAlmostEqual(
123 final_ap_corr_map["base_PsfFlux_instFluxErr"],
124 0.05 * (0.25**2) * np.sqrt(90) / 5,
125 atol=1e-8,
126 )
128 self.assertFloatsAlmostEqual(
129 final_ap_corr_map["base_GaussianFlux_instFluxErr"],
130 0.6 / (1.5**2) * ((1.5 * 0.25) ** 2) * np.sqrt(90) / 5,
131 atol=1e-8,
132 )
134 @lsst.utils.tests.methodParameters(
135 do_coadd_inverse_ap_corr=[True, False],
136 )
137 def test_weight_normalization(self, do_coadd_inverse_ap_corr):
138 """Test that the coadd aperture corrections stay the same when the
139 relative weights are the same.
140 """
141 stacker = CoaddApCorrMapStacker(
142 evaluation_point=self.evaluation_point,
143 do_coadd_inverse_ap_corr=do_coadd_inverse_ap_corr,
144 )
146 normalized_weights = np.random.rand(self.visit_count)
147 normalized_weights /= np.sum(normalized_weights)
149 for idx in range(self.visit_count):
150 stacker.add(
151 self.ap_corr_map_list[idx],
152 weight=normalized_weights[idx],
153 )
155 reference_ap_corr_map = stacker.final_ap_corr_map
157 for scale in (
158 1.0,
159 0.5,
160 2.0,
161 ):
162 stacker = CoaddApCorrMapStacker(
163 evaluation_point=self.evaluation_point,
164 do_coadd_inverse_ap_corr=do_coadd_inverse_ap_corr,
165 )
166 weights = scale * normalized_weights
167 for idx in range(self.visit_count):
168 stacker.add(
169 self.ap_corr_map_list[idx],
170 weight=weights[idx],
171 )
172 final_ap_corr_map = stacker.final_ap_corr_map
174 for field_name in reference_ap_corr_map:
175 with self.subTest(scale=scale, field_name=field_name):
176 self.assertFloatsAlmostEqual(
177 final_ap_corr_map[field_name],
178 reference_ap_corr_map[field_name],
179 atol=1e-9,
180 )
182 @lsst.utils.tests.methodParameters(
183 do_coadd_inverse_ap_corr=[True, False],
184 )
185 def test_constant_weight(self, do_coadd_inverse_ap_corr):
186 """Test that the coadd aperture corrections stay the same when the
187 weights are all equal.
188 """
189 stacker = CoaddApCorrMapStacker(
190 evaluation_point=self.evaluation_point,
191 do_coadd_inverse_ap_corr=do_coadd_inverse_ap_corr,
192 )
194 for idx in range(self.visit_count):
195 stacker.add(
196 self.ap_corr_map_list[idx],
197 weight=1.0,
198 )
200 reference_ap_corr_map = stacker.final_ap_corr_map
202 stacker = CoaddApCorrMapStacker(
203 evaluation_point=self.evaluation_point,
204 do_coadd_inverse_ap_corr=do_coadd_inverse_ap_corr,
205 )
206 for idx in range(self.visit_count):
207 stacker.add(
208 self.ap_corr_map_list[idx],
209 weight=2.0,
210 )
211 final_ap_corr_map = stacker.final_ap_corr_map
213 for algorithm_name in stacker.ap_corr_names:
214 field_name = f"{algorithm_name}_instFlux"
215 with self.subTest(field_name=field_name):
216 self.assertFloatsAlmostEqual(
217 final_ap_corr_map[field_name],
218 reference_ap_corr_map[field_name],
219 atol=1e-8,
220 )
222 field_name = f"{algorithm_name}_instFluxErr"
223 # Errors are down by sqrt(visit_count).
224 conversion_factor = 1.0 # np.sqrt(self.visit_count)
225 with self.subTest(field_name=field_name):
226 self.assertFloatsAlmostEqual(
227 final_ap_corr_map[field_name] / conversion_factor,
228 reference_ap_corr_map[field_name],
229 atol=1e-8,
230 )
232 @lsst.utils.tests.methodParameters(
233 do_coadd_inverse_ap_corr=[True, False],
234 )
235 def test_constant_apcorr(self, do_coadd_inverse_ap_corr):
236 """Test that the coadd aperture corrections are constant when the
237 input aperture corrections are constant.
238 """
239 stacker = CoaddApCorrMapStacker(
240 evaluation_point=self.evaluation_point,
241 do_coadd_inverse_ap_corr=do_coadd_inverse_ap_corr,
242 )
244 weights = np.random.rand(self.visit_count)
245 initial_ap_corr_map = self.ap_corr_map_list[0]
246 for idx in range(self.visit_count):
247 stacker.add(
248 initial_ap_corr_map, # Add the same over and over again.
249 weight=weights[idx],
250 )
252 final_ap_corr_map = stacker.final_ap_corr_map
254 for algorithm_name in stacker.ap_corr_names:
255 field_name = f"{algorithm_name}_instFlux"
256 with self.subTest(field_name=field_name):
257 self.assertFloatsAlmostEqual(
258 final_ap_corr_map[field_name],
259 initial_ap_corr_map[field_name].evaluate(geom.Point2D(self.evaluation_point)),
260 atol=1e-8,
261 )
263 field_name = f"{algorithm_name}_instFluxErr"
264 # Errors are down by conversion_factor.
265 conversion_factor = np.sqrt(np.sum(weights**2)) / np.sum(weights)
266 with self.subTest(field_name=field_name):
267 self.assertFloatsAlmostEqual(
268 final_ap_corr_map[field_name] / conversion_factor,
269 initial_ap_corr_map[field_name].evaluate(geom.Point2D(self.evaluation_point)),
270 atol=1e-8,
271 )
273 # Evaluate aperture corrections at several points.
274 evaluate_at_x = np.linspace(self.bbox.getMinX(), self.bbox.getMaxX(), 5)
275 evaluate_at_y = np.linspace(self.bbox.getMinY(), self.bbox.getMaxY(), 5)
276 for algorithm_name in stacker.ap_corr_names:
277 field_name = f"{algorithm_name}_instFlux"
278 reference_value = initial_ap_corr_map[field_name].evaluate(geom.Point2D(self.evaluation_point))
279 aperture_correction_values = initial_ap_corr_map[field_name].evaluate(
280 evaluate_at_x,
281 evaluate_at_y,
282 )
283 with self.subTest(field_name=field_name):
284 np.testing.assert_array_equal(aperture_correction_values, reference_value)
287class TestCoaddApCorrMapMemoryTestCase(lsst.utils.tests.MemoryTestCase):
288 """Test the CoaddApCorrMapStacker for memory leaks."""
291def setup_module(module): # noqa: D103
292 lsst.utils.tests.init()
295if __name__ == "__main__": 295 ↛ 296line 295 didn't jump to line 296 because the condition on line 295 was never true
296 lsst.utils.tests.init()
297 unittest.main()