28from lsst.obs.base
import ExposureIdInfo
30from .forcedMeasurement
import ForcedMeasurementTask
31from .applyApCorr
import ApplyApCorrTask
32from .catalogCalculation
import CatalogCalculationTask
34__all__ = (
"ForcedPhotCoaddConfig",
"ForcedPhotCoaddTask")
38 dimensions=(
"band",
"skymap",
"tract",
"patch"),
39 defaultTemplates={
"inputCoaddName":
"deep",
40 "outputCoaddName":
"deep"}):
41 inputSchema = pipeBase.connectionTypes.InitInput(
42 doc=
"Schema for the input measurement catalogs.",
43 name=
"{inputCoaddName}Coadd_ref_schema",
44 storageClass=
"SourceCatalog",
46 outputSchema = pipeBase.connectionTypes.InitOutput(
47 doc=
"Schema for the output forced measurement catalogs.",
48 name=
"{outputCoaddName}Coadd_forced_src_schema",
49 storageClass=
"SourceCatalog",
51 exposure = pipeBase.connectionTypes.Input(
52 doc=
"Input exposure to perform photometry on.",
53 name=
"{inputCoaddName}Coadd_calexp",
54 storageClass=
"ExposureF",
55 dimensions=[
"band",
"skymap",
"tract",
"patch"],
57 refCat = pipeBase.connectionTypes.Input(
58 doc=
"Catalog of shapes and positions at which to force photometry.",
59 name=
"{inputCoaddName}Coadd_ref",
60 storageClass=
"SourceCatalog",
61 dimensions=[
"skymap",
"tract",
"patch"],
63 refCatInBand = pipeBase.connectionTypes.Input(
64 doc=
"Catalog of shapes and positions in the band having forced photometry done",
65 name=
"{inputCoaddName}Coadd_meas",
66 storageClass=
"SourceCatalog",
67 dimensions=(
"band",
"skymap",
"tract",
"patch")
69 footprintCatInBand = pipeBase.connectionTypes.Input(
70 doc=
"Catalog of footprints to attach to sources",
71 name=
"{inputCoaddName}Coadd_deblendedFlux",
72 storageClass=
"SourceCatalog",
73 dimensions=(
"band",
"skymap",
"tract",
"patch")
75 scarletModels = pipeBase.connectionTypes.Input(
76 doc=
"Multiband scarlet models produced by the deblender",
77 name=
"{inputCoaddName}Coadd_scarletModelData",
78 storageClass=
"ScarletModelData",
79 dimensions=(
"tract",
"patch",
"skymap"),
81 refWcs = pipeBase.connectionTypes.Input(
82 doc=
"Reference world coordinate system.",
83 name=
"{inputCoaddName}Coadd.wcs",
85 dimensions=[
"band",
"skymap",
"tract",
"patch"],
87 measCat = pipeBase.connectionTypes.Output(
88 doc=
"Output forced photometry catalog.",
89 name=
"{outputCoaddName}Coadd_forced_src",
90 storageClass=
"SourceCatalog",
91 dimensions=[
"band",
"skymap",
"tract",
"patch"],
94 def __init__(self, *, config=None):
95 super().__init__(config=config)
96 if config.footprintDatasetName !=
"ScarletModelData":
97 self.inputs.remove(
"scarletModels")
98 if config.footprintDatasetName !=
"DeblendedFlux":
99 self.inputs.remove(
"footprintCatInBand")
102class ForcedPhotCoaddConfig(pipeBase.PipelineTaskConfig,
103 pipelineConnections=ForcedPhotCoaddConnections):
105 target=ForcedMeasurementTask,
106 doc=
"subtask to do forced measurement"
109 doc=
"coadd name: typically one of deep or goodSeeing",
116 doc=
"Run subtask to apply aperture corrections"
119 target=ApplyApCorrTask,
120 doc=
"Subtask to apply aperture corrections"
123 target=CatalogCalculationTask,
124 doc=
"Subtask to run catalogCalculation plugins on catalog"
127 doc=
"Dataset (without coadd prefix) that should be used to obtain (Heavy)Footprints for sources. "
128 "Must have IDs that match those of the reference catalog."
129 "If None, Footprints will be generated by transforming the reference Footprints.",
131 default=
"ScarletModelData",
137 doc=
"Whether to use the deblender models as templates to re-distribute the flux "
138 "from the 'exposure' (True), or to perform measurements on the deblender model footprints. "
139 "If footprintDatasetName != 'ScarletModelData' then this field is ignored.")
143 doc=
"Whether to strip footprints from the output catalog before "
145 "This is usually done when using scarlet models to save disk space.")
149 doc=
"Should be set to True if fake sources have been inserted into the input data."
152 def setDefaults(self):
156 super().setDefaults()
158 self.catalogCalculation.plugins.names = []
159 self.measurement.copyColumns[
"id"] =
"id"
160 self.measurement.copyColumns[
"parent"] =
"parent"
161 self.measurement.plugins.names |= [
'base_InputCount',
'base_Variance']
162 self.measurement.plugins[
'base_PixelFlags'].masksFpAnywhere = [
'CLIPPED',
'SENSOR_EDGE',
163 'REJECTED',
'INEXACT_PSF']
164 self.measurement.plugins[
'base_PixelFlags'].masksFpCenter = [
'CLIPPED',
'SENSOR_EDGE',
165 'REJECTED',
'INEXACT_PSF']
168class ForcedPhotCoaddTask(pipeBase.PipelineTask):
169 """A pipeline task for performing forced measurement on coadd images.
174 Deprecated
and unused. Should always be `
None`.
176 The schema of the reference catalog, passed to the constructor of the
177 references subtask. Optional, but must be specified
if ``initInputs``
178 is not;
if both are specified, ``initInputs`` takes precedence.
180 Dictionary that can contain a key ``inputSchema`` containing the
181 schema. If present will override the value of ``refSchema``.
183 Keyword arguments are passed to the supertask constructor.
186 ConfigClass = ForcedPhotCoaddConfig
187 _DefaultName = "forcedPhotCoadd"
188 dataPrefix =
"deepCoadd_"
190 def __init__(self, butler=None, refSchema=None, initInputs=None, **kwds):
191 super().__init__(**kwds)
193 if butler
is not None:
194 warnings.warn(
"The 'butler' parameter is no longer used and can be safely removed.",
195 category=FutureWarning, stacklevel=2)
198 if initInputs
is not None:
199 refSchema = initInputs[
'inputSchema'].schema
201 if refSchema
is None:
202 raise ValueError(
"No reference schema provided.")
203 self.makeSubtask(
"measurement", refSchema=refSchema)
206 if self.config.doApCorr:
207 self.makeSubtask(
"applyApCorr", schema=self.measurement.schema)
208 self.makeSubtask(
'catalogCalculation', schema=self.measurement.schema)
211 def runQuantum(self, butlerQC, inputRefs, outputRefs):
212 inputs = butlerQC.get(inputRefs)
214 refCatInBand = inputs.pop(
'refCatInBand')
215 if self.config.footprintDatasetName ==
"ScarletModelData":
216 footprintData = inputs.pop(
"scarletModels")
217 elif self.config.footprintDatasetName ==
"DeblendedFlux":
218 footprintData = inputs.pop(
"footprintCatIndBand")
221 inputs[
'measCat'], inputs[
'exposureId'] = self.generateMeasCat(inputRefs.exposure.dataId,
228 outputs = self.run(**inputs)
230 if self.config.footprintDatasetName ==
"ScarletModelData" and self.config.doStripFootprints:
231 sources = outputs.measCat
232 for source
in sources[sources[
"parent"] != 0]:
233 source.setFootprint(
None)
234 butlerQC.put(outputs, outputRefs)
236 def generateMeasCat(self, exposureDataId, exposure, refCat, refCatInBand, refWcs, idPackerName,
238 """Generate a measurement catalog.
242 exposureDataId : `DataId`
243 Butler dataId for this exposure.
245 Exposure to generate the catalog
for.
247 Catalog of shapes
and positions at which to force photometry.
249 Catalog of shapes
and position
in the band forced photometry
is
250 currently being performed
251 refWcs : `lsst.afw.image.SkyWcs`
252 Reference world coordinate system.
254 Type of ID packer to construct
from the registry.
256 Either the scarlet data models
or the deblended catalog
257 containing footprints.
258 If `footprintData`
is `
None` then the footprints contained
259 in `refCatInBand` are used.
264 Catalog of forced sources to measure.
266 Unique binary id associated
with the input exposure
271 Raised
if a footprint
with a given source id was
in the reference
272 catalog but
not in the reference catalog
in band (meaning there
273 was some sort of mismatch
in the two input catalogs)
275 exposureIdInfo = ExposureIdInfo.fromDataId(exposureDataId, idPackerName)
276 idFactory = exposureIdInfo.makeSourceIdFactory()
278 measCat = self.measurement.generateMeasCat(exposure, refCat, refWcs,
281 if self.config.footprintDatasetName ==
"ScarletModelData":
283 self._attachScarletFootprints(
285 modelData=footprintData,
287 band=exposureDataId[
"band"]
290 if self.config.footprintDatasetName
is None:
291 footprintCat = refCatInBand
293 footprintCat = footprintData
294 for srcRecord
in measCat:
295 fpRecord = footprintCat.find(srcRecord.getId())
297 raise LookupError(
"Cannot find Footprint for source {}; please check that {} "
298 "IDs are compatible with reference source IDs"
299 .format(srcRecord.getId(), footprintCat))
300 srcRecord.setFootprint(fpRecord.getFootprint())
301 return measCat, exposureIdInfo.expId
303 def run(self, measCat, exposure, refCat, refWcs, exposureId=None):
304 """Perform forced measurement on a single exposure.
309 The measurement catalog, based on the sources listed in the
312 The measurement image upon which to perform forced detection.
314 The reference catalog of sources to measure.
315 refWcs : `lsst.afw.image.SkyWcs`
316 The WCS
for the references.
318 Optional unique exposureId used
for random seed
in measurement
323 result : ~`lsst.pipe.base.Struct`
324 Structure
with fields:
327 Catalog of forced measurement results
330 self.measurement.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
331 if self.config.doApCorr:
332 self.applyApCorr.run(
334 apCorrMap=exposure.getInfo().getApCorrMap()
336 self.catalogCalculation.run(measCat)
338 return pipeBase.Struct(measCat=measCat)
340 def _attachScarletFootprints(self, catalog, modelData, exposure, band):
341 """Attach scarlet models as HeavyFootprints
343 if self.config.doConserveFlux:
344 redistributeImage = exposure.image
346 redistributeImage =
None
348 modelData.updateCatalogFootprints(
351 psfModel=exposure.getPsf(),
352 redistributeImage=redistributeImage,
353 removeScarletData=
True,
354 updateFluxColumns=
False,
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Defines the fields and offsets for a table.