Hide keyboard shortcuts

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# (https://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 <https://www.gnu.org/licenses/>. 

21 

22import unittest 

23import unittest.mock 

24 

25import astropy.units as u 

26 

27import lsst.utils.tests 

28from lsst.pex.config import Config 

29from lsst.daf.base import PropertySet 

30from lsst.dax.apdb import Apdb 

31from lsst.pipe.base import Task, Struct 

32from lsst.verify import Name 

33from lsst.verify.gen2tasks.testUtils import MetricTaskTestCase 

34from lsst.verify.tasks import MetricComputationError 

35from lsst.verify.tasks.testUtils import MetadataMetricTestCase, ApdbMetricTestCase 

36 

37from lsst.ap.association.metrics import \ 

38 NumberNewDiaObjectsMetricTask, \ 

39 NumberUnassociatedDiaObjectsMetricTask, \ 

40 FractionUpdatedDiaObjectsMetricTask, \ 

41 TotalUnassociatedDiaObjectsMetricTask 

42 

43 

44def _makeAssociationMetadata(numUpdated=27, numNew=4, numUnassociated=15): 

45 metadata = PropertySet() 

46 metadata.add("association.numUpdatedDiaObjects", numUpdated) 

47 metadata.add("association.numNewDiaObjects", numNew) 

48 metadata.add("association.numUnassociatedDiaObjects", numUnassociated) 

49 return metadata 

50 

51 

52class TestNewDiaObjects(MetadataMetricTestCase): 

53 

54 @classmethod 

55 def makeTask(cls): 

56 return NumberNewDiaObjectsMetricTask() 

57 

58 def testValid(self): 

59 metadata = _makeAssociationMetadata() 

60 result = self.task.run(metadata) 

61 meas = result.measurement 

62 

63 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects")) 

64 self.assertEqual(meas.quantity, metadata.getAsDouble("association.numNewDiaObjects") * u.count) 

65 

66 def testNoNew(self): 

67 metadata = _makeAssociationMetadata(numNew=0) 

68 result = self.task.run(metadata) 

69 meas = result.measurement 

70 

71 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects")) 

72 self.assertEqual(meas.quantity, 0.0 * u.count) 

73 

74 def testMissingData(self): 

75 result = self.task.run(None) 

76 meas = result.measurement 

77 self.assertIsNone(meas) 

78 

79 def testAssociationFailed(self): 

80 result = self.task.run(PropertySet()) 

81 meas = result.measurement 

82 self.assertIsNone(meas) 

83 

84 def testBadlyTypedKeys(self): 

85 metadata = _makeAssociationMetadata() 

86 metadata.set("association.numNewDiaObjects", "Ultimate Answer") 

87 

88 with self.assertRaises(MetricComputationError): 

89 self.task.run(metadata) 

90 

91 

92class TestUnassociatedDiaObjects(MetadataMetricTestCase): 

93 

94 @classmethod 

95 def makeTask(cls): 

96 return NumberUnassociatedDiaObjectsMetricTask() 

97 

98 def testValid(self): 

99 metadata = _makeAssociationMetadata() 

100 result = self.task.run(metadata) 

101 meas = result.measurement 

102 

103 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects")) 

104 self.assertEqual(meas.quantity, 

105 metadata.getAsDouble("association.numUnassociatedDiaObjects") * u.count) 

106 

107 def testAllUpdated(self): 

108 metadata = _makeAssociationMetadata(numUnassociated=0) 

109 result = self.task.run(metadata) 

110 meas = result.measurement 

111 

112 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects")) 

113 self.assertEqual(meas.quantity, 0.0 * u.count) 

114 

115 def testMissingData(self): 

116 result = self.task.run(None) 

117 meas = result.measurement 

118 self.assertIsNone(meas) 

119 

120 def testAssociationFailed(self): 

121 result = self.task.run(PropertySet()) 

122 meas = result.measurement 

123 self.assertIsNone(meas) 

124 

125 def testBadlyTypedKeys(self): 

126 metadata = _makeAssociationMetadata() 

127 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer") 

128 

129 with self.assertRaises(MetricComputationError): 

130 self.task.run(metadata) 

131 

132 

133class TestFracUpdatedDiaObjects(MetadataMetricTestCase): 

134 

135 @classmethod 

136 def makeTask(cls): 

137 return FractionUpdatedDiaObjectsMetricTask() 

138 

139 def testValid(self): 

140 metadata = _makeAssociationMetadata() 

141 result = self.task.run(metadata) 

142 meas = result.measurement 

143 

144 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects")) 

145 nUpdated = metadata.getAsDouble("association.numUpdatedDiaObjects") 

146 nTotal = metadata.getAsDouble("association.numUnassociatedDiaObjects") + nUpdated 

147 self.assertEqual(meas.quantity, nUpdated / nTotal * u.dimensionless_unscaled) 

148 

149 def testNoUpdated(self): 

150 metadata = _makeAssociationMetadata(numUpdated=0) 

151 result = self.task.run(metadata) 

152 meas = result.measurement 

153 

154 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects")) 

155 self.assertEqual(meas.quantity, 0.0 * u.dimensionless_unscaled) 

156 

157 def testAllUpdated(self): 

158 metadata = _makeAssociationMetadata(numUnassociated=0) 

159 result = self.task.run(metadata) 

160 meas = result.measurement 

161 

162 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects")) 

163 self.assertEqual(meas.quantity, 1.0 * u.dimensionless_unscaled) 

164 

165 def testNoObjects(self): 

166 metadata = _makeAssociationMetadata(numUpdated=0, numUnassociated=0) 

167 result = self.task.run(metadata) 

168 meas = result.measurement 

169 

170 self.assertIsNone(meas) 

171 

172 def testMissingData(self): 

173 result = self.task.run(None) 

174 meas = result.measurement 

175 self.assertIsNone(meas) 

176 

177 def testAssociationFailed(self): 

178 result = self.task.run(PropertySet()) 

179 meas = result.measurement 

180 self.assertIsNone(meas) 

181 

182 def testBadlyTypedKeys(self): 

183 metadata = _makeAssociationMetadata() 

184 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer") 

185 

186 with self.assertRaises(MetricComputationError): 

187 self.task.run(metadata) 

188 

189 

190class TestTotalUnassociatedObjects(ApdbMetricTestCase): 

191 

192 @staticmethod 

193 def _makeApdb(dummy_dbInfo): 

194 """Create a dummy apdb. 

195 

196 We don't have access to the apdb in the task directly so mocking 

197 return values is difficult. We thus make use of the dummy dbInfo 

198 that is passed to the init task to pass values to the apdb object 

199 instantiated. 

200 """ 

201 apdb = unittest.mock.Mock(Apdb) 

202 test_value = dummy_dbInfo["test_value"] 

203 apdb.countUnassociatedObjects = unittest.mock.MagicMock( 

204 return_value=test_value) 

205 return apdb 

206 

207 @classmethod 

208 def makeTask(cls): 

209 class SimpleDbLoader(Task): 

210 ConfigClass = Config 

211 

212 def run(self, dummy): 

213 if dummy is not None: 

214 return Struct(apdb=cls._makeApdb(dummy)) 

215 else: 

216 return Struct(apdb=None) 

217 

218 config = TotalUnassociatedDiaObjectsMetricTask.ConfigClass() 

219 config.dbLoader.retarget(SimpleDbLoader) 

220 return TotalUnassociatedDiaObjectsMetricTask(config=config) 

221 

222 @classmethod 

223 def makeDbInfo(cls): 

224 return {"test_value": "whatever"} 

225 

226 def setUp(self): 

227 super().setUp() 

228 

229 def testValid(self): 

230 result = self.task.run([{"test_value": 42}]) 

231 meas = result.measurement 

232 

233 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects")) 

234 self.assertEqual(meas.quantity, 42 * u.count) 

235 

236 def testAllAssociated(self): 

237 result = self.task.run([{"test_value": 0}]) 

238 meas = result.measurement 

239 

240 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects")) 

241 self.assertEqual(meas.quantity, 0.0 * u.count) 

242 

243 def testMissingData(self): 

244 result = self.task.run(None) 

245 meas = result.measurement 

246 self.assertIsNone(meas) 

247 

248 def testFineGrainedMetric(self): 

249 with self.assertRaises(ValueError): 

250 self.task.run([self.makeDbInfo()], outputDataId={"visit": 42}) 

251 

252 

253# Hack around unittest's hacky test setup system 

254del MetricTaskTestCase 

255del MetadataMetricTestCase 

256del ApdbMetricTestCase 

257 

258 

259class MemoryTester(lsst.utils.tests.MemoryTestCase): 

260 pass 

261 

262 

263def setup_module(module): 

264 lsst.utils.tests.init() 

265 

266 

267if __name__ == "__main__": 267 ↛ 268line 267 didn't jump to line 268, because the condition on line 267 was never true

268 lsst.utils.tests.init() 

269 unittest.main()