24from lsstDebug
import getDebugFrame
27import lsst.pipe.base.connectionTypes
as cT
29from lsst.meas.astrom import AstrometryTask, displayAstrometry, denormalizeMatches
31from lsst.obs.base
import ExposureIdInfo
38 CatalogCalculationTask)
40from lsst.utils.timer
import timeMethod
42from .fakes
import BaseFakeSourcesTask
43from .photoCal
import PhotoCalTask
44from .computeExposureSummaryStats
import ComputeExposureSummaryStatsTask
47__all__ = [
"CalibrateConfig",
"CalibrateTask"]
50class CalibrateConnections(pipeBase.PipelineTaskConnections, dimensions=(
"instrument",
"visit",
"detector"),
53 icSourceSchema = cT.InitInput(
54 doc=
"Schema produced by characterize image task, used to initialize this task",
56 storageClass=
"SourceCatalog",
59 outputSchema = cT.InitOutput(
60 doc=
"Schema after CalibrateTask has been initialized",
62 storageClass=
"SourceCatalog",
66 doc=
"Input image to calibrate",
68 storageClass=
"ExposureF",
69 dimensions=(
"instrument",
"visit",
"detector"),
72 background = cT.Input(
73 doc=
"Backgrounds determined by characterize task",
74 name=
"icExpBackground",
75 storageClass=
"Background",
76 dimensions=(
"instrument",
"visit",
"detector"),
79 icSourceCat = cT.Input(
80 doc=
"Source catalog created by characterize task",
82 storageClass=
"SourceCatalog",
83 dimensions=(
"instrument",
"visit",
"detector"),
86 astromRefCat = cT.PrerequisiteInput(
87 doc=
"Reference catalog to use for astrometry",
89 storageClass=
"SimpleCatalog",
90 dimensions=(
"skypix",),
95 photoRefCat = cT.PrerequisiteInput(
96 doc=
"Reference catalog to use for photometric calibration",
98 storageClass=
"SimpleCatalog",
99 dimensions=(
"skypix",),
104 outputExposure = cT.Output(
105 doc=
"Exposure after running calibration task",
107 storageClass=
"ExposureF",
108 dimensions=(
"instrument",
"visit",
"detector"),
111 outputCat = cT.Output(
112 doc=
"Source catalog produced in calibrate task",
114 storageClass=
"SourceCatalog",
115 dimensions=(
"instrument",
"visit",
"detector"),
118 outputBackground = cT.Output(
119 doc=
"Background models estimated in calibration task",
120 name=
"calexpBackground",
121 storageClass=
"Background",
122 dimensions=(
"instrument",
"visit",
"detector"),
126 doc=
"Source/refObj matches from the astrometry solver",
128 storageClass=
"Catalog",
129 dimensions=(
"instrument",
"visit",
"detector"),
132 matchesDenormalized = cT.Output(
133 doc=
"Denormalized matches from astrometry solver",
135 storageClass=
"Catalog",
136 dimensions=(
"instrument",
"visit",
"detector"),
142 if config.doAstrometry
is False:
143 self.prerequisiteInputs.remove(
"astromRefCat")
144 if config.doPhotoCal
is False:
145 self.prerequisiteInputs.remove(
"photoRefCat")
147 if config.doWriteMatches
is False or config.doAstrometry
is False:
148 self.outputs.remove(
"matches")
149 if config.doWriteMatchesDenormalized
is False or config.doAstrometry
is False:
150 self.outputs.remove(
"matchesDenormalized")
153class CalibrateConfig(pipeBase.PipelineTaskConfig, pipelineConnections=CalibrateConnections):
154 """Config for CalibrateTask"""
155 doWrite = pexConfig.Field(
158 doc=
"Save calibration results?",
160 doWriteHeavyFootprintsInSources = pexConfig.Field(
163 doc=
"Include HeavyFootprint data in source table? If false then heavy "
164 "footprints are saved as normal footprints, which saves some space"
166 doWriteMatches = pexConfig.Field(
169 doc=
"Write reference matches (ignored if doWrite or doAstrometry false)?",
171 doWriteMatchesDenormalized = pexConfig.Field(
174 doc=(
"Write reference matches in denormalized format? "
175 "This format uses more disk space, but is more convenient to "
176 "read. Ignored if doWriteMatches=False or doWrite=False."),
178 doAstrometry = pexConfig.Field(
181 doc=
"Perform astrometric calibration?",
183 astromRefObjLoader = pexConfig.ConfigurableField(
184 target=LoadIndexedReferenceObjectsTask,
185 doc=
"reference object loader for astrometric calibration",
187 photoRefObjLoader = pexConfig.ConfigurableField(
188 target=LoadIndexedReferenceObjectsTask,
189 doc=
"reference object loader for photometric calibration",
191 astrometry = pexConfig.ConfigurableField(
192 target=AstrometryTask,
193 doc=
"Perform astrometric calibration to refine the WCS",
195 requireAstrometry = pexConfig.Field(
198 doc=(
"Raise an exception if astrometry fails? Ignored if doAstrometry "
201 doPhotoCal = pexConfig.Field(
204 doc=
"Perform phometric calibration?",
206 requirePhotoCal = pexConfig.Field(
209 doc=(
"Raise an exception if photoCal fails? Ignored if doPhotoCal "
212 photoCal = pexConfig.ConfigurableField(
214 doc=
"Perform photometric calibration",
216 icSourceFieldsToCopy = pexConfig.ListField(
218 default=(
"calib_psf_candidate",
"calib_psf_used",
"calib_psf_reserved"),
219 doc=(
"Fields to copy from the icSource catalog to the output catalog "
220 "for matching sources Any missing fields will trigger a "
221 "RuntimeError exception. Ignored if icSourceCat is not provided.")
223 matchRadiusPix = pexConfig.Field(
226 doc=(
"Match radius for matching icSourceCat objects to sourceCat "
229 checkUnitsParseStrict = pexConfig.Field(
230 doc=(
"Strictness of Astropy unit compatibility check, can be 'raise', "
231 "'warn' or 'silent'"),
235 detection = pexConfig.ConfigurableField(
236 target=SourceDetectionTask,
239 doDeblend = pexConfig.Field(
242 doc=
"Run deblender input exposure"
244 deblend = pexConfig.ConfigurableField(
245 target=SourceDeblendTask,
246 doc=
"Split blended sources into their components"
248 doSkySources = pexConfig.Field(
251 doc=
"Generate sky sources?",
253 skySources = pexConfig.ConfigurableField(
254 target=SkyObjectsTask,
255 doc=
"Generate sky sources",
257 measurement = pexConfig.ConfigurableField(
258 target=SingleFrameMeasurementTask,
259 doc=
"Measure sources"
261 postCalibrationMeasurement = pexConfig.ConfigurableField(
262 target=SingleFrameMeasurementTask,
263 doc=
"Second round of measurement for plugins that need to be run after photocal"
265 setPrimaryFlags = pexConfig.ConfigurableField(
266 target=SetPrimaryFlagsTask,
267 doc=(
"Set flags for primary source classification in single frame "
268 "processing. True if sources are not sky sources and not a parent.")
270 doApCorr = pexConfig.Field(
273 doc=
"Run subtask to apply aperture correction"
275 applyApCorr = pexConfig.ConfigurableField(
276 target=ApplyApCorrTask,
277 doc=
"Subtask to apply aperture corrections"
282 catalogCalculation = pexConfig.ConfigurableField(
283 target=CatalogCalculationTask,
284 doc=
"Subtask to run catalogCalculation plugins on catalog"
286 doInsertFakes = pexConfig.Field(
289 doc=
"Run fake sources injection task"
291 insertFakes = pexConfig.ConfigurableField(
292 target=BaseFakeSourcesTask,
293 doc=
"Injection of fake sources for testing purposes (must be "
296 doComputeSummaryStats = pexConfig.Field(
299 doc=
"Run subtask to measure exposure summary statistics?"
301 computeSummaryStats = pexConfig.ConfigurableField(
302 target=ComputeExposureSummaryStatsTask,
303 doc=
"Subtask to run computeSummaryStats on exposure"
305 doWriteExposure = pexConfig.Field(
308 doc=
"Write the calexp? If fakes have been added then we do not want to write out the calexp as a "
309 "normal calexp but as a fakes_calexp."
314 self.
detectiondetection.doTempLocalBackground =
False
315 self.
deblenddeblend.maxFootprintSize = 2000
323 astromRefCatGen2 = getattr(self.
astromRefObjLoaderastromRefObjLoader,
"ref_dataset_name",
None)
324 if astromRefCatGen2
is not None and astromRefCatGen2 != self.connections.astromRefCat:
326 f
"Gen2 ({astromRefCatGen2}) and Gen3 ({self.connections.astromRefCat}) astrometry reference "
327 f
"catalogs are different. These options must be kept in sync until Gen2 is retired."
329 photoRefCatGen2 = getattr(self.
photoRefObjLoaderphotoRefObjLoader,
"ref_dataset_name",
None)
330 if photoRefCatGen2
is not None and photoRefCatGen2 != self.connections.photoRefCat:
332 f
"Gen2 ({photoRefCatGen2}) and Gen3 ({self.connections.photoRefCat}) photometry reference "
333 f
"catalogs are different. These options must be kept in sync until Gen2 is retired."
345 r"""!Calibrate an exposure: measure sources and perform astrometric and
346 photometric calibration
348 @anchor CalibrateTask_
350 @section pipe_tasks_calibrate_Contents Contents
352 -
@ref pipe_tasks_calibrate_Purpose
353 -
@ref pipe_tasks_calibrate_Initialize
354 -
@ref pipe_tasks_calibrate_IO
355 -
@ref pipe_tasks_calibrate_Config
356 -
@ref pipe_tasks_calibrate_Metadata
357 -
@ref pipe_tasks_calibrate_Debug
360 @section pipe_tasks_calibrate_Purpose Description
362 Given an exposure
with a good PSF model
and aperture correction map
363 (e.g.
as provided by
@ref CharacterizeImageTask), perform the following
365 - Run detection
and measurement
366 - Run astrometry subtask to fit an improved WCS
367 - Run photoCal subtask to fit the exposure
's photometric zero-point
369 @section pipe_tasks_calibrate_Initialize Task initialisation
371 @copydoc \_\_init\_\_
373 @section pipe_tasks_calibrate_IO Invoking the Task
375 If you want this task to unpersist inputs
or persist outputs, then call
376 the `runDataRef` method (a wrapper around the `run` method).
378 If you already have the inputs unpersisted
and do
not want to persist the
379 output then it
is more direct to call the `run` method:
381 @section pipe_tasks_calibrate_Config Configuration parameters
383 See
@ref CalibrateConfig
385 @section pipe_tasks_calibrate_Metadata Quantities set
in exposure Metadata
389 <dt>MAGZERO_RMS <dd>MAGZERO
's RMS == sigma reported by photoCal task
390 <dt>MAGZERO_NOBJ <dd>Number of stars used == ngood reported by photoCal
392 <dt>COLORTERM1 <dd>?? (always 0.0)
393 <dt>COLORTERM2 <dd>?? (always 0.0)
394 <dt>COLORTERM3 <dd>?? (always 0.0)
397 @section pipe_tasks_calibrate_Debug Debug variables
399 The
@link lsst.pipe.base.cmdLineTask.CmdLineTask command line task
@endlink
400 interface supports a flag
401 `--debug` to
import `debug.py`
from your `$PYTHONPATH`; see
@ref baseDebug
402 for more about `debug.py`.
404 CalibrateTask has a debug dictionary containing one key:
407 <dd>frame (an int; <= 0 to
not display)
in which to display the exposure,
408 sources
and matches. See
@ref lsst.meas.astrom.displayAstrometry
for
409 the meaning of the various symbols.
412 For example, put something like:
418 if name ==
"lsst.pipe.tasks.calibrate":
427 into your `debug.py` file
and run `calibrateTask.py`
with the `--debug`
430 Some subtasks may have their own debug variables; see individual Task
437 ConfigClass = CalibrateConfig
438 _DefaultName =
"calibrate"
439 RunnerClass = pipeBase.ButlerInitializedTaskRunner
441 def __init__(self, butler=None, astromRefObjLoader=None,
442 photoRefObjLoader=None, icSourceSchema=None,
443 initInputs=None, **kwargs):
444 """!Construct a CalibrateTask
446 @param[
in] butler The butler
is passed to the refObjLoader constructor
447 in case it
is needed. Ignored
if the refObjLoader argument
448 provides a loader directly.
449 @param[
in] astromRefObjLoader An instance of LoadReferenceObjectsTasks
450 that supplies an external reference catalog
for astrometric
451 calibration. May be
None if the desired loader can be constructed
452 from the butler argument
or all steps requiring a reference catalog
454 @param[
in] photoRefObjLoader An instance of LoadReferenceObjectsTasks
455 that supplies an external reference catalog
for photometric
456 calibration. May be
None if the desired loader can be constructed
457 from the butler argument
or all steps requiring a reference catalog
459 @param[
in] icSourceSchema schema
for icSource catalog,
or None.
460 Schema values specified
in config.icSourceFieldsToCopy will be
461 taken
from this schema. If set to
None, no values will be
462 propagated
from the icSourceCatalog
463 @param[
in,out] kwargs other keyword arguments
for
464 lsst.pipe.base.CmdLineTask
468 if icSourceSchema
is None and butler
is not None:
470 icSourceSchema = butler.get(
"icSrc_schema", immediate=
True).schema
472 if icSourceSchema
is None and butler
is None and initInputs
is not None:
473 icSourceSchema = initInputs[
'icSourceSchema'].schema
475 if icSourceSchema
is not None:
478 minimumSchema = afwTable.SourceTable.makeMinimalSchema()
479 self.
schemaMapperschemaMapper.addMinimalSchema(minimumSchema,
False)
487 afwTable.Field[
"Flag"](
"calib_detected",
488 "Source was detected as an icSource"))
489 missingFieldNames = []
490 for fieldName
in self.config.icSourceFieldsToCopy:
492 schemaItem = icSourceSchema.find(fieldName)
494 missingFieldNames.append(fieldName)
497 self.
schemaMapperschemaMapper.addMapping(schemaItem.getKey())
499 if missingFieldNames:
500 raise RuntimeError(
"isSourceCat is missing fields {} "
501 "specified in icSourceFieldsToCopy"
502 .format(missingFieldNames))
509 self.
schemaschema = afwTable.SourceTable.makeMinimalSchema()
510 self.makeSubtask(
'detection', schema=self.
schemaschema)
517 if self.config.doInsertFakes:
518 self.makeSubtask(
"insertFakes")
520 if self.config.doDeblend:
521 self.makeSubtask(
"deblend", schema=self.
schemaschema)
522 if self.config.doSkySources:
523 self.makeSubtask(
"skySources")
524 self.
skySourceKeyskySourceKey = self.
schemaschema.addField(
"sky_source", type=
"Flag", doc=
"Sky objects.")
525 self.makeSubtask(
'measurement', schema=self.
schemaschema,
527 self.makeSubtask(
'postCalibrationMeasurement', schema=self.
schemaschema,
529 self.makeSubtask(
"setPrimaryFlags", schema=self.
schemaschema, isSingleFrame=
True)
530 if self.config.doApCorr:
531 self.makeSubtask(
'applyApCorr', schema=self.
schemaschema)
532 self.makeSubtask(
'catalogCalculation', schema=self.
schemaschema)
534 if self.config.doAstrometry:
535 if astromRefObjLoader
is None and butler
is not None:
536 self.makeSubtask(
'astromRefObjLoader', butler=butler)
537 astromRefObjLoader = self.astromRefObjLoader
538 self.makeSubtask(
"astrometry", refObjLoader=astromRefObjLoader,
540 if self.config.doPhotoCal:
541 if photoRefObjLoader
is None and butler
is not None:
542 self.makeSubtask(
'photoRefObjLoader', butler=butler)
543 photoRefObjLoader = self.photoRefObjLoader
544 self.makeSubtask(
"photoCal", refObjLoader=photoRefObjLoader,
546 if self.config.doComputeSummaryStats:
547 self.makeSubtask(
'computeSummaryStats')
549 if initInputs
is not None and (astromRefObjLoader
is not None or photoRefObjLoader
is not None):
550 raise RuntimeError(
"PipelineTask form of this task should not be initialized with "
551 "reference object loaders.")
556 self.
schemaschema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
558 sourceCatSchema = afwTable.SourceCatalog(self.
schemaschema)
563 def runDataRef(self, dataRef, exposure=None, background=None, icSourceCat=None,
565 """!Calibrate an exposure, optionally unpersisting inputs and
568 This is a wrapper around the `run` method that unpersists inputs
569 (
if `doUnpersist` true)
and persists outputs (
if `config.doWrite` true)
571 @param[
in] dataRef butler data reference corresponding to a science
573 @param[
in,out] exposure characterized exposure (an
574 lsst.afw.image.ExposureF
or similar),
or None to unpersist existing
575 icExp
and icBackground. See `run` method
for details of what
is
577 @param[
in,out] background initial model of background already
578 subtracted
from exposure (an lsst.afw.math.BackgroundList). May be
579 None if no background has been subtracted, though that
is unusual
580 for calibration. A refined background model
is output. Ignored
if
582 @param[
in] icSourceCat catalog
from which to copy the fields specified
583 by icSourceKeys,
or None;
584 @param[
in] doUnpersist unpersist data:
585 -
if True, exposure, background
and icSourceCat are read
from
586 dataRef
and those three arguments must all be
None;
587 -
if False the exposure must be provided; background
and
588 icSourceCat are optional.
True is intended
for running
as a
589 command-line task,
False for running
as a subtask
590 @return same data
as the calibrate method
592 self.log.info("Processing %s", dataRef.dataId)
595 if any(item
is not None for item
in (exposure, background,
597 raise RuntimeError(
"doUnpersist true; exposure, background "
598 "and icSourceCat must all be None")
599 exposure = dataRef.get(
"icExp", immediate=
True)
600 background = dataRef.get(
"icExpBackground", immediate=
True)
601 icSourceCat = dataRef.get(
"icSrc", immediate=
True)
602 elif exposure
is None:
603 raise RuntimeError(
"doUnpersist false; exposure must be provided")
605 exposureIdInfo = dataRef.get(
"expIdInfo")
607 calRes = self.
runrun(
609 exposureIdInfo=exposureIdInfo,
610 background=background,
611 icSourceCat=icSourceCat,
614 if self.config.doWrite:
617 exposure=calRes.exposure,
618 background=calRes.background,
619 sourceCat=calRes.sourceCat,
620 astromMatches=calRes.astromMatches,
621 matchMeta=calRes.matchMeta,
627 inputs = butlerQC.get(inputRefs)
628 inputs[
'exposureIdInfo'] = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId,
"visit_detector")
630 if self.config.doAstrometry:
631 refObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
632 for ref
in inputRefs.astromRefCat],
633 refCats=inputs.pop(
'astromRefCat'),
634 config=self.config.astromRefObjLoader, log=self.log)
635 self.astrometry.setRefObjLoader(refObjLoader)
637 if self.config.doPhotoCal:
638 photoRefObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
639 for ref
in inputRefs.photoRefCat],
640 refCats=inputs.pop(
'photoRefCat'),
641 config=self.config.photoRefObjLoader,
643 self.photoCal.match.setRefObjLoader(photoRefObjLoader)
645 outputs = self.
runrun(**inputs)
647 if self.config.doWriteMatches
and self.config.doAstrometry:
648 normalizedMatches = afwTable.packMatches(outputs.astromMatches)
649 normalizedMatches.table.setMetadata(outputs.matchMeta)
650 if self.config.doWriteMatchesDenormalized:
651 denormMatches = denormalizeMatches(outputs.astromMatches, outputs.matchMeta)
652 outputs.matchesDenormalized = denormMatches
653 outputs.matches = normalizedMatches
654 butlerQC.put(outputs, outputRefs)
657 def run(self, exposure, exposureIdInfo=None, background=None,
659 """!Calibrate an exposure (science image or coadd)
661 @param[
in,out] exposure exposure to calibrate (an
662 lsst.afw.image.ExposureF
or similar);
667 - MaskedImage has background subtracted
669 - PhotoCalib
is replaced
670 @param[
in] exposureIdInfo ID info
for exposure (an
671 lsst.obs.base.ExposureIdInfo) If
not provided, returned
672 SourceCatalog IDs will
not be globally unique.
673 @param[
in,out] background background model already subtracted
from
674 exposure (an lsst.afw.math.BackgroundList). May be
None if no
675 background has been subtracted, though that
is unusual
for
676 calibration. A refined background model
is output.
677 @param[
in] icSourceCat A SourceCatalog
from CharacterizeImageTask
678 from which we can copy some fields.
680 @return pipe_base Struct containing these fields:
681 - exposure calibrate science exposure
with refined WCS
and PhotoCalib
682 - background model of background subtracted
from exposure (an
683 lsst.afw.math.BackgroundList)
684 - sourceCat catalog of measured sources
685 - astromMatches list of source/refObj matches
from the astrometry
689 if exposureIdInfo
is None:
690 exposureIdInfo = ExposureIdInfo()
692 if background
is None:
693 background = BackgroundList()
694 sourceIdFactory = exposureIdInfo.makeSourceIdFactory()
695 table = SourceTable.make(self.
schemaschema, sourceIdFactory)
698 detRes = self.detection.
run(table=table, exposure=exposure,
700 sourceCat = detRes.sources
701 if detRes.fpSets.background:
702 for bg
in detRes.fpSets.background:
703 background.append(bg)
704 if self.config.doSkySources:
705 skySourceFootprints = self.skySources.
run(mask=exposure.mask, seed=exposureIdInfo.expId)
706 if skySourceFootprints:
707 for foot
in skySourceFootprints:
708 s = sourceCat.addNew()
711 if self.config.doDeblend:
712 self.deblend.
run(exposure=exposure, sources=sourceCat)
713 self.measurement.
run(
716 exposureId=exposureIdInfo.expId
718 if self.config.doApCorr:
719 self.applyApCorr.
run(
721 apCorrMap=exposure.getInfo().getApCorrMap()
723 self.catalogCalculation.
run(sourceCat)
725 self.setPrimaryFlags.
run(sourceCat)
727 if icSourceCat
is not None and \
728 len(self.config.icSourceFieldsToCopy) > 0:
736 if not sourceCat.isContiguous():
737 sourceCat = sourceCat.copy(deep=
True)
743 if self.config.doAstrometry:
745 astromRes = self.astrometry.
run(
749 astromMatches = astromRes.matches
750 matchMeta = astromRes.matchMeta
751 except Exception
as e:
752 if self.config.requireAstrometry:
754 self.log.warning(
"Unable to perform astrometric calibration "
755 "(%s): attempting to proceed", e)
758 if self.config.doPhotoCal:
760 photoRes = self.photoCal.
run(exposure, sourceCat=sourceCat, expId=exposureIdInfo.expId)
761 exposure.setPhotoCalib(photoRes.photoCalib)
763 self.log.info(
"Photometric zero-point: %f",
764 photoRes.photoCalib.instFluxToMagnitude(1.0))
765 self.
setMetadatasetMetadata(exposure=exposure, photoRes=photoRes)
766 except Exception
as e:
767 if self.config.requirePhotoCal:
769 self.log.warning(
"Unable to perform photometric calibration "
770 "(%s): attempting to proceed", e)
771 self.
setMetadatasetMetadata(exposure=exposure, photoRes=
None)
773 self.postCalibrationMeasurement.
run(
776 exposureId=exposureIdInfo.expId
779 if self.config.doInsertFakes:
780 self.insertFakes.
run(exposure, background=background)
782 table = SourceTable.make(self.
schemaschema, sourceIdFactory)
785 detRes = self.detection.
run(table=table, exposure=exposure,
787 sourceCat = detRes.sources
788 if detRes.fpSets.background:
789 for bg
in detRes.fpSets.background:
790 background.append(bg)
791 if self.config.doDeblend:
792 self.deblend.
run(exposure=exposure, sources=sourceCat)
793 self.measurement.
run(
796 exposureId=exposureIdInfo.expId
798 self.postCalibrationMeasurement.
run(
801 exposureId=exposureIdInfo.expId
803 if self.config.doApCorr:
804 self.applyApCorr.
run(
806 apCorrMap=exposure.getInfo().getApCorrMap()
808 self.catalogCalculation.
run(sourceCat)
810 if icSourceCat
is not None and len(self.config.icSourceFieldsToCopy) > 0:
814 if self.config.doComputeSummaryStats:
815 summary = self.computeSummaryStats.
run(exposure=exposure,
817 background=background)
818 exposure.getInfo().setSummaryStats(summary)
820 frame = getDebugFrame(self._display,
"calibrate")
825 matches=astromMatches,
830 return pipeBase.Struct(
832 background=background,
834 astromMatches=astromMatches,
838 outputExposure=exposure,
840 outputBackground=background,
844 astromMatches, matchMeta):
845 """Write output data to the output repository
847 @param[
in] dataRef butler data reference corresponding to a science
849 @param[
in] exposure exposure to write
850 @param[
in] background background model
for exposure
851 @param[
in] sourceCat catalog of measured sources
852 @param[
in] astromMatches list of source/refObj matches
from the
855 dataRef.put(sourceCat, "src")
856 if self.config.doWriteMatches
and astromMatches
is not None:
857 normalizedMatches = afwTable.packMatches(astromMatches)
858 normalizedMatches.table.setMetadata(matchMeta)
859 dataRef.put(normalizedMatches,
"srcMatch")
860 if self.config.doWriteMatchesDenormalized:
861 denormMatches = denormalizeMatches(astromMatches, matchMeta)
862 dataRef.put(denormMatches,
"srcMatchFull")
863 if self.config.doWriteExposure:
864 dataRef.put(exposure,
"calexp")
865 dataRef.put(background,
"calexpBackground")
868 """Return a dict of empty catalogs for each catalog dataset produced
871 sourceCat = afwTable.SourceCatalog(self.schemaschema)
873 return {
"src": sourceCat}
876 """!Set task and exposure metadata
878 Logs a warning and continues
if needed data
is missing.
880 @param[
in,out] exposure exposure whose metadata
is to be set
881 @param[
in] photoRes results of running photoCal;
if None then it was
887 metadata = exposure.getMetadata()
891 exposureTime = exposure.getInfo().getVisitInfo().getExposureTime()
892 magZero = photoRes.zp - 2.5*math.log10(exposureTime)
894 self.log.warning(
"Could not set normalized MAGZERO in header: no "
899 metadata.set(
'MAGZERO', magZero)
900 metadata.set(
'MAGZERO_RMS', photoRes.sigma)
901 metadata.set(
'MAGZERO_NOBJ', photoRes.ngood)
902 metadata.set(
'COLORTERM1', 0.0)
903 metadata.set(
'COLORTERM2', 0.0)
904 metadata.set(
'COLORTERM3', 0.0)
905 except Exception
as e:
906 self.log.warning(
"Could not set exposure metadata: %s", e)
909 """!Match sources in icSourceCat and sourceCat and copy the specified fields
911 @param[
in] icSourceCat catalog
from which to copy fields
912 @param[
in,out] sourceCat catalog to which to copy fields
914 The fields copied are those specified by `config.icSourceFieldsToCopy`
915 that actually exist
in the schema. This was set up by the constructor
919 raise RuntimeError(
"To copy icSource fields you must specify "
920 "icSourceSchema nd icSourceKeys when "
921 "constructing this task")
922 if icSourceCat
is None or sourceCat
is None:
923 raise RuntimeError(
"icSourceCat and sourceCat must both be "
925 if len(self.config.icSourceFieldsToCopy) == 0:
926 self.log.warning(
"copyIcSourceFields doing nothing because "
927 "icSourceFieldsToCopy is empty")
930 mc = afwTable.MatchControl()
931 mc.findOnlyClosest =
False
932 matches = afwTable.matchXy(icSourceCat, sourceCat,
933 self.config.matchRadiusPix, mc)
934 if self.config.doDeblend:
935 deblendKey = sourceCat.schema[
"deblend_nChild"].asKey()
937 matches = [m
for m
in matches
if m[1].get(deblendKey) == 0]
944 for m0, m1, d
in matches:
946 match = bestMatches.get(id0)
947 if match
is None or d <= match[2]:
948 bestMatches[id0] = (m0, m1, d)
949 matches = list(bestMatches.values())
954 numMatches = len(matches)
955 numUniqueSources = len(set(m[1].getId()
for m
in matches))
956 if numUniqueSources != numMatches:
957 self.log.warning(
"%d icSourceCat sources matched only %d sourceCat "
958 "sources", numMatches, numUniqueSources)
960 self.log.info(
"Copying flags from icSourceCat to sourceCat for "
961 "%d sources", numMatches)
965 for icSrc, src, d
in matches:
971 icSrcFootprint = icSrc.getFootprint()
973 icSrc.setFootprint(src.getFootprint())
976 icSrc.setFootprint(icSrcFootprint)
postCalibrationMeasurement
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)