Coverage for tests / test_dynamicDetection.py: 26%
114 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:26 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:26 +0000
1import unittest
3import lsst.utils.tests
4import numpy as np
5from lsst.afw.geom import makeCdMatrix, makeSkyWcs
6from lsst.afw.table import SourceTable
7from lsst.geom import Box2I, Extent2I, Point2D, Point2I, SpherePoint, degrees
8from lsst.meas.algorithms import DynamicDetectionTask, InsufficientSourcesError
9from lsst.meas.algorithms.testUtils import plantSources
10from lsst.pex.config import FieldValidationError
13class DynamicDetectionTest(lsst.utils.tests.TestCase):
14 def setUp(self):
15 xy0 = Point2I(12345, 67890) # xy0 for image
16 dims = Extent2I(2345, 2345) # Dimensions of image
17 box = Box2I(xy0, dims) # Bounding box of image
18 sigma = 3.21 # PSF sigma
19 buffer = 4.0 # Buffer for star centers around edge
20 nSigmaForKernel = 5.0 # Number of PSF sigmas for kernel
21 sky = 12345.6 # Sky level
22 numStars = 100 # Number of stars
23 noise = np.sqrt(sky)*np.pi*sigma**2 # Poisson noise per PSF
24 faint = 1.0*noise # Faintest level for star fluxes
25 bright = 100.0*noise # Brightest level for star fluxes
26 starBox = Box2I(box) # Area on image in which we can put star centers
27 starBox.grow(-int(buffer*sigma))
28 scale = 1.0e-5*degrees # Pixel scale
30 rng = np.random.Generator(np.random.MT19937(12345))
31 stars = [(xx, yy, ff, sigma) for xx, yy, ff in
32 zip(rng.uniform(starBox.getMinX(), starBox.getMaxX(), numStars),
33 rng.uniform(starBox.getMinY(), starBox.getMaxY(), numStars),
34 np.linspace(faint, bright, numStars))]
35 self.exposure = plantSources(box, 2*int(nSigmaForKernel*sigma) + 1, sky, stars, True)
36 self.exposure.setWcs(makeSkyWcs(crpix=Point2D(0, 0),
37 crval=SpherePoint(0, 0, degrees),
38 cdMatrix=makeCdMatrix(scale=scale)))
40 self.config = DynamicDetectionTask.ConfigClass()
41 self.rtol = 0.1
43 def tearDown(self):
44 del self.exposure
46 def check(self, expectFactor, config):
47 schema = SourceTable.makeMinimalSchema()
48 task = DynamicDetectionTask(config=config, schema=schema)
49 table = SourceTable.make(schema)
51 results = task.run(table, self.exposure, expId=12345)
52 self.assertFloatsAlmostEqual(results.factor, expectFactor, rtol=self.rtol)
54 def testVanilla(self):
55 """Dynamic detection used as normal detection."""
56 self.check(1.0, self.config)
58 def testDynamic(self):
59 """Modify the variance plane, and see if the task is able to determine
60 what we did.
61 """
62 factor = 2.0
63 self.exposure.maskedImage.variance /= factor
64 self.check(1.0/np.sqrt(factor), self.config)
66 def testNoWcs(self):
67 """Check that dynamic detection runs when the exposure wcs is None."""
68 self.exposure.setWcs(None)
69 self.check(1.0, self.config)
71 def testMinimalSkyObjects(self):
72 """Check that dynamic detection runs when there are a relatively small
73 number of sky objects.
74 """
75 config = DynamicDetectionTask.ConfigClass()
76 config.skyObjects.nSources = int(0.1 * self.config.skyObjects.nSources)
77 self.check(1.0, config)
79 def testDynamicNoLimits(self):
80 """Test setting the threshold scaling and background tweak limits to
81 None (i.e. no limits imposed).
82 """
83 config = DynamicDetectionTask.ConfigClass()
84 config.minThresholdScaleFactor = None
85 config.maxThresholdScaleFactor = None
86 config.minBackgroundTweak = None
87 config.maxBackgroundTweak = None
88 self.check(1.0, config)
90 def testNoThresholdScaling(self):
91 """Check that dynamic detection runs when doThresholdScaling is False.
92 """
93 config = DynamicDetectionTask.ConfigClass()
94 config.doThresholdScaling = False
95 self.check(1.0, config)
97 def testNoBackgroundTweak(self):
98 """Check that dynamic detection runs when doBackgroundTweak is False.
99 """
100 config = DynamicDetectionTask.ConfigClass()
101 config.doBackgroundTweak = False
102 self.check(1.0, config)
104 def testThresholdScalingAndNoBackgroundTweak(self):
105 """Check that dynamic detection runs when both doThresholdScaling and
106 doBackgroundTweak are False.
107 """
108 config = DynamicDetectionTask.ConfigClass()
109 config.doThresholdScaling = False
110 config.doBackgroundTweak = False
111 self.check(1.0, config)
113 def testBrightDetectionPass(self):
114 """Check that the maximum number of bright detection iterations is
115 observed.
117 The bright detection loop is forced by setting config.brightMultiplier
118 so low such that the entire image is marked as DETECTED. As such, the
119 task is doomed to fail where it tries to lay down sky sources."
120 """
121 config = DynamicDetectionTask.ConfigClass()
122 config.brightMultiplier = 0.05
123 config.brightDetectionIterMax = 2
124 with self.assertRaisesRegex(
125 InsufficientSourcesError, "Insufficient good sky source flux measurements"):
126 self.check(1.0, config)
128 def testThresholdsOutsideBounds(self):
129 """Check that dynamic detection properly sets threshold limits.
130 """
131 schema = SourceTable.makeMinimalSchema()
132 config = DynamicDetectionTask.ConfigClass()
133 config.minThresholdScaleFactor = 1.05
134 config.maxBackgroundTweak = -0.1
135 table = SourceTable.make(schema)
136 task = DynamicDetectionTask(config=config, schema=schema)
137 task.run(table, self.exposure, expId=12345)
139 config = DynamicDetectionTask.ConfigClass()
140 config.maxThresholdScaleFactor = 0.99
141 config.minBackgroundTweak = 0.1
142 table = SourceTable.make(schema)
143 task = DynamicDetectionTask(config=config, schema=schema)
144 task.run(table, self.exposure, expId=12345)
146 def testConfigValidation(self):
147 """Check that the field validation is working correctly.
148 """
149 schema = SourceTable.makeMinimalSchema()
150 config = DynamicDetectionTask.ConfigClass()
151 config.minThresholdScaleFactor = 1.05
152 config.maxThresholdScaleFactor = 1.01
153 with self.assertRaisesRegex(
154 FieldValidationError, "minThresholdScaleFactor must be <= maxThresholdScaleFactor"):
155 DynamicDetectionTask(config=config, schema=schema)
157 config = DynamicDetectionTask.ConfigClass()
158 config.minBackgroundTweak = 2.0
159 config.maxBackgroundTweak = 1.0
160 with self.assertRaisesRegex(
161 FieldValidationError, "minBackgroundTweak must be <= maxBackgroundTweak"):
162 DynamicDetectionTask(config=config, schema=schema)
164 def testNoSkyObjects(self):
165 """Check that dynamic detection runs when there are no sky objects.
167 Notes
168 -----
169 This test, originally named 'testNoSources', was deactivated on
170 DM-39809 because the dynamic detection task is not able to function
171 without any sky objects. This test should be reactivated once DM-39826
172 has been completed.
173 """
174 pass
177class TestMemory(lsst.utils.tests.MemoryTestCase):
178 pass
181def setup_module(module):
182 lsst.utils.tests.init()
185if __name__ == "__main__": 185 ↛ 186line 185 didn't jump to line 186 because the condition on line 185 was never true
186 import sys
187 setup_module(sys.modules['__main__'])
188 unittest.main()