Coverage for tests/test_catalogMatch.py: 24%

91 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-04-21 10:05 +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 lsst.analysis.tools.tasks import CatalogMatchConfig, CatalogMatchTask 

31from lsst.daf.base import PropertyList 

32from lsst.meas.algorithms import ReferenceObjectLoader 

33from lsst.pipe.base import InMemoryDatasetHandle 

34 

35 

36class MockDataId: 

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

38 

39 def __init__(self, region): 

40 self.region = region 

41 

42 

43class TestCatalogMatch(unittest.TestCase): 

44 """Test CatalogMatchTask""" 

45 

46 def setUp(self): 

47 config = CatalogMatchConfig() 

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

49 self.task = CatalogMatchTask(config=config) 

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

51 

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

53 

54 self.skymap = self._make_skymap() 

55 self.tract = 9813 

56 

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

58 self.tractPoly = tract.getOuterSkyPolygon() 

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

60 

61 self.nStars = 1000 

62 starIds = np.arange(self.nStars) 

63 starRas = ( 

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

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

66 ) 

67 starDecs = ( 

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

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

70 ) 

71 

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

73 

74 self.task.refObjLoader = ReferenceObjectLoader( 

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

76 ) 

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

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

79 

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

81 

82 def _make_skymap(self): 

83 """Make a testing skymap. 

84 

85 Returns 

86 ------- 

87 `lsst.skymap.ringsSkyMap.RingsSkyMap` 

88 Skymap that mimics the "hsc_rings_v1" skymap 

89 """ 

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

91 skymap_config.numRings = 120 

92 skymap_config.projection = "TAN" 

93 skymap_config.tractOverlap = 1.0 / 60 

94 skymap_config.pixelScale = 0.168 

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

96 

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

98 """Make a mock `deferredDatasetReference` and 

99 `DeferredDatasetHandle.dataId for a reference catalog. 

100 

101 Parameters 

102 ---------- 

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

104 Source ids for the simulated stars 

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

106 RAs of the simulated stars 

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

108 Decs of the simulated stars 

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

110 Bounding polygon containing the simulated stars 

111 

112 Returns 

113 ------- 

114 refDataId : MockDataId 

115 Object that replicates the functionality of a dataId 

116 deferredRefCat : InMemoryDatasetHandle 

117 Object that replicates the functionality of a `DeferredDatasetRef` 

118 """ 

119 refSchema = afwTable.SimpleTable.makeMinimalSchema() 

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

121 fluxKey = refSchema.addField("phot_g_mean_flux", 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 refDataId = MockDataId(poly) 

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

134 return refDataId, deferredRefCat 

135 

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

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

138 object selector. 

139 

140 Parameters 

141 ---------- 

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

143 Source ids for the simulated stars 

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

145 RAs of the simulated stars 

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

147 Decs of the simulated stars 

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

149 Bounding polygon containing the simulated stars 

150 

151 Returns 

152 ------- 

153 sourceCat : `pd.DataFrame` 

154 Catalog containing the simulated stars 

155 """ 

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

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

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

159 sourceDict = { 

160 "sourceId": starIds, 

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

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

163 "x": x, 

164 "y": y, 

165 } 

166 

167 for key in [ 

168 "r_psfFlux_flag", 

169 "y_extendedness_flag", 

170 "i_pixelFlags_saturatedCenter", 

171 "r_extendedness_flag", 

172 "y_extendedness", 

173 "g_extendedness_flag", 

174 "z_extendedness", 

175 "i_extendedness", 

176 "z_pixelFlags_saturatedCenter", 

177 "i_psfFlux_flag", 

178 "r_pixelFlags_saturatedCenter", 

179 "xy_flag", 

180 "r_extendedness", 

181 "y_pixelFlags_saturatedCenter", 

182 "i_extendedness_flag", 

183 "patch", 

184 "g_psfFlux_flag", 

185 "y_psfFlux_flag", 

186 "z_psfFlux_flag", 

187 "g_pixelFlags_saturatedCenter", 

188 "z_extendedness_flag", 

189 "g_extendedness", 

190 ]: 

191 sourceDict[key] = 0 

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

193 sourceDict[key] = 1 

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

195 sourceDict[key] = 1000 

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

197 sourceDict[key] = 1 

198 sourceCat = pd.DataFrame(sourceDict) 

199 return sourceCat 

200 

201 def test_setRefCat(self): 

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

203 expected footprint and that we get as many as expected 

204 """ 

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

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

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

208 self.assertTrue(inFootprint.all()) 

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

210 

211 def test_run(self): 

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

213 reference catalog. 

214 """ 

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

216 

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

218 self.assertListEqual( 

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

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

221 ) 

222 

223 

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

225 pass 

226 

227 

228def setup_module(module): 

229 lsst.utils.tests.init() 

230 

231 

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

233 lsst.utils.tests.init() 

234 unittest.main()