38import lsst.pipe.base.connectionTypes
as cT
41from lsst.skymap
import BaseSkyMap
43from .forcedMeasurement
import ForcedMeasurementTask
44from .applyApCorr
import ApplyApCorrTask
45from .catalogCalculation
import CatalogCalculationTask
46from ._id_generator
import DetectorVisitIdGeneratorConfig
48__all__ = (
"ForcedPhotCcdConfig",
"ForcedPhotCcdTask",
49 "ForcedPhotCcdFromDataFrameTask",
"ForcedPhotCcdFromDataFrameConfig")
53 dimensions=(
"instrument",
"visit",
"detector",
"skymap",
"tract"),
54 defaultTemplates={
"inputCoaddName":
"deep",
55 "inputName":
"calexp",
56 "skyWcsName":
"gbdesAstrometricFit",
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=
"finalVisitSummary",
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=
"finalVisitSummary",
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).",
251 idGenerator = DetectorVisitIdGeneratorConfig.make_field()
253 def setDefaults(self):
257 super().setDefaults()
258 self.measurement.plugins.names |= [
'base_LocalPhotoCalib',
'base_LocalWcs']
259 self.catalogCalculation.plugins.names = []
263 """A pipeline task for performing forced measurement on CCD images.
268 Deprecated
and unused. Should always be `
None`.
270 The schema of the reference catalog, passed to the constructor of the
271 references subtask. Optional, but must be specified
if ``initInputs``
272 is not;
if both are specified, ``initInputs`` takes precedence.
274 Dictionary that can contain a key ``inputSchema`` containing the
275 schema. If present will override the value of ``refSchema``.
277 Keyword arguments are passed to the supertask constructor.
280 ConfigClass = ForcedPhotCcdConfig
281 _DefaultName = "forcedPhotCcd"
284 def __init__(self, butler=None, refSchema=None, initInputs=None, **kwds):
285 super().__init__(**kwds)
287 if butler
is not None:
288 warnings.warn(
"The 'butler' parameter is no longer used and can be safely removed.",
289 category=FutureWarning, stacklevel=2)
292 if initInputs
is not None:
293 refSchema = initInputs[
'inputSchema'].schema
295 if refSchema
is None:
296 raise ValueError(
"No reference schema provided.")
298 self.makeSubtask(
"measurement", refSchema=refSchema)
302 if self.config.doApCorr:
303 self.makeSubtask(
"applyApCorr", schema=self.measurement.schema)
304 self.makeSubtask(
'catalogCalculation', schema=self.measurement.schema)
307 def runQuantum(self, butlerQC, inputRefs, outputRefs):
308 inputs = butlerQC.get(inputRefs)
310 tract = butlerQC.quantum.dataId[
'tract']
311 skyMap = inputs.pop(
'skyMap')
312 inputs[
'refWcs'] = skyMap[tract].getWcs()
315 skyCorr = inputs.pop(
'skyCorr',
None)
316 if self.config.useGlobalExternalSkyWcs:
317 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsGlobalCatalog',
None)
319 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsTractCatalog',
None)
320 if self.config.useGlobalExternalPhotoCalib:
321 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibGlobalCatalog',
None)
323 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibTractCatalog',
None)
324 finalizedPsfApCorrCatalog = inputs.pop(
'finalizedPsfApCorrCatalog',
None)
326 inputs[
'exposure'] = self.prepareCalibratedExposure(
329 externalSkyWcsCatalog=externalSkyWcsCatalog,
330 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
331 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog
334 inputs[
'refCat'] = self.mergeAndFilterReferences(inputs[
'exposure'], inputs[
'refCat'],
337 if inputs[
'refCat']
is None:
338 self.log.info(
"No WCS for exposure %s. No %s catalog will be written.",
339 butlerQC.quantum.dataId, outputRefs.measCat.datasetType.name)
341 inputs[
'measCat'], inputs[
'exposureId'] = self.generateMeasCat(inputRefs.exposure.dataId,
343 inputs[
'refCat'], inputs[
'refWcs'])
344 self.attachFootprints(inputs[
'measCat'], inputs[
'refCat'], inputs[
'exposure'], inputs[
'refWcs'])
345 outputs = self.run(**inputs)
346 butlerQC.put(outputs, outputRefs)
348 def prepareCalibratedExposure(self, exposure, skyCorr=None, externalSkyWcsCatalog=None,
349 externalPhotoCalibCatalog=None, finalizedPsfApCorrCatalog=None):
350 """Prepare a calibrated exposure and apply external calibrations
351 and sky corrections
if so configured.
355 exposure : `lsst.afw.image.exposure.Exposure`
356 Input exposure to adjust calibrations.
357 skyCorr : `lsst.afw.math.backgroundList`, optional
358 Sky correction frame to apply
if doApplySkyCorr=
True.
360 Exposure catalog
with external skyWcs to be applied
361 if config.doApplyExternalSkyWcs=
True. Catalog uses the detector id
362 for the catalog id, sorted on id
for fast lookup.
364 Exposure catalog
with external photoCalib to be applied
365 if config.doApplyExternalPhotoCalib=
True. Catalog uses the detector
366 id
for the catalog id, sorted on id
for fast lookup.
368 Exposure catalog
with finalized psf models
and aperture correction
369 maps to be applied
if config.doApplyFinalizedPsf=
True. Catalog uses
370 the detector id
for the catalog id, sorted on id
for fast lookup.
374 exposure : `lsst.afw.image.exposure.Exposure`
375 Exposure
with adjusted calibrations.
377 detectorId = exposure.getInfo().getDetector().getId()
379 if externalPhotoCalibCatalog
is not None:
380 row = externalPhotoCalibCatalog.find(detectorId)
382 self.log.warning(
"Detector id %s not found in externalPhotoCalibCatalog; "
383 "Using original photoCalib.", detectorId)
385 photoCalib = row.getPhotoCalib()
386 if photoCalib
is None:
387 self.log.warning(
"Detector id %s has None for photoCalib in externalPhotoCalibCatalog; "
388 "Using original photoCalib.", detectorId)
390 exposure.setPhotoCalib(photoCalib)
392 if externalSkyWcsCatalog
is not None:
393 row = externalSkyWcsCatalog.find(detectorId)
395 self.log.warning(
"Detector id %s not found in externalSkyWcsCatalog; "
396 "Using original skyWcs.", detectorId)
398 skyWcs = row.getWcs()
400 self.log.warning(
"Detector id %s has None for skyWcs in externalSkyWcsCatalog; "
401 "Using original skyWcs.", detectorId)
403 exposure.setWcs(skyWcs)
405 if finalizedPsfApCorrCatalog
is not None:
406 row = finalizedPsfApCorrCatalog.find(detectorId)
408 self.log.warning(
"Detector id %s not found in finalizedPsfApCorrCatalog; "
409 "Using original psf.", detectorId)
412 apCorrMap = row.getApCorrMap()
413 if psf
is None or apCorrMap
is None:
414 self.log.warning(
"Detector id %s has None for psf/apCorrMap in "
415 "finalizedPsfApCorrCatalog; Using original psf.", detectorId)
418 exposure.setApCorrMap(apCorrMap)
420 if skyCorr
is not None:
421 exposure.maskedImage -= skyCorr.getImage()
425 def mergeAndFilterReferences(self, exposure, refCats, refWcs):
426 """Filter reference catalog so that all sources are within the
427 boundaries of the exposure.
431 exposure : `lsst.afw.image.exposure.Exposure`
432 Exposure to generate the catalog for.
433 refCats : sequence of `lsst.daf.butler.DeferredDatasetHandle`
434 Handles
for catalogs of shapes
and positions at which to force
436 refWcs : `lsst.afw.image.SkyWcs`
437 Reference world coordinate system.
442 Filtered catalog of forced sources to measure.
446 The majority of this code
is based on the methods of
447 lsst.meas.algorithms.loadReferenceObjects.ReferenceObjectLoader
454 expWcs = exposure.getWcs()
456 self.log.info(
"Exposure has no WCS. Returning None for mergedRefCat.")
458 expRegion = exposure.getBBox(lsst.afw.image.PARENT)
460 expBoxCorners = expBBox.getCorners()
461 expSkyCorners = [expWcs.pixelToSky(corner).getVector()
for
462 corner
in expBoxCorners]
470 for refCat
in refCats:
471 refCat = refCat.get()
472 if mergedRefCat
is None:
475 for record
in refCat:
476 if (expPolygon.contains(record.getCoord().getVector())
and record.getParent()
478 record.setFootprint(record.getFootprint())
479 mergedRefCat.append(record)
480 containedIds.add(record.getId())
481 if mergedRefCat
is None:
482 raise RuntimeError(
"No reference objects for forced photometry.")
486 def generateMeasCat(self, dataId, exposure, refCat, refWcs):
487 """Generate a measurement catalog.
491 dataId : `lsst.daf.butler.DataCoordinate`
492 Butler data ID for this image,
with ``{visit, detector}`` keys.
493 exposure : `lsst.afw.image.exposure.Exposure`
494 Exposure to generate the catalog
for.
496 Catalog of shapes
and positions at which to force photometry.
497 refWcs : `lsst.afw.image.SkyWcs`
498 Reference world coordinate system.
499 This parameter
is not currently used.
504 Catalog of forced sources to measure.
506 Unique binary id associated
with the input exposure
508 id_generator = self.config.idGenerator.apply(dataId)
509 measCat = self.measurement.generateMeasCat(exposure, refCat, refWcs,
510 idFactory=id_generator.make_table_id_factory())
511 return measCat, id_generator.catalog_id
513 def run(self, measCat, exposure, refCat, refWcs, exposureId=None):
514 """Perform forced measurement on a single exposure.
519 The measurement catalog, based on the sources listed in the
522 The measurement image upon which to perform forced detection.
524 The reference catalog of sources to measure.
525 refWcs : `lsst.afw.image.SkyWcs`
526 The WCS
for the references.
528 Optional unique exposureId used
for random seed
in measurement
533 result : `lsst.pipe.base.Struct`
534 Structure
with fields:
537 Catalog of forced measurement results
540 self.measurement.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
541 if self.config.doApCorr:
542 self.applyApCorr.run(
544 apCorrMap=exposure.getInfo().getApCorrMap()
546 self.catalogCalculation.run(measCat)
548 return pipeBase.Struct(measCat=measCat)
550 def attachFootprints(self, sources, refCat, exposure, refWcs):
551 """Attach footprints to blank sources prior to measurements.
556 be
in the pixel coordinate system of the image being measured,
while
557 the actual detections may start out
in a different coordinate system.
559 Subclasses of this
class may implement this method to define how
562 This default implementation transforms depends on the
563 ``footprintSource`` configuration parameter.
565 if self.config.footprintSource ==
"transformed":
566 return self.measurement.attachTransformedFootprints(sources, refCat, exposure, refWcs)
567 elif self.config.footprintSource ==
"psf":
568 return self.measurement.attachPsfShapeFootprints(sources, exposure,
569 scaling=self.config.psfFootprintScaling)
572class ForcedPhotCcdFromDataFrameConnections(PipelineTaskConnections,
573 dimensions=(
"instrument",
"visit",
"detector",
"skymap",
"tract"),
574 defaultTemplates={
"inputCoaddName":
"goodSeeing",
575 "inputName":
"calexp",
576 "skyWcsName":
"gbdesAstrometricFit",
577 "photoCalibName":
"fgcm"}):
579 doc=
"Catalog of positions at which to force photometry.",
580 name=
"{inputCoaddName}Diff_fullDiaObjTable",
581 storageClass=
"DataFrame",
582 dimensions=[
"skymap",
"tract",
"patch"],
587 doc=
"Input exposure to perform photometry on.",
589 storageClass=
"ExposureF",
590 dimensions=[
"instrument",
"visit",
"detector"],
593 doc=
"Input Sky Correction to be subtracted from the calexp if doApplySkyCorr=True",
595 storageClass=
"Background",
596 dimensions=(
"instrument",
"visit",
"detector"),
598 externalSkyWcsTractCatalog = cT.Input(
599 doc=(
"Per-tract, per-visit wcs calibrations. These catalogs use the detector "
600 "id for the catalog id, sorted on id for fast lookup."),
601 name=
"{skyWcsName}SkyWcsCatalog",
602 storageClass=
"ExposureCatalog",
603 dimensions=[
"instrument",
"visit",
"tract"],
605 externalSkyWcsGlobalCatalog = cT.Input(
606 doc=(
"Per-visit wcs calibrations computed globally (with no tract information). "
607 "These catalogs use the detector id for the catalog id, sorted on id for "
609 name=
"{skyWcsName}SkyWcsCatalog",
610 storageClass=
"ExposureCatalog",
611 dimensions=[
"instrument",
"visit"],
613 externalPhotoCalibTractCatalog = cT.Input(
614 doc=(
"Per-tract, per-visit photometric calibrations. These catalogs use the "
615 "detector id for the catalog id, sorted on id for fast lookup."),
616 name=
"{photoCalibName}PhotoCalibCatalog",
617 storageClass=
"ExposureCatalog",
618 dimensions=[
"instrument",
"visit",
"tract"],
620 externalPhotoCalibGlobalCatalog = cT.Input(
621 doc=(
"Per-visit photometric calibrations computed globally (with no tract "
622 "information). These catalogs use the detector id for the catalog id, "
623 "sorted on id for fast lookup."),
624 name=
"{photoCalibName}PhotoCalibCatalog",
625 storageClass=
"ExposureCatalog",
626 dimensions=[
"instrument",
"visit"],
628 finalizedPsfApCorrCatalog = cT.Input(
629 doc=(
"Per-visit finalized psf models and aperture correction maps. "
630 "These catalogs use the detector id for the catalog id, "
631 "sorted on id for fast lookup."),
632 name=
"finalized_psf_ap_corr_catalog",
633 storageClass=
"ExposureCatalog",
634 dimensions=[
"instrument",
"visit"],
637 doc=
"Output forced photometry catalog.",
638 name=
"forced_src_diaObject",
639 storageClass=
"SourceCatalog",
640 dimensions=[
"instrument",
"visit",
"detector",
"skymap",
"tract"],
642 outputSchema = cT.InitOutput(
643 doc=
"Schema for the output forced measurement catalogs.",
644 name=
"forced_src_diaObject_schema",
645 storageClass=
"SourceCatalog",
648 def __init__(self, *, config=None):
649 super().__init__(config=config)
650 if not config.doApplySkyCorr:
651 self.inputs.remove(
"skyCorr")
652 if config.doApplyExternalSkyWcs:
653 if config.useGlobalExternalSkyWcs:
654 self.inputs.remove(
"externalSkyWcsTractCatalog")
656 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
658 self.inputs.remove(
"externalSkyWcsTractCatalog")
659 self.inputs.remove(
"externalSkyWcsGlobalCatalog")
660 if config.doApplyExternalPhotoCalib:
661 if config.useGlobalExternalPhotoCalib:
662 self.inputs.remove(
"externalPhotoCalibTractCatalog")
664 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
666 self.inputs.remove(
"externalPhotoCalibTractCatalog")
667 self.inputs.remove(
"externalPhotoCalibGlobalCatalog")
668 if not config.doApplyFinalizedPsf:
669 self.inputs.remove(
"finalizedPsfApCorrCatalog")
672class ForcedPhotCcdFromDataFrameConfig(ForcedPhotCcdConfig,
673 pipelineConnections=ForcedPhotCcdFromDataFrameConnections):
674 def setDefaults(self):
675 super().setDefaults()
676 self.footprintSource =
"psf"
677 self.measurement.doReplaceWithNoise =
False
678 self.measurement.plugins.names = [
"base_LocalPhotoCalib",
"base_LocalWcs",
"base_LocalBackground",
679 "base_TransformedCentroidFromCoord",
"base_PsfFlux",
681 self.measurement.copyColumns = {
'id':
'diaObjectId',
'coord_ra':
'coord_ra',
'coord_dec':
'coord_dec'}
682 self.measurement.slots.centroid =
"base_TransformedCentroidFromCoord"
683 self.measurement.slots.psfFlux =
"base_PsfFlux"
684 self.measurement.slots.shape =
None
688 if self.footprintSource ==
"transformed":
689 raise ValueError(
"Cannot transform footprints from reference catalog, "
690 "because DataFrames can't hold footprints.")
693class ForcedPhotCcdFromDataFrameTask(ForcedPhotCcdTask):
694 """Force Photometry on a per-detector exposure with coords from a DataFrame
696 Uses input from a DataFrame instead of SourceCatalog
697 like the base
class ForcedPhotCcd does.
698 Writes out a SourceCatalog so that the downstream
699 WriteForcedSourceTableTask can be reused
with output
from this Task.
701 _DefaultName = "forcedPhotCcdFromDataFrame"
702 ConfigClass = ForcedPhotCcdFromDataFrameConfig
704 def __init__(self, butler=None, refSchema=None, initInputs=None, **kwds):
706 pipeBase.PipelineTask.__init__(self, **kwds)
708 if butler
is not None:
709 warnings.warn(
"The 'butler' parameter is no longer used and can be safely removed.",
710 category=FutureWarning, stacklevel=2)
715 if self.config.doApCorr:
716 self.makeSubtask(
"applyApCorr", schema=self.measurement.schema)
717 self.makeSubtask(
'catalogCalculation', schema=self.measurement.schema)
721 inputs = butlerQC.get(inputRefs)
724 inputs[
'refWcs'] =
None
727 skyCorr = inputs.pop(
'skyCorr',
None)
728 if self.config.useGlobalExternalSkyWcs:
729 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsGlobalCatalog',
None)
731 externalSkyWcsCatalog = inputs.pop(
'externalSkyWcsTractCatalog',
None)
732 if self.config.useGlobalExternalPhotoCalib:
733 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibGlobalCatalog',
None)
735 externalPhotoCalibCatalog = inputs.pop(
'externalPhotoCalibTractCatalog',
None)
736 finalizedPsfApCorrCatalog = inputs.pop(
'finalizedPsfApCorrCatalog',
None)
738 inputs[
'exposure'] = self.prepareCalibratedExposure(
741 externalSkyWcsCatalog=externalSkyWcsCatalog,
742 externalPhotoCalibCatalog=externalPhotoCalibCatalog,
743 finalizedPsfApCorrCatalog=finalizedPsfApCorrCatalog
746 self.log.info(
"Filtering ref cats: %s",
','.
join([
str(i.dataId)
for i
in inputs[
'refCat']]))
747 if inputs[
"exposure"].getWcs()
is not None:
748 refCat = self.
df2RefCat([i.get(parameters={
"columns": [
'diaObjectId',
'ra',
'decl']})
749 for i
in inputs[
'refCat']],
750 inputs[
'exposure'].
getBBox(), inputs[
'exposure'].getWcs())
751 inputs[
'refCat'] = refCat
753 inputs[
'measCat'], inputs[
'exposureId'] = self.generateMeasCat(
754 inputRefs.exposure.dataId, inputs[
'exposure'], inputs[
'refCat'], inputs[
'refWcs']
758 self.attachFootprints(inputs[
"measCat"], inputs[
"refCat"], inputs[
"exposure"], inputs[
"refWcs"])
759 outputs = self.run(**inputs)
761 butlerQC.put(outputs, outputRefs)
763 self.log.info(
"No WCS for %s. Skipping and no %s catalog will be written.",
764 butlerQC.quantum.dataId, outputRefs.measCat.datasetType.name)
767 """Convert list of DataFrames to reference catalog
769 Concatenate list of DataFrames presumably from multiple patches
and
770 downselect rows that overlap the exposureBBox using the exposureWcs.
774 dfList : `list` of `pandas.DataFrame`
775 Each element containst diaObjects
with ra/decl position
in degrees
776 Columns
'diaObjectId',
'ra',
'decl' are expected
778 Bounding box on which to select rows that overlap
780 World coordinate system to convert sky coords
in ref cat to
781 pixel coords
with which to compare
with exposureBBox
786 Source Catalog
with minimal schema that overlaps exposureBBox
788 df = pd.concat(dfList)
791 mapping = exposureWcs.getTransform().getMapping()
792 x, y = mapping.applyInverse(np.array(df[[
'ra',
'decl']].values*2*np.pi/360).T)
798 """Create minimal schema SourceCatalog from a pandas DataFrame.
800 The forced measurement subtask expects this as input.
804 df : `pandas.DataFrame`
805 DiaObjects
with locations
and ids.
810 Output catalog
with minimal schema.
814 outputCatalog.reserve(len(df))
816 for diaObjectId, ra, decl
in df[[
'ra',
'decl']].itertuples():
817 outputRecord = outputCatalog.addNew()
818 outputRecord.setId(diaObjectId)
std::string join(std::string const &a, std::string const &b) const
static Schema makeMinimalSchema()
static Key< RecordId > getParentKey()
def df2RefCat(self, dfList, exposureBBox, exposureWcs)
def runQuantum(self, butlerQC, inputRefs, outputRefs)
def df2SourceCat(self, df)