Coverage for tests/qg_test_utils.py: 54%

68 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 09:26 +0000

1# This file is part of ctrl_bps. 

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"""QuantumGraph-related utilities to support ctrl_bps testing. 

22""" 

23 

24# Not actually running Quantum so do not need to override 'run' Method 

25# pylint: disable=abstract-method 

26 

27# Many dummy classes for testing. 

28# pylint: disable=missing-class-docstring 

29 

30import lsst.pipe.base.connectionTypes as cT 

31from lsst.daf.butler import Config, DataCoordinate, DatasetRef, DatasetType, DimensionUniverse, Quantum 

32from lsst.pex.config import Field 

33from lsst.pipe.base import PipelineTask, PipelineTaskConfig, PipelineTaskConnections, QuantumGraph, TaskDef 

34from lsst.utils.introspection import get_full_type_name 

35 

36METADATA = {"D1": [1, 2, 3]} 

37 

38 

39# For each dummy task, create a Connections, Config, and PipelineTask 

40 

41 

42class Dummy1Connections(PipelineTaskConnections, dimensions=("D1", "D2")): 

43 initOutput = cT.InitOutput(name="Dummy1InitOutput", storageClass="ExposureF", doc="n/a") 

44 input = cT.Input(name="Dummy1Input", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

45 output = cT.Output(name="Dummy1Output", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

46 

47 

48class Dummy1Config(PipelineTaskConfig, pipelineConnections=Dummy1Connections): 

49 conf1 = Field(dtype=int, default=1, doc="dummy config") 

50 

51 

52class Dummy1PipelineTask(PipelineTask): 

53 ConfigClass = Dummy1Config 

54 

55 

56class Dummy2Connections(PipelineTaskConnections, dimensions=("D1", "D2")): 

57 initInput = cT.InitInput(name="Dummy1InitOutput", storageClass="ExposureF", doc="n/a") 

58 initOutput = cT.InitOutput(name="Dummy2InitOutput", storageClass="ExposureF", doc="n/a") 

59 input = cT.Input(name="Dummy1Output", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

60 output = cT.Output(name="Dummy2Output", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

61 

62 

63class Dummy2Config(PipelineTaskConfig, pipelineConnections=Dummy2Connections): 

64 conf1 = Field(dtype=int, default=1, doc="dummy config") 

65 

66 

67class Dummy2PipelineTask(PipelineTask): 

68 ConfigClass = Dummy2Config 

69 

70 

71class Dummy3Connections(PipelineTaskConnections, dimensions=("D1", "D2")): 

72 initInput = cT.InitInput(name="Dummy2InitOutput", storageClass="ExposureF", doc="n/a") 

73 initOutput = cT.InitOutput(name="Dummy3InitOutput", storageClass="ExposureF", doc="n/a") 

74 input = cT.Input(name="Dummy2Output", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

75 output = cT.Output(name="Dummy3Output", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

76 

77 

78class Dummy3Config(PipelineTaskConfig, pipelineConnections=Dummy3Connections): 

79 conf1 = Field(dtype=int, default=1, doc="dummy config") 

80 

81 

82class Dummy3PipelineTask(PipelineTask): 

83 ConfigClass = Dummy3Config 

84 

85 

86# Test if a Task that does not interact with the other Tasks works fine in 

87# the graph. 

88class Dummy4Connections(PipelineTaskConnections, dimensions=("D1", "D2")): 

89 input = cT.Input(name="Dummy4Input", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

90 output = cT.Output(name="Dummy4Output", storageClass="ExposureF", doc="n/a", dimensions=("D1", "D2")) 

91 

92 

93class Dummy4Config(PipelineTaskConfig, pipelineConnections=Dummy4Connections): 

94 conf1 = Field(dtype=int, default=1, doc="dummy config") 

95 

96 

97class Dummy4PipelineTask(PipelineTask): 

98 ConfigClass = Dummy4Config 

99 

100 

101def make_test_quantum_graph(run: str = "run"): 

102 """Create a QuantumGraph for unit tests. 

103 

104 Parameters 

105 ---------- 

106 run : `str`, optional 

107 Name of the RUN collection for output datasets. 

108 

109 Returns 

110 ------- 

111 qgraph : `lsst.pipe.base.QuantumGraph` 

112 A test QuantumGraph looking like the following: 

113 (DummyTask4 is completely independent.) 

114 

115 Numbers in parens are the values for the two dimensions (D1, D2). 

116 

117 T1(1,2) T1(3,4) T4(1,2) T4(3,4) 

118 | | 

119 T2(1,2) T2(3,4) 

120 | | 

121 T3(1,2) T3(3,4) 

122 """ 

123 config = Config( 

124 { 

125 "version": 1, 

126 "skypix": { 

127 "common": "htm7", 

128 "htm": { 

129 "class": "lsst.sphgeom.HtmPixelization", 

130 "max_level": 24, 

131 }, 

132 }, 

133 "elements": { 

134 "D1": { 

135 "keys": [ 

136 { 

137 "name": "id", 

138 "type": "int", 

139 } 

140 ], 

141 "storage": { 

142 "cls": "lsst.daf.butler.registry.dimensions.table.TableDimensionRecordStorage", 

143 }, 

144 }, 

145 "D2": { 

146 "keys": [ 

147 { 

148 "name": "id", 

149 "type": "int", 

150 } 

151 ], 

152 "storage": { 

153 "cls": "lsst.daf.butler.registry.dimensions.table.TableDimensionRecordStorage", 

154 }, 

155 }, 

156 }, 

157 "packers": {}, 

158 } 

159 ) 

160 

161 universe = DimensionUniverse(config=config) 

162 # need to make a mapping of TaskDef to set of quantum 

163 quantum_map = {} 

164 tasks = [] 

165 # Map to keep output/intermediate refs. 

166 intermediate_refs: dict[tuple[DatasetType, DataCoordinate], DatasetRef] = {} 

167 for task, label in ( 

168 (Dummy1PipelineTask, "T1"), 

169 (Dummy2PipelineTask, "T2"), 

170 (Dummy3PipelineTask, "T3"), 

171 (Dummy4PipelineTask, "T4"), 

172 ): 

173 task_def = TaskDef(get_full_type_name(task), task.ConfigClass(), task, label) 

174 tasks.append(task_def) 

175 quantum_set = set() 

176 for dim1, dim2 in ((1, 2), (3, 4)): 

177 if task_def.connections.initInputs: 

178 init_init_ds_type = DatasetType( 

179 task_def.connections.initInput.name, 

180 tuple(), 

181 storageClass=task_def.connections.initInput.storageClass, 

182 universe=universe, 

183 ) 

184 init_refs = [DatasetRef(init_init_ds_type, DataCoordinate.makeEmpty(universe), run=run)] 

185 else: 

186 init_refs = None 

187 input_ds_type = DatasetType( 

188 task_def.connections.input.name, 

189 task_def.connections.input.dimensions, 

190 storageClass=task_def.connections.input.storageClass, 

191 universe=universe, 

192 ) 

193 data_id = DataCoordinate.standardize({"D1": dim1, "D2": dim2}, universe=universe) 

194 if ref := intermediate_refs.get((input_ds_type, data_id)): 

195 input_refs = [ref] 

196 else: 

197 input_refs = [DatasetRef(input_ds_type, data_id, run=run)] 

198 output_ds_type = DatasetType( 

199 task_def.connections.output.name, 

200 task_def.connections.output.dimensions, 

201 storageClass=task_def.connections.output.storageClass, 

202 universe=universe, 

203 ) 

204 ref = DatasetRef(output_ds_type, data_id, run=run) 

205 intermediate_refs[(output_ds_type, data_id)] = ref 

206 output_refs = [ref] 

207 quantum_set.add( 

208 Quantum( 

209 taskName=task.__qualname__, 

210 dataId=data_id, 

211 taskClass=task, 

212 initInputs=init_refs, 

213 inputs={input_ds_type: input_refs}, 

214 outputs={output_ds_type: output_refs}, 

215 ) 

216 ) 

217 quantum_map[task_def] = quantum_set 

218 qgraph = QuantumGraph(quantum_map, metadata=METADATA) 

219 

220 return qgraph