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

119 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-20 02:33 -0700

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.verify import Measurement 

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

36 ApdbMetricTask, ApdbMetricConfig, MetricComputationError 

37 

38 

39class NumberNewDiaObjectsMetricConfig(MetadataMetricConfig): 

40 def setDefaults(self): 

41 self.connections.package = "ap_association" 

42 self.connections.metric = "numNewDiaObjects" 

43 

44 

45class NumberNewDiaObjectsMetricTask(MetadataMetricTask): 

46 """Task that computes the number of DIASources that create new DIAObjects 

47 in an image, visit, etc.. 

48 """ 

49 _DefaultName = "numNewDiaObjects" 

50 ConfigClass = NumberNewDiaObjectsMetricConfig 

51 

52 def makeMeasurement(self, values): 

53 """Compute the number of new DIAObjects. 

54 

55 Parameters 

56 ---------- 

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

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

59 following key: 

60 

61 ``"newObjects"`` 

62 The number of new objects created for this image (`int` 

63 or `None`). May be `None` if the image was not successfully 

64 associated. 

65 

66 Returns 

67 ------- 

68 measurement : `lsst.verify.Measurement` or `None` 

69 The total number of new objects. 

70 """ 

71 if values["newObjects"] is not None: 

72 try: 

73 nNew = int(values["newObjects"]) 

74 except (ValueError, TypeError) as e: 

75 raise MetricComputationError("Corrupted value of numNewDiaObjects") from e 

76 else: 

77 return Measurement(self.config.metricName, nNew * u.count) 

78 else: 

79 self.log.info("Nothing to do: no association results found.") 

80 return None 

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 self.log.info("Nothing to do: no association results found.") 

128 return None 

129 

130 @classmethod 

131 def getInputMetadataKeys(cls, config): 

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

133 

134 

135class FractionUpdatedDiaObjectsMetricConfig(MetadataMetricConfig): 

136 def setDefaults(self): 

137 self.connections.package = "ap_association" 

138 self.connections.metric = "fracUpdatedDiaObjects" 

139 

140 

141class FractionUpdatedDiaObjectsMetricTask(MetadataMetricTask): 

142 """Task that computes the fraction of previously created DIAObjects that 

143 have a new association in this image, visit, etc.. 

144 """ 

145 _DefaultName = "fracUpdatedDiaObjects" 

146 ConfigClass = FractionUpdatedDiaObjectsMetricConfig 

147 

148 def makeMeasurement(self, values): 

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

150 

151 AssociationTask reports each pre-existing DIAObject as either updated 

152 (associated with a new DIASource) or unassociated. 

153 

154 Parameters 

155 ---------- 

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

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

158 following keys: 

159 

160 ``"updatedObjects"`` 

161 The number of DIAObjects updated for this image (`int` or 

162 `None`). May be `None` if the image was not 

163 successfully associated. 

164 ``"unassociatedObjects"`` 

165 The number of DIAObjects not associated with a DiaSource in 

166 this image (`int` or `None`). May be `None` if the image was 

167 not successfully associated. 

168 

169 Returns 

170 ------- 

171 measurement : `lsst.verify.Measurement` or `None` 

172 The total number of unassociated objects. 

173 """ 

174 if values["updatedObjects"] is not None \ 

175 and values["unassociatedObjects"] is not None: 

176 try: 

177 nUpdated = int(values["updatedObjects"]) 

178 nUnassociated = int(values["unassociatedObjects"]) 

179 except (ValueError, TypeError) as e: 

180 raise MetricComputationError("Corrupted value of numUpdatedDiaObjects " 

181 "or numUnassociatedDiaObjects") from e 

182 else: 

183 if nUpdated <= 0 and nUnassociated <= 0: 

184 return None # No pre-existing DIAObjects; no fraction to compute 

185 else: 

186 fraction = nUpdated / (nUpdated + nUnassociated) 

187 return Measurement(self.config.metricName, fraction * u.dimensionless_unscaled) 

188 else: 

189 self.log.info("Nothing to do: no association results found.") 

190 return None 

191 

192 @classmethod 

193 def getInputMetadataKeys(cls, config): 

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

195 "unassociatedObjects": ".numUnassociatedDiaObjects"} 

196 

197 

198class NumberSolarSystemObjectsMetricConfig(MetadataMetricConfig): 

199 def setDefaults(self): 

200 self.connections.package = "ap_association" 

201 self.connections.metric = "numTotalSolarSystemObjects" 

202 

203 

204class NumberSolarSystemObjectsMetricTask(MetadataMetricTask): 

205 """Task that computes the number of SolarSystemObjects that are 

206 observable within this detectorVisit. 

207 """ 

208 _DefaultName = "numTotalSolarSystemObjects" 

209 ConfigClass = NumberSolarSystemObjectsMetricConfig 

210 

211 def makeMeasurement(self, values): 

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

213 detectorVisit. 

214 

215 Parameters 

216 ---------- 

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

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

219 following key: 

220 

221 ``"numTotalSolarSystemObjects"`` 

222 The number of SolarSystemObjects within the observable detector 

223 area (`int` or `None`). May be `None` if solar system 

224 association was not attempted or the image was not 

225 successfully associated. 

226 

227 Returns 

228 ------- 

229 measurement : `lsst.verify.Measurement` or `None` 

230 The total number of Solar System objects. 

231 """ 

232 if values["numTotalSolarSystemObjects"] is not None: 

233 try: 

234 nNew = int(values["numTotalSolarSystemObjects"]) 

235 except (ValueError, TypeError) as e: 

236 raise MetricComputationError("Corrupted value of numTotalSolarSystemObjects") from e 

237 else: 

238 return Measurement(self.config.metricName, nNew * u.count) 

239 else: 

240 self.log.info("Nothing to do: no solar system results found.") 

241 return None 

242 

243 @classmethod 

244 def getInputMetadataKeys(cls, config): 

245 return {"numTotalSolarSystemObjects": ".numTotalSolarSystemObjects"} 

246 

247 

248class NumberAssociatedSolarSystemObjectsMetricConfig(MetadataMetricConfig): 

249 def setDefaults(self): 

250 self.connections.package = "ap_association" 

251 self.connections.metric = "numAssociatedSsObjects" 

252 

253 

254class NumberAssociatedSolarSystemObjectsMetricTask(MetadataMetricTask): 

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

256 for this detectorVisit. 

257 """ 

258 _DefaultName = "numAssociatedSsObjects" 

259 ConfigClass = NumberAssociatedSolarSystemObjectsMetricConfig 

260 

261 def makeMeasurement(self, values): 

262 """Compute the number of associated SolarSystemObjects. 

263 

264 Parameters 

265 ---------- 

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

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

268 following key: 

269 

270 ``"numAssociatedSsObjects"`` 

271 The number of successfully associated SolarSystem Objects 

272 (`int` or `None`). May be `None` if solar system association 

273 was not attempted or the image was not successfully associated. 

274 

275 Returns 

276 ------- 

277 measurement : `lsst.verify.Measurement` or `None` 

278 The total number of associated SolarSystemObjects. 

279 """ 

280 if values["numAssociatedSsObjects"] is not None: 

281 try: 

282 nNew = int(values["numAssociatedSsObjects"]) 

283 except (ValueError, TypeError) as e: 

284 raise MetricComputationError("Corrupted value of numAssociatedSsObjects") from e 

285 else: 

286 return Measurement(self.config.metricName, nNew * u.count) 

287 else: 

288 self.log.info("Nothing to do: no solar system results found.") 

289 return None 

290 

291 @classmethod 

292 def getInputMetadataKeys(cls, config): 

293 return {"numAssociatedSsObjects": ".numAssociatedSsObjects"} 

294 

295 

296class TotalUnassociatedDiaObjectsMetricConfig(ApdbMetricConfig): 

297 def setDefaults(self): 

298 self.connections.package = "ap_association" 

299 self.connections.metric = "totalUnassociatedDiaObjects" 

300 

301 

302class TotalUnassociatedDiaObjectsMetricTask(ApdbMetricTask): 

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

304 associated DIASource. 

305 """ 

306 _DefaultName = "totalUnassociatedDiaObjects" 

307 ConfigClass = TotalUnassociatedDiaObjectsMetricConfig 

308 

309 def makeMeasurement(self, dbHandle, outputDataId): 

310 """Compute the number of unassociated DIAObjects. 

311 

312 Parameters 

313 ---------- 

314 dbHandle : `lsst.dax.apdb.Apdb` 

315 A database instance. 

316 outputDataId : any data ID type 

317 The subset of the database to which this measurement applies. 

318 Must be empty, as the number of unassociated sources is 

319 ill-defined for subsets of the dataset. 

320 

321 Returns 

322 ------- 

323 measurement : `lsst.verify.Measurement` 

324 The total number of unassociated objects. 

325 

326 Raises 

327 ------ 

328 MetricComputationError 

329 Raised on any failure to query the database. 

330 ValueError 

331 Raised if outputDataId is not empty 

332 """ 

333 # All data ID types define keys() 

334 if outputDataId.keys() - {'instrument'}: 

335 raise ValueError("%s must not be associated with specific data IDs (gave %s)." 

336 % (self.config.metricName, outputDataId)) 

337 

338 try: 

339 nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects() 

340 except Exception as e: 

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

342 

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

344 return meas