37from lsst.obs.base
import ExposureIdInfo
39import lsst.pipe.base.connectionTypes
as cT
42from lsst.skymap
import BaseSkyMap
44from .forcedMeasurement
import ForcedMeasurementTask
45from .applyApCorr
import ApplyApCorrTask
46from .catalogCalculation
import CatalogCalculationTask
48__all__ = (
"ForcedPhotCcdConfig",
"ForcedPhotCcdTask",
49 "ForcedPhotCcdFromDataFrameTask",
"ForcedPhotCcdFromDataFrameConfig")
53 dimensions=(
"instrument",
"visit",
"detector",
"skymap",
"tract"),
54 defaultTemplates={
"inputCoaddName":
"deep",
55 "inputName":
"calexp",
56 "skyWcsName":
"jointcal",
57 "photoCalibName":
"fgcm"}):
58 inputSchema = cT.InitInput(
59 doc=
"Schema for the input measurement catalogs.",
60 name=
"{inputCoaddName}Coadd_ref_schema",
61 storageClass=
"SourceCatalog",
63 outputSchema = cT.InitOutput(
64 doc=
"Schema for the output forced measurement catalogs.",
65 name=
"forced_src_schema",
66 storageClass=
"SourceCatalog",
69 doc=
"Input exposure to perform photometry on.",
71 storageClass=
"ExposureF",
72 dimensions=[
"instrument",
"visit",
"detector"],
75 doc=
"Catalog of shapes and positions at which to force photometry.",
76 name=
"{inputCoaddName}Coadd_ref",
77 storageClass=
"SourceCatalog",
78 dimensions=[
"skymap",
"tract",
"patch"],
83 doc=
"SkyMap dataset that defines the coordinate system of the reference catalog.",
84 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
85 storageClass=
"SkyMap",
86 dimensions=[
"skymap"],
89 doc=
"Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
91 storageClass=
"Background",
92 dimensions=(
"instrument",
"visit",
"detector"),
94 externalSkyWcsTractCatalog = cT.Input(
95 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
96 "id for the catalog id, sorted on id for fast lookup."),
97 name=
"{skyWcsName}SkyWcsCatalog",
98 storageClass=
"ExposureCatalog",
99 dimensions=[
"instrument",
"visit",
"tract"],
101 externalSkyWcsGlobalCatalog = cT.Input(
102 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
103 "These catalogs use the detector id for the catalog id, sorted on id for "
105 name=
"{skyWcsName}SkyWcsCatalog",
106 storageClass=
"ExposureCatalog",
107 dimensions=[
"instrument",
"visit"],
109 externalPhotoCalibTractCatalog = cT.Input(
110 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
111 "detector id for the catalog id, sorted on id for fast lookup."),
112 name=
"{photoCalibName}PhotoCalibCatalog",
113 storageClass=
"ExposureCatalog",
114 dimensions=[
"instrument",
"visit",
"tract"],
116 externalPhotoCalibGlobalCatalog = cT.Input(
117 doc=(
"Per-visit photometric calibrations computed globally (with no tract "
118 "information). These catalogs use the detector id for the catalog id, "
119 "sorted on id for fast lookup."),
120 name=
"{photoCalibName}PhotoCalibCatalog",
121 storageClass=
"ExposureCatalog",
122 dimensions=[
"instrument",
"visit"],
124 finalizedPsfApCorrCatalog = cT.Input(
125 doc=(
"Per-visit finalized psf models and aperture correction maps. "
126 "These catalogs use the detector id for the catalog id, "
127 "sorted on id for fast lookup."),
128 name=
"finalized_psf_ap_corr_catalog",
129 storageClass=
"ExposureCatalog",
130 dimensions=[
"instrument",
"visit"],
133 doc=
"Output forced photometry catalog.",
135 storageClass=
"SourceCatalog",
136 dimensions=[
"instrument",
"visit",
"detector",
"skymap",
"tract"],
139 def __init__(self, *, config=None):
140 super().__init__(config=config)
141 if not config.doApplySkyCorr:
142 self.inputs.remove(
"skyCorr")
143 if config.doApplyExternalSkyWcs:
144 if config.useGlobalExternalSkyWcs:
145 self.inputs.remove(
"externalSkyWcsTractCatalog")
147 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
149 self.inputs.remove(
"externalSkyWcsTractCatalog")
150 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
151 if config.doApplyExternalPhotoCalib:
152 if config.useGlobalExternalPhotoCalib:
153 self.inputs.remove(
"externalPhotoCalibTractCatalog")
155 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
157 self.inputs.remove(
"externalPhotoCalibTractCatalog")
158 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
159 if not config.doApplyFinalizedPsf:
160 self.inputs.remove(
"finalizedPsfApCorrCatalog")
163class ForcedPhotCcdConfig(pipeBase.PipelineTaskConfig,
164 pipelineConnections=ForcedPhotCcdConnections):
165 """Config class for forced measurement driver task."""
166 measurement = lsst.pex.config.ConfigurableField(
167 target=ForcedMeasurementTask,
168 doc=
"subtask to do forced measurement"
170 coaddName = lsst.pex.config.Field(
171 doc=
"coadd name: typically one of deep or goodSeeing",
175 doApCorr = lsst.pex.config.Field(
178 doc=
"Run subtask to apply aperture corrections"
180 applyApCorr = lsst.pex.config.ConfigurableField(
181 target=ApplyApCorrTask,
182 doc=
"Subtask to apply aperture corrections"
184 catalogCalculation = lsst.pex.config.ConfigurableField(
185 target=CatalogCalculationTask,
186 doc=
"Subtask to run catalogCalculation plugins on catalog"
188 doApplyUberCal = lsst.pex.config.Field(
190 doc=
"Apply meas_mosaic ubercal results to input calexps?",
192 deprecated=
"Deprecated by DM-23352; use doApplyExternalPhotoCalib and doApplyExternalSkyWcs instead",
194 doApplyExternalPhotoCalib = lsst.pex.config.Field(
197 doc=(
"Whether to apply external photometric calibration via an "
198 "`lsst.afw.image.PhotoCalib` object."),
200 useGlobalExternalPhotoCalib = lsst.pex.config.Field(
203 doc=(
"When using doApplyExternalPhotoCalib, use 'global' calibrations "
204 "that are not run per-tract. When False, use per-tract photometric "
205 "calibration files.")
207 doApplyExternalSkyWcs = lsst.pex.config.Field(
210 doc=(
"Whether to apply external astrometric calibration via an "
211 "`lsst.afw.geom.SkyWcs` object."),
213 useGlobalExternalSkyWcs = lsst.pex.config.Field(
216 doc=(
"When using doApplyExternalSkyWcs, use 'global' calibrations "
217 "that are not run per-tract. When False, use per-tract wcs "
220 doApplyFinalizedPsf = lsst.pex.config.Field(
223 doc=
"Whether to apply finalized psf models and aperture correction map.",
225 doApplySkyCorr = lsst.pex.config.Field(
228 doc=
"Apply sky correction?",
230 includePhotoCalibVar = lsst.pex.config.Field(
233 doc=
"Add photometric calibration variance to warp variance plane?",
235 footprintSource = lsst.pex.config.ChoiceField(
237 doc=
"Where to obtain footprints to install in the measurement catalog, prior to measurement.",
239 "transformed":
"Transform footprints from the reference catalog (downgrades HeavyFootprints).",
240 "psf": (
"Use the scaled shape of the PSF at the position of each source (does not generate "
241 "HeavyFootprints)."),
244 default=
"transformed",
246 psfFootprintScaling = lsst.pex.config.Field(
248 doc=
"Scaling factor to apply to the PSF shape when footprintSource='psf' (ignored otherwise).",
252 def setDefaults(self):
256 super().setDefaults()
257 self.measurement.plugins.names |= [
'base_LocalPhotoCalib',
'base_LocalWcs']
258 self.catalogCalculation.plugins.names = []
261class ForcedPhotCcdTask(pipeBase.PipelineTask):
262 """A pipeline task for performing forced measurement on CCD images.
267 Deprecated
and unused. Should always be `
None`.
269 The schema of the reference catalog, passed to the constructor of the
270 references subtask. Optional, but must be specified
if ``initInputs``
271 is not;
if both are specified, ``initInputs`` takes precedence.
273 Dictionary that can contain a key ``inputSchema`` containing the
274 schema. If present will override the value of ``refSchema``.
276 Keyword arguments are passed to the supertask constructor.
279 ConfigClass = ForcedPhotCcdConfig
280 _DefaultName = "forcedPhotCcd"
283 def __init__(self, butler=None, refSchema=None, initInputs=None, **kwds):
284 super().__init__(**kwds)
286 if butler
is not None:
287 warnings.warn(
"The 'butler' parameter is no longer used and can be safely removed.",
288 category=FutureWarning, stacklevel=2)
291 if initInputs
is not None:
292 refSchema = initInputs[
'inputSchema'].schema
294 if refSchema
is None:
295 raise ValueError(
"No reference schema provided.")
297 self.makeSubtask(
"measurement", refSchema=refSchema)
300 if self.config.doApCorr:
301 self.makeSubtask(
"applyApCorr", schema=self.measurement.schema)
302 self.makeSubtask(
'catalogCalculation', schema=self.measurement.schema)
305 def runQuantum(self, butlerQC, inputRefs, outputRefs):
306 inputs = butlerQC.get(inputRefs)
308 tract = butlerQC.quantum.dataId[
'tract']
309 skyMap = inputs.pop(
'skyMap')
310 inputs[
'refWcs'] = skyMap[tract].getWcs()
313 skyCorr = inputs.pop(
'skyCorr',
None)
314 if self.config.useGlobalExternalSkyWcs:
315 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsGlobalCatalog',
None)
317 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsTractCatalog',
None)
318 if self.config.useGlobalExternalPhotoCalib:
319 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibGlobalCatalog',
None)
321 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibTractCatalog',
None)
322 finalizedPsfApCorrCatalog = inputs.pop(
'finalizedPsfApCorrCatalog',
None)
324 inputs[
'exposure'] = self.prepareCalibratedExposure(
327 externalSkyWcsCatalog=externalSkyWcsCatalog,
328 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
329 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog
332 inputs[
'refCat'] = self.mergeAndFilterReferences(inputs[
'exposure'], inputs[
'refCat'],
335 inputs[
'measCat'], inputs[
'exposureId'] = self.generateMeasCat(inputRefs.exposure.dataId,
337 inputs[
'refCat'], inputs[
'refWcs'],
339 self.attachFootprints(inputs[
'measCat'], inputs[
'refCat'], inputs[
'exposure'], inputs[
'refWcs'])
340 outputs = self.run(**inputs)
341 butlerQC.put(outputs, outputRefs)
343 def prepareCalibratedExposure(self, exposure, skyCorr=None, externalSkyWcsCatalog=None,
344 externalPhotoCalibCatalog=None, finalizedPsfApCorrCatalog=None):
345 """Prepare a calibrated exposure and apply external calibrations
346 and sky corrections
if so configured.
350 exposure : `lsst.afw.image.exposure.Exposure`
351 Input exposure to adjust calibrations.
352 skyCorr : `lsst.afw.math.backgroundList`, optional
353 Sky correction frame to apply
if doApplySkyCorr=
True.
355 Exposure catalog
with external skyWcs to be applied
356 if config.doApplyExternalSkyWcs=
True. Catalog uses the detector id
357 for the catalog id, sorted on id
for fast lookup.
359 Exposure catalog
with external photoCalib to be applied
360 if config.doApplyExternalPhotoCalib=
True. Catalog uses the detector
361 id
for the catalog id, sorted on id
for fast lookup.
363 Exposure catalog
with finalized psf models
and aperture correction
364 maps to be applied
if config.doApplyFinalizedPsf=
True. Catalog uses
365 the detector id
for the catalog id, sorted on id
for fast lookup.
369 exposure : `lsst.afw.image.exposure.Exposure`
370 Exposure
with adjusted calibrations.
372 detectorId = exposure.getInfo().getDetector().getId()
374 if externalPhotoCalibCatalog
is not None:
375 row = externalPhotoCalibCatalog.find(detectorId)
377 self.log.warning(
"Detector id %s not found in externalPhotoCalibCatalog; "
378 "Using original photoCalib.", detectorId)
380 photoCalib = row.getPhotoCalib()
381 if photoCalib
is None:
382 self.log.warning(
"Detector id %s has None for photoCalib in externalPhotoCalibCatalog; "
383 "Using original photoCalib.", detectorId)
385 exposure.setPhotoCalib(photoCalib)
387 if externalSkyWcsCatalog
is not None:
388 row = externalSkyWcsCatalog.find(detectorId)
390 self.log.warning(
"Detector id %s not found in externalSkyWcsCatalog; "
391 "Using original skyWcs.", detectorId)
393 skyWcs = row.getWcs()
395 self.log.warning(
"Detector id %s has None for skyWcs in externalSkyWcsCatalog; "
396 "Using original skyWcs.", detectorId)
398 exposure.setWcs(skyWcs)
400 if finalizedPsfApCorrCatalog
is not None:
401 row = finalizedPsfApCorrCatalog.find(detectorId)
403 self.log.warning(
"Detector id %s not found in finalizedPsfApCorrCatalog; "
404 "Using original psf.", detectorId)
407 apCorrMap = row.getApCorrMap()
408 if psf
is None or apCorrMap
is None:
409 self.log.warning(
"Detector id %s has None for psf/apCorrMap in "
410 "finalizedPsfApCorrCatalog; Using original psf.", detectorId)
413 exposure.setApCorrMap(apCorrMap)
415 if skyCorr
is not None:
416 exposure.maskedImage -= skyCorr.getImage()
420 def mergeAndFilterReferences(self, exposure, refCats, refWcs):
421 """Filter reference catalog so that all sources are within the
422 boundaries of the exposure.
426 exposure : `lsst.afw.image.exposure.Exposure`
427 Exposure to generate the catalog for.
428 refCats : sequence of `lsst.daf.butler.DeferredDatasetHandle`
429 Handles
for catalogs of shapes
and positions at which to force
431 refWcs : `lsst.afw.image.SkyWcs`
432 Reference world coordinate system.
437 Filtered catalog of forced sources to measure.
441 The majority of this code
is based on the methods of
442 lsst.meas.algorithms.loadReferenceObjects.ReferenceObjectLoader
448 expWcs = exposure.getWcs()
449 expRegion = exposure.getBBox(lsst.afw.image.PARENT)
451 expBoxCorners = expBBox.getCorners()
452 expSkyCorners = [expWcs.pixelToSky(corner).getVector()
for
453 corner
in expBoxCorners]
462 for refCat
in refCats:
463 refCat = refCat.get()
464 if mergedRefCat
is None:
467 for record
in refCat:
468 if expPolygon.contains(record.getCoord().getVector())
and record.getParent()
in containedIds:
469 record.setFootprint(record.getFootprint())
470 mergedRefCat.append(record)
471 containedIds.add(record.getId())
472 if mergedRefCat
is None:
473 raise RuntimeError(
"No reference objects for forced photometry.")
477 def generateMeasCat(self, exposureDataId, exposure, refCat, refWcs, idPackerName):
478 """Generate a measurement catalog.
482 exposureDataId : `DataId`
483 Butler dataId for this exposure.
484 exposure : `lsst.afw.image.exposure.Exposure`
485 Exposure to generate the catalog
for.
487 Catalog of shapes
and positions at which to force photometry.
488 refWcs : `lsst.afw.image.SkyWcs`
489 Reference world coordinate system.
490 This parameter
is not currently used.
492 Type of ID packer to construct
from the registry.
497 Catalog of forced sources to measure.
499 Unique binary id associated
with the input exposure
501 exposureIdInfo = ExposureIdInfo.fromDataId(exposureDataId, idPackerName)
502 idFactory = exposureIdInfo.makeSourceIdFactory()
504 measCat = self.measurement.generateMeasCat(exposure, refCat, refWcs,
506 return measCat, exposureIdInfo.expId
508 def run(self, measCat, exposure, refCat, refWcs, exposureId=None):
509 """Perform forced measurement on a single exposure.
514 The measurement catalog, based on the sources listed in the
517 The measurement image upon which to perform forced detection.
519 The reference catalog of sources to measure.
520 refWcs : `lsst.afw.image.SkyWcs`
521 The WCS
for the references.
523 Optional unique exposureId used
for random seed
in measurement
528 result : `lsst.pipe.base.Struct`
529 Structure
with fields:
532 Catalog of forced measurement results
535 self.measurement.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
536 if self.config.doApCorr:
537 self.applyApCorr.run(
539 apCorrMap=exposure.getInfo().getApCorrMap()
541 self.catalogCalculation.run(measCat)
543 return pipeBase.Struct(measCat=measCat)
545 def attachFootprints(self, sources, refCat, exposure, refWcs):
546 r"""Attach footprints to blank sources prior to measurements.
551 pixel coordinate system of the image being measured,
while the actual
552 detections may start out
in a different coordinate system.
554 Subclasses of this
class may implement this method to define how
557 This default implementation transforms depends on the
558 ``footprintSource`` configuration parameter.
560 if self.config.footprintSource ==
"transformed":
561 return self.measurement.attachTransformedFootprints(sources, refCat, exposure, refWcs)
562 elif self.config.footprintSource ==
"psf":
563 return self.measurement.attachPsfShapeFootprints(sources, exposure,
564 scaling=self.config.psfFootprintScaling)
566 def getSchemaCatalogs(self):
567 """The schema catalogs that will be used by this task.
571 schemaCatalogs : `dict`
572 Dictionary mapping dataset type to schema catalog.
576 There is only one schema
for each type of forced measurement. The
577 dataset type
for this measurement
is defined
in the mapper.
580 catalog.getTable().setMetadata(self.measurement.algMetadata)
581 datasetType = self.dataPrefix + "forced_src"
582 return {datasetType: catalog}
585class ForcedPhotCcdFromDataFrameConnections(PipelineTaskConnections,
586 dimensions=(
"instrument",
"visit",
"detector",
"skymap",
"tract"),
587 defaultTemplates={
"inputCoaddName":
"goodSeeing",
588 "inputName":
"calexp",
589 "skyWcsName":
"jointcal",
590 "photoCalibName":
"fgcm"}):
592 doc=
"Catalog of positions at which to force photometry.",
593 name=
"{inputCoaddName}Diff_fullDiaObjTable",
594 storageClass=
"DataFrame",
595 dimensions=[
"skymap",
"tract",
"patch"],
600 doc=
"Input exposure to perform photometry on.",
602 storageClass=
"ExposureF",
603 dimensions=[
"instrument",
"visit",
"detector"],
606 doc=
"Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
608 storageClass=
"Background",
609 dimensions=(
"instrument",
"visit",
"detector"),
611 externalSkyWcsTractCatalog = cT.Input(
612 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
613 "id for the catalog id, sorted on id for fast lookup."),
614 name=
"{skyWcsName}SkyWcsCatalog",
615 storageClass=
"ExposureCatalog",
616 dimensions=[
"instrument",
"visit",
"tract"],
618 externalSkyWcsGlobalCatalog = cT.Input(
619 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
620 "These catalogs use the detector id for the catalog id, sorted on id for "
622 name=
"{skyWcsName}SkyWcsCatalog",
623 storageClass=
"ExposureCatalog",
624 dimensions=[
"instrument",
"visit"],
626 externalPhotoCalibTractCatalog = cT.Input(
627 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
628 "detector id for the catalog id, sorted on id for fast lookup."),
629 name=
"{photoCalibName}PhotoCalibCatalog",
630 storageClass=
"ExposureCatalog",
631 dimensions=[
"instrument",
"visit",
"tract"],
633 externalPhotoCalibGlobalCatalog = cT.Input(
634 doc=(
"Per-visit photometric calibrations computed globally (with no tract "
635 "information). These catalogs use the detector id for the catalog id, "
636 "sorted on id for fast lookup."),
637 name=
"{photoCalibName}PhotoCalibCatalog",
638 storageClass=
"ExposureCatalog",
639 dimensions=[
"instrument",
"visit"],
641 finalizedPsfApCorrCatalog = cT.Input(
642 doc=(
"Per-visit finalized psf models and aperture correction maps. "
643 "These catalogs use the detector id for the catalog id, "
644 "sorted on id for fast lookup."),
645 name=
"finalized_psf_ap_corr_catalog",
646 storageClass=
"ExposureCatalog",
647 dimensions=[
"instrument",
"visit"],
650 doc=
"Output forced photometry catalog.",
651 name=
"forced_src_diaObject",
652 storageClass=
"SourceCatalog",
653 dimensions=[
"instrument",
"visit",
"detector",
"skymap",
"tract"],
655 outputSchema = cT.InitOutput(
656 doc=
"Schema for the output forced measurement catalogs.",
657 name=
"forced_src_diaObject_schema",
658 storageClass=
"SourceCatalog",
661 def __init__(self, *, config=None):
662 super().__init__(config=config)
663 if not config.doApplySkyCorr:
664 self.inputs.remove(
"skyCorr")
665 if config.doApplyExternalSkyWcs:
666 if config.useGlobalExternalSkyWcs:
667 self.inputs.remove(
"externalSkyWcsTractCatalog")
669 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
671 self.inputs.remove(
"externalSkyWcsTractCatalog")
672 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
673 if config.doApplyExternalPhotoCalib:
674 if config.useGlobalExternalPhotoCalib:
675 self.inputs.remove(
"externalPhotoCalibTractCatalog")
677 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
679 self.inputs.remove(
"externalPhotoCalibTractCatalog")
680 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
681 if not config.doApplyFinalizedPsf:
682 self.inputs.remove(
"finalizedPsfApCorrCatalog")
685class ForcedPhotCcdFromDataFrameConfig(ForcedPhotCcdConfig,
686 pipelineConnections=ForcedPhotCcdFromDataFrameConnections):
687 def setDefaults(self):
688 super().setDefaults()
689 self.footprintSource =
"psf"
690 self.measurement.doReplaceWithNoise =
False
691 self.measurement.plugins.names = [
"base_LocalPhotoCalib",
"base_LocalWcs",
"base_LocalBackground",
692 "base_TransformedCentroidFromCoord",
"base_PsfFlux",
694 self.measurement.copyColumns = {
'id':
'diaObjectId',
'coord_ra':
'coord_ra',
'coord_dec':
'coord_dec'}
695 self.measurement.slots.centroid =
"base_TransformedCentroidFromCoord"
696 self.measurement.slots.psfFlux =
"base_PsfFlux"
697 self.measurement.slots.shape =
None
701 if self.footprintSource ==
"transformed":
702 raise ValueError(
"Cannot transform footprints from reference catalog, "
703 "because DataFrames can't hold footprints.")
706class ForcedPhotCcdFromDataFrameTask(ForcedPhotCcdTask):
707 """Force Photometry on a per-detector exposure with coords from a DataFrame
709 Uses input from a DataFrame instead of SourceCatalog
711 Writes out a SourceCatalog so that the downstream
712 WriteForcedSourceTableTask can be reused
with output
from this Task.
714 _DefaultName = "forcedPhotCcdFromDataFrame"
715 ConfigClass = ForcedPhotCcdFromDataFrameConfig
717 def __init__(self, butler=None, refSchema=None, initInputs=None, **kwds):
719 pipeBase.PipelineTask.__init__(self, **kwds)
721 if butler
is not None:
722 warnings.warn(
"The 'butler' parameter is no longer used and can be safely removed.",
723 category=FutureWarning, stacklevel=2)
728 if self.config.doApCorr:
729 self.makeSubtask(
"applyApCorr", schema=self.measurement.schema)
730 self.makeSubtask(
'catalogCalculation', schema=self.measurement.schema)
733 def runQuantum(self, butlerQC, inputRefs, outputRefs):
734 inputs = butlerQC.get(inputRefs)
737 inputs[
'refWcs'] =
None
740 skyCorr = inputs.pop(
'skyCorr',
None)
741 if self.config.useGlobalExternalSkyWcs:
742 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsGlobalCatalog',
None)
744 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsTractCatalog',
None)
745 if self.config.useGlobalExternalPhotoCalib:
746 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibGlobalCatalog',
None)
748 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibTractCatalog',
None)
749 finalizedPsfApCorrCatalog = inputs.pop(
'finalizedPsfApCorrCatalog',
None)
751 inputs[
'exposure'] = self.prepareCalibratedExposure(
754 externalSkyWcsCatalog=externalSkyWcsCatalog,
755 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
756 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog
759 self.log.info(
"Filtering ref cats: %s",
','.join([
str(i.dataId)
for i
in inputs[
'refCat']]))
760 refCat = self.df2RefCat([i.get(parameters={
"columns": [
'diaObjectId',
'ra',
'decl']})
761 for i
in inputs[
'refCat']],
762 inputs[
'exposure'].getBBox(), inputs[
'exposure'].getWcs())
763 inputs[
'refCat'] = refCat
765 inputs[
'measCat'], inputs[
'exposureId'] = self.generateMeasCat(inputRefs.exposure.dataId,
766 inputs[
'exposure'], inputs[
'refCat'],
771 self.attachFootprints(inputs[
"measCat"], inputs[
"refCat"], inputs[
"exposure"], inputs[
"refWcs"])
772 outputs = self.run(**inputs)
774 butlerQC.put(outputs, outputRefs)
776 def df2RefCat(self, dfList, exposureBBox, exposureWcs):
777 """Convert list of DataFrames to reference catalog
779 Concatenate list of DataFrames presumably from multiple patches
and
780 downselect rows that overlap the exposureBBox using the exposureWcs.
784 dfList : `list` of `pandas.DataFrame`
785 Each element containst diaObjects
with ra/decl position
in degrees
786 Columns
'diaObjectId',
'ra',
'decl' are expected
788 Bounding box on which to select rows that overlap
790 World coordinate system to convert sky coords
in ref cat to
791 pixel coords
with which to compare
with exposureBBox
796 Source Catalog
with minimal schema that overlaps exposureBBox
798 df = pd.concat(dfList)
801 mapping = exposureWcs.getTransform().getMapping()
802 x, y = mapping.applyInverse(np.array(df[[
'ra',
'decl']].values*2*np.pi/360).T)
804 refCat = self.df2SourceCat(df[inBBox])
807 def df2SourceCat(self, df):
808 """Create minimal schema SourceCatalog from a pandas DataFrame.
810 The forced measurement subtask expects this as input.
814 df : `pandas.DataFrame`
815 DiaObjects
with locations
and ids.
820 Output catalog
with minimal schema.
824 outputCatalog.reserve(len(df))
826 for diaObjectId, ra, decl
in df[[
'ra',
'decl']].itertuples():
827 outputRecord = outputCatalog.addNew()
828 outputRecord.setId(diaObjectId)
static Schema makeMinimalSchema()
static Key< RecordId > getParentKey()