def makeCoaddApCorrMap(catalog, coaddBox, coaddWcs, weightFieldName="weight"): """Construct an ApCorrMap for a coadd @param catalog: Table of coadd inputs (lsst.afw.table.ExposureCatalog) @param coaddBox: Bounding box for coadd (lsst.afw.geom.Box2I) @param coaddWcs: Wcs for coadd @param weightFieldName: name of weight field in catalog @return aperture corrections """ # Assemble the BoundedFields for each type everything = {} # name --> list of CoaddBoundedFieldElement weightKey = catalog.schema[weightFieldName].asKey() for row in catalog: apCorrMap = row.getApCorrMap() if not apCorrMap: continue weight = row.get(weightKey) wcs = row.getWcs() validPolygon = row.getValidPolygon() for name, bf in apCorrMap.items(): if name not in everything: everything[name] = [] everything[name].append( CoaddBoundedFieldElement(bf, wcs, validPolygon, weight)) # Construct a CoaddBoundedField for each type apCorrMap = ApCorrMap() for name, elements in everything.items(): apCorrMap.set(name, CoaddBoundedField(coaddBox, coaddWcs, elements)) return apCorrMap
def makeCoaddApCorrMap(catalog, coaddBox, coaddWcs, weightFieldName="weight"): """Construct an ApCorrMap for a coadd @param catalog: Table of coadd inputs (lsst.afw.table.ExposureCatalog) @param coaddBox: Bounding box for coadd (lsst.afw.geom.Box2I) @param coaddWcs: Wcs for coadd @param weightFieldName: name of weight field in catalog @return aperture corrections """ # Assemble the BoundedFields for each type everything = {} # name --> list of CoaddBoundedFieldElement weightKey = catalog.schema[weightFieldName].asKey() for row in catalog: apCorrMap = row.getApCorrMap() if not apCorrMap: continue weight = row.get(weightKey) wcs = row.getWcs() validPolygon = row.getValidPolygon() for name, bf in apCorrMap.items(): if name not in everything: everything[name] = [] everything[name].append(CoaddBoundedFieldElement(bf, wcs, validPolygon, weight)) # Construct a CoaddBoundedField for each type apCorrMap = ApCorrMap() for name, elements in everything.iteritems(): apCorrMap.set(name, CoaddBoundedField(coaddBox, coaddWcs, elements)) return apCorrMap
def testMultiPlaneFitsReaders(self): """Run tests for MaskedImageFitsReader and ExposureFitsReader. """ metadata = PropertyList() metadata.add("FIVE", 5) metadata.add("SIX", 6.0) wcs = makeSkyWcs(Point2D(2.5, 3.75), SpherePoint(40.0 * degrees, 50.0 * degrees), np.array([[1E-5, 0.0], [0.0, -1E-5]])) defineFilter("test_readers_filter", lambdaEff=470.0) calib = PhotoCalib(2.5E4) psf = GaussianPsf(21, 21, 8.0) polygon = Polygon(Box2D(self.bbox)) apCorrMap = ApCorrMap() visitInfo = VisitInfo(exposureTime=5.0) transmissionCurve = TransmissionCurve.makeIdentity() coaddInputs = CoaddInputs(ExposureTable.makeMinimalSchema(), ExposureTable.makeMinimalSchema()) detector = DetectorWrapper().detector record = coaddInputs.ccds.addNew() record.setWcs(wcs) record.setPhotoCalib(calib) record.setPsf(psf) record.setValidPolygon(polygon) record.setApCorrMap(apCorrMap) record.setVisitInfo(visitInfo) record.setTransmissionCurve(transmissionCurve) record.setDetector(detector) for n, dtypeIn in enumerate(self.dtypes): with self.subTest(dtypeIn=dtypeIn): exposureIn = Exposure(self.bbox, dtype=dtypeIn) shape = exposureIn.image.array.shape exposureIn.image.array[:, :] = np.random.randint(low=1, high=5, size=shape) exposureIn.mask.array[:, :] = np.random.randint(low=1, high=5, size=shape) exposureIn.variance.array[:, :] = np.random.randint(low=1, high=5, size=shape) exposureIn.setMetadata(metadata) exposureIn.setWcs(wcs) exposureIn.setFilter(Filter("test_readers_filter")) exposureIn.setFilterLabel( FilterLabel(physical="test_readers_filter")) exposureIn.setPhotoCalib(calib) exposureIn.setPsf(psf) exposureIn.getInfo().setValidPolygon(polygon) exposureIn.getInfo().setApCorrMap(apCorrMap) exposureIn.getInfo().setVisitInfo(visitInfo) exposureIn.getInfo().setTransmissionCurve(transmissionCurve) exposureIn.getInfo().setCoaddInputs(coaddInputs) exposureIn.setDetector(detector) with lsst.utils.tests.getTempFilePath(".fits") as fileName: exposureIn.writeFits(fileName) self.checkMaskedImageFitsReader(exposureIn, fileName, self.dtypes[n:]) self.checkExposureFitsReader(exposureIn, fileName, self.dtypes[n:])
def makeCoaddApCorrMap(catalog, coaddBox, coaddWcs, weightFieldName="weight"): """Construct an ApCorrMap for a coadd Parameters ---------- catalog: `lsst.afw.table.ExposureCatalog` A table of coadd inputs coaddBox : `lsst.geom.Box2I` Bounding box for coadd coaddWcs : `lsst.afw.geom.SkyWcs Wcs for coadd weightFieldName : `str` Name of the weight field in the catalog Returns ------- apCorrMap : `lsst.afw.image.ApCorrMap` Aperture corrections """ # Assemble the BoundedFields for each type everything = {} # name --> list of CoaddBoundedFieldElement weightKey = catalog.schema[weightFieldName].asKey() for row in catalog: apCorrMap = row.getApCorrMap() if not apCorrMap: continue weight = row.get(weightKey) wcs = row.getWcs() validPolygon = row.getValidPolygon() for name, bf in apCorrMap.items(): if name not in everything: everything[name] = [] everything[name].append( CoaddBoundedFieldElement(bf, wcs, validPolygon, weight)) # Construct a CoaddBoundedField for each type apCorrMap = ApCorrMap() for name, elements in everything.items(): apCorrMap.set(name, CoaddBoundedField(coaddBox, coaddWcs, elements)) return apCorrMap
def run(self, exposure, catalog): """!Measure aperture correction @param[in] exposure Exposure aperture corrections are being measured on. The bounding box is retrieved from it, and it is passed to the sourceSelector. The output aperture correction map is *not* added to the exposure; this is left to the caller. @param[in] catalog SourceCatalog containing measurements to be used to compute aperturecorrections. @return an lsst.pipe.base.Struct containing: - apCorrMap: an aperture correction map (lsst.afw.image.ApCorrMap) that contains two entries for each flux field: - flux field (e.g. base_PsfFlux_instFlux): 2d model - flux sigma field (e.g. base_PsfFlux_instFluxErr): 2d model of error """ bbox = exposure.getBBox() import lsstDebug display = lsstDebug.Info(__name__).display doPause = lsstDebug.Info(__name__).doPause self.log.info("Measuring aperture corrections for %d flux fields" % (len(self.toCorrect), )) # First, create a subset of the catalog that contains only selected stars # with non-flagged reference fluxes. subset1 = [ record for record in self.sourceSelector.run( catalog, exposure=exposure).sourceCat if (not record.get(self.refFluxKeys.flag) and numpy.isfinite(record.get(self.refFluxKeys.flux))) ] apCorrMap = ApCorrMap() # Outer loop over the fields we want to correct for name, keys in self.toCorrect.items(): fluxName = name + "_instFlux" fluxErrName = name + "_instFluxErr" # Create a more restricted subset with only the objects where the to-be-correct flux # is not flagged. fluxes = numpy.fromiter( (record.get(keys.flux) for record in subset1), float) with numpy.errstate(invalid="ignore"): # suppress NAN warnings isGood = numpy.logical_and.reduce([ numpy.fromiter((not record.get(keys.flag) for record in subset1), bool), numpy.isfinite(fluxes), fluxes > 0.0, ]) subset2 = [record for record, good in zip(subset1, isGood) if good] # Check that we have enough data points that we have at least the minimum of degrees of # freedom specified in the config. if len(subset2) - 1 < self.config.minDegreesOfFreedom: if name in self.config.allowFailure: self.log.warn( "Unable to measure aperture correction for '%s': " "only %d sources, but require at least %d." % (name, len(subset2), self.config.minDegreesOfFreedom + 1)) continue raise RuntimeError( "Unable to measure aperture correction for required algorithm '%s': " "only %d sources, but require at least %d." % (name, len(subset2), self.config.minDegreesOfFreedom + 1)) # If we don't have enough data points to constrain the fit, reduce the order until we do ctrl = self.config.fitConfig.makeControl() while len(subset2) - ctrl.computeSize( ) < self.config.minDegreesOfFreedom: if ctrl.orderX > 0: ctrl.orderX -= 1 if ctrl.orderY > 0: ctrl.orderY -= 1 # Fill numpy arrays with positions and the ratio of the reference flux to the to-correct flux x = numpy.zeros(len(subset2), dtype=float) y = numpy.zeros(len(subset2), dtype=float) apCorrData = numpy.zeros(len(subset2), dtype=float) indices = numpy.arange(len(subset2), dtype=int) for n, record in enumerate(subset2): x[n] = record.getX() y[n] = record.getY() apCorrData[n] = record.get(self.refFluxKeys.flux) / record.get( keys.flux) for _i in range(self.config.numIter): # Do the fit, save it in the output map apCorrField = ChebyshevBoundedField.fit( bbox, x, y, apCorrData, ctrl) if display: plotApCorr(bbox, x, y, apCorrData, apCorrField, "%s, iteration %d" % (name, _i), doPause) # Compute errors empirically, using the RMS difference between the true reference flux and the # corrected to-be-corrected flux. apCorrDiffs = apCorrField.evaluate(x, y) apCorrDiffs -= apCorrData apCorrErr = numpy.mean(apCorrDiffs**2)**0.5 # Clip bad data points apCorrDiffLim = self.config.numSigmaClip * apCorrErr with numpy.errstate(invalid="ignore"): # suppress NAN warning keep = numpy.fabs(apCorrDiffs) <= apCorrDiffLim x = x[keep] y = y[keep] apCorrData = apCorrData[keep] indices = indices[keep] # Final fit after clipping apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl) self.log.info( "Aperture correction for %s: RMS %f from %d" % (name, numpy.mean((apCorrField.evaluate(x, y) - apCorrData)**2) **0.5, len(indices))) if display: plotApCorr(bbox, x, y, apCorrData, apCorrField, "%s, final" % (name, ), doPause) # Save the result in the output map # The error is constant spatially (we could imagine being # more clever, but we're not yet sure if it's worth the effort). # We save the errors as a 0th-order ChebyshevBoundedField apCorrMap[fluxName] = apCorrField apCorrErrCoefficients = numpy.array([[apCorrErr]], dtype=float) apCorrMap[fluxErrName] = ChebyshevBoundedField( bbox, apCorrErrCoefficients) # Record which sources were used for i in indices: subset2[i].set(keys.used, True) return Struct(apCorrMap=apCorrMap, )
def run(self, exposure, catalog): """!Measure aperture correction @param[in] exposure Exposure aperture corrections are being measured on. Aside from the bounding box, the exposure is only used by the starSelector subtask (which may need it to construct PsfCandidates, as PsfCanidate construction can do some filtering). The output aperture correction map is *not* added to the exposure; this is left to the caller. @param[in] catalog SourceCatalog containing measurements to be used to compute aperturecorrections. @return an lsst.pipe.base.Struct containing: - apCorrMap: an aperture correction map (lsst.afw.image.ApCorrMap) that contains two entries for each flux field: - flux field (e.g. base_PsfFlux_flux): 2d model - flux sigma field (e.g. base_PsfFlux_fluxSigma): 2d model of error """ bbox = exposure.getBBox() self.log.info("Measuring aperture corrections for %d flux fields" % (len(self.toCorrect), )) # First, create a subset of the catalog that contains only selected stars # with non-flagged reference fluxes. subset1 = [ record for record in self.starSelector.selectStars( exposure, catalog).starCat if not record.get(self.refFluxKeys.flag) ] apCorrMap = ApCorrMap() # Outer loop over the fields we want to correct for name, keys in self.toCorrect.iteritems(): fluxName = name + "_flux" fluxSigmaName = name + "_fluxSigma" # Create a more restricted subset with only the objects where the to-be-correct flux # is not flagged. subset2 = [ record for record in subset1 if not record.get(keys.flag) ] # Check that we have enough data points that we have at least the minimum of degrees of # freedom specified in the config. if len(subset2) - 1 < self.config.minDegreesOfFreedom: raise RuntimeError( "Only %d sources for calculation of aperture correction for '%s'; " "require at least %d." % (len(subset2), name, self.config.minDegreesOfFreedom + 1)) apCorrMap[fluxName] = ChebyshevBoundedField( bbox, numpy.ones((1, 1), dtype=float)) apCorrMap[fluxSigmaName] = ChebyshevBoundedField( bbox, numpy.zeros((1, 1), dtype=float)) continue # If we don't have enough data points to constrain the fit, reduce the order until we do ctrl = self.config.fitConfig.makeControl() while len(subset2) - ctrl.computeSize( ) < self.config.minDegreesOfFreedom: if ctrl.orderX > 0: ctrl.orderX -= 1 if ctrl.orderY > 0: ctrl.orderY -= 1 # Fill numpy arrays with positions and the ratio of the reference flux to the to-correct flux x = numpy.zeros(len(subset2), dtype=float) y = numpy.zeros(len(subset2), dtype=float) apCorrData = numpy.zeros(len(subset2), dtype=float) indices = numpy.arange(len(subset2), dtype=int) for n, record in enumerate(subset2): x[n] = record.getX() y[n] = record.getY() apCorrData[n] = record.get(self.refFluxKeys.flux) / record.get( keys.flux) for _i in range(self.config.numIter): # Do the fit, save it in the output map apCorrField = ChebyshevBoundedField.fit( bbox, x, y, apCorrData, ctrl) # Compute errors empirically, using the RMS difference between the true reference flux and the # corrected to-be-corrected flux. apCorrDiffs = apCorrField.evaluate(x, y) apCorrDiffs -= apCorrData apCorrErr = numpy.mean(apCorrDiffs**2)**0.5 # Clip bad data points apCorrDiffLim = self.config.numSigmaClip * apCorrErr keep = numpy.fabs(apCorrDiffs) <= apCorrDiffLim x = x[keep] y = y[keep] apCorrData = apCorrData[keep] indices = indices[keep] # Final fit after clipping apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl) self.log.info( "Aperture correction for %s: RMS %f from %d" % (name, numpy.mean((apCorrField.evaluate(x, y) - apCorrData)**2) **0.5, len(indices))) # Save the result in the output map # The error is constant spatially (we could imagine being # more clever, but we're not yet sure if it's worth the effort). # We save the errors as a 0th-order ChebyshevBoundedField apCorrMap[fluxName] = apCorrField apCorrErrCoefficients = numpy.array([[apCorrErr]], dtype=float) apCorrMap[fluxSigmaName] = ChebyshevBoundedField( bbox, apCorrErrCoefficients) # Record which sources were used for i in indices: subset2[i].set(keys.used, True) return Struct(apCorrMap=apCorrMap, )