Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

35__all__ = ("DeblendCoaddSourcesSingleConfig", "DeblendCoaddSourcesSingleTask", 

36 "DeblendCoaddSourcesMultiConfig", "DeblendCoaddSourcesMultiTask") 

37 

38 

39deblendBaseTemplates = {"inputCoaddName": "deep", "outputCoaddName": "deep"} 

40 

41 

42class DeblendCoaddSourceSingleConnections(PipelineTaskConnections, 

43 dimensions=("tract", "patch", "band", "skymap"), 

44 defaultTemplates=deblendBaseTemplates): 

45 inputSchema = cT.InitInput( 

46 doc="Input schema to use in the deblend catalog", 

47 name="{inputCoaddName}Coadd_mergeDet_schema", 

48 storageClass="SourceCatalog" 

49 ) 

50 peakSchema = cT.InitInput( 

51 doc="Schema of the footprint peak catalogs", 

52 name="{inputCoaddName}Coadd_peak_schema", 

53 storageClass="PeakCatalog" 

54 ) 

55 mergedDetections = cT.Input( 

56 doc="Detection catalog merged across bands", 

57 name="{inputCoaddName}Coadd_mergeDet", 

58 storageClass="SourceCatalog", 

59 dimensions=("tract", "patch", "skymap") 

60 ) 

61 coadd = cT.Input( 

62 doc="Exposure on which to run deblending", 

63 name="{inputCoaddName}Coadd_calexp", 

64 storageClass="ExposureF", 

65 dimensions=("tract", "patch", "band", "skymap") 

66 ) 

67 measureCatalog = cT.Output( 

68 doc="The output measurement catalog of deblended sources", 

69 name="{outputCoaddName}Coadd_deblendedFlux", 

70 storageClass="SourceCatalog", 

71 dimensions=("tract", "patch", "band", "skymap") 

72 ) 

73 outputSchema = cT.InitOutput( 

74 doc="Output of the schema used in deblending task", 

75 name="{outputCoaddName}Coadd_deblendedFlux_schema", 

76 storageClass="SourceCatalog" 

77 ) 

78 

79 def setDefaults(self): 

80 super().setDefaults() 

81 self.singleBandDeblend.propagateAllPeaks = True 

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 

91 

92class DeblendCoaddSourcesMultiConnections(PipelineTaskConnections, 

93 dimensions=("tract", "patch", "skymap"), 

94 defaultTemplates=deblendBaseTemplates): 

95 inputSchema = cT.InitInput( 

96 doc="Input schema to use in the deblend catalog", 

97 name="{inputCoaddName}Coadd_mergeDet_schema", 

98 storageClass="SourceCatalog" 

99 ) 

100 peakSchema = cT.InitInput( 

101 doc="Schema of the footprint peak catalogs", 

102 name="{inputCoaddName}Coadd_peak_schema", 

103 storageClass="PeakCatalog" 

104 ) 

105 mergedDetections = cT.Input( 

106 doc="Detection catalog merged across bands", 

107 name="{inputCoaddName}Coadd_mergeDet", 

108 storageClass="SourceCatalog", 

109 dimensions=("tract", "patch", "skymap") 

110 ) 

111 coadds = cT.Input( 

112 doc="Exposure on which to run deblending", 

113 name="{inputCoaddName}Coadd_calexp", 

114 storageClass="ExposureF", 

115 multiple=True, 

116 dimensions=("tract", "patch", "band", "skymap") 

117 ) 

118 outputSchema = cT.InitOutput( 

119 doc="Output of the schema used in deblending task", 

120 name="{outputCoaddName}Coadd_deblendedFlux_schema", 

121 storageClass="SourceCatalog" 

122 ) 

123 templateCatalogs = cT.Output( 

124 doc="Template catalogs produced by multiband deblending", 

125 name="{outputCoaddName}Coadd_deblendedFlux", 

126 storageClass="SourceCatalog", 

127 dimensions=("tract", "patch", "band", "skymap"), 

128 multiple=True 

129 ) 

130 

131 

132class DeblendCoaddSourcesMultiConfig(PipelineTaskConfig, 

133 pipelineConnections=DeblendCoaddSourcesMultiConnections): 

134 multibandDeblend = ConfigurableField( 

135 target=ScarletDeblendTask, 

136 doc="Task to deblend an images in multiple bands" 

137 ) 

138 

139 

140class DeblendCoaddSourcesBaseTask(PipelineTask): 

141 def __init__(self, initInputs, **kwargs): 

142 super().__init__(initInputs=initInputs, **kwargs) 

143 schema = initInputs["inputSchema"].schema 

144 self.peakSchema = initInputs["peakSchema"].schema 

145 self.schemaMapper = afwTable.SchemaMapper(schema) 

146 self.schemaMapper.addMinimalSchema(schema) 

147 self.schema = self.schemaMapper.getOutputSchema() 

148 

149 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

150 inputs = butlerQC.get(inputRefs) 

151 inputs["idFactory"] = ExposureIdInfo.fromDataId( 

152 butlerQC.quantum.dataId, 

153 "tract_patch" 

154 ).makeSourceIdFactory() 

155 outputs = self.run(**inputs) 

156 butlerQC.put(outputs, outputRefs) 

157 

158 def _makeSourceCatalog(self, mergedDetections, idFactory): 

159 # There may be gaps in the mergeDet catalog, which will cause the 

160 # source ids to be inconsistent. So we update the id factory 

161 # with the largest id already in the catalog. 

162 maxId = np.max(mergedDetections["id"]) 

163 idFactory.notify(maxId) 

164 table = afwTable.SourceTable.make(self.schema, idFactory) 

165 sources = afwTable.SourceCatalog(table) 

166 sources.extend(mergedDetections, self.schemaMapper) 

167 return sources 

168 

169 

170class DeblendCoaddSourcesSingleTask(DeblendCoaddSourcesBaseTask): 

171 ConfigClass = DeblendCoaddSourcesSingleConfig 

172 _DefaultName = "deblendCoaddSourcesSingle" 

173 

174 def __init__(self, initInputs, **kwargs): 

175 super().__init__(initInputs=initInputs, **kwargs) 

176 self.makeSubtask("singleBandDeblend", schema=self.schema, peakSchema=self.peakSchema) 

177 self.outputSchema = afwTable.SourceCatalog(self.schema) 

178 

179 def run(self, coadd, mergedDetections, idFactory): 

180 sources = self._makeSourceCatalog(mergedDetections, idFactory) 

181 self.singleBandDeblend.run(coadd, sources) 

182 if not sources.isContiguous(): 

183 sources = sources.copy(deep=True) 

184 return Struct(measureCatalog=sources) 

185 

186 

187class DeblendCoaddSourcesMultiTask(DeblendCoaddSourcesBaseTask): 

188 ConfigClass = DeblendCoaddSourcesMultiConfig 

189 _DefaultName = "deblendCoaddSourcesMulti" 

190 

191 def __init__(self, initInputs, **kwargs): 

192 super().__init__(initInputs=initInputs, **kwargs) 

193 self.makeSubtask("multibandDeblend", schema=self.schema, peakSchema=self.peakSchema) 

194 self.outputSchema = afwTable.SourceCatalog(self.schema) 

195 

196 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

197 inputs = butlerQC.get(inputRefs) 

198 exposureIdInfo = ExposureIdInfo.fromDataId(butlerQC.quantum.dataId, "tract_patch") 

199 inputs["idFactory"] = exposureIdInfo.makeSourceIdFactory() 

200 inputs["filters"] = [dRef.dataId["band"] for dRef in inputRefs.coadds] 

201 outputs = self.run(**inputs) 

202 for outRef in outputRefs.templateCatalogs: 

203 band = outRef.dataId['band'] 

204 if (catalog := outputs.templateCatalogs.get(band)) is not None: 

205 butlerQC.put(catalog, outRef) 

206 

207 def run(self, coadds, filters, mergedDetections, idFactory): 

208 sources = self._makeSourceCatalog(mergedDetections, idFactory) 

209 multiExposure = afwImage.MultibandExposure.fromExposures(filters, coadds) 

210 templateCatalogs = self.multibandDeblend.run(multiExposure, sources) 

211 retStruct = Struct(templateCatalogs=templateCatalogs) 

212 return retStruct