Coverage for python/lsst/ap/association/metrics.py : 45%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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 ``"unassociatedObjects"``
227 The number of DIAObjects not associated with a DiaSource in
228 this image (`int` or `None`). May be `None` if the image was
229 not successfully associated.
231 Returns
232 -------
233 measurement : `lsst.verify.Measurement` or `None`
234 The total number of Solar System objects.
235 """
236 if values["numTotalSolarSystemObjects"] is not None:
237 try:
238 nNew = int(values["numTotalSolarSystemObjects"])
239 except (ValueError, TypeError) as e:
240 raise MetricComputationError("Corrupted value of numTotalSolarSystemObjects") from e
241 else:
242 return Measurement(self.config.metricName, nNew * u.count)
243 else:
244 self.log.info("Nothing to do: no solar system results found.")
245 return None
247 @classmethod
248 def getInputMetadataKeys(cls, config):
249 return {"numTotalSolarSystemObjects": ".numTotalSolarSystemObjects"}
252class NumberAssociatedSolarSystemObjectsMetricConfig(MetadataMetricConfig):
253 def setDefaults(self):
254 self.connections.package = "ap_association"
255 self.connections.metric = "numAssociatedSsObjects"
258@register("numAssociatedSsObjects")
259class NumberAssociatedSolarSystemObjectsMetricTask(MetadataMetricTask):
260 """Number of SolarSystemObjects that were associated with new DiaSources
261 for this detectorVisit.
262 """
263 _DefaultName = "numAssociatedSsObjects"
264 ConfigClass = NumberAssociatedSolarSystemObjectsMetricConfig
266 def makeMeasurement(self, values):
267 """Compute the number of associated SolarSystemObjects.
269 Parameters
270 ----------
271 values : `dict` [`str`, `int` or `None`]
272 A `dict` representation of the metadata. Each `dict` has the
273 following key:
275 ``"unassociatedObjects"``
276 The number of DIAObjects not associated with a DiaSource in
277 this image (`int` or `None`). May be `None` if the image was
278 not successfully associated.
280 Returns
281 -------
282 measurement : `lsst.verify.Measurement` or `None`
283 The total number of associated SolarSystemObjects.
284 """
285 if values["numAssociatedSsObjects"] is not None:
286 try:
287 nNew = int(values["numAssociatedSsObjects"])
288 except (ValueError, TypeError) as e:
289 raise MetricComputationError("Corrupted value of numAssociatedSsObjects") from e
290 else:
291 return Measurement(self.config.metricName, nNew * u.count)
292 else:
293 self.log.info("Nothing to do: no solar system results found.")
294 return None
296 @classmethod
297 def getInputMetadataKeys(cls, config):
298 return {"numAssociatedSsObjects": ".numAssociatedSsObjects"}
301class TotalUnassociatedDiaObjectsMetricConfig(ApdbMetricConfig):
302 def setDefaults(self):
303 self.connections.package = "ap_association"
304 self.connections.metric = "totalUnassociatedDiaObjects"
307@register("totalUnassociatedDiaObjects")
308class TotalUnassociatedDiaObjectsMetricTask(ApdbMetricTask):
309 """Task that computes the number of DIAObjects with only one
310 associated DIASource.
311 """
312 _DefaultName = "totalUnassociatedDiaObjects"
313 ConfigClass = TotalUnassociatedDiaObjectsMetricConfig
315 def makeMeasurement(self, dbHandle, outputDataId):
316 """Compute the number of unassociated DIAObjects.
318 Parameters
319 ----------
320 dbHandle : `lsst.dax.apdb.Apdb`
321 A database instance.
322 outputDataId : any data ID type
323 The subset of the database to which this measurement applies.
324 Must be empty, as the number of unassociated sources is
325 ill-defined for subsets of the dataset.
327 Returns
328 -------
329 measurement : `lsst.verify.Measurement`
330 The total number of unassociated objects.
332 Raises
333 ------
334 MetricComputationError
335 Raised on any failure to query the database.
336 ValueError
337 Raised if outputDataId is not empty
338 """
339 # All data ID types define keys()
340 if outputDataId.keys() - {'instrument'}:
341 raise ValueError("%s must not be associated with specific data IDs (gave %s)."
342 % (self.config.metricName, outputDataId))
344 try:
345 nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects()
346 except Exception as e:
347 raise MetricComputationError("Could not get unassociated objects from database") from e
349 meas = Measurement(self.config.metricName, nUnassociatedDiaObjects * u.count)
350 return meas