Coverage for tests/test_catalogMatch.py: 24%
103 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-08 02:34 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-08 02:34 -0800
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 CatalogMatchConfig, 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 config = CatalogMatchConfig()
78 config.bands = ["g", "r", "i", "z", "y"]
79 self.task = CatalogMatchTask(config=config)
80 self.task.config.extraColumns.append("sourceId")
82 self.skymap = self._make_skymap()
83 self.tract = 9813
85 tract = self.skymap.generateTract(self.tract)
86 self.tractPoly = tract.getOuterSkyPolygon()
87 self.tractBbox = self.tractPoly.getBoundingBox()
89 self.nStars = 1000
90 starIds = np.arange(self.nStars)
91 starRas = (
92 np.random.random(self.nStars) * self.tractBbox.getWidth().asDegrees()
93 + self.tractBbox.getLon().getA().asDegrees()
94 )
95 starDecs = (
96 np.random.random(self.nStars) * self.tractBbox.getHeight().asDegrees()
97 + self.tractBbox.getLat().getA().asDegrees()
98 )
100 refDataId, deferredRefCat = self._make_refCat(starIds, starRas, starDecs, self.tractPoly)
102 self.task.refObjLoader = ReferenceObjectLoader(
103 dataIds=[refDataId], refCats=[deferredRefCat], name="gaia_dr2_20200414"
104 )
105 self.task.refObjLoader.config.anyFilterMapsToThis = "phot_g_mean"
106 self.task.setRefCat(self.skymap, self.tract)
108 self.objectTable = self._make_objectCat(starIds, starRas, starDecs)
110 def _make_skymap(self):
111 """Make a testing skymap.
113 Returns
114 -------
115 `lsst.skymap.ringsSkyMap.RingsSkyMap`
116 Skymap that mimics the "hsc_rings_v1" skymap
117 """
118 skymap_config = lsst.skymap.ringsSkyMap.RingsSkyMapConfig()
119 skymap_config.numRings = 120
120 skymap_config.projection = "TAN"
121 skymap_config.tractOverlap = 1.0 / 60
122 skymap_config.pixelScale = 0.168
123 return lsst.skymap.ringsSkyMap.RingsSkyMap(skymap_config)
125 def _make_refCat(self, starIds, starRas, starDecs, poly):
126 """Make a mock `deferredDatasetReference` and
127 `DeferredDatasetHandle.dataId for a reference catalog.
129 Parameters
130 ----------
131 starIds : `np.ndarray` of `int`
132 Source ids for the simulated stars
133 starRas : `np.ndarray` of `float`
134 RAs of the simulated stars
135 starDecs : `np.ndarray` of `float`
136 Decs of the simulated stars
137 poly : `lsst.sphgeom._sphgeom.ConvexPolygon`
138 Bounding polygon containing the simulated stars
140 Returns
141 -------
142 refDataId : MockDataId
143 Object that replicates the functionality of a dataId
144 deferredRefCat : MockSourceTableRef
145 Object that replicates the functionality of a `DeferredDatasetRef`
146 """
147 refSchema = afwTable.SimpleTable.makeMinimalSchema()
148 idKey = refSchema.addField("sourceId", type="I")
149 fluxKey = refSchema.addField("phot_g_mean_flux", units="nJy", type=np.float64)
150 refCat = afwTable.SimpleCatalog(refSchema)
151 ref_md = PropertyList()
152 ref_md.set("REFCAT_FORMAT_VERSION", 1)
153 refCat.table.setMetadata(ref_md)
154 for i in range(len(starIds)):
155 record = refCat.addNew()
156 record.set(idKey, starIds[i])
157 record.setRa(lsst.geom.Angle(starRas[i], lsst.geom.degrees))
158 record.setDec(lsst.geom.Angle(starDecs[i], lsst.geom.degrees))
159 record.set(fluxKey, 1)
160 refDataId = MockDataId(poly)
161 deferredRefCat = MockSourceTableRef(refCat, ref=refDataId.ref)
162 return refDataId, deferredRefCat
164 def _make_objectCat(self, starIds, starRas, starDecs):
165 """Make a `pd.DataFrame` catalog with the columns needed for the
166 object selector.
168 Parameters
169 ----------
170 starIds : `np.ndarray` of `int`
171 Source ids for the simulated stars
172 starRas : `np.ndarray` of `float`
173 RAs of the simulated stars
174 starDecs : `np.ndarray` of `float`
175 Decs of the simulated stars
176 poly : `lsst.sphgeom._sphgeom.ConvexPolygon`
177 Bounding polygon containing the simulated stars
179 Returns
180 -------
181 sourceCat : `pd.DataFrame`
182 Catalog containing the simulated stars
183 """
184 x = np.random.random(self.nStars) * 4000
185 y = np.random.random(self.nStars) * 4000
186 radecErr = 1.0 / (3600 * 10) # Let random scatter be about 1/10 arcsecond
187 sourceDict = {
188 "sourceId": starIds,
189 "coord_ra": starRas + np.random.randn(self.nStars) * radecErr,
190 "coord_dec": starDecs + np.random.randn(self.nStars) * radecErr,
191 "x": x,
192 "y": y,
193 }
195 for key in [
196 "r_psfFlux_flag",
197 "y_extendedness_flag",
198 "i_pixelFlags_saturatedCenter",
199 "r_extendedness_flag",
200 "y_extendedness",
201 "g_extendedness_flag",
202 "z_extendedness",
203 "i_extendedness",
204 "z_pixelFlags_saturatedCenter",
205 "i_psfFlux_flag",
206 "r_pixelFlags_saturatedCenter",
207 "xy_flag",
208 "r_extendedness",
209 "y_pixelFlags_saturatedCenter",
210 "i_extendedness_flag",
211 "patch",
212 "g_psfFlux_flag",
213 "y_psfFlux_flag",
214 "z_psfFlux_flag",
215 "g_pixelFlags_saturatedCenter",
216 "z_extendedness_flag",
217 "g_extendedness",
218 ]:
219 sourceDict[key] = 0
220 for key in ["detect_isPatchInner", "detect_isDeblendedSource"]:
221 sourceDict[key] = 1
222 for key in ["i_psfFlux", "g_psfFlux", "r_psfFlux", "y_psfFlux", "z_psfFlux"]:
223 sourceDict[key] = 1000
224 for key in ["z_psfFluxErr", "i_psfFluxErr", "r_psfFluxErr", "g_psfFluxErr", "y_psfFluxErr"]:
225 sourceDict[key] = 1
226 sourceCat = pd.DataFrame(sourceDict)
227 return sourceCat
229 def test_setRefCat(self):
230 """Test whether the objects in the reference catalog are in the
231 expected footprint and that we get as many as expected
232 """
233 coord_ra = (self.task.refCat["coord_ra"].to_numpy() * u.degree).to(u.radian).value
234 coord_dec = (self.task.refCat["coord_dec"].to_numpy() * u.degree).to(u.radian).value
235 inFootprint = self.tractBbox.contains(coord_ra, coord_dec)
236 self.assertTrue(inFootprint.all())
237 self.assertEqual(len(self.task.refCat), self.nStars)
239 def test_run(self):
240 """Test whether `CatalogMatchTask` correctly associates the target and
241 reference catalog.
242 """
243 output = self.task.run(self.objectTable)
245 self.assertEqual(len(output.matchedCatalog), self.nStars)
246 self.assertListEqual(
247 output.matchedCatalog["sourceId_target"].to_list(),
248 output.matchedCatalog["sourceId_ref"].to_list(),
249 )
252class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
253 pass
256def setup_module(module):
257 lsst.utils.tests.init()
260if __name__ == "__main__": 260 ↛ 261line 260 didn't jump to line 261, because the condition on line 260 was never true
261 lsst.utils.tests.init()
262 unittest.main()