Coverage for python/lsst/pipe/tasks/metrics.py: 48%

57 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-07-30 11:31 +0000

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__ = [ 

23 "NumberDeblendedSourcesMetricTask", "NumberDeblendedSourcesMetricConfig", 

24 "NumberDeblendChildSourcesMetricTask", "NumberDeblendChildSourcesMetricConfig", 

25] 

26 

27 

28import numpy as np 

29import astropy.units as u 

30 

31from lsst.pipe.base import Struct, connectionTypes 

32from lsst.verify import Measurement 

33from lsst.verify.gen2tasks import register 

34from lsst.verify.tasks import MetricTask, MetricConfig, MetricConnections, MetricComputationError 

35 

36 

37class NumberDeblendedSourcesMetricConnections( 

38 MetricConnections, 

39 defaultTemplates={"package": "pipe_tasks", 

40 "metric": "numDeblendedSciSources"}, 

41 dimensions={"instrument", "visit", "detector"}, 

42): 

43 sources = connectionTypes.Input( 

44 doc="The catalog of science sources.", 

45 name="src", 

46 storageClass="SourceCatalog", 

47 dimensions={"instrument", "visit", "detector"}, 

48 ) 

49 

50 

51class NumberDeblendedSourcesMetricConfig( 

52 MetricConfig, 

53 pipelineConnections=NumberDeblendedSourcesMetricConnections): 

54 pass 

55 

56 

57@register("numDeblendedSciSources") 

58class NumberDeblendedSourcesMetricTask(MetricTask): 

59 """Task that computes the number of science sources that have 

60 been deblended. 

61 

62 This task only counts sources that existed prior to any deblending; 

63 i.e., if deblending was run more than once or with multiple iterations, 

64 only the "top-level" deblended sources are counted, and not any 

65 intermediate ones. If sky source information is present, sky sources 

66 are excluded. 

67 

68 Notes 

69 ----- 

70 The task excludes any non-sky sources in the catalog, but it does 

71 not require that the catalog include a ``sky_sources`` column. 

72 """ 

73 _DefaultName = "numDeblendedSciSources" 

74 ConfigClass = NumberDeblendedSourcesMetricConfig 

75 

76 def run(self, sources): 

77 """Count the number of deblended science sources. 

78 

79 Parameters 

80 ---------- 

81 sources : `lsst.afw.table.SourceCatalog` or `None` 

82 A science source catalog, which may be empty or `None`. 

83 

84 Returns 

85 ------- 

86 result : `lsst.pipe.base.Struct` 

87 A `~lsst.pipe.base.Struct` containing the following component: 

88 

89 ``measurement`` 

90 the total number of deblended science sources 

91 (`lsst.verify.Measurement`). If no deblending information is 

92 available in ``sources``, this is `None`. 

93 

94 Raises 

95 ------ 

96 MetricComputationError 

97 Raised if ``sources`` is missing mandatory keys for 

98 source catalogs. 

99 """ 

100 if sources is None: 

101 self.log.info("Nothing to do: no catalogs found.") 

102 meas = None 

103 elif "deblend_nChild" not in sources.schema: 

104 self.log.info("Nothing to do: no deblending performed.") 

105 meas = None 

106 else: 

107 try: 

108 deblended = ((sources["parent"] == 0) # top-level source 

109 & (sources["deblend_nChild"] > 0) # deblended 

110 ) 

111 deblended = _filterSkySources(sources, deblended) 

112 except LookupError as e: 

113 # Probably "parent"; all other columns already checked 

114 raise MetricComputationError("Invalid input catalog") from e 

115 else: 

116 nDeblended = np.count_nonzero(deblended) 

117 meas = Measurement(self.config.metricName, nDeblended * u.dimensionless_unscaled) 

118 

119 return Struct(measurement=meas) 

120 

121 

122class NumberDeblendChildSourcesMetricConnections( 

123 MetricConnections, 

124 defaultTemplates={"package": "pipe_tasks", 

125 "metric": "numDeblendChildSciSources"}, 

126 dimensions={"instrument", "visit", "detector"}, 

127): 

128 sources = connectionTypes.Input( 

129 doc="The catalog of science sources.", 

130 name="src", 

131 storageClass="SourceCatalog", 

132 dimensions={"instrument", "visit", "detector"}, 

133 ) 

134 

135 

136class NumberDeblendChildSourcesMetricConfig( 

137 MetricConfig, 

138 pipelineConnections=NumberDeblendChildSourcesMetricConnections): 

139 pass 

140 

141 

142@register("numDeblendChildSciSources") 

143class NumberDeblendChildSourcesMetricTask(MetricTask): 

144 """Task that computes the number of science sources created 

145 through deblending. 

146 

147 This task only counts final deblending products; i.e., if deblending was 

148 run more than once or with multiple iterations, only the final set of 

149 deblended sources are counted, and not any intermediate ones. 

150 If sky source information is present, sky sources are excluded. 

151 

152 Notes 

153 ----- 

154 The task excludes any non-sky sources in the catalog, but it does 

155 not require that the catalog include a ``sky_sources`` column. 

156 """ 

157 _DefaultName = "numDeblendChildSciSources" 

158 ConfigClass = NumberDeblendChildSourcesMetricConfig 

159 

160 def run(self, sources): 

161 """Count the number of science sources created by deblending. 

162 

163 Parameters 

164 ---------- 

165 sources : `lsst.afw.table.SourceCatalog` or `None` 

166 A science source catalog, which may be empty or `None`. 

167 

168 Returns 

169 ------- 

170 result : `lsst.pipe.base.Struct` 

171 A `~lsst.pipe.base.Struct` containing the following component: 

172 

173 ``measurement`` 

174 the total number of science sources from deblending 

175 (`lsst.verify.Measurement`). If no deblending information is 

176 available in ``sources``, this is `None`. 

177 

178 Raises 

179 ------ 

180 MetricComputationError 

181 Raised if ``sources`` is missing mandatory keys for 

182 source catalogs. 

183 """ 

184 if sources is None: 

185 self.log.info("Nothing to do: no catalogs found.") 

186 meas = None 

187 # Use deblend_parentNChild rather than detect_fromBlend because the 

188 # latter need not be defined in post-deblending catalogs. 

189 elif "deblend_parentNChild" not in sources.schema or "deblend_nChild" not in sources.schema: 

190 self.log.info("Nothing to do: no deblending performed.") 

191 meas = None 

192 else: 

193 try: 

194 children = ((sources["deblend_parentNChild"] > 1) # deblend child 

195 & (sources["deblend_nChild"] == 0) # not deblended 

196 ) 

197 children = _filterSkySources(sources, children) 

198 except LookupError as e: 

199 # Probably "parent"; all other columns already checked 

200 raise MetricComputationError("Invalid input catalog") from e 

201 else: 

202 nChildren = np.count_nonzero(children) 

203 meas = Measurement(self.config.metricName, nChildren * u.dimensionless_unscaled) 

204 

205 return Struct(measurement=meas) 

206 

207 

208def _filterSkySources(catalog, selection): 

209 """Filter out any sky sources from a vector of selected sources. 

210 

211 If no sky source information is available, all sources are assumed to 

212 be non-sky. 

213 

214 Parameters 

215 ---------- 

216 catalog : `lsst.afw.table.SourceCatalog` 

217 The catalog to filter. 

218 selection : `numpy.ndarray` [`bool`], (N,) 

219 A vector of existing source selections, of the same length as 

220 ``catalog``, where selected sources are marked `True`. 

221 

222 Returns 

223 ------- 

224 filtered : `numpy.ndarray` [`bool`], (N,) 

225 A version of ``selection`` with any sky sources filtered out 

226 (set to `False`). May be the same vector as ``selection`` if 

227 no changes were made. 

228 """ 

229 if "sky_source" in catalog.schema: 

230 # E712 is not applicable, because afw.table.SourceRecord.ColumnView 

231 # is not a bool. 

232 return selection & (catalog["sky_source"] == False) # noqa: E712 

233 else: 

234 return selection