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 daf_butler. 

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 

22import unittest 

23import copy 

24import pickle 

25import itertools 

26 

27from lsst.daf.butler.core.utils import NamedKeyDict 

28from lsst.daf.butler.core.dimensions import DimensionUniverse, DimensionGraph, Dimension 

29from lsst.daf.butler.core.dimensions.schema import makeElementTableSpec 

30 

31 

32class DimensionTestCase(unittest.TestCase): 

33 """Tests for dimensions. 

34 

35 All tests here rely on the content of ``config/dimensions.yaml``, either 

36 to test that the definitions there are read in properly or just as generic 

37 data for testing various operations. 

38 """ 

39 

40 def setUp(self): 

41 self.universe = DimensionUniverse() 

42 

43 def checkGraphInvariants(self, graph): 

44 elements = list(graph.elements) 

45 for n, element in enumerate(elements): 

46 # Ordered comparisons on graphs behave like sets. 

47 self.assertLessEqual(element.graph, graph) 

48 # Ordered comparisons on elements correspond to the ordering within 

49 # a DimensionUniverse (topological, with deterministic 

50 # tiebreakers). 

51 for other in elements[:n]: 

52 self.assertLess(other, element) 

53 self.assertLessEqual(other, element) 

54 for other in elements[n + 1:]: 

55 self.assertGreater(other, element) 

56 self.assertGreaterEqual(other, element) 

57 self.assertEqual(DimensionGraph(self.universe, graph.required), graph) 

58 self.assertCountEqual(graph.required, 

59 [dimension for dimension in graph.dimensions 

60 if not any(dimension in other.graph.implied for other in graph.elements)]) 

61 self.assertCountEqual(graph.implied, graph.dimensions - graph.required) 

62 self.assertCountEqual(graph.dimensions, 

63 [element for element in graph.elements 

64 if isinstance(element, Dimension)]) 

65 self.assertCountEqual(graph.dimensions, itertools.chain(graph.required, graph.implied)) 

66 

67 def testConfigRead(self): 

68 self.assertEqual(self.universe.dimensions.names, 

69 {"instrument", "visit", "exposure", "detector", "physical_filter", 

70 "abstract_filter", "subfilter", "calibration_label", 

71 "skymap", "tract", "patch", "htm7", "htm9"}) 

72 

73 def testGraphs(self): 

74 self.checkGraphInvariants(self.universe.empty) 

75 self.checkGraphInvariants(self.universe) 

76 for element in self.universe.elements: 

77 self.checkGraphInvariants(element.graph) 

78 

79 def testInstrumentDimensions(self): 

80 graph = DimensionGraph(self.universe, names=("exposure", "detector", "calibration_label")) 

81 self.assertCountEqual(graph.dimensions.names, 

82 ("instrument", "exposure", "detector", "calibration_label", 

83 "visit", "physical_filter", "abstract_filter")) 

84 self.assertCountEqual(graph.required.names, ("instrument", "exposure", "detector", 

85 "calibration_label")) 

86 self.assertCountEqual(graph.implied.names, ("visit", "physical_filter", "abstract_filter")) 

87 self.assertCountEqual(graph.elements.names - graph.dimensions.names, ("visit_detector_region",)) 

88 

89 def testCalibrationDimensions(self): 

90 graph = DimensionGraph(self.universe, names=("calibration_label", "physical_filter", "detector")) 

91 self.assertCountEqual(graph.dimensions.names, 

92 ("instrument", "detector", "calibration_label", 

93 "physical_filter", "abstract_filter")) 

94 self.assertCountEqual(graph.required.names, ("instrument", "detector", "calibration_label", 

95 "physical_filter")) 

96 self.assertCountEqual(graph.implied.names, ("abstract_filter",)) 

97 self.assertCountEqual(graph.elements.names, graph.dimensions.names) 

98 

99 def testObservationDimensions(self): 

100 graph = DimensionGraph(self.universe, names=("exposure", "detector")) 

101 self.assertCountEqual(graph.dimensions.names, ("instrument", "detector", "visit", "exposure", 

102 "physical_filter", "abstract_filter")) 

103 self.assertCountEqual(graph.required.names, ("instrument", "detector", "exposure")) 

104 self.assertCountEqual(graph.implied.names, ("physical_filter", "abstract_filter", "visit")) 

105 self.assertCountEqual(graph.elements.names - graph.dimensions.names, ("visit_detector_region",)) 

106 self.assertCountEqual(graph.spatial.names, ("visit_detector_region",)) 

107 self.assertCountEqual(graph.getSpatial(independent=False).names, 

108 ("visit", "visit_detector_region",)) 

109 self.assertCountEqual(graph.getSpatial(prefer=("visit",)).names, ("visit",)) 

110 self.assertCountEqual(graph.getTemporal(independent=False).names, ("visit", "exposure")) 

111 self.assertCountEqual(graph.temporal.names, ("exposure",)) 

112 self.assertCountEqual(graph.getTemporal(prefer=("visit",)).names, ("visit",)) 

113 

114 def testSkyMapDimensions(self): 

115 graph = DimensionGraph(self.universe, names=("patch",)) 

116 self.assertCountEqual(graph.dimensions.names, ("skymap", "tract", "patch")) 

117 self.assertCountEqual(graph.required.names, ("skymap", "tract", "patch")) 

118 self.assertCountEqual(graph.implied.names, ()) 

119 self.assertCountEqual(graph.elements.names, graph.dimensions.names) 

120 self.assertCountEqual(graph.spatial.names, ("patch",)) 

121 self.assertCountEqual(graph.getSpatial(independent=False).names, ("patch", "tract")) 

122 self.assertCountEqual(graph.getSpatial(prefer=("tract",)).names, ("tract",)) 

123 

124 def testSubsetCalculation(self): 

125 """Test that independent spatial and temporal options are computed 

126 correctly. 

127 """ 

128 graph = DimensionGraph(self.universe, names=("visit", "detector", "tract", "patch", "htm7", 

129 "exposure", "calibration_label")) 

130 self.assertCountEqual(graph.spatial.names, 

131 ("visit_detector_region", "patch", "htm7")) 

132 self.assertCountEqual(graph.getSpatial(independent=False).names, 

133 ("visit_detector_region", "patch", "htm7", "visit", "tract")) 

134 self.assertCountEqual(graph.getSpatial(prefer=["tract"]).names, 

135 ("visit_detector_region", "tract", "htm7")) 

136 self.assertCountEqual(graph.temporal.names, 

137 ("exposure", "calibration_label")) 

138 self.assertCountEqual(graph.getTemporal(independent=False).names, 

139 ("visit", "exposure", "calibration_label")) 

140 self.assertCountEqual(graph.getTemporal(prefer=["visit"]).names, 

141 ("visit", "calibration_label")) 

142 

143 def testSchemaGeneration(self): 

144 tableSpecs = NamedKeyDict({}) 

145 for element in self.universe.elements: 

146 if element.hasTable and element.viewOf is None: 

147 tableSpecs[element] = makeElementTableSpec(element) 

148 for element, tableSpec in tableSpecs.items(): 

149 for dep in element.graph.required: 

150 with self.subTest(element=element.name, dep=dep.name): 

151 if dep != element: 

152 self.assertIn(dep.name, tableSpec.fields) 

153 self.assertEqual(tableSpec.fields[dep.name].dtype, dep.primaryKey.dtype) 

154 self.assertEqual(tableSpec.fields[dep.name].length, dep.primaryKey.length) 

155 self.assertEqual(tableSpec.fields[dep.name].nbytes, dep.primaryKey.nbytes) 

156 self.assertFalse(tableSpec.fields[dep.name].nullable) 

157 self.assertTrue(tableSpec.fields[dep.name].primaryKey) 

158 else: 

159 self.assertIn(element.primaryKey.name, tableSpec.fields) 

160 self.assertEqual(tableSpec.fields[element.primaryKey.name].dtype, 

161 dep.primaryKey.dtype) 

162 self.assertEqual(tableSpec.fields[element.primaryKey.name].length, 

163 dep.primaryKey.length) 

164 self.assertEqual(tableSpec.fields[element.primaryKey.name].nbytes, 

165 dep.primaryKey.nbytes) 

166 self.assertFalse(tableSpec.fields[element.primaryKey.name].nullable) 

167 self.assertTrue(tableSpec.fields[element.primaryKey.name].primaryKey) 

168 for dep in element.implied: 

169 with self.subTest(element=element.name, dep=dep.name): 

170 self.assertIn(dep.name, tableSpec.fields) 

171 self.assertEqual(tableSpec.fields[dep.name].dtype, dep.primaryKey.dtype) 

172 self.assertFalse(tableSpec.fields[dep.name].primaryKey) 

173 for foreignKey in tableSpec.foreignKeys: 

174 self.assertIn(foreignKey.table, tableSpecs) 

175 self.assertIn(foreignKey.table, element.graph.dimensions.names) 

176 self.assertEqual(len(foreignKey.source), len(foreignKey.target)) 

177 for source, target in zip(foreignKey.source, foreignKey.target): 

178 self.assertIn(source, tableSpec.fields.names) 

179 self.assertIn(target, tableSpecs[foreignKey.table].fields.names) 

180 self.assertEqual(tableSpec.fields[source].dtype, 

181 tableSpecs[foreignKey.table].fields[target].dtype) 

182 self.assertEqual(tableSpec.fields[source].length, 

183 tableSpecs[foreignKey.table].fields[target].length) 

184 self.assertEqual(tableSpec.fields[source].nbytes, 

185 tableSpecs[foreignKey.table].fields[target].nbytes) 

186 self.assertEqual(tuple(tableSpec.fields.names), element.RecordClass.__slots__) 

187 

188 def testPickling(self): 

189 # Pickling and copying should always yield the exact same object within 

190 # a single process (cross-process is impossible to test here). 

191 universe1 = DimensionUniverse() 

192 universe2 = pickle.loads(pickle.dumps(universe1)) 

193 universe3 = copy.copy(universe1) 

194 universe4 = copy.deepcopy(universe1) 

195 self.assertIs(universe1, universe2) 

196 self.assertIs(universe1, universe3) 

197 self.assertIs(universe1, universe4) 

198 for element1 in universe1.elements: 

199 element2 = pickle.loads(pickle.dumps(element1)) 

200 self.assertIs(element1, element2) 

201 graph1 = element1.graph 

202 graph2 = pickle.loads(pickle.dumps(graph1)) 

203 self.assertIs(graph1, graph2) 

204 

205 

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

207 unittest.main()