Coverage for tests/test_trailedSources.py : 30%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#
2# This file is part of meas_extensions_trailedSources.
3#
4# Developed for the LSST Data Management System.
5# This product includes software developed by the LSST Project
6# (http://www.lsst.org).
7# See the COPYRIGHT file at the top-level directory of this distribution
8# for details of code ownership.
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
24import numpy as np
25import unittest
26import lsst.utils.tests
27import lsst.meas.extensions.trailedSources
28from lsst.meas.base.tests import AlgorithmTestCase
29from lsst.utils.tests import classParameters
30from lsst.geom import Angle
32# Trailed-source length, angle, and centroid.
33rng = np.random.default_rng(432)
34nTrails = 50
35Ls = rng.uniform(2, 20, nTrails)
36thetas = rng.uniform(0, 2*np.pi, nTrails)
37xcs = rng.uniform(0, 100, nTrails)
38ycs = rng.uniform(0, 100, nTrails)
41class TrailedSource:
42 """Holds a set of true trail parameters.
43 """
45 def __init__(self, instFlux, length, angle, xc, yc):
46 self.instFlux = instFlux
47 self.length = length
48 self.angle = angle
49 self.center = lsst.geom.Point2D(xc, yc)
50 self.x0 = xc - length/2 * np.cos(angle)
51 self.y0 = yc - length/2 * np.sin(angle)
52 self.x1 = xc + length/2 * np.cos(angle)
53 self.y1 = yc + length/2 * np.sin(angle)
56# "Extend" meas.base.tests.TestDataset
57class TrailedTestDataset(lsst.meas.base.tests.TestDataset):
58 """A dataset for testing trailed source measurements.
59 Given a `TrailedSource`, construct a record of the true values and an
60 Exposure.
61 """
63 def __init__(self, bbox, threshold=10.0, exposure=None, **kwds):
64 super().__init__(bbox, threshold, exposure, **kwds)
66 def addTrailedSource(self, trail):
67 """Add a trailed source to the simulation.
68 'Re-implemented' version of
69 `lsst.meas.base.tests.TestDataset.addSource`. Numerically integrates a
70 Gaussian PSF over a line to obtain am image of a trailed source.
71 """
73 record = self.catalog.addNew()
74 record.set(self.keys["centroid"], trail.center)
75 rng = np.random.default_rng(32)
76 covariance = rng.normal(0, 0.1, 4).reshape(2, 2)
77 covariance[0, 1] = covariance[1, 0]
78 record.set(self.keys["centroid_sigma"], covariance.astype(np.float32))
79 record.set(self.keys["shape"], self.psfShape)
80 record.set(self.keys["isStar"], False)
82 # Sum the psf at each
83 numIter = int(5*trail.length)
84 xp = np.linspace(trail.x0, trail.x1, num=numIter)
85 yp = np.linspace(trail.y0, trail.y1, num=numIter)
86 for (x, y) in zip(xp, yp):
87 pt = lsst.geom.Point2D(x, y)
88 im = self.drawGaussian(self.exposure.getBBox(), trail.instFlux,
89 lsst.afw.geom.Ellipse(self.psfShape, pt))
90 self.exposure.getMaskedImage().getImage().getArray()[:, :] += im.getArray()
92 record.set(self.keys["instFlux"], self.exposure.getImage().array.sum())
93 self._installFootprint(record, self.exposure.getImage())
95 return record, self.exposure.getImage()
98# Following from meas_base/test_NaiveCentroid.py
99# Taken from NaiveCentroidTestCase
100@classParameters(length=Ls, theta=thetas, xc=xcs, yc=ycs)
101class TrailedSourcesTestCase(AlgorithmTestCase, lsst.utils.tests.TestCase):
103 def setUp(self):
104 self.center = lsst.geom.Point2D(50.1, 49.8)
105 self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(-20, -30),
106 lsst.geom.Extent2I(140, 160))
107 self.dataset = TrailedTestDataset(self.bbox)
109 self.trail = TrailedSource(100000.0, self.length, self.theta, self.xc, self.yc)
110 self.dataset.addTrailedSource(self.trail)
112 def tearDown(self):
113 del self.center
114 del self.bbox
115 del self.trail
116 del self.dataset
118 def makeTrailedSourceMeasurementTask(self, plugin=None, dependencies=(),
119 config=None, schema=None, algMetadata=None):
120 """Set up a measurement task for a trailed source plugin.
121 """
123 config = self.makeSingleFrameMeasurementConfig(plugin=plugin,
124 dependencies=dependencies)
126 # Make sure the shape slot is base_SdssShape
127 config.slots.shape = "base_SdssShape"
128 return self.makeSingleFrameMeasurementTask(plugin=plugin,
129 dependencies=dependencies,
130 config=config, schema=schema,
131 algMetadata=algMetadata)
133 def testNaivePlugin(self):
134 """Test the NaivePlugin measurements.
135 Given a `TrailedTestDataset`, run the NaivePlugin measurement and
136 compare the measured parameters to the true values.
137 """
139 # Set up and run Naive measurement.
140 task = self.makeTrailedSourceMeasurementTask(
141 plugin="ext_trailedSources_Naive",
142 dependencies=("base_SdssCentroid", "base_SdssShape")
143 )
144 exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0)
145 task.run(catalog, exposure)
146 record = catalog[0]
148 # Check that root finder converged
149 converged = record.get("ext_trailedSources_Naive_flag_noConverge")
150 self.assertFalse(converged)
152 # Compare true with measured length and angle.
153 # Accuracy is dependent on the second-moments measurements, so the
154 # rtol values are simply rough upper bounds.
155 length = record.get("ext_trailedSources_Naive_length")
156 theta = record.get("ext_trailedSources_Naive_angle")
157 self.assertFloatsAlmostEqual(length, self.trail.length, atol=None, rtol=0.1)
158 self.assertAnglesAlmostEqual(
159 Angle(theta % np.pi), Angle(self.trail.angle % np.pi),
160 maxDiff=0.05*Angle(self.trail.angle % np.pi).wrapCtr()
161 )
163 # Check test setup
164 self.assertNotEqual(length, self.trail.length)
165 self.assertNotEqual(theta, self.trail.angle)
167 # Make sure measurement flag is False
168 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
170 def testVeresPlugin(self):
171 """Test the VeresPlugin measurements.
172 Given a `TrailedTestDataset`, run the VeresPlugin measurement and
173 compare the measured parameters to the true values.
174 """
176 # Set up and run Veres measurement.
177 task = self.makeTrailedSourceMeasurementTask(
178 plugin="ext_trailedSources_Veres",
179 dependencies=("base_SdssShape", "ext_trailedSources_Naive")
180 )
181 exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0)
182 task.config.plugins['ext_trailedSources_Veres'].optimizerMethod = 'Powell'
183 task.run(catalog, exposure)
184 record = catalog[0]
186 # Make sure optmizer converged
187 converged = record.get("ext_trailedSources_Veres_flag_nonConvergence")
188 self.assertFalse(converged)
190 # Compare measured trail length and angle to true values
191 # These measurements should perform at least as well as NaivePlugin
192 length = record.get("ext_trailedSources_Veres_length")
193 theta = record.get("ext_trailedSources_Veres_angle")
194 self.assertFloatsAlmostEqual(length, self.trail.length, atol=None, rtol=0.1)
195 self.assertAnglesAlmostEqual(
196 Angle(theta % np.pi), Angle(self.trail.angle % np.pi),
197 maxDiff=0.05*Angle(self.trail.angle % np.pi).wrapCtr()
198 )
200 # Make sure test setup is working as expected
201 self.assertNotEqual(length, self.trail.length)
202 self.assertNotEqual(theta, self.trail.angle)
204 # Test that reduced chi-squared is reasonable
205 rChiSq = record.get("ext_trailedSources_Veres_rChiSq")
206 self.assertGreater(rChiSq, 0.9)
207 self.assertLess(rChiSq, 1.1)
209 # Make sure measurement flag is False
210 self.assertFalse(record.get("ext_trailedSources_Veres_flag"))
213class TestMemory(lsst.utils.tests.MemoryTestCase):
214 pass
217def setup_module(module):
218 lsst.utils.tests.init()
221if __name__ == "__main__": 221 ↛ 222line 221 didn't jump to line 222, because the condition on line 221 was never true
222 lsst.utils.tests.init()
223 unittest.main()