Example #1
0
    def readSrc(self, dataRef):
        """Read source catalog etc for input dataRef

        The following are returned:
        Source catalog, matched list, and wcs will be read from 'src', 'srcMatch', and 'calexp_md',
        respectively.

        NOTE: If the detector has nQuarter%4 != 0 (i.e. it is rotated w.r.t the focal plane
              coordinate system), the (x, y) pixel values of the centroid slot for the source
              catalogs are rotated such that pixel (0, 0) is the LLC (i.e. the coordinate system
              expected by meas_mosaic).

        If color transformation information is given, it will be applied to the reference flux
        of the matched list.  The source catalog and matched list will be converted to measMosaic's
        Source and SourceMatch and returned.

        The number of 'Source's in each cell defined by config.cellSize will be limited to brightest
        config.nStarPerCell.
        """

        self.log = Log.getDefaultLogger()

        dataId = dataRef.dataId

        try:
            if not dataRef.datasetExists("src"):
                raise RuntimeError("no data for src %s" % (dataId))
            if not dataRef.datasetExists("calexp_md"):
                raise RuntimeError("no data for calexp_md %s" % (dataId))

            calexp_md = dataRef.get("calexp_md", immediate=True)
            detector = dataRef.get("camera")[dataRef.dataId[
                "ccd"]]  # OK for HSC; maybe not for other cameras
            wcs = afwGeom.makeSkyWcs(calexp_md)
            nQuarter = detector.getOrientation().getNQuarter()
            sources = dataRef.get("src",
                                  immediate=True,
                                  flags=afwTable.SOURCE_IO_NO_FOOTPRINTS)

            # Check if we are looking at HSC stack outputs: if so, no pixel rotation of sources is
            # required, but alias mapping must be set to associate HSC's schema with that of LSST.
            hscRun = mosaicUtils.checkHscStack(calexp_md)
            if hscRun is None:
                if nQuarter % 4 != 0:
                    dims = afwImage.bboxFromMetadata(calexp_md).getDimensions()
                    sources = mosaicUtils.rotatePixelCoords(
                        sources, dims.getX(), dims.getY(), nQuarter)

            # Set the aliap map for the source catalog
            if self.config.srcSchemaMap is not None and hscRun is not None:
                aliasMap = sources.schema.getAliasMap()
                for lsstName, otherName in self.config.srcSchemaMap.items():
                    aliasMap.set(lsstName, otherName)

            refObjLoader = self.config.loadAstrom.apply(
                butler=dataRef.getButler())
            srcMatch = dataRef.get("srcMatch", immediate=True)
            if hscRun is not None:
                # The reference object loader grows the bbox by the config parameter pixelMargin.  This
                # is set to 50 by default but is not reflected by the radius parameter set in the
                # metadata, so some matches may reside outside the circle searched within this radius
                # Thus, increase the radius set in the metadata fed into joinMatchListWithCatalog() to
                # accommodate.
                matchmeta = srcMatch.table.getMetadata()
                rad = matchmeta.getDouble("RADIUS")
                matchmeta.setDouble(
                    "RADIUS", rad * 1.05,
                    "field radius in degrees, approximate, padded")
            matches = refObjLoader.joinMatchListWithCatalog(srcMatch, sources)

            # Set the aliap map for the matched sources (i.e. the [1] attribute schema for each match)
            if self.config.srcSchemaMap is not None and hscRun is not None:
                for mm in matches:
                    aliasMap = mm[1].schema.getAliasMap()
                    for lsstName, otherName in self.config.srcSchemaMap.items(
                    ):
                        aliasMap.set(lsstName, otherName)

            if hscRun is not None:
                for slot in ("PsfFlux", "ModelFlux", "ApFlux", "InstFlux",
                             "Centroid", "Shape"):
                    getattr(matches[0][1].getTable(), "define" + slot)(getattr(
                        sources, "get" + slot + "Definition")())
                    # For some reason, the CalibFlux slot in sources is coming up as centroid_sdss, so
                    # set it to flux_naive explicitly
                    for slot in ("CalibFlux", ):
                        getattr(matches[0][1].getTable(),
                                "define" + slot)("flux_naive")
            matches = [m for m in matches if m[0] is not None]
            refSchema = matches[0][0].schema if matches else None

            if self.cterm is not None and len(matches) != 0:
                # Add a "flux" field to the input schema of the first element
                # of the match and populate it with a colorterm correct flux.
                mapper = afwTable.SchemaMapper(refSchema)
                for key, field in refSchema:
                    mapper.addMapping(key)
                fluxKey = mapper.editOutputSchema().addField(
                    "flux", type=float, doc="Reference flux")
                fluxSigmaKey = mapper.editOutputSchema().addField(
                    "fluxSigma", type=float, doc="Reference flux uncertainty")
                table = afwTable.SimpleTable.make(mapper.getOutputSchema())
                table.preallocate(len(matches))
                for match in matches:
                    newMatch = table.makeRecord()
                    newMatch.assign(match[0], mapper)
                    match[0] = newMatch
                primaryFluxKey = refSchema.find(
                    refSchema.join(self.cterm.primary, "flux")).key
                secondaryFluxKey = refSchema.find(
                    refSchema.join(self.cterm.secondary, "flux")).key
                primaryFluxSigmaKey = refSchema.find(
                    refSchema.join(self.cterm.primary, "fluxSigma")).key
                secondaryFluxSigmaKey = refSchema.find(
                    refSchema.join(self.cterm.secondary, "fluxSigma")).key
                refFlux1 = numpy.array(
                    [m[0].get(primaryFluxKey) for m in matches])
                refFlux2 = numpy.array(
                    [m[0].get(secondaryFluxKey) for m in matches])
                refFluxSigma1 = numpy.array(
                    [m[0].get(primaryFluxSigmaKey) for m in matches])
                refFluxSigma2 = numpy.array(
                    [m[0].get(secondaryFluxSigmaKey) for m in matches])
                refMag1 = -2.5 * numpy.log10(refFlux1)
                refMag2 = -2.5 * numpy.log10(refFlux2)
                refMag = self.cterm.transformMags(refMag1, refMag2)
                refFlux = numpy.power(10.0, -0.4 * refMag)
                refFluxSigma = self.cterm.propagateFluxErrors(
                    refFluxSigma1, refFluxSigma2)
                matches = [
                    self.setCatFlux(m, flux, fluxKey, fluxSigma, fluxSigmaKey)
                    for m, flux, fluxSigma in zip(matches, refFlux,
                                                  refFluxSigma) if flux == flux
                ]
            else:
                filterName = afwImage.Filter(calexp_md).getName()
                refFluxField = measAlg.getRefFluxField(refSchema, filterName)
                refSchema.getAliasMap().set("flux", refFluxField)

            # LSST reads in a_net catalogs with flux in "janskys", so must convert back to DN.
            matches = mosaicUtils.matchJanskyToDn(matches)

            selSources = self.selectStars(sources,
                                          self.config.includeSaturated)
            selMatches = self.selectStars(matches,
                                          self.config.includeSaturated)

            retSrc = list()
            retMatch = list()

            if len(selMatches) > self.config.minNumMatch:
                naxis1, naxis2 = afwImage.bboxFromMetadata(
                    calexp_md).getDimensions()
                if hscRun is None:
                    if nQuarter % 2 != 0:
                        naxis1, naxis2 = naxis2, naxis1
                bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0),
                                     afwGeom.Extent2I(naxis1, naxis2))
                cellSet = afwMath.SpatialCellSet(bbox, self.config.cellSize,
                                                 self.config.cellSize)
                for s in selSources:
                    if numpy.isfinite(s.getRa().asDegrees()):  # get rid of NaN
                        src = measMosaic.Source(s)
                        src.setExp(dataId["visit"])
                        src.setChip(dataId["ccd"])
                        try:
                            tmp = measMosaic.SpatialCellSource(src)
                            cellSet.insertCandidate(tmp)
                        except:
                            self.log.info(
                                "FAILED TO INSERT CANDIDATE: visit=%d ccd=%d x=%f y=%f"
                                % (dataRef.dataId["visit"], dataRef.
                                   dataId["ccd"], src.getX(), src.getY()) +
                                " bbox=" + str(bbox))
                for cell in cellSet.getCellList():
                    cell.sortCandidates()
                    for i, cand in enumerate(cell):
                        src = cand.getSource()
                        retSrc.append(src)
                        if i == self.config.nStarPerCell - 1:
                            break
                for m in selMatches:
                    if m[0] is not None and m[1] is not None:
                        match = (measMosaic.Source(m[0], wcs),
                                 measMosaic.Source(m[1]))
                        match[1].setExp(dataId["visit"])
                        match[1].setChip(dataId["ccd"])
                        retMatch.append(match)
            else:
                self.log.info(
                    "%8d %3d : %d/%d matches  Suspicious to wrong match. Ignore this CCD"
                    % (dataRef.dataId["visit"], dataRef.dataId["ccd"],
                       len(selMatches), len(matches)))

        except Exception as e:
            self.log.warn("Failed to read %s: %s" % (dataId, e))
            return dataId, [None, None, None]

        return dataId, [retSrc, retMatch, wcs]
    def run(self, matchLists, filterName, wcsList, butler):

        if self.config.applyColorTerms:
            ct = self.config.colorterms.selectColorTerm(filterName)
        else:
            ct = None

        # Convert matchLists to meas_mosaic specific format
        mlVisit = dict()
        for ccdId in matchLists:
            if matchLists[ccdId] is None:
                continue
            visit, ccd = self.decodeCcdExposureId(ccdId)
            if visit not in mlVisit:
                mlVisit[visit] = list()
            matches = [m for m in matchLists[ccdId] if m[0] is not None]
            keys = self.getKeys(matches[0][1].schema)
            matches = self.selectMatches(matches, keys)
            matches = self.selectStars(matches)

            # Apply color term
            if ct is not None and len(matches) != 0:
                refSchema = matches[0][0].schema
                key_p = refSchema.find(ct.primary).key
                key_s = refSchema.find(ct.secondary).key
                key_f = refSchema.find("flux").key
                refFlux1 = numpy.array([m[0].get(key_p) for m in matches])
                refFlux2 = numpy.array([m[0].get(key_s) for m in matches])
                refMag1 = -2.5 * numpy.log10(refFlux1)
                refMag2 = -2.5 * numpy.log10(refFlux2)
                refMag = ct.transformMags(refMag1, refMag2)
                refFlux = numpy.power(10.0, -0.4 * refMag)
                matches = [
                    self.setCatFlux(m, f, key_f)
                    for m, f in zip(matches, refFlux) if f == f
                ]

            for m in matches:
                if m[0] is not None and m[1] is not None:
                    match = (measMosaic.Source(m[0], wcsList[ccdId]),
                             measMosaic.Source(m[1]))
                    match[1].setExp(visit)
                    match[1].setChip(ccd)
                    mlVisit[visit].append(match)

        matchList = []
        for visit in mlVisit:
            matchList.append(mlVisit[visit])

        rootMat = measMosaic.kdtreeMat(matchList)
        allMat = rootMat.mergeMat()

        # Read CCD information
        ccdSet = {}
        for ccdId in matchLists:
            if matchLists[ccdId] is None:
                continue
            visit, ccd = self.decodeCcdExposureId(ccdId)
            if ccd not in ccdSet:
                ccdDev = cameraGeomUtils.findCcd(butler.mapper.camera,
                                                 cameraGeom.Id(int(ccd)))
                ccdSet[ccd] = ccdDev

        # meas_mosaic specific wcs information
        wcsDic = {}
        for ccdId in wcsList:
            visit, ccd = self.decodeCcdExposureId(ccdId)
            if visit not in wcsDic and wcsList[ccdId] is not None:
                wcs = wcsList[ccdId]
                ccdDev = ccdSet[ccd]
                offset = afwGeom.Extent2D(ccdDev.getCenter().getPixels(
                    ccdDev.getPixelSize()))
                wcsDic[visit] = wcs.copyAtShiftedPixelOrigin(offset)

        # meas_mosaic specific object list
        matchVec = measMosaic.obsVecFromSourceGroup(allMat, wcsDic, ccdSet)
        sourceVec = []

        # Apply Jocabian correction calculated from wcs
        for m in matchVec:
            wcs = wcsList[m.iexp * 200 + m.ichip]
            m.mag -= 2.5 * math.log10(
                measMosaic.computeJacobian(wcs, afwGeom.Point2D(m.x, m.y)))

        fluxFitOrder = self.config.fluxFitOrder
        absolute = True
        chebyshev = True
        commonFluxCorr = False
        solveCcdScale = True
        ffpSet = {}
        for visit in wcsDic:
            ffp = measMosaic.FluxFitParams(fluxFitOrder, absolute, chebyshev)
            u_max, v_max = self.getExtent(matchVec)
            ffp.u_max = (math.floor(u_max / 10.) + 1) * 10
            ffp.v_max = (math.floor(v_max / 10.) + 1) * 10
            ffpSet[visit] = ffp

        fexp = {}
        fchip = {}

        matchVec, sourceVec, wcsDic, ccdSet, fexp, fchip, ffpSet = measMosaic.fluxFit(
            absolute, commonFluxCorr, matchVec, len(matchVec), sourceVec,
            len(sourceVec), wcsDic, ccdSet, fexp, fchip, ffpSet, solveCcdScale)

        self.writeFcr(butler, list(matchLists.keys()), ccdSet, filterName,
                      fexp, fchip, ffpSet)

        return (1.0 / fexp[list(fexp.keys())[0]])
    def run(self, dataRefList, ct=None, debug=False, verbose=False):
        ccdSet = self.readCcd(dataRefList)
        self.removeNonExistCcd(dataRefList, ccdSet)

        sourceSet = []
        matchList = []
        astrom = measAstrom.ANetBasicAstrometryTask(self.config.astrom)
        ssVisit = dict()
        mlVisit = dict()
        dataRefListUsed = list()
        wcsDic = dict()
        calibDic = dict()
        ffpDic = dict()
        for dataRef in dataRefList:
            if dataRef.dataId['visit'] not in ssVisit:
                ssVisit[dataRef.dataId['visit']] = list()
                mlVisit[dataRef.dataId['visit']] = list()
                wcsDic[dataRef.dataId['visit']] = dict()
                calibDic[dataRef.dataId['visit']] = dict()
                ffpDic[dataRef.dataId['visit']] = dict()

            try:
                if not dataRef.datasetExists('src'):
                    raise RuntimeError("no data for src %s" % (dataRef.dataId))

                if not dataRef.datasetExists('jointcal_wcs'):
                    raise RuntimeError("no data for wcs %s" % (dataRef.dataId))

                if not dataRef.datasetExists('fcr'):
                    raise RuntimeError("no data for fcr %s" % (dataRef.dataId))

                wcs = dataRef.get('jointcal_wcs')

                md = dataRef.get('calexp_md')
                filterName = afwImage.Filter(md).getName()

                md = dataRef.get('fcr_md')
                ffp = measMosaic.FluxFitParams(md)
                photoCalib = afwImage.makePhotoCalibFromMetadata(md)

                sources = dataRef.get('src',
                              flags=afwTable.SOURCE_IO_NO_FOOTPRINTS,
                              immediate=True)

                icSrces = dataRef.get('icSrc',
                              flags=afwTable.SOURCE_IO_NO_FOOTPRINTS,
                              immediate=True)
                packedMatches = dataRef.get('icMatch')
                matches = astrom.joinMatchListWithCatalog(packedMatches, icSrces)

                matches = [m for m in matches if m[0] is not None]

                if matches:
                    refSchema = matches[0][0].schema
                    if ct:
                        # Add a "flux" field to the match records which contains the
                        # colorterm-corrected reference flux. The field name is hard-coded in
                        # lsst::meas::mosaic::Source.
                        mapper = afwTable.SchemaMapper(refSchema)
                        for key, field in refSchema:
                            mapper.addMapping(key)
                        key_f = mapper.editOutputSchema().addField("flux", type=float, doc="Reference flux")
                        table = afwTable.SimpleTable.make(mapper.getOutputSchema())
                        table.preallocate(len(matches))
                        for match in matches:
                            newMatch = table.makeRecord()
                            newMatch.assign(match[0], mapper)
                            match[0] = newMatch

                        key_p = refSchema.find(refSchema.join(ct.primary, "flux")).key
                        key_s = refSchema.find(refSchema.join(ct.secondary, "flux")).key
                        refFlux1 = numpy.array([m[0].get(key_p) for m in matches])
                        refFlux2 = numpy.array([m[0].get(key_s) for m in matches])
                        refMag1 = -2.5*numpy.log10(refFlux1)
                        refMag2 = -2.5*numpy.log10(refFlux2)
                        refMag = ct.transformMags(refMag1, refMag2)
                        refFlux = numpy.power(10.0, -0.4*refMag)
                        matches = [setCatFlux(m, f, key_f) for m, f in zip(matches, refFlux) if f == f]
                    else:
                        # No colorterm; we can get away with aliasing the reference flux.
                        refFluxField = measAlg.getRefFluxField(refSchema, filterName)
                        refSchema.getAliasMap().set("flux", refFluxField)

                sources = self.selectStars(sources)
                matches = self.selectStars(matches, True)
            except Exception as e:
                print("Failed to read: %s" % (e))
                sources = None
                continue

            if sources is not None:
                for s in sources:
                    if numpy.isfinite(s.getRa().asDegrees()): # get rid of NaN
                        src = measMosaic.Source(s)
                        src.setExp(dataRef.dataId['visit'])
                        src.setChip(dataRef.dataId['ccd'])
                        ssVisit[dataRef.dataId['visit']].append(src)
                for m in matches:
                    if m[0] is not None and m[1] is not None:
                        match = (measMosaic.Source(m[0], wcs), measMosaic.Source(m[1]))
                        match[1].setExp(dataRef.dataId['visit'])
                        match[1].setChip(dataRef.dataId['ccd'])
                        mlVisit[dataRef.dataId['visit']].append(match)
                wcsDic[dataRef.dataId['visit']][dataRef.dataId['ccd']] = wcs
                calibDic[dataRef.dataId['visit']][dataRef.dataId['ccd']] = photoCalib
                ffpDic[dataRef.dataId['visit']][dataRef.dataId['ccd']] = ffp
                dataRefListUsed.append(dataRef)

        for visit in ssVisit:
            sourceSet.append(ssVisit[visit])
            matchList.append(mlVisit[visit])

        d_lim = afwGeom.Angle(self.config.radXMatch, afwGeom.arcseconds)
        nbrightest = self.config.nBrightest

        allMat, allSource = self.mergeCatalog(sourceSet, matchList, ccdSet, d_lim)

        dx_m, dy_m, dx_s, dy_s, m0_m, dm_m, m0_s, dm_s  = self.makeDiffPosFlux(allMat, allSource, wcsDic, calibDic, ffpDic)
        self.plotFlux(m0_m, dm_m, m0_s, dm_s)
        self.makeFluxStat(allMat, allSource, calibDic, ffpDic, wcsDic)
        self.plotPos(dx_m, dy_m, dx_s, dy_s)
        self.plotPosAsMag(m0_s, dx_s, dy_s)
        self.writeCatalog(allSource, wcsDic, calibDic, ffpDic)