def run(self, exposure, exposureIdInfo, background=None, allowApCorr=True): """!Detect, deblend and perform single-frame measurement on sources and refine the background model @param[in,out] exposure exposure to process. Background must already be subtracted to reasonable accuracy, else detection will fail. The background model is refined and resubtracted. apCorrMap is set if measuring aperture correction. @param[in] exposureIdInfo ID info for exposure (an lsst.daf.butlerUtils.ExposureIdInfo) @param[in,out] background background model to be modified (an lsst.afw.math.BackgroundList), or None to create a new background model @param[in] allowApCorr allow measuring and applying aperture correction? @return pipe_base Struct containing these fields: - exposure: input exposure (as modified in the course of runing) - sourceCat: source catalog (an lsst.afw.table.SourceTable) - background: background model (input as modified, or a new model if input is None); an lsst.afw.math.BackgroundList """ if background is None: background = BackgroundList() sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources if detRes.fpSets.background: background.append(detRes.fpSets.background) if self.config.doDeblend: self.deblend.run( exposure=exposure, sources=sourceCat, psf=exposure.getPsf(), ) self.measure( exposure=exposure, exposureIdInfo=exposureIdInfo, sourceCat=sourceCat, allowApCorr=allowApCorr, ) return pipeBase.Struct( exposure=exposure, sourceCat=sourceCat, background=background, )
def run(self, exposure, exposureIdInfo, background=None, allowApCorr=True): """!Detect, deblend and perform single-frame measurement on sources and refine the background model @param[in,out] exposure exposure to process. Background must already be subtracted to reasonable accuracy, else detection will fail. The background model is refined and resubtracted. apCorrMap is set if measuring aperture correction. @param[in] exposureIdInfo ID info for exposure (an lsst.daf.butlerUtils.ExposureIdInfo) @param[in,out] background background model to be modified (an lsst.afw.math.BackgroundList), or None to create a new background model @param[in] allowApCorr allow measuring and applying aperture correction? @return pipe_base Struct containing these fields: - exposure: input exposure (as modified in the course of runing) - sourceCat: source catalog (an lsst.afw.table.SourceTable) - background: background model (input as modified, or a new model if input is None); an lsst.afw.math.BackgroundList """ if background is None: background = BackgroundList() sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources if detRes.fpSets.background: background.append(detRes.fpSets.background) if self.config.doDeblend: self.deblend.run( exposure = exposure, sources = sourceCat, psf = exposure.getPsf(), ) self.measure( exposure = exposure, exposureIdInfo = exposureIdInfo, sourceCat = sourceCat, allowApCorr = allowApCorr, ) return pipeBase.Struct( exposure = exposure, sourceCat = sourceCat, background = background, )
def detectMeasureAndEstimatePsf(self, exposure, exposureIdInfo, background): """!Perform one iteration of detect, measure and estimate PSF Performs the following operations: - if config.doMeasurePsf or not exposure.hasPsf(): - install a simple PSF model (replacing the existing one, if need be) - interpolate over cosmic rays with keepCRs=True - estimate background and subtract it from the exposure - detect, deblend and measure sources, and subtract a refined background model; - if config.doMeasurePsf: - measure PSF @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar) The following changes are made: - update or set psf - update detection and cosmic ray mask planes - subtract background @param[in] exposureIdInfo ID info for exposure (an lsst.obs_base.ExposureIdInfo) @param[in,out] background initial model of background already subtracted from exposure (an lsst.afw.math.BackgroundList). @return pipe_base Struct containing these fields, all from the final iteration of detect sources, measure sources and estimate PSF: - exposure characterized exposure; image is repaired by interpolating over cosmic rays, mask is updated accordingly, and the PSF model is set - sourceCat detected sources (an lsst.afw.table.SourceCatalog) - background model of background subtracted from exposure (an lsst.afw.math.BackgroundList) - psfCellSet spatial cells of PSF candidates (an lsst.afw.math.SpatialCellSet) """ # install a simple PSF model, if needed or wanted if not exposure.hasPsf() or (self.config.doMeasurePsf and self.config.useSimplePsf): self.log.warn( "Source catalog detected and measured with placeholder or default PSF" ) self.installSimplePsf.run(exposure=exposure) # run repair, but do not interpolate over cosmic rays (do that elsewhere, with the final PSF model) self.repair.run(exposure=exposure, keepCRs=True) self.display("repair_iter", exposure=exposure) if background is None: background = BackgroundList() sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources if detRes.fpSets.background: for bg in detRes.fpSets.background: background.append(bg) if self.config.doDeblend: self.deblend.run(exposure=exposure, sources=sourceCat) self.measurement.run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId) measPsfRes = pipeBase.Struct(cellSet=None) if self.config.doMeasurePsf: if self.measurePsf.usesMatches: matches = self.ref_match.loadAndMatch( exposure=exposure, sourceCat=sourceCat).matches else: matches = None measPsfRes = self.measurePsf.run(exposure=exposure, sources=sourceCat, matches=matches, expId=exposureIdInfo.expId) self.display("measure_iter", exposure=exposure, sourceCat=sourceCat) return pipeBase.Struct( exposure=exposure, sourceCat=sourceCat, background=background, psfCellSet=measPsfRes.cellSet, )
def run(self, exposure, exposureIdInfo=None, background=None, icSourceCat=None): """!Calibrate an exposure (science image or coadd) @param[in,out] exposure exposure to calibrate (an lsst.afw.image.ExposureF or similar); in: - MaskedImage - Psf out: - MaskedImage has background subtracted - Wcs is replaced - PhotoCalib is replaced @param[in] exposureIdInfo ID info for exposure (an lsst.obs.base.ExposureIdInfo) If not provided, returned SourceCatalog IDs will not be globally unique. @param[in,out] background background model already subtracted from exposure (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted, though that is unusual for calibration. A refined background model is output. @param[in] icSourceCat A SourceCatalog from CharacterizeImageTask from which we can copy some fields. @return pipe_base Struct containing these fields: - exposure calibrate science exposure with refined WCS and PhotoCalib - background model of background subtracted from exposure (an lsst.afw.math.BackgroundList) - sourceCat catalog of measured sources - astromMatches list of source/refObj matches from the astrometry solver """ # detect, deblend and measure sources if exposureIdInfo is None: exposureIdInfo = ExposureIdInfo() if background is None: background = BackgroundList() sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources if detRes.fpSets.background: for bg in detRes.fpSets.background: background.append(bg) if self.config.doSkySources: skySourceFootprints = self.skySources.run(mask=exposure.mask, seed=exposureIdInfo.expId) if skySourceFootprints: for foot in skySourceFootprints: s = sourceCat.addNew() s.setFootprint(foot) s.set(self.skySourceKey, True) if self.config.doDeblend: self.deblend.run(exposure=exposure, sources=sourceCat) self.measurement.run( measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId ) if self.config.doApCorr: self.applyApCorr.run( catalog=sourceCat, apCorrMap=exposure.getInfo().getApCorrMap() ) self.catalogCalculation.run(sourceCat) self.setPrimaryFlags.run(sourceCat, includeDeblend=self.config.doDeblend) if icSourceCat is not None and \ len(self.config.icSourceFieldsToCopy) > 0: self.copyIcSourceFields(icSourceCat=icSourceCat, sourceCat=sourceCat) # TODO DM-11568: this contiguous check-and-copy could go away if we # reserve enough space during SourceDetection and/or SourceDeblend. # NOTE: sourceSelectors require contiguous catalogs, so ensure # contiguity now, so views are preserved from here on. if not sourceCat.isContiguous(): sourceCat = sourceCat.copy(deep=True) # perform astrometry calibration: # fit an improved WCS and update the exposure's WCS in place astromMatches = None matchMeta = None if self.config.doAstrometry: try: astromRes = self.astrometry.run( exposure=exposure, sourceCat=sourceCat, ) astromMatches = astromRes.matches matchMeta = astromRes.matchMeta except Exception as e: if self.config.requireAstrometry: raise self.log.warn("Unable to perform astrometric calibration " "(%s): attempting to proceed" % e) # compute photometric calibration if self.config.doPhotoCal: try: photoRes = self.photoCal.run(exposure, sourceCat=sourceCat, expId=exposureIdInfo.expId) exposure.setPhotoCalib(photoRes.photoCalib) # TODO: reword this to phrase it in terms of the calibration factor? self.log.info("Photometric zero-point: %f" % photoRes.photoCalib.instFluxToMagnitude(1.0)) self.setMetadata(exposure=exposure, photoRes=photoRes) except Exception as e: if self.config.requirePhotoCal: raise self.log.warn("Unable to perform photometric calibration " "(%s): attempting to proceed" % e) self.setMetadata(exposure=exposure, photoRes=None) if self.config.doInsertFakes: self.insertFakes.run(exposure, background=background) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources if detRes.fpSets.background: for bg in detRes.fpSets.background: background.append(bg) if self.config.doDeblend: self.deblend.run(exposure=exposure, sources=sourceCat) self.measurement.run( measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId ) if self.config.doApCorr: self.applyApCorr.run( catalog=sourceCat, apCorrMap=exposure.getInfo().getApCorrMap() ) self.catalogCalculation.run(sourceCat) if icSourceCat is not None and len(self.config.icSourceFieldsToCopy) > 0: self.copyIcSourceFields(icSourceCat=icSourceCat, sourceCat=sourceCat) frame = getDebugFrame(self._display, "calibrate") if frame: displayAstrometry( sourceCat=sourceCat, exposure=exposure, matches=astromMatches, frame=frame, pause=False, ) return pipeBase.Struct( exposure=exposure, background=background, sourceCat=sourceCat, astromMatches=astromMatches, matchMeta=matchMeta, # These are duplicate entries with different names for use with # gen3 middleware outputExposure=exposure, outputCat=sourceCat, outputBackground=background, )
def calibrate(self, exposure, exposureIdInfo=None, background=None, icSourceCat=None): """!Calibrate an exposure (science image or coadd) @param[in,out] exposure exposure to calibrate (an lsst.afw.image.ExposureF or similar); in: - MaskedImage - Psf out: - MaskedImage has background subtracted - Wcs is replaced - Calib zero-point is set @param[in] exposureIdInfo ID info for exposure (an lsst.obs.base.ExposureIdInfo) If not provided, returned SourceCatalog IDs will not be globally unique. @param[in,out] background background model already subtracted from exposure (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted, though that is unusual for calibration. A refined background model is output. @param[in] icSourceCat A SourceCatalog from CharacterizeImageTask from which we can copy some fields. @return pipe_base Struct containing these fields: - exposure calibrate science exposure with refined WCS and Calib - background model of background subtracted from exposure (an lsst.afw.math.BackgroundList) - sourceCat catalog of measured sources - astromMatches list of source/refObj matches from the astrometry solver """ # detect, deblend and measure sources if exposureIdInfo is None: exposureIdInfo = ExposureIdInfo() if background is None: background = BackgroundList() sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) if self.config.doInsertFakes: self.insertFakes.run(exposure, background=background) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources if detRes.fpSets.background: background.append(detRes.fpSets.background) if self.config.doDeblend: self.deblend.run(exposure=exposure, sources=sourceCat) self.measurement.run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId) if self.config.doApCorr: self.applyApCorr.run(catalog=sourceCat, apCorrMap=exposure.getInfo().getApCorrMap()) self.catalogCalculation.run(sourceCat) if icSourceCat is not None and \ len(self.config.icSourceFieldsToCopy) > 0: self.copyIcSourceFields(icSourceCat=icSourceCat, sourceCat=sourceCat) # perform astrometry calibration: # fit an improved WCS and update the exposure's WCS in place astromMatches = None if self.config.doAstrometry: try: astromRes = self.astrometry.run( exposure=exposure, sourceCat=sourceCat, ) astromMatches = astromRes.matches except Exception as e: if self.config.requireAstrometry: raise self.log.warn("Unable to perform astrometric calibration " "(%s): attempting to proceed" % e) # compute photometric calibration if self.config.doPhotoCal: try: photoRes = self.photoCal.run(exposure, sourceCat=sourceCat) exposure.getCalib().setFluxMag0(photoRes.calib.getFluxMag0()) self.log.info("Photometric zero-point: %f" % photoRes.calib.getMagnitude(1.0)) self.setMetadata(exposure=exposure, photoRes=photoRes) except Exception as e: if self.config.requirePhotoCal: raise self.log.warn("Unable to perform photometric calibration " "(%s): attempting to proceed" % e) self.setMetadata(exposure=exposure, photoRes=None) frame = getDebugFrame(self._display, "calibrate") if frame: displayAstrometry( sourceCat=sourceCat, exposure=exposure, matches=astromMatches, frame=frame, pause=False, ) return pipeBase.Struct( exposure=exposure, background=background, sourceCat=sourceCat, astromMatches=astromMatches, )
def run(self, fakeCat, exposure, wcs=None, photoCalib=None, exposureIdInfo=None, icSourceCat=None, sfdSourceCat=None): """Add fake sources to a calexp and then run detection, deblending and measurement. Parameters ---------- fakeCat : `pandas.core.frame.DataFrame` The catalog of fake sources to add to the exposure exposure : `lsst.afw.image.exposure.exposure.ExposureF` The exposure to add the fake sources to wcs : `lsst.afw.geom.SkyWcs` WCS to use to add fake sources photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` Photometric calibration to be used to calibrate the fake sources exposureIdInfo : `lsst.obs.base.ExposureIdInfo` icSourceCat : `lsst.afw.table.SourceCatalog` Default : None Catalog to take the information about which sources were used for calibration from. sfdSourceCat : `lsst.afw.table.SourceCatalog` Default : None Catalog produced by singleFrameDriver, needed to copy some calibration flags from. Returns ------- resultStruct : `lsst.pipe.base.struct.Struct` contains : outputExposure : `lsst.afw.image.exposure.exposure.ExposureF` outputCat : `lsst.afw.table.source.source.SourceCatalog` Notes ----- Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half light radius = 0 (if ``config.cleanCat = True``). These columns are called ``x`` and ``y`` and are in pixels. Adds the ``Fake`` mask plane to the exposure which is then set by `addFakeSources` to mark where fake sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim) and fake stars, using the PSF models from the PSF information for the calexp. These are then added to the calexp and the calexp with fakes included returned. The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk, this is then convolved with the PSF at that point. If exposureIdInfo is not provided then the SourceCatalog IDs will not be globally unique. """ if wcs is None: wcs = exposure.getWcs() if photoCalib is None: photoCalib = exposure.getPhotoCalib() self.insertFakes.run(fakeCat, exposure, wcs, photoCalib) # detect, deblend and measure sources if exposureIdInfo is None: exposureIdInfo = ExposureIdInfo() sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources self.deblend.run(exposure=exposure, sources=sourceCat) self.measurement.run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId) self.applyApCorr.run(catalog=sourceCat, apCorrMap=exposure.getInfo().getApCorrMap()) self.catalogCalculation.run(sourceCat) sourceCat = self.copyCalibrationFields( icSourceCat, sourceCat, self.config.calibrationFieldsToCopy) sourceCat = self.copyCalibrationFields(sfdSourceCat, sourceCat, self.config.srcFieldsToCopy) resultStruct = pipeBase.Struct(outputExposure=exposure, outputCat=sourceCat) return resultStruct
def run(self, exposure=None, exposureIdInfo=None, background=None, doPsf=True, doApCorr=True, doWrite=True, doCalc=True): if not exposure.hasPsf(): self.log.warn("Using SimplePsf for astrometry source detection") self.installSimplePsf.run(exposure=exposure) #Repair cosmic rays #self.repair.run(exposure=exposure, keepCRs=True) # subtract an initial estimate of background level background = self.background.run(exposure).background #Table schema needs to be set up prior to detection: sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) #Perform detection sourceCat = self.detection.run(table=table, exposure=exposure, doSmooth=True).sources #Perform measurement self.measurement.run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId) if self.config.doEarlyAstrometry: astromRes = self.astrometry.run(exposure=exposure, sourceCat=sourceCat) measPsfRes = pipeBase.Struct(cellSet=None) if doPsf and self.config.doMeasurePsf: if self.measurePsf.usesMatches: matches = self.ref_match.loadAndMatch( exposure=exposure, sourceCat=sourceCat).matches else: matches = None psfIterations = self.config.psfIterations if self.config.doMeasurePsf else 1 for i in range(psfIterations): measPsfRes = self.measurePsf.run(exposure=exposure, sources=sourceCat, matches=matches, expId=exposureIdInfo.expId) # perform final repair with final PSF self.repair.run(exposure=exposure) if doApCorr and self.config.doApCorr: apCorrMap = self.measureApCorr.run(exposure=exposure, catalog=sourceCat).apCorrMap exposure.getInfo().setApCorrMap(apCorrMap) self.applyApCorr.run(catalog=sourceCat, apCorrMap=exposure.getInfo().getApCorrMap()) if doCalc: self.catalogCalculation.run(sourceCat) return pipeBase.Struct( exposure=exposure, sourceCat=sourceCat, background=background, psfCellSet=measPsfRes.cellSet, )
def detectMeasureAndEstimatePsf(self, exposure, exposureIdInfo, background): """!Perform one iteration of detect, measure and estimate PSF Performs the following operations: - if config.doMeasurePsf or not exposure.hasPsf(): - install a simple PSF model (replacing the existing one, if need be) - interpolate over cosmic rays with keepCRs=True - estimate background and subtract it from the exposure - detect, deblend and measure sources, and subtract a refined background model; - if config.doMeasurePsf: - measure PSF @param[in,out] exposure exposure to characterize (an lsst.afw.image.ExposureF or similar) The following changes are made: - update or set psf - update detection and cosmic ray mask planes - subtract background @param[in] exposureIdInfo ID info for exposure (an lsst.obs_base.ExposureIdInfo) @param[in,out] background initial model of background already subtracted from exposure (an lsst.afw.math.BackgroundList). @return pipe_base Struct containing these fields, all from the final iteration of detect sources, measure sources and estimate PSF: - exposure characterized exposure; image is repaired by interpolating over cosmic rays, mask is updated accordingly, and the PSF model is set - sourceCat detected sources (an lsst.afw.table.SourceCatalog) - background model of background subtracted from exposure (an lsst.afw.math.BackgroundList) - psfCellSet spatial cells of PSF candidates (an lsst.afw.math.SpatialCellSet) """ # install a simple PSF model, if needed or wanted if not exposure.hasPsf() or (self.config.doMeasurePsf and self.config.useSimplePsf): self.log.warn("Source catalog detected and measured with placeholder or default PSF") self.installSimplePsf.run(exposure=exposure) # run repair, but do not interpolate over cosmic rays (do that elsewhere, with the final PSF model) self.repair.run(exposure=exposure, keepCRs=True) self.display("repair_iter", exposure=exposure) if background is None: background = BackgroundList() sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) detRes = self.detection.run(table=table, exposure=exposure, doSmooth=True) sourceCat = detRes.sources if detRes.fpSets.background: for bg in detRes.fpSets.background: background.append(bg) if self.config.doDeblend: self.deblend.run(exposure=exposure, sources=sourceCat) self.measurement.run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId) measPsfRes = pipeBase.Struct(cellSet=None) if self.config.doMeasurePsf: if self.measurePsf.usesMatches: matches = self.ref_match.loadAndMatch(exposure=exposure, sourceCat=sourceCat).matches else: matches = None measPsfRes = self.measurePsf.run(exposure=exposure, sources=sourceCat, matches=matches, expId=exposureIdInfo.expId) self.display("measure_iter", exposure=exposure, sourceCat=sourceCat) return pipeBase.Struct( exposure=exposure, sourceCat=sourceCat, background=background, psfCellSet=measPsfRes.cellSet, )
def run(self, dataRef, exposure=None, background=None, doUnpersist=True, doPsf=True, doApCorr=True, doWrite=True, doCalc=True): self.log.info("gotoCharTask Processing %s" % (dataRef.dataId)) if doUnpersist: if exposure is not None or background is not None: raise RuntimeError( "doUnpersist true; exposure and background must be None") exposure = dataRef.get("postISRCCD", immediate=True) elif exposure is None: raise RuntimeError("doUnpersist false; exposure must be provided") exposureIdInfo = dataRef.get("expIdInfo") if not exposure.hasPsf(): self.log.warn("Using SimplePsf for astrometry source detection") self.installSimplePsf.run(exposure=exposure) #Repair cosmic rays self.repair.run(exposure=exposure, keepCRs=True) # subtract an initial estimate of background level background = self.background.run(exposure).background #Table schema needs to be set up prior to detection: sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId, exposureIdInfo.unusedBits) table = SourceTable.make(self.schema, sourceIdFactory) table.setMetadata(self.algMetadata) #Perform detection sourceCat = self.detection.run(table=table, exposure=exposure, doSmooth=True).sources #Perform measurement self.measurement.run(measCat=sourceCat, exposure=exposure, exposureId=exposureIdInfo.expId) if self.config.doEarlyAstrometry: astromRes = self.astrometry.run(exposure=exposure, sourceCat=sourceCat) measPsfRes = pipeBase.Struct(cellSet=None) if doPsf and self.config.doMeasurePsf: if self.measurePsf.usesMatches: matches = self.ref_match.loadAndMatch( exposure=exposure, sourceCat=sourceCat).matches else: matches = None psfIterations = self.config.psfIterations if self.config.doMeasurePsf else 1 for i in range(psfIterations): measPsfRes = self.measurePsf.run(exposure=exposure, sources=sourceCat, matches=matches, expId=exposureIdInfo.expId) # perform final repair with final PSF self.repair.run(exposure=exposure) if doApCorr and self.config.doApCorr: apCorrMap = self.measureApCorr.run(exposure=exposure, catalog=sourceCat).apCorrMap exposure.getInfo().setApCorrMap(apCorrMap) self.applyApCorr.run(catalog=sourceCat, apCorrMap=exposure.getInfo().getApCorrMap()) if doCalc: self.catalogCalculation.run(sourceCat) if doWrite and self.config.doWrite: dataRef.put(sourceCat, "icSrc") if self.config.doWriteExposure: dataRef.put(exposure, "icExp") dataRef.put(background, "icExpBackground") return pipeBase.Struct( exposure=exposure, sourceCat=sourceCat, background=background, psfCellSet=measPsfRes.cellSet, )