Coverage for tests/test_CentroidChecker.py: 27%

157 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-28 02:19 -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, optional=False, 

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

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

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

41 dist = lsst.pex.config.Field(dtype=int, default=None, optional=False, 

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

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

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

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

46 doc="uncertainty on X measurement") 

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

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 makeConfig(self, algName=None): 

122 if algName is None: 

123 algName = self.algName 

124 config = lsst.meas.base.SingleFrameMeasurementConfig() 

125 config.plugins = [algName] 

126 config.slots.centroid = None 

127 config.slots.apFlux = None 

128 config.slots.calibFlux = None 

129 config.slots.gaussianFlux = None 

130 config.slots.modelFlux = None 

131 config.slots.psfFlux = None 

132 config.slots.shape = None 

133 config.slots.psfShape = None 

134 return config 

135 

136 def tearDown(self): 

137 del self.dataset 

138 

139 def testNoError(self): 

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

141 """ 

142 schema = self.dataset.makeMinimalSchema() 

143 config = self.makeConfig() 

144 config.slots.centroid = "truth" 

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 source = cat[0] 

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

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

151 

152 def testCheckErrors(self): 

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

154 """ 

155 def runMeasurement(errX, errY): 

156 schema = self.dataset.makeMinimalSchema() 

157 config = self.makeConfig() 

158 config.slots.centroid = "truth" 

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

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

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

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

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

164 task.run(cat, exposure) 

165 return cat[0] 

166 

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

168 source = runMeasurement(1.0, 1.0) 

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

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

171 

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

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

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

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

176 

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

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

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

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

181 

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

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

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

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

186 

187 def testCentroidDistance(self): 

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

189 """ 

190 schema = self.dataset.makeMinimalSchema() 

191 config = self.makeConfig() 

192 config.slots.centroid = "truth" 

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

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

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

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

197 source = cat[0] 

198 task.run(cat, exposure) 

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

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

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

202 

203 def testCentroidOutsideFootprint(self): 

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

205 """ 

206 schema = self.dataset.makeMinimalSchema() 

207 config = self.makeConfig() 

208 config.slots.centroid = "truth" 

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

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

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

212 source = cat[0] 

213 task.run(cat, exposure) 

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

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

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

217 

218 def testNaiveCentroid(self): 

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

220 """ 

221 schema = self.dataset.makeMinimalSchema() 

222 config = self.makeConfig("base_NaiveCentroid") 

223 config.plugins["base_NaiveCentroid"].maxDistToPeak = .0001 

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

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

226 source = cat[0] 

227 task.run(cat, exposure) 

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

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

230 

231 def testSdssCentroid(self): 

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

233 """ 

234 schema = self.dataset.makeMinimalSchema() 

235 config = self.makeConfig("base_SdssCentroid") 

236 config.plugins["base_SdssCentroid"].maxDistToPeak = .0001 

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

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

239 source = cat[0] 

240 task.run(cat, exposure) 

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

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

243 

244 

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

246 pass 

247 

248 

249def setup_module(module): 

250 lsst.utils.tests.init() 

251 

252 

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

254 lsst.utils.tests.init() 

255 unittest.main()