Coverage for tests/test_matchSourceInjected.py: 22%
82 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-12 07:55 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-12 07:55 +0000
1#
2# This file is part of ap_pipe.
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
27from astropy.table import Table
28from lsst.afw.table import SourceCatalog, SourceTable
29from lsst.afw.image import ExposureF
30from lsst.afw.geom import makeSkyWcs
31from lsst.geom import SpherePoint, degrees, Point2D, Extent2I
32import lsst.utils.tests
34from lsst.pipe.tasks.matchDiffimSourceInjected import (
35 MatchInjectedToDiaSourceTask,
36 MatchInjectedToAssocDiaSourceTask,
37 MatchInjectedToDiaSourceConfig,
38 MatchInjectedToAssocDiaSourceConfig,
39)
42class BaseTestMatchInjected(lsst.utils.tests.TestCase):
43 def setUp(self):
44 rng = np.random.RandomState(6)
45 # 0.35 arcsec = 0.5/np.sqrt(2) arcsec (half of the diagonal of a 0.5 arcsec square)
46 offsetFactor = 1./3600 * 0.35
48 # Create a mock injected catalog
49 self.injectedCat = Table(
50 {
51 "injection_id": [1, 2, 3, 5, 6, 7, 12, 21, 31, 49],
52 "injection_flag": np.repeat(0, 10),
53 # random positions with 10 arcmin size
54 # ra centered at 0, dec centered at -30
55 "ra": (1/6.) * rng.random(size=10),
56 "dec": -30 + (1/6.) * rng.random(size=10),
57 "mag": 20.25 - 0.5 * rng.random(size=10), # random magnitudes
58 "source_type": np.repeat("DeltaFunction", 10)
59 }
60 )
62 # Create a mock diaSources catalog
63 schema = SourceTable.makeMinimalSchema()
64 self.diaSources = SourceCatalog(schema)
65 for i in range(5):
66 record = self.diaSources.addNew()
67 record.setId(100 + i)
68 record.setCoord(
69 # Random posisions of diaSources
70 SpherePoint((1/6.) * rng.random(), -30 + (1/6.) * rng.random(), degrees)
71 )
73 for i, (ra, dec) in enumerate(self.injectedCat[['ra', 'dec']][:8]):
74 record = self.diaSources.addNew()
75 sign = rng.choice([-1, 1], size=2)
76 record.setId(i + 200)
77 record.setCoord(
78 SpherePoint(
79 ra+sign[0]*rng.random()*offsetFactor,
80 dec+sign[1]*rng.random()*offsetFactor,
81 degrees
82 )
83 )
85 # Create a mock difference image
86 self.diffIm = ExposureF(Extent2I(4096, 4096))
87 crpix = Point2D(0, 0)
88 crval = SpherePoint(0, -30, degrees)
89 cdMatrix = np.array([[5.19513851e-05, 2.81124812e-07],
90 [3.25186974e-07, 5.19112119e-05]])
91 wcs = makeSkyWcs(crpix, crval, cdMatrix)
92 self.diffIm.setWcs(wcs)
94 # add a fake source outside of the image
95 self.injectedCatForTrimming = self.injectedCat.copy()
96 self.injectedCatForTrimming.add_row(
97 {
98 'injection_id': 50,
99 'injection_flag': 0,
100 'ra': 360. - 0.1/6.,
101 'dec': -30 - 0.1/6.,
102 'mag': 20.0,
103 'source_type': 'DeltaFunction'
104 }
105 )
107 # only 4 injected sources are associated
108 self.assocDiaSources = Table(
109 {
110 "diaSourceId": [101, 102, 103, 201, 202, 205, 207],
111 "band": np.repeat("r", 7),
112 "visit": np.repeat(410001, 7),
113 "detector": np.repeat(0, 7),
114 "diaObjectId": np.arange(7),
115 }
116 )
119class TestMatchInjectedToDiaSourceTask(BaseTestMatchInjected):
121 def test_run(self):
122 config = MatchInjectedToDiaSourceConfig()
123 config.matchDistanceArcseconds = 0.5
124 config.doMatchVisit = False
125 config.doForcedMeasurement = False
127 task = MatchInjectedToDiaSourceTask(config=config)
129 result = task.run(self.injectedCat, self.diffIm, self.diaSources)
130 self.assertEqual(len(result.matchDiaSources), len(self.injectedCat))
131 self.assertEqual(np.sum(result.matchDiaSources['diaSourceId'] > 0), 8)
132 self.assertEqual(np.sum(result.matchDiaSources['dist_diaSrc'] > 0), 8)
133 self.assertEqual(
134 np.sum(np.abs(result.matchDiaSources['dist_diaSrc']) < config.matchDistanceArcseconds), 8
135 )
137 def test_run_trimming(self):
138 config = MatchInjectedToDiaSourceConfig()
139 config.matchDistanceArcseconds = 0.5
140 config.doMatchVisit = True
141 config.doForcedMeasurement = False
143 task = MatchInjectedToDiaSourceTask(config=config)
144 result = task.run(self.injectedCatForTrimming, self.diffIm, self.diaSources)
146 self.assertEqual(len(result.matchDiaSources), len(self.injectedCatForTrimming) - 1)
147 self.assertEqual(np.sum(result.matchDiaSources['diaSourceId'] > 0), 8)
148 self.assertEqual(np.sum(result.matchDiaSources['dist_diaSrc'] > 0), 8)
149 self.assertEqual(
150 np.sum(np.abs(result.matchDiaSources['dist_diaSrc']) < config.matchDistanceArcseconds), 8
151 )
153 def test_getVectors(self):
154 config = MatchInjectedToDiaSourceConfig()
155 config.matchDistanceArcseconds = 0.5
156 config.doMatchVisit = False
157 config.doForcedMeasurement = False
159 task = MatchInjectedToDiaSourceTask(config=config)
161 ras = np.radians(self.injectedCat['ra'])
162 decs = np.radians(self.injectedCat['dec'])
163 vectors = task._getVectors(ras, decs)
164 self.assertEqual(vectors.shape, (10, 3))
167class TestMatchInjectedToAssocDiaSourceTask(BaseTestMatchInjected):
169 def test_run(self):
170 config = MatchInjectedToDiaSourceConfig()
171 config.matchDistanceArcseconds = 0.5
172 config.doMatchVisit = False
173 config.doForcedMeasurement = False
175 task = MatchInjectedToDiaSourceTask(config=config)
177 result = task.run(self.injectedCat, self.diffIm, self.diaSources)
179 configAssoc = MatchInjectedToAssocDiaSourceConfig()
180 taskAssoc = MatchInjectedToAssocDiaSourceTask(config=configAssoc)
181 resultAssoc = taskAssoc.run(self.assocDiaSources, result.matchDiaSources)
182 self.assertEqual(len(resultAssoc.matchAssocDiaSources), len(self.injectedCat))
183 self.assertEqual(np.sum(resultAssoc.matchAssocDiaSources['isAssocDiaSource']), 4)
186if __name__ == "__main__": 186 ↛ 187line 186 didn't jump to line 187 because the condition on line 186 was never true
187 unittest.main()