Coverage for tests / test_dynamicDetection.py: 26%

108 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-20 23:47 -0800

1import unittest 

2 

3import lsst.utils.tests 

4import numpy as np 

5from lsst.afw.geom import makeCdMatrix, makeSkyWcs 

6from lsst.afw.table import SourceTable 

7from lsst.geom import Box2I, Extent2I, Point2D, Point2I, SpherePoint, degrees 

8from lsst.meas.algorithms import DynamicDetectionTask 

9from lsst.meas.algorithms.testUtils import plantSources 

10from lsst.pex.config import FieldValidationError 

11 

12 

13class DynamicDetectionTest(lsst.utils.tests.TestCase): 

14 def setUp(self): 

15 xy0 = Point2I(12345, 67890) # xy0 for image 

16 dims = Extent2I(2345, 2345) # Dimensions of image 

17 box = Box2I(xy0, dims) # Bounding box of image 

18 sigma = 3.21 # PSF sigma 

19 buffer = 4.0 # Buffer for star centers around edge 

20 nSigmaForKernel = 5.0 # Number of PSF sigmas for kernel 

21 sky = 12345.6 # Sky level 

22 numStars = 100 # Number of stars 

23 noise = np.sqrt(sky)*np.pi*sigma**2 # Poisson noise per PSF 

24 faint = 1.0*noise # Faintest level for star fluxes 

25 bright = 100.0*noise # Brightest level for star fluxes 

26 starBox = Box2I(box) # Area on image in which we can put star centers 

27 starBox.grow(-int(buffer*sigma)) 

28 scale = 1.0e-5*degrees # Pixel scale 

29 

30 rng = np.random.Generator(np.random.MT19937(12345)) 

31 stars = [(xx, yy, ff, sigma) for xx, yy, ff in 

32 zip(rng.uniform(starBox.getMinX(), starBox.getMaxX(), numStars), 

33 rng.uniform(starBox.getMinY(), starBox.getMaxY(), numStars), 

34 np.linspace(faint, bright, numStars))] 

35 self.exposure = plantSources(box, 2*int(nSigmaForKernel*sigma) + 1, sky, stars, True) 

36 self.exposure.setWcs(makeSkyWcs(crpix=Point2D(0, 0), 

37 crval=SpherePoint(0, 0, degrees), 

38 cdMatrix=makeCdMatrix(scale=scale))) 

39 

40 self.config = DynamicDetectionTask.ConfigClass() 

41 self.rtol = 0.1 

42 

43 def tearDown(self): 

44 del self.exposure 

45 

46 def check(self, expectFactor, config): 

47 schema = SourceTable.makeMinimalSchema() 

48 task = DynamicDetectionTask(config=config, schema=schema) 

49 table = SourceTable.make(schema) 

50 

51 results = task.run(table, self.exposure, expId=12345) 

52 self.assertFloatsAlmostEqual(results.factor, expectFactor, rtol=self.rtol) 

53 

54 def testVanilla(self): 

55 """Dynamic detection used as normal detection.""" 

56 self.check(1.0, self.config) 

57 

58 def testDynamic(self): 

59 """Modify the variance plane, and see if the task is able to determine 

60 what we did. 

61 """ 

62 factor = 2.0 

63 self.exposure.maskedImage.variance /= factor 

64 self.check(1.0/np.sqrt(factor), self.config) 

65 

66 def testNoWcs(self): 

67 """Check that dynamic detection runs when the exposure wcs is None.""" 

68 self.exposure.setWcs(None) 

69 self.check(1.0, self.config) 

70 

71 def testMinimalSkyObjects(self): 

72 """Check that dynamic detection runs when there are a relatively small 

73 number of sky objects. 

74 """ 

75 config = DynamicDetectionTask.ConfigClass() 

76 config.skyObjects.nSources = int(0.1 * self.config.skyObjects.nSources) 

77 self.check(1.0, config) 

78 

79 def testDynamicNoLimits(self): 

80 """Test setting the threshold scaling and background tweak limits to 

81 None (i.e. no limits imposed). 

82 """ 

83 config = DynamicDetectionTask.ConfigClass() 

84 config.minThresholdScaleFactor = None 

85 config.maxThresholdScaleFactor = None 

86 config.minBackgroundTweak = None 

87 config.maxBackgroundTweak = None 

88 self.check(1.0, config) 

89 

90 def testNoThresholdScaling(self): 

91 """Check that dynamic detection runs when doThresholdScaling is False. 

92 """ 

93 config = DynamicDetectionTask.ConfigClass() 

94 config.doThresholdScaling = False 

95 self.check(1.0, config) 

96 

97 def testNoBackgroundTweak(self): 

98 """Check that dynamic detection runs when doBackgroundTweak is False. 

99 """ 

100 config = DynamicDetectionTask.ConfigClass() 

101 config.doBackgroundTweak = False 

102 self.check(1.0, config) 

103 

104 def testThresholdScalingAndNoBackgroundTweak(self): 

105 """Check that dynamic detection runs when both doThresholdScaling and 

106 doBackgroundTweak are False. 

107 """ 

108 config = DynamicDetectionTask.ConfigClass() 

109 config.doThresholdScaling = False 

110 config.doBackgroundTweak = False 

111 self.check(1.0, config) 

112 

113 def testThresholdsOutsideBounds(self): 

114 """Check that dynamic detection properly sets threshold limits. 

115 """ 

116 schema = SourceTable.makeMinimalSchema() 

117 config = DynamicDetectionTask.ConfigClass() 

118 config.minThresholdScaleFactor = 1.05 

119 config.maxBackgroundTweak = -0.1 

120 table = SourceTable.make(schema) 

121 task = DynamicDetectionTask(config=config, schema=schema) 

122 task.run(table, self.exposure, expId=12345) 

123 

124 config = DynamicDetectionTask.ConfigClass() 

125 config.maxThresholdScaleFactor = 0.99 

126 config.minBackgroundTweak = 0.1 

127 table = SourceTable.make(schema) 

128 task = DynamicDetectionTask(config=config, schema=schema) 

129 task.run(table, self.exposure, expId=12345) 

130 

131 def testConfigValidation(self): 

132 """Check that the field validation is working correctly. 

133 """ 

134 schema = SourceTable.makeMinimalSchema() 

135 config = DynamicDetectionTask.ConfigClass() 

136 config.minThresholdScaleFactor = 1.05 

137 config.maxThresholdScaleFactor = 1.01 

138 with self.assertRaisesRegex( 

139 FieldValidationError, "minThresholdScaleFactor must be <= maxThresholdScaleFactor"): 

140 DynamicDetectionTask(config=config, schema=schema) 

141 

142 config = DynamicDetectionTask.ConfigClass() 

143 config.minBackgroundTweak = 2.0 

144 config.maxBackgroundTweak = 1.0 

145 with self.assertRaisesRegex( 

146 FieldValidationError, "minBackgroundTweak must be <= maxBackgroundTweak"): 

147 DynamicDetectionTask(config=config, schema=schema) 

148 

149 def testNoSkyObjects(self): 

150 """Check that dynamic detection runs when there are no sky objects. 

151 

152 Notes 

153 ----- 

154 This test, originally named 'testNoSources', was deactivated on 

155 DM-39809 because the dynamic detection task is not able to function 

156 without any sky objects. This test should be reactivated once DM-39826 

157 has been completed. 

158 """ 

159 pass 

160 

161 

162class TestMemory(lsst.utils.tests.MemoryTestCase): 

163 pass 

164 

165 

166def setup_module(module): 

167 lsst.utils.tests.init() 

168 

169 

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

171 import sys 

172 setup_module(sys.modules['__main__']) 

173 unittest.main()