def testLoadVersion0(self): """Test reading a pre-written format_version=0 (Jy flux) catalog. It should be converted to have nJy fluxes. """ path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'version0', 'ref_cats', 'cal_ref_cat') filenames = sorted(glob.glob(os.path.join(path, '????.fits'))) loader = MockReferenceObjectLoaderFromFiles(filenames, name='cal_ref_cat', htmLevel=4) result = loader.loadSkyCircle(ingestIndexTestBase.make_coord(10, 20), 5 * lsst.geom.degrees, 'a') self.assertTrue(hasNanojanskyFluxUnits(result.refCat.schema)) catalog = afwTable.SimpleCatalog.readFits(filenames[0]) self.assertFloatsEqual(catalog['a_flux'] * 1e9, result.refCat['a_flux']) self.assertFloatsEqual(catalog['a_fluxSigma'] * 1e9, result.refCat['a_fluxErr']) self.assertFloatsEqual(catalog['b_flux'] * 1e9, result.refCat['b_flux']) self.assertFloatsEqual(catalog['b_fluxSigma'] * 1e9, result.refCat['b_fluxErr'])
def setUp(self): np.random.seed(12345) filenames = sorted( glob.glob( os.path.join(RefCatDir, 'ref_cats', 'cal_ref_cat', '??????.fits'))) self.refObjLoader = MockReferenceObjectLoaderFromFiles(filenames, htmLevel=8) center = lsst.geom.SpherePoint(215.5, 53.0, lsst.geom.degrees) radius = 0.5 * lsst.geom.degrees self.filter = "r" self.references = self.refObjLoader.loadSkyCircle( center, radius, self.filter).refCat
def setUp(self): refCatDir = os.path.join(os.path.dirname(__file__), "data", "sdssrefcat") self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(3001, 3001)) crpix = lsst.geom.Box2D(self.bbox).getCenter() self.tanWcs = afwGeom.makeSkyWcs( crpix=crpix, crval=lsst.geom.SpherePoint(215.5, 53.0, lsst.geom.degrees), cdMatrix=afwGeom.makeCdMatrix(scale=5.1e-5 * lsst.geom.degrees)) self.exposure = afwImage.ExposureF(self.bbox) self.exposure.setWcs(self.tanWcs) self.exposure.setFilter( afwImage.FilterLabel(band="r", physical="rTest")) filenames = sorted( glob.glob( os.path.join(refCatDir, 'ref_cats', 'cal_ref_cat', '??????.fits'))) self.refObjLoader = MockReferenceObjectLoaderFromFiles(filenames, htmLevel=8)
class DirectMatchTestCase(lsst.utils.tests.TestCase): """Tests for lsst.meas.astrom.DirectMatchTask""" def setUp(self): np.random.seed(12345) filenames = sorted( glob.glob( os.path.join(RefCatDir, 'ref_cats', 'cal_ref_cat', '??????.fits'))) self.refObjLoader = MockReferenceObjectLoaderFromFiles(filenames, htmLevel=8) center = lsst.geom.SpherePoint(215.5, 53.0, lsst.geom.degrees) radius = 0.5 * lsst.geom.degrees self.filter = "r" self.references = self.refObjLoader.loadSkyCircle( center, radius, self.filter).refCat def tearDown(self): del self.refObjLoader del self.references def checkMatching(self, catalog): config = lsst.meas.astrom.DirectMatchConfig() task = lsst.meas.astrom.DirectMatchTask(config=config, refObjLoader=self.refObjLoader) results = task.run(catalog, self.filter) self.assertEqual(len(results.matches), len(catalog)) for match in results.matches: self.assertEqual(match.first.getId(), match.second.getId()) maxDistance = max(match.distance for match in results.matches) self.assertLess(maxDistance, config.matchRadius) # match.distance is in arcsec self.assertIsNotNone(results.matchMeta) names = results.matchMeta.names() for key in ("RA", "DEC", "RADIUS", "SMATCHV", "FILTER"): self.assertIn(key, names) def testWithoutNoise(self): """Match the reference catalog against itself""" self.checkMatching(self.references) def testWithNoise(self): """Match the reference catalog against a noised version of itself""" references = self.references.copy(True) offset = (0.1 * lsst.geom.arcseconds).asRadians() num = len(references) ra, dec = references["coord_ra"], references["coord_dec"] cosDec = np.cos(dec.mean()) ra += offset / cosDec * np.random.uniform(-1.0, 1.0, num) dec += offset * np.random.uniform(-1.0, 1.0, num) self.checkMatching(references)
def setUp(self): # Load sample input from disk testDir = os.path.dirname(__file__) self.srcCat = afwTable.SourceCatalog.readFits( os.path.join(testDir, "data", "v695833-e0-c000.xy.fits")) self.srcCat["slot_ApFlux_instFluxErr"] = 1 self.srcCat["slot_PsfFlux_instFluxErr"] = 1 # The .xy.fits file has sources in the range ~ [0,2000],[0,4500] # which is bigger than the exposure self.bbox = geom.Box2I(geom.Point2I(0, 0), geom.Extent2I(2048, 4612)) smallExposure = afwImage.ExposureF( os.path.join(testDir, "data", "v695833-e0-c000-a00.sci.fits")) self.exposure = afwImage.ExposureF(self.bbox) self.exposure.setWcs(smallExposure.getWcs()) self.exposure.setFilter( afwImage.FilterLabel(band="i", physical="test-i")) self.exposure.setPhotoCalib(smallExposure.getPhotoCalib()) coordKey = self.srcCat.getCoordKey() centroidKey = self.srcCat.getCentroidSlot().getMeasKey() wcs = self.exposure.getWcs() for src in self.srcCat: src.set(coordKey, wcs.pixelToSky(src.get(centroidKey))) # Make a reference loader filenames = sorted( glob.glob( os.path.join(RefCatDir, 'ref_cats', 'cal_ref_cat', '??????.fits'))) self.refObjLoader = MockReferenceObjectLoaderFromFiles(filenames, htmLevel=8) self.log = logging.getLogger('lsst.testPhotoCal') self.log.setLevel(TRACE) self.config = PhotoCalConfig() self.config.match.matchRadius = 0.5 self.config.match.referenceSelection.doMagLimit = True self.config.match.referenceSelection.magLimit.maximum = 22.0 self.config.match.referenceSelection.magLimit.fluxField = "i_flux" self.config.match.referenceSelection.doFlags = True self.config.match.referenceSelection.flags.good = ['photometric'] self.config.match.referenceSelection.flags.bad = ['resolved'] self.config.match.sourceSelection.doUnresolved = False # Don't have star/galaxy in the srcCat # The test and associated data have been prepared on the basis that we # use the PsfFlux to perform photometry. self.config.fluxField = "base_PsfFlux_instFlux"
def setUp(self): # Load sample input from disk testDir = os.path.dirname(__file__) self.srcSet = SourceCatalog.readFits( os.path.join(testDir, "v695833-e0-c000.xy.fits")) self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(2048, 4612)) # approximate # create an exposure with the right metadata; the closest thing we have is # apparently v695833-e0-c000-a00.sci.fits, which is much too small smallExposure = ExposureF( os.path.join(testDir, "v695833-e0-c000-a00.sci.fits")) self.exposure = ExposureF(self.bbox) self.exposure.setWcs(smallExposure.getWcs()) self.exposure.setFilter(smallExposure.getFilter()) # copy the pixels we can, in case the user wants a debug display mi = self.exposure.getMaskedImage() mi.assign(smallExposure.getMaskedImage(), smallExposure.getBBox()) logLevel = logging.INFO refCatDir = os.path.join(testDir, "data", "sdssrefcat") filenames = sorted( glob.glob( os.path.join(refCatDir, 'ref_cats', 'cal_ref_cat', '??????.fits'))) refObjLoader = MockReferenceObjectLoaderFromFiles(filenames, htmLevel=8) astrometryConfig = AstrometryTask.ConfigClass() self.astrom = AstrometryTask(config=astrometryConfig, refObjLoader=refObjLoader) self.astrom.log.setLevel(logLevel) # Since our sourceSelector is a registry object we have to wait for it to be created # before setting default values. self.astrom.sourceSelector.config.minSnr = 0
class TestAstrometricSolver(lsst.utils.tests.TestCase): def setUp(self): refCatDir = os.path.join(os.path.dirname(__file__), "data", "sdssrefcat") self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(3001, 3001)) crpix = lsst.geom.Box2D(self.bbox).getCenter() self.tanWcs = afwGeom.makeSkyWcs( crpix=crpix, crval=lsst.geom.SpherePoint(215.5, 53.0, lsst.geom.degrees), cdMatrix=afwGeom.makeCdMatrix(scale=5.1e-5 * lsst.geom.degrees)) self.exposure = afwImage.ExposureF(self.bbox) self.exposure.setWcs(self.tanWcs) self.exposure.setFilter( afwImage.FilterLabel(band="r", physical="rTest")) filenames = sorted( glob.glob( os.path.join(refCatDir, 'ref_cats', 'cal_ref_cat', '??????.fits'))) self.refObjLoader = MockReferenceObjectLoaderFromFiles(filenames, htmLevel=8) def tearDown(self): del self.tanWcs del self.exposure del self.refObjLoader def testTrivial(self): """Test fit with no distortion """ self.doTest(afwGeom.makeIdentityTransform()) def testRadial(self): """Test fit with radial distortion The offset comes from the fact that the CCD is not centered """ self.doTest(afwGeom.makeRadialTransform([0, 1.01, 1e-7])) def testUsedFlag(self): """Test that the solver will record number of sources used to table if it is passed a schema on initialization. """ self.exposure.setWcs(self.tanWcs) config = AstrometryTask.ConfigClass() config.wcsFitter.order = 2 config.wcsFitter.numRejIter = 0 sourceSchema = afwTable.SourceTable.makeMinimalSchema() measBase.SingleFrameMeasurementTask( schema=sourceSchema) # expand the schema # schema must be passed to the solver task constructor solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader, schema=sourceSchema) sourceCat = self.makeSourceCat(self.tanWcs, sourceSchema=sourceSchema) results = solver.run( sourceCat=sourceCat, exposure=self.exposure, ) # check that the used flag is set the right number of times count = 0 for source in sourceCat: if source.get('calib_astrometry_used'): count += 1 self.assertEqual(count, len(results.matches)) def testMaxMeanDistance(self): """If the astrometric fit does not satisfy the maxMeanDistanceArcsec threshold, ensure task raises an lsst.pipe.base.TaskError. """ self.exposure.setWcs(self.tanWcs) config = AstrometryTask.ConfigClass() config.maxMeanDistanceArcsec = 0.0 # To ensure a "deemed" WCS failure solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader) sourceCat = self.makeSourceCat(self.tanWcs, doScatterCentroids=True) with self.assertRaisesRegex(pipeBase.TaskError, "Fatal astrometry failure detected"): solver.run(sourceCat=sourceCat, exposure=self.exposure) def doTest(self, pixelsToTanPixels, order=3): """Test using pixelsToTanPixels to distort the source positions """ distortedWcs = afwGeom.makeModifiedWcs( pixelTransform=pixelsToTanPixels, wcs=self.tanWcs, modifyActualPixels=False) self.exposure.setWcs(distortedWcs) sourceCat = self.makeSourceCat(distortedWcs) config = AstrometryTask.ConfigClass() config.wcsFitter.order = order config.wcsFitter.numRejIter = 0 solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader) results = solver.run( sourceCat=sourceCat, exposure=self.exposure, ) fitWcs = self.exposure.getWcs() self.assertRaises(Exception, self.assertWcsAlmostEqualOverBBox, fitWcs, distortedWcs) self.assertWcsAlmostEqualOverBBox(distortedWcs, fitWcs, self.bbox, maxDiffSky=0.01 * lsst.geom.arcseconds, maxDiffPix=0.02) srcCoordKey = afwTable.CoordKey(sourceCat.schema["coord"]) refCoordKey = afwTable.CoordKey(results.refCat.schema["coord"]) refCentroidKey = afwTable.Point2DKey(results.refCat.schema["centroid"]) maxAngSep = 0 * lsst.geom.radians maxPixSep = 0 for refObj, src, d in results.matches: refCoord = refObj.get(refCoordKey) refPixPos = refObj.get(refCentroidKey) srcCoord = src.get(srcCoordKey) srcPixPos = src.getCentroid() angSep = refCoord.separation(srcCoord) maxAngSep = max(maxAngSep, angSep) pixSep = math.hypot(*(srcPixPos - refPixPos)) maxPixSep = max(maxPixSep, pixSep) print("max angular separation = %0.4f arcsec" % (maxAngSep.asArcseconds(), )) print("max pixel separation = %0.3f" % (maxPixSep, )) self.assertLess(maxAngSep.asArcseconds(), 0.0038) self.assertLess(maxPixSep, 0.021) # try again, invoking the reference selector config.referenceSelector.doUnresolved = True config.referenceSelector.unresolved.name = 'resolved' solverRefSelect = AstrometryTask(config=config, refObjLoader=self.refObjLoader) self.exposure.setWcs(distortedWcs) resultsRefSelect = solverRefSelect.run( sourceCat=sourceCat, exposure=self.exposure, ) self.assertLess(len(resultsRefSelect.matches), len(results.matches)) # try again, allowing magnitude outlier rejection. config.doMagnitudeOutlierRejection = True solverMagOutlierRejection = AstrometryTask( config=config, refObjLoader=self.refObjLoader) self.exposure.setWcs(distortedWcs) resultsMagOutlierRejection = solverMagOutlierRejection.run( sourceCat=sourceCat, exposure=self.exposure, ) self.assertLess(len(resultsMagOutlierRejection.matches), len(resultsRefSelect.matches)) config.doMagnitudeOutlierRejection = False # try again, but without fitting the WCS, no reference selector config.referenceSelector.doUnresolved = False config.forceKnownWcs = True solverNoFit = AstrometryTask(config=config, refObjLoader=self.refObjLoader) self.exposure.setWcs(distortedWcs) resultsNoFit = solverNoFit.run( sourceCat=sourceCat, exposure=self.exposure, ) self.assertIsNone(resultsNoFit.scatterOnSky) # fitting should result in matches that are at least as good # (strictly speaking fitting might result in a larger match list with # some outliers, but in practice this test passes) meanFitDist = np.mean([match.distance for match in results.matches]) meanNoFitDist = np.mean( [match.distance for match in resultsNoFit.matches]) self.assertLessEqual(meanFitDist, meanNoFitDist) # try once again, without fitting the WCS, with the reference selector # (this goes through a different code path) config.referenceSelector.doUnresolved = True solverNoFitRefSelect = AstrometryTask(config=config, refObjLoader=self.refObjLoader) resultsNoFitRefSelect = solverNoFitRefSelect.run( sourceCat=sourceCat, exposure=self.exposure, ) self.assertLess(len(resultsNoFitRefSelect.matches), len(resultsNoFit.matches)) def makeSourceCat(self, wcs, sourceSchema=None, doScatterCentroids=False): """Make a source catalog by reading the position reference stars using the proviced WCS. Optionally provide a schema for the source catalog (to allow AstrometryTask in the test methods to update it with the "calib_astrometry_used" flag). Otherwise, a minimal SourceTable schema will be created. Optionally, via doScatterCentroids, add some scatter to the centroids assiged to the source catalog (otherwise they will be identical to those of the reference catalog). """ loadRes = self.refObjLoader.loadPixelBox(bbox=self.bbox, wcs=wcs, filterName="r") refCat = loadRes.refCat if sourceSchema is None: sourceSchema = afwTable.SourceTable.makeMinimalSchema() measBase.SingleFrameMeasurementTask( schema=sourceSchema) # expand the schema sourceCat = afwTable.SourceCatalog(sourceSchema) sourceCat.resize(len(refCat)) scatterFactor = 1.0 if doScatterCentroids: np.random.seed(12345) scatterFactor = np.random.uniform(0.999, 1.001, len(sourceCat)) sourceCat["slot_Centroid_x"] = scatterFactor * refCat["centroid_x"] sourceCat["slot_Centroid_y"] = scatterFactor * refCat["centroid_y"] sourceCat["slot_ApFlux_instFlux"] = refCat["r_flux"] sourceCat["slot_ApFlux_instFluxErr"] = refCat["r_flux"] / 100 # Deliberately add some outliers to check that the magnitude # outlier rejection code is being run. sourceCat["slot_ApFlux_instFlux"][0:4] *= 1000.0 return sourceCat