Coverage for python/lsst/ap/association/metrics.py: 40%
115 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-05-30 22:34 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-05-30 22: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.pipe.base import NoWorkFound
35from lsst.verify import Measurement
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"
46class NumberNewDiaObjectsMetricTask(MetadataMetricTask):
47 """Task that computes the number of DIASources that create new DIAObjects
48 in an image, visit, etc..
49 """
50 _DefaultName = "numNewDiaObjects"
51 ConfigClass = NumberNewDiaObjectsMetricConfig
53 def makeMeasurement(self, values):
54 """Compute the number of new DIAObjects.
56 Parameters
57 ----------
58 values : `dict` [`str`, `int` or `None`]
59 A `dict` representation of the metadata. Each `dict` has the
60 following key:
62 ``"newObjects"``
63 The number of new objects created for this image (`int`
64 or `None`). May be `None` if the image was not successfully
65 associated.
67 Returns
68 -------
69 measurement : `lsst.verify.Measurement` or `None`
70 The total number of new objects.
71 """
72 if values["newObjects"] is not None:
73 try:
74 nNew = int(values["newObjects"])
75 except (ValueError, TypeError) as e:
76 raise MetricComputationError("Corrupted value of numNewDiaObjects") from e
77 else:
78 return Measurement(self.config.metricName, nNew * u.count)
79 else:
80 raise NoWorkFound("Nothing to do: no association results found.")
82 @classmethod
83 def getInputMetadataKeys(cls, config):
84 return {"newObjects": ".numNewDiaObjects"}
87class NumberUnassociatedDiaObjectsMetricConfig(MetadataMetricConfig):
88 def setDefaults(self):
89 self.connections.package = "ap_association"
90 self.connections.metric = "numUnassociatedDiaObjects"
93class NumberUnassociatedDiaObjectsMetricTask(MetadataMetricTask):
94 """Task that computes the number of previously-known DIAObjects that do
95 not have detected DIASources in an image, visit, etc..
96 """
97 _DefaultName = "numUnassociatedDiaObjects"
98 ConfigClass = NumberUnassociatedDiaObjectsMetricConfig
100 def makeMeasurement(self, values):
101 """Compute the number of non-updated DIAObjects.
103 Parameters
104 ----------
105 values : `dict` [`str`, `int` or `None`]
106 A `dict` representation of the metadata. Each `dict` has the
107 following key:
109 ``"unassociatedObjects"``
110 The number of DIAObjects not associated with a DiaSource in
111 this image (`int` or `None`). May be `None` if the image was
112 not successfully associated.
114 Returns
115 -------
116 measurement : `lsst.verify.Measurement` or `None`
117 The total number of unassociated objects.
118 """
119 if values["unassociatedObjects"] is not None:
120 try:
121 nNew = int(values["unassociatedObjects"])
122 except (ValueError, TypeError) as e:
123 raise MetricComputationError("Corrupted value of numUnassociatedDiaObjects") from e
124 else:
125 return Measurement(self.config.metricName, nNew * u.count)
126 else:
127 raise NoWorkFound("Nothing to do: no association results found.")
129 @classmethod
130 def getInputMetadataKeys(cls, config):
131 return {"unassociatedObjects": ".numUnassociatedDiaObjects"}
134class FractionUpdatedDiaObjectsMetricConfig(MetadataMetricConfig):
135 def setDefaults(self):
136 self.connections.package = "ap_association"
137 self.connections.metric = "fracUpdatedDiaObjects"
140class FractionUpdatedDiaObjectsMetricTask(MetadataMetricTask):
141 """Task that computes the fraction of previously created DIAObjects that
142 have a new association in this image, visit, etc..
143 """
144 _DefaultName = "fracUpdatedDiaObjects"
145 ConfigClass = FractionUpdatedDiaObjectsMetricConfig
147 def makeMeasurement(self, values):
148 """Compute the number of non-updated DIAObjects.
150 AssociationTask reports each pre-existing DIAObject as either updated
151 (associated with a new DIASource) or unassociated.
153 Parameters
154 ----------
155 values : `dict` [`str`, `int` or `None`]
156 A `dict` representation of the metadata. Each `dict` has the
157 following keys:
159 ``"updatedObjects"``
160 The number of DIAObjects updated for this image (`int` or
161 `None`). May be `None` if the image was not
162 successfully associated.
163 ``"unassociatedObjects"``
164 The number of DIAObjects not associated with a DiaSource in
165 this image (`int` or `None`). May be `None` if the image was
166 not successfully associated.
168 Returns
169 -------
170 measurement : `lsst.verify.Measurement` or `None`
171 The total number of unassociated objects.
172 """
173 if values["updatedObjects"] is not None \
174 and values["unassociatedObjects"] is not None:
175 try:
176 nUpdated = int(values["updatedObjects"])
177 nUnassociated = int(values["unassociatedObjects"])
178 except (ValueError, TypeError) as e:
179 raise MetricComputationError("Corrupted value of numUpdatedDiaObjects "
180 "or numUnassociatedDiaObjects") from e
181 else:
182 if nUpdated <= 0 and nUnassociated <= 0:
183 return None # No pre-existing DIAObjects; no fraction to compute
184 else:
185 fraction = nUpdated / (nUpdated + nUnassociated)
186 return Measurement(self.config.metricName, fraction * u.dimensionless_unscaled)
187 else:
188 raise NoWorkFound("Nothing to do: no association results found.")
190 @classmethod
191 def getInputMetadataKeys(cls, config):
192 return {"updatedObjects": ".numUpdatedDiaObjects",
193 "unassociatedObjects": ".numUnassociatedDiaObjects"}
196class NumberSolarSystemObjectsMetricConfig(MetadataMetricConfig):
197 def setDefaults(self):
198 self.connections.package = "ap_association"
199 self.connections.metric = "numTotalSolarSystemObjects"
202class NumberSolarSystemObjectsMetricTask(MetadataMetricTask):
203 """Task that computes the number of SolarSystemObjects that are
204 observable within this detectorVisit.
205 """
206 _DefaultName = "numTotalSolarSystemObjects"
207 ConfigClass = NumberSolarSystemObjectsMetricConfig
209 def makeMeasurement(self, values):
210 """Compute the total number of SolarSystemObjects within a
211 detectorVisit.
213 Parameters
214 ----------
215 values : `dict` [`str`, `int` or `None`]
216 A `dict` representation of the metadata. Each `dict` has the
217 following key:
219 ``"numTotalSolarSystemObjects"``
220 The number of SolarSystemObjects within the observable detector
221 area (`int` or `None`). May be `None` if solar system
222 association was not attempted or the image was not
223 successfully associated.
225 Returns
226 -------
227 measurement : `lsst.verify.Measurement` or `None`
228 The total number of Solar System objects.
229 """
230 if values["numTotalSolarSystemObjects"] is not None:
231 try:
232 nNew = int(values["numTotalSolarSystemObjects"])
233 except (ValueError, TypeError) as e:
234 raise MetricComputationError("Corrupted value of numTotalSolarSystemObjects") from e
235 else:
236 return Measurement(self.config.metricName, nNew * u.count)
237 else:
238 raise NoWorkFound("Nothing to do: no solar system results found.")
240 @classmethod
241 def getInputMetadataKeys(cls, config):
242 return {"numTotalSolarSystemObjects": ".numTotalSolarSystemObjects"}
245class NumberAssociatedSolarSystemObjectsMetricConfig(MetadataMetricConfig):
246 def setDefaults(self):
247 self.connections.package = "ap_association"
248 self.connections.metric = "numAssociatedSsObjects"
251class NumberAssociatedSolarSystemObjectsMetricTask(MetadataMetricTask):
252 """Number of SolarSystemObjects that were associated with new DiaSources
253 for this detectorVisit.
254 """
255 _DefaultName = "numAssociatedSsObjects"
256 ConfigClass = NumberAssociatedSolarSystemObjectsMetricConfig
258 def makeMeasurement(self, values):
259 """Compute the number of associated SolarSystemObjects.
261 Parameters
262 ----------
263 values : `dict` [`str`, `int` or `None`]
264 A `dict` representation of the metadata. Each `dict` has the
265 following key:
267 ``"numAssociatedSsObjects"``
268 The number of successfully associated SolarSystem Objects
269 (`int` or `None`). May be `None` if solar system association
270 was not attempted or the image was not successfully associated.
272 Returns
273 -------
274 measurement : `lsst.verify.Measurement` or `None`
275 The total number of associated SolarSystemObjects.
276 """
277 if values["numAssociatedSsObjects"] is not None:
278 try:
279 nNew = int(values["numAssociatedSsObjects"])
280 except (ValueError, TypeError) as e:
281 raise MetricComputationError("Corrupted value of numAssociatedSsObjects") from e
282 else:
283 return Measurement(self.config.metricName, nNew * u.count)
284 else:
285 raise NoWorkFound("Nothing to do: no solar system results found.")
287 @classmethod
288 def getInputMetadataKeys(cls, config):
289 return {"numAssociatedSsObjects": ".numAssociatedSsObjects"}
292class TotalUnassociatedDiaObjectsMetricConfig(ApdbMetricConfig):
293 def setDefaults(self):
294 self.connections.package = "ap_association"
295 self.connections.metric = "totalUnassociatedDiaObjects"
298class TotalUnassociatedDiaObjectsMetricTask(ApdbMetricTask):
299 """Task that computes the number of DIAObjects with only one
300 associated DIASource.
301 """
302 _DefaultName = "totalUnassociatedDiaObjects"
303 ConfigClass = TotalUnassociatedDiaObjectsMetricConfig
305 def makeMeasurement(self, dbHandle, outputDataId):
306 """Compute the number of unassociated DIAObjects.
308 Parameters
309 ----------
310 dbHandle : `lsst.dax.apdb.Apdb`
311 A database instance.
312 outputDataId : any data ID type
313 The subset of the database to which this measurement applies.
314 Must be empty, as the number of unassociated sources is
315 ill-defined for subsets of the dataset.
317 Returns
318 -------
319 measurement : `lsst.verify.Measurement`
320 The total number of unassociated objects.
322 Raises
323 ------
324 MetricComputationError
325 Raised on any failure to query the database.
326 ValueError
327 Raised if outputDataId is not empty
328 """
329 # All data ID types define keys()
330 if outputDataId.keys() - {'instrument'}:
331 raise ValueError("%s must not be associated with specific data IDs (gave %s)."
332 % (self.config.metricName, outputDataId))
334 try:
335 nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects()
336 except Exception as e:
337 raise MetricComputationError("Could not get unassociated objects from database") from e
339 meas = Measurement(self.config.metricName, nUnassociatedDiaObjects * u.count)
340 return meas