lsst.ip.diffim  18.1.0-7-g5f9ea2f+4
metrics.py
Go to the documentation of this file.
1 # This file is part of ip_diffim.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (http://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 <http://www.gnu.org/licenses/>.
21 #
22 
23 __all__ = [
24  "NumberSciSourcesMetricTask", "NumberSciSourcesMetricConfig",
25  "FractionDiaSourcesToSciSourcesMetricTask", "FractionDiaSourcesToSciSourcesMetricConfig",
26 ]
27 
28 
29 import astropy.units as u
30 
31 from lsst.pipe.base import Struct, PipelineTaskConnections, connectionTypes
32 from lsst.verify import Measurement
33 from lsst.verify.gen2tasks import MetricTask, register
34 from lsst.verify.tasks import MetricComputationError
35 
36 
38  PipelineTaskConnections,
39  dimensions={"Instrument", "Exposure", "Detector"}):
40  sources = connectionTypes.Input(
41  doc="The catalog of science sources.",
42  name="src",
43  storageClass="SourceCatalog",
44  dimensions={"Instrument", "Exposure", "Detector"},
45  multiple=True,
46  )
47 
48 
50  MetricTask.ConfigClass,
51  pipelineConnections=NumberSciSourcesMetricConnections):
52  pass
53 
54 
55 @register("numSciSources")
56 class NumberSciSourcesMetricTask(MetricTask):
57  """Task that computes the number of cataloged science sources.
58  """
59  _DefaultName = "numSciSources"
60  ConfigClass = NumberSciSourcesMetricConfig
61 
62  def run(self, sources):
63  """Count the number of science sources.
64 
65  Parameters
66  ----------
67  sources : iterable of `lsst.afw.table.SourceCatalog`
68  A collection of science source catalogs, one for each unit of
69  processing to be incorporated into this metric. Its elements may
70  be `None` to represent missing data.
71 
72  Returns
73  -------
74  result : `lsst.pipe.base.Struct`
75  A `~lsst.pipe.base.Struct` containing the following component:
76 
77  ``measurement``
78  the total number of science sources (`lsst.verify.Measurement`
79  or `None`)
80  """
81  nSciSources = 0
82  inputData = False
83  for catalog in sources:
84  if catalog is not None:
85  nSciSources += len(catalog)
86  inputData = True
87 
88  if inputData:
89  meas = Measurement(self.getOutputMetricName(self.config), nSciSources * u.count)
90  else:
91  self.log.info("Nothing to do: no catalogs found.")
92  meas = None
93  return Struct(measurement=meas)
94 
95  @classmethod
96  def getOutputMetricName(cls, config):
97  return "ip_diffim.numSciSources"
98 
99 
101  PipelineTaskConnections,
102  dimensions={"Instrument", "Exposure", "Detector"},
103  defaultTemplates={"coaddName": "deep"}):
104  sciSources = connectionTypes.Input(
105  doc="The catalog of science sources.",
106  name="src",
107  storageClass="SourceCatalog",
108  dimensions={"Instrument", "Exposure", "Detector"},
109  multiple=True,
110  )
111  diaSources = connectionTypes.Input(
112  doc="The catalog of DIASources.",
113  name="{coaddName}Diff_diaSrc",
114  storageClass="SourceCatalog",
115  dimensions={"Instrument", "Exposure", "Detector"},
116  multiple=True,
117  )
118 
119 
120 class FractionDiaSourcesToSciSourcesMetricConfig(
121  MetricTask.ConfigClass,
122  pipelineConnections=FractionDiaSourcesToSciSourcesMetricConnections):
123  pass
124 
125 
126 @register("fracDiaSourcesToSciSources")
127 class FractionDiaSourcesToSciSourcesMetricTask(MetricTask):
128  """Task that computes the ratio of difference image sources to science
129  sources in an image, visit, etc.
130  """
131  _DefaultName = "fracDiaSourcesToSciSources"
132  ConfigClass = FractionDiaSourcesToSciSourcesMetricConfig
133 
134  def run(self, sciSources, diaSources):
135  """Compute the ratio of DIASources to science sources.
136 
137  Parameters
138  ----------
139  sciSources : iterable of `lsst.afw.table.SourceCatalog`
140  A collection of science source catalogs, one for each unit of
141  processing to be incorporated into this metric. Its elements may
142  be `None` to represent missing data.
143  diaSources : iterable of `lsst.afw.table.SourceCatalog`
144  A collection of difference imaging catalogs similar to
145  ``sciSources``.
146 
147  Returns
148  -------
149  result : `lsst.pipe.base.Struct`
150  A `~lsst.pipe.base.Struct` containing the following component:
151 
152  ``measurement``
153  the ratio (`lsst.verify.Measurement` or `None`)
154  """
155  nSciSources = 0
156  nDiaSources = 0
157  inputData = False
158 
159  for sciCatalog, diaCatalog in zip(sciSources, diaSources):
160  if diaCatalog is not None and sciCatalog is not None:
161  nSciSources += len(sciCatalog)
162  nDiaSources += len(diaCatalog)
163  inputData = True
164 
165  if inputData:
166  metricName = self.getOutputMetricName(self.config)
167  if nSciSources <= 0.0:
168  raise MetricComputationError(
169  "No science sources found; ratio of DIASources to science sources ill-defined.")
170  meas = Measurement(metricName, 0.0 * u.dimensionless_unscaled)
171  else:
172  meas = Measurement(metricName, nDiaSources / nSciSources * u.dimensionless_unscaled)
173  else:
174  self.log.info("Nothing to do: no catalogs found.")
175  meas = None
176  return Struct(measurement=meas)
177 
178  @classmethod
179  def getOutputMetricName(cls, config):
180  return "ip_diffim.fracDiaSourcesToSciSources"