Coverage for tests/test_CentroidChecker.py: 27%

141 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-08 02:11 -0700

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 lsst.utils.tests 

25import lsst.geom 

26import lsst.meas.base 

27import lsst.meas.base.tests 

28from lsst.meas.base.tests import (AlgorithmTestCase) 

29import lsst.pex.config 

30from lsst.meas.base.pluginRegistry import register 

31from lsst.meas.base.sfm import SingleFramePluginConfig, SingleFramePlugin 

32from lsst.meas.base import FlagDefinitionList, FlagHandler 

33 

34 

35class CentroiderConfig(SingleFramePluginConfig): 

36 

37 moveX = lsst.pex.config.Field(dtype=int, default=0, 

38 doc="amount to re-position in X") 

39 moveY = lsst.pex.config.Field(dtype=int, default=0, 

40 doc="amount to re-position in Y") 

41 dist = lsst.pex.config.Field(dtype=int, default=0, 

42 doc="distance to allow centroid to be off") 

43 setErrors = lsst.pex.config.Field(dtype=bool, default=False, 

44 doc="set errors on measurement to errX, errY") 

45 errX = lsst.pex.config.Field(dtype=float, default=0, 

46 doc="uncertainty on X measurement") 

47 errY = lsst.pex.config.Field(dtype=float, default=0, 

48 doc="uncertainty on X measurement") 

49 

50 

51@register("test_Centroider") 

52class Centroider(SingleFramePlugin): 

53 """Sample Python measurement plugin. 

54 

55 The flag handler for this plugin is created during construction, and is 

56 called using the method `fail`. All plugins are required to implement 

57 this method, which is used to set the flags in the output source record if 

58 an error occurs. 

59 """ 

60 ConfigClass = CentroiderConfig 

61 # Class variables ErrEnum and FLAGDEFS are added by the decorator 

62 

63 @classmethod 

64 def getExecutionOrder(cls): 

65 return cls.CENTROID_ORDER 

66 

67 def __init__(self, config, name, schema, metadata): 

68 SingleFramePlugin.__init__(self, config, name, schema, metadata) 

69 

70 flagDefs = FlagDefinitionList() 

71 flagDefs.add("flag", "General Failure error") 

72 flagDefs.add("test_flag", "second flag") 

73 self.flagHandler = FlagHandler.addFields(schema, name, flagDefs) 

74 

75 if self.config.setErrors: 

76 uncertainty = lsst.meas.base.UncertaintyEnum.SIGMA_ONLY 

77 else: 

78 uncertainty = lsst.meas.base.UncertaintyEnum.NO_UNCERTAINTY 

79 

80 self.centroidKey = lsst.meas.base.CentroidResultKey.addFields(schema, name, name, uncertainty) 

81 

82 if self.config.dist is None: 

83 self.centroidChecker = lsst.meas.base.CentroidChecker(schema, name) 

84 else: 

85 self.centroidChecker = lsst.meas.base.CentroidChecker(schema, name, True, self.config.dist) 

86 

87 def measure(self, measRecord, exposure): 

88 """This measure routine moves the centroid by design to create an error. 

89 """ 

90 measRecord.set(self.centroidKey.getX(), measRecord.getX() + self.config.moveX) 

91 measRecord.set(self.centroidKey.getY(), measRecord.getY() + self.config.moveY) 

92 if self.centroidKey.getCentroidErr().isValid(): 

93 err = measRecord.get(self.centroidKey.getCentroidErr()) 

94 err[0][0] = self.config.errX 

95 err[1][1] = self.config.errY 

96 measRecord.set(self.centroidKey.getCentroidErr(), err) 

97 self.centroidChecker(measRecord) 

98 

99 def fail(self, measRecord, error=None): 

100 """Respond to measurement failures. 

101 

102 This routine responds to the standard failure call in baseMeasurement 

103 If the exception is a MeasurementError, the error will be passed to 

104 the fail method by the MeasurementFramework. 

105 """ 

106 if error is None: 

107 self.flagHandler.handleFailure(measRecord) 

108 else: 

109 self.flagHandler.handleFailure(measRecord, error.cpp) 

110 

111 

112class CentroidCheckerTestCase(AlgorithmTestCase, lsst.utils.tests.TestCase): 

113 

114 # Setup a configuration and datasource to be used by the plugin tests 

115 def setUp(self): 

116 self.algName = "test_Centroider" 

117 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(100, 100)) 

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

119 self.dataset.addSource(instFlux=1E5, centroid=lsst.geom.Point2D(25, 26)) 

120 

121 def tearDown(self): 

122 del self.dataset 

123 

124 def testNoError(self): 

125 """Test that the ``resetToPeak`` flag is not set when no error seen. 

126 """ 

127 schema = self.dataset.makeMinimalSchema() 

128 config = self.makeSingleFrameMeasurementConfig(plugin=self.algName) 

129 task = lsst.meas.base.SingleFrameMeasurementTask(schema=schema, config=config) 

130 exposure, cat = self.dataset.realize(noise=100.0, schema=schema, randomSeed=0) 

131 task.run(cat, exposure) 

132 source = cat[0] 

133 self.assertFalse(source.get("test_Centroider_flag")) 

134 self.assertFalse(source.get("test_Centroider_flag_resetToPeak")) 

135 

136 def testCheckErrors(self): 

137 """Test that centroids with invalid (NaN) errors are flagged. 

138 """ 

139 def runMeasurement(errX, errY): 

140 schema = self.dataset.makeMinimalSchema() 

141 config = self.makeSingleFrameMeasurementConfig(plugin=self.algName) 

142 config.plugins[self.algName].setErrors = True 

143 config.plugins[self.algName].errX = errX 

144 config.plugins[self.algName].errY = errY 

145 task = lsst.meas.base.SingleFrameMeasurementTask(schema=schema, config=config) 

146 exposure, cat = self.dataset.realize(noise=100.0, schema=schema, randomSeed=0) 

147 task.run(cat, exposure) 

148 return cat[0] 

149 

150 # Errors are real numbers: flags should not be set. 

151 source = runMeasurement(1.0, 1.0) 

152 self.assertFalse(source.get("test_Centroider_flag")) 

153 self.assertFalse(source.get("test_Centroider_flag_badError")) 

154 

155 # Error on X is NaN: flags should be set. 

156 source = runMeasurement(float('nan'), 1.0) 

157 self.assertTrue(source.get("test_Centroider_flag")) 

158 self.assertTrue(source.get("test_Centroider_flag_badError")) 

159 

160 # Error on Y is NaN: flags should be set. 

161 source = runMeasurement(1.0, float('nan')) 

162 self.assertTrue(source.get("test_Centroider_flag")) 

163 self.assertTrue(source.get("test_Centroider_flag_badError")) 

164 

165 # Error on both X and Y is NaN: flags should be set. 

166 source = runMeasurement(float('nan'), float('nan')) 

167 self.assertTrue(source.get("test_Centroider_flag")) 

168 self.assertTrue(source.get("test_Centroider_flag_badError")) 

169 

170 def testCentroidDistance(self): 

171 """Test that a slight centroid movement triggers the distance error. 

172 """ 

173 schema = self.dataset.makeMinimalSchema() 

174 config = self.makeSingleFrameMeasurementConfig(plugin=self.algName) 

175 config.plugins[self.algName].moveX = -2 

176 config.plugins[self.algName].dist = 1 

177 task = lsst.meas.base.SingleFrameMeasurementTask(schema=schema, config=config) 

178 exposure, cat = self.dataset.realize(noise=100.0, schema=schema, randomSeed=1) 

179 source = cat[0] 

180 task.run(cat, exposure) 

181 self.assertTrue(source.get("test_Centroider_flag")) 

182 self.assertTrue(source.get("test_Centroider_flag_resetToPeak")) 

183 self.assertEqual(source.getFootprint().getPeaks()[0].getFx(), source.get("test_Centroider_x")) 

184 

185 def testCentroidOutsideFootprint(self): 

186 """A large centroid movement should trigger a move back to first peak. 

187 """ 

188 schema = self.dataset.makeMinimalSchema() 

189 config = self.makeSingleFrameMeasurementConfig(plugin=self.algName) 

190 config.plugins[self.algName].moveX = -30 

191 task = lsst.meas.base.SingleFrameMeasurementTask(schema=schema, config=config) 

192 exposure, cat = self.dataset.realize(noise=100.0, schema=schema, randomSeed=2) 

193 source = cat[0] 

194 task.run(cat, exposure) 

195 self.assertTrue(source.get("test_Centroider_flag")) 

196 self.assertTrue(source.get("test_Centroider_flag_resetToPeak")) 

197 self.assertEqual(source.getFootprint().getPeaks()[0].getFx(), source.get("test_Centroider_x")) 

198 

199 def testNaiveCentroid(self): 

200 """Test the `NaiveCentroid` works with the ``maxDistance`` check. 

201 """ 

202 schema = self.dataset.makeMinimalSchema() 

203 self.algName = "base_NaiveCentroid" 

204 config = self.makeSingleFrameMeasurementConfig(plugin=self.algName) 

205 config.plugins[self.algName].maxDistToPeak = .0001 

206 task = lsst.meas.base.SingleFrameMeasurementTask(schema=schema, config=config) 

207 exposure, cat = self.dataset.realize(noise=100.0, schema=schema, randomSeed=3) 

208 source = cat[0] 

209 task.run(cat, exposure) 

210 self.assertTrue(source.get("base_NaiveCentroid_flag")) 

211 self.assertTrue(source.get("base_NaiveCentroid_flag_resetToPeak")) 

212 

213 def testSdssCentroid(self): 

214 """Test the `SdssCentroid` works with the ``maxDistance`` check. 

215 """ 

216 schema = self.dataset.makeMinimalSchema() 

217 self.algName = "base_SdssCentroid" 

218 config = self.makeSingleFrameMeasurementConfig(plugin=self.algName) 

219 config.plugins[self.algName].maxDistToPeak = .0001 

220 task = lsst.meas.base.SingleFrameMeasurementTask(schema=schema, config=config) 

221 exposure, cat = self.dataset.realize(noise=100.0, schema=schema, randomSeed=4) 

222 source = cat[0] 

223 task.run(cat, exposure) 

224 self.assertTrue(source.get("base_SdssCentroid_flag")) 

225 self.assertTrue(source.get("base_SdssCentroid_flag_resetToPeak")) 

226 

227 

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

229 pass 

230 

231 

232def setup_module(module): 

233 lsst.utils.tests.init() 

234 

235 

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

237 lsst.utils.tests.init() 

238 unittest.main()