Coverage for python/lsst/pipe/tasks/metrics.py: 46%
54 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-07 10:57 +0000
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-07 10:57 +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/>.
22__all__ = [
23 "NumberDeblendedSourcesMetricTask", "NumberDeblendedSourcesMetricConfig",
24 "NumberDeblendChildSourcesMetricTask", "NumberDeblendChildSourcesMetricConfig",
25]
28import numpy as np
29import astropy.units as u
31from lsst.pipe.base import Struct, connectionTypes
32from lsst.verify import Measurement
33from lsst.verify.tasks import MetricTask, MetricConfig, MetricConnections, MetricComputationError
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 )
50class NumberDeblendedSourcesMetricConfig(
51 MetricConfig,
52 pipelineConnections=NumberDeblendedSourcesMetricConnections):
53 pass
56class NumberDeblendedSourcesMetricTask(MetricTask):
57 """Task that computes the number of science sources that have
58 been deblended.
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.
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
74 def run(self, sources):
75 """Count the number of deblended science sources.
77 Parameters
78 ----------
79 sources : `lsst.afw.table.SourceCatalog` or `None`
80 A science source catalog, which may be empty or `None`.
82 Returns
83 -------
84 result : `lsst.pipe.base.Struct`
85 A `~lsst.pipe.base.Struct` containing the following component:
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`.
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)
117 return Struct(measurement=meas)
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 )
134class NumberDeblendChildSourcesMetricConfig(
135 MetricConfig,
136 pipelineConnections=NumberDeblendChildSourcesMetricConnections):
137 pass
140class NumberDeblendChildSourcesMetricTask(MetricTask):
141 """Task that computes the number of science sources created
142 through deblending.
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.
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
157 def run(self, sources):
158 """Count the number of science sources created by deblending.
160 Parameters
161 ----------
162 sources : `lsst.afw.table.SourceCatalog` or `None`
163 A science source catalog, which may be empty or `None`.
165 Returns
166 -------
167 result : `lsst.pipe.base.Struct`
168 A `~lsst.pipe.base.Struct` containing the following component:
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`.
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)
202 return Struct(measurement=meas)
205def _filterSkySources(catalog, selection):
206 """Filter out any sky sources from a vector of selected sources.
208 If no sky source information is available, all sources are assumed to
209 be non-sky.
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`.
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