Coverage for tests/qg_test_utils.py: 54%
68 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-16 09:09 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-16 09:09 +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"""
24# Not actually running Quantum so do not need to override 'run' Method
25# pylint: disable=abstract-method
27# Many dummy classes for testing.
28# pylint: disable=missing-class-docstring
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
36METADATA = {"D1": [1, 2, 3]}
39# For each dummy task, create a Connections, Config, and PipelineTask
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"))
48class Dummy1Config(PipelineTaskConfig, pipelineConnections=Dummy1Connections):
49 conf1 = Field(dtype=int, default=1, doc="dummy config")
52class Dummy1PipelineTask(PipelineTask):
53 ConfigClass = Dummy1Config
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"))
63class Dummy2Config(PipelineTaskConfig, pipelineConnections=Dummy2Connections):
64 conf1 = Field(dtype=int, default=1, doc="dummy config")
67class Dummy2PipelineTask(PipelineTask):
68 ConfigClass = Dummy2Config
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"))
78class Dummy3Config(PipelineTaskConfig, pipelineConnections=Dummy3Connections):
79 conf1 = Field(dtype=int, default=1, doc="dummy config")
82class Dummy3PipelineTask(PipelineTask):
83 ConfigClass = Dummy3Config
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"))
93class Dummy4Config(PipelineTaskConfig, pipelineConnections=Dummy4Connections):
94 conf1 = Field(dtype=int, default=1, doc="dummy config")
97class Dummy4PipelineTask(PipelineTask):
98 ConfigClass = Dummy4Config
101def make_test_quantum_graph(run: str = "run"):
102 """Create a QuantumGraph for unit tests.
104 Parameters
105 ----------
106 run : `str`, optional
107 Name of the RUN collection for output datasets.
109 Returns
110 -------
111 qgraph : `lsst.pipe.base.QuantumGraph`
112 A test QuantumGraph looking like the following:
113 (DummyTask4 is completely independent.)
115 Numbers in parens are the values for the two dimensions (D1, D2).
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 )
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)
220 return qgraph