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 pandas as pd 

24import unittest 

25 

26from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

27import lsst.afw.geom as afwGeom 

28import lsst.afw.image as afwImage 

29import lsst.afw.image.utils as afwImageUtils 

30import lsst.afw.table as afwTable 

31import lsst.daf.base as dafBase 

32import lsst.geom as geom 

33import lsst.sphgeom as sphgeom 

34import lsst.utils.tests 

35 

36from lsst.ap.association import \ 

37 AssociationTask, \ 

38 make_dia_source_schema, \ 

39 make_dia_object_schema 

40 

41 

42def create_test_points(point_locs_deg, 

43 wcs=None, 

44 start_id=0, 

45 schema=None, 

46 scatter_arcsec=1.0, 

47 indexer_ids=None, 

48 associated_ids=None): 

49 """Create dummy DIASources or DIAObjects for use in our tests. 

50 Parameters 

51 ---------- 

52 point_locs_deg : array-like (N, 2) of `float`s 

53 Positions of the test points to create in RA, DEC. 

54 wcs : `lsst.afw.geom.SkyWcs` 

55 Wcs to convert RA/Dec to x/y if provided. 

56 start_id : `int` 

57 Unique id of the first object to create. The remaining sources are 

58 incremented by one from the first id. 

59 schema : `lsst.afw.table.Schema` 

60 Schema of the objects to create. Defaults to the DIASource schema. 

61 scatter_arcsec : `float` 

62 Scatter to add to the position of each DIASource. 

63 indexer_ids : `list` of `ints`s 

64 Id numbers of pixelization indexer to store. Must be the same length 

65 as the first dimension of point_locs_deg. 

66 associated_ids : `list` of `ints`s 

67 Id numbers of associated DIAObjects to store. Must be the same length 

68 as the first dimension of point_locs_deg. 

69 Returns 

70 ------- 

71 test_points : `lsst.afw.table.SourceCatalog` 

72 Catalog of points to test. 

73 """ 

74 if schema is None: 

75 schema = make_dia_source_schema() 

76 sources = afwTable.SourceCatalog(schema) 

77 

78 for src_idx, (ra, dec,) in enumerate(point_locs_deg): 

79 src = sources.addNew() 

80 src['id'] = src_idx + start_id 

81 coord = geom.SpherePoint(ra, dec, geom.degrees) 

82 if scatter_arcsec > 0.0: 

83 coord = coord.offset( 

84 np.random.rand() * 360 * geom.degrees, 

85 np.random.rand() * scatter_arcsec * geom.arcseconds) 

86 if indexer_ids is not None: 

87 src['pixelId'] = indexer_ids[src_idx] 

88 if associated_ids is not None: 

89 src['diaObjectId'] = associated_ids[src_idx] 

90 src.setCoord(coord) 

91 

92 if wcs is not None: 

93 xyCentroid = wcs.skykToPixel(coord) 

94 src.set("x", xyCentroid.getX()) 

95 src.set("y", xyCentroid.getY()) 

96 

97 return sources 

98 

99 

100def create_test_points_pandas(point_locs_deg, 

101 wcs=None, 

102 start_id=0, 

103 schema=None, 

104 scatter_arcsec=1.0, 

105 indexer_ids=None, 

106 associated_ids=None): 

107 """Create dummy DIASources or DIAObjects for use in our tests. 

108 Parameters 

109 ---------- 

110 point_locs_deg : array-like (N, 2) of `float`s 

111 Positions of the test points to create in RA, DEC. 

112 wcs : `lsst.afw.geom.SkyWcs` 

113 Wcs to convert RA/Dec to x/y if provided. 

114 start_id : `int` 

115 Unique id of the first object to create. The remaining sources are 

116 incremented by one from the first id. 

117 schema : `lsst.afw.table.Schema` 

118 Schema of the objects to create. Defaults to the DIASource schema. 

119 scatter_arcsec : `float` 

120 Scatter to add to the position of each DIASource. 

121 indexer_ids : `list` of `ints`s 

122 Id numbers of pixelization indexer to store. Must be the same length 

123 as the first dimension of point_locs_deg. 

124 associated_ids : `list` of `ints`s 

125 Id numbers of associated DIAObjects to store. Must be the same length 

126 as the first dimension of point_locs_deg. 

127 Returns 

128 ------- 

129 test_points : `pandas.DataFrame` 

130 Catalog of points to test. 

131 """ 

132 if schema is None: 

133 schema = make_dia_source_schema() 

134 sources = afwTable.SourceCatalog(schema) 

135 

136 for src_idx, (ra, dec,) in enumerate(point_locs_deg): 

137 src = sources.addNew() 

138 src['id'] = src_idx + start_id 

139 coord = geom.SpherePoint(ra, dec, geom.degrees) 

140 if scatter_arcsec > 0.0: 

141 coord = coord.offset( 

142 np.random.rand() * 360 * geom.degrees, 

143 np.random.rand() * scatter_arcsec * geom.arcseconds) 

144 if indexer_ids is not None: 

145 src['pixelId'] = indexer_ids[src_idx] 

146 if associated_ids is not None: 

147 src['diaObjectId'] = associated_ids[src_idx] 

148 src.setCoord(coord) 

149 

150 if wcs is not None: 

151 xyCentroid = wcs.skykToPixel(coord) 

152 src.set("x", xyCentroid.getX()) 

153 src.set("y", xyCentroid.getY()) 

154 

155 sources = sources.asAstropy().to_pandas() 

156 

157 return sources 

158 

159 

160class TestAssociationTask(unittest.TestCase): 

161 

162 def setUp(self): 

163 """Create a sqlite3 database with default tables and schemas. 

164 """ 

165 # CFHT Filters from the camera mapper. 

166 self.filter_names = ["u", "g", "r", "i", "z"] 

167 afwImageUtils.resetFilters() 

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

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

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

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

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

173 

174 self.dia_object_schema = make_dia_object_schema() 

175 

176 # metadata taken from CFHT data 

177 # v695856-e0/v695856-e0-c000-a00.sci_img.fits 

178 

179 self.metadata = dafBase.PropertySet() 

180 

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

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

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

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

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

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

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

188 

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

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

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

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

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

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

195 

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

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

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

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

200 

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

202 self.exposure = afwImage.makeExposure( 

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

204 self.wcs) 

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

206 visit = afwImage.VisitInfo( 

207 exposureId=1234, 

208 exposureTime=200., 

209 date=dafBase.DateTime("2014-05-13T17:00:00.000000000", 

210 dafBase.DateTime.Timescale.TAI)) 

211 self.exposure.setDetector(detector) 

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

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

214 self.flux0 = 10000 

215 self.flux0_err = 100 

216 self.exposure.setPhotoCalib( 

217 afwImage.PhotoCalib(self.flux0, self.flux0_err)) 

218 

219 bbox = geom.Box2D(self.exposure.getBBox()) 

220 wcs = self.exposure.getWcs() 

221 

222 self.pixelator = sphgeom.HtmPixelization(20) 

223 region = sphgeom.ConvexPolygon([wcs.pixelToSky(pp).getVector() 

224 for pp in bbox.getCorners()]) 

225 

226 indices = self.pixelator.envelope(region, 64) 

227 # Index types must be cast to int to work with dax_apdb. 

228 self.index_ranges = indices.ranges() 

229 

230 def tearDown(self): 

231 """Delete the database after we are done with it. 

232 """ 

233 del self.metadata 

234 del self.wcs 

235 del self.exposure 

236 

237 def test_run(self): 

238 """Test the run method with a database that already exists and 

239 contains DIAObjects and Sources. 

240 """ 

241 dia_objects = self._run_association_and_retrieve_objects(True) 

242 not_updated_idx = 0 

243 updated_idx_start = 1 

244 new_idx_start = 5 

245 total_expected_dia_objects = 10 

246 self.assertEqual(len(dia_objects), total_expected_dia_objects) 

247 

248 # Test to make sure the number of DIAObjects have been properly 

249 # associated within the db. 

250 for obj_idx, (df_idx, dia_object) in enumerate(dia_objects.iterrows()): 

251 if df_idx == not_updated_idx: 

252 # Test the DIAObject we expect to not be associated with any 

253 # new DIASources. 

254 self.assertEqual(dia_object['gPSFluxNdata'], 1) 

255 self.assertEqual(dia_object['rPSFluxNdata'], 1) 

256 self.assertEqual(dia_object['nDiaSources'], 2) 

257 self.assertEqual(df_idx, obj_idx) 

258 elif updated_idx_start <= df_idx < new_idx_start: 

259 # Test that associating to the existing DIAObjects went 

260 # as planned and test that the IDs of the newly associated 

261 # DIASources is correct. 

262 self.assertEqual(dia_object['gPSFluxNdata'], 2) 

263 self.assertEqual(dia_object['rPSFluxNdata'], 1) 

264 self.assertEqual(dia_object['nDiaSources'], 3) 

265 self.assertEqual(df_idx, obj_idx) 

266 else: 

267 self.assertEqual(dia_object['gPSFluxNdata'], 1) 

268 self.assertEqual(dia_object['nDiaSources'], 1) 

269 self.assertEqual(df_idx, obj_idx + 4 + 5) 

270 

271 def test_run_no_existing_objects(self): 

272 """Test the run method with a completely empty database. 

273 """ 

274 dia_objects = self._run_association_and_retrieve_objects(False) 

275 total_expected_dia_objects = 9 

276 self.assertEqual(len(dia_objects), 

277 total_expected_dia_objects) 

278 for obj_idx, (df_idx, output_dia_object) in enumerate(dia_objects.iterrows()): 

279 self.assertEqual(output_dia_object['gPSFluxNdata'], 1) 

280 self.assertEqual(df_idx, obj_idx + 10) 

281 

282 def _run_association_and_retrieve_objects(self, create_objects=False): 

283 """Convenience method for testing the Association run method. 

284 

285 Parameters 

286 ---------- 

287 create_objects : `bool` 

288 Boolean specifying if seed DIAObjects and DIASources should be 

289 inserted into the database before association. 

290 

291 Return 

292 ------ 

293 dia_objects : `lsst.afw.table.SourceCatalog` 

294 Final set of DIAObjects to be tested. 

295 """ 

296 if create_objects: 

297 diaObjects, diaSourceHistory = \ 

298 self._create_dia_objects_and_sources() 

299 else: 

300 diaObjects = pd.DataFrame(columns=["diaObjectId"]) 

301 diaSourceHistory = pd.DataFrame(columns=["diaObjectId", 

302 "filterName", 

303 "diaSourceId"]) 

304 diaObjects.set_index("diaObjectId", 

305 inplace=True, 

306 drop=False) 

307 diaSourceHistory.set_index(["diaObjectId", 

308 "filterName", 

309 "diaSourceId"], 

310 inplace=True, 

311 drop=False) 

312 

313 source_centers = [ 

314 [self.wcs.pixelToSky(idx, idx).getRa().asDegrees(), 

315 self.wcs.pixelToSky(idx, idx).getDec().asDegrees()] 

316 for idx in np.linspace(1, 1000, 10)[1:]] 

317 dia_sources = create_test_points( 

318 point_locs_deg=source_centers, 

319 start_id=10, 

320 scatter_arcsec=-1) 

321 for dia_source in dia_sources: 

322 self._set_source_values( 

323 dia_source=dia_source, 

324 flux=10000, 

325 fluxErr=100, 

326 # TODO DM-27170: fix this [0] workaround which gets a 

327 # single character representation of the band. 

328 filterName=self.exposure.getFilter().getCanonicalName()[0], 

329 filterId=self.exposure.getFilter().getId(), 

330 ccdVisitId=self.exposure.getInfo().getVisitInfo().getExposureId(), 

331 midPointTai=self.exposure.getInfo().getVisitInfo().getDate().get(system=dafBase.DateTime.MJD)) 

332 

333 assoc_task = AssociationTask() 

334 

335 diaSources = dia_sources.asAstropy().to_pandas() 

336 diaSources.rename(columns={"coord_ra": "ra", 

337 "coord_dec": "decl", 

338 "id": "diaSourceId", 

339 "parent": "parentDiaSourceId"}, 

340 inplace=True) 

341 diaSources["ra"] = np.degrees(diaSources["ra"]) 

342 diaSources["decl"] = np.degrees(diaSources["decl"]) 

343 

344 if len(diaObjects) == 0: 

345 diaSourceHistory = pd.DataFrame(columns=["diaObjectId", 

346 "filterName", 

347 "diaSourceId"]) 

348 diaSourceHistory.set_index( 

349 ["diaObjectId", "filterName", "diaSourceId"], 

350 drop=False, 

351 inplace=True) 

352 

353 results = assoc_task.run(diaSources, 

354 diaObjects, 

355 diaSourceHistory) 

356 return results.diaObjects 

357 

358 def _set_source_values(self, dia_source, flux, fluxErr, filterName, 

359 filterId, ccdVisitId, midPointTai): 

360 """Set fluxes and visit info for DiaSources. 

361 

362 Parameters 

363 ---------- 

364 dia_source : `lsst.afw.table.SourceRecord` 

365 SourceRecord object to edit. 

366 flux : `double` 

367 Flux of DiaSource 

368 fluxErr : `double` 

369 Flux error of DiaSource 

370 filterName : `string` 

371 Name of filter for flux. 

372 filterId : `int` 

373 Unique id of filter. 

374 ccdVisitId : `int` 

375 Integer id of this ccd/visit. 

376 midPointTai : `double` 

377 Time of observation 

378 """ 

379 dia_source['ccdVisitId'] = ccdVisitId 

380 dia_source["midPointTai"] = midPointTai 

381 dia_source["psFlux"] = flux / self.flux0 

382 dia_source["psFluxErr"] = np.sqrt( 

383 (fluxErr / self.flux0) ** 2 

384 + (flux * self.flux0_err / self.flux0 ** 2) ** 2) 

385 dia_source["apFlux"] = flux / self.flux0 

386 dia_source["apFluxErr"] = np.sqrt( 

387 (fluxErr / self.flux0) ** 2 

388 + (flux * self.flux0_err / self.flux0 ** 2) ** 2) 

389 dia_source["totFlux"] = flux / self.flux0 

390 dia_source["totFluxErr"] = np.sqrt( 

391 (fluxErr / self.flux0) ** 2 

392 + (flux * self.flux0_err / self.flux0 ** 2) ** 2) 

393 dia_source["filterName"] = filterName 

394 dia_source["filterId"] = filterId 

395 dia_source["x"] = 0. 

396 dia_source["y"] = 0. 

397 

398 def _create_dia_objects_and_sources(self): 

399 """Method for storing a set of test DIAObjects and sources into 

400 the L1 database. 

401 """ 

402 

403 # This should create a DB of 5 DIAObjects with 2 DIASources associated 

404 # to them. The DIASources are "observed" in g and r. 

405 

406 # Create DIObjects, give them fluxes, and store them 

407 n_objects = 5 

408 object_centers = np.array([ 

409 [self.wcs.pixelToSky(idx, idx).getRa().asDegrees(), 

410 self.wcs.pixelToSky(idx, idx).getDec().asDegrees()] 

411 for idx in np.linspace(1, 1000, 10)]) 

412 dia_objects = create_test_points( 

413 point_locs_deg=object_centers[:n_objects], 

414 start_id=0, 

415 schema=self.dia_object_schema, 

416 scatter_arcsec=-1,) 

417 # Set the DIAObject fluxes and number of associated sources. 

418 for dia_object in dia_objects: 

419 dia_object["nDiaSources"] = 2 

420 for filter_name in self.filter_names: 

421 sphPoint = geom.SpherePoint(dia_object.getCoord()) 

422 htmIndex = self.pixelator.index(sphPoint.getVector()) 

423 dia_object["pixelId"] = htmIndex 

424 dia_object['%sPSFluxMean' % filter_name] = 1 

425 dia_object['%sPSFluxMeanErr' % filter_name] = 1 

426 dia_object['%sPSFluxSigma' % filter_name] = 1 

427 dia_object['%sPSFluxNdata' % filter_name] = 1 

428 dia_objects = dia_objects.asAstropy().to_pandas() 

429 dia_objects.rename(columns={"coord_ra": "ra", 

430 "coord_dec": "decl", 

431 "id": "diaObjectId"}, 

432 inplace=True) 

433 dia_objects["ra"] = np.degrees(dia_objects["ra"]) 

434 dia_objects["decl"] = np.degrees(dia_objects["decl"]) 

435 

436 dateTime = dafBase.DateTime("2014-05-13T16:00:00.000000000", 

437 dafBase.DateTime.Timescale.TAI) 

438 

439 # Create DIASources, update their ccdVisitId and fluxes, and store 

440 # them. 

441 dia_sources = create_test_points( 

442 point_locs_deg=np.concatenate( 

443 [object_centers[:n_objects], object_centers[:n_objects]]), 

444 start_id=0, 

445 scatter_arcsec=-1, 

446 associated_ids=[0, 1, 2, 3, 4, 

447 0, 1, 2, 3, 4]) 

448 for src_idx, dia_source in enumerate(dia_sources): 

449 if src_idx < n_objects: 

450 self._set_source_values( 

451 dia_source=dia_source, 

452 flux=10000, 

453 fluxErr=100, 

454 filterName='g', 

455 filterId=1, 

456 ccdVisitId=1232, 

457 midPointTai=dateTime.get(system=dafBase.DateTime.MJD)) 

458 else: 

459 self._set_source_values( 

460 dia_source=dia_source, 

461 flux=10000, 

462 fluxErr=100, 

463 filterName='r', 

464 filterId=2, 

465 ccdVisitId=1233, 

466 midPointTai=dateTime.get(system=dafBase.DateTime.MJD)) 

467 dia_sources = dia_sources.asAstropy().to_pandas() 

468 dia_sources.rename(columns={"coord_ra": "ra", 

469 "coord_dec": "decl", 

470 "id": "diaSourceId", 

471 "parent": "parentDiaSourceId"}, 

472 inplace=True) 

473 dia_sources["ra"] = np.degrees(dia_sources["ra"]) 

474 dia_sources["decl"] = np.degrees(dia_sources["decl"]) 

475 return dia_objects, dia_sources 

476 

477 def test_associate_sources(self): 

478 """Test the performance of the associate_sources method in 

479 AssociationTask. 

480 """ 

481 n_objects = 5 

482 dia_objects = create_test_points_pandas( 

483 point_locs_deg=[[0.04 * obj_idx, 0.04 * obj_idx] 

484 for obj_idx in range(n_objects)], 

485 start_id=0, 

486 schema=self.dia_object_schema, 

487 scatter_arcsec=-1,) 

488 dia_objects.rename(columns={"coord_ra": "ra", 

489 "coord_dec": "decl", 

490 "id": "diaObjectId"}, 

491 inplace=True) 

492 

493 n_sources = 5 

494 dia_sources = create_test_points_pandas( 

495 point_locs_deg=[ 

496 [0.04 * (src_idx + 1), 

497 0.04 * (src_idx + 1)] 

498 for src_idx in range(n_sources)], 

499 start_id=n_objects, 

500 scatter_arcsec=0.1) 

501 dia_sources.rename(columns={"coord_ra": "ra", 

502 "coord_dec": "decl", 

503 "id": "diaSourceId"}, 

504 inplace=True) 

505 

506 assoc_task = AssociationTask() 

507 assoc_result = assoc_task.associate_sources( 

508 dia_objects, dia_sources) 

509 

510 for test_obj_id, expected_obj_id in zip( 

511 assoc_result.associated_dia_object_ids, 

512 [1, 2, 3, 4, 9]): 

513 self.assertEqual(test_obj_id, expected_obj_id) 

514 

515 def test_score_and_match(self): 

516 """Test association between a set of sources and an existing 

517 DIAObjectCollection. 

518 

519 This also tests that a DIASource that can't be associated within 

520 tolerance is appended to the DIAObjectCollection as a new 

521 DIAObject. 

522 """ 

523 

524 assoc_task = AssociationTask() 

525 # Create a set of DIAObjects that contain only one DIASource 

526 n_objects = 5 

527 dia_objects = create_test_points_pandas( 

528 point_locs_deg=[[0.04 * obj_idx, 0.04 * obj_idx] 

529 for obj_idx in range(n_objects)], 

530 start_id=0, 

531 schema=self.dia_object_schema, 

532 scatter_arcsec=-1,) 

533 dia_objects.rename(columns={"coord_ra": "ra", 

534 "coord_dec": "decl", 

535 "id": "diaObjectId"}, 

536 inplace=True) 

537 

538 n_sources = 5 

539 dia_sources = create_test_points_pandas( 

540 point_locs_deg=[ 

541 [0.04 * (src_idx + 1), 

542 0.04 * (src_idx + 1)] 

543 for src_idx in range(n_sources)], 

544 start_id=n_objects, 

545 scatter_arcsec=-1) 

546 dia_sources.rename(columns={"coord_ra": "ra", 

547 "coord_dec": "decl", 

548 "id": "diaSourceId"}, 

549 inplace=True) 

550 

551 score_struct = assoc_task.score(dia_objects, 

552 dia_sources, 

553 1.0 * geom.arcseconds) 

554 self.assertFalse(np.isfinite(score_struct.scores[-1])) 

555 for src_idx in range(4): 

556 # Our scores should be extremely close to 0 but not exactly so due 

557 # to machine noise. 

558 self.assertAlmostEqual(score_struct.scores[src_idx], 0.0, 

559 places=16) 

560 

561 # After matching each DIAObject should now contain 2 DIASources 

562 # except the last DIAObject in this collection which should be 

563 # newly created during the matching step and contain only one 

564 # DIASource. 

565 match_result = assoc_task.match(dia_objects, dia_sources, score_struct) 

566 updated_ids = match_result.associated_dia_object_ids 

567 self.assertEqual(len(updated_ids), 5) 

568 self.assertEqual(match_result.n_updated_dia_objects, 4) 

569 self.assertEqual(match_result.n_new_dia_objects, 1) 

570 self.assertEqual(match_result.n_unassociated_dia_objects, 1) 

571 

572 # Test updating all DiaObjects 

573 n_objects = 4 

574 dia_objects = create_test_points_pandas( 

575 point_locs_deg=[[0.04 * obj_idx, 0.04 * obj_idx] 

576 for obj_idx in range(n_objects)], 

577 start_id=0, 

578 schema=self.dia_object_schema, 

579 scatter_arcsec=-1,) 

580 dia_objects.rename(columns={"coord_ra": "ra", 

581 "coord_dec": "decl", 

582 "id": "diaObjectId"}, 

583 inplace=True) 

584 

585 n_sources = 4 

586 dia_sources = create_test_points_pandas( 

587 point_locs_deg=[ 

588 [0.04 * src_idx, 

589 0.04 * src_idx] 

590 for src_idx in range(n_sources)], 

591 start_id=n_objects, 

592 scatter_arcsec=-1) 

593 

594 dia_sources.rename(columns={"coord_ra": "ra", 

595 "coord_dec": "decl", 

596 "id": "diaSourceId"}, 

597 inplace=True) 

598 score_struct = assoc_task.score(dia_objects[1:], 

599 dia_sources[:-1], 

600 1.0 * geom.arcseconds) 

601 match_result = assoc_task.match(dia_objects, dia_sources, score_struct) 

602 updated_ids = match_result.associated_dia_object_ids 

603 self.assertEqual(len(updated_ids), 4) 

604 

605 def test_remove_nan_dia_sources(self): 

606 n_sources = 6 

607 dia_sources = create_test_points_pandas( 

608 point_locs_deg=[ 

609 [0.04 * (src_idx + 1), 

610 0.04 * (src_idx + 1)] 

611 for src_idx in range(n_sources)], 

612 start_id=0, 

613 scatter_arcsec=-1) 

614 dia_sources.rename(columns={"coord_ra": "ra", 

615 "coord_dec": "decl", 

616 "id": "diaSourceId"}, 

617 inplace=True) 

618 

619 dia_sources.loc[2, "ra"] = np.nan 

620 dia_sources.loc[3, "decl"] = np.nan 

621 dia_sources.loc[4, "ra"] = np.nan 

622 dia_sources.loc[4, "decl"] = np.nan 

623 assoc_task = AssociationTask() 

624 out_dia_sources = assoc_task.check_dia_source_radec(dia_sources) 

625 self.assertEqual(len(out_dia_sources), n_sources - 3) 

626 

627 

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

629 pass 

630 

631 

632def setup_module(module): 

633 lsst.utils.tests.init() 

634 

635 

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

637 lsst.utils.tests.init() 

638 unittest.main()