Coverage for tests/test_association_task.py : 10%

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/>.
22import numpy as np
23import pandas as pd
24import unittest
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
36from lsst.ap.association import \
37 AssociationTask, \
38 make_dia_source_schema, \
39 make_dia_object_schema
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)
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)
92 if wcs is not None:
93 xyCentroid = wcs.skykToPixel(coord)
94 src.set("x", xyCentroid.getX())
95 src.set("y", xyCentroid.getY())
97 return sources
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)
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)
150 if wcs is not None:
151 xyCentroid = wcs.skykToPixel(coord)
152 src.set("x", xyCentroid.getX())
153 src.set("y", xyCentroid.getY())
155 sources = sources.asAstropy().to_pandas()
157 return sources
160class TestAssociationTask(unittest.TestCase):
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")
174 self.dia_object_schema = make_dia_object_schema()
176 # metadata taken from CFHT data
177 # v695856-e0/v695856-e0-c000-a00.sci_img.fits
179 self.metadata = dafBase.PropertySet()
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.)
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')
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)
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))
219 bbox = geom.Box2D(self.exposure.getBBox())
220 wcs = self.exposure.getWcs()
222 self.pixelator = sphgeom.HtmPixelization(20)
223 region = sphgeom.ConvexPolygon([wcs.pixelToSky(pp).getVector()
224 for pp in bbox.getCorners()])
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()
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
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)
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)
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)
282 def _run_association_and_retrieve_objects(self, create_objects=False):
283 """Convenience method for testing the Association run method.
285 Parameters
286 ----------
287 create_objects : `bool`
288 Boolean specifying if seed DIAObjects and DIASources should be
289 inserted into the database before association.
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)
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-21333: Remove [0] (first character only) workaround
327 filterName=self.exposure.getFilter().getCanonicalName()[0],
328 filterId=self.exposure.getFilter().getId(),
329 ccdVisitId=self.exposure.getInfo().getVisitInfo().getExposureId(),
330 midPointTai=self.exposure.getInfo().getVisitInfo().getDate().get(system=dafBase.DateTime.MJD))
332 assoc_task = AssociationTask()
334 diaSources = dia_sources.asAstropy().to_pandas()
335 diaSources.rename(columns={"coord_ra": "ra",
336 "coord_dec": "decl",
337 "id": "diaSourceId",
338 "parent": "parentDiaSourceId"},
339 inplace=True)
340 diaSources["ra"] = np.degrees(diaSources["ra"])
341 diaSources["decl"] = np.degrees(diaSources["decl"])
343 if len(diaObjects) == 0:
344 diaSourceHistory = pd.DataFrame(columns=["diaObjectId",
345 "filterName",
346 "diaSourceId"])
347 diaSourceHistory.set_index(
348 ["diaObjectId", "filterName", "diaSourceId"],
349 drop=False,
350 inplace=True)
352 results = assoc_task.run(diaSources,
353 diaObjects,
354 diaSourceHistory)
355 return results.diaObjects
357 def _set_source_values(self, dia_source, flux, fluxErr, filterName,
358 filterId, ccdVisitId, midPointTai):
359 """Set fluxes and visit info for DiaSources.
361 Parameters
362 ----------
363 dia_source : `lsst.afw.table.SourceRecord`
364 SourceRecord object to edit.
365 flux : `double`
366 Flux of DiaSource
367 fluxErr : `double`
368 Flux error of DiaSource
369 filterName : `string`
370 Name of filter for flux.
371 filterId : `int`
372 Unique id of filter.
373 ccdVisitId : `int`
374 Integer id of this ccd/visit.
375 midPointTai : `double`
376 Time of observation
377 """
378 dia_source['ccdVisitId'] = ccdVisitId
379 dia_source["midPointTai"] = midPointTai
380 dia_source["psFlux"] = flux / self.flux0
381 dia_source["psFluxErr"] = np.sqrt(
382 (fluxErr / self.flux0) ** 2
383 + (flux * self.flux0_err / self.flux0 ** 2) ** 2)
384 dia_source["apFlux"] = flux / self.flux0
385 dia_source["apFluxErr"] = np.sqrt(
386 (fluxErr / self.flux0) ** 2
387 + (flux * self.flux0_err / self.flux0 ** 2) ** 2)
388 dia_source["totFlux"] = flux / self.flux0
389 dia_source["totFluxErr"] = np.sqrt(
390 (fluxErr / self.flux0) ** 2
391 + (flux * self.flux0_err / self.flux0 ** 2) ** 2)
392 dia_source["filterName"] = filterName
393 dia_source["filterId"] = filterId
394 dia_source["x"] = 0.
395 dia_source["y"] = 0.
397 def _create_dia_objects_and_sources(self):
398 """Method for storing a set of test DIAObjects and sources into
399 the L1 database.
400 """
402 # This should create a DB of 5 DIAObjects with 2 DIASources associated
403 # to them. The DIASources are "observed" in g and r.
405 # Create DIObjects, give them fluxes, and store them
406 n_objects = 5
407 object_centers = np.array([
408 [self.wcs.pixelToSky(idx, idx).getRa().asDegrees(),
409 self.wcs.pixelToSky(idx, idx).getDec().asDegrees()]
410 for idx in np.linspace(1, 1000, 10)])
411 dia_objects = create_test_points(
412 point_locs_deg=object_centers[:n_objects],
413 start_id=0,
414 schema=self.dia_object_schema,
415 scatter_arcsec=-1,)
416 # Set the DIAObject fluxes and number of associated sources.
417 for dia_object in dia_objects:
418 dia_object["nDiaSources"] = 2
419 for filter_name in self.filter_names:
420 sphPoint = geom.SpherePoint(dia_object.getCoord())
421 htmIndex = self.pixelator.index(sphPoint.getVector())
422 dia_object["pixelId"] = htmIndex
423 dia_object['%sPSFluxMean' % filter_name] = 1
424 dia_object['%sPSFluxMeanErr' % filter_name] = 1
425 dia_object['%sPSFluxSigma' % filter_name] = 1
426 dia_object['%sPSFluxNdata' % filter_name] = 1
427 dia_objects = dia_objects.asAstropy().to_pandas()
428 dia_objects.rename(columns={"coord_ra": "ra",
429 "coord_dec": "decl",
430 "id": "diaObjectId"},
431 inplace=True)
432 dia_objects["ra"] = np.degrees(dia_objects["ra"])
433 dia_objects["decl"] = np.degrees(dia_objects["decl"])
435 dateTime = dafBase.DateTime("2014-05-13T16:00:00.000000000",
436 dafBase.DateTime.Timescale.TAI)
438 # Create DIASources, update their ccdVisitId and fluxes, and store
439 # them.
440 dia_sources = create_test_points(
441 point_locs_deg=np.concatenate(
442 [object_centers[:n_objects], object_centers[:n_objects]]),
443 start_id=0,
444 scatter_arcsec=-1,
445 associated_ids=[0, 1, 2, 3, 4,
446 0, 1, 2, 3, 4])
447 for src_idx, dia_source in enumerate(dia_sources):
448 if src_idx < n_objects:
449 self._set_source_values(
450 dia_source=dia_source,
451 flux=10000,
452 fluxErr=100,
453 filterName='g',
454 filterId=1,
455 ccdVisitId=1232,
456 midPointTai=dateTime.get(system=dafBase.DateTime.MJD))
457 else:
458 self._set_source_values(
459 dia_source=dia_source,
460 flux=10000,
461 fluxErr=100,
462 filterName='r',
463 filterId=2,
464 ccdVisitId=1233,
465 midPointTai=dateTime.get(system=dafBase.DateTime.MJD))
466 dia_sources = dia_sources.asAstropy().to_pandas()
467 dia_sources.rename(columns={"coord_ra": "ra",
468 "coord_dec": "decl",
469 "id": "diaSourceId",
470 "parent": "parentDiaSourceId"},
471 inplace=True)
472 dia_sources["ra"] = np.degrees(dia_sources["ra"])
473 dia_sources["decl"] = np.degrees(dia_sources["decl"])
474 return dia_objects, dia_sources
476 def test_associate_sources(self):
477 """Test the performance of the associate_sources method in
478 AssociationTask.
479 """
480 n_objects = 5
481 dia_objects = create_test_points_pandas(
482 point_locs_deg=[[0.04 * obj_idx, 0.04 * obj_idx]
483 for obj_idx in range(n_objects)],
484 start_id=0,
485 schema=self.dia_object_schema,
486 scatter_arcsec=-1,)
487 dia_objects.rename(columns={"coord_ra": "ra",
488 "coord_dec": "decl",
489 "id": "diaObjectId"},
490 inplace=True)
492 n_sources = 5
493 dia_sources = create_test_points_pandas(
494 point_locs_deg=[
495 [0.04 * (src_idx + 1),
496 0.04 * (src_idx + 1)]
497 for src_idx in range(n_sources)],
498 start_id=n_objects,
499 scatter_arcsec=0.1)
500 dia_sources.rename(columns={"coord_ra": "ra",
501 "coord_dec": "decl",
502 "id": "diaSourceId"},
503 inplace=True)
505 assoc_task = AssociationTask()
506 assoc_result = assoc_task.associate_sources(
507 dia_objects, dia_sources)
509 for test_obj_id, expected_obj_id in zip(
510 assoc_result.associated_dia_object_ids,
511 [1, 2, 3, 4, 9]):
512 self.assertEqual(test_obj_id, expected_obj_id)
514 def test_score_and_match(self):
515 """Test association between a set of sources and an existing
516 DIAObjectCollection.
518 This also tests that a DIASource that can't be associated within
519 tolerance is appended to the DIAObjectCollection as a new
520 DIAObject.
521 """
523 assoc_task = AssociationTask()
524 # Create a set of DIAObjects that contain only one DIASource
525 n_objects = 5
526 dia_objects = create_test_points_pandas(
527 point_locs_deg=[[0.04 * obj_idx, 0.04 * obj_idx]
528 for obj_idx in range(n_objects)],
529 start_id=0,
530 schema=self.dia_object_schema,
531 scatter_arcsec=-1,)
532 dia_objects.rename(columns={"coord_ra": "ra",
533 "coord_dec": "decl",
534 "id": "diaObjectId"},
535 inplace=True)
537 n_sources = 5
538 dia_sources = create_test_points_pandas(
539 point_locs_deg=[
540 [0.04 * (src_idx + 1),
541 0.04 * (src_idx + 1)]
542 for src_idx in range(n_sources)],
543 start_id=n_objects,
544 scatter_arcsec=-1)
545 dia_sources.rename(columns={"coord_ra": "ra",
546 "coord_dec": "decl",
547 "id": "diaSourceId"},
548 inplace=True)
550 score_struct = assoc_task.score(dia_objects,
551 dia_sources,
552 1.0 * geom.arcseconds)
553 self.assertFalse(np.isfinite(score_struct.scores[-1]))
554 for src_idx in range(4):
555 # Our scores should be extremely close to 0 but not exactly so due
556 # to machine noise.
557 self.assertAlmostEqual(score_struct.scores[src_idx], 0.0,
558 places=16)
560 # After matching each DIAObject should now contain 2 DIASources
561 # except the last DIAObject in this collection which should be
562 # newly created during the matching step and contain only one
563 # DIASource.
564 match_result = assoc_task.match(dia_objects, dia_sources, score_struct)
565 updated_ids = match_result.associated_dia_object_ids
566 self.assertEqual(len(updated_ids), 5)
567 self.assertEqual(match_result.n_updated_dia_objects, 4)
568 self.assertEqual(match_result.n_new_dia_objects, 1)
569 self.assertEqual(match_result.n_unassociated_dia_objects, 1)
571 # Test updating all DiaObjects
572 n_objects = 4
573 dia_objects = create_test_points_pandas(
574 point_locs_deg=[[0.04 * obj_idx, 0.04 * obj_idx]
575 for obj_idx in range(n_objects)],
576 start_id=0,
577 schema=self.dia_object_schema,
578 scatter_arcsec=-1,)
579 dia_objects.rename(columns={"coord_ra": "ra",
580 "coord_dec": "decl",
581 "id": "diaObjectId"},
582 inplace=True)
584 n_sources = 4
585 dia_sources = create_test_points_pandas(
586 point_locs_deg=[
587 [0.04 * src_idx,
588 0.04 * src_idx]
589 for src_idx in range(n_sources)],
590 start_id=n_objects,
591 scatter_arcsec=-1)
593 dia_sources.rename(columns={"coord_ra": "ra",
594 "coord_dec": "decl",
595 "id": "diaSourceId"},
596 inplace=True)
597 score_struct = assoc_task.score(dia_objects[1:],
598 dia_sources[:-1],
599 1.0 * geom.arcseconds)
600 match_result = assoc_task.match(dia_objects, dia_sources, score_struct)
601 updated_ids = match_result.associated_dia_object_ids
602 self.assertEqual(len(updated_ids), 4)
604 def test_remove_nan_dia_sources(self):
605 n_sources = 6
606 dia_sources = create_test_points_pandas(
607 point_locs_deg=[
608 [0.04 * (src_idx + 1),
609 0.04 * (src_idx + 1)]
610 for src_idx in range(n_sources)],
611 start_id=0,
612 scatter_arcsec=-1)
613 dia_sources.rename(columns={"coord_ra": "ra",
614 "coord_dec": "decl",
615 "id": "diaSourceId"},
616 inplace=True)
618 dia_sources.loc[2, "ra"] = np.nan
619 dia_sources.loc[3, "decl"] = np.nan
620 dia_sources.loc[4, "ra"] = np.nan
621 dia_sources.loc[4, "decl"] = np.nan
622 assoc_task = AssociationTask()
623 out_dia_sources = assoc_task.check_dia_source_radec(dia_sources)
624 self.assertEqual(len(out_dia_sources), n_sources - 3)
627class MemoryTester(lsst.utils.tests.MemoryTestCase):
628 pass
631def setup_module(module):
632 lsst.utils.tests.init()
635if __name__ == "__main__": 635 ↛ 636line 635 didn't jump to line 636, because the condition on line 635 was never true
636 lsst.utils.tests.init()
637 unittest.main()