37from lsst.utils.introspection
import find_outside_stacklevel
39import lsst.pipe.base.connectionTypes
as cT
44from .forcedMeasurement
import ForcedMeasurementTask
45from .applyApCorr
import ApplyApCorrTask
46from .catalogCalculation
import CatalogCalculationTask
47from ._id_generator
import DetectorVisitIdGeneratorConfig
49__all__ = (
"ForcedPhotCcdConfig",
"ForcedPhotCcdTask",
50 "ForcedPhotCcdFromDataFrameTask",
"ForcedPhotCcdFromDataFrameConfig")
54 dimensions=(
"instrument",
"visit",
"detector",
"skymap",
"tract"),
55 defaultTemplates={
"inputCoaddName":
"deep",
56 "inputName":
"calexp",
57 "skyWcsName":
"gbdesAstrometricFit",
58 "photoCalibName":
"fgcm"},
60 deprecatedTemplates={
"skyWcsName":
"Deprecated; will be removed after v26.",
61 "photoCalibName":
"Deprecated; will be removed after v26."
63 inputSchema = cT.InitInput(
64 doc=
"Schema for the input measurement catalogs.",
65 name=
"{inputCoaddName}Coadd_ref_schema",
66 storageClass=
"SourceCatalog",
68 outputSchema = cT.InitOutput(
69 doc=
"Schema for the output forced measurement catalogs.",
70 name=
"forced_src_schema",
71 storageClass=
"SourceCatalog",
74 doc=
"Input exposure to perform photometry on.",
76 storageClass=
"ExposureF",
77 dimensions=[
"instrument",
"visit",
"detector"],
80 doc=
"Catalog of shapes and positions at which to force photometry.",
81 name=
"{inputCoaddName}Coadd_ref",
82 storageClass=
"SourceCatalog",
83 dimensions=[
"skymap",
"tract",
"patch"],
88 doc=
"SkyMap dataset that defines the coordinate system of the reference catalog.",
89 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
90 storageClass=
"SkyMap",
91 dimensions=[
"skymap"],
94 doc=
"Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
96 storageClass=
"Background",
97 dimensions=(
"instrument",
"visit",
"detector"),
99 visitSummary = cT.Input(
100 doc=
"Input visit-summary catalog with updated calibration objects.",
101 name=
"finalVisitSummary",
102 storageClass=
"ExposureCatalog",
103 dimensions=(
"instrument",
"visit"),
105 externalSkyWcsTractCatalog = cT.Input(
106 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
107 "id for the catalog id, sorted on id for fast lookup."),
108 name=
"{skyWcsName}SkyWcsCatalog",
109 storageClass=
"ExposureCatalog",
110 dimensions=[
"instrument",
"visit",
"tract"],
112 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
114 externalSkyWcsGlobalCatalog = cT.Input(
115 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
116 "These catalogs use the detector id for the catalog id, sorted on id for "
118 name=
"finalVisitSummary",
119 storageClass=
"ExposureCatalog",
120 dimensions=[
"instrument",
"visit"],
122 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
124 externalPhotoCalibTractCatalog = cT.Input(
125 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
126 "detector id for the catalog id, sorted on id for fast lookup."),
127 name=
"{photoCalibName}PhotoCalibCatalog",
128 storageClass=
"ExposureCatalog",
129 dimensions=[
"instrument",
"visit",
"tract"],
131 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
133 externalPhotoCalibGlobalCatalog = cT.Input(
134 doc=(
"Per-visit photometric calibrations computed globally (with no tract "
135 "information). These catalogs use the detector id for the catalog id, "
136 "sorted on id for fast lookup."),
137 name=
"finalVisitSummary",
138 storageClass=
"ExposureCatalog",
139 dimensions=[
"instrument",
"visit"],
141 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
143 finalizedPsfApCorrCatalog = cT.Input(
144 doc=(
"Per-visit finalized psf models and aperture correction maps. "
145 "These catalogs use the detector id for the catalog id, "
146 "sorted on id for fast lookup."),
147 name=
"finalized_psf_ap_corr_catalog",
148 storageClass=
"ExposureCatalog",
149 dimensions=[
"instrument",
"visit"],
151 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
154 doc=
"Output forced photometry catalog.",
156 storageClass=
"SourceCatalog",
157 dimensions=[
"instrument",
"visit",
"detector",
"skymap",
"tract"],
160 def __init__(self, *, config=None):
161 super().__init__(config=config)
162 if not config.doApplySkyCorr:
163 self.inputs.remove(
"skyCorr")
164 if config.doApplyExternalSkyWcs:
165 if config.useGlobalExternalSkyWcs:
166 self.inputs.remove(
"externalSkyWcsTractCatalog")
168 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
170 self.inputs.remove(
"externalSkyWcsTractCatalog")
171 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
172 if config.doApplyExternalPhotoCalib:
173 if config.useGlobalExternalPhotoCalib:
174 self.inputs.remove(
"externalPhotoCalibTractCatalog")
176 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
178 self.inputs.remove(
"externalPhotoCalibTractCatalog")
179 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
180 if not config.doApplyFinalizedPsf:
181 self.inputs.remove(
"finalizedPsfApCorrCatalog")
184class ForcedPhotCcdConfig(pipeBase.PipelineTaskConfig,
185 pipelineConnections=ForcedPhotCcdConnections):
186 """Config class for forced measurement driver task."""
188 target=ForcedMeasurementTask,
189 doc=
"subtask to do forced measurement"
192 doc=
"coadd name: typically one of deep or goodSeeing",
199 doc=
"Run subtask to apply aperture corrections"
202 target=ApplyApCorrTask,
203 doc=
"Subtask to apply aperture corrections"
206 target=CatalogCalculationTask,
207 doc=
"Subtask to run catalogCalculation plugins on catalog"
212 doc=(
"Whether to apply external photometric calibration via an "
213 "`lsst.afw.image.PhotoCalib` object."),
215 deprecated=
"Removed in favor of the 'visitSummary' connection; will be removed after v26.",
220 doc=(
"When using doApplyExternalPhotoCalib, use 'global' calibrations "
221 "that are not run per-tract. When False, use per-tract photometric "
222 "calibration files."),
224 deprecated=
"Removed in favor of the 'visitSummary' connection; will be removed after v26.",
229 doc=(
"Whether to apply external astrometric calibration via an "
230 "`lsst.afw.geom.SkyWcs` object."),
232 deprecated=
"Removed in favor of the 'visitSummary' connection; will be removed after v26.",
237 doc=(
"When using doApplyExternalSkyWcs, use 'global' calibrations "
238 "that are not run per-tract. When False, use per-tract wcs "
241 deprecated=
"Removed in favor of the 'visitSummary' connection; will be removed after v26.",
246 doc=
"Whether to apply finalized psf models and aperture correction map.",
248 deprecated=
"Removed in favor of the 'visitSummary' connection; will be removed after v26.",
253 doc=
"Apply sky correction?",
258 doc=
"Add photometric calibration variance to warp variance plane?",
262 doc=
"Where to obtain footprints to install in the measurement catalog, prior to measurement.",
264 "transformed":
"Transform footprints from the reference catalog (downgrades HeavyFootprints).",
265 "psf": (
"Use the scaled shape of the PSF at the position of each source (does not generate "
266 "HeavyFootprints)."),
269 default=
"transformed",
273 doc=
"Scaling factor to apply to the PSF shape when footprintSource='psf' (ignored otherwise).",
276 idGenerator = DetectorVisitIdGeneratorConfig.make_field()
278 def setDefaults(self):
280 super().setDefaults()
283 self.measurement.doReplaceWithNoise =
False
286 self.measurement.plugins.names = [
"base_PixelFlags",
287 "base_TransformedCentroid",
289 "base_LocalBackground",
290 "base_LocalPhotoCalib",
293 self.measurement.slots.shape =
None
296 self.catalogCalculation.plugins.names = []
299class ForcedPhotCcdTask(pipeBase.PipelineTask):
300 """A pipeline task for performing forced measurement on CCD images.
305 The schema of the reference catalog, passed to the constructor of the
306 references subtask. Optional, but must be specified if ``initInputs``
307 is not;
if both are specified, ``initInputs`` takes precedence.
309 Dictionary that can contain a key ``inputSchema`` containing the
310 schema. If present will override the value of ``refSchema``.
312 Keyword arguments are passed to the supertask constructor.
315 ConfigClass = ForcedPhotCcdConfig
316 _DefaultName = "forcedPhotCcd"
319 def __init__(self, refSchema=None, initInputs=None, **kwargs):
320 super().__init__(**kwargs)
322 if initInputs
is not None:
323 refSchema = initInputs[
'inputSchema'].schema
325 if refSchema
is None:
326 raise ValueError(
"No reference schema provided.")
328 self.makeSubtask(
"measurement", refSchema=refSchema)
332 if self.config.doApCorr:
333 self.makeSubtask(
"applyApCorr", schema=self.measurement.schema)
334 self.makeSubtask(
'catalogCalculation', schema=self.measurement.schema)
337 def runQuantum(self, butlerQC, inputRefs, outputRefs):
338 inputs = butlerQC.get(inputRefs)
340 tract = butlerQC.quantum.dataId[
'tract']
341 skyMap = inputs.pop(
'skyMap')
342 inputs[
'refWcs'] = skyMap[tract].getWcs()
345 skyCorr = inputs.pop(
'skyCorr',
None)
346 if self.config.useGlobalExternalSkyWcs:
347 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsGlobalCatalog',
None)
349 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsTractCatalog',
None)
350 if self.config.useGlobalExternalPhotoCalib:
351 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibGlobalCatalog',
None)
353 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibTractCatalog',
None)
354 finalizedPsfApCorrCatalog = inputs.pop(
'finalizedPsfApCorrCatalog',
None)
356 inputs[
'exposure'] = self.prepareCalibratedExposure(
359 externalSkyWcsCatalog=externalSkyWcsCatalog,
360 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
361 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog,
362 visitSummary=inputs.pop(
"visitSummary"),
365 inputs[
'refCat'] = self.mergeAndFilterReferences(inputs[
'exposure'], inputs[
'refCat'],
368 if inputs[
'refCat']
is None:
369 self.log.info(
"No WCS for exposure %s. No %s catalog will be written.",
370 butlerQC.quantum.dataId, outputRefs.measCat.datasetType.name)
372 inputs[
'measCat'], inputs[
'exposureId'] = self.generateMeasCat(inputRefs.exposure.dataId,
374 inputs[
'refCat'], inputs[
'refWcs'])
375 self.attachFootprints(inputs[
'measCat'], inputs[
'refCat'], inputs[
'exposure'], inputs[
'refWcs'])
376 outputs = self.run(**inputs)
377 butlerQC.put(outputs, outputRefs)
379 def prepareCalibratedExposure(self, exposure, skyCorr=None, externalSkyWcsCatalog=None,
380 externalPhotoCalibCatalog=None, finalizedPsfApCorrCatalog=None,
382 """Prepare a calibrated exposure and apply external calibrations
383 and sky corrections
if so configured.
387 exposure : `lsst.afw.image.exposure.Exposure`
388 Input exposure to adjust calibrations.
389 skyCorr : `lsst.afw.math.backgroundList`, optional
390 Sky correction frame to apply
if doApplySkyCorr=
True.
392 Exposure catalog
with external skyWcs to be applied
if
393 config.doApplyExternalSkyWcs=
True. Catalog uses the detector id
394 for the catalog id, sorted on id
for fast lookup. Deprecated
in
395 favor of ``visitSummary``
and will be removed after v26.
397 Exposure catalog
with external photoCalib to be applied
if
398 config.doApplyExternalPhotoCalib=
True. Catalog uses the detector
399 id
for the catalog id, sorted on id
for fast lookup. Deprecated
in
400 favor of ``visitSummary``
and will be removed after v26.
402 Exposure catalog
with finalized psf models
and aperture correction
403 maps to be applied
if config.doApplyFinalizedPsf=
True. Catalog
404 uses the detector id
for the catalog id, sorted on id
for fast
405 lookup. Deprecated
in favor of ``visitSummary``
and will be removed
408 Exposure catalog
with update calibrations; any
not-
None calibration
409 objects attached will be used. These are applied first
and may be
410 overridden by other arguments.
414 exposure : `lsst.afw.image.exposure.Exposure`
415 Exposure
with adjusted calibrations.
417 detectorId = exposure.getInfo().getDetector().getId()
419 if visitSummary
is not None:
420 row = visitSummary.find(detectorId)
422 raise RuntimeError(f
"Detector id {detectorId} not found in visitSummary.")
423 if (photoCalib := row.getPhotoCalib())
is not None:
424 exposure.setPhotoCalib(photoCalib)
425 if (skyWcs := row.getWcs())
is not None:
426 exposure.setWcs(skyWcs)
427 if (psf := row.getPsf())
is not None:
429 if (apCorrMap := row.getApCorrMap())
is not None:
430 exposure.info.setApCorrMap(apCorrMap)
432 if externalPhotoCalibCatalog
is not None:
435 "The 'externalPhotoCalibCatalog' argument is deprecated in favor of 'visitSummary' and will "
436 "be removed after v26.",
438 stacklevel=find_outside_stacklevel(
"lsst.meas.base"),
440 row = externalPhotoCalibCatalog.find(detectorId)
442 self.log.warning(
"Detector id %s not found in externalPhotoCalibCatalog; "
443 "Using original photoCalib.", detectorId)
445 photoCalib = row.getPhotoCalib()
446 if photoCalib
is None:
447 self.log.warning(
"Detector id %s has None for photoCalib in externalPhotoCalibCatalog; "
448 "Using original photoCalib.", detectorId)
450 exposure.setPhotoCalib(photoCalib)
452 if externalSkyWcsCatalog
is not None:
455 "The 'externalSkyWcsCatalog' argument is deprecated in favor of 'visitSummary' and will "
456 "be removed after v26.",
458 stacklevel=find_outside_stacklevel(
"lsst.meas.base"),
460 row = externalSkyWcsCatalog.find(detectorId)
462 self.log.warning(
"Detector id %s not found in externalSkyWcsCatalog; "
463 "Using original skyWcs.", detectorId)
465 skyWcs = row.getWcs()
467 self.log.warning(
"Detector id %s has None for skyWcs in externalSkyWcsCatalog; "
468 "Using original skyWcs.", detectorId)
470 exposure.setWcs(skyWcs)
472 if finalizedPsfApCorrCatalog
is not None:
475 "The 'finalizedPsfApCorrCatalog' argument is deprecated in favor of 'visitSummary' and will "
476 "be removed after v26.",
478 stacklevel=find_outside_stacklevel(
"lsst.meas.base"),
480 row = finalizedPsfApCorrCatalog.find(detectorId)
482 self.log.warning(
"Detector id %s not found in finalizedPsfApCorrCatalog; "
483 "Using original psf.", detectorId)
486 apCorrMap = row.getApCorrMap()
487 if psf
is None or apCorrMap
is None:
488 self.log.warning(
"Detector id %s has None for psf/apCorrMap in "
489 "finalizedPsfApCorrCatalog; Using original psf.", detectorId)
492 exposure.setApCorrMap(apCorrMap)
494 if skyCorr
is not None:
495 exposure.maskedImage -= skyCorr.getImage()
499 def mergeAndFilterReferences(self, exposure, refCats, refWcs):
500 """Filter reference catalog so that all sources are within the
501 boundaries of the exposure.
505 exposure : `lsst.afw.image.exposure.Exposure`
506 Exposure to generate the catalog for.
507 refCats : sequence of `lsst.daf.butler.DeferredDatasetHandle`
508 Handles
for catalogs of shapes
and positions at which to force
510 refWcs : `lsst.afw.image.SkyWcs`
511 Reference world coordinate system.
516 Filtered catalog of forced sources to measure.
520 The majority of this code
is based on the methods of
528 expWcs = exposure.getWcs()
530 self.log.info(
"Exposure has no WCS. Returning None for mergedRefCat.")
532 expRegion = exposure.getBBox(lsst.afw.image.PARENT)
534 expBoxCorners = expBBox.getCorners()
535 expSkyCorners = [expWcs.pixelToSky(corner).getVector()
for
536 corner
in expBoxCorners]
544 for refCat
in refCats:
545 refCat = refCat.get()
546 if mergedRefCat
is None:
549 for record
in refCat:
550 if (expPolygon.contains(record.getCoord().getVector())
and record.getParent()
552 record.setFootprint(record.getFootprint())
553 mergedRefCat.append(record)
554 containedIds.add(record.getId())
555 if mergedRefCat
is None:
556 raise RuntimeError(
"No reference objects for forced photometry.")
560 def generateMeasCat(self, dataId, exposure, refCat, refWcs):
561 """Generate a measurement catalog.
565 dataId : `lsst.daf.butler.DataCoordinate`
566 Butler data ID for this image,
with ``{visit, detector}`` keys.
567 exposure : `lsst.afw.image.exposure.Exposure`
568 Exposure to generate the catalog
for.
570 Catalog of shapes
and positions at which to force photometry.
571 refWcs : `lsst.afw.image.SkyWcs`
572 Reference world coordinate system.
573 This parameter
is not currently used.
578 Catalog of forced sources to measure.
580 Unique binary id associated
with the input exposure
582 id_generator = self.config.idGenerator.apply(dataId)
583 measCat = self.measurement.generateMeasCat(exposure, refCat, refWcs,
584 idFactory=id_generator.make_table_id_factory())
585 return measCat, id_generator.catalog_id
587 def run(self, measCat, exposure, refCat, refWcs, exposureId=None):
588 """Perform forced measurement on a single exposure.
593 The measurement catalog, based on the sources listed in the
596 The measurement image upon which to perform forced detection.
598 The reference catalog of sources to measure.
599 refWcs : `lsst.afw.image.SkyWcs`
600 The WCS
for the references.
602 Optional unique exposureId used
for random seed
in measurement
607 result : `lsst.pipe.base.Struct`
608 Structure
with fields:
611 Catalog of forced measurement results
614 self.measurement.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
615 if self.config.doApCorr:
616 apCorrMap = exposure.getInfo().getApCorrMap()
617 if apCorrMap
is None:
618 self.log.warning(
"Forced exposure image does not have valid aperture correction; skipping.")
620 self.applyApCorr.run(
624 self.catalogCalculation.run(measCat)
626 return pipeBase.Struct(measCat=measCat)
628 def attachFootprints(self, sources, refCat, exposure, refWcs):
629 """Attach footprints to blank sources prior to measurements.
634 be
in the pixel coordinate system of the image being measured,
while
635 the actual detections may start out
in a different coordinate system.
637 Subclasses of this
class may implement this method
to define how
640 This default implementation transforms depends on the
641 ``footprintSource`` configuration parameter.
643 if self.
config.footprintSource ==
"transformed":
644 return self.measurement.attachTransformedFootprints(sources, refCat, exposure, refWcs)
645 elif self.
config.footprintSource ==
"psf":
646 return self.measurement.attachPsfShapeFootprints(sources, exposure,
647 scaling=self.
config.psfFootprintScaling)
650class ForcedPhotCcdFromDataFrameConnections(PipelineTaskConnections,
651 dimensions=(
"instrument",
"visit",
"detector",
"skymap",
"tract"),
652 defaultTemplates={
"inputCoaddName":
"goodSeeing",
653 "inputName":
"calexp",
654 "skyWcsName":
"gbdesAstrometricFit",
655 "photoCalibName":
"fgcm"},
657 deprecatedTemplates={
658 "skyWcsName":
"Deprecated; will be removed after v26.",
659 "photoCalibName":
"Deprecated; will be removed after v26."
662 doc=
"Catalog of positions at which to force photometry.",
663 name=
"{inputCoaddName}Diff_fullDiaObjTable",
664 storageClass=
"DataFrame",
665 dimensions=[
"skymap",
"tract",
"patch"],
670 doc=
"Input exposure to perform photometry on.",
672 storageClass=
"ExposureF",
673 dimensions=[
"instrument",
"visit",
"detector"],
676 doc=
"Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
678 storageClass=
"Background",
679 dimensions=(
"instrument",
"visit",
"detector"),
681 visitSummary = cT.Input(
682 doc=
"Input visit-summary catalog with updated calibration objects.",
683 name=
"finalVisitSummary",
684 storageClass=
"ExposureCatalog",
685 dimensions=(
"instrument",
"visit"),
687 externalSkyWcsTractCatalog = cT.Input(
688 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
689 "id for the catalog id, sorted on id for fast lookup."),
690 name=
"{skyWcsName}SkyWcsCatalog",
691 storageClass=
"ExposureCatalog",
692 dimensions=[
"instrument",
"visit",
"tract"],
694 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
696 externalSkyWcsGlobalCatalog = cT.Input(
697 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
698 "These catalogs use the detector id for the catalog id, sorted on id for "
700 name=
"{skyWcsName}SkyWcsCatalog",
701 storageClass=
"ExposureCatalog",
702 dimensions=[
"instrument",
"visit"],
704 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
706 externalPhotoCalibTractCatalog = cT.Input(
707 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
708 "detector id for the catalog id, sorted on id for fast lookup."),
709 name=
"{photoCalibName}PhotoCalibCatalog",
710 storageClass=
"ExposureCatalog",
711 dimensions=[
"instrument",
"visit",
"tract"],
713 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
715 externalPhotoCalibGlobalCatalog = cT.Input(
716 doc=(
"Per-visit photometric calibrations computed globally (with no tract "
717 "information). These catalogs use the detector id for the catalog id, "
718 "sorted on id for fast lookup."),
719 name=
"{photoCalibName}PhotoCalibCatalog",
720 storageClass=
"ExposureCatalog",
721 dimensions=[
"instrument",
"visit"],
723 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
725 finalizedPsfApCorrCatalog = cT.Input(
726 doc=(
"Per-visit finalized psf models and aperture correction maps. "
727 "These catalogs use the detector id for the catalog id, "
728 "sorted on id for fast lookup."),
729 name=
"finalized_psf_ap_corr_catalog",
730 storageClass=
"ExposureCatalog",
731 dimensions=[
"instrument",
"visit"],
733 deprecated=
"Deprecated in favor of 'visitSummary'; will be removed after v26."
736 doc=
"Output forced photometry catalog.",
737 name=
"forced_src_diaObject",
738 storageClass=
"SourceCatalog",
739 dimensions=[
"instrument",
"visit",
"detector",
"skymap",
"tract"],
741 outputSchema = cT.InitOutput(
742 doc=
"Schema for the output forced measurement catalogs.",
743 name=
"forced_src_diaObject_schema",
744 storageClass=
"SourceCatalog",
747 def __init__(self, *, config=None):
748 super().__init__(config=config)
749 if not config.doApplySkyCorr:
750 self.inputs.remove(
"skyCorr")
751 if config.doApplyExternalSkyWcs:
752 if config.useGlobalExternalSkyWcs:
753 self.inputs.remove(
"externalSkyWcsTractCatalog")
755 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
757 self.inputs.remove(
"externalSkyWcsTractCatalog")
758 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
759 if config.doApplyExternalPhotoCalib:
760 if config.useGlobalExternalPhotoCalib:
761 self.inputs.remove(
"externalPhotoCalibTractCatalog")
763 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
765 self.inputs.remove(
"externalPhotoCalibTractCatalog")
766 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
767 if not config.doApplyFinalizedPsf:
768 self.inputs.remove(
"finalizedPsfApCorrCatalog")
771class ForcedPhotCcdFromDataFrameConfig(ForcedPhotCcdConfig,
772 pipelineConnections=ForcedPhotCcdFromDataFrameConnections):
773 def setDefaults(self):
774 super().setDefaults()
775 self.footprintSource =
"psf"
776 self.measurement.doReplaceWithNoise =
False
779 self.measurement.plugins.names = [
"base_PixelFlags",
780 "base_TransformedCentroidFromCoord",
782 "base_LocalBackground",
783 "base_LocalPhotoCalib",
786 self.measurement.slots.shape =
None
789 self.catalogCalculation.plugins.names = []
791 self.measurement.copyColumns = {
'id':
'diaObjectId',
'coord_ra':
'coord_ra',
'coord_dec':
'coord_dec'}
792 self.measurement.slots.centroid =
"base_TransformedCentroidFromCoord"
793 self.measurement.slots.psfFlux =
"base_PsfFlux"
797 if self.footprintSource ==
"transformed":
798 raise ValueError(
"Cannot transform footprints from reference catalog, "
799 "because DataFrames can't hold footprints.")
802class ForcedPhotCcdFromDataFrameTask(ForcedPhotCcdTask):
803 """Force Photometry on a per-detector exposure with coords from a DataFrame
805 Uses input from a DataFrame instead of SourceCatalog
806 like the base
class ForcedPhotCcd does.
807 Writes out a SourceCatalog so that the downstream
808 WriteForcedSourceTableTask can be reused
with output
from this Task.
810 _DefaultName = "forcedPhotCcdFromDataFrame"
811 ConfigClass = ForcedPhotCcdFromDataFrameConfig
813 def __init__(self, refSchema=None, initInputs=None, **kwargs):
815 pipeBase.PipelineTask.__init__(self, **kwargs)
821 self.makeSubtask(
'catalogCalculation', schema=self.measurement.schema)
825 inputs = butlerQC.get(inputRefs)
828 inputs[
'refWcs'] =
None
831 skyCorr = inputs.pop(
'skyCorr',
None)
832 if self.config.useGlobalExternalSkyWcs:
833 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsGlobalCatalog',
None)
835 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsTractCatalog',
None)
836 if self.config.useGlobalExternalPhotoCalib:
837 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibGlobalCatalog',
None)
839 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibTractCatalog',
None)
840 finalizedPsfApCorrCatalog = inputs.pop(
'finalizedPsfApCorrCatalog',
None)
842 inputs[
'exposure'] = self.prepareCalibratedExposure(
845 externalSkyWcsCatalog=externalSkyWcsCatalog,
846 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
847 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog,
848 visitSummary=inputs.pop(
"visitSummary"),
851 self.log.info(
"Filtering ref cats: %s",
','.join([str(i.dataId)
for i
in inputs[
'refCat']]))
852 if inputs[
"exposure"].getWcs()
is not None:
853 refCat = self.df2RefCat([i.get(parameters={
"columns": [
'diaObjectId',
'ra',
'dec']})
854 for i
in inputs[
'refCat']],
855 inputs[
'exposure'].getBBox(), inputs[
'exposure'].getWcs())
856 inputs[
'refCat'] = refCat
858 inputs[
'measCat'], inputs[
'exposureId'] = self.generateMeasCat(
859 inputRefs.exposure.dataId, inputs[
'exposure'], inputs[
'refCat'], inputs[
'refWcs']
863 self.attachFootprints(inputs[
"measCat"], inputs[
"refCat"], inputs[
"exposure"], inputs[
"refWcs"])
864 outputs = self.run(**inputs)
866 butlerQC.put(outputs, outputRefs)
868 self.log.info(
"No WCS for %s. Skipping and no %s catalog will be written.",
869 butlerQC.quantum.dataId, outputRefs.measCat.datasetType.name)
872 """Convert list of DataFrames to reference catalog
874 Concatenate list of DataFrames presumably from multiple patches
and
875 downselect rows that overlap the exposureBBox using the exposureWcs.
879 dfList : `list` of `pandas.DataFrame`
880 Each element containst diaObjects
with ra/dec position
in degrees
881 Columns
'diaObjectId',
'ra',
'dec' are expected
883 Bounding box on which to select rows that overlap
885 World coordinate system to convert sky coords
in ref cat to
886 pixel coords
with which to compare
with exposureBBox
891 Source Catalog
with minimal schema that overlaps exposureBBox
893 df = pd.concat(dfList)
896 mapping = exposureWcs.getTransform().getMapping()
897 x, y = mapping.applyInverse(np.array(df[[
'ra',
'dec']].values*2*np.pi/360).T)
899 refCat = self.df2SourceCat(df[inBBox])
903 """Create minimal schema SourceCatalog from a pandas DataFrame.
905 The forced measurement subtask expects this as input.
909 df : `pandas.DataFrame`
910 DiaObjects
with locations
and ids.
915 Output catalog
with minimal schema.
919 outputCatalog.reserve(len(df))
921 for diaObjectId, ra, dec
in df[[
'ra',
'dec']].itertuples():
922 outputRecord = outputCatalog.addNew()
923 outputRecord.setId(diaObjectId)
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Custom catalog class for ExposureRecord/Table.
Defines the fields and offsets for a table.
Table class that contains measurements made on a single exposure.
static Schema makeMinimalSchema()
Return a minimal schema for Source tables and records.
static Key< RecordId > getParentKey()
Key for the parent ID.
A floating-point coordinate rectangle geometry.
An integer coordinate rectangle.
Point in an unspecified spherical coordinate system.
ConvexPolygon is a closed convex polygon on the unit sphere.
df2RefCat(self, dfList, exposureBBox, exposureWcs)
runQuantum(self, butlerQC, inputRefs, outputRefs)