Coverage for tests/test_SdssCentroid.py: 22%

115 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-09 03:19 -0800

1# This file is part of meas_base. 

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 

23 

24import numpy as np 

25 

26import lsst.geom 

27from lsst.meas.base.tests import (AlgorithmTestCase, CentroidTransformTestCase, 

28 SingleFramePluginTransformSetupHelper) 

29import lsst.afw.geom 

30import lsst.utils.tests 

31 

32# N.B. Some tests here depend on the noise realization in the test data 

33# or from the numpy random number generator. 

34# For the current test data and seed value, they pass, but they may not 

35# if the test data is regenerated or the seed value changes. I've marked 

36# these with an "rng dependent" comment. In most cases, they test that 

37# the measured instFlux lies within 2 sigma of the correct value, which we 

38# should expect to fail sometimes. 

39 

40 

41class SdssCentroidTestCase(AlgorithmTestCase, lsst.utils.tests.TestCase): 

42 

43 def setUp(self): 

44 self.center = lsst.geom.Point2D(50.1, 49.8) 

45 self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(-20, -30), 

46 lsst.geom.Extent2I(140, 160)) 

47 self.dataset = lsst.meas.base.tests.TestDataset(self.bbox) 

48 self.dataset.addSource(100000.0, self.center) 

49 

50 def tearDown(self): 

51 

52 del self.center 

53 del self.bbox 

54 del self.dataset 

55 

56 def makeAlgorithm(self, ctrl=None): 

57 """Construct an algorithm and return both it and its schema. 

58 """ 

59 if ctrl is None: 

60 ctrl = lsst.meas.base.SdssCentroidControl() 

61 schema = lsst.meas.base.tests.TestDataset.makeMinimalSchema() 

62 algorithm = lsst.meas.base.SdssCentroidAlgorithm(ctrl, "base_SdssCentroid", schema) 

63 return algorithm, schema 

64 

65 def testSingleFramePlugin(self): 

66 """Test calling the algorithm through the plugin interface. 

67 """ 

68 task = self.makeSingleFrameMeasurementTask("base_SdssCentroid") 

69 exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0) 

70 task.run(catalog, exposure) 

71 record = catalog[0] 

72 self.assertFalse(record.get("base_SdssCentroid_flag")) 

73 self.assertFalse(record.get("base_SdssCentroid_flag_edge")) 

74 self.assertFloatsAlmostEqual(record.get("base_SdssCentroid_x"), record.get("truth_x"), rtol=0.005) 

75 self.assertFloatsAlmostEqual(record.get("base_SdssCentroid_y"), record.get("truth_y"), rtol=0.005) 

76 

77 def testMonteCarlo(self): 

78 """Test an ideal simulation, with no noise. 

79 

80 Demonstrate that: 

81 

82 - We get exactly the right answer, and 

83 - The reported uncertainty agrees with a Monte Carlo test of the noise. 

84 """ 

85 algorithm, schema = self.makeAlgorithm() 

86 exposure, catalog = self.dataset.realize(0.0, schema, randomSeed=1) 

87 record = catalog[0] 

88 x = record.get("truth_x") 

89 y = record.get("truth_y") 

90 instFlux = record.get("truth_instFlux") 

91 algorithm.measure(record, exposure) 

92 self.assertFloatsAlmostEqual(record.get("base_SdssCentroid_x"), x, rtol=1E-4) 

93 self.assertFloatsAlmostEqual(record.get("base_SdssCentroid_y"), y, rtol=1E-4) 

94 for noise in (0.001, 0.01): 

95 xList = [] 

96 yList = [] 

97 xErrList = [] 

98 yErrList = [] 

99 nSamples = 1000 

100 for repeat in range(nSamples): 

101 # By using ``repeat`` to seed the RNG, we get results which 

102 # fall within the tolerances defined below. If we allow this 

103 # test to be truly random, passing becomes RNG-dependent. 

104 exposure, catalog = self.dataset.realize(noise*instFlux, schema, randomSeed=repeat) 

105 record = catalog[0] 

106 algorithm.measure(record, exposure) 

107 xList.append(record.get("base_SdssCentroid_x")) 

108 yList.append(record.get("base_SdssCentroid_y")) 

109 xErrList.append(record.get("base_SdssCentroid_xErr")) 

110 yErrList.append(record.get("base_SdssCentroid_yErr")) 

111 xMean = np.mean(xList) 

112 yMean = np.mean(yList) 

113 xErrMean = np.mean(xErrList) 

114 yErrMean = np.mean(yErrList) 

115 xStandardDeviation = np.std(xList) 

116 yStandardDeviation = np.std(yList) 

117 self.assertFloatsAlmostEqual(xErrMean, xStandardDeviation, rtol=0.2) # rng dependent 

118 self.assertFloatsAlmostEqual(yErrMean, yStandardDeviation, rtol=0.2) # rng dependent 

119 self.assertLess(abs(xMean - x), 3.0*xErrMean / nSamples**0.5) # rng dependent 

120 self.assertLess(abs(yMean - y), 3.0*yErrMean / nSamples**0.5) # rng dependent 

121 

122 def testEdge(self): 

123 task = self.makeSingleFrameMeasurementTask("base_SdssCentroid") 

124 exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=2) 

125 psfImage = exposure.getPsf().computeImage(self.center) 

126 # construct a box that won't fit the full PSF model 

127 bbox = psfImage.getBBox() 

128 bbox.grow(-5) 

129 subImage = lsst.afw.image.ExposureF(exposure, bbox) 

130 # we also need to install a smaller footprint, or NoiseReplacer 

131 # complains before we even get to measuring the centroid 

132 record = catalog[0] 

133 spanSet = lsst.afw.geom.SpanSet(bbox) 

134 newFootprint = lsst.afw.detection.Footprint(spanSet) 

135 peak = record.getFootprint().getPeaks()[0] 

136 newFootprint.addPeak(peak.getFx(), peak.getFy(), peak.getPeakValue()) 

137 record.setFootprint(newFootprint) 

138 # just measure the one object we've prepared for 

139 task.measure(catalog, subImage) 

140 self.assertTrue(record.get("base_SdssCentroid_flag")) 

141 self.assertTrue(record.get("base_SdssCentroid_flag_edge")) 

142 

143 def testNo2ndDerivative(self): 

144 task = self.makeSingleFrameMeasurementTask("base_SdssCentroid") 

145 exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=3) 

146 # cutout a subimage around object in the test image 

147 bbox = lsst.geom.Box2I(lsst.geom.Point2I(self.center), lsst.geom.Extent2I(1, 1)) 

148 bbox.grow(20) 

149 subImage = lsst.afw.image.ExposureF(exposure, bbox) 

150 # A completely flat image will trigger the no 2nd derivative error 

151 subImage.getMaskedImage().getImage().getArray()[:] = 0 

152 task.measure(catalog, subImage) 

153 self.assertTrue(catalog[0].get("base_SdssCentroid_flag")) 

154 self.assertTrue(catalog[0].get("base_SdssCentroid_flag_noSecondDerivative")) 

155 

156 def testNotAtMaximum(self): 

157 task = self.makeSingleFrameMeasurementTask("base_SdssCentroid") 

158 exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=4) 

159 # cutout a subimage around the object in the test image 

160 bbox = lsst.geom.Box2I(lsst.geom.Point2I(self.center), lsst.geom.Extent2I(1, 1)) 

161 bbox.grow(20) 

162 subImage = lsst.afw.image.ExposureF(exposure, bbox) 

163 # zero out the central region, which will destroy the maximum 

164 subImage.getMaskedImage().getImage().getArray()[18:22, 18:22] = 0 

165 task.measure(catalog, subImage) 

166 self.assertTrue(catalog[0].get("base_SdssCentroid_flag")) 

167 self.assertTrue(catalog[0].get("base_SdssCentroid_flag_notAtMaximum")) 

168 

169 

170class SdssCentroidTransformTestCase(CentroidTransformTestCase, 

171 SingleFramePluginTransformSetupHelper, 

172 lsst.utils.tests.TestCase): 

173 controlClass = lsst.meas.base.SdssCentroidControl 

174 algorithmClass = lsst.meas.base.SdssCentroidAlgorithm 

175 transformClass = lsst.meas.base.SdssCentroidTransform 

176 flagNames = ('flag', 'flag_edge', 'flag_badData') 

177 singleFramePlugins = ('base_SdssCentroid',) 

178 forcedPlugins = ('base_SdssCentroid',) 

179 

180 

181class TestMemory(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()