Coverage for python/lsst/ap/association/metrics.py: 49%
126 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-03 12:34 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-03 12:34 +0000
1# This file is part of ap_association.
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#
23__all__ = ["NumberNewDiaObjectsMetricTask",
24 "NumberUnassociatedDiaObjectsMetricTask",
25 "FractionUpdatedDiaObjectsMetricTask",
26 "NumberSolarSystemObjectsMetricTask",
27 "NumberAssociatedSolarSystemObjectsMetricTask",
28 "TotalUnassociatedDiaObjectsMetricTask",
29 ]
32import astropy.units as u
34from lsst.verify import Measurement
35from lsst.verify.gen2tasks import register
36from lsst.verify.tasks import MetadataMetricTask, MetadataMetricConfig, \
37 ApdbMetricTask, ApdbMetricConfig, MetricComputationError
40class NumberNewDiaObjectsMetricConfig(MetadataMetricConfig):
41 def setDefaults(self):
42 self.connections.package = "ap_association"
43 self.connections.metric = "numNewDiaObjects"
46@register("numNewDiaObjects")
47class NumberNewDiaObjectsMetricTask(MetadataMetricTask):
48 """Task that computes the number of DIASources that create new DIAObjects
49 in an image, visit, etc..
50 """
51 _DefaultName = "numNewDiaObjects"
52 ConfigClass = NumberNewDiaObjectsMetricConfig
54 def makeMeasurement(self, values):
55 """Compute the number of new DIAObjects.
57 Parameters
58 ----------
59 values : `dict` [`str`, `int` or `None`]
60 A `dict` representation of the metadata. Each `dict` has the
61 following key:
63 ``"newObjects"``
64 The number of new objects created for this image (`int`
65 or `None`). May be `None` if the image was not successfully
66 associated.
68 Returns
69 -------
70 measurement : `lsst.verify.Measurement` or `None`
71 The total number of new objects.
72 """
73 if values["newObjects"] is not None:
74 try:
75 nNew = int(values["newObjects"])
76 except (ValueError, TypeError) as e:
77 raise MetricComputationError("Corrupted value of numNewDiaObjects") from e
78 else:
79 return Measurement(self.config.metricName, nNew * u.count)
80 else:
81 self.log.info("Nothing to do: no association results found.")
82 return None
84 @classmethod
85 def getInputMetadataKeys(cls, config):
86 return {"newObjects": ".numNewDiaObjects"}
89class NumberUnassociatedDiaObjectsMetricConfig(MetadataMetricConfig):
90 def setDefaults(self):
91 self.connections.package = "ap_association"
92 self.connections.metric = "numUnassociatedDiaObjects"
95@register("numUnassociatedDiaObjects")
96class NumberUnassociatedDiaObjectsMetricTask(MetadataMetricTask):
97 """Task that computes the number of previously-known DIAObjects that do
98 not have detected DIASources in an image, visit, etc..
99 """
100 _DefaultName = "numUnassociatedDiaObjects"
101 ConfigClass = NumberUnassociatedDiaObjectsMetricConfig
103 def makeMeasurement(self, values):
104 """Compute the number of non-updated DIAObjects.
106 Parameters
107 ----------
108 values : `dict` [`str`, `int` or `None`]
109 A `dict` representation of the metadata. Each `dict` has the
110 following key:
112 ``"unassociatedObjects"``
113 The number of DIAObjects not associated with a DiaSource in
114 this image (`int` or `None`). May be `None` if the image was
115 not successfully associated.
117 Returns
118 -------
119 measurement : `lsst.verify.Measurement` or `None`
120 The total number of unassociated objects.
121 """
122 if values["unassociatedObjects"] is not None:
123 try:
124 nNew = int(values["unassociatedObjects"])
125 except (ValueError, TypeError) as e:
126 raise MetricComputationError("Corrupted value of numUnassociatedDiaObjects") from e
127 else:
128 return Measurement(self.config.metricName, nNew * u.count)
129 else:
130 self.log.info("Nothing to do: no association results found.")
131 return None
133 @classmethod
134 def getInputMetadataKeys(cls, config):
135 return {"unassociatedObjects": ".numUnassociatedDiaObjects"}
138class FractionUpdatedDiaObjectsMetricConfig(MetadataMetricConfig):
139 def setDefaults(self):
140 self.connections.package = "ap_association"
141 self.connections.metric = "fracUpdatedDiaObjects"
144@register("fracUpdatedDiaObjects")
145class FractionUpdatedDiaObjectsMetricTask(MetadataMetricTask):
146 """Task that computes the fraction of previously created DIAObjects that
147 have a new association in this image, visit, etc..
148 """
149 _DefaultName = "fracUpdatedDiaObjects"
150 ConfigClass = FractionUpdatedDiaObjectsMetricConfig
152 def makeMeasurement(self, values):
153 """Compute the number of non-updated DIAObjects.
155 AssociationTask reports each pre-existing DIAObject as either updated
156 (associated with a new DIASource) or unassociated.
158 Parameters
159 ----------
160 values : `dict` [`str`, `int` or `None`]
161 A `dict` representation of the metadata. Each `dict` has the
162 following keys:
164 ``"updatedObjects"``
165 The number of DIAObjects updated for this image (`int` or
166 `None`). May be `None` if the image was not
167 successfully associated.
168 ``"unassociatedObjects"``
169 The number of DIAObjects not associated with a DiaSource in
170 this image (`int` or `None`). May be `None` if the image was
171 not successfully associated.
173 Returns
174 -------
175 measurement : `lsst.verify.Measurement` or `None`
176 The total number of unassociated objects.
177 """
178 if values["updatedObjects"] is not None \
179 and values["unassociatedObjects"] is not None:
180 try:
181 nUpdated = int(values["updatedObjects"])
182 nUnassociated = int(values["unassociatedObjects"])
183 except (ValueError, TypeError) as e:
184 raise MetricComputationError("Corrupted value of numUpdatedDiaObjects "
185 "or numUnassociatedDiaObjects") from e
186 else:
187 if nUpdated <= 0 and nUnassociated <= 0:
188 return None # No pre-existing DIAObjects; no fraction to compute
189 else:
190 fraction = nUpdated / (nUpdated + nUnassociated)
191 return Measurement(self.config.metricName, fraction * u.dimensionless_unscaled)
192 else:
193 self.log.info("Nothing to do: no association results found.")
194 return None
196 @classmethod
197 def getInputMetadataKeys(cls, config):
198 return {"updatedObjects": ".numUpdatedDiaObjects",
199 "unassociatedObjects": ".numUnassociatedDiaObjects"}
202class NumberSolarSystemObjectsMetricConfig(MetadataMetricConfig):
203 def setDefaults(self):
204 self.connections.package = "ap_association"
205 self.connections.metric = "numTotalSolarSystemObjects"
208@register("numTotalSolarSystemObjects")
209class NumberSolarSystemObjectsMetricTask(MetadataMetricTask):
210 """Task that computes the number of SolarSystemObjects that are
211 observable within this detectorVisit.
212 """
213 _DefaultName = "numTotalSolarSystemObjects"
214 ConfigClass = NumberSolarSystemObjectsMetricConfig
216 def makeMeasurement(self, values):
217 """Compute the total number of SolarSystemObjects within a
218 detectorVisit.
220 Parameters
221 ----------
222 values : `dict` [`str`, `int` or `None`]
223 A `dict` representation of the metadata. Each `dict` has the
224 following key:
226 ``"numTotalSolarSystemObjects"``
227 The number of SolarSystemObjects within the observable detector
228 area (`int` or `None`). May be `None` if solar system
229 association was not attempted or the image was not
230 successfully associated.
232 Returns
233 -------
234 measurement : `lsst.verify.Measurement` or `None`
235 The total number of Solar System objects.
236 """
237 if values["numTotalSolarSystemObjects"] is not None:
238 try:
239 nNew = int(values["numTotalSolarSystemObjects"])
240 except (ValueError, TypeError) as e:
241 raise MetricComputationError("Corrupted value of numTotalSolarSystemObjects") from e
242 else:
243 return Measurement(self.config.metricName, nNew * u.count)
244 else:
245 self.log.info("Nothing to do: no solar system results found.")
246 return None
248 @classmethod
249 def getInputMetadataKeys(cls, config):
250 return {"numTotalSolarSystemObjects": ".numTotalSolarSystemObjects"}
253class NumberAssociatedSolarSystemObjectsMetricConfig(MetadataMetricConfig):
254 def setDefaults(self):
255 self.connections.package = "ap_association"
256 self.connections.metric = "numAssociatedSsObjects"
259@register("numAssociatedSsObjects")
260class NumberAssociatedSolarSystemObjectsMetricTask(MetadataMetricTask):
261 """Number of SolarSystemObjects that were associated with new DiaSources
262 for this detectorVisit.
263 """
264 _DefaultName = "numAssociatedSsObjects"
265 ConfigClass = NumberAssociatedSolarSystemObjectsMetricConfig
267 def makeMeasurement(self, values):
268 """Compute the number of associated SolarSystemObjects.
270 Parameters
271 ----------
272 values : `dict` [`str`, `int` or `None`]
273 A `dict` representation of the metadata. Each `dict` has the
274 following key:
276 ``"numAssociatedSsObjects"``
277 The number of successfully associated SolarSystem Objects
278 (`int` or `None`). May be `None` if solar system association
279 was not attempted or the image was not successfully associated.
281 Returns
282 -------
283 measurement : `lsst.verify.Measurement` or `None`
284 The total number of associated SolarSystemObjects.
285 """
286 if values["numAssociatedSsObjects"] is not None:
287 try:
288 nNew = int(values["numAssociatedSsObjects"])
289 except (ValueError, TypeError) as e:
290 raise MetricComputationError("Corrupted value of numAssociatedSsObjects") from e
291 else:
292 return Measurement(self.config.metricName, nNew * u.count)
293 else:
294 self.log.info("Nothing to do: no solar system results found.")
295 return None
297 @classmethod
298 def getInputMetadataKeys(cls, config):
299 return {"numAssociatedSsObjects": ".numAssociatedSsObjects"}
302class TotalUnassociatedDiaObjectsMetricConfig(ApdbMetricConfig):
303 def setDefaults(self):
304 self.connections.package = "ap_association"
305 self.connections.metric = "totalUnassociatedDiaObjects"
308@register("totalUnassociatedDiaObjects")
309class TotalUnassociatedDiaObjectsMetricTask(ApdbMetricTask):
310 """Task that computes the number of DIAObjects with only one
311 associated DIASource.
312 """
313 _DefaultName = "totalUnassociatedDiaObjects"
314 ConfigClass = TotalUnassociatedDiaObjectsMetricConfig
316 def makeMeasurement(self, dbHandle, outputDataId):
317 """Compute the number of unassociated DIAObjects.
319 Parameters
320 ----------
321 dbHandle : `lsst.dax.apdb.Apdb`
322 A database instance.
323 outputDataId : any data ID type
324 The subset of the database to which this measurement applies.
325 Must be empty, as the number of unassociated sources is
326 ill-defined for subsets of the dataset.
328 Returns
329 -------
330 measurement : `lsst.verify.Measurement`
331 The total number of unassociated objects.
333 Raises
334 ------
335 MetricComputationError
336 Raised on any failure to query the database.
337 ValueError
338 Raised if outputDataId is not empty
339 """
340 # All data ID types define keys()
341 if outputDataId.keys() - {'instrument'}:
342 raise ValueError("%s must not be associated with specific data IDs (gave %s)."
343 % (self.config.metricName, outputDataId))
345 try:
346 nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects()
347 except Exception as e:
348 raise MetricComputationError("Could not get unassociated objects from database") from e
350 meas = Measurement(self.config.metricName, nUnassociatedDiaObjects * u.count)
351 return meas