24 from lsstDebug
import getDebugFrame
29 from lsst.meas.astrom import AstrometryTask, displayAstrometry, denormalizeMatches
31 from lsst.obs.base
import ExposureIdInfo
38 CatalogCalculationTask)
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"),
139 if config.doAstrometry
is False:
140 self.prerequisiteInputs.remove(
"astromRefCat")
141 if config.doPhotoCal
is False:
142 self.prerequisiteInputs.remove(
"photoRefCat")
144 if config.doWriteMatches
is False or config.doAstrometry
is False:
145 self.outputs.remove(
"matches")
146 if config.doWriteMatchesDenormalized
is False or config.doAstrometry
is False:
147 self.outputs.remove(
"matchesDenormalized")
150 class CalibrateConfig(pipeBase.PipelineTaskConfig, pipelineConnections=CalibrateConnections):
151 """Config for CalibrateTask"""
152 doWrite = pexConfig.Field(
155 doc=
"Save calibration results?",
157 doWriteHeavyFootprintsInSources = pexConfig.Field(
160 doc=
"Include HeavyFootprint data in source table? If false then heavy "
161 "footprints are saved as normal footprints, which saves some space"
163 doWriteMatches = pexConfig.Field(
166 doc=
"Write reference matches (ignored if doWrite or doAstrometry false)?",
168 doWriteMatchesDenormalized = pexConfig.Field(
171 doc=(
"Write reference matches in denormalized format? "
172 "This format uses more disk space, but is more convenient to "
173 "read. Ignored if doWriteMatches=False or doWrite=False."),
175 doAstrometry = pexConfig.Field(
178 doc=
"Perform astrometric calibration?",
180 astromRefObjLoader = pexConfig.ConfigurableField(
181 target=LoadIndexedReferenceObjectsTask,
182 doc=
"reference object loader for astrometric calibration",
184 photoRefObjLoader = pexConfig.ConfigurableField(
185 target=LoadIndexedReferenceObjectsTask,
186 doc=
"reference object loader for photometric calibration",
188 astrometry = pexConfig.ConfigurableField(
189 target=AstrometryTask,
190 doc=
"Perform astrometric calibration to refine the WCS",
192 requireAstrometry = pexConfig.Field(
195 doc=(
"Raise an exception if astrometry fails? Ignored if doAstrometry "
198 doPhotoCal = pexConfig.Field(
201 doc=
"Perform phometric calibration?",
203 requirePhotoCal = pexConfig.Field(
206 doc=(
"Raise an exception if photoCal fails? Ignored if doPhotoCal "
209 photoCal = pexConfig.ConfigurableField(
211 doc=
"Perform photometric calibration",
213 icSourceFieldsToCopy = pexConfig.ListField(
215 default=(
"calib_psf_candidate",
"calib_psf_used",
"calib_psf_reserved"),
216 doc=(
"Fields to copy from the icSource catalog to the output catalog "
217 "for matching sources Any missing fields will trigger a "
218 "RuntimeError exception. Ignored if icSourceCat is not provided.")
220 matchRadiusPix = pexConfig.Field(
223 doc=(
"Match radius for matching icSourceCat objects to sourceCat "
226 checkUnitsParseStrict = pexConfig.Field(
227 doc=(
"Strictness of Astropy unit compatibility check, can be 'raise', "
228 "'warn' or 'silent'"),
232 detection = pexConfig.ConfigurableField(
233 target=SourceDetectionTask,
236 doDeblend = pexConfig.Field(
239 doc=
"Run deblender input exposure"
241 deblend = pexConfig.ConfigurableField(
242 target=SourceDeblendTask,
243 doc=
"Split blended sources into their components"
245 doSkySources = pexConfig.Field(
248 doc=
"Generate sky sources?",
250 skySources = pexConfig.ConfigurableField(
251 target=SkyObjectsTask,
252 doc=
"Generate sky sources",
254 measurement = pexConfig.ConfigurableField(
255 target=SingleFrameMeasurementTask,
256 doc=
"Measure sources"
258 setPrimaryFlags = pexConfig.ConfigurableField(
259 target=SetPrimaryFlagsTask,
260 doc=(
"Set flags for primary source classification in single frame "
261 "processing. True if sources are not sky sources and not a parent.")
263 doApCorr = pexConfig.Field(
266 doc=
"Run subtask to apply aperture correction"
268 applyApCorr = pexConfig.ConfigurableField(
269 target=ApplyApCorrTask,
270 doc=
"Subtask to apply aperture corrections"
275 catalogCalculation = pexConfig.ConfigurableField(
276 target=CatalogCalculationTask,
277 doc=
"Subtask to run catalogCalculation plugins on catalog"
279 doInsertFakes = pexConfig.Field(
282 doc=
"Run fake sources injection task"
284 insertFakes = pexConfig.ConfigurableField(
285 target=BaseFakeSourcesTask,
286 doc=
"Injection of fake sources for testing purposes (must be "
289 doWriteExposure = pexConfig.Field(
292 doc=
"Write the calexp? If fakes have been added then we do not want to write out the calexp as a "
293 "normal calexp but as a fakes_calexp."
298 self.
detectiondetection.doTempLocalBackground =
False
299 self.
deblenddeblend.maxFootprintSize = 2000
300 self.
measurementmeasurement.plugins.names |= [
"base_LocalPhotoCalib",
"base_LocalWcs"]
304 astromRefCatGen2 = getattr(self.
astromRefObjLoaderastromRefObjLoader,
"ref_dataset_name",
None)
305 if astromRefCatGen2
is not None and astromRefCatGen2 != self.connections.astromRefCat:
307 f
"Gen2 ({astromRefCatGen2}) and Gen3 ({self.connections.astromRefCat}) astrometry reference "
308 f
"catalogs are different. These options must be kept in sync until Gen2 is retired."
310 photoRefCatGen2 = getattr(self.
photoRefObjLoaderphotoRefObjLoader,
"ref_dataset_name",
None)
311 if photoRefCatGen2
is not None and photoRefCatGen2 != self.connections.photoRefCat:
313 f
"Gen2 ({photoRefCatGen2}) and Gen3 ({self.connections.photoRefCat}) photometry reference "
314 f
"catalogs are different. These options must be kept in sync until Gen2 is retired."
326 r"""!Calibrate an exposure: measure sources and perform astrometric and
327 photometric calibration
329 @anchor CalibrateTask_
331 @section pipe_tasks_calibrate_Contents Contents
333 - @ref pipe_tasks_calibrate_Purpose
334 - @ref pipe_tasks_calibrate_Initialize
335 - @ref pipe_tasks_calibrate_IO
336 - @ref pipe_tasks_calibrate_Config
337 - @ref pipe_tasks_calibrate_Metadata
338 - @ref pipe_tasks_calibrate_Debug
341 @section pipe_tasks_calibrate_Purpose Description
343 Given an exposure with a good PSF model and aperture correction map
344 (e.g. as provided by @ref CharacterizeImageTask), perform the following
346 - Run detection and measurement
347 - Run astrometry subtask to fit an improved WCS
348 - Run photoCal subtask to fit the exposure's photometric zero-point
350 @section pipe_tasks_calibrate_Initialize Task initialisation
352 @copydoc \_\_init\_\_
354 @section pipe_tasks_calibrate_IO Invoking the Task
356 If you want this task to unpersist inputs or persist outputs, then call
357 the `runDataRef` method (a wrapper around the `run` method).
359 If you already have the inputs unpersisted and do not want to persist the
360 output then it is more direct to call the `run` method:
362 @section pipe_tasks_calibrate_Config Configuration parameters
364 See @ref CalibrateConfig
366 @section pipe_tasks_calibrate_Metadata Quantities set in exposure Metadata
370 <dt>MAGZERO_RMS <dd>MAGZERO's RMS == sigma reported by photoCal task
371 <dt>MAGZERO_NOBJ <dd>Number of stars used == ngood reported by photoCal
373 <dt>COLORTERM1 <dd>?? (always 0.0)
374 <dt>COLORTERM2 <dd>?? (always 0.0)
375 <dt>COLORTERM3 <dd>?? (always 0.0)
378 @section pipe_tasks_calibrate_Debug Debug variables
380 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink
381 interface supports a flag
382 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug
383 for more about `debug.py`.
385 CalibrateTask has a debug dictionary containing one key:
388 <dd>frame (an int; <= 0 to not display) in which to display the exposure,
389 sources and matches. See @ref lsst.meas.astrom.displayAstrometry for
390 the meaning of the various symbols.
393 For example, put something like:
397 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would
398 # call us recursively
399 if name == "lsst.pipe.tasks.calibrate":
406 lsstDebug.Info = DebugInfo
408 into your `debug.py` file and run `calibrateTask.py` with the `--debug`
411 Some subtasks may have their own debug variables; see individual Task
418 ConfigClass = CalibrateConfig
419 _DefaultName =
"calibrate"
420 RunnerClass = pipeBase.ButlerInitializedTaskRunner
422 def __init__(self, butler=None, astromRefObjLoader=None,
423 photoRefObjLoader=None, icSourceSchema=None,
424 initInputs=None, **kwargs):
425 """!Construct a CalibrateTask
427 @param[in] butler The butler is passed to the refObjLoader constructor
428 in case it is needed. Ignored if the refObjLoader argument
429 provides a loader directly.
430 @param[in] astromRefObjLoader An instance of LoadReferenceObjectsTasks
431 that supplies an external reference catalog for astrometric
432 calibration. May be None if the desired loader can be constructed
433 from the butler argument or all steps requiring a reference catalog
435 @param[in] photoRefObjLoader An instance of LoadReferenceObjectsTasks
436 that supplies an external reference catalog for photometric
437 calibration. May be None if the desired loader can be constructed
438 from the butler argument or all steps requiring a reference catalog
440 @param[in] icSourceSchema schema for icSource catalog, or None.
441 Schema values specified in config.icSourceFieldsToCopy will be
442 taken from this schema. If set to None, no values will be
443 propagated from the icSourceCatalog
444 @param[in,out] kwargs other keyword arguments for
445 lsst.pipe.base.CmdLineTask
449 if icSourceSchema
is None and butler
is not None:
451 icSourceSchema = butler.get(
"icSrc_schema", immediate=
True).schema
453 if icSourceSchema
is None and butler
is None and initInputs
is not None:
454 icSourceSchema = initInputs[
'icSourceSchema'].schema
456 if icSourceSchema
is not None:
459 minimumSchema = afwTable.SourceTable.makeMinimalSchema()
460 self.
schemaMapperschemaMapper.addMinimalSchema(minimumSchema,
False)
468 afwTable.Field[
"Flag"](
"calib_detected",
469 "Source was detected as an icSource"))
470 missingFieldNames = []
471 for fieldName
in self.config.icSourceFieldsToCopy:
473 schemaItem = icSourceSchema.find(fieldName)
475 missingFieldNames.append(fieldName)
478 self.
schemaMapperschemaMapper.addMapping(schemaItem.getKey())
480 if missingFieldNames:
481 raise RuntimeError(
"isSourceCat is missing fields {} "
482 "specified in icSourceFieldsToCopy"
483 .format(missingFieldNames))
490 self.
schemaschema = afwTable.SourceTable.makeMinimalSchema()
491 self.makeSubtask(
'detection', schema=self.
schemaschema)
498 if self.config.doInsertFakes:
499 self.makeSubtask(
"insertFakes")
501 if self.config.doDeblend:
502 self.makeSubtask(
"deblend", schema=self.
schemaschema)
503 if self.config.doSkySources:
504 self.makeSubtask(
"skySources")
505 self.
skySourceKeyskySourceKey = self.
schemaschema.addField(
"sky_source", type=
"Flag", doc=
"Sky objects.")
506 self.makeSubtask(
'measurement', schema=self.
schemaschema,
508 self.makeSubtask(
"setPrimaryFlags", schema=self.
schemaschema, isSingleFrame=
True)
509 if self.config.doApCorr:
510 self.makeSubtask(
'applyApCorr', schema=self.
schemaschema)
511 self.makeSubtask(
'catalogCalculation', schema=self.
schemaschema)
513 if self.config.doAstrometry:
514 if astromRefObjLoader
is None and butler
is not None:
515 self.makeSubtask(
'astromRefObjLoader', butler=butler)
516 astromRefObjLoader = self.astromRefObjLoader
517 self.
pixelMarginpixelMargin = astromRefObjLoader.config.pixelMargin
518 self.makeSubtask(
"astrometry", refObjLoader=astromRefObjLoader,
520 if self.config.doPhotoCal:
521 if photoRefObjLoader
is None and butler
is not None:
522 self.makeSubtask(
'photoRefObjLoader', butler=butler)
523 photoRefObjLoader = self.photoRefObjLoader
524 self.
pixelMarginpixelMargin = photoRefObjLoader.config.pixelMargin
525 self.makeSubtask(
"photoCal", refObjLoader=photoRefObjLoader,
528 if initInputs
is not None and (astromRefObjLoader
is not None or photoRefObjLoader
is not None):
529 raise RuntimeError(
"PipelineTask form of this task should not be initialized with "
530 "reference object loaders.")
535 self.
schemaschema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
537 sourceCatSchema = afwTable.SourceCatalog(self.
schemaschema)
542 def runDataRef(self, dataRef, exposure=None, background=None, icSourceCat=None,
544 """!Calibrate an exposure, optionally unpersisting inputs and
547 This is a wrapper around the `run` method that unpersists inputs
548 (if `doUnpersist` true) and persists outputs (if `config.doWrite` true)
550 @param[in] dataRef butler data reference corresponding to a science
552 @param[in,out] exposure characterized exposure (an
553 lsst.afw.image.ExposureF or similar), or None to unpersist existing
554 icExp and icBackground. See `run` method for details of what is
556 @param[in,out] background initial model of background already
557 subtracted from exposure (an lsst.afw.math.BackgroundList). May be
558 None if no background has been subtracted, though that is unusual
559 for calibration. A refined background model is output. Ignored if
561 @param[in] icSourceCat catalog from which to copy the fields specified
562 by icSourceKeys, or None;
563 @param[in] doUnpersist unpersist data:
564 - if True, exposure, background and icSourceCat are read from
565 dataRef and those three arguments must all be None;
566 - if False the exposure must be provided; background and
567 icSourceCat are optional. True is intended for running as a
568 command-line task, False for running as a subtask
569 @return same data as the calibrate method
571 self.log.info(
"Processing %s" % (dataRef.dataId))
574 if any(item
is not None for item
in (exposure, background,
576 raise RuntimeError(
"doUnpersist true; exposure, background "
577 "and icSourceCat must all be None")
578 exposure = dataRef.get(
"icExp", immediate=
True)
579 background = dataRef.get(
"icExpBackground", immediate=
True)
580 icSourceCat = dataRef.get(
"icSrc", immediate=
True)
581 elif exposure
is None:
582 raise RuntimeError(
"doUnpersist false; exposure must be provided")
584 exposureIdInfo = dataRef.get(
"expIdInfo")
586 calRes = self.
runrun(
588 exposureIdInfo=exposureIdInfo,
589 background=background,
590 icSourceCat=icSourceCat,
593 if self.config.doWrite:
596 exposure=calRes.exposure,
597 background=calRes.background,
598 sourceCat=calRes.sourceCat,
599 astromMatches=calRes.astromMatches,
600 matchMeta=calRes.matchMeta,
606 inputs = butlerQC.get(inputRefs)
607 inputs[
'exposureIdInfo'] = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId,
"visit_detector")
609 if self.config.doAstrometry:
610 refObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
611 for ref
in inputRefs.astromRefCat],
612 refCats=inputs.pop(
'astromRefCat'),
613 config=self.config.astromRefObjLoader, log=self.log)
614 self.
pixelMarginpixelMargin = refObjLoader.config.pixelMargin
615 self.astrometry.setRefObjLoader(refObjLoader)
617 if self.config.doPhotoCal:
618 photoRefObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
619 for ref
in inputRefs.photoRefCat],
620 refCats=inputs.pop(
'photoRefCat'),
621 config=self.config.photoRefObjLoader,
623 self.
pixelMarginpixelMargin = photoRefObjLoader.config.pixelMargin
624 self.photoCal.match.setRefObjLoader(photoRefObjLoader)
626 outputs = self.
runrun(**inputs)
628 if self.config.doWriteMatches
and self.config.doAstrometry:
629 normalizedMatches = afwTable.packMatches(outputs.astromMatches)
630 normalizedMatches.table.setMetadata(outputs.matchMeta)
631 if self.config.doWriteMatchesDenormalized:
632 denormMatches = denormalizeMatches(outputs.astromMatches, outputs.matchMeta)
633 outputs.matchesDenormalized = denormMatches
634 outputs.matches = normalizedMatches
635 butlerQC.put(outputs, outputRefs)
638 def run(self, exposure, exposureIdInfo=None, background=None,
640 """!Calibrate an exposure (science image or coadd)
642 @param[in,out] exposure exposure to calibrate (an
643 lsst.afw.image.ExposureF or similar);
648 - MaskedImage has background subtracted
650 - PhotoCalib is replaced
651 @param[in] exposureIdInfo ID info for exposure (an
652 lsst.obs.base.ExposureIdInfo) If not provided, returned
653 SourceCatalog IDs will not be globally unique.
654 @param[in,out] background background model already subtracted from
655 exposure (an lsst.afw.math.BackgroundList). May be None if no
656 background has been subtracted, though that is unusual for
657 calibration. A refined background model is output.
658 @param[in] icSourceCat A SourceCatalog from CharacterizeImageTask
659 from which we can copy some fields.
661 @return pipe_base Struct containing these fields:
662 - exposure calibrate science exposure with refined WCS and PhotoCalib
663 - background model of background subtracted from exposure (an
664 lsst.afw.math.BackgroundList)
665 - sourceCat catalog of measured sources
666 - astromMatches list of source/refObj matches from the astrometry
670 if exposureIdInfo
is None:
671 exposureIdInfo = ExposureIdInfo()
673 if background
is None:
674 background = BackgroundList()
675 sourceIdFactory = exposureIdInfo.makeSourceIdFactory()
676 table = SourceTable.make(self.
schemaschema, sourceIdFactory)
679 detRes = self.detection.
run(table=table, exposure=exposure,
681 sourceCat = detRes.sources
682 if detRes.fpSets.background:
683 for bg
in detRes.fpSets.background:
684 background.append(bg)
685 if self.config.doSkySources:
686 skySourceFootprints = self.skySources.
run(mask=exposure.mask, seed=exposureIdInfo.expId)
687 if skySourceFootprints:
688 for foot
in skySourceFootprints:
689 s = sourceCat.addNew()
692 if self.config.doDeblend:
693 self.deblend.
run(exposure=exposure, sources=sourceCat)
694 self.measurement.
run(
697 exposureId=exposureIdInfo.expId
699 if self.config.doApCorr:
700 self.applyApCorr.
run(
702 apCorrMap=exposure.getInfo().getApCorrMap()
704 self.catalogCalculation.
run(sourceCat)
706 self.setPrimaryFlags.
run(sourceCat)
708 if icSourceCat
is not None and \
709 len(self.config.icSourceFieldsToCopy) > 0:
717 if not sourceCat.isContiguous():
718 sourceCat = sourceCat.copy(deep=
True)
724 if self.config.doAstrometry:
726 astromRes = self.astrometry.
run(
730 astromMatches = astromRes.matches
731 matchMeta = astromRes.matchMeta
732 except Exception
as e:
733 if self.config.requireAstrometry:
735 self.log.warn(
"Unable to perform astrometric calibration "
736 "(%s): attempting to proceed" % e)
739 if self.config.doPhotoCal:
741 photoRes = self.photoCal.
run(exposure, sourceCat=sourceCat, expId=exposureIdInfo.expId)
742 exposure.setPhotoCalib(photoRes.photoCalib)
744 self.log.info(
"Photometric zero-point: %f" %
745 photoRes.photoCalib.instFluxToMagnitude(1.0))
746 self.
setMetadatasetMetadata(exposure=exposure, photoRes=photoRes)
747 except Exception
as e:
748 if self.config.requirePhotoCal:
750 self.log.warn(
"Unable to perform photometric calibration "
751 "(%s): attempting to proceed" % e)
752 self.
setMetadatasetMetadata(exposure=exposure, photoRes=
None)
754 if self.config.doInsertFakes:
755 self.insertFakes.
run(exposure, background=background)
757 table = SourceTable.make(self.
schemaschema, sourceIdFactory)
760 detRes = self.detection.
run(table=table, exposure=exposure,
762 sourceCat = detRes.sources
763 if detRes.fpSets.background:
764 for bg
in detRes.fpSets.background:
765 background.append(bg)
766 if self.config.doDeblend:
767 self.deblend.
run(exposure=exposure, sources=sourceCat)
768 self.measurement.
run(
771 exposureId=exposureIdInfo.expId
773 if self.config.doApCorr:
774 self.applyApCorr.
run(
776 apCorrMap=exposure.getInfo().getApCorrMap()
778 self.catalogCalculation.
run(sourceCat)
780 if icSourceCat
is not None and len(self.config.icSourceFieldsToCopy) > 0:
784 frame = getDebugFrame(self._display,
"calibrate")
789 matches=astromMatches,
794 return pipeBase.Struct(
796 background=background,
798 astromMatches=astromMatches,
802 outputExposure=exposure,
804 outputBackground=background,
808 astromMatches, matchMeta):
809 """Write output data to the output repository
811 @param[in] dataRef butler data reference corresponding to a science
813 @param[in] exposure exposure to write
814 @param[in] background background model for exposure
815 @param[in] sourceCat catalog of measured sources
816 @param[in] astromMatches list of source/refObj matches from the
819 dataRef.put(sourceCat,
"src")
820 if self.config.doWriteMatches
and astromMatches
is not None:
821 normalizedMatches = afwTable.packMatches(astromMatches)
822 normalizedMatches.table.setMetadata(matchMeta)
823 dataRef.put(normalizedMatches,
"srcMatch")
824 if self.config.doWriteMatchesDenormalized:
825 denormMatches = denormalizeMatches(astromMatches, matchMeta)
826 dataRef.put(denormMatches,
"srcMatchFull")
827 if self.config.doWriteExposure:
828 dataRef.put(exposure,
"calexp")
829 dataRef.put(background,
"calexpBackground")
832 """Return a dict of empty catalogs for each catalog dataset produced
835 sourceCat = afwTable.SourceCatalog(self.
schemaschema)
837 return {
"src": sourceCat}
840 """!Set task and exposure metadata
842 Logs a warning and continues if needed data is missing.
844 @param[in,out] exposure exposure whose metadata is to be set
845 @param[in] photoRes results of running photoCal; if None then it was
851 metadata = exposure.getMetadata()
855 exposureTime = exposure.getInfo().getVisitInfo().getExposureTime()
856 magZero = photoRes.zp - 2.5*math.log10(exposureTime)
858 self.log.warn(
"Could not set normalized MAGZERO in header: no "
863 metadata.set(
'MAGZERO', magZero)
864 metadata.set(
'MAGZERO_RMS', photoRes.sigma)
865 metadata.set(
'MAGZERO_NOBJ', photoRes.ngood)
866 metadata.set(
'COLORTERM1', 0.0)
867 metadata.set(
'COLORTERM2', 0.0)
868 metadata.set(
'COLORTERM3', 0.0)
869 except Exception
as e:
870 self.log.warn(
"Could not set exposure metadata: %s" % (e,))
873 """!Match sources in icSourceCat and sourceCat and copy the specified fields
875 @param[in] icSourceCat catalog from which to copy fields
876 @param[in,out] sourceCat catalog to which to copy fields
878 The fields copied are those specified by `config.icSourceFieldsToCopy`
879 that actually exist in the schema. This was set up by the constructor
880 using self.schemaMapper.
883 raise RuntimeError(
"To copy icSource fields you must specify "
884 "icSourceSchema nd icSourceKeys when "
885 "constructing this task")
886 if icSourceCat
is None or sourceCat
is None:
887 raise RuntimeError(
"icSourceCat and sourceCat must both be "
889 if len(self.config.icSourceFieldsToCopy) == 0:
890 self.log.warn(
"copyIcSourceFields doing nothing because "
891 "icSourceFieldsToCopy is empty")
894 mc = afwTable.MatchControl()
895 mc.findOnlyClosest =
False
896 matches = afwTable.matchXy(icSourceCat, sourceCat,
897 self.config.matchRadiusPix, mc)
898 if self.config.doDeblend:
899 deblendKey = sourceCat.schema[
"deblend_nChild"].asKey()
901 matches = [m
for m
in matches
if m[1].get(deblendKey) == 0]
908 for m0, m1, d
in matches:
910 match = bestMatches.get(id0)
911 if match
is None or d <= match[2]:
912 bestMatches[id0] = (m0, m1, d)
913 matches = list(bestMatches.values())
918 numMatches = len(matches)
919 numUniqueSources = len(set(m[1].getId()
for m
in matches))
920 if numUniqueSources != numMatches:
921 self.log.warn(
"{} icSourceCat sources matched only {} sourceCat "
922 "sources".format(numMatches, numUniqueSources))
924 self.log.info(
"Copying flags from icSourceCat to sourceCat for "
925 "%s sources" % (numMatches,))
929 for icSrc, src, d
in matches:
935 icSrcFootprint = icSrc.getFootprint()
937 icSrc.setFootprint(src.getFootprint())
940 icSrc.setFootprint(icSrcFootprint)
def __init__(self, *config=None)
Calibrate an exposure: measure sources and perform astrometric and photometric calibration.
def writeOutputs(self, dataRef, exposure, background, sourceCat, astromMatches, matchMeta)
def runDataRef(self, dataRef, exposure=None, background=None, icSourceCat=None, doUnpersist=True)
Calibrate an exposure, optionally unpersisting inputs and persisting outputs.
def __init__(self, butler=None, astromRefObjLoader=None, photoRefObjLoader=None, icSourceSchema=None, initInputs=None, **kwargs)
Construct a CalibrateTask.
def setMetadata(self, exposure, photoRes=None)
Set task and exposure metadata.
def runQuantum(self, butlerQC, inputRefs, outputRefs)
def getSchemaCatalogs(self)
def copyIcSourceFields(self, icSourceCat, sourceCat)
Match sources in icSourceCat and sourceCat and copy the specified fields.
def run(self, exposure, exposureIdInfo=None, background=None, icSourceCat=None)
Calibrate an exposure (science image or coadd)