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

86 statements  

« prev     ^ index     » next       coverage.py v7.2.1, created at 2023-03-12 10:45 +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# 

22 

23__all__ = ["NumberNewDiaObjectsMetricTask", 

24 "NumberUnassociatedDiaObjectsMetricTask", 

25 "FractionUpdatedDiaObjectsMetricTask", 

26 "TotalUnassociatedDiaObjectsMetricTask", 

27 ] 

28 

29 

30import astropy.units as u 

31 

32from lsst.verify import Measurement 

33from lsst.verify.gen2tasks import register 

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

35 ApdbMetricTask, ApdbMetricConfig, MetricComputationError 

36 

37 

38class NumberNewDiaObjectsMetricConfig(MetadataMetricConfig): 

39 def setDefaults(self): 

40 self.connections.package = "ap_association" 

41 self.connections.metric = "numNewDiaObjects" 

42 

43 

44@register("numNewDiaObjects") 

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 

93@register("numUnassociatedDiaObjects") 

94class NumberUnassociatedDiaObjectsMetricTask(MetadataMetricTask): 

95 """Task that computes the number of previously-known DIAObjects that do 

96 not have detected DIASources in an image, visit, etc.. 

97 """ 

98 _DefaultName = "numUnassociatedDiaObjects" 

99 ConfigClass = NumberUnassociatedDiaObjectsMetricConfig 

100 

101 def makeMeasurement(self, values): 

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

103 

104 Parameters 

105 ---------- 

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

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

108 following key: 

109 

110 ``"unassociatedObjects"`` 

111 The number of DIAObjects not associated with a DiaSource in 

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

113 not successfully associated. 

114 

115 Returns 

116 ------- 

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

118 The total number of unassociated objects. 

119 """ 

120 if values["unassociatedObjects"] is not None: 

121 try: 

122 nNew = int(values["unassociatedObjects"]) 

123 except (ValueError, TypeError) as e: 

124 raise MetricComputationError("Corrupted value of numUnassociatedDiaObjects") from e 

125 else: 

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

127 else: 

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

129 return None 

130 

131 @classmethod 

132 def getInputMetadataKeys(cls, config): 

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

134 

135 

136class FractionUpdatedDiaObjectsMetricConfig(MetadataMetricConfig): 

137 def setDefaults(self): 

138 self.connections.package = "ap_association" 

139 self.connections.metric = "fracUpdatedDiaObjects" 

140 

141 

142@register("fracUpdatedDiaObjects") 

143class FractionUpdatedDiaObjectsMetricTask(MetadataMetricTask): 

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

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

146 """ 

147 _DefaultName = "fracUpdatedDiaObjects" 

148 ConfigClass = FractionUpdatedDiaObjectsMetricConfig 

149 

150 def makeMeasurement(self, values): 

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

152 

153 AssociationTask reports each pre-existing DIAObject as either updated 

154 (associated with a new DIASource) or unassociated. 

155 

156 Parameters 

157 ---------- 

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

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

160 following keys: 

161 

162 ``"updatedObjects"`` 

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

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

165 successfully associated. 

166 ``"unassociatedObjects"`` 

167 The number of DIAObjects not associated with a DiaSource in 

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

169 not successfully associated. 

170 

171 Returns 

172 ------- 

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

174 The total number of unassociated objects. 

175 """ 

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

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

178 try: 

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

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

181 except (ValueError, TypeError) as e: 

182 raise MetricComputationError("Corrupted value of numUpdatedDiaObjects " 

183 "or numUnassociatedDiaObjects") from e 

184 else: 

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

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

187 else: 

188 fraction = nUpdated / (nUpdated + nUnassociated) 

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

190 else: 

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

192 return None 

193 

194 @classmethod 

195 def getInputMetadataKeys(cls, config): 

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

197 "unassociatedObjects": ".numUnassociatedDiaObjects"} 

198 

199 

200class TotalUnassociatedDiaObjectsMetricConfig(ApdbMetricConfig): 

201 def setDefaults(self): 

202 self.connections.package = "ap_association" 

203 self.connections.metric = "totalUnassociatedDiaObjects" 

204 

205 

206@register("totalUnassociatedDiaObjects") 

207class TotalUnassociatedDiaObjectsMetricTask(ApdbMetricTask): 

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

209 associated DIASource. 

210 """ 

211 _DefaultName = "totalUnassociatedDiaObjects" 

212 ConfigClass = TotalUnassociatedDiaObjectsMetricConfig 

213 

214 def makeMeasurement(self, dbHandle, outputDataId): 

215 """Compute the number of unassociated DIAObjects. 

216 

217 Parameters 

218 ---------- 

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

220 A database instance. 

221 outputDataId : any data ID type 

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

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

224 ill-defined for subsets of the dataset. 

225 

226 Returns 

227 ------- 

228 measurement : `lsst.verify.Measurement` 

229 The total number of unassociated objects. 

230 

231 Raises 

232 ------ 

233 MetricComputationError 

234 Raised on any failure to query the database. 

235 ValueError 

236 Raised if outputDataId is not empty 

237 """ 

238 # All data ID types define keys() 

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

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

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

242 

243 try: 

244 nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects() 

245 except Exception as e: 

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

247 

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

249 return meas