LSST Applications g04e9c324dd+8c5ae1fdc5,g134cb467dc+1b3060144d,g18429d2f64+f642bf4753,g199a45376c+0ba108daf9,g1fd858c14a+2dcf163641,g262e1987ae+7b8c96d2ca,g29ae962dfc+3bd6ecb08a,g2cef7863aa+aef1011c0b,g35bb328faa+8c5ae1fdc5,g3fd5ace14f+53e1a9e7c5,g4595892280+fef73a337f,g47891489e3+2efcf17695,g4d44eb3520+642b70b07e,g53246c7159+8c5ae1fdc5,g67b6fd64d1+2efcf17695,g67fd3c3899+b70e05ef52,g74acd417e5+317eb4c7d4,g786e29fd12+668abc6043,g87389fa792+8856018cbb,g89139ef638+2efcf17695,g8d7436a09f+3be3c13596,g8ea07a8fe4+9f5ccc88ac,g90f42f885a+a4e7b16d9b,g97be763408+ad77d7208f,g9dd6db0277+b70e05ef52,ga681d05dcb+a3f46e7fff,gabf8522325+735880ea63,gac2eed3f23+2efcf17695,gb89ab40317+2efcf17695,gbf99507273+8c5ae1fdc5,gd8ff7fe66e+b70e05ef52,gdab6d2f7ff+317eb4c7d4,gdc713202bf+b70e05ef52,gdfd2d52018+b10e285e0f,ge365c994fd+310e8507c4,ge410e46f29+2efcf17695,geaed405ab2+562b3308c0,gffca2db377+8c5ae1fdc5,w.2025.35
LSST Data Management Base Package
Loading...
Searching...
No Matches
deblendCoaddSourcesPipeline.py
Go to the documentation of this file.
1# This file is part of pipe_tasks.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22__all__ = ["DeblendCoaddSourcesSingleConfig", "DeblendCoaddSourcesSingleTask",
23 "DeblendCoaddSourcesMultiConfig", "DeblendCoaddSourcesMultiTask"]
24
25import numpy as np
26
27from deprecated.sphinx import deprecated
28
29from lsst.pipe.base import (Struct, PipelineTask, PipelineTaskConfig, PipelineTaskConnections)
30import lsst.pipe.base.connectionTypes as cT
31
32from lsst.pex.config import ConfigurableField, Field
33from lsst.meas.base import SkyMapIdGeneratorConfig
34from lsst.meas.deblender import SourceDeblendTask
35from lsst.meas.extensions.scarlet import ScarletDeblendTask
36
37import lsst.afw.image as afwImage
38import lsst.afw.table as afwTable
39
40from .coaddBase import reorderRefs
41
42
43deblendBaseTemplates = {"inputCoaddName": "deep", "outputCoaddName": "deep"}
44
45
46class DeblendCoaddSourceSingleConnections(PipelineTaskConnections,
47 dimensions=("tract", "patch", "band", "skymap"),
48 defaultTemplates=deblendBaseTemplates):
49 inputSchema = cT.InitInput(
50 doc="Input schema to use in the deblend catalog",
51 name="{inputCoaddName}Coadd_mergeDet_schema",
52 storageClass="SourceCatalog"
53 )
54 peakSchema = cT.InitInput(
55 doc="Schema of the footprint peak catalogs",
56 name="{inputCoaddName}Coadd_peak_schema",
57 storageClass="PeakCatalog"
58 )
59 mergedDetections = cT.Input(
60 doc="Detection catalog merged across bands",
61 name="{inputCoaddName}Coadd_mergeDet",
62 storageClass="SourceCatalog",
63 dimensions=("tract", "patch", "skymap")
64 )
65 coadd = cT.Input(
66 doc="Exposure on which to run deblending",
67 name="{inputCoaddName}Coadd_calexp",
68 storageClass="ExposureF",
69 dimensions=("tract", "patch", "band", "skymap")
70 )
71 measureCatalog = cT.Output(
72 doc="The output measurement catalog of deblended sources",
73 name="{outputCoaddName}Coadd_deblendedFlux",
74 storageClass="SourceCatalog",
75 dimensions=("tract", "patch", "band", "skymap")
76 )
77 outputSchema = cT.InitOutput(
78 doc="Output of the schema used in deblending task",
79 name="{outputCoaddName}Coadd_deblendedFlux_schema",
80 storageClass="SourceCatalog"
81 )
82
83
84class DeblendCoaddSourcesSingleConfig(PipelineTaskConfig,
85 pipelineConnections=DeblendCoaddSourceSingleConnections):
86 singleBandDeblend = ConfigurableField(
87 target=SourceDeblendTask,
88 doc="Task to deblend an image in one band"
89 )
90 idGenerator = SkyMapIdGeneratorConfig.make_field()
91
92 def setDefaults(self):
93 super().setDefaults()
94 self.singleBandDeblend.propagateAllPeaks = True
95
96
97class DeblendCoaddSourcesMultiConnections(PipelineTaskConnections,
98 dimensions=("tract", "patch", "skymap"),
99 defaultTemplates=deblendBaseTemplates):
100 inputSchema = cT.InitInput(
101 doc="Input schema to use in the deblend catalog",
102 name="{inputCoaddName}Coadd_mergeDet_schema",
103 storageClass="SourceCatalog"
104 )
105 peakSchema = cT.InitInput(
106 doc="Schema of the footprint peak catalogs",
107 name="{inputCoaddName}Coadd_peak_schema",
108 storageClass="PeakCatalog"
109 )
110 mergedDetections = cT.Input(
111 doc="Detection catalog merged across bands",
112 name="{inputCoaddName}Coadd_mergeDet",
113 storageClass="SourceCatalog",
114 dimensions=("tract", "patch", "skymap")
115 )
116 coadds = cT.Input(
117 doc="Exposure on which to run deblending",
118 name="{inputCoaddName}Coadd_calexp",
119 storageClass="ExposureF",
120 multiple=True,
121 dimensions=("tract", "patch", "band", "skymap")
122 )
123 coadds_cell = cT.Input(
124 doc="Exposure on which to run deblending",
125 name="{inputCoaddName}CoaddCell",
126 storageClass="MultipleCellCoadd",
127 multiple=True,
128 dimensions=("tract", "patch", "band", "skymap")
129 )
130 backgrounds = cT.Input(
131 doc="Background model to subtract from the cell-based coadd",
132 name="{inputCoaddName}Coadd_calexp_background",
133 storageClass="Background",
134 multiple=True,
135 dimensions=("tract", "patch", "band", "skymap")
136 )
137 deconvolvedCoadds = cT.Input(
138 doc="Deconvolved coadds",
139 name="deconvolved_{inputCoaddName}_coadd",
140 storageClass="ExposureF",
141 multiple=True,
142 dimensions=("tract", "patch", "band", "skymap")
143 )
144 outputSchema = cT.InitOutput(
145 doc="Output of the schema used in deblending task",
146 name="{outputCoaddName}Coadd_deblendedFlux_schema",
147 storageClass="SourceCatalog"
148 )
149 # TODO[DM-47405]: remove this deprecated connection.
150 fluxCatalogs = cT.Output(
151 doc="Flux weighted catalogs produced by multiband deblending",
152 name="{outputCoaddName}Coadd_deblendedFlux",
153 storageClass="SourceCatalog",
154 dimensions=("tract", "patch", "band", "skymap"),
155 multiple=True,
156 deprecated="Deprecated and unused; will be removed after v29."
157 )
158 # TODO[DM-47405]: remove this deprecated connection.
159 templateCatalogs = cT.Output(
160 doc="Template catalogs produced by multiband deblending",
161 name="{outputCoaddName}Coadd_deblendedModel",
162 storageClass="SourceCatalog",
163 dimensions=("tract", "patch", "band", "skymap"),
164 multiple=True,
165 deprecated="Deprecated and unused; will be removed after v29."
166 )
167 deblendedCatalog = cT.Output(
168 doc="Catalogs produced by multiband deblending",
169 name="{outputCoaddName}Coadd_deblendedCatalog",
170 storageClass="SourceCatalog",
171 dimensions=("tract", "patch", "skymap"),
172 )
173 scarletModelData = cT.Output(
174 doc="Multiband scarlet models produced by the deblender",
175 name="{outputCoaddName}Coadd_scarletModelData",
176 storageClass="ScarletModelData",
177 dimensions=("tract", "patch", "skymap"),
178 )
179 objectParents = cT.Output(
180 doc="Parents of the deblended objects",
181 name="object_parents",
182 storageClass="SourceCatalog",
183 dimensions=("tract", "patch", "skymap"),
184 )
185
186 def __init__(self, *, config=None):
187 super().__init__(config=config)
188 del self.fluxCatalogs
189 del self.templateCatalogs
190
191 if config:
192 if config.useCellCoadds:
193 del self.coadds
194 else:
195 del self.coadds_cell
196 del self.backgrounds
197
198
199class DeblendCoaddSourcesMultiConfig(PipelineTaskConfig,
200 pipelineConnections=DeblendCoaddSourcesMultiConnections):
201 useCellCoadds = Field[bool](
202 doc="Use cell-based coadds instead of regular coadds?",
203 default=False,
204 )
205 multibandDeblend = ConfigurableField(
206 target=ScarletDeblendTask,
207 doc="Task to deblend an images in multiple bands"
208 )
209 idGenerator = SkyMapIdGeneratorConfig.make_field()
210
211
212# TODO[DM-47797] Remove this task.
213@deprecated(
214 "Support for the old single-band deblender on coadds will be removed "
215 "after v29.",
216 version="v29",
217 category=FutureWarning
218)
219class DeblendCoaddSourcesBaseTask(PipelineTask):
220 def __init__(self, initInputs, **kwargs):
221 super().__init__(initInputs=initInputs, **kwargs)
222 schema = initInputs["inputSchema"].schema
223 self.peakSchema = initInputs["peakSchema"].schema
225 self.schemaMapper.addMinimalSchema(schema)
226 self.schema = self.schemaMapper.getOutputSchema()
227
228 def runQuantum(self, butlerQC, inputRefs, outputRefs):
229 inputs = butlerQC.get(inputRefs)
230 inputs["idFactory"] = self.config.idGenerator.apply(butlerQC.quantum.dataId).make_table_id_factory()
231 outputs = self.run(**inputs)
232 butlerQC.put(outputs, outputRefs)
233
234 def _makeSourceCatalog(self, mergedDetections, idFactory):
235 # There may be gaps in the mergeDet catalog, which will cause the
236 # source ids to be inconsistent. So we update the id factory
237 # with the largest id already in the catalog.
238 maxId = np.max(mergedDetections["id"])
239 idFactory.notify(maxId)
240 table = afwTable.SourceTable.make(self.schema, idFactory)
241 sources = afwTable.SourceCatalog(table)
242 sources.extend(mergedDetections, self.schemaMapper)
243 return sources
244
245
246# TODO[DM-47797] Remove this task, its connections, and its config.
247@deprecated(
248 "Support for the old single-band deblender on coadds will be removed "
249 "after v29.",
250 version="v29",
251 category=FutureWarning
252)
254 ConfigClass = DeblendCoaddSourcesSingleConfig
255 _DefaultName = "deblendCoaddSourcesSingle"
256
257 def __init__(self, initInputs, **kwargs):
258 super().__init__(initInputs=initInputs, **kwargs)
259 self.makeSubtask("singleBandDeblend", schema=self.schema, peakSchema=self.peakSchema)
261
262 def run(self, coadd, mergedDetections, idFactory):
263 sources = self._makeSourceCatalog(mergedDetections, idFactory)
264 self.singleBandDeblend.run(coadd, sources)
265 if not sources.isContiguous():
266 sources = sources.copy(deep=True)
267 return Struct(measureCatalog=sources)
268
269
271 ConfigClass = DeblendCoaddSourcesMultiConfig
272 _DefaultName = "deblendCoaddSourcesMulti"
273
274 def __init__(self, initInputs, **kwargs):
275 super().__init__(initInputs=initInputs, **kwargs)
276 schema = initInputs["inputSchema"].schema
277 self.peakSchema = initInputs["peakSchema"].schema
279 self.schemaMapper.addMinimalSchema(schema)
280 self.schema = self.schemaMapper.getOutputSchema()
281 self.makeSubtask("multibandDeblend", schema=self.schema, peakSchema=self.peakSchema)
283
284 def runQuantum(self, butlerQC, inputRefs, outputRefs):
285 # Obtain the list of bands, sort them (alphabetically), then reorder
286 # all input lists to match this band order.
287 coaddRefs = inputRefs.coadds_cell if self.config.useCellCoadds else inputRefs.coadds
288 bandOrder = [dRef.dataId["band"] for dRef in coaddRefs]
289 bandOrder.sort()
290 inputRefs = reorderRefs(inputRefs, bandOrder, dataIdKey="band")
291 inputs = butlerQC.get(inputRefs)
292 bands = [dRef.dataId["band"] for dRef in coaddRefs]
293 mergedDetections = inputs.pop("mergedDetections")
294 if self.config.useCellCoadds:
295 exposures = [mcc.stitch().asExposure() for mcc in inputs.pop("coadds_cell")]
296 backgrounds = inputs.pop("backgrounds")
297 for exposure, background in zip(exposures, backgrounds):
298 exposure.image -= background.getImage()
299 coadds = exposures
300 else:
301 coadds = inputs.pop("coadds")
302
303 # Ensure that the coadd bands and deconvolved coadd bands match
304 deconvBands = [dRef.dataId["band"] for dRef in inputRefs.deconvolvedCoadds]
305 if bands != deconvBands:
306 self.log.error("Coadd bands %s != deconvolved coadd bands %s", bands, deconvBands)
307 raise RuntimeError("Number of coadd bands and deconvolved coadd bands do not match")
308
309 deconvolvedCoadds = inputs.pop("deconvolvedCoadds")
310
311 # Check that all inputs have been extracted correctly.
312 assert not inputs, "runQuantum got extra inputs"
313
314 outputs = self.run(
315 coadds=coadds,
316 bands=bands,
317 mergedDetections=mergedDetections,
318 idFactory=self.config.idGenerator.apply(butlerQC.quantum.dataId).make_table_id_factory(),
319 deconvolvedCoadds=deconvolvedCoadds,
320 )
321 butlerQC.put(outputs, outputRefs)
322
323 def run(self, coadds, bands, mergedDetections, deconvolvedCoadds, idFactory):
324 sources = self._makeSourceCatalog(mergedDetections, idFactory)
325 multiExposure = afwImage.MultibandExposure.fromExposures(bands, coadds)
326 mDeconvolved = afwImage.MultibandExposure.fromExposures(bands, deconvolvedCoadds)
327 result = self.multibandDeblend.run(multiExposure, mDeconvolved, sources)
328 return result
329
330 def _makeSourceCatalog(self, mergedDetections, idFactory):
331 # There may be gaps in the mergeDet catalog, which will cause the
332 # source ids to be inconsistent. So we update the id factory
333 # with the largest id already in the catalog.
334 maxId = np.max(mergedDetections["id"])
335 idFactory.notify(maxId)
336 table = afwTable.SourceTable.make(self.schema, idFactory)
337 sources = afwTable.SourceCatalog(table)
338 sources.extend(mergedDetections, self.schemaMapper)
339 return sources
A mapping between the keys of two Schemas, used to copy data between them.
run(self, coadds, bands, mergedDetections, deconvolvedCoadds, idFactory)