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 setPrimaryFlags = pexConfig.ConfigurableField(
262 target=SetPrimaryFlagsTask,
263 doc=(
"Set flags for primary source classification in single frame "
264 "processing. True if sources are not sky sources and not a parent.")
266 doApCorr = pexConfig.Field(
269 doc=
"Run subtask to apply aperture correction"
271 applyApCorr = pexConfig.ConfigurableField(
272 target=ApplyApCorrTask,
273 doc=
"Subtask to apply aperture corrections"
278 catalogCalculation = pexConfig.ConfigurableField(
279 target=CatalogCalculationTask,
280 doc=
"Subtask to run catalogCalculation plugins on catalog"
282 doInsertFakes = pexConfig.Field(
285 doc=
"Run fake sources injection task"
287 insertFakes = pexConfig.ConfigurableField(
288 target=BaseFakeSourcesTask,
289 doc=
"Injection of fake sources for testing purposes (must be "
292 doComputeSummaryStats = pexConfig.Field(
295 doc=
"Run subtask to measure exposure summary statistics?"
297 computeSummaryStats = pexConfig.ConfigurableField(
298 target=ComputeExposureSummaryStatsTask,
299 doc=
"Subtask to run computeSummaryStats on exposure"
301 doWriteExposure = pexConfig.Field(
304 doc=
"Write the calexp? If fakes have been added then we do not want to write out the calexp as a "
305 "normal calexp but as a fakes_calexp."
310 self.
detectiondetection.doTempLocalBackground =
False
311 self.
deblenddeblend.maxFootprintSize = 2000
312 self.
measurementmeasurement.plugins.names |= [
"base_LocalPhotoCalib",
"base_LocalWcs"]
316 astromRefCatGen2 = getattr(self.
astromRefObjLoaderastromRefObjLoader,
"ref_dataset_name",
None)
317 if astromRefCatGen2
is not None and astromRefCatGen2 != self.connections.astromRefCat:
319 f
"Gen2 ({astromRefCatGen2}) and Gen3 ({self.connections.astromRefCat}) astrometry reference "
320 f
"catalogs are different. These options must be kept in sync until Gen2 is retired."
322 photoRefCatGen2 = getattr(self.
photoRefObjLoaderphotoRefObjLoader,
"ref_dataset_name",
None)
323 if photoRefCatGen2
is not None and photoRefCatGen2 != self.connections.photoRefCat:
325 f
"Gen2 ({photoRefCatGen2}) and Gen3 ({self.connections.photoRefCat}) photometry reference "
326 f
"catalogs are different. These options must be kept in sync until Gen2 is retired."
338 r"""!Calibrate an exposure: measure sources and perform astrometric and
339 photometric calibration
341 @anchor CalibrateTask_
343 @section pipe_tasks_calibrate_Contents Contents
345 -
@ref pipe_tasks_calibrate_Purpose
346 -
@ref pipe_tasks_calibrate_Initialize
347 -
@ref pipe_tasks_calibrate_IO
348 -
@ref pipe_tasks_calibrate_Config
349 -
@ref pipe_tasks_calibrate_Metadata
350 -
@ref pipe_tasks_calibrate_Debug
353 @section pipe_tasks_calibrate_Purpose Description
355 Given an exposure
with a good PSF model
and aperture correction map
356 (e.g.
as provided by
@ref CharacterizeImageTask), perform the following
358 - Run detection
and measurement
359 - Run astrometry subtask to fit an improved WCS
360 - Run photoCal subtask to fit the exposure
's photometric zero-point
362 @section pipe_tasks_calibrate_Initialize Task initialisation
364 @copydoc \_\_init\_\_
366 @section pipe_tasks_calibrate_IO Invoking the Task
368 If you want this task to unpersist inputs
or persist outputs, then call
369 the `runDataRef` method (a wrapper around the `run` method).
371 If you already have the inputs unpersisted
and do
not want to persist the
372 output then it
is more direct to call the `run` method:
374 @section pipe_tasks_calibrate_Config Configuration parameters
376 See
@ref CalibrateConfig
378 @section pipe_tasks_calibrate_Metadata Quantities set
in exposure Metadata
382 <dt>MAGZERO_RMS <dd>MAGZERO
's RMS == sigma reported by photoCal task
383 <dt>MAGZERO_NOBJ <dd>Number of stars used == ngood reported by photoCal
385 <dt>COLORTERM1 <dd>?? (always 0.0)
386 <dt>COLORTERM2 <dd>?? (always 0.0)
387 <dt>COLORTERM3 <dd>?? (always 0.0)
390 @section pipe_tasks_calibrate_Debug Debug variables
392 The
@link lsst.pipe.base.cmdLineTask.CmdLineTask command line task
@endlink
393 interface supports a flag
394 `--debug` to
import `debug.py`
from your `$PYTHONPATH`; see
@ref baseDebug
395 for more about `debug.py`.
397 CalibrateTask has a debug dictionary containing one key:
400 <dd>frame (an int; <= 0 to
not display)
in which to display the exposure,
401 sources
and matches. See
@ref lsst.meas.astrom.displayAstrometry
for
402 the meaning of the various symbols.
405 For example, put something like:
411 if name ==
"lsst.pipe.tasks.calibrate":
420 into your `debug.py` file
and run `calibrateTask.py`
with the `--debug`
423 Some subtasks may have their own debug variables; see individual Task
430 ConfigClass = CalibrateConfig
431 _DefaultName =
"calibrate"
432 RunnerClass = pipeBase.ButlerInitializedTaskRunner
434 def __init__(self, butler=None, astromRefObjLoader=None,
435 photoRefObjLoader=None, icSourceSchema=None,
436 initInputs=None, **kwargs):
437 """!Construct a CalibrateTask
439 @param[
in] butler The butler
is passed to the refObjLoader constructor
440 in case it
is needed. Ignored
if the refObjLoader argument
441 provides a loader directly.
442 @param[
in] astromRefObjLoader An instance of LoadReferenceObjectsTasks
443 that supplies an external reference catalog
for astrometric
444 calibration. May be
None if the desired loader can be constructed
445 from the butler argument
or all steps requiring a reference catalog
447 @param[
in] photoRefObjLoader An instance of LoadReferenceObjectsTasks
448 that supplies an external reference catalog
for photometric
449 calibration. May be
None if the desired loader can be constructed
450 from the butler argument
or all steps requiring a reference catalog
452 @param[
in] icSourceSchema schema
for icSource catalog,
or None.
453 Schema values specified
in config.icSourceFieldsToCopy will be
454 taken
from this schema. If set to
None, no values will be
455 propagated
from the icSourceCatalog
456 @param[
in,out] kwargs other keyword arguments
for
457 lsst.pipe.base.CmdLineTask
461 if icSourceSchema
is None and butler
is not None:
463 icSourceSchema = butler.get(
"icSrc_schema", immediate=
True).schema
465 if icSourceSchema
is None and butler
is None and initInputs
is not None:
466 icSourceSchema = initInputs[
'icSourceSchema'].schema
468 if icSourceSchema
is not None:
471 minimumSchema = afwTable.SourceTable.makeMinimalSchema()
472 self.
schemaMapperschemaMapper.addMinimalSchema(minimumSchema,
False)
480 afwTable.Field[
"Flag"](
"calib_detected",
481 "Source was detected as an icSource"))
482 missingFieldNames = []
483 for fieldName
in self.config.icSourceFieldsToCopy:
485 schemaItem = icSourceSchema.find(fieldName)
487 missingFieldNames.append(fieldName)
490 self.
schemaMapperschemaMapper.addMapping(schemaItem.getKey())
492 if missingFieldNames:
493 raise RuntimeError(
"isSourceCat is missing fields {} "
494 "specified in icSourceFieldsToCopy"
495 .format(missingFieldNames))
502 self.
schemaschema = afwTable.SourceTable.makeMinimalSchema()
503 self.makeSubtask(
'detection', schema=self.
schemaschema)
510 if self.config.doInsertFakes:
511 self.makeSubtask(
"insertFakes")
513 if self.config.doDeblend:
514 self.makeSubtask(
"deblend", schema=self.
schemaschema)
515 if self.config.doSkySources:
516 self.makeSubtask(
"skySources")
517 self.
skySourceKeyskySourceKey = self.
schemaschema.addField(
"sky_source", type=
"Flag", doc=
"Sky objects.")
518 self.makeSubtask(
'measurement', schema=self.
schemaschema,
520 self.makeSubtask(
"setPrimaryFlags", schema=self.
schemaschema, isSingleFrame=
True)
521 if self.config.doApCorr:
522 self.makeSubtask(
'applyApCorr', schema=self.
schemaschema)
523 self.makeSubtask(
'catalogCalculation', schema=self.
schemaschema)
525 if self.config.doAstrometry:
526 if astromRefObjLoader
is None and butler
is not None:
527 self.makeSubtask(
'astromRefObjLoader', butler=butler)
528 astromRefObjLoader = self.astromRefObjLoader
529 self.makeSubtask(
"astrometry", refObjLoader=astromRefObjLoader,
531 if self.config.doPhotoCal:
532 if photoRefObjLoader
is None and butler
is not None:
533 self.makeSubtask(
'photoRefObjLoader', butler=butler)
534 photoRefObjLoader = self.photoRefObjLoader
535 self.makeSubtask(
"photoCal", refObjLoader=photoRefObjLoader,
537 if self.config.doComputeSummaryStats:
538 self.makeSubtask(
'computeSummaryStats')
540 if initInputs
is not None and (astromRefObjLoader
is not None or photoRefObjLoader
is not None):
541 raise RuntimeError(
"PipelineTask form of this task should not be initialized with "
542 "reference object loaders.")
547 self.
schemaschema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
549 sourceCatSchema = afwTable.SourceCatalog(self.
schemaschema)
554 def runDataRef(self, dataRef, exposure=None, background=None, icSourceCat=None,
556 """!Calibrate an exposure, optionally unpersisting inputs and
559 This is a wrapper around the `run` method that unpersists inputs
560 (
if `doUnpersist` true)
and persists outputs (
if `config.doWrite` true)
562 @param[
in] dataRef butler data reference corresponding to a science
564 @param[
in,out] exposure characterized exposure (an
565 lsst.afw.image.ExposureF
or similar),
or None to unpersist existing
566 icExp
and icBackground. See `run` method
for details of what
is
568 @param[
in,out] background initial model of background already
569 subtracted
from exposure (an lsst.afw.math.BackgroundList). May be
570 None if no background has been subtracted, though that
is unusual
571 for calibration. A refined background model
is output. Ignored
if
573 @param[
in] icSourceCat catalog
from which to copy the fields specified
574 by icSourceKeys,
or None;
575 @param[
in] doUnpersist unpersist data:
576 -
if True, exposure, background
and icSourceCat are read
from
577 dataRef
and those three arguments must all be
None;
578 -
if False the exposure must be provided; background
and
579 icSourceCat are optional.
True is intended
for running
as a
580 command-line task,
False for running
as a subtask
581 @return same data
as the calibrate method
583 self.log.info("Processing %s", dataRef.dataId)
586 if any(item
is not None for item
in (exposure, background,
588 raise RuntimeError(
"doUnpersist true; exposure, background "
589 "and icSourceCat must all be None")
590 exposure = dataRef.get(
"icExp", immediate=
True)
591 background = dataRef.get(
"icExpBackground", immediate=
True)
592 icSourceCat = dataRef.get(
"icSrc", immediate=
True)
593 elif exposure
is None:
594 raise RuntimeError(
"doUnpersist false; exposure must be provided")
596 exposureIdInfo = dataRef.get(
"expIdInfo")
598 calRes = self.
runrun(
600 exposureIdInfo=exposureIdInfo,
601 background=background,
602 icSourceCat=icSourceCat,
605 if self.config.doWrite:
608 exposure=calRes.exposure,
609 background=calRes.background,
610 sourceCat=calRes.sourceCat,
611 astromMatches=calRes.astromMatches,
612 matchMeta=calRes.matchMeta,
618 inputs = butlerQC.get(inputRefs)
619 inputs[
'exposureIdInfo'] = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId,
"visit_detector")
621 if self.config.doAstrometry:
622 refObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
623 for ref
in inputRefs.astromRefCat],
624 refCats=inputs.pop(
'astromRefCat'),
625 config=self.config.astromRefObjLoader, log=self.log)
626 self.astrometry.setRefObjLoader(refObjLoader)
628 if self.config.doPhotoCal:
629 photoRefObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
630 for ref
in inputRefs.photoRefCat],
631 refCats=inputs.pop(
'photoRefCat'),
632 config=self.config.photoRefObjLoader,
634 self.photoCal.match.setRefObjLoader(photoRefObjLoader)
636 outputs = self.
runrun(**inputs)
638 if self.config.doWriteMatches
and self.config.doAstrometry:
639 normalizedMatches = afwTable.packMatches(outputs.astromMatches)
640 normalizedMatches.table.setMetadata(outputs.matchMeta)
641 if self.config.doWriteMatchesDenormalized:
642 denormMatches = denormalizeMatches(outputs.astromMatches, outputs.matchMeta)
643 outputs.matchesDenormalized = denormMatches
644 outputs.matches = normalizedMatches
645 butlerQC.put(outputs, outputRefs)
648 def run(self, exposure, exposureIdInfo=None, background=None,
650 """!Calibrate an exposure (science image or coadd)
652 @param[
in,out] exposure exposure to calibrate (an
653 lsst.afw.image.ExposureF
or similar);
658 - MaskedImage has background subtracted
660 - PhotoCalib
is replaced
661 @param[
in] exposureIdInfo ID info
for exposure (an
662 lsst.obs.base.ExposureIdInfo) If
not provided, returned
663 SourceCatalog IDs will
not be globally unique.
664 @param[
in,out] background background model already subtracted
from
665 exposure (an lsst.afw.math.BackgroundList). May be
None if no
666 background has been subtracted, though that
is unusual
for
667 calibration. A refined background model
is output.
668 @param[
in] icSourceCat A SourceCatalog
from CharacterizeImageTask
669 from which we can copy some fields.
671 @return pipe_base Struct containing these fields:
672 - exposure calibrate science exposure
with refined WCS
and PhotoCalib
673 - background model of background subtracted
from exposure (an
674 lsst.afw.math.BackgroundList)
675 - sourceCat catalog of measured sources
676 - astromMatches list of source/refObj matches
from the astrometry
680 if exposureIdInfo
is None:
681 exposureIdInfo = ExposureIdInfo()
683 if background
is None:
684 background = BackgroundList()
685 sourceIdFactory = exposureIdInfo.makeSourceIdFactory()
686 table = SourceTable.make(self.
schemaschema, sourceIdFactory)
689 detRes = self.detection.
run(table=table, exposure=exposure,
691 sourceCat = detRes.sources
692 if detRes.fpSets.background:
693 for bg
in detRes.fpSets.background:
694 background.append(bg)
695 if self.config.doSkySources:
696 skySourceFootprints = self.skySources.
run(mask=exposure.mask, seed=exposureIdInfo.expId)
697 if skySourceFootprints:
698 for foot
in skySourceFootprints:
699 s = sourceCat.addNew()
702 if self.config.doDeblend:
703 self.deblend.
run(exposure=exposure, sources=sourceCat)
704 self.measurement.
run(
707 exposureId=exposureIdInfo.expId
709 if self.config.doApCorr:
710 self.applyApCorr.
run(
712 apCorrMap=exposure.getInfo().getApCorrMap()
714 self.catalogCalculation.
run(sourceCat)
716 self.setPrimaryFlags.
run(sourceCat)
718 if icSourceCat
is not None and \
719 len(self.config.icSourceFieldsToCopy) > 0:
727 if not sourceCat.isContiguous():
728 sourceCat = sourceCat.copy(deep=
True)
734 if self.config.doAstrometry:
736 astromRes = self.astrometry.
run(
740 astromMatches = astromRes.matches
741 matchMeta = astromRes.matchMeta
742 except Exception
as e:
743 if self.config.requireAstrometry:
745 self.log.warning(
"Unable to perform astrometric calibration "
746 "(%s): attempting to proceed", e)
749 if self.config.doPhotoCal:
751 photoRes = self.photoCal.
run(exposure, sourceCat=sourceCat, expId=exposureIdInfo.expId)
752 exposure.setPhotoCalib(photoRes.photoCalib)
754 self.log.info(
"Photometric zero-point: %f",
755 photoRes.photoCalib.instFluxToMagnitude(1.0))
756 self.
setMetadatasetMetadata(exposure=exposure, photoRes=photoRes)
757 except Exception
as e:
758 if self.config.requirePhotoCal:
760 self.log.warning(
"Unable to perform photometric calibration "
761 "(%s): attempting to proceed", e)
762 self.
setMetadatasetMetadata(exposure=exposure, photoRes=
None)
764 if self.config.doInsertFakes:
765 self.insertFakes.
run(exposure, background=background)
767 table = SourceTable.make(self.
schemaschema, sourceIdFactory)
770 detRes = self.detection.
run(table=table, exposure=exposure,
772 sourceCat = detRes.sources
773 if detRes.fpSets.background:
774 for bg
in detRes.fpSets.background:
775 background.append(bg)
776 if self.config.doDeblend:
777 self.deblend.
run(exposure=exposure, sources=sourceCat)
778 self.measurement.
run(
781 exposureId=exposureIdInfo.expId
783 if self.config.doApCorr:
784 self.applyApCorr.
run(
786 apCorrMap=exposure.getInfo().getApCorrMap()
788 self.catalogCalculation.
run(sourceCat)
790 if icSourceCat
is not None and len(self.config.icSourceFieldsToCopy) > 0:
794 if self.config.doComputeSummaryStats:
795 summary = self.computeSummaryStats.
run(exposure=exposure,
797 background=background)
798 exposure.getInfo().setSummaryStats(summary)
800 frame = getDebugFrame(self._display,
"calibrate")
805 matches=astromMatches,
810 return pipeBase.Struct(
812 background=background,
814 astromMatches=astromMatches,
818 outputExposure=exposure,
820 outputBackground=background,
824 astromMatches, matchMeta):
825 """Write output data to the output repository
827 @param[
in] dataRef butler data reference corresponding to a science
829 @param[
in] exposure exposure to write
830 @param[
in] background background model
for exposure
831 @param[
in] sourceCat catalog of measured sources
832 @param[
in] astromMatches list of source/refObj matches
from the
835 dataRef.put(sourceCat, "src")
836 if self.config.doWriteMatches
and astromMatches
is not None:
837 normalizedMatches = afwTable.packMatches(astromMatches)
838 normalizedMatches.table.setMetadata(matchMeta)
839 dataRef.put(normalizedMatches,
"srcMatch")
840 if self.config.doWriteMatchesDenormalized:
841 denormMatches = denormalizeMatches(astromMatches, matchMeta)
842 dataRef.put(denormMatches,
"srcMatchFull")
843 if self.config.doWriteExposure:
844 dataRef.put(exposure,
"calexp")
845 dataRef.put(background,
"calexpBackground")
848 """Return a dict of empty catalogs for each catalog dataset produced
851 sourceCat = afwTable.SourceCatalog(self.schemaschema)
853 return {
"src": sourceCat}
856 """!Set task and exposure metadata
858 Logs a warning and continues
if needed data
is missing.
860 @param[
in,out] exposure exposure whose metadata
is to be set
861 @param[
in] photoRes results of running photoCal;
if None then it was
867 metadata = exposure.getMetadata()
871 exposureTime = exposure.getInfo().getVisitInfo().getExposureTime()
872 magZero = photoRes.zp - 2.5*math.log10(exposureTime)
874 self.log.warning(
"Could not set normalized MAGZERO in header: no "
879 metadata.set(
'MAGZERO', magZero)
880 metadata.set(
'MAGZERO_RMS', photoRes.sigma)
881 metadata.set(
'MAGZERO_NOBJ', photoRes.ngood)
882 metadata.set(
'COLORTERM1', 0.0)
883 metadata.set(
'COLORTERM2', 0.0)
884 metadata.set(
'COLORTERM3', 0.0)
885 except Exception
as e:
886 self.log.warning(
"Could not set exposure metadata: %s", e)
889 """!Match sources in icSourceCat and sourceCat and copy the specified fields
891 @param[
in] icSourceCat catalog
from which to copy fields
892 @param[
in,out] sourceCat catalog to which to copy fields
894 The fields copied are those specified by `config.icSourceFieldsToCopy`
895 that actually exist
in the schema. This was set up by the constructor
899 raise RuntimeError(
"To copy icSource fields you must specify "
900 "icSourceSchema nd icSourceKeys when "
901 "constructing this task")
902 if icSourceCat
is None or sourceCat
is None:
903 raise RuntimeError(
"icSourceCat and sourceCat must both be "
905 if len(self.config.icSourceFieldsToCopy) == 0:
906 self.log.warning(
"copyIcSourceFields doing nothing because "
907 "icSourceFieldsToCopy is empty")
910 mc = afwTable.MatchControl()
911 mc.findOnlyClosest =
False
912 matches = afwTable.matchXy(icSourceCat, sourceCat,
913 self.config.matchRadiusPix, mc)
914 if self.config.doDeblend:
915 deblendKey = sourceCat.schema[
"deblend_nChild"].asKey()
917 matches = [m
for m
in matches
if m[1].get(deblendKey) == 0]
924 for m0, m1, d
in matches:
926 match = bestMatches.get(id0)
927 if match
is None or d <= match[2]:
928 bestMatches[id0] = (m0, m1, d)
929 matches = list(bestMatches.values())
934 numMatches = len(matches)
935 numUniqueSources = len(set(m[1].getId()
for m
in matches))
936 if numUniqueSources != numMatches:
937 self.log.warning(
"%d icSourceCat sources matched only %d sourceCat "
938 "sources", numMatches, numUniqueSources)
940 self.log.info(
"Copying flags from icSourceCat to sourceCat for "
941 "%d sources", numMatches)
945 for icSrc, src, d
in matches:
951 icSrcFootprint = icSrc.getFootprint()
953 icSrc.setFootprint(src.getFootprint())
956 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)