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 os 

23import unittest 

24 

25from lsst.ap.association import ( 

26 MapApDataConfig, 

27 MapApDataTask, 

28 MapDiaSourceConfig, 

29 MapDiaSourceTask, 

30 UnpackApdbFlags) 

31from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

32import lsst.afw.table as afwTable 

33import lsst.daf.base as dafBase 

34import lsst.afw.image as afwImage 

35import lsst.geom as geom 

36import lsst.meas.base.tests as measTests 

37from lsst.utils import getPackageDir 

38import lsst.utils.tests 

39 

40 

41def make_input_source_catalog(dataset, add_flags=False): 

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

43 

44 Parameters 

45 ---------- 

46 dataset : `lsst.meas.base.tests.TestDataset` 

47 Test dataset to create random exposure realizations with inserted 

48 sources and detected sources in a catalog. 

49 """ 

50 schema = dataset.makeMinimalSchema() 

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

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

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

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

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

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

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

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

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

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

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

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

63 if add_flags: 

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

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

66 

67 exposure, catalog = dataset.realize(10.0, schema, randomSeed=1234) 

68 catalog.definePsfFlux("base_PsfFlux") 

69 catalog.defineCentroid("base_NaiveCentroid") 

70 

71 # Fill in simple values for the test DiaSources. Create specific values 

72 # where required such as negative values for the negative lobe of the 

73 # dipole flux. 

74 for src in catalog: 

75 for subSchema in schema: 

76 if subSchema.getField().getName() == "ip_diffim_DipoleFit_neg_instFlux": 

77 src.set(subSchema.getKey(), -1) 

78 elif (subSchema.getField().getName().startswith("ip") 

79 or subSchema.getField().getName().startswith("base")): 

80 src.set(subSchema.getKey(), 1) 

81 src.set("base_NaiveCentroid_x", src["truth_x"]) 

82 src.set("base_NaiveCentroid_y", src["truth_y"]) 

83 src.setCoord(exposure.getWcs().pixelToSky(src.getCentroid())) 

84 return catalog, exposure 

85 

86 

87class TestAPDataMapperTask(unittest.TestCase): 

88 

89 def setUp(self): 

90 nSources = 10 

91 self.bbox = geom.Box2I(geom.Point2I(0, 0), 

92 geom.Extent2I(1024, 1153)) 

93 dataset = measTests.TestDataset(self.bbox) 

94 for srcIdx in range(nSources): 

95 dataset.addSource(100000.0, geom.Point2D(100, 100)) 

96 self.inputCatalogNoFlags, _ = make_input_source_catalog(dataset, False) 

97 self.inputCatalog, self.exposure = \ 

98 make_input_source_catalog(dataset, True) 

99 

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

101 visit = afwImage.VisitInfo( 

102 exposureId=4321, 

103 exposureTime=200., 

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

105 self.exposure.setDetector(detector) 

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

107 self.exposure.setFilterLabel(afwImage.FilterLabel(band='g', physical='g.MP9401')) 

108 scale = 2 

109 scaleErr = 1 

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

111 self.exposure.setPhotoCalib(self.photoCalib) 

112 

113 def test_run(self): 

114 """Test the generic data product mapper. 

115 """ 

116 outSchema = afwTable.SourceTable.makeMinimalSchema() 

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

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

119 

120 mapApDConfig = MapApDataConfig() 

121 mapApDConfig.copyColumns = { 

122 "id": "id", 

123 "parent": "parent", 

124 "coord_ra": "coord_ra", 

125 "coord_dec": "coord_dec", 

126 "slot_PsfFlux_instFlux": "psFlux", 

127 "slot_PsfFlux_instFluxErr": "psFluxErr" 

128 } 

129 

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

131 outputSchema=outSchema, 

132 config=mapApDConfig) 

133 outputCatalog = mapApD.run(self.inputCatalog) 

134 

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

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

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

138 

139 def test_run_dia_source(self): 

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

141 """ 

142 mapApDConfig = self._create_map_dia_source_config() 

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

144 config=mapApDConfig) 

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

146 

147 expectedMeanDip = 2. 

148 expectedDiffFlux = 0. 

149 expectedLength = 0.19999994425496748 

150 expectedBBox = 18 

151 

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

153 self.assertEqual( 

154 outObj["ccdVisitId"], 

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

156 self.assertEqual( 

157 outObj["midPointTai"], 

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

159 system=dafBase.DateTime.MJD)) 

160 self.assertEqual( 

161 outObj["flags"], 

162 1 * 2 ** 0 + 1 * 2 ** 1) 

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

164 if inputName.startswith("slot_PsfFlux"): 

165 self._test_calibrated_flux(inObj, outObj) 

166 else: 

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

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

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

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

171 self.assertEqual(outObj["bboxSize"], expectedBBox) 

172 # Mapper should always emit standardized filters 

173 self.assertEqual(outObj["filterName"], 'g') 

174 

175 def _create_map_dia_source_config(self): 

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

177 

178 Returns 

179 ------- 

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

181 Configurable for use in MapDiaSourceTask. 

182 """ 

183 configurable = MapDiaSourceConfig() 

184 configurable.copyColumns = { 

185 "id": "id", 

186 "parent": "parent", 

187 "coord_ra": "coord_ra", 

188 "coord_dec": "coord_dec", 

189 "slot_PsfFlux_instFlux": "psFlux", 

190 "slot_PsfFlux_instFluxErr": "psFluxErr" 

191 } 

192 configurable.calibrateColumns = ["slot_PsfFlux"] 

193 configurable.flagMap = os.path.join( 

194 getPackageDir("ap_association"), 

195 "tests", 

196 "data", 

197 "test-flag-map.yaml") 

198 

199 return configurable 

200 

201 def test_run_dia_source_wrong_flags(self): 

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

203 that are not in the input schema. 

204 """ 

205 mapApDConfig = self._create_map_dia_source_config() 

206 with self.assertRaises(KeyError): 

207 MapDiaSourceTask(inputSchema=self.inputCatalogNoFlags.schema, 

208 config=mapApDConfig) 

209 

210 def test_calibrateFluxes(self): 

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

212 """ 

213 outSchema = afwTable.SourceTable.makeMinimalSchema() 

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

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

216 

217 outputCatalog = afwTable.SourceCatalog(outSchema) 

218 outRecord = outputCatalog.addNew() 

219 

220 mapApDConfig = self._create_map_dia_source_config() 

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

222 config=mapApDConfig) 

223 

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

225 outRecord, 

226 self.photoCalib) 

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

228 

229 def _test_calibrated_flux(self, inputRecord, outputRecord): 

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

231 

232 Parameters 

233 ---------- 

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

235 Input source record with uncalibrated flux values. 

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

237 Source record with calibrated fluxes. 

238 """ 

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

240 inputRecord["slot_PsfFlux_instFluxErr"]) 

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

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

243 

244 def test_computeBBoxSize(self): 

245 """Test the values created for diaSourceBBox. 

246 """ 

247 outSchema = afwTable.SourceTable.makeMinimalSchema() 

248 outSchema.addField("bboxSize", type="I") 

249 outputCatalog = afwTable.SourceCatalog(outSchema) 

250 outRecord = outputCatalog.addNew() 

251 mapApDConfig = self._create_map_dia_source_config() 

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

253 config=mapApDConfig) 

254 mapApD.computeBBoxSize(self.inputCatalog[0], outRecord) 

255 

256 self.assertEqual(outRecord["bboxSize"], 18) 

257 

258 def test_bit_unpacker(self): 

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

260 """ 

261 mapApDConfig = self._create_map_dia_source_config() 

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

263 config=mapApDConfig) 

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

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

266 obj.set("base_PixelFlags_flag", 0) 

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

268 obj.set("base_PixelFlags_flag_offimage", 0) 

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

270 

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

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

273 

274 for idx, flag in enumerate(flag_values): 

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

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

277 else: 

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

279 

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

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

282 else: 

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

284 

285 

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

287 pass 

288 

289 

290def setup_module(module): 

291 lsst.utils.tests.init() 

292 

293 

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

295 lsst.utils.tests.init() 

296 unittest.main()