Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of ap_association. 

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 numpy as np 

23import os 

24import unittest 

25 

26from lsst.ap.association import ( 

27 MapApDataConfig, 

28 MapApDataTask, 

29 MapDiaSourceConfig, 

30 MapDiaSourceTask, 

31 UnpackApdbFlags) 

32from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

33import lsst.afw.table as afwTable 

34import lsst.daf.base as dafBase 

35import lsst.afw.geom as afwGeom 

36import lsst.afw.image as afwImage 

37import lsst.afw.image.utils as afwImageUtils 

38import lsst.geom as geom 

39from lsst.utils import getPackageDir 

40import lsst.utils.tests 

41 

42 

43def make_input_source_catalog(n_objects, add_flags=False): 

44 """Create tests objects to map into apData products. 

45 

46 Parameters 

47 ---------- 

48 n_objects: `int` 

49 Number of objects to create. 

50 """ 

51 schema = afwTable.SourceTable.makeMinimalSchema() 

52 schema.addField("base_NaiveCentroid_x", type="D") 

53 schema.addField("base_NaiveCentroid_y", type="D") 

54 schema.addField("base_PsfFlux_instFlux", type="D") 

55 schema.addField("base_PsfFlux_instFluxErr", type="D") 

56 schema.addField("ip_diffim_DipoleFit_separation", type="D") 

57 schema.addField("ip_diffim_DipoleFit_orientation", type="D") 

58 schema.addField("ip_diffim_DipoleFit_neg_instFlux", type="D") 

59 schema.addField("ip_diffim_DipoleFit_neg_instFluxErr", type="D") 

60 schema.addField("ip_diffim_DipoleFit_pos_instFlux", type="D") 

61 schema.addField("ip_diffim_DipoleFit_pos_instFluxErr", type="D") 

62 schema.addField("ip_diffim_forced_PsfFlux_instFlux", type="D") 

63 schema.addField("ip_diffim_forced_PsfFlux_instFluxErr", type="D") 

64 if add_flags: 

65 schema.addField("base_PixelFlags_flag", type="Flag") 

66 schema.addField("base_PixelFlags_flag_offimage", type="Flag") 

67 

68 objects = afwTable.SourceCatalog(schema) 

69 objects.preallocate(n_objects) 

70 objects.definePsfFlux("base_PsfFlux") 

71 objects.defineCentroid("base_NaiveCentroid") 

72 

73 for obj_idx in range(n_objects): 

74 obj = objects.addNew() 

75 for subSchema in schema: 

76 if isinstance(obj.get(subSchema.getKey()), geom.Angle): 

77 obj.set(subSchema.getKey(), 1. * geom.degrees) 

78 elif subSchema.getField().getName() == "ip_diffim_DipoleFit_neg_instFlux": 

79 obj.set(subSchema.getKey(), -1) 

80 else: 

81 obj.set(subSchema.getKey(), 1) 

82 return objects 

83 

84 

85class TestAPDataMapperTask(unittest.TestCase): 

86 

87 def setUp(self): 

88 # CFHT Filters from the camera mapper. 

89 afwImageUtils.resetFilters() 

90 afwImageUtils.defineFilter('u', lambdaEff=374, alias="u.MP9301") 

91 afwImageUtils.defineFilter('g', lambdaEff=487, alias="g.MP9401") 

92 afwImageUtils.defineFilter('r', lambdaEff=628, alias="r.MP9601") 

93 afwImageUtils.defineFilter('i', lambdaEff=778, alias="i.MP9701") 

94 afwImageUtils.defineFilter('z', lambdaEff=1170, alias="z.MP9801") 

95 

96 self.metadata = dafBase.PropertySet() 

97 

98 self.metadata.set("SIMPLE", "T") 

99 self.metadata.set("BITPIX", -32) 

100 self.metadata.set("NAXIS", 2) 

101 self.metadata.set("NAXIS1", 1024) 

102 self.metadata.set("NAXIS2", 1153) 

103 self.metadata.set("RADECSYS", 'FK5') 

104 self.metadata.set("EQUINOX", 2000.) 

105 

106 self.metadata.setDouble("CRVAL1", 215.604025685476) 

107 self.metadata.setDouble("CRVAL2", 53.1595451514076) 

108 self.metadata.setDouble("CRPIX1", 1109.99981456774) 

109 self.metadata.setDouble("CRPIX2", 560.018167811613) 

110 self.metadata.set("CTYPE1", 'RA---SIN') 

111 self.metadata.set("CTYPE2", 'DEC--SIN') 

112 

113 self.metadata.setDouble("CD1_1", 5.10808596133527E-05) 

114 self.metadata.setDouble("CD1_2", 1.85579539217196E-07) 

115 self.metadata.setDouble("CD2_2", -5.10281493481982E-05) 

116 self.metadata.setDouble("CD2_1", -8.27440751733828E-07) 

117 

118 self.wcs = afwGeom.makeSkyWcs(self.metadata) 

119 self.exposure = afwImage.makeExposure( 

120 afwImage.makeMaskedImageFromArrays(np.ones((1024, 1153))), 

121 self.wcs) 

122 detector = DetectorWrapper(id=23, bbox=self.exposure.getBBox()).detector 

123 visit = afwImage.VisitInfo( 

124 exposureId=4321, 

125 exposureTime=200., 

126 date=dafBase.DateTime(nsecs=1400000000 * 10**9)) 

127 self.exposure.setDetector(detector) 

128 self.exposure.getInfo().setVisitInfo(visit) 

129 self.exposure.setFilter(afwImage.Filter('g')) 

130 scale = 2 

131 scaleErr = 1 

132 self.photoCalib = afwImage.PhotoCalib(scale, scaleErr) 

133 self.exposure.setPhotoCalib(self.photoCalib) 

134 

135 self.inputCatalogNoFlags = make_input_source_catalog(10, False) 

136 self.inputCatalog = make_input_source_catalog(10, True) 

137 

138 def test_run(self): 

139 """Test the generic data product mapper. 

140 """ 

141 outSchema = afwTable.SourceTable.makeMinimalSchema() 

142 outSchema.addField("psFlux", type="D") 

143 outSchema.addField("psFluxErr", type="D") 

144 

145 mapApDConfig = MapApDataConfig() 

146 mapApDConfig.copyColumns = { 

147 "id": "id", 

148 "parent": "parent", 

149 "coord_ra": "coord_ra", 

150 "coord_dec": "coord_dec", 

151 "slot_PsfFlux_instFlux": "psFlux", 

152 "slot_PsfFlux_instFluxErr": "psFluxErr" 

153 } 

154 

155 mapApD = MapApDataTask(inputSchema=self.inputCatalog.schema, 

156 outputSchema=outSchema, 

157 config=mapApDConfig) 

158 outputCatalog = mapApD.run(self.inputCatalog) 

159 

160 for inObj, outObj in zip(self.inputCatalog, outputCatalog): 

161 for inputName, outputName in mapApDConfig.copyColumns.items(): 

162 self.assertEqual(inObj[inputName], outObj[outputName]) 

163 

164 def test_run_dia_source(self): 

165 """Test the DiaSource specific data product mapper/calibrator. 

166 """ 

167 mapApDConfig = self._create_map_dia_source_config() 

168 mapApD = MapDiaSourceTask(inputSchema=self.inputCatalog.schema, 

169 config=mapApDConfig) 

170 outputCatalog = mapApD.run(self.inputCatalog, self.exposure) 

171 

172 expectedMeanDip = 2. 

173 expectedDiffFlux = 0. 

174 expectedLength = 0.18379083 

175 

176 for inObj, outObj in zip(self.inputCatalog, outputCatalog): 

177 self.assertEqual( 

178 outObj["ccdVisitId"], 

179 self.exposure.getInfo().getVisitInfo().getExposureId()) 

180 self.assertEqual( 

181 outObj["midPointTai"], 

182 self.exposure.getInfo().getVisitInfo().getDate().get( 

183 system=dafBase.DateTime.MJD)) 

184 self.assertEqual( 

185 outObj["flags"], 

186 1 * 2 ** 0 + 1 * 2 ** 1) 

187 for inputName, outputName in mapApDConfig.copyColumns.items(): 

188 if inputName.startswith("slot_PsfFlux"): 

189 self._test_calibrated_flux(inObj, outObj) 

190 else: 

191 self.assertEqual(inObj[inputName], outObj[outputName]) 

192 self.assertEqual(outObj["dipMeanFlux"], expectedMeanDip) 

193 self.assertEqual(outObj["dipFluxDiff"], expectedDiffFlux) 

194 self.assertAlmostEqual(outObj["dipLength"], expectedLength) 

195 

196 def _create_map_dia_source_config(self): 

197 """Create a test config for use in MapDiaSourceTask. 

198 

199 Returns 

200 ------- 

201 configurable : `lsst.pex.config.Config` 

202 Configurable for use in MapDiaSourceTask. 

203 """ 

204 configurable = MapDiaSourceConfig() 

205 configurable.copyColumns = { 

206 "id": "id", 

207 "parent": "parent", 

208 "coord_ra": "coord_ra", 

209 "coord_dec": "coord_dec", 

210 "slot_PsfFlux_instFlux": "psFlux", 

211 "slot_PsfFlux_instFluxErr": "psFluxErr" 

212 } 

213 configurable.calibrateColumns = ["slot_PsfFlux"] 

214 configurable.flagMap = os.path.join( 

215 getPackageDir("ap_association"), 

216 "tests", 

217 "test-flag-map.yaml") 

218 

219 return configurable 

220 

221 def test_run_dia_source_wrong_flags(self): 

222 """Test that the proper errors are thrown when requesting flag columns 

223 that are not in the input schema. 

224 """ 

225 mapApDConfig = self._create_map_dia_source_config() 

226 with self.assertRaises(KeyError): 

227 MapDiaSourceTask(inputSchema=self.inputCatalogNoFlags.schema, 

228 config=mapApDConfig) 

229 

230 def test_calibrateFluxes(self): 

231 """Test that flux calibration works as expected. 

232 """ 

233 outSchema = afwTable.SourceTable.makeMinimalSchema() 

234 outSchema.addField("psFlux", type="D") 

235 outSchema.addField("psFluxErr", type="D") 

236 

237 outputCatalog = afwTable.SourceCatalog(outSchema) 

238 outRecord = outputCatalog.addNew() 

239 

240 mapApDConfig = self._create_map_dia_source_config() 

241 mapApD = MapDiaSourceTask(inputSchema=self.inputCatalog.schema, 

242 config=mapApDConfig) 

243 

244 mapApD.calibrateFluxes(self.inputCatalog[0], 

245 outRecord, 

246 self.photoCalib) 

247 self._test_calibrated_flux(self.inputCatalog[0], outRecord) 

248 

249 def _test_calibrated_flux(self, inputRecord, outputRecord): 

250 """Compare calibrated fluxes to expectation from zero point. 

251 

252 Parameters 

253 ---------- 

254 inputRecord: `lsst.afw.table.SourceRecord` 

255 Input source record with uncalibrated flux values. 

256 outputRecord: `lsst.afw.table.SourceRecord` 

257 Source record with calibrated fluxes. 

258 """ 

259 expected = self.photoCalib.instFluxToNanojansky(inputRecord["slot_PsfFlux_instFlux"], 

260 inputRecord["slot_PsfFlux_instFluxErr"]) 

261 self.assertAlmostEqual(outputRecord["psFlux"], expected.value) 

262 self.assertAlmostEqual(outputRecord["psFluxErr"], expected.error) 

263 

264 def test_bit_unpacker(self): 

265 """Test that the integer bit packer is functioning correctly. 

266 """ 

267 mapApDConfig = self._create_map_dia_source_config() 

268 mapApD = MapDiaSourceTask(inputSchema=self.inputCatalog.schema, 

269 config=mapApDConfig) 

270 for idx, obj in enumerate(self.inputCatalog): 

271 if idx in [1, 3, 5]: 

272 obj.set("base_PixelFlags_flag", 0) 

273 if idx in [1, 4, 6]: 

274 obj.set("base_PixelFlags_flag_offimage", 0) 

275 outputCatalog = mapApD.run(self.inputCatalog, self.exposure) 

276 

277 unpacker = UnpackApdbFlags(mapApDConfig.flagMap, "DiaSource") 

278 flag_values = unpacker.unpack(outputCatalog.get("flags"), "flags") 

279 

280 for idx, flag in enumerate(flag_values): 

281 if idx in [1, 3, 5]: 

282 self.assertFalse(flag['base_PixelFlags_flag']) 

283 else: 

284 self.assertTrue(flag['base_PixelFlags_flag']) 

285 

286 if idx in [1, 4, 6]: 

287 self.assertFalse(flag['base_PixelFlags_flag_offimage']) 

288 else: 

289 self.assertTrue(flag['base_PixelFlags_flag_offimage']) 

290 

291 

292class MemoryTester(lsst.utils.tests.MemoryTestCase): 

293 pass 

294 

295 

296def setup_module(module): 

297 lsst.utils.tests.init() 

298 

299 

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

301 lsst.utils.tests.init() 

302 unittest.main()