def get_approx_psf_size_and_shape(bbox, psf, wcs, ra, dec, nx=20, ny=20, orderx=2, ordery=2): pts = [ lsst.geom.SpherePoint(r * lsst.geom.degrees, d * lsst.geom.degrees) for r, d in zip(ra, dec) ] pixels = wcs.skyToPixel(pts) ctrl = ChebyshevBoundedFieldControl() ctrl.orderX = orderx ctrl.orderY = ordery ctrl.triangular = False xSteps = np.linspace(bbox.getMinX(), bbox.getMaxX(), nx) ySteps = np.linspace(bbox.getMinY(), bbox.getMaxY(), ny) x = np.tile(xSteps, nx) y = np.repeat(ySteps, ny) psf_size = np.zeros(x.size) psf_e1 = np.zeros(x.size) psf_e2 = np.zeros(x.size) for i in range(x.size): shape = psf.computeShape(lsst.geom.Point2D(x[i], y[i])) psf_size[i] = shape.getDeterminantRadius() ixx = shape.getIxx() iyy = shape.getIyy() ixy = shape.getIxy() psf_e1[i] = (ixx - iyy) / (ixx + iyy + 2. * psf_size[i]**2.) psf_e2[i] = (2. * ixy) / (ixx + iyy + 2. * psf_size[i]**2.) pixel_x = np.array([pix.getX() for pix in pixels]) pixel_y = np.array([pix.getY() for pix in pixels]) cheb_size = ChebyshevBoundedField.fit(lsst.geom.Box2I(bbox), x, y, psf_size, ctrl) psf_size_pts = cheb_size.evaluate(pixel_x, pixel_y) cheb_e1 = ChebyshevBoundedField.fit(lsst.geom.Box2I(bbox), x, y, psf_e1, ctrl) psf_e1_pts = cheb_e1.evaluate(pixel_x, pixel_y) cheb_e2 = ChebyshevBoundedField.fit(lsst.geom.Box2I(bbox), x, y, psf_e2, ctrl) psf_e2_pts = cheb_e2.evaluate(pixel_x, pixel_y) return psf_size_pts, psf_e1_pts, psf_e2_pts
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. 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 compute_approx_psf_size_and_shape(ccd_row, ra, dec, nx=20, ny=20, orderx=2, ordery=2): """Compute the approximate psf size and shape. This routine fits how the psf size and shape varies over a field by approximating with a Chebyshev bounded field. Parameters ---------- ccd_row : `lsst.afw.table.ExposureRecord` Exposure metadata for a given detector exposure. ra : `np.ndarray` Right ascension of points to compute size and shape (degrees). dec : `np.ndarray` Declination of points to compute size and shape (degrees). nx : `int`, optional Number of sampling points in the x direction. ny : `int`, optional Number of sampling points in the y direction. orderx : `int`, optional Chebyshev polynomial order for fit in x direction. ordery : `int`, optional Chebyshev polynomial order for fit in y direction. Returns ------- psf_array : `np.ndarray` Record array with "psf_size", "psf_e1", "psf_e2". """ pts = [ lsst.geom.SpherePoint(r * lsst.geom.degrees, d * lsst.geom.degrees) for r, d in zip(ra, dec) ] pixels = ccd_row.getWcs().skyToPixel(pts) ctrl = ChebyshevBoundedFieldControl() ctrl.orderX = orderx ctrl.orderY = ordery ctrl.triangular = False bbox = ccd_row.getBBox() xSteps = np.linspace(bbox.getMinX(), bbox.getMaxX(), nx) ySteps = np.linspace(bbox.getMinY(), bbox.getMaxY(), ny) x = np.tile(xSteps, nx) y = np.repeat(ySteps, ny) psf_size = np.zeros(x.size) psf_e1 = np.zeros(x.size) psf_e2 = np.zeros(x.size) psf_area = np.zeros(x.size) psf = ccd_row.getPsf() for i in range(x.size): shape = psf.computeShape(lsst.geom.Point2D(x[i], y[i])) psf_size[i] = shape.getDeterminantRadius() ixx = shape.getIxx() iyy = shape.getIyy() ixy = shape.getIxy() psf_e1[i] = (ixx - iyy) / (ixx + iyy + 2. * psf_size[i]**2.) psf_e2[i] = (2. * ixy) / (ixx + iyy + 2. * psf_size[i]**2.) im = psf.computeKernelImage(lsst.geom.Point2D(x[i], y[i])) psf_area[i] = np.sum(im.array) / np.sum(im.array**2.) pixel_x = np.array([pix.getX() for pix in pixels]) pixel_y = np.array([pix.getY() for pix in pixels]) psf_array = np.zeros(pixel_x.size, dtype=[("psf_size", "f8"), ("psf_e1", "f8"), ("psf_e2", "f8"), ("psf_area", "f8")]) cheb_size = ChebyshevBoundedField.fit(lsst.geom.Box2I(bbox), x, y, psf_size, ctrl) psf_array["psf_size"] = cheb_size.evaluate(pixel_x, pixel_y) cheb_e1 = ChebyshevBoundedField.fit(lsst.geom.Box2I(bbox), x, y, psf_e1, ctrl) psf_array["psf_e1"] = cheb_e1.evaluate(pixel_x, pixel_y) cheb_e2 = ChebyshevBoundedField.fit(lsst.geom.Box2I(bbox), x, y, psf_e2, ctrl) psf_array["psf_e2"] = cheb_e2.evaluate(pixel_x, pixel_y) cheb_area = ChebyshevBoundedField.fit(lsst.geom.Box2I(bbox), x, y, psf_area, ctrl) psf_array["psf_area"] = cheb_area.evaluate(pixel_x, pixel_y) return psf_array
def run(self, bbox, catalog): """!Measure aperture correction @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 """ self.log.info("Measuring aperture corrections for %d flux fields" % (len(self.toCorrect),)) # First, create a subset of the catalog that contains only objects with inputFilterFlag set # and non-flagged reference fluxes. subset1 = [record for record in catalog if record.get(self.inputFilterFlag) and 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: self.log.warn("Only %d sources for calculation of aperture correction for '%s'; " "setting to 1.0" % (len(subset2), name,)) 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, )
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, )