Coverage for tests/test_detectAndMeasure.py: 8%
314 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-15 11:30 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-15 11:30 +0000
1# This file is part of ip_diffim.
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 numpy as np
23import unittest
25import lsst.geom
26from lsst.ip.diffim import detectAndMeasure, subtractImages
27from lsst.ip.diffim.utils import makeTestImage
28import lsst.utils.tests
31class DetectAndMeasureTestBase(lsst.utils.tests.TestCase):
33 def _check_diaSource(self, refSources, diaSource, refIds=None,
34 matchDistance=1., scale=1., usePsfFlux=True,
35 rtol=0.02, atol=None):
36 """Match a diaSource with a source in a reference catalog
37 and compare properties.
39 Parameters
40 ----------
41 refSources : `lsst.afw.table.SourceCatalog`
42 The reference catalog.
43 diaSource : `lsst.afw.table.SourceRecord`
44 The new diaSource to match to the reference catalog.
45 refIds : `list` of `int`, optional
46 Source IDs of previously associated diaSources.
47 matchDistance : `float`, optional
48 Maximum distance allowed between the detected and reference source
49 locations, in pixels.
50 scale : `float`, optional
51 Optional factor to scale the flux by before performing the test.
52 usePsfFlux : `bool`, optional
53 If set, test the PsfInstFlux field, otherwise use ApInstFlux.
54 rtol : `float`, optional
55 Relative tolerance of the flux value test.
56 atol : `float`, optional
57 Absolute tolerance of the flux value test.
58 """
59 distance = np.sqrt((diaSource.getX() - refSources.getX())**2
60 + (diaSource.getY() - refSources.getY())**2)
61 self.assertLess(min(distance), matchDistance)
62 src = refSources[np.argmin(distance)]
63 if refIds is not None:
64 # Check that the same source was not previously associated
65 self.assertNotIn(src.getId(), refIds)
66 refIds.append(src.getId())
67 if atol is None:
68 atol = rtol*src.getPsfInstFlux() if usePsfFlux else rtol*src.getApInstFlux()
69 if usePsfFlux:
70 self.assertFloatsAlmostEqual(src.getPsfInstFlux()*scale, diaSource.getPsfInstFlux(),
71 rtol=rtol, atol=atol)
72 else:
73 self.assertFloatsAlmostEqual(src.getApInstFlux()*scale, diaSource.getApInstFlux(),
74 rtol=rtol, atol=atol)
76 def _check_values(self, values, minValue=None, maxValue=None):
77 """Verify that an array has finite values, and optionally that they are
78 within specified minimum and maximum bounds.
80 Parameters
81 ----------
82 values : `numpy.ndarray`
83 Array of values to check.
84 minValue : `float`, optional
85 Minimum allowable value.
86 maxValue : `float`, optional
87 Maximum allowable value.
88 """
89 self.assertTrue(np.all(np.isfinite(values)))
90 if minValue is not None:
91 self.assertTrue(np.all(values >= minValue))
92 if maxValue is not None:
93 self.assertTrue(np.all(values <= maxValue))
95 def _setup_detection(self, doApCorr=False, doMerge=False,
96 doSkySources=False, doForcedMeasurement=False):
97 """Setup and configure the detection and measurement PipelineTask.
99 Parameters
100 ----------
101 doApCorr : `bool`, optional
102 Run subtask to apply aperture corrections.
103 doMerge : `bool`, optional
104 Merge positive and negative diaSources.
105 doSkySources : `bool`, optional
106 Generate sky sources.
107 doForcedMeasurement : `bool`, optional
108 Force photometer diaSource locations on PVI.
110 Returns
111 -------
112 `lsst.pipe.base.PipelineTask`
113 The configured Task to use for detection and measurement.
114 """
115 config = self.detectionTask.ConfigClass()
116 config.doApCorr = doApCorr
117 config.doMerge = doMerge
118 config.doSkySources = doSkySources
119 config.doForcedMeasurement = doForcedMeasurement
120 if doSkySources:
121 config.skySources.nSources = 5
122 return self.detectionTask(config=config)
125class DetectAndMeasureTest(DetectAndMeasureTestBase):
126 detectionTask = detectAndMeasure.DetectAndMeasureTask
128 def test_detection_xy0(self):
129 """Basic functionality test with non-zero x0 and y0.
130 """
131 # Set up the simulated images
132 noiseLevel = 1.
133 staticSeed = 1
134 fluxLevel = 500
135 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel, "x0": 12345, "y0": 67890}
136 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
137 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
138 difference = science.clone()
140 # Configure the detection Task
141 detectionTask = self._setup_detection()
143 # Run detection and check the results
144 output = detectionTask.run(science, matchedTemplate, difference)
145 subtractedMeasuredExposure = output.subtractedMeasuredExposure
147 self.assertImagesEqual(subtractedMeasuredExposure.image, difference.image)
149 def test_measurements_finite(self):
150 """Measured fluxes and centroids should always be finite.
151 """
152 columnNames = ["coord_ra", "coord_dec", "ip_diffim_forced_PsfFlux_instFlux"]
154 # Set up the simulated images
155 noiseLevel = 1.
156 staticSeed = 1
157 transientSeed = 6
158 xSize = 256
159 ySize = 256
160 kwargs = {"psfSize": 2.4, "x0": 0, "y0": 0,
161 "xSize": xSize, "ySize": ySize}
162 science, sources = makeTestImage(seed=staticSeed, noiseLevel=noiseLevel, noiseSeed=6,
163 nSrc=1, **kwargs)
164 matchedTemplate, _ = makeTestImage(seed=staticSeed, noiseLevel=noiseLevel/4, noiseSeed=7,
165 nSrc=1, **kwargs)
166 rng = np.random.RandomState(3)
167 xLoc = np.arange(-5, xSize+5, 10)
168 rng.shuffle(xLoc)
169 yLoc = np.arange(-5, ySize+5, 10)
170 rng.shuffle(yLoc)
171 transients, transientSources = makeTestImage(seed=transientSeed,
172 nSrc=len(xLoc), fluxLevel=1000.,
173 noiseLevel=noiseLevel, noiseSeed=8,
174 xLoc=xLoc, yLoc=yLoc,
175 **kwargs)
176 difference = science.clone()
177 difference.maskedImage -= matchedTemplate.maskedImage
178 difference.maskedImage += transients.maskedImage
180 # Configure the detection Task
181 detectionTask = self._setup_detection(doForcedMeasurement=True)
183 # Run detection and check the results
184 output = detectionTask.run(science, matchedTemplate, difference)
186 for column in columnNames:
187 self._check_values(output.diaSources[column])
188 self._check_values(output.diaSources.getX(), minValue=0, maxValue=xSize)
189 self._check_values(output.diaSources.getY(), minValue=0, maxValue=ySize)
190 self._check_values(output.diaSources.getPsfInstFlux())
192 def test_detect_transients(self):
193 """Run detection on a difference image containing transients.
194 """
195 # Set up the simulated images
196 noiseLevel = 1.
197 staticSeed = 1
198 transientSeed = 6
199 fluxLevel = 500
200 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel}
201 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
202 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
204 # Configure the detection Task
205 detectionTask = self._setup_detection()
207 # Run detection and check the results
208 def _detection_wrapper(positive=True):
209 transients, transientSources = makeTestImage(seed=transientSeed, psfSize=2.4,
210 nSrc=10, fluxLevel=1000.,
211 noiseLevel=noiseLevel, noiseSeed=8)
212 difference = science.clone()
213 difference.maskedImage -= matchedTemplate.maskedImage
214 if positive:
215 difference.maskedImage += transients.maskedImage
216 else:
217 difference.maskedImage -= transients.maskedImage
218 output = detectionTask.run(science, matchedTemplate, difference)
219 refIds = []
220 scale = 1. if positive else -1.
221 for diaSource in output.diaSources:
222 self._check_diaSource(transientSources, diaSource, refIds=refIds, scale=scale)
223 _detection_wrapper(positive=True)
224 _detection_wrapper(positive=False)
226 def test_detect_dipoles(self):
227 """Run detection on a difference image containing dipoles.
228 """
229 # Set up the simulated images
230 noiseLevel = 1.
231 staticSeed = 1
232 fluxLevel = 1000
233 fluxRange = 1.5
234 nSources = 10
235 offset = 1
236 dipoleFlag = "ip_diffim_DipoleFit_flag_classification"
237 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel, "fluxRange": fluxRange,
238 "nSrc": nSources}
239 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
240 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
241 difference = science.clone()
242 matchedTemplate.image.array[...] = np.roll(matchedTemplate.image.array[...], offset, axis=0)
243 matchedTemplate.variance.array[...] = np.roll(matchedTemplate.variance.array[...], offset, axis=0)
244 matchedTemplate.mask.array[...] = np.roll(matchedTemplate.mask.array[...], offset, axis=0)
245 difference.maskedImage -= matchedTemplate.maskedImage[science.getBBox()]
247 # Configure the detection Task
248 detectionTask = self._setup_detection()
250 # Run detection and check the results
251 output = detectionTask.run(science, matchedTemplate, difference)
252 self.assertIn(dipoleFlag, output.diaSources.schema.getNames())
253 nSourcesDet = len(sources)
254 self.assertEqual(len(output.diaSources), 2*nSourcesDet)
255 refIds = []
256 # The diaSource check should fail if we don't merge positive and negative footprints
257 for diaSource in output.diaSources:
258 with self.assertRaises(AssertionError):
259 self._check_diaSource(sources, diaSource, refIds=refIds, scale=0,
260 atol=np.sqrt(fluxRange*fluxLevel))
262 detectionTask2 = self._setup_detection(doMerge=True)
263 output2 = detectionTask2.run(science, matchedTemplate, difference)
264 self.assertEqual(len(output2.diaSources), nSourcesDet)
265 refIds = []
266 for diaSource in output2.diaSources:
267 if diaSource[dipoleFlag]:
268 self._check_diaSource(sources, diaSource, refIds=refIds, scale=0,
269 rtol=0.05, atol=None, usePsfFlux=False)
270 self.assertFloatsAlmostEqual(diaSource["ip_diffim_DipoleFit_orientation"], -90., atol=2.)
271 self.assertFloatsAlmostEqual(diaSource["ip_diffim_DipoleFit_separation"], offset, rtol=0.1)
272 else:
273 raise ValueError("DiaSource with ID %s is not a dipole!", diaSource.getId())
275 def test_sky_sources(self):
276 """Add sky sources and check that they are sufficiently far from other
277 sources and have negligible flux.
278 """
279 # Set up the simulated images
280 noiseLevel = 1.
281 staticSeed = 1
282 transientSeed = 6
283 transientFluxLevel = 1000.
284 transientFluxRange = 1.5
285 fluxLevel = 500
286 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel}
287 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
288 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
289 transients, transientSources = makeTestImage(seed=transientSeed, psfSize=2.4,
290 nSrc=10, fluxLevel=transientFluxLevel,
291 fluxRange=transientFluxRange,
292 noiseLevel=noiseLevel, noiseSeed=8)
293 difference = science.clone()
294 difference.maskedImage -= matchedTemplate.maskedImage
295 difference.maskedImage += transients.maskedImage
296 kernelWidth = np.max(science.psf.getKernel().getDimensions())//2
298 # Configure the detection Task
299 detectionTask = self._setup_detection(doSkySources=True)
301 # Run detection and check the results
302 output = detectionTask.run(science, matchedTemplate, difference)
303 skySources = output.diaSources[output.diaSources["sky_source"]]
304 self.assertEqual(len(skySources), detectionTask.config.skySources.nSources)
305 for skySource in skySources:
306 # The sky sources should not be close to any other source
307 with self.assertRaises(AssertionError):
308 self._check_diaSource(transientSources, skySource, matchDistance=kernelWidth)
309 with self.assertRaises(AssertionError):
310 self._check_diaSource(sources, skySource, matchDistance=kernelWidth)
311 # The sky sources should have low flux levels.
312 self._check_diaSource(transientSources, skySource, matchDistance=1000, scale=0.,
313 atol=np.sqrt(transientFluxRange*transientFluxLevel))
315 def _check_diaSource(self, refSources, diaSource, refIds=None,
316 matchDistance=1., scale=1., usePsfFlux=True,
317 rtol=0.02, atol=None):
318 """Match a diaSource with a source in a reference catalog
319 and compare properties.
320 """
321 distance = np.sqrt((diaSource.getX() - refSources.getX())**2
322 + (diaSource.getY() - refSources.getY())**2)
323 self.assertLess(min(distance), matchDistance)
324 src = refSources[np.argmin(distance)]
325 if refIds is not None:
326 # Check that the same source was not previously associated
327 self.assertNotIn(src.getId(), refIds)
328 refIds.append(src.getId())
329 if atol is None:
330 atol = rtol*src.getPsfInstFlux() if usePsfFlux else rtol*src.getApInstFlux()
331 if usePsfFlux:
332 self.assertFloatsAlmostEqual(src.getPsfInstFlux()*scale, diaSource.getPsfInstFlux(),
333 rtol=rtol, atol=atol)
334 else:
335 self.assertFloatsAlmostEqual(src.getApInstFlux()*scale, diaSource.getApInstFlux(),
336 rtol=rtol, atol=atol)
339class DetectAndMeasureScoreTest(DetectAndMeasureTestBase):
340 detectionTask = detectAndMeasure.DetectAndMeasureScoreTask
342 def test_detection_xy0(self):
343 """Basic functionality test with non-zero x0 and y0.
344 """
345 # Set up the simulated images
346 noiseLevel = 1.
347 staticSeed = 1
348 fluxLevel = 500
349 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel, "x0": 12345, "y0": 67890}
350 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
351 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
352 difference = science.clone()
353 subtractTask = subtractImages.AlardLuptonPreconvolveSubtractTask()
354 scienceKernel = science.psf.getKernel()
355 score = subtractTask._convolveExposure(difference, scienceKernel, subtractTask.convolutionControl)
357 # Configure the detection Task
358 detectionTask = self._setup_detection()
360 # Run detection and check the results
361 output = detectionTask.run(science, matchedTemplate, difference, score)
362 subtractedMeasuredExposure = output.subtractedMeasuredExposure
364 self.assertImagesEqual(subtractedMeasuredExposure.image, difference.image)
366 def test_measurements_finite(self):
367 """Measured fluxes and centroids should always be finite.
368 """
369 columnNames = ["coord_ra", "coord_dec", "ip_diffim_forced_PsfFlux_instFlux"]
371 # Set up the simulated images
372 noiseLevel = 1.
373 staticSeed = 1
374 transientSeed = 6
375 xSize = 256
376 ySize = 256
377 kwargs = {"psfSize": 2.4, "x0": 0, "y0": 0,
378 "xSize": xSize, "ySize": ySize}
379 science, sources = makeTestImage(seed=staticSeed, noiseLevel=noiseLevel, noiseSeed=6,
380 nSrc=1, **kwargs)
381 matchedTemplate, _ = makeTestImage(seed=staticSeed, noiseLevel=noiseLevel/4, noiseSeed=7,
382 nSrc=1, **kwargs)
383 rng = np.random.RandomState(3)
384 xLoc = np.arange(-5, xSize+5, 10)
385 rng.shuffle(xLoc)
386 yLoc = np.arange(-5, ySize+5, 10)
387 rng.shuffle(yLoc)
388 transients, transientSources = makeTestImage(seed=transientSeed,
389 nSrc=len(xLoc), fluxLevel=1000.,
390 noiseLevel=noiseLevel, noiseSeed=8,
391 xLoc=xLoc, yLoc=yLoc,
392 **kwargs)
393 difference = science.clone()
394 difference.maskedImage -= matchedTemplate.maskedImage
395 difference.maskedImage += transients.maskedImage
396 subtractTask = subtractImages.AlardLuptonPreconvolveSubtractTask()
397 scienceKernel = science.psf.getKernel()
398 score = subtractTask._convolveExposure(difference, scienceKernel, subtractTask.convolutionControl)
400 # Configure the detection Task
401 detectionTask = self._setup_detection(doForcedMeasurement=True)
403 # Run detection and check the results
404 output = detectionTask.run(science, matchedTemplate, difference, score)
406 for column in columnNames:
407 self._check_values(output.diaSources[column])
408 self._check_values(output.diaSources.getX(), minValue=0, maxValue=xSize)
409 self._check_values(output.diaSources.getY(), minValue=0, maxValue=ySize)
410 self._check_values(output.diaSources.getPsfInstFlux())
412 def test_detect_transients(self):
413 """Run detection on a difference image containing transients.
414 """
415 # Set up the simulated images
416 noiseLevel = 1.
417 staticSeed = 1
418 transientSeed = 6
419 fluxLevel = 500
420 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel}
421 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
422 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
423 scienceKernel = science.psf.getKernel()
424 subtractTask = subtractImages.AlardLuptonPreconvolveSubtractTask()
426 # Configure the detection Task
427 detectionTask = self._setup_detection()
429 # Run detection and check the results
430 def _detection_wrapper(positive=True):
431 """Simulate positive or negative transients and run detection.
433 Parameters
434 ----------
435 positive : `bool`, optional
436 If set, use positive transient sources.
437 """
438 transients, transientSources = makeTestImage(seed=transientSeed, psfSize=2.4,
439 nSrc=10, fluxLevel=1000.,
440 noiseLevel=noiseLevel, noiseSeed=8)
441 difference = science.clone()
442 difference.maskedImage -= matchedTemplate.maskedImage
443 if positive:
444 difference.maskedImage += transients.maskedImage
445 else:
446 difference.maskedImage -= transients.maskedImage
447 score = subtractTask._convolveExposure(difference, scienceKernel, subtractTask.convolutionControl)
448 output = detectionTask.run(science, matchedTemplate, difference, score)
449 refIds = []
450 scale = 1. if positive else -1.
451 for diaSource in output.diaSources:
452 self._check_diaSource(transientSources, diaSource, refIds=refIds, scale=scale)
453 _detection_wrapper(positive=True)
454 _detection_wrapper(positive=False)
456 def test_detect_dipoles(self):
457 """Run detection on a difference image containing dipoles.
458 """
459 # Set up the simulated images
460 noiseLevel = 1.
461 staticSeed = 1
462 fluxLevel = 1000
463 fluxRange = 1.5
464 nSources = 10
465 offset = 1
466 dipoleFlag = "ip_diffim_DipoleFit_flag_classification"
467 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel, "fluxRange": fluxRange,
468 "nSrc": nSources}
469 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
470 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
471 difference = science.clone()
472 # Shift the template by a pixel in order to make dipoles in the difference image.
473 matchedTemplate.image.array[...] = np.roll(matchedTemplate.image.array[...], offset, axis=0)
474 matchedTemplate.variance.array[...] = np.roll(matchedTemplate.variance.array[...], offset, axis=0)
475 matchedTemplate.mask.array[...] = np.roll(matchedTemplate.mask.array[...], offset, axis=0)
476 difference.maskedImage -= matchedTemplate.maskedImage[science.getBBox()]
477 subtractTask = subtractImages.AlardLuptonPreconvolveSubtractTask()
478 scienceKernel = science.psf.getKernel()
479 score = subtractTask._convolveExposure(difference, scienceKernel, subtractTask.convolutionControl)
481 # Configure the detection Task
482 detectionTask = self._setup_detection()
484 # Run detection and check the results
485 output = detectionTask.run(science, matchedTemplate, difference, score)
486 self.assertIn(dipoleFlag, output.diaSources.schema.getNames())
487 nSourcesDet = len(sources)
488 # Since we did not merge the dipoles, each source should result in
489 # both a positive and a negative diaSource
490 self.assertEqual(len(output.diaSources), 2*nSourcesDet)
491 refIds = []
492 # The diaSource check should fail if we don't merge positive and negative footprints
493 for diaSource in output.diaSources:
494 with self.assertRaises(AssertionError):
495 self._check_diaSource(sources, diaSource, refIds=refIds, scale=0,
496 atol=np.sqrt(fluxRange*fluxLevel))
498 detectionTask2 = self._setup_detection(doMerge=True)
499 output2 = detectionTask2.run(science, matchedTemplate, difference, score)
500 self.assertEqual(len(output2.diaSources), nSourcesDet)
501 refIds = []
502 for diaSource in output2.diaSources:
503 if diaSource[dipoleFlag]:
504 self._check_diaSource(sources, diaSource, refIds=refIds, scale=0,
505 rtol=0.05, atol=None, usePsfFlux=False)
506 self.assertFloatsAlmostEqual(diaSource["ip_diffim_DipoleFit_orientation"], -90., atol=2.)
507 self.assertFloatsAlmostEqual(diaSource["ip_diffim_DipoleFit_separation"], offset, rtol=0.1)
508 else:
509 raise ValueError("DiaSource with ID %s is not a dipole!", diaSource.getId())
511 def test_sky_sources(self):
512 """Add sky sources and check that they are sufficiently far from other
513 sources and have negligible flux.
514 """
515 # Set up the simulated images
516 noiseLevel = 1.
517 staticSeed = 1
518 transientSeed = 6
519 transientFluxLevel = 1000.
520 transientFluxRange = 1.5
521 fluxLevel = 500
522 kwargs = {"seed": staticSeed, "psfSize": 2.4, "fluxLevel": fluxLevel}
523 science, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6, **kwargs)
524 matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7, **kwargs)
525 transients, transientSources = makeTestImage(seed=transientSeed, psfSize=2.4,
526 nSrc=10, fluxLevel=transientFluxLevel,
527 fluxRange=transientFluxRange,
528 noiseLevel=noiseLevel, noiseSeed=8)
529 difference = science.clone()
530 difference.maskedImage -= matchedTemplate.maskedImage
531 difference.maskedImage += transients.maskedImage
532 subtractTask = subtractImages.AlardLuptonPreconvolveSubtractTask()
533 scienceKernel = science.psf.getKernel()
534 kernelWidth = np.max(scienceKernel.getDimensions())//2
535 score = subtractTask._convolveExposure(difference, scienceKernel, subtractTask.convolutionControl)
537 # Configure the detection Task
538 detectionTask = self._setup_detection(doSkySources=True)
540 # Run detection and check the results
541 output = detectionTask.run(science, matchedTemplate, difference, score)
542 skySources = output.diaSources[output.diaSources["sky_source"]]
543 self.assertEqual(len(skySources), detectionTask.config.skySources.nSources)
544 for skySource in skySources:
545 # The sky sources should not be close to any other source
546 with self.assertRaises(AssertionError):
547 self._check_diaSource(transientSources, skySource, matchDistance=kernelWidth)
548 with self.assertRaises(AssertionError):
549 self._check_diaSource(sources, skySource, matchDistance=kernelWidth)
550 # The sky sources should have low flux levels.
551 self._check_diaSource(transientSources, skySource, matchDistance=1000, scale=0.,
552 atol=np.sqrt(transientFluxRange*transientFluxLevel))
555def setup_module(module):
556 lsst.utils.tests.init()
559class MemoryTestCase(lsst.utils.tests.MemoryTestCase):
560 pass
563if __name__ == "__main__": 563 ↛ 564line 563 didn't jump to line 564, because the condition on line 563 was never true
564 lsst.utils.tests.init()
565 unittest.main()