Coverage for tests / test_extended_psf_candidates.py: 22%

71 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-24 08:38 +0000

1# This file is part of pipe_tasks. 

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/>. 

21 

22import unittest 

23from typing import cast 

24 

25import astropy.units as u 

26import lsst.images.tests 

27import lsst.utils.tests 

28import numpy as np 

29from lsst.images import Image, Mask, MaskedImage, MaskSchema 

30from lsst.pipe.tasks.extended_psf import ExtendedPsfCandidate, ExtendedPsfCandidateInfo, ExtendedPsfCandidates 

31 

32 

33class ExtendedPsfCandidatesTestCase(lsst.utils.tests.TestCase): 

34 """Test ExtendedPsfCandidates.""" 

35 

36 def setUp(self): 

37 rng = np.random.Generator(np.random.MT19937(seed=5)) 

38 cutout_size = (25, 25) 

39 

40 # Generate simulated stars 

41 extended_psf_candidates = [] 

42 self.star_infos = [] 

43 mask_schema = MaskSchema([]) 

44 

45 for i in range(3): 

46 candidate_masked_image = MaskedImage( 

47 image=Image(rng.random(cutout_size).astype(np.float32)), 

48 mask=Mask(0, schema=mask_schema, shape=cutout_size), 

49 variance=Image(rng.random(cutout_size).astype(np.float32)), 

50 ) 

51 

52 star_info = ExtendedPsfCandidateInfo( 

53 visit=100 + i, 

54 detector=200 + i, 

55 ref_id=1000 + i, 

56 ref_mag=10.0 + i, 

57 position_x=float(rng.random()), 

58 position_y=float(rng.random()), 

59 focal_plane_radius=float(rng.random()) * u.mm, 

60 focal_plane_angle=float(rng.random()) * u.rad, 

61 ) 

62 self.star_infos.append(star_info) 

63 

64 # Build a normalized 2-D Gaussian kernel image directly. 

65 yy, xx = np.mgrid[: cutout_size[0], : cutout_size[1]] 

66 cy = (cutout_size[0] - 1) / 2.0 

67 cx = (cutout_size[1] - 1) / 2.0 

68 sigma = 1.5 

69 kernel_array = np.exp(-((yy - cy) ** 2 + (xx - cx) ** 2) / (2.0 * sigma**2)) 

70 kernel_array /= np.sum(kernel_array) 

71 psf_kernel_image = Image(kernel_array.astype(np.float64)) 

72 

73 extended_psf_candidates.append( 

74 ExtendedPsfCandidate( 

75 image=candidate_masked_image.image, 

76 mask=candidate_masked_image.mask, 

77 variance=candidate_masked_image.variance, 

78 psf_kernel_image=psf_kernel_image, 

79 star_info=star_info, 

80 ) 

81 ) 

82 

83 self.global_metadata = {"TEST_KEY": "TEST VALUE"} 

84 self.extended_psf_candidates = ExtendedPsfCandidates(extended_psf_candidates, self.global_metadata) 

85 

86 def tearDown(self): 

87 del self.extended_psf_candidates 

88 del self.star_infos 

89 del self.global_metadata 

90 

91 def testExtendedPsfCandidates(self): 

92 """Test that ExtendedPsfCandidates can be serialized/deserialized.""" 

93 

94 with lsst.images.tests.RoundtripFits( 

95 self, self.extended_psf_candidates, storage_class="ExtendedPsfCandidates" 

96 ) as roundtrip: 

97 pass 

98 extended_psf_candidates = roundtrip.result 

99 

100 global_metadata = extended_psf_candidates.metadata 

101 self.assertEqual(self.global_metadata["TEST_KEY"], global_metadata["TEST_KEY"]) 

102 self.assertEqual(len(self.extended_psf_candidates), len(extended_psf_candidates)) 

103 for input_info, input_candidate, output_candidate in zip( 

104 self.star_infos, self.extended_psf_candidates, extended_psf_candidates 

105 ): 

106 self.assertEqual(input_candidate.metadata, {}) 

107 self.assertEqual(output_candidate.metadata, {}) 

108 

109 output_info = output_candidate.star_info 

110 self.assertEqual(input_info.visit, output_info.visit) 

111 self.assertEqual(input_info.detector, output_info.detector) 

112 self.assertEqual(input_info.ref_id, output_info.ref_id) 

113 self.assertAlmostEqual(input_info.ref_mag, output_info.ref_mag, places=10) 

114 self.assertAlmostEqual(input_info.position_x, output_info.position_x, places=10) 

115 self.assertAlmostEqual(input_info.position_y, output_info.position_y, places=10) 

116 

117 self.assertIsNotNone(input_info.focal_plane_radius) 

118 self.assertIsNotNone(output_info.focal_plane_radius) 

119 input_radius = cast(u.Quantity, input_info.focal_plane_radius) 

120 output_radius = cast(u.Quantity, output_info.focal_plane_radius) 

121 self.assertAlmostEqual(input_radius.to_value(u.mm), output_radius.to_value(u.mm), places=10) 

122 

123 self.assertIsNotNone(input_info.focal_plane_angle) 

124 self.assertIsNotNone(output_info.focal_plane_angle) 

125 input_angle = cast(u.Quantity, input_info.focal_plane_angle) 

126 output_angle = cast(u.Quantity, output_info.focal_plane_angle) 

127 self.assertAlmostEqual(input_angle.to_value(u.rad), output_angle.to_value(u.rad), places=10) 

128 

129 np.testing.assert_allclose( 

130 input_candidate.image.array, output_candidate.image.array, rtol=0.0, atol=1e-10 

131 ) 

132 np.testing.assert_array_equal(input_candidate.mask.array, output_candidate.mask.array) 

133 np.testing.assert_allclose( 

134 input_candidate.variance.array, output_candidate.variance.array, rtol=0.0, atol=1e-10 

135 ) 

136 np.testing.assert_allclose( 

137 input_candidate.psf_kernel_image.array, 

138 output_candidate.psf_kernel_image.array, 

139 rtol=0.0, 

140 atol=1e-10, 

141 ) 

142 

143 

144class MemoryTester(lsst.utils.tests.MemoryTestCase): 

145 pass 

146 

147 

148def setup_module(module): 

149 lsst.utils.tests.init() 

150 

151 

152if __name__ == "__main__": 152 ↛ 153line 152 didn't jump to line 153 because the condition on line 152 was never true

153 lsst.utils.tests.init() 

154 unittest.main()