lsst.pipe.tasks ge89ed3dd84+28a26a0b34
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
22import numpy as np
23
24from lsst.pipe.base import (Struct, PipelineTask, PipelineTaskConfig, PipelineTaskConnections)
25import lsst.pipe.base.connectionTypes as cT
26
27from lsst.pex.config import ConfigurableField
28from lsst.meas.deblender import SourceDeblendTask
29from lsst.meas.extensions.scarlet import ScarletDeblendTask
30from lsst.obs.base import ExposureIdInfo
31
32import lsst.afw.image as afwImage
33import lsst.afw.table as afwTable
34
35from .makeCoaddTempExp import reorderRefs
36
37__all__ = ("DeblendCoaddSourcesSingleConfig", "DeblendCoaddSourcesSingleTask",
38 "DeblendCoaddSourcesMultiConfig", "DeblendCoaddSourcesMultiTask")
39
40
41deblendBaseTemplates = {"inputCoaddName": "deep", "outputCoaddName": "deep"}
42
43
44class DeblendCoaddSourceSingleConnections(PipelineTaskConnections,
45 dimensions=("tract", "patch", "band", "skymap"),
46 defaultTemplates=deblendBaseTemplates):
47 inputSchema = cT.InitInput(
48 doc="Input schema to use in the deblend catalog",
49 name="{inputCoaddName}Coadd_mergeDet_schema",
50 storageClass="SourceCatalog"
51 )
52 peakSchema = cT.InitInput(
53 doc="Schema of the footprint peak catalogs",
54 name="{inputCoaddName}Coadd_peak_schema",
55 storageClass="PeakCatalog"
56 )
57 mergedDetections = cT.Input(
58 doc="Detection catalog merged across bands",
59 name="{inputCoaddName}Coadd_mergeDet",
60 storageClass="SourceCatalog",
61 dimensions=("tract", "patch", "skymap")
62 )
63 coadd = cT.Input(
64 doc="Exposure on which to run deblending",
65 name="{inputCoaddName}Coadd_calexp",
66 storageClass="ExposureF",
67 dimensions=("tract", "patch", "band", "skymap")
68 )
69 measureCatalog = cT.Output(
70 doc="The output measurement catalog of deblended sources",
71 name="{outputCoaddName}Coadd_deblendedFlux",
72 storageClass="SourceCatalog",
73 dimensions=("tract", "patch", "band", "skymap")
74 )
75 outputSchema = cT.InitOutput(
76 doc="Output of the schema used in deblending task",
77 name="{outputCoaddName}Coadd_deblendedFlux_schema",
78 storageClass="SourceCatalog"
79 )
80
81 def setDefaults(self):
82 super().setDefaults()
83 self.singleBandDeblend.propagateAllPeaks = True
84
85
86class DeblendCoaddSourcesSingleConfig(PipelineTaskConfig,
87 pipelineConnections=DeblendCoaddSourceSingleConnections):
88 singleBandDeblend = ConfigurableField(
89 target=SourceDeblendTask,
90 doc="Task to deblend an image in one band"
91 )
92
93
94class DeblendCoaddSourcesMultiConnections(PipelineTaskConnections,
95 dimensions=("tract", "patch", "skymap"),
96 defaultTemplates=deblendBaseTemplates):
97 inputSchema = cT.InitInput(
98 doc="Input schema to use in the deblend catalog",
99 name="{inputCoaddName}Coadd_mergeDet_schema",
100 storageClass="SourceCatalog"
101 )
102 peakSchema = cT.InitInput(
103 doc="Schema of the footprint peak catalogs",
104 name="{inputCoaddName}Coadd_peak_schema",
105 storageClass="PeakCatalog"
106 )
107 mergedDetections = cT.Input(
108 doc="Detection catalog merged across bands",
109 name="{inputCoaddName}Coadd_mergeDet",
110 storageClass="SourceCatalog",
111 dimensions=("tract", "patch", "skymap")
112 )
113 coadds = cT.Input(
114 doc="Exposure on which to run deblending",
115 name="{inputCoaddName}Coadd_calexp",
116 storageClass="ExposureF",
117 multiple=True,
118 dimensions=("tract", "patch", "band", "skymap")
119 )
120 outputSchema = cT.InitOutput(
121 doc="Output of the schema used in deblending task",
122 name="{outputCoaddName}Coadd_deblendedFlux_schema",
123 storageClass="SourceCatalog"
124 )
125 templateCatalogs = cT.Output(
126 doc="Template catalogs produced by multiband deblending",
127 name="{outputCoaddName}Coadd_deblendedFlux",
128 storageClass="SourceCatalog",
129 dimensions=("tract", "patch", "band", "skymap"),
130 multiple=True
131 )
132
133
134class DeblendCoaddSourcesMultiConfig(PipelineTaskConfig,
135 pipelineConnections=DeblendCoaddSourcesMultiConnections):
136 multibandDeblend = ConfigurableField(
137 target=ScarletDeblendTask,
138 doc="Task to deblend an images in multiple bands"
139 )
140
141
142class DeblendCoaddSourcesBaseTask(PipelineTask):
143 def __init__(self, initInputs, **kwargs):
144 super().__init__(initInputs=initInputs, **kwargs)
145 schema = initInputs["inputSchema"].schema
146 self.peakSchemapeakSchema = initInputs["peakSchema"].schema
147 self.schemaMapperschemaMapper = afwTable.SchemaMapper(schema)
148 self.schemaMapperschemaMapper.addMinimalSchema(schema)
149 self.schemaschema = self.schemaMapperschemaMapper.getOutputSchema()
150
151 def runQuantum(self, butlerQC, inputRefs, outputRefs):
152 inputs = butlerQC.get(inputRefs)
153 inputs["idFactory"] = ExposureIdInfo.fromDataId(
154 butlerQC.quantum.dataId,
155 "tract_patch"
156 ).makeSourceIdFactory()
157 outputs = self.run(**inputs)
158 butlerQC.put(outputs, outputRefs)
159
160 def _makeSourceCatalog(self, mergedDetections, idFactory):
161 # There may be gaps in the mergeDet catalog, which will cause the
162 # source ids to be inconsistent. So we update the id factory
163 # with the largest id already in the catalog.
164 maxId = np.max(mergedDetections["id"])
165 idFactory.notify(maxId)
166 table = afwTable.SourceTable.make(self.schemaschema, idFactory)
167 sources = afwTable.SourceCatalog(table)
168 sources.extend(mergedDetections, self.schemaMapperschemaMapper)
169 return sources
170
171
173 ConfigClass = DeblendCoaddSourcesSingleConfig
174 _DefaultName = "deblendCoaddSourcesSingle"
175
176 def __init__(self, initInputs, **kwargs):
177 super().__init__(initInputs=initInputs, **kwargs)
178 self.makeSubtask("singleBandDeblend", schema=self.schemaschema, peakSchema=self.peakSchemapeakSchema)
179 self.outputSchemaoutputSchema = afwTable.SourceCatalog(self.schemaschema)
180
181 def run(self, coadd, mergedDetections, idFactory):
182 sources = self._makeSourceCatalog_makeSourceCatalog(mergedDetections, idFactory)
183 self.singleBandDeblend.run(coadd, sources)
184 if not sources.isContiguous():
185 sources = sources.copy(deep=True)
186 return Struct(measureCatalog=sources)
187
188
190 ConfigClass = DeblendCoaddSourcesMultiConfig
191 _DefaultName = "deblendCoaddSourcesMulti"
192
193 def __init__(self, initInputs, **kwargs):
194 super().__init__(initInputs=initInputs, **kwargs)
195 self.makeSubtask("multibandDeblend", schema=self.schemaschema, peakSchema=self.peakSchemapeakSchema)
196 self.outputSchemaoutputSchema = afwTable.SourceCatalog(self.schemaschema)
197
198 def runQuantum(self, butlerQC, inputRefs, outputRefs):
199 # Obtain the list of bands, sort them (alphabetically), then reorder
200 # all input lists to match this band order.
201 bandOrder = [dRef.dataId["band"] for dRef in inputRefs.coadds]
202 bandOrder.sort()
203 inputRefs = reorderRefs(inputRefs, bandOrder, dataIdKey="band")
204 inputs = butlerQC.get(inputRefs)
205 exposureIdInfo = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId, "tract_patch")
206 inputs["idFactory"] = exposureIdInfo.makeSourceIdFactory()
207 inputs["filters"] = [dRef.dataId["band"] for dRef in inputRefs.coadds]
208 outputs = self.runrun(**inputs)
209 for outRef in outputRefs.templateCatalogs:
210 band = outRef.dataId['band']
211 if (catalog := outputs.templateCatalogs.get(band)) is not None:
212 butlerQC.put(catalog, outRef)
213
214 def run(self, coadds, filters, mergedDetections, idFactory):
215 sources = self._makeSourceCatalog_makeSourceCatalog(mergedDetections, idFactory)
216 multiExposure = afwImage.MultibandExposure.fromExposures(filters, coadds)
217 templateCatalogs = self.multibandDeblend.run(multiExposure, sources)
218 retStruct = Struct(templateCatalogs=templateCatalogs)
219 return retStruct