Coverage for tests/test_catalogMatch.py: 24%

103 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-07 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/>. 

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.skymap = self._make_skymap() 

83 self.tract = 9813 

84 

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

86 self.tractPoly = tract.getOuterSkyPolygon() 

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

88 

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 ) 

99 

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

101 

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) 

107 

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

109 

110 def _make_skymap(self): 

111 """Make a testing skymap. 

112 

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) 

124 

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

126 """Make a mock `deferredDatasetReference` and 

127 `DeferredDatasetHandle.dataId for a reference catalog. 

128 

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 

139 

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 

163 

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

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

166 object selector. 

167 

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 

178 

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 } 

194 

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 

228 

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) 

238 

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) 

244 

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 ) 

250 

251 

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

253 pass 

254 

255 

256def setup_module(module): 

257 lsst.utils.tests.init() 

258 

259 

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