def test_chebyshev_evaluate(self, seed=1000): """ Test the evaluation of chebyshev polynomials. Parameters ---------- seed: `int`, optional Numpy random seed """ # Set numpy seed for stability np.random.seed(seed=seed) xPos = self.xSize * np.random.rand(self.nStar) yPos = self.ySize * np.random.rand(self.nStar) bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(self.xSize - 1, self.ySize - 1)) # Compute the chebyshev values using the fgcm code fgcmField = fgcm.fgcmUtilities.Cheb2dField(self.xSize, self.ySize, self.pars) fgcmValues = fgcmField.evaluate(xPos, yPos) # Compute the chebyshev values using the afw code field = afwMath.ChebyshevBoundedField(bbox, self.pars) fieldValues = field.evaluate(xPos, yPos) self.assertFloatsAlmostEqual(fieldValues, fgcmValues, rtol=5e-15)
def _getChebyshevPhotoCalib(self, coefficients, err, xyMax, offset, scaling): """ Get the PhotoCalib object from a chebyshev polynomial zeropoint. Parameters ---------- coefficients: `np.array` Flattened array of chebyshev coefficients err: `float` Error on zeropoint xyMax: `list` of length 2 Maximum x and y of the chebyshev bounding box offset: `float` Absolute calibration offset scaling: `float` Flat scaling value from fgcmBuildStars Returns ------- photoCalib: `afwImage.PhotoCalib` """ orderPlus1 = self.chebyshevOrder + 1 pars = np.zeros((orderPlus1, orderPlus1)) bbox = lsst.geom.Box2I(lsst.geom.Point2I(0.0, 0.0), lsst.geom.Point2I(*xyMax)) # Take the zeropoint, apply the absolute relative calibration offset, # and whatever flat-field scaling was applied pars[:, :] = (coefficients.reshape(orderPlus1, orderPlus1) * (offset * units.ABmag).to_value(units.nJy) * scaling) field = afwMath.ChebyshevBoundedField(bbox, pars) calibMean = field.mean() calibErr = (np.log(10.) / 2.5) * calibMean * err photoCalib = afwImage.PhotoCalib(field, calibErr) return photoCalib
def _getChebyshevBoundedField(self, coefficients, xyMax, offset=0.0, scaling=1.0): """ Make a ChebyshevBoundedField from fgcm coefficients, with optional offset and scaling. Parameters ---------- coefficients: `numpy.array` Flattened array of chebyshev coefficients xyMax: `list` of length 2 Maximum x and y of the chebyshev bounding box offset: `float`, optional Absolute calibration offset. Default is 0.0 scaling: `float`, optional Flat scaling value from fgcmBuildStars. Default is 1.0 Returns ------- boundedField: `lsst.afw.math.ChebyshevBoundedField` """ orderPlus1 = int(np.sqrt(coefficients.size)) pars = np.zeros((orderPlus1, orderPlus1)) bbox = lsst.geom.Box2I(lsst.geom.Point2I(0.0, 0.0), lsst.geom.Point2I(*xyMax)) pars[:, :] = (coefficients.reshape(orderPlus1, orderPlus1) * (10.**(offset / -2.5)) * scaling) boundedField = afwMath.ChebyshevBoundedField(bbox, pars) return boundedField
def testCoaddApCorrMap(self): """Check that we can create and use a coadd ApCorrMap.""" coaddBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(100, 100)) scale = 5.0e-5*afwGeom.degrees cdMatrix = afwGeom.makeCdMatrix(scale=scale) crval = afwGeom.SpherePoint(0.0, 0.0, afwGeom.degrees) center = afwGeom.Point2D(afwGeom.Extent2D(coaddBox.getDimensions())*0.5) coaddWcs = afwGeom.makeSkyWcs(crpix=afwGeom.Point2D(0, 0), crval=crval, cdMatrix=cdMatrix) schema = afwTable.ExposureTable.makeMinimalSchema() weightKey = schema.addField("customweightname", type="D", doc="Coadd weight") catalog = afwTable.ExposureCatalog(schema) # Non-overlapping num = 5 inputBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(10, 10)) validBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(7, 7)) pointList = [] pointListValid = [] for i in range(num): value = np.array([[1]], dtype=float) # Constant with value = i+1 apCorrMap = afwImage.ApCorrMap() bf = afwMath.ChebyshevBoundedField(inputBox, value*(i + 1)) apCorrMap.set("only", bf) point = afwGeom.Point2D(0, 0) - afwGeom.Extent2D(coaddBox.getDimensions())*(i+0.5)/num wcs = afwGeom.makeSkyWcs(crpix=point, crval=crval, cdMatrix=cdMatrix) center = afwGeom.Box2D(inputBox).getCenter() pointList.append(coaddWcs.skyToPixel(wcs.pixelToSky(center))) # This point will only be valid for the second overlapping record pointValid = center + afwGeom.Extent2D(4, 4) pointListValid.append(coaddWcs.skyToPixel(wcs.pixelToSky(pointValid))) # A record with the valid polygon defining a limited region record = catalog.getTable().makeRecord() record.setWcs(wcs) record.setBBox(inputBox) record.setApCorrMap(apCorrMap) record.set(weightKey, i + 1) record['id'] = i record.setValidPolygon(afwGeom.Polygon(afwGeom.Box2D(validBox))) catalog.append(record) # An overlapping record with the whole region as valid record = catalog.getTable().makeRecord() record.setWcs(wcs) record.setBBox(inputBox) apCorrMap = afwImage.ApCorrMap() bf = afwMath.ChebyshevBoundedField(inputBox, value*(i + 2)) apCorrMap.set("only", bf) record.setApCorrMap(apCorrMap) record.set(weightKey, i + 2) record['id'] = i + num record.setValidPolygon(afwGeom.Polygon(afwGeom.Box2D(inputBox))) catalog.append(record) apCorrMap = measAlg.makeCoaddApCorrMap(catalog, coaddBox, coaddWcs, "customweightname") # This will test a point where both records contribute self.assertApCorrMap(apCorrMap, pointList) # Only the second record will be valid for this point self.assertApCorrMapValid(apCorrMap, pointListValid) filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "coaddApCorrMap.fits") exposure = afwImage.ExposureF(1, 1) exposure.getInfo().setApCorrMap(apCorrMap) exposure.writeFits(filename) exposure = afwImage.ExposureF(filename) self.assertApCorrMap(exposure.getInfo().getApCorrMap(), pointList) self.assertApCorrMapValid(exposure.getInfo().getApCorrMap(), pointListValid) os.unlink(filename)
def _outputZeropoints(self, butler, offsets): """ Output the zeropoints in jointcal_photoCalib format. Parameters ---------- butler: lsst.daf.persistence.Butler """ self.log.info("Outputting jointcal_photoCalib objects") zptCat = butler.get('fgcmZeropoints', fgcmcycle=self.useCycle) visitCat = butler.get('fgcmVisitCatalog') # Only output those that we have a calibration selected = (zptCat['fgcmflag'] < 16) # Get the mapping from filtername to dataId filter name filterMapping = {} nFound = 0 for rec in zptCat[selected]: if rec['filtername'] in filterMapping: continue dataId = { self.visitDataRefName: int(rec['visit']), self.ccdDataRefName: int(rec['ccd']) } dataRef = butler.dataRef('raw', dataId=dataId) filterMapping[rec['filtername']] = dataRef.dataId['filter'] nFound += 1 if nFound == len(self.filterToBand): break # Get a mapping from filtername to the offsets offsetMapping = {} for f in self.filterToBand: offsetMapping[f] = offsets[self.bands.index(self.filterToBand[f])] # Get a mapping from "ccd" to the ccd index used for the scaling camera = butler.get('camera') ccdMapping = {} for ccdIndex, detector in enumerate(camera): ccdMapping[detector.getId()] = ccdIndex # And a mapping to get the flat-field scaling values scalingMapping = {} for rec in visitCat: scalingMapping[rec['visit']] = rec['scaling'] # Make a fixed variable to hold the parameters to build a ChebyshevBoundedField orderPlus1 = self.chebyshevOrder + 1 pars = np.zeros((orderPlus1, orderPlus1)) for rec in zptCat[selected]: if self.superStarSubCcd: # Spatially varying zeropoint bbox = lsst.geom.Box2I( lsst.geom.Point2I(0.0, 0.0), lsst.geom.Point2I(*rec['fgcmfzptchebxymax'])) # Take the zeropoint, apply the absolute relative calibration offset, # and whatever flat-field scaling was applied pars[:, :] = ( rec['fgcmfzptcheb'].reshape(orderPlus1, orderPlus1) * 10.**(offsetMapping[rec['filtername']] / (-2.5)) * scalingMapping[rec['visit']][ccdMapping[rec['ccd']]]) # Apply absolute relative calibration offset to 0/0 term field = afwMath.ChebyshevBoundedField(bbox, pars) calibMean = field.mean() calibErr = (np.log(10.) / 2.5) * calibMean * rec['fgcmzpterr'] photoCalib = afwImage.PhotoCalib(field, calibErr) else: # Spatially constant zeropoint # Take the zeropoint, apply the absolute relative calibration offset, # and whatever flat-field scaling was applied calibMean = ( 10.**(rec['fgcmzpt'] / (-2.5)) * 10.**(offsetMapping[rec['filtername']] / (-2.5)) * scalingMapping[rec['visit']][ccdMapping[rec['ccd']]]) calibErr = (np.log(10.) / 2.5) * calibMean * rec['fgcmzpterr'] photoCalib = afwImage.PhotoCalib(calibMean, calibErr) butler.put(photoCalib, 'jointcal_photoCalib', dataId={ self.visitDataRefName: int(rec['visit']), self.ccdDataRefName: int(rec['ccd']), 'filter': filterMapping[rec['filtername']], 'tract': 0 }) self.log.info("Done outputting jointcal_photoCalib objects")
def testUndeblendedMeasurement(self): """Check undeblended measurement and aperture correction""" width, height = 100, 100 # Dimensions of image x0, y0 = 1234, 5678 # Offset of image radius = 3.0 # Aperture radius xCenter, yCenter = width//2, height//2 # Position of first source; integer values, for convenience xOffset, yOffset = 1, 1 # Offset from first source to second source flux1, flux2 = 1000, 1 # Flux of sources apCorrValue = 3.21 # Aperture correction value to apply image = afwImage.MaskedImageF(afwGeom.ExtentI(width, height)) image.setXY0(x0, y0) image.getVariance().set(1.0) schema = afwTable.SourceTable.makeMinimalSchema() schema.addField("centroid_x", type=np.float64) schema.addField("centroid_y", type=np.float64) schema.addField("centroid_flag", type='Flag') schema.getAliasMap().set("slot_Centroid", "centroid") sfmConfig = measBase.SingleFrameMeasurementConfig() algName = "base_CircularApertureFlux" for subConfig in (sfmConfig.plugins, sfmConfig.undeblended): subConfig.names = [algName] subConfig[algName].radii = [radius] subConfig[algName].maxSincRadius = 0 # Disable sinc photometry because we're undersampled slots = sfmConfig.slots slots.centroid = "centroid" slots.shape = None slots.psfShape = None slots.apFlux = None slots.modelFlux = None slots.psfFlux = None slots.instFlux = None slots.calibFlux = None fieldName = lsst.meas.base.CircularApertureFluxAlgorithm.makeFieldPrefix(algName, radius) measBase.addApCorrName(fieldName) apCorrConfig = measBase.ApplyApCorrConfig() apCorrConfig.proxies = {"undeblended_" + fieldName: fieldName} sfm = measBase.SingleFrameMeasurementTask(config=sfmConfig, schema=schema) apCorr = measBase.ApplyApCorrTask(config=apCorrConfig, schema=schema) cat = afwTable.SourceCatalog(schema) parent = cat.addNew() parent.set("centroid_x", x0 + xCenter) parent.set("centroid_y", y0 + yCenter) spanSetParent = afwGeom.SpanSet.fromShape(int(radius)) spanSetParent = spanSetParent.shiftedBy(x0 + xCenter, y0 + yCenter) parent.setFootprint(afwDetection.Footprint(spanSetParent)) # First child is bright, dominating the blend child1 = cat.addNew() child1.set("centroid_x", parent.get("centroid_x")) child1.set("centroid_y", parent.get("centroid_y")) child1.setParent(parent.getId()) image.set(xCenter, yCenter, (flux1, 0, 0)) spanSetChild1 = afwGeom.SpanSet.fromShape(1) spanSetChild1 = spanSetChild1.shiftedBy(x0 + xCenter, y0 + yCenter) foot1 = afwDetection.Footprint(spanSetChild1) child1.setFootprint(afwDetection.HeavyFootprintF(foot1, image)) # Second child is fainter, but we want to be able to measure it! child2 = cat.addNew() child2.set("centroid_x", parent.get("centroid_x") + xOffset) child2.set("centroid_y", parent.get("centroid_y") + yOffset) child2.setParent(parent.getId()) image.set(xCenter + xOffset, yCenter + yOffset, (flux2, 0, 0)) spanSetChild2 = afwGeom.SpanSet.fromShape(1) tmpPoint = (x0 + xCenter + xOffset, y0 + yCenter + yOffset) spanSetChild2 = spanSetChild2.shiftedBy(*tmpPoint) foot2 = afwDetection.Footprint(spanSetChild2) child2.setFootprint(afwDetection.HeavyFootprintF(foot2, image)) spans = foot1.spans.union(foot2.spans) bbox = afwGeom.Box2I() bbox.include(foot1.getBBox()) bbox.include(foot2.getBBox()) parent.setFootprint(afwDetection.Footprint(spans, bbox)) exposure = afwImage.makeExposure(image) sfm.run(cat, exposure) def checkSource(source, baseName, expectedFlux): """Check that we get the expected results""" self.assertEqual(source.get(baseName + "_flux"), expectedFlux) self.assertGreater(source.get(baseName + "_fluxSigma"), 0) self.assertFalse(source.get(baseName + "_flag")) # Deblended checkSource(child1, fieldName, flux1) checkSource(child2, fieldName, flux2) # Undeblended checkSource(child1, "undeblended_" + fieldName, flux1 + flux2) checkSource(child2, "undeblended_" + fieldName, flux1 + flux2) # Apply aperture correction apCorrMap = afwImage.ApCorrMap() apCorrMap[fieldName + "_flux"] = afwMath.ChebyshevBoundedField( image.getBBox(), apCorrValue*np.ones((1, 1), dtype=np.float64) ) apCorrMap[fieldName + "_fluxSigma"] = afwMath.ChebyshevBoundedField( image.getBBox(), apCorrValue*np.zeros((1, 1), dtype=np.float64) ) apCorr.run(cat, apCorrMap) # Deblended checkSource(child1, fieldName, flux1*apCorrValue) checkSource(child2, fieldName, flux2*apCorrValue) # Undeblended checkSource(child1, "undeblended_" + fieldName, (flux1 + flux2)*apCorrValue) checkSource(child2, "undeblended_" + fieldName, (flux1 + flux2)*apCorrValue) self.assertIn(fieldName + "_apCorr", schema) self.assertIn(fieldName + "_apCorrSigma", schema) self.assertIn("undeblended_" + fieldName + "_apCorr", schema) self.assertIn("undeblended_" + fieldName + "_apCorrSigma", schema)