Coverage for tests/test_catalogMatch.py: 29%
101 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-06 10:00 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-06 10:00 +0000
1# This file is part of analysis_tools.
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 astropy.units as u
25import lsst.afw.table as afwTable
26import lsst.geom
27import lsst.skymap
28import numpy as np
29import pandas as pd
30from lsst.analysis.tools.tasks import CatalogMatchTask
31from lsst.daf.base import PropertyList
32from lsst.daf.butler import DatasetRef, DatasetType, DimensionUniverse, StorageClass
33from lsst.meas.algorithms import ReferenceObjectLoader
36class MockSourceTableRef:
37 """Replicate functionality of `DeferredDatasetHandle`"""
39 def __init__(self, source_table, ref=None, dataId=None):
40 self.source_table = source_table
41 self.ref = ref
42 self.dataId = dataId
44 def get(self, parameters={}, **kwargs):
45 """Retrieve the specified dataset using the API of the Gen3 Butler.
46 Parameters
47 ----------
48 parameters : `dict`, optional
49 Parameter dictionary. Supported key is ``columns``.
50 Returns
51 -------
52 dataframe : `pandas.DataFrame`
53 dataframe, cut to the specified columns.
54 """
55 if "columns" in parameters:
56 _columns = parameters["columns"]
57 return self.source_table[_columns]
58 else:
59 return self.source_table.copy()
62class MockDataId:
63 """Replicate functionality of `DeferredDatasetHandle.dataId`"""
65 def __init__(self, region):
66 self.region = region
68 datasetDimensions = DimensionUniverse().extract(["htm7"])
69 datasetType = DatasetType("gaia_dr2_20200414", datasetDimensions, StorageClass("SimpleCatalog"))
70 self.ref = DatasetRef(datasetType, {"htm7": "mockRefCat"})
73class TestCatalogMatch(unittest.TestCase):
74 """Test CatalogMatchTask"""
76 def setUp(self):
77 self.task = CatalogMatchTask()
78 self.task.config.extraColumns.append("sourceId")
80 self.skymap = self._make_skymap()
81 self.tract = 9813
83 tract = self.skymap.generateTract(self.tract)
84 self.tractPoly = tract.getOuterSkyPolygon()
85 self.tractBbox = self.tractPoly.getBoundingBox()
87 self.nStars = 1000
88 starIds = np.arange(self.nStars)
89 starRas = (
90 np.random.random(self.nStars) * self.tractBbox.getWidth().asDegrees()
91 + self.tractBbox.getLon().getA().asDegrees()
92 )
93 starDecs = (
94 np.random.random(self.nStars) * self.tractBbox.getHeight().asDegrees()
95 + self.tractBbox.getLat().getA().asDegrees()
96 )
98 refDataId, deferredRefCat = self._make_refCat(starIds, starRas, starDecs, self.tractPoly)
100 self.task.refObjLoader = ReferenceObjectLoader(
101 dataIds=[refDataId],
102 refCats=[deferredRefCat],
103 )
104 self.task.refObjLoader.config.anyFilterMapsToThis = "phot_g_mean"
105 self.task.setRefCat(self.skymap, self.tract)
107 self.objectTable = self._make_objectCat(starIds, starRas, starDecs)
109 def _make_skymap(self):
110 """Make a testing skymap.
112 Returns
113 -------
114 `lsst.skymap.ringsSkyMap.RingsSkyMap`
115 Skymap that mimics the "hsc_rings_v1" skymap
116 """
117 skymap_config = lsst.skymap.ringsSkyMap.RingsSkyMapConfig()
118 skymap_config.numRings = 120
119 skymap_config.projection = "TAN"
120 skymap_config.tractOverlap = 1.0 / 60
121 skymap_config.pixelScale = 0.168
122 return lsst.skymap.ringsSkyMap.RingsSkyMap(skymap_config)
124 def _make_refCat(self, starIds, starRas, starDecs, poly):
125 """Make a mock `deferredDatasetReference` and
126 `DeferredDatasetHandle.dataId for a reference catalog.
128 Parameters
129 ----------
130 starIds : `np.ndarray` of `int`
131 Source ids for the simulated stars
132 starRas : `np.ndarray` of `float`
133 RAs of the simulated stars
134 starDecs : `np.ndarray` of `float`
135 Decs of the simulated stars
136 poly : `lsst.sphgeom._sphgeom.ConvexPolygon`
137 Bounding polygon containing the simulated stars
139 Returns
140 -------
141 refDataId : MockDataId
142 Object that replicates the functionality of a dataId
143 deferredRefCat : MockSourceTableRef
144 Object that replicates the functionality of a `DeferredDatasetRef`
145 """
146 refSchema = afwTable.SimpleTable.makeMinimalSchema()
147 idKey = refSchema.addField("sourceId", type="I")
148 fluxKey = refSchema.addField("phot_g_mean_flux", units="nJy", type=np.float64)
149 refCat = afwTable.SimpleCatalog(refSchema)
150 ref_md = PropertyList()
151 ref_md.set("REFCAT_FORMAT_VERSION", 1)
152 refCat.table.setMetadata(ref_md)
153 for i in range(len(starIds)):
154 record = refCat.addNew()
155 record.set(idKey, starIds[i])
156 record.setRa(lsst.geom.Angle(starRas[i], lsst.geom.degrees))
157 record.setDec(lsst.geom.Angle(starDecs[i], lsst.geom.degrees))
158 record.set(fluxKey, 1)
159 refDataId = MockDataId(poly)
160 deferredRefCat = MockSourceTableRef(refCat, ref=refDataId.ref)
161 return refDataId, deferredRefCat
163 def _make_objectCat(self, starIds, starRas, starDecs):
164 """Make a `pd.DataFrame` catalog with the columns needed for the
165 object selector.
167 Parameters
168 ----------
169 starIds : `np.ndarray` of `int`
170 Source ids for the simulated stars
171 starRas : `np.ndarray` of `float`
172 RAs of the simulated stars
173 starDecs : `np.ndarray` of `float`
174 Decs of the simulated stars
175 poly : `lsst.sphgeom._sphgeom.ConvexPolygon`
176 Bounding polygon containing the simulated stars
178 Returns
179 -------
180 sourceCat : `pd.DataFrame`
181 Catalog containing the simulated stars
182 """
183 x = np.random.random(self.nStars) * 4000
184 y = np.random.random(self.nStars) * 4000
185 radecErr = 1.0 / (3600 * 10) # Let random scatter be about 1/10 arcsecond
186 sourceDict = {
187 "sourceId": starIds,
188 "coord_ra": starRas + np.random.randn(self.nStars) * radecErr,
189 "coord_dec": starDecs + np.random.randn(self.nStars) * radecErr,
190 "x": x,
191 "y": y,
192 }
194 for key in [
195 "r_psfFlux_flag",
196 "y_extendedness_flag",
197 "i_pixelFlags_saturatedCenter",
198 "r_extendedness_flag",
199 "y_extendedness",
200 "g_extendedness_flag",
201 "z_extendedness",
202 "i_extendedness",
203 "z_pixelFlags_saturatedCenter",
204 "i_psfFlux_flag",
205 "r_pixelFlags_saturatedCenter",
206 "xy_flag",
207 "r_extendedness",
208 "y_pixelFlags_saturatedCenter",
209 "i_extendedness_flag",
210 "patch",
211 "g_psfFlux_flag",
212 "y_psfFlux_flag",
213 "z_psfFlux_flag",
214 "g_pixelFlags_saturatedCenter",
215 "z_extendedness_flag",
216 "g_extendedness",
217 ]:
218 sourceDict[key] = 0
219 for key in ["detect_isPatchInner", "detect_isDeblendedSource"]:
220 sourceDict[key] = 1
221 for key in ["i_psfFlux", "g_psfFlux", "r_psfFlux", "y_psfFlux", "z_psfFlux"]:
222 sourceDict[key] = 1000
223 for key in ["z_psfFluxErr", "i_psfFluxErr", "r_psfFluxErr", "g_psfFluxErr", "y_psfFluxErr"]:
224 sourceDict[key] = 1
225 sourceCat = pd.DataFrame(sourceDict)
226 return sourceCat
228 def test_setRefCat(self):
229 """Test whether the objects in the reference catalog are in the
230 expected footprint and that we get as many as expected
231 """
232 coord_ra = (self.task.refCat["coord_ra"].to_numpy() * u.degree).to(u.radian).value
233 coord_dec = (self.task.refCat["coord_dec"].to_numpy() * u.degree).to(u.radian).value
234 inFootprint = self.tractBbox.contains(coord_ra, coord_dec)
235 self.assertTrue(inFootprint.all())
236 self.assertEqual(len(self.task.refCat), self.nStars)
238 def test_run(self):
239 """Test whether `CatalogMatchTask` correctly associates the target and
240 reference catalog.
241 """
242 output = self.task.run(self.objectTable)
244 self.assertEqual(len(output.matchedCatalog), self.nStars)
245 self.assertListEqual(
246 output.matchedCatalog["sourceId_target"].to_list(),
247 output.matchedCatalog["sourceId_ref"].to_list(),
248 )
251class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
252 pass
255def setup_module(module):
256 lsst.utils.tests.init()
259if __name__ == "__main__": 259 ↛ 260line 259 didn't jump to line 260, because the condition on line 259 was never true
260 lsst.utils.tests.init()
261 unittest.main()