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

126 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-07-09 07:01 -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.gen2tasks import register 

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 

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 

53 

54 def makeMeasurement(self, values): 

55 """Compute the number of new DIAObjects. 

56 

57 Parameters 

58 ---------- 

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

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

61 following key: 

62 

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. 

67 

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 

83 

84 @classmethod 

85 def getInputMetadataKeys(cls, config): 

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

87 

88 

89class NumberUnassociatedDiaObjectsMetricConfig(MetadataMetricConfig): 

90 def setDefaults(self): 

91 self.connections.package = "ap_association" 

92 self.connections.metric = "numUnassociatedDiaObjects" 

93 

94 

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 

102 

103 def makeMeasurement(self, values): 

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

105 

106 Parameters 

107 ---------- 

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

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

110 following key: 

111 

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. 

116 

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 

132 

133 @classmethod 

134 def getInputMetadataKeys(cls, config): 

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

136 

137 

138class FractionUpdatedDiaObjectsMetricConfig(MetadataMetricConfig): 

139 def setDefaults(self): 

140 self.connections.package = "ap_association" 

141 self.connections.metric = "fracUpdatedDiaObjects" 

142 

143 

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 

151 

152 def makeMeasurement(self, values): 

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

154 

155 AssociationTask reports each pre-existing DIAObject as either updated 

156 (associated with a new DIASource) or unassociated. 

157 

158 Parameters 

159 ---------- 

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

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

162 following keys: 

163 

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. 

172 

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 

195 

196 @classmethod 

197 def getInputMetadataKeys(cls, config): 

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

199 "unassociatedObjects": ".numUnassociatedDiaObjects"} 

200 

201 

202class NumberSolarSystemObjectsMetricConfig(MetadataMetricConfig): 

203 def setDefaults(self): 

204 self.connections.package = "ap_association" 

205 self.connections.metric = "numTotalSolarSystemObjects" 

206 

207 

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 

215 

216 def makeMeasurement(self, values): 

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

218 detectorVisit. 

219 

220 Parameters 

221 ---------- 

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

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

224 following key: 

225 

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. 

231 

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 

247 

248 @classmethod 

249 def getInputMetadataKeys(cls, config): 

250 return {"numTotalSolarSystemObjects": ".numTotalSolarSystemObjects"} 

251 

252 

253class NumberAssociatedSolarSystemObjectsMetricConfig(MetadataMetricConfig): 

254 def setDefaults(self): 

255 self.connections.package = "ap_association" 

256 self.connections.metric = "numAssociatedSsObjects" 

257 

258 

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 

266 

267 def makeMeasurement(self, values): 

268 """Compute the number of associated SolarSystemObjects. 

269 

270 Parameters 

271 ---------- 

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

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

274 following key: 

275 

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. 

280 

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 

296 

297 @classmethod 

298 def getInputMetadataKeys(cls, config): 

299 return {"numAssociatedSsObjects": ".numAssociatedSsObjects"} 

300 

301 

302class TotalUnassociatedDiaObjectsMetricConfig(ApdbMetricConfig): 

303 def setDefaults(self): 

304 self.connections.package = "ap_association" 

305 self.connections.metric = "totalUnassociatedDiaObjects" 

306 

307 

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 

315 

316 def makeMeasurement(self, dbHandle, outputDataId): 

317 """Compute the number of unassociated DIAObjects. 

318 

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. 

327 

328 Returns 

329 ------- 

330 measurement : `lsst.verify.Measurement` 

331 The total number of unassociated objects. 

332 

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

344 

345 try: 

346 nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects() 

347 except Exception as e: 

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

349 

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

351 return meas