Coverage for tests/test_CatalogCalculation.py: 44%

95 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-08 02:59 -0800

1# This file is part of meas_base. 

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 lsst.utils.tests 

24 

25import lsst.meas.base.catalogCalculation as catCalc 

26import lsst.afw.table as afwTable 

27from lsst.meas.base.pluginRegistry import register 

28from lsst.meas.base import MeasurementError 

29 

30 

31@register("FailcatalogCalculation") 

32class FailCC(catCalc.CatalogCalculationPlugin): 

33 """Plugin which is guaranteed to fail, testing the failure framework. 

34 """ 

35 @classmethod 

36 def getExecutionOrder(cls): 

37 return cls.DEFAULT_CATALOGCALCULATION 

38 

39 def __init__(self, config, name, schema, metadata): 

40 catCalc.CatalogCalculationPlugin.__init__(self, config, name, schema, metadata) 

41 self.failKey = schema.addField(name + "_fail", type="Flag", doc="Failure test") 

42 

43 def calculate(self, measRecord): 

44 # note: flagbit doesn't matter, since a FlagHandler isn't used 

45 raise MeasurementError("Supposed to fail", 0) 

46 

47 def fail(self, measRecord, error=None): 

48 measRecord.set(self.failKey, True) 

49 

50 

51@register("singleRecordCatalogCalculation") 

52class SingleRecordCC(catCalc.CatalogCalculationPlugin): 

53 """Test plugin which operates on single records. 

54 

55 Takes a single record, reads a value, squares it, and writes out the 

56 results to the record. 

57 """ 

58 @classmethod 

59 def getExecutionOrder(cls): 

60 return cls.DEFAULT_CATALOGCALCULATION 

61 

62 def __init__(self, config, name, schema, metadata): 

63 catCalc.CatalogCalculationPlugin.__init__(self, config, name, schema, metadata) 

64 self.failKey = schema.addField(name + "_fail", type="Flag", doc="Failure flag") 

65 self.squareKey = schema.addField(name + "_square", type="D", doc="Square of input catalog") 

66 

67 def calculate(self, measRecord): 

68 value = measRecord.get("start") 

69 measRecord.set(self.squareKey, value**2) 

70 

71 def fail(self, measRecord, error=None): 

72 measRecord.set(self.failKey, True) 

73 

74 

75@register("multiRecordCatalogCalculation") 

76class MultiRecordAb(catCalc.CatalogCalculationPlugin): 

77 """Test plugin which operates on multiple records. 

78 

79 This plugin takes the whole source catalog at once, and loops over the 

80 catalog internally. The algorithm simply reads a value, cubes it, and 

81 writes the results out to the table. 

82 """ 

83 plugType = 'multi' 

84 

85 @classmethod 

86 def getExecutionOrder(cls): 

87 return cls.DEFAULT_CATALOGCALCULATION 

88 

89 def __init__(self, config, name, schema, metadata): 

90 catCalc.CatalogCalculationPlugin.__init__(self, config, name, schema, metadata) 

91 self.failKey = schema.addField(name + "_fail", type="Flag", doc="Failure flag") 

92 self.cubeKey = schema.addField(name + "_cube", type="D", doc="Cube of input catalog") 

93 

94 def calculate(self, catalog): 

95 for rec in catalog: 

96 value = rec.get("start") 

97 rec.set(self.cubeKey, value**3) 

98 

99 def fail(self, catalog, error=None): 

100 for rec in catalog: 

101 rec.set(self.failKey, True) 

102 

103 

104@register("dependentCatalogCalulation") 

105class DependentAb(catCalc.CatalogCalculationPlugin): 

106 """Test plugin which depends on a previous plugin execution. 

107 

108 Used to test runlevel resolution. This plugin takes in single records, 

109 reads a value calculated by a previous plugin, computes a square root, and 

110 writes the results to the table. 

111 """ 

112 @classmethod 

113 def getExecutionOrder(cls): 

114 return cls.DEFAULT_CATALOGCALCULATION + 1 

115 

116 def __init__(self, config, name, schema, metadata): 

117 catCalc.CatalogCalculationPlugin.__init__(self, config, name, schema, metadata) 

118 self.failKey = schema.addField(name + "_fail", type="Flag", doc="Failure flag") 

119 self.sqrtKey = schema.addField(name + "_sqrt", type="D", 

120 doc="Square root of singleRecord catalogCalculation") 

121 

122 def calculate(self, measRecord): 

123 value = measRecord.get("singleRecordCatalogCalculation_square") 

124 measRecord.set(self.sqrtKey, value**0.5) 

125 

126 def fail(self, measRecord, error=None): 

127 measRecord.set(self.failKey, True) 

128 

129 

130class CatalogCalculationTest(unittest.TestCase): 

131 """Test the catalogCalculation framework using plugins defined above. 

132 """ 

133 def setUp(self): 

134 # Create a schema object, and populate it with a field to simulate 

135 # results from measurements on an image 

136 schema = afwTable.SourceTable.makeMinimalSchema() 

137 schema.addField("start", type="D") 

138 # Instantiate a config object adding each of the above plugins, and 

139 # use it to create a task 

140 catCalcConfig = catCalc.CatalogCalculationConfig() 

141 catCalcConfig.plugins.names = ["FailcatalogCalculation", "singleRecordCatalogCalculation", 

142 "multiRecordCatalogCalculation", "dependentCatalogCalulation"] 

143 catCalcTask = catCalc.CatalogCalculationTask(schema=schema, config=catCalcConfig) 

144 # Create a catalog with five sources as input to the task 

145 self.catalog = afwTable.SourceCatalog(schema) 

146 self.numObjects = 5 

147 for i in range(self.numObjects): 

148 rec = self.catalog.addNew() 

149 rec.set("start", float(i + 1)) 

150 

151 # Run the catalogCalculation task, outputs will be checked in test 

152 # methods 

153 catCalcTask.run(self.catalog) 

154 

155 def testCatalogCalculation(self): 

156 # Verify the failure flag got set for the plugin expected to fail 

157 self.assertEqual(len(self.catalog), self.numObjects) 

158 for src in self.catalog: 

159 self.assertTrue(src.get("FailcatalogCalculation_fail")) 

160 

161 # Verify the single record plugin ran successfully 

162 for rec in self.catalog: 

163 self.assertAlmostEqual(rec.get("start")**2, rec.get("singleRecordCatalogCalculation_square"), 4) 

164 

165 # Verify that the system correctly handled a plugin which expects a 

166 # full catalog to be passed 

167 for rec in self.catalog: 

168 self.assertAlmostEqual(rec.get("start")**3, rec.get("multiRecordCatalogCalculation_cube"), 4) 

169 

170 # Verify that the system runs plugins in the correct run order 

171 for rec in self.catalog: 

172 self.assertAlmostEqual(rec.get("start"), rec.get("dependentCatalogCalulation_sqrt"), 4) 

173 

174 def tearDown(self): 

175 del self.catalog, self.numObjects, 

176 

177 

178class TestMemory(lsst.utils.tests.MemoryTestCase): 

179 pass 

180 

181 

182def setup_module(module): 

183 lsst.utils.tests.init() 

184 

185 

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

187 lsst.utils.tests.init() 

188 unittest.main()