Coverage for tests/test_CentroidChecker.py: 27%
157 statements
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-24 02:06 -0700
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-24 02:06 -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/>.
22import unittest
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
35class CentroiderConfig(SingleFramePluginConfig):
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")
51@register("test_Centroider")
52class Centroider(SingleFramePlugin):
53 """Sample Python measurement plugin.
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
63 @classmethod
64 def getExecutionOrder(cls):
65 return cls.CENTROID_ORDER
67 def __init__(self, config, name, schema, metadata):
68 SingleFramePlugin.__init__(self, config, name, schema, metadata)
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)
75 if self.config.setErrors:
76 uncertainty = lsst.meas.base.UncertaintyEnum.SIGMA_ONLY
77 else:
78 uncertainty = lsst.meas.base.UncertaintyEnum.NO_UNCERTAINTY
80 self.centroidKey = lsst.meas.base.CentroidResultKey.addFields(schema, name, name, uncertainty)
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)
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)
99 def fail(self, measRecord, error=None):
100 """Respond to measurement failures.
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)
112class CentroidCheckerTestCase(AlgorithmTestCase, lsst.utils.tests.TestCase):
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))
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
136 def tearDown(self):
137 del self.dataset
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"))
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]
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"))
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"))
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"))
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"))
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"))
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"))
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"))
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"))
245class TestMemory(lsst.utils.tests.MemoryTestCase):
246 pass
249def setup_module(module):
250 lsst.utils.tests.init()
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()