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
Esempio n. 3
0
 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:])
Esempio n. 4
0
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
Esempio n. 5
0
    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, )
Esempio n. 6
0
    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, )