24 from lsstDebug
import getDebugFrame
25 import lsst.pex.config
as pexConfig
29 from lsst.meas.astrom import AstrometryTask, displayAstrometry, denormalizeMatches
38 CatalogCalculationTask,
39 EvaluateLocalCalibrationTask)
41 from .fakes
import BaseFakeSourcesTask
42 from .photoCal
import PhotoCalTask
44 __all__ = [
"CalibrateConfig",
"CalibrateTask"]
47 class CalibrateConnections(pipeBase.PipelineTaskConnections, dimensions=(
"instrument",
"visit",
"detector"),
50 icSourceSchema = cT.InitInput(
51 doc=
"Schema produced by characterize image task, used to initialize this task",
53 storageClass=
"SourceCatalog",
56 outputSchema = cT.InitOutput(
57 doc=
"Schema after CalibrateTask has been initialized",
59 storageClass=
"SourceCatalog",
63 doc=
"Input image to calibrate",
65 storageClass=
"ExposureF",
66 dimensions=(
"instrument",
"visit",
"detector"),
69 background = cT.Input(
70 doc=
"Backgrounds determined by characterize task",
71 name=
"icExpBackground",
72 storageClass=
"Background",
73 dimensions=(
"instrument",
"visit",
"detector"),
76 icSourceCat = cT.Input(
77 doc=
"Source catalog created by characterize task",
79 storageClass=
"SourceCatalog",
80 dimensions=(
"instrument",
"visit",
"detector"),
83 astromRefCat = cT.PrerequisiteInput(
84 doc=
"Reference catalog to use for astrometry",
86 storageClass=
"SimpleCatalog",
87 dimensions=(
"skypix",),
92 photoRefCat = cT.PrerequisiteInput(
93 doc=
"Reference catalog to use for photometric calibration",
95 storageClass=
"SimpleCatalog",
96 dimensions=(
"skypix",),
101 outputExposure = cT.Output(
102 doc=
"Exposure after running calibration task",
104 storageClass=
"ExposureF",
105 dimensions=(
"instrument",
"visit",
"detector"),
108 outputCat = cT.Output(
109 doc=
"Source catalog produced in calibrate task",
111 storageClass=
"SourceCatalog",
112 dimensions=(
"instrument",
"visit",
"detector"),
115 outputBackground = cT.Output(
116 doc=
"Background models estimated in calibration task",
117 name=
"calexpBackground",
118 storageClass=
"Background",
119 dimensions=(
"instrument",
"visit",
"detector"),
123 doc=
"Source/refObj matches from the astrometry solver",
125 storageClass=
"Catalog",
126 dimensions=(
"instrument",
"visit",
"detector"),
129 matchesDenormalized = cT.Output(
130 doc=
"Denormalized matches from astrometry solver",
132 storageClass=
"Catalog",
133 dimensions=(
"instrument",
"visit",
"detector"),
138 if config.doWriteMatches
is False:
139 self.outputs.remove(
"matches")
140 if config.doWriteMatchesDenormalized
is False:
141 self.outputs.remove(
"matchesDenormalized")
144 class CalibrateConfig(pipeBase.PipelineTaskConfig, pipelineConnections=CalibrateConnections):
145 """Config for CalibrateTask""" 146 doWrite = pexConfig.Field(
149 doc=
"Save calibration results?",
151 doWriteHeavyFootprintsInSources = pexConfig.Field(
154 doc=
"Include HeavyFootprint data in source table? If false then heavy " 155 "footprints are saved as normal footprints, which saves some space" 157 doWriteMatches = pexConfig.Field(
160 doc=
"Write reference matches (ignored if doWrite false)?",
162 doWriteMatchesDenormalized = pexConfig.Field(
165 doc=(
"Write reference matches in denormalized format? " 166 "This format uses more disk space, but is more convenient to " 167 "read. Ignored if doWriteMatches=False or doWrite=False."),
169 doAstrometry = pexConfig.Field(
172 doc=
"Perform astrometric calibration?",
174 astromRefObjLoader = pexConfig.ConfigurableField(
175 target=LoadIndexedReferenceObjectsTask,
176 doc=
"reference object loader for astrometric calibration",
178 photoRefObjLoader = pexConfig.ConfigurableField(
179 target=LoadIndexedReferenceObjectsTask,
180 doc=
"reference object loader for photometric calibration",
182 astrometry = pexConfig.ConfigurableField(
183 target=AstrometryTask,
184 doc=
"Perform astrometric calibration to refine the WCS",
186 requireAstrometry = pexConfig.Field(
189 doc=(
"Raise an exception if astrometry fails? Ignored if doAstrometry " 192 doPhotoCal = pexConfig.Field(
195 doc=
"Perform phometric calibration?",
197 requirePhotoCal = pexConfig.Field(
200 doc=(
"Raise an exception if photoCal fails? Ignored if doPhotoCal " 203 photoCal = pexConfig.ConfigurableField(
205 doc=
"Perform photometric calibration",
207 doEvalLocCalibration = pexConfig.Field(
210 doc=
"Store calibration products (local wcs and PhotoCalib) in output " 213 evalLocCalib = pexConfig.ConfigurableField(
214 target=EvaluateLocalCalibrationTask,
215 doc=
"Task to strip calibrations from an exposure and store their " 216 "local values in the output source catalog." 218 icSourceFieldsToCopy = pexConfig.ListField(
220 default=(
"calib_psf_candidate",
"calib_psf_used",
"calib_psf_reserved"),
221 doc=(
"Fields to copy from the icSource catalog to the output catalog " 222 "for matching sources Any missing fields will trigger a " 223 "RuntimeError exception. Ignored if icSourceCat is not provided.")
225 matchRadiusPix = pexConfig.Field(
228 doc=(
"Match radius for matching icSourceCat objects to sourceCat " 231 checkUnitsParseStrict = pexConfig.Field(
232 doc=(
"Strictness of Astropy unit compatibility check, can be 'raise', " 233 "'warn' or 'silent'"),
237 detection = pexConfig.ConfigurableField(
238 target=SourceDetectionTask,
241 doDeblend = pexConfig.Field(
244 doc=
"Run deblender input exposure" 246 deblend = pexConfig.ConfigurableField(
247 target=SourceDeblendTask,
248 doc=
"Split blended sources into their components" 250 measurement = pexConfig.ConfigurableField(
251 target=SingleFrameMeasurementTask,
252 doc=
"Measure sources" 254 doApCorr = pexConfig.Field(
257 doc=
"Run subtask to apply aperture correction" 259 applyApCorr = pexConfig.ConfigurableField(
260 target=ApplyApCorrTask,
261 doc=
"Subtask to apply aperture corrections" 266 catalogCalculation = pexConfig.ConfigurableField(
267 target=CatalogCalculationTask,
268 doc=
"Subtask to run catalogCalculation plugins on catalog" 270 doInsertFakes = pexConfig.Field(
273 doc=
"Run fake sources injection task" 275 insertFakes = pexConfig.ConfigurableField(
276 target=BaseFakeSourcesTask,
277 doc=
"Injection of fake sources for testing purposes (must be " 280 doWriteExposure = pexConfig.Field(
283 doc=
"Write the calexp? If fakes have been added then we do not want to write out the calexp as a " 284 "normal calexp but as a fakes_calexp." 289 self.
detection.doTempLocalBackground =
False 290 self.
deblend.maxFootprintSize = 2000
295 if astromRefCatGen2
is not None and astromRefCatGen2 != self.connections.astromRefCat:
297 f
"Gen2 ({astromRefCatGen2}) and Gen3 ({self.connections.astromRefCat}) astrometry reference " 298 f
"catalogs are different. These options must be kept in sync until Gen2 is retired." 301 if photoRefCatGen2
is not None and photoRefCatGen2 != self.connections.photoRefCat:
303 f
"Gen2 ({photoRefCatGen2}) and Gen3 ({self.connections.photoRefCat}) photometry reference " 304 f
"catalogs are different. These options must be kept in sync until Gen2 is retired." 316 r"""!Calibrate an exposure: measure sources and perform astrometric and 317 photometric calibration 319 @anchor CalibrateTask_ 321 @section pipe_tasks_calibrate_Contents Contents 323 - @ref pipe_tasks_calibrate_Purpose 324 - @ref pipe_tasks_calibrate_Initialize 325 - @ref pipe_tasks_calibrate_IO 326 - @ref pipe_tasks_calibrate_Config 327 - @ref pipe_tasks_calibrate_Metadata 328 - @ref pipe_tasks_calibrate_Debug 331 @section pipe_tasks_calibrate_Purpose Description 333 Given an exposure with a good PSF model and aperture correction map 334 (e.g. as provided by @ref CharacterizeImageTask), perform the following 336 - Run detection and measurement 337 - Run astrometry subtask to fit an improved WCS 338 - Run photoCal subtask to fit the exposure's photometric zero-point 340 @section pipe_tasks_calibrate_Initialize Task initialisation 342 @copydoc \_\_init\_\_ 344 @section pipe_tasks_calibrate_IO Invoking the Task 346 If you want this task to unpersist inputs or persist outputs, then call 347 the `runDataRef` method (a wrapper around the `run` method). 349 If you already have the inputs unpersisted and do not want to persist the 350 output then it is more direct to call the `run` method: 352 @section pipe_tasks_calibrate_Config Configuration parameters 354 See @ref CalibrateConfig 356 @section pipe_tasks_calibrate_Metadata Quantities set in exposure Metadata 360 <dt>MAGZERO_RMS <dd>MAGZERO's RMS == sigma reported by photoCal task 361 <dt>MAGZERO_NOBJ <dd>Number of stars used == ngood reported by photoCal 363 <dt>COLORTERM1 <dd>?? (always 0.0) 364 <dt>COLORTERM2 <dd>?? (always 0.0) 365 <dt>COLORTERM3 <dd>?? (always 0.0) 368 @section pipe_tasks_calibrate_Debug Debug variables 370 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink 371 interface supports a flag 372 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug 373 for more about `debug.py`. 375 CalibrateTask has a debug dictionary containing one key: 378 <dd>frame (an int; <= 0 to not display) in which to display the exposure, 379 sources and matches. See @ref lsst.meas.astrom.displayAstrometry for 380 the meaning of the various symbols. 383 For example, put something like: 387 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would 388 # call us recursively 389 if name == "lsst.pipe.tasks.calibrate": 396 lsstDebug.Info = DebugInfo 398 into your `debug.py` file and run `calibrateTask.py` with the `--debug` 401 Some subtasks may have their own debug variables; see individual Task 408 ConfigClass = CalibrateConfig
409 _DefaultName =
"calibrate" 410 RunnerClass = pipeBase.ButlerInitializedTaskRunner
412 def __init__(self, butler=None, astromRefObjLoader=None,
413 photoRefObjLoader=None, icSourceSchema=None,
414 initInputs=None, **kwargs):
415 """!Construct a CalibrateTask 417 @param[in] butler The butler is passed to the refObjLoader constructor 418 in case it is needed. Ignored if the refObjLoader argument 419 provides a loader directly. 420 @param[in] astromRefObjLoader An instance of LoadReferenceObjectsTasks 421 that supplies an external reference catalog for astrometric 422 calibration. May be None if the desired loader can be constructed 423 from the butler argument or all steps requiring a reference catalog 425 @param[in] photoRefObjLoader An instance of LoadReferenceObjectsTasks 426 that supplies an external reference catalog for photometric 427 calibration. May be None if the desired loader can be constructed 428 from the butler argument or all steps requiring a reference catalog 430 @param[in] icSourceSchema schema for icSource catalog, or None. 431 Schema values specified in config.icSourceFieldsToCopy will be 432 taken from this schema. If set to None, no values will be 433 propagated from the icSourceCatalog 434 @param[in,out] kwargs other keyword arguments for 435 lsst.pipe.base.CmdLineTask 439 if icSourceSchema
is None and butler
is not None:
441 icSourceSchema = butler.get(
"icSrc_schema", immediate=
True).schema
443 if icSourceSchema
is None and butler
is None and initInputs
is not None:
444 icSourceSchema = initInputs[
'icSourceSchema'].schema
446 if icSourceSchema
is not None:
449 minimumSchema = afwTable.SourceTable.makeMinimalSchema()
450 self.
schemaMapper.addMinimalSchema(minimumSchema,
False)
458 afwTable.Field[
"Flag"](
"calib_detected",
459 "Source was detected as an icSource"))
460 missingFieldNames = []
461 for fieldName
in self.config.icSourceFieldsToCopy:
463 schemaItem = icSourceSchema.find(fieldName)
465 missingFieldNames.append(fieldName)
470 if missingFieldNames:
471 raise RuntimeError(
"isSourceCat is missing fields {} " 472 "specified in icSourceFieldsToCopy" 473 .format(missingFieldNames))
480 self.
schema = afwTable.SourceTable.makeMinimalSchema()
481 self.makeSubtask(
'detection', schema=self.
schema)
488 if self.config.doInsertFakes:
489 self.makeSubtask(
"insertFakes")
491 if self.config.doDeblend:
492 self.makeSubtask(
"deblend", schema=self.
schema)
493 self.makeSubtask(
'measurement', schema=self.
schema,
495 if self.config.doApCorr:
496 self.makeSubtask(
'applyApCorr', schema=self.
schema)
497 self.makeSubtask(
'catalogCalculation', schema=self.
schema)
499 if self.config.doAstrometry:
500 if astromRefObjLoader
is None and butler
is not None:
501 self.makeSubtask(
'astromRefObjLoader', butler=butler)
502 astromRefObjLoader = self.astromRefObjLoader
504 self.makeSubtask(
"astrometry", refObjLoader=astromRefObjLoader,
506 if self.config.doPhotoCal:
507 if photoRefObjLoader
is None and butler
is not None:
508 self.makeSubtask(
'photoRefObjLoader', butler=butler)
509 photoRefObjLoader = self.photoRefObjLoader
510 self.
pixelMargin = photoRefObjLoader.config.pixelMargin
511 self.makeSubtask(
"photoCal", refObjLoader=photoRefObjLoader,
514 if self.config.doEvalLocCalibration
and self.config.doAstrometry
and self.config.doPhotoCal:
515 self.makeSubtask(
"evalLocCalib", schema=self.
schema)
517 if initInputs
is not None and (astromRefObjLoader
is not None or photoRefObjLoader
is not None):
518 raise RuntimeError(
"PipelineTask form of this task should not be initialized with " 519 "reference object loaders.")
524 self.
schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
526 sourceCatSchema = afwTable.SourceCatalog(self.
schema)
531 def runDataRef(self, dataRef, exposure=None, background=None, icSourceCat=None,
533 """!Calibrate an exposure, optionally unpersisting inputs and 536 This is a wrapper around the `run` method that unpersists inputs 537 (if `doUnpersist` true) and persists outputs (if `config.doWrite` true) 539 @param[in] dataRef butler data reference corresponding to a science 541 @param[in,out] exposure characterized exposure (an 542 lsst.afw.image.ExposureF or similar), or None to unpersist existing 543 icExp and icBackground. See `run` method for details of what is 545 @param[in,out] background initial model of background already 546 subtracted from exposure (an lsst.afw.math.BackgroundList). May be 547 None if no background has been subtracted, though that is unusual 548 for calibration. A refined background model is output. Ignored if 550 @param[in] icSourceCat catalog from which to copy the fields specified 551 by icSourceKeys, or None; 552 @param[in] doUnpersist unpersist data: 553 - if True, exposure, background and icSourceCat are read from 554 dataRef and those three arguments must all be None; 555 - if False the exposure must be provided; background and 556 icSourceCat are optional. True is intended for running as a 557 command-line task, False for running as a subtask 558 @return same data as the calibrate method 560 self.log.info(
"Processing %s" % (dataRef.dataId))
563 if any(item
is not None for item
in (exposure, background,
565 raise RuntimeError(
"doUnpersist true; exposure, background " 566 "and icSourceCat must all be None")
567 exposure = dataRef.get(
"icExp", immediate=
True)
568 background = dataRef.get(
"icExpBackground", immediate=
True)
569 icSourceCat = dataRef.get(
"icSrc", immediate=
True)
570 elif exposure
is None:
571 raise RuntimeError(
"doUnpersist false; exposure must be provided")
573 exposureIdInfo = dataRef.get(
"expIdInfo")
577 exposureIdInfo=exposureIdInfo,
578 background=background,
579 icSourceCat=icSourceCat,
582 if self.config.doWrite:
585 exposure=calRes.exposure,
586 background=calRes.background,
587 sourceCat=calRes.sourceCat,
588 astromMatches=calRes.astromMatches,
589 matchMeta=calRes.matchMeta,
595 inputs = butlerQC.get(inputRefs)
596 expId, expBits = butlerQC.quantum.dataId.pack(
"visit_detector",
598 inputs[
'exposureIdInfo'] = ExposureIdInfo(expId, expBits)
600 if self.config.doAstrometry:
601 refObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
602 for ref
in inputRefs.astromRefCat],
603 refCats=inputs.pop(
'astromRefCat'),
604 config=self.config.astromRefObjLoader, log=self.log)
606 self.astrometry.setRefObjLoader(refObjLoader)
608 if self.config.doPhotoCal:
609 photoRefObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
610 for ref
in inputRefs.photoRefCat],
611 refCats=inputs.pop(
'photoRefCat'),
612 config=self.config.photoRefObjLoader,
614 self.
pixelMargin = photoRefObjLoader.config.pixelMargin
615 self.photoCal.match.setRefObjLoader(photoRefObjLoader)
617 outputs = self.
run(**inputs)
619 if self.config.doWriteMatches:
620 normalizedMatches = afwTable.packMatches(outputs.astromMatches)
621 normalizedMatches.table.setMetadata(outputs.matchMeta)
622 if self.config.doWriteMatchesDenormalized:
623 denormMatches = denormalizeMatches(outputs.astromMatches, outputs.matchMeta)
624 outputs.matchesDenormalized = denormMatches
625 outputs.matches = normalizedMatches
626 butlerQC.put(outputs, outputRefs)
628 def run(self, exposure, exposureIdInfo=None, background=None,
630 """!Calibrate an exposure (science image or coadd) 632 @param[in,out] exposure exposure to calibrate (an 633 lsst.afw.image.ExposureF or similar); 638 - MaskedImage has background subtracted 640 - PhotoCalib is replaced 641 @param[in] exposureIdInfo ID info for exposure (an 642 lsst.obs.base.ExposureIdInfo) If not provided, returned 643 SourceCatalog IDs will not be globally unique. 644 @param[in,out] background background model already subtracted from 645 exposure (an lsst.afw.math.BackgroundList). May be None if no 646 background has been subtracted, though that is unusual for 647 calibration. A refined background model is output. 648 @param[in] icSourceCat A SourceCatalog from CharacterizeImageTask 649 from which we can copy some fields. 651 @return pipe_base Struct containing these fields: 652 - exposure calibrate science exposure with refined WCS and PhotoCalib 653 - background model of background subtracted from exposure (an 654 lsst.afw.math.BackgroundList) 655 - sourceCat catalog of measured sources 656 - astromMatches list of source/refObj matches from the astrometry 660 if exposureIdInfo
is None:
661 exposureIdInfo = ExposureIdInfo()
663 if background
is None:
664 background = BackgroundList()
665 sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId,
666 exposureIdInfo.unusedBits)
667 table = SourceTable.make(self.
schema, sourceIdFactory)
670 detRes = self.detection.
run(table=table, exposure=exposure,
672 sourceCat = detRes.sources
673 if detRes.fpSets.background:
674 for bg
in detRes.fpSets.background:
675 background.append(bg)
676 if self.config.doDeblend:
677 self.deblend.
run(exposure=exposure, sources=sourceCat)
678 self.measurement.
run(
681 exposureId=exposureIdInfo.expId
683 if self.config.doApCorr:
684 self.applyApCorr.
run(
686 apCorrMap=exposure.getInfo().getApCorrMap()
688 self.catalogCalculation.
run(sourceCat)
690 if icSourceCat
is not None and \
691 len(self.config.icSourceFieldsToCopy) > 0:
699 if not sourceCat.isContiguous():
700 sourceCat = sourceCat.copy(deep=
True)
706 if self.config.doAstrometry:
708 astromRes = self.astrometry.
run(
712 astromMatches = astromRes.matches
713 matchMeta = astromRes.matchMeta
714 except Exception
as e:
715 if self.config.requireAstrometry:
717 self.log.warn(
"Unable to perform astrometric calibration " 718 "(%s): attempting to proceed" % e)
721 if self.config.doPhotoCal:
723 photoRes = self.photoCal.
run(exposure, sourceCat=sourceCat, expId=exposureIdInfo.expId)
724 exposure.setPhotoCalib(photoRes.photoCalib)
726 self.log.info(
"Photometric zero-point: %f" %
727 photoRes.photoCalib.instFluxToMagnitude(1.0))
728 self.
setMetadata(exposure=exposure, photoRes=photoRes)
729 except Exception
as e:
730 if self.config.requirePhotoCal:
732 self.log.warn(
"Unable to perform photometric calibration " 733 "(%s): attempting to proceed" % e)
736 if self.config.doEvalLocCalibration
and self.config.doAstrometry
and self.config.doPhotoCal:
737 self.evalLocCalib.
run(sourceCat, exposure)
739 if self.config.doInsertFakes:
740 self.insertFakes.
run(exposure, background=background)
742 table = SourceTable.make(self.
schema, sourceIdFactory)
745 detRes = self.detection.
run(table=table, exposure=exposure,
747 sourceCat = detRes.sources
748 if detRes.fpSets.background:
749 for bg
in detRes.fpSets.background:
750 background.append(bg)
751 if self.config.doDeblend:
752 self.deblend.
run(exposure=exposure, sources=sourceCat)
753 self.measurement.
run(
756 exposureId=exposureIdInfo.expId
758 if self.config.doApCorr:
759 self.applyApCorr.
run(
761 apCorrMap=exposure.getInfo().getApCorrMap()
763 self.catalogCalculation.
run(sourceCat)
765 if icSourceCat
is not None and len(self.config.icSourceFieldsToCopy) > 0:
769 frame = getDebugFrame(self._display,
"calibrate")
774 matches=astromMatches,
779 return pipeBase.Struct(
781 background=background,
783 astromMatches=astromMatches,
787 outputExposure=exposure,
789 outputBackground=background,
792 def writeOutputs(self, dataRef, exposure, background, sourceCat,
793 astromMatches, matchMeta):
794 """Write output data to the output repository 796 @param[in] dataRef butler data reference corresponding to a science 798 @param[in] exposure exposure to write 799 @param[in] background background model for exposure 800 @param[in] sourceCat catalog of measured sources 801 @param[in] astromMatches list of source/refObj matches from the 804 dataRef.put(sourceCat,
"src")
805 if self.config.doWriteMatches
and astromMatches
is not None:
806 normalizedMatches = afwTable.packMatches(astromMatches)
807 normalizedMatches.table.setMetadata(matchMeta)
808 dataRef.put(normalizedMatches,
"srcMatch")
809 if self.config.doWriteMatchesDenormalized:
810 denormMatches = denormalizeMatches(astromMatches, matchMeta)
811 dataRef.put(denormMatches,
"srcMatchFull")
812 if self.config.doWriteExposure:
813 dataRef.put(exposure,
"calexp")
814 dataRef.put(background,
"calexpBackground")
817 """Return a dict of empty catalogs for each catalog dataset produced 820 sourceCat = afwTable.SourceCatalog(self.
schema)
822 return {
"src": sourceCat}
825 """!Set task and exposure metadata 827 Logs a warning and continues if needed data is missing. 829 @param[in,out] exposure exposure whose metadata is to be set 830 @param[in] photoRes results of running photoCal; if None then it was 836 metadata = exposure.getMetadata()
840 exposureTime = exposure.getInfo().getVisitInfo().getExposureTime()
841 magZero = photoRes.zp - 2.5*math.log10(exposureTime)
843 self.log.warn(
"Could not set normalized MAGZERO in header: no " 848 metadata.set(
'MAGZERO', magZero)
849 metadata.set(
'MAGZERO_RMS', photoRes.sigma)
850 metadata.set(
'MAGZERO_NOBJ', photoRes.ngood)
851 metadata.set(
'COLORTERM1', 0.0)
852 metadata.set(
'COLORTERM2', 0.0)
853 metadata.set(
'COLORTERM3', 0.0)
854 except Exception
as e:
855 self.log.warn(
"Could not set exposure metadata: %s" % (e,))
858 """!Match sources in icSourceCat and sourceCat and copy the specified fields 860 @param[in] icSourceCat catalog from which to copy fields 861 @param[in,out] sourceCat catalog to which to copy fields 863 The fields copied are those specified by `config.icSourceFieldsToCopy` 864 that actually exist in the schema. This was set up by the constructor 865 using self.schemaMapper. 868 raise RuntimeError(
"To copy icSource fields you must specify " 869 "icSourceSchema nd icSourceKeys when " 870 "constructing this task")
871 if icSourceCat
is None or sourceCat
is None:
872 raise RuntimeError(
"icSourceCat and sourceCat must both be " 874 if len(self.config.icSourceFieldsToCopy) == 0:
875 self.log.warn(
"copyIcSourceFields doing nothing because " 876 "icSourceFieldsToCopy is empty")
879 mc = afwTable.MatchControl()
880 mc.findOnlyClosest =
False 881 matches = afwTable.matchXy(icSourceCat, sourceCat,
882 self.config.matchRadiusPix, mc)
883 if self.config.doDeblend:
884 deblendKey = sourceCat.schema[
"deblend_nChild"].asKey()
886 matches = [m
for m
in matches
if m[1].get(deblendKey) == 0]
893 for m0, m1, d
in matches:
895 match = bestMatches.get(id0)
896 if match
is None or d <= match[2]:
897 bestMatches[id0] = (m0, m1, d)
898 matches = list(bestMatches.values())
903 numMatches = len(matches)
904 numUniqueSources = len(set(m[1].getId()
for m
in matches))
905 if numUniqueSources != numMatches:
906 self.log.warn(
"{} icSourceCat sources matched only {} sourceCat " 907 "sources".format(numMatches, numUniqueSources))
909 self.log.info(
"Copying flags from icSourceCat to sourceCat for " 910 "%s sources" % (numMatches,))
914 for icSrc, src, d
in matches:
920 icSrcFootprint = icSrc.getFootprint()
922 icSrc.setFootprint(src.getFootprint())
925 icSrc.setFootprint(icSrcFootprint)
def copyIcSourceFields(self, icSourceCat, sourceCat)
Match sources in icSourceCat and sourceCat and copy the specified fields.
def __init__(self, butler=None, astromRefObjLoader=None, photoRefObjLoader=None, icSourceSchema=None, initInputs=None, kwargs)
Construct a CalibrateTask.
def run(self, exposure, exposureIdInfo=None, background=None, icSourceCat=None)
Calibrate an exposure (science image or coadd)
def writeOutputs(self, dataRef, exposure, background, sourceCat, astromMatches, matchMeta)
def __init__(self, config=None)
def setMetadata(self, exposure, photoRes=None)
Set task and exposure metadata.
def getSchemaCatalogs(self)
def runDataRef(self, dataRef, exposure=None, background=None, icSourceCat=None, doUnpersist=True)
Calibrate an exposure, optionally unpersisting inputs and persisting outputs.
Calibrate an exposure: measure sources and perform astrometric and photometric calibration.
def runQuantum(self, butlerQC, inputRefs, outputRefs)