Coverage for tests/test_catalogMatch.py: 24%

104 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-14 03:19 -0700

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/>. 

21 

22import unittest 

23 

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 

34 

35 

36class MockSourceTableRef: 

37 """Replicate functionality of `DeferredDatasetHandle`""" 

38 

39 def __init__(self, source_table, ref=None, dataId=None): 

40 self.source_table = source_table 

41 self.ref = ref 

42 self.dataId = dataId 

43 

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() 

60 

61 

62class MockDataId: 

63 """Replicate functionality of `DeferredDatasetHandle.dataId`""" 

64 

65 def __init__(self, region): 

66 self.region = region 

67 

68 datasetDimensions = DimensionUniverse().extract(["htm7"]) 

69 datasetType = DatasetType("gaia_dr2_20200414", datasetDimensions, StorageClass("SimpleCatalog")) 

70 self.ref = DatasetRef(datasetType, {"htm7": "mockRefCat"}) 

71 

72 

73class TestCatalogMatch(unittest.TestCase): 

74 """Test CatalogMatchTask""" 

75 

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") 

81 

82 self.rng = np.random.default_rng(12345) 

83 

84 self.skymap = self._make_skymap() 

85 self.tract = 9813 

86 

87 tract = self.skymap.generateTract(self.tract) 

88 self.tractPoly = tract.getOuterSkyPolygon() 

89 self.tractBbox = self.tractPoly.getBoundingBox() 

90 

91 self.nStars = 1000 

92 starIds = np.arange(self.nStars) 

93 starRas = ( 

94 self.rng.random(self.nStars) * self.tractBbox.getWidth().asDegrees() 

95 + self.tractBbox.getLon().getA().asDegrees() 

96 ) 

97 starDecs = ( 

98 self.rng.random(self.nStars) * self.tractBbox.getHeight().asDegrees() 

99 + self.tractBbox.getLat().getA().asDegrees() 

100 ) 

101 

102 refDataId, deferredRefCat = self._make_refCat(starIds, starRas, starDecs, self.tractPoly) 

103 

104 self.task.refObjLoader = ReferenceObjectLoader( 

105 dataIds=[refDataId], refCats=[deferredRefCat], name="gaia_dr2_20200414" 

106 ) 

107 self.task.refObjLoader.config.anyFilterMapsToThis = "phot_g_mean" 

108 self.task.setRefCat(self.skymap, self.tract) 

109 

110 self.objectTable = self._make_objectCat(starIds, starRas, starDecs) 

111 

112 def _make_skymap(self): 

113 """Make a testing skymap. 

114 

115 Returns 

116 ------- 

117 `lsst.skymap.ringsSkyMap.RingsSkyMap` 

118 Skymap that mimics the "hsc_rings_v1" skymap 

119 """ 

120 skymap_config = lsst.skymap.ringsSkyMap.RingsSkyMapConfig() 

121 skymap_config.numRings = 120 

122 skymap_config.projection = "TAN" 

123 skymap_config.tractOverlap = 1.0 / 60 

124 skymap_config.pixelScale = 0.168 

125 return lsst.skymap.ringsSkyMap.RingsSkyMap(skymap_config) 

126 

127 def _make_refCat(self, starIds, starRas, starDecs, poly): 

128 """Make a mock `deferredDatasetReference` and 

129 `DeferredDatasetHandle.dataId for a reference catalog. 

130 

131 Parameters 

132 ---------- 

133 starIds : `np.ndarray` of `int` 

134 Source ids for the simulated stars 

135 starRas : `np.ndarray` of `float` 

136 RAs of the simulated stars 

137 starDecs : `np.ndarray` of `float` 

138 Decs of the simulated stars 

139 poly : `lsst.sphgeom._sphgeom.ConvexPolygon` 

140 Bounding polygon containing the simulated stars 

141 

142 Returns 

143 ------- 

144 refDataId : MockDataId 

145 Object that replicates the functionality of a dataId 

146 deferredRefCat : MockSourceTableRef 

147 Object that replicates the functionality of a `DeferredDatasetRef` 

148 """ 

149 refSchema = afwTable.SimpleTable.makeMinimalSchema() 

150 idKey = refSchema.addField("sourceId", type="I") 

151 fluxKey = refSchema.addField("phot_g_mean_flux", units="nJy", type=np.float64) 

152 refCat = afwTable.SimpleCatalog(refSchema) 

153 ref_md = PropertyList() 

154 ref_md.set("REFCAT_FORMAT_VERSION", 1) 

155 refCat.table.setMetadata(ref_md) 

156 for i in range(len(starIds)): 

157 record = refCat.addNew() 

158 record.set(idKey, starIds[i]) 

159 record.setRa(lsst.geom.Angle(starRas[i], lsst.geom.degrees)) 

160 record.setDec(lsst.geom.Angle(starDecs[i], lsst.geom.degrees)) 

161 record.set(fluxKey, 1) 

162 refDataId = MockDataId(poly) 

163 deferredRefCat = MockSourceTableRef(refCat, ref=refDataId.ref) 

164 return refDataId, deferredRefCat 

165 

166 def _make_objectCat(self, starIds, starRas, starDecs): 

167 """Make a `pd.DataFrame` catalog with the columns needed for the 

168 object selector. 

169 

170 Parameters 

171 ---------- 

172 starIds : `np.ndarray` of `int` 

173 Source ids for the simulated stars 

174 starRas : `np.ndarray` of `float` 

175 RAs of the simulated stars 

176 starDecs : `np.ndarray` of `float` 

177 Decs of the simulated stars 

178 poly : `lsst.sphgeom._sphgeom.ConvexPolygon` 

179 Bounding polygon containing the simulated stars 

180 

181 Returns 

182 ------- 

183 sourceCat : `pd.DataFrame` 

184 Catalog containing the simulated stars 

185 """ 

186 x = self.rng.random(self.nStars) * 4000 

187 y = self.rng.random(self.nStars) * 4000 

188 radecErr = 1.0 / (3600 * 10) # Let random scatter be about 1/10 arcsecond 

189 sourceDict = { 

190 "sourceId": starIds, 

191 "coord_ra": starRas + self.rng.standard_normal(self.nStars) * radecErr, 

192 "coord_dec": starDecs + self.rng.standard_normal(self.nStars) * radecErr, 

193 "x": x, 

194 "y": y, 

195 } 

196 

197 for key in [ 

198 "r_psfFlux_flag", 

199 "y_extendedness_flag", 

200 "i_pixelFlags_saturatedCenter", 

201 "r_extendedness_flag", 

202 "y_extendedness", 

203 "g_extendedness_flag", 

204 "z_extendedness", 

205 "i_extendedness", 

206 "z_pixelFlags_saturatedCenter", 

207 "i_psfFlux_flag", 

208 "r_pixelFlags_saturatedCenter", 

209 "xy_flag", 

210 "r_extendedness", 

211 "y_pixelFlags_saturatedCenter", 

212 "i_extendedness_flag", 

213 "patch", 

214 "g_psfFlux_flag", 

215 "y_psfFlux_flag", 

216 "z_psfFlux_flag", 

217 "g_pixelFlags_saturatedCenter", 

218 "z_extendedness_flag", 

219 "g_extendedness", 

220 ]: 

221 sourceDict[key] = 0 

222 for key in ["detect_isPatchInner", "detect_isDeblendedSource"]: 

223 sourceDict[key] = 1 

224 for key in ["i_psfFlux", "g_psfFlux", "r_psfFlux", "y_psfFlux", "z_psfFlux"]: 

225 sourceDict[key] = 1000 

226 for key in ["z_psfFluxErr", "i_psfFluxErr", "r_psfFluxErr", "g_psfFluxErr", "y_psfFluxErr"]: 

227 sourceDict[key] = 1 

228 sourceCat = pd.DataFrame(sourceDict) 

229 return sourceCat 

230 

231 def test_setRefCat(self): 

232 """Test whether the objects in the reference catalog are in the 

233 expected footprint and that we get as many as expected 

234 """ 

235 coord_ra = (self.task.refCat["coord_ra"].to_numpy() * u.degree).to(u.radian).value 

236 coord_dec = (self.task.refCat["coord_dec"].to_numpy() * u.degree).to(u.radian).value 

237 inFootprint = self.tractBbox.contains(coord_ra, coord_dec) 

238 self.assertTrue(inFootprint.all()) 

239 self.assertEqual(len(self.task.refCat), self.nStars) 

240 

241 def test_run(self): 

242 """Test whether `CatalogMatchTask` correctly associates the target and 

243 reference catalog. 

244 """ 

245 output = self.task.run(self.objectTable) 

246 

247 self.assertEqual(len(output.matchedCatalog), self.nStars) 

248 self.assertListEqual( 

249 output.matchedCatalog["sourceId_target"].to_list(), 

250 output.matchedCatalog["sourceId_ref"].to_list(), 

251 ) 

252 

253 

254class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

255 pass 

256 

257 

258def setup_module(module): 

259 lsst.utils.tests.init() 

260 

261 

262if __name__ == "__main__": 262 ↛ 263line 262 didn't jump to line 263, because the condition on line 262 was never true

263 lsst.utils.tests.init() 

264 unittest.main()