Coverage for tests/test_catalogMatch.py: 24%

93 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-04 11:06 +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/>. 

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 astropy.table import Table 

31from lsst.analysis.tools.tasks import AstrometricCatalogMatchConfig, AstrometricCatalogMatchTask 

32from lsst.daf.base import PropertyList 

33from lsst.meas.algorithms.testUtils import MockRefcatDataId 

34from lsst.pipe.base import InMemoryDatasetHandle 

35from lsst.pipe.tasks.loadReferenceCatalog import LoadReferenceCatalogTask 

36 

37 

38class TestCatalogMatch(unittest.TestCase): 

39 """Test AstrometricCatalogMatchTask""" 

40 

41 def setUp(self): 

42 config = AstrometricCatalogMatchConfig() 

43 config.bands = ["g", "r", "i", "z", "y"] 

44 self.task = AstrometricCatalogMatchTask(config=config) 

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

46 self.task.config.referenceCatalogLoader.refObjLoader.requireProperMotion = False 

47 

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

49 

50 self.skymap = self._make_skymap() 

51 self.tract = 9813 

52 

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

54 self.tractPoly = tract.getOuterSkyPolygon() 

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

56 

57 self.nStars = 1000 

58 starIds = np.arange(self.nStars) 

59 starRas = ( 

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

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

62 ) 

63 starDecs = ( 

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

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

66 ) 

67 

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

69 

70 loaderTask = LoadReferenceCatalogTask( 

71 config=config.referenceCatalogLoader, 

72 dataIds=[refDataId], 

73 name="gaia_dr3_20230707", 

74 refCats=[deferredRefCat], 

75 ) 

76 self.loadedRefCat = self.task._loadRefCat(loaderTask, tract) 

77 self.loadedRefCat["sourceId"] = np.arange(0, len(self.loadedRefCat)) 

78 

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

80 

81 def _make_skymap(self): 

82 """Make a testing skymap. 

83 

84 Returns 

85 ------- 

86 `lsst.skymap.ringsSkyMap.RingsSkyMap` 

87 Skymap that mimics the "hsc_rings_v1" skymap 

88 """ 

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

90 skymap_config.numRings = 120 

91 skymap_config.projection = "TAN" 

92 skymap_config.tractOverlap = 1.0 / 60 

93 skymap_config.pixelScale = 0.168 

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

95 

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

97 """Make a mock `deferredDatasetReference` and 

98 `DeferredDatasetHandle.dataId for a reference catalog. 

99 

100 Parameters 

101 ---------- 

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

103 Source ids for the simulated stars 

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

105 RAs of the simulated stars 

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

107 Decs of the simulated stars 

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

109 Bounding polygon containing the simulated stars 

110 

111 Returns 

112 ------- 

113 refDataId : `lsst.meas.algorithms.testUtils.MockRefcatDataId` 

114 Object that replicates the functionality of a dataId 

115 deferredRefCat : InMemoryDatasetHandle 

116 Object that replicates the functionality of a `DeferredDatasetRef` 

117 """ 

118 refSchema = afwTable.SimpleTable.makeMinimalSchema() 

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

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

121 fluxErrKey = refSchema.addField("phot_g_mean_fluxErr", units="nJy", type=np.float64) 

122 refCat = afwTable.SimpleCatalog(refSchema) 

123 ref_md = PropertyList() 

124 ref_md.set("REFCAT_FORMAT_VERSION", 1) 

125 refCat.table.setMetadata(ref_md) 

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

127 record = refCat.addNew() 

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

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

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

131 record.set(fluxKey, 1) 

132 record.set(fluxErrKey, 0.01) 

133 refDataId = MockRefcatDataId(poly) 

134 deferredRefCat = InMemoryDatasetHandle(refCat, storageClass="SimpleCatalog", htm7="mockRefCat") 

135 return refDataId, deferredRefCat 

136 

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

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

139 object selector. 

140 

141 Parameters 

142 ---------- 

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

144 Source ids for the simulated stars 

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

146 RAs of the simulated stars 

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

148 Decs of the simulated stars 

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

150 Bounding polygon containing the simulated stars 

151 

152 Returns 

153 ------- 

154 sourceCat : `pd.DataFrame` 

155 Catalog containing the simulated stars 

156 """ 

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

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

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

160 sourceDict = { 

161 "sourceId": starIds, 

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

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

164 "x": x, 

165 "y": y, 

166 } 

167 

168 for key in [ 

169 "r_psfFlux_flag", 

170 "y_extendedness_flag", 

171 "i_pixelFlags_saturatedCenter", 

172 "r_extendedness_flag", 

173 "y_extendedness", 

174 "g_extendedness_flag", 

175 "z_extendedness", 

176 "i_extendedness", 

177 "z_pixelFlags_saturatedCenter", 

178 "i_psfFlux_flag", 

179 "r_pixelFlags_saturatedCenter", 

180 "xy_flag", 

181 "r_extendedness", 

182 "y_pixelFlags_saturatedCenter", 

183 "i_extendedness_flag", 

184 "patch", 

185 "g_psfFlux_flag", 

186 "y_psfFlux_flag", 

187 "z_psfFlux_flag", 

188 "g_pixelFlags_saturatedCenter", 

189 "z_extendedness_flag", 

190 "g_extendedness", 

191 ]: 

192 sourceDict[key] = 0 

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

194 sourceDict[key] = 1 

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

196 sourceDict[key] = 1000 

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

198 sourceDict[key] = 1 

199 sourceCat = Table.from_pandas(pd.DataFrame(sourceDict)) 

200 return sourceCat 

201 

202 def test_setRefCat(self): 

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

204 expected footprint and that we get as many as expected 

205 """ 

206 coord_ra = (self.loadedRefCat["ra"] * u.degree).to(u.radian).value 

207 coord_dec = (self.loadedRefCat["dec"] * u.degree).to(u.radian).value 

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

209 self.assertTrue(inFootprint.all()) 

210 self.assertEqual(len(self.loadedRefCat), self.nStars) 

211 

212 def test_run(self): 

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

214 reference catalog. 

215 """ 

216 output = self.task.run( 

217 targetCatalog=self.objectTable, refCatalog=self.loadedRefCat, bands=self.task.config.bands 

218 ) 

219 

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

221 self.assertListEqual( 

222 list(output.matchedCatalog["sourceId_target"]), 

223 list(output.matchedCatalog["sourceId_ref"]), 

224 ) 

225 

226 

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

228 pass 

229 

230 

231def setup_module(module): 

232 lsst.utils.tests.init() 

233 

234 

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

236 lsst.utils.tests.init() 

237 unittest.main()