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

54 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-12 01:27 -0700

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.tasks import MetricTask, MetricConfig, MetricConnections, MetricComputationError 

34 

35 

36class NumberDeblendedSourcesMetricConnections( 

37 MetricConnections, 

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

39 "metric": "numDeblendedSciSources"}, 

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

41): 

42 sources = connectionTypes.Input( 

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

44 name="src", 

45 storageClass="SourceCatalog", 

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

47 ) 

48 

49 

50class NumberDeblendedSourcesMetricConfig( 

51 MetricConfig, 

52 pipelineConnections=NumberDeblendedSourcesMetricConnections): 

53 pass 

54 

55 

56class NumberDeblendedSourcesMetricTask(MetricTask): 

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

58 been deblended. 

59 

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

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

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

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

64 are excluded. 

65 

66 Notes 

67 ----- 

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

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

70 """ 

71 _DefaultName = "numDeblendedSciSources" 

72 ConfigClass = NumberDeblendedSourcesMetricConfig 

73 

74 def run(self, sources): 

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

76 

77 Parameters 

78 ---------- 

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

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

81 

82 Returns 

83 ------- 

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

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

86 

87 ``measurement`` 

88 the total number of deblended science sources 

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

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

91 

92 Raises 

93 ------ 

94 MetricComputationError 

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

96 source catalogs. 

97 """ 

98 if sources is None: 

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

100 meas = None 

101 elif "deblend_nChild" not in sources.schema: 

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

103 meas = None 

104 else: 

105 try: 

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

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

108 ) 

109 deblended = _filterSkySources(sources, deblended) 

110 except LookupError as e: 

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

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

113 else: 

114 nDeblended = np.count_nonzero(deblended) 

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

116 

117 return Struct(measurement=meas) 

118 

119 

120class NumberDeblendChildSourcesMetricConnections( 

121 MetricConnections, 

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

123 "metric": "numDeblendChildSciSources"}, 

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

125): 

126 sources = connectionTypes.Input( 

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

128 name="src", 

129 storageClass="SourceCatalog", 

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

131 ) 

132 

133 

134class NumberDeblendChildSourcesMetricConfig( 

135 MetricConfig, 

136 pipelineConnections=NumberDeblendChildSourcesMetricConnections): 

137 pass 

138 

139 

140class NumberDeblendChildSourcesMetricTask(MetricTask): 

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

142 through deblending. 

143 

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

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

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

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

148 

149 Notes 

150 ----- 

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

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

153 """ 

154 _DefaultName = "numDeblendChildSciSources" 

155 ConfigClass = NumberDeblendChildSourcesMetricConfig 

156 

157 def run(self, sources): 

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

159 

160 Parameters 

161 ---------- 

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

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

164 

165 Returns 

166 ------- 

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

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

169 

170 ``measurement`` 

171 the total number of science sources from deblending 

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

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

174 

175 Raises 

176 ------ 

177 MetricComputationError 

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

179 source catalogs. 

180 """ 

181 if sources is None: 

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

183 meas = None 

184 # Use deblend_parentNChild rather than detect_fromBlend because the 

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

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

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

188 meas = None 

189 else: 

190 try: 

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

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

193 ) 

194 children = _filterSkySources(sources, children) 

195 except LookupError as e: 

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

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

198 else: 

199 nChildren = np.count_nonzero(children) 

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

201 

202 return Struct(measurement=meas) 

203 

204 

205def _filterSkySources(catalog, selection): 

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

207 

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

209 be non-sky. 

210 

211 Parameters 

212 ---------- 

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

214 The catalog to filter. 

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

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

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

218 

219 Returns 

220 ------- 

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

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

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

224 no changes were made. 

225 """ 

226 if "sky_source" in catalog.schema: 

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

228 # is not a bool. 

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

230 else: 

231 return selection