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

115 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-03 01:47 -0800

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# 

22 

23__all__ = ["NumberNewDiaObjectsMetricTask", 

24 "NumberUnassociatedDiaObjectsMetricTask", 

25 "FractionUpdatedDiaObjectsMetricTask", 

26 "NumberSolarSystemObjectsMetricTask", 

27 "NumberAssociatedSolarSystemObjectsMetricTask", 

28 "TotalUnassociatedDiaObjectsMetricTask", 

29 ] 

30 

31 

32import astropy.units as u 

33 

34from lsst.pipe.base import NoWorkFound 

35from lsst.verify import Measurement 

36from lsst.verify.tasks import MetadataMetricTask, MetadataMetricConfig, \ 

37 ApdbMetricTask, ApdbMetricConfig, MetricComputationError 

38 

39 

40class NumberNewDiaObjectsMetricConfig(MetadataMetricConfig): 

41 def setDefaults(self): 

42 self.connections.package = "ap_association" 

43 self.connections.metric = "numNewDiaObjects" 

44 

45 

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 

52 

53 def makeMeasurement(self, values): 

54 """Compute the number of new DIAObjects. 

55 

56 Parameters 

57 ---------- 

58 values : `dict` [`str`, `int` or `None`] 

59 A `dict` representation of the metadata. Each `dict` has the 

60 following key: 

61 

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. 

66 

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.") 

81 

82 @classmethod 

83 def getInputMetadataKeys(cls, config): 

84 return {"newObjects": ".numNewDiaObjects"} 

85 

86 

87class NumberUnassociatedDiaObjectsMetricConfig(MetadataMetricConfig): 

88 def setDefaults(self): 

89 self.connections.package = "ap_association" 

90 self.connections.metric = "numUnassociatedDiaObjects" 

91 

92 

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 

99 

100 def makeMeasurement(self, values): 

101 """Compute the number of non-updated DIAObjects. 

102 

103 Parameters 

104 ---------- 

105 values : `dict` [`str`, `int` or `None`] 

106 A `dict` representation of the metadata. Each `dict` has the 

107 following key: 

108 

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. 

113 

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.") 

128 

129 @classmethod 

130 def getInputMetadataKeys(cls, config): 

131 return {"unassociatedObjects": ".numUnassociatedDiaObjects"} 

132 

133 

134class FractionUpdatedDiaObjectsMetricConfig(MetadataMetricConfig): 

135 def setDefaults(self): 

136 self.connections.package = "ap_association" 

137 self.connections.metric = "fracUpdatedDiaObjects" 

138 

139 

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 

146 

147 def makeMeasurement(self, values): 

148 """Compute the number of non-updated DIAObjects. 

149 

150 AssociationTask reports each pre-existing DIAObject as either updated 

151 (associated with a new DIASource) or unassociated. 

152 

153 Parameters 

154 ---------- 

155 values : `dict` [`str`, `int` or `None`] 

156 A `dict` representation of the metadata. Each `dict` has the 

157 following keys: 

158 

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. 

167 

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.") 

189 

190 @classmethod 

191 def getInputMetadataKeys(cls, config): 

192 return {"updatedObjects": ".numUpdatedDiaObjects", 

193 "unassociatedObjects": ".numUnassociatedDiaObjects"} 

194 

195 

196class NumberSolarSystemObjectsMetricConfig(MetadataMetricConfig): 

197 def setDefaults(self): 

198 self.connections.package = "ap_association" 

199 self.connections.metric = "numTotalSolarSystemObjects" 

200 

201 

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 

208 

209 def makeMeasurement(self, values): 

210 """Compute the total number of SolarSystemObjects within a 

211 detectorVisit. 

212 

213 Parameters 

214 ---------- 

215 values : `dict` [`str`, `int` or `None`] 

216 A `dict` representation of the metadata. Each `dict` has the 

217 following key: 

218 

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. 

224 

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.") 

239 

240 @classmethod 

241 def getInputMetadataKeys(cls, config): 

242 return {"numTotalSolarSystemObjects": ".numTotalSolarSystemObjects"} 

243 

244 

245class NumberAssociatedSolarSystemObjectsMetricConfig(MetadataMetricConfig): 

246 def setDefaults(self): 

247 self.connections.package = "ap_association" 

248 self.connections.metric = "numAssociatedSsObjects" 

249 

250 

251class NumberAssociatedSolarSystemObjectsMetricTask(MetadataMetricTask): 

252 """Number of SolarSystemObjects that were associated with new DiaSources 

253 for this detectorVisit. 

254 """ 

255 _DefaultName = "numAssociatedSsObjects" 

256 ConfigClass = NumberAssociatedSolarSystemObjectsMetricConfig 

257 

258 def makeMeasurement(self, values): 

259 """Compute the number of associated SolarSystemObjects. 

260 

261 Parameters 

262 ---------- 

263 values : `dict` [`str`, `int` or `None`] 

264 A `dict` representation of the metadata. Each `dict` has the 

265 following key: 

266 

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. 

271 

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.") 

286 

287 @classmethod 

288 def getInputMetadataKeys(cls, config): 

289 return {"numAssociatedSsObjects": ".numAssociatedSsObjects"} 

290 

291 

292class TotalUnassociatedDiaObjectsMetricConfig(ApdbMetricConfig): 

293 def setDefaults(self): 

294 self.connections.package = "ap_association" 

295 self.connections.metric = "totalUnassociatedDiaObjects" 

296 

297 

298class TotalUnassociatedDiaObjectsMetricTask(ApdbMetricTask): 

299 """Task that computes the number of DIAObjects with only one 

300 associated DIASource. 

301 """ 

302 _DefaultName = "totalUnassociatedDiaObjects" 

303 ConfigClass = TotalUnassociatedDiaObjectsMetricConfig 

304 

305 def makeMeasurement(self, dbHandle, outputDataId): 

306 """Compute the number of unassociated DIAObjects. 

307 

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. 

316 

317 Returns 

318 ------- 

319 measurement : `lsst.verify.Measurement` 

320 The total number of unassociated objects. 

321 

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)) 

333 

334 try: 

335 nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects() 

336 except Exception as e: 

337 raise MetricComputationError("Could not get unassociated objects from database") from e 

338 

339 meas = Measurement(self.config.metricName, nUnassociatedDiaObjects * u.count) 

340 return meas