Coverage for tests/test_inject_engine.py: 33%

65 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-11 09:21 +0000

1# This file is part of source_injection. 

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 logging 

23import unittest 

24from types import GeneratorType 

25 

26import galsim 

27import numpy as np 

28from galsim import BoundsI, GSObject 

29 

30import lsst.utils.tests 

31from lsst.geom import Point2D, SpherePoint, degrees 

32from lsst.source.injection.inject_engine import ( 

33 generate_galsim_objects, 

34 infer_gain_from_image, 

35 inject_galsim_objects_into_exposure, 

36 make_galsim_object, 

37) 

38from lsst.source.injection.utils.test_utils import make_test_exposure, make_test_injection_catalog 

39from lsst.utils.tests import TestCase 

40 

41 

42class InjectEngineTestCase(TestCase): 

43 """Test the inject_engine.py module.""" 

44 

45 def setUp(self): 

46 """Set up synthetic source injection inputs. 

47 

48 This method sets up a noisy synthetic image with Gaussian PSFs injected 

49 into the frame, an example source injection catalog, and a generator of 

50 GalSim objects intended for injection. 

51 """ 

52 self.exposure = make_test_exposure() 

53 self.injection_catalog = make_test_injection_catalog( 

54 self.exposure.getWcs(), 

55 self.exposure.getBBox(), 

56 ) 

57 self.galsim_objects = generate_galsim_objects( 

58 injection_catalog=self.injection_catalog, 

59 photo_calib=self.exposure.photoCalib, 

60 wcs=self.exposure.wcs, 

61 fits_alignment="wcs", 

62 stamp_prefix="", 

63 ) 

64 

65 def tearDown(self): 

66 del self.exposure 

67 del self.injection_catalog 

68 del self.galsim_objects 

69 

70 def test_make_galsim_object(self): 

71 source_data = self.injection_catalog[0] 

72 sky_coords = SpherePoint(float(source_data["ra"]), float(source_data["dec"]), degrees) 

73 pixel_coords = self.exposure.wcs.skyToPixel(sky_coords) 

74 inst_flux = self.exposure.photoCalib.magnitudeToInstFlux(source_data["mag"], pixel_coords) 

75 object = make_galsim_object( 

76 source_data=source_data, 

77 source_type=source_data["source_type"], 

78 inst_flux=inst_flux, 

79 ) 

80 self.assertIsInstance(object, GSObject) 

81 self.assertIsInstance(object, getattr(galsim, source_data["source_type"])) 

82 

83 def test_generate_galsim_objects(self): 

84 self.assertTrue(isinstance(self.galsim_objects, GeneratorType)) 

85 for galsim_object in self.galsim_objects: 

86 self.assertIsInstance(galsim_object, tuple) 

87 self.assertEqual(len(galsim_object), 4) 

88 self.assertIsInstance(galsim_object[0], SpherePoint) # RA/Dec 

89 self.assertIsInstance(galsim_object[1], Point2D) # x/y 

90 self.assertIsInstance(galsim_object[2], int) # draw size 

91 self.assertIsInstance(galsim_object[3], GSObject) # GSObject 

92 

93 def test_infer_gain_nonexistent_mask(self): 

94 """Test that infer_gain_from_image returns a gain value and logs a 

95 warning when provided with nonexistent mask plane names. 

96 """ 

97 logger = logging.getLogger(__name__) 

98 with self.assertLogs(logger, level="WARNING") as cm: 

99 gain = infer_gain_from_image( 

100 self.exposure, 

101 bad_mask_names=["NONEXISTENT_MASK1", "NONEXISTENT_MASK2"], 

102 logger=logger, 

103 ) 

104 self.assertTrue(any("NONEXISTENT_MASK1" in msg for msg in cm.output)) 

105 self.assertIsInstance(gain, float) 

106 self.assertTrue(np.isfinite(gain)) 

107 gain_no_mask = infer_gain_from_image(self.exposure, bad_mask_names=[]) 

108 self.assertAlmostEqual(gain, gain_no_mask) 

109 

110 def test_inject_galsim_objects_into_exposure(self): 

111 flux0 = np.sum(self.exposure.image.array) 

112 injected_outputs = inject_galsim_objects_into_exposure( 

113 exposure=self.exposure, 

114 objects=self.galsim_objects, 

115 mask_plane_name="INJECTED", 

116 calib_flux_radius=12.0, 

117 draw_size_max=1000, 

118 add_noise=False, 

119 ) 

120 pc = self.exposure.getPhotoCalib() 

121 inst_fluxes = [float(pc.magnitudeToInstFlux(mag)) for mag in self.injection_catalog["mag"]] 

122 self.assertAlmostEqual( 

123 np.sum(self.exposure.image.array) - flux0, 

124 np.sum(inst_fluxes), 

125 delta=0.00015 * np.sum(inst_fluxes), 

126 ) 

127 self.assertEqual(len(injected_outputs[0]), len(self.injection_catalog["ra"])) 

128 self.assertTrue(all(isinstance(injected_output, list) for injected_output in injected_outputs)) 

129 self.assertTrue(all(isinstance(item, int) for item in injected_outputs[0])) # draw sizes 

130 self.assertTrue(all(isinstance(item, BoundsI) for item in injected_outputs[1])) # common bounds 

131 self.assertTrue(all(isinstance(item, bool) for item in injected_outputs[2])) # FFT size errors 

132 self.assertTrue(all(isinstance(item, bool) for item in injected_outputs[3])) # PSF compute errors 

133 

134 

135class MemoryTestCase(lsst.utils.tests.MemoryTestCase): 

136 """Test memory usage of functions in this script.""" 

137 

138 pass 

139 

140 

141def setup_module(module): 

142 """Configure pytest.""" 

143 lsst.utils.tests.init() 

144 

145 

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

147 lsst.utils.tests.init() 

148 unittest.main()