Coverage for tests/test_catalogMatch.py: 29%

101 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-08-05 01:25 -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 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 self.task = CatalogMatchTask() 

78 self.task.config.extraColumns.append("sourceId") 

79 

80 self.skymap = self._make_skymap() 

81 self.tract = 9813 

82 

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

84 self.tractPoly = tract.getOuterSkyPolygon() 

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

86 

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 ) 

97 

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

99 

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) 

106 

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

108 

109 def _make_skymap(self): 

110 """Make a testing skymap. 

111 

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) 

123 

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

125 """Make a mock `deferredDatasetReference` and 

126 `DeferredDatasetHandle.dataId for a reference catalog. 

127 

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 

138 

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 

162 

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

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

165 object selector. 

166 

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 

177 

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 } 

193 

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 

227 

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) 

237 

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) 

243 

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 ) 

249 

250 

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

252 pass 

253 

254 

255def setup_module(module): 

256 lsst.utils.tests.init() 

257 

258 

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