Peano
Loading...
Searching...
No Matches
test_fuse_steps.py
Go to the documentation of this file.
1from peano4.api.taskgraph import Graph, Task
2from peano4.api.taskgraph.compiler import FuseSteps
3from peano4.api.solversteps import SynchroniseActionSets, get_task_groups
4
5
6VERTEX = Task.GridEntity.VERTEX
7FACE = Task.GridEntity.FACE
8CELL = Task.GridEntity.CELL
9
10READ_AND_DISCARD = Task.DataAccess.READ_AND_DISCARD
11WRITE = Task.DataAccess.WRITE
12READ_AND_WRITE = Task.DataAccess.READ_AND_WRITE
13TEMPORARY = Task.DataAccess.TEMPORARY
14
15
17 def __init__(self, name, descend_invocation_order=0):
18 self.name = name
19 self.descend_invocation_order = descend_invocation_order
20 self.parallel = False
21
23 return self.name
24
25
27 def __init__(self, name):
28 self.name = name
29
30
31def _chain(*entities):
32 """A chain of tasks t0 -> t1 -> ... with the given grid entities"""
33 graph = Graph()
34 tasks = [Task(name=f"t{i}", entity=entity) for i, entity in enumerate(entities)]
35 for previous_task, task in zip(tasks, tasks[1:]):
36 task.add_dependency(previous_task)
37 for task in tasks:
38 task.add_action_set(_FakeActionSet(task.name))
39 graph.add_task(task)
40 return graph, tasks
41
42
43def _groups(step):
44 """Names of the action sets per task group of the generated observer"""
45 return [
46 [action_set.name for action_set in group]
47 for group in get_task_groups(step.action_sets)
48 ]
49
50
51def _step_names(graph, non_fusable_task_pairs=None):
52 return [
53 step.name
54 for step in FuseSteps.translate(graph, "S", None, non_fusable_task_pairs)
55 ]
56
57
58def test_entity_order_fuses_vertex_face_cell_but_not_cell_vertex():
59 graph, _ = _chain(VERTEX, FACE, CELL, VERTEX, CELL)
60 assert _step_names(graph) == ["S_T0__T1__T2", "S_T3__T4"]
61
62
63def test_repeating_task_is_never_fused():
64 graph = Graph()
65 refine = Task("refine", CELL, task_has_to_repeat="condition")
66 initial_condition = Task("init", CELL)
67 initial_condition.add_dependency(refine)
68 graph.add_task(refine)
69 graph.add_task(initial_condition)
70 steps = FuseSteps.translate(graph, "S", None)
71 assert [step.name for step in steps] == ["S_Refine", "S_Init"]
72 assert steps[0].has_to_repeat == "condition"
73
74
75def test_non_fusable_pair_starts_new_traversal():
76 graph, tasks = _chain(CELL, CELL)
77 assert _step_names(graph) == ["S_T0__T1"]
78 assert _step_names(graph, [(tasks[1], tasks[0])]) == ["S_T0", "S_T1"]
79
80
81def test_cell_data_do_not_constrain_fusion():
82 graph, tasks = _chain(CELL, CELL)
83 patch = _FakeSubmodel("Q")
84 tasks[0].use_cell(patch, WRITE)
85 tasks[1].use_cell(patch, READ_AND_WRITE)
86 assert _step_names(graph) == ["S_T0__T1"]
87
88
89def test_face_data_written_then_read_prevents_fusion():
90 graph, tasks = _chain(CELL, CELL)
91 face_data = _FakeSubmodel("F")
92 tasks[0].use_face(face_data, WRITE)
93 tasks[1].use_face(face_data, READ_AND_DISCARD)
94 assert _step_names(graph) == ["S_T0", "S_T1"]
95
96
97def test_face_data_read_then_written_prevents_fusion():
98 """The reconstruction/projection pattern of a Finite Volume time step"""
99 graph, tasks = _chain(CELL, CELL, CELL)
100 face_data = _FakeSubmodel("F")
101 tasks[0].use_face(face_data, READ_AND_DISCARD)
102 tasks[2].use_face(face_data, WRITE)
103 assert _step_names(graph) == ["S_T0__T1", "S_T2"]
104
105
106def test_vertex_data_written_twice_prevents_fusion():
107 graph, tasks = _chain(CELL, CELL)
108 vertex_data = _FakeSubmodel("V")
109 tasks[0].use_vertex(vertex_data, WRITE)
110 tasks[1].use_vertex(vertex_data, WRITE)
111 assert _step_names(graph) == ["S_T0", "S_T1"]
112
113
114def test_face_data_read_twice_or_temporary_does_not_prevent_fusion():
115 graph, tasks = _chain(CELL, CELL, CELL)
116 face_data = _FakeSubmodel("F")
117 tasks[0].use_face(face_data, READ_AND_DISCARD)
118 tasks[1].use_face(face_data, READ_AND_DISCARD)
119 tasks[2].use_face(face_data, TEMPORARY)
120 assert _step_names(graph) == ["S_T0__T1__T2"]
121
122
123def test_independent_tasks_form_one_parallel_node():
124 graph = Graph()
125 first, second = Task("a", VERTEX), Task("b", CELL)
126 for task in (first, second):
127 task.add_action_set(_FakeActionSet(task.name))
128 graph.add_task(task)
129 steps = FuseSteps.translate(graph, "S", None)
130 assert [step.name for step in steps] == ["S_A__B"]
131 assert _groups(steps[0]) == [["a", "b"]]
132 assert all(action_set.parallel for action_set in steps[0].action_sets)
133
134
135def test_independent_tasks_with_conflicting_data_are_not_parallel():
136 graph = Graph()
137 first, second = Task("a", CELL), Task("b", CELL)
138 face_data = _FakeSubmodel("F")
139 first.use_face(face_data, WRITE)
140 second.use_face(face_data, READ_AND_DISCARD)
141 for task in (first, second):
142 task.add_action_set(_FakeActionSet(task.name))
143 graph.add_task(task)
144 steps = FuseSteps.translate(graph, "S", None)
145 assert [step.name for step in steps] == ["S_A", "S_B"]
146 assert not any(
147 action_set.parallel for step in steps for action_set in step.action_sets
148 )
149
150
151def test_independent_tasks_with_conflicting_cell_data_are_fused_but_not_parallel():
152 graph = Graph()
153 first, second, third = Task("a", CELL), Task("b", CELL), Task("c", CELL)
154 cell_data = _FakeSubmodel("Q")
155 first.use_cell(cell_data, READ_AND_WRITE)
156 second.use_cell(cell_data, READ_AND_WRITE)
157 third.use_cell(cell_data, READ_AND_DISCARD)
158 for task in (first, second, third):
159 task.add_action_set(_FakeActionSet(task.name))
160 graph.add_task(task)
161 steps = FuseSteps.translate(graph, "S", None)
162 assert [step.name for step in steps] == ["S_A__B__C"]
163 assert _groups(steps[0]) == [["a"], ["b"], ["c"]]
164 assert not any(action_set.parallel for action_set in steps[0].action_sets)
165
166
167def test_independent_tasks_reading_the_same_cell_data_are_parallel():
168 graph = Graph()
169 first, second = Task("a", CELL), Task("b", CELL)
170 cell_data = _FakeSubmodel("Q")
171 first.use_cell(cell_data, READ_AND_DISCARD)
172 second.use_cell(cell_data, READ_AND_DISCARD)
173 for task in (first, second):
174 task.add_action_set(_FakeActionSet(task.name))
175 graph.add_task(task)
176 steps = FuseSteps.translate(graph, "S", None)
177 assert _groups(steps[0]) == [["a", "b"]]
178
179
180def test_adjacent_parallel_nodes_are_separated_by_a_synchronisation_marker():
181 graph = Graph()
182 v1, v2, c1, c2 = (
183 Task("v1", VERTEX),
184 Task("v2", VERTEX),
185 Task("c1", CELL),
186 Task("c2", CELL),
187 )
188 c1.add_dependency(v1)
189 c2.add_dependency(v2)
190 for task in (v1, v2, c1, c2):
191 task.add_action_set(_FakeActionSet(task.name))
192 graph.add_task(task)
193 steps = FuseSteps.translate(graph, "S", None)
194 assert [step.name for step in steps] == ["S_V1__V2__C1__C2"]
195 assert [type(action_set) for action_set in steps[0].action_sets].count(
196 SynchroniseActionSets
197 ) == 1
198 assert isinstance(steps[0].action_sets[2], SynchroniseActionSets)
199 assert _groups(steps[0]) == [["v1", "v2"], ["c1", "c2"]]
200
201
202def test_task_with_several_action_sets_is_never_part_of_a_parallel_node():
203 graph = Graph()
204 first, second = Task("a", CELL), Task("b", CELL)
205 first.add_action_set(_FakeActionSet("a1"))
206 first.add_action_set(_FakeActionSet("a2"))
207 second.add_action_set(_FakeActionSet("b"))
208 graph.add_task(first)
209 graph.add_task(second)
210 steps = FuseSteps.translate(graph, "S", None)
211 assert [step.name for step in steps] == ["S_A__B"]
212 assert not any(action_set.parallel for action_set in steps[0].action_sets)
213 assert _groups(steps[0]) == [["a1"], ["a2"], ["b"]]
214
215
216def test_face_task_writing_face_data_fuses_with_subsequent_cell_task():
217 """Faces are touched for the first time before their adjacent cells, e.g. boundary conditions before a reconstruction"""
218 graph, tasks = _chain(FACE, CELL)
219 face_data = _FakeSubmodel("F")
220 tasks[0].use_face(face_data, READ_AND_WRITE)
221 tasks[1].use_face(face_data, READ_AND_DISCARD)
222 assert _step_names(graph) == ["S_T0__T1"]
223
224
225def test_face_task_reading_face_data_fuses_with_subsequent_cell_task_writing_it():
226 graph, tasks = _chain(FACE, CELL)
227 face_data = _FakeSubmodel("F")
228 tasks[0].use_face(face_data, READ_AND_DISCARD)
229 tasks[1].use_face(face_data, WRITE)
230 assert _step_names(graph) == ["S_T0__T1"]
231
232
233def test_face_tasks_with_conflicting_face_data_are_fused_but_not_parallel():
234 graph = Graph()
235 first, second = Task("a", FACE), Task("b", FACE)
236 face_data = _FakeSubmodel("F")
237 first.use_face(face_data, WRITE)
238 second.use_face(face_data, READ_AND_WRITE)
239 for task in (first, second):
240 task.add_action_set(_FakeActionSet(task.name))
241 graph.add_task(task)
242 steps = FuseSteps.translate(graph, "S", None)
243 assert [step.name for step in steps] == ["S_A__B"]
244 assert _groups(steps[0]) == [["a"], ["b"]]
245 assert not any(action_set.parallel for action_set in steps[0].action_sets)
246
247
248def test_cell_task_writing_face_data_is_not_fused_with_subsequent_face_task():
249 graph, tasks = _chain(CELL, FACE)
250 face_data = _FakeSubmodel("F")
251 tasks[0].use_face(face_data, WRITE)
252 tasks[1].use_face(face_data, READ_AND_DISCARD)
253 assert _step_names(graph) == ["S_T0", "S_T1"]
254
255
256def test_vertex_task_writing_vertex_data_fuses_with_subsequent_cell_task():
257 graph, tasks = _chain(VERTEX, CELL)
258 vertex_data = _FakeSubmodel("V")
259 tasks[0].use_vertex(vertex_data, WRITE)
260 tasks[1].use_vertex(vertex_data, READ_AND_DISCARD)
261 assert _step_names(graph) == ["S_T0__T1"]
262
263
264def test_face_task_writing_vertex_data_prevents_fusion():
265 """A vertex is adjacent to faces which are touched only after the cell"""
266 graph, tasks = _chain(FACE, CELL)
267 vertex_data = _FakeSubmodel("V")
268 tasks[0].use_vertex(vertex_data, WRITE)
269 tasks[1].use_vertex(vertex_data, READ_AND_DISCARD)
270 assert _step_names(graph) == ["S_T0", "S_T1"]
Holds a sequence of Task instances.
Definition Graph.py:9
One task within a peano4.api.taskgraph.Graph.
Definition Task.py:8
__init__(self, name, descend_invocation_order=0)
The Python-side model of "what happens during one mesh sweep".
Definition __init__.py:1
Compilers are simple functions.
Definition __init__.py:1
The task graph abstraction allows users to formalise their solver steps over small task graphs which ...
Definition __init__.py:1
_groups(step)
Names of the action sets per task group of the generated observer.
_chain(*entities)
A chain of tasks t0 -> t1 -> ... with the given grid entities.