lsst.pipe.tasks g0f82ab2f21+b4c33f426f
metrics.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__ = [
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
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 ----------
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