Peano
Loading...
Searching...
No Matches
test_rkfd4_time_step.py
Go to the documentation of this file.
1import types
2
3import pytest
4
5import peano4.api
8
9import exahype2.api
10
11
12def _solver(time_step_size=None, rk_order=3, boundary="dirichlet"):
13 """!
14
15 @param boundary: str
16 "dirichlet", "sommerfeld", or None for a solver without boundary
17 condition term, i.e. for a domain that is periodic along all axes.
18
19 """
20 solver = exahype2.api.Solver("Probe", 2, 1)
21 solver.add_pde_term(exahype2.api.Flux(implementation="F[0] = Q[0]; F[1] = Q[1];"))
22 solver.add_pde_term(exahype2.api.MaxEigenvalue(implementation="*lambdaMax = 1.0;"))
23 if boundary == "dirichlet":
24 solver.add_pde_term(
25 exahype2.api.DirichletBoundaryConditions(implementation="Q[0] = 0.0;")
26 )
27 elif boundary == "sommerfeld":
28 solver.add_pde_term(
29 exahype2.api.SommerfeldBoundaryConditions(implementation="Q[0] = 0.0;")
30 )
32 solver, patch_size=4, rk_order=rk_order, time_step_size=time_step_size
33 )
34 return solver
35
36
37def _construct_time_step(solver, directory, periodic_BC=(False, False, False)):
38 exahype2_project = types.SimpleNamespace(periodic_BC=list(periodic_BC))
39 peano_project = peano4.api.Project(["probe"], "probe", str(directory))
40 time_step = peano4.api.taskgraph.Graph()
41 solver.numerical_scheme.construct_output(exahype2_project, peano_project, time_step)
42 return {task.name: task for task in time_step.tasks}
43
44
46 tasks = _construct_time_step(_solver(rk_order=3), tmp_path)
47 assert set(tasks) == {
48 "Probe_Apply Dirichlet BCs stage 0",
49 "Probe_Apply Dirichlet BCs stage 1",
50 "Probe_Apply Dirichlet BCs stage 2",
51 "Probe_Reconstruct stage 0",
52 "Probe_Reconstruct stage 1",
53 "Probe_Reconstruct stage 2",
54 "Probe_Evaluate right hand side stage 0",
55 "Probe_Evaluate right hand side stage 1",
56 "Probe_Evaluate right hand side stage 2",
57 "Probe_Prepare stage 1",
58 "Probe_Prepare stage 2",
59 "Probe_Complete time step",
60 "Probe_Project stage 1 onto faces",
61 "Probe_Project stage 2 onto faces",
62 "Probe_Project solution onto faces",
63 "Probe_Compute time step size",
64 "Probe_Dynamic mesh refinement",
65 }
66 for stage in [1, 2]:
67 assert (
68 tasks[f"Probe_Prepare stage {stage}"]
69 in tasks[f"Probe_Project stage {stage} onto faces"].dependencies
70 )
71 assert (
72 tasks[f"Probe_Project stage {stage} onto faces"]
73 in tasks[f"Probe_Reconstruct stage {stage}"].dependencies
74 )
75 # The boundary values have to be set after the previous stage's roll-over
76 assert (
77 tasks["Probe_Project stage 1 onto faces"]
78 in tasks["Probe_Apply Dirichlet BCs stage 1"].dependencies
79 )
80 assert (
81 tasks["Probe_Apply Dirichlet BCs stage 1"]
82 in tasks["Probe_Reconstruct stage 1"].dependencies
83 )
84 assert (
85 tasks["Probe_Project solution onto faces"]
86 in tasks["Probe_Compute time step size"].dependencies
87 )
88
89
90def _tagged(tasks, tag):
91 return {name for name, task in tasks.items() if tag in task.tags}
92
93
95 solver = _solver(rk_order=3)
96 tasks = _construct_time_step(solver, tmp_path)
97 final = _tagged(tasks, exahype2.api.Solver.SolutionUpdateTaskTag)
98 intermediate = _tagged(tasks, exahype2.api.Solver.IntermediateSolutionUpdateTag)
99 assert set(final) == {"Probe_Complete time step"}
100 assert set(intermediate) == {"Probe_Prepare stage 1", "Probe_Prepare stage 2"}
101 tagged = final | intermediate
102 # The combination must not project onto the faces itself: the projection
103 # has to see the postprocessed solution
104 for name in tagged:
105 assert not any(
107 for action_set in tasks[name].action_sets
108 )
109
110
112 tasks = _construct_time_step(_solver(rk_order=3), tmp_path)
113 [action_set] = tasks["Probe_Apply Dirichlet BCs stage 2"].action_sets
114 body = action_set.get_body_of_operation(
115 peano4.api.solversteps.ActionSet.OPERATION_TOUCH_FACE_FIRST_TIME
116 )
117 # c_2 = 1 for Kutta's third-order scheme
118 assert (
119 "repositories::instanceOfProbe.getMinTimeStamp() + 1.0 * "
120 "repositories::instanceOfProbe.getMinTimeStepSize()," in body
121 )
122
123
125 tasks = _construct_time_step(
126 _solver(rk_order=2, boundary=None), tmp_path, periodic_BC=(True, True, True)
127 )
128 assert not any("Dirichlet" in name for name in tasks)
129 assert not any(
130 isinstance(
131 action_set,
133 )
134 for task in tasks.values()
135 for action_set in task.action_sets
136 )
137
138
140 with pytest.raises(ValueError, match="all axes are periodic"):
141 _construct_time_step(_solver(), tmp_path, periodic_BC=(True, True, True))
142
143
144def _roll_overs(tasks):
145 return {
146 name: action_set
147 for name, task in tasks.items()
148 for action_set in task.action_sets
150 }
151
152
154 tasks = _construct_time_step(_solver(rk_order=3, boundary="sommerfeld"), tmp_path)
155 assert not any("Dirichlet" in name for name in tasks)
156 roll_overs = _roll_overs(tasks)
157 assert set(roll_overs) == {
158 "Probe_Project stage 1 onto faces",
159 "Probe_Project stage 2 onto faces",
160 "Probe_Project solution onto faces",
161 }
162 # Kutta's third-order scheme has c = 0, 1/2, 1, i.e. the roll overs
163 # advance the ghost grid cells by 1/2, 1/2 and 0 times the time step size
164 for name, increment in [
165 ("Probe_Project stage 1 onto faces", "0.5"),
166 ("Probe_Project stage 2 onto faces", "0.5"),
167 ("Probe_Project solution onto faces", "0.0"),
168 ]:
169 action_set = roll_overs[name]
170 assert isinstance(
171 action_set,
173 )
174 body = action_set.get_body_of_operation(
175 peano4.api.solversteps.ActionSet.OPERATION_TOUCH_FACE_LAST_TIME
176 )
177 assert "::exahype2::blockstructured::applySommerfeldBoundaryConditions(" in body
178 assert (
179 f"{increment} * repositories::instanceOfProbe.getMinTimeStepSize()," in body
180 )
181 assert "tarch::la::Vector<Dimensions, double>({0.0, 0.0, 0.0})" in body
182 # The Sommerfeld update has to precede the copy into the persistent data
183 assert body.index("applySommerfeldBoundaryConditions") < body.index(
184 "copyHalfOfHalo"
185 )
186
187
189 solver = _solver(rk_order=3, boundary="sommerfeld")
190 # The third-order scheme of Shu and Osher has c = 0, 1, 1/2
191 monkeypatch.setattr(
192 solver.numerical_scheme.butcher_tableau,
193 "relative_time_step_sizes",
194 lambda: [0.0, 1.0, 0.5],
195 )
196 with pytest.raises(ValueError, match="decrease"):
197 _construct_time_step(solver, tmp_path)
198
199
201 solver = _solver(rk_order=3)
202 monkeypatch.setattr(
203 solver.numerical_scheme.butcher_tableau,
204 "relative_time_step_sizes",
205 lambda: [0.0, 1.0, 0.5],
206 )
207 tasks = _construct_time_step(solver, tmp_path)
208 assert "Probe_Apply Dirichlet BCs stage 2" in tasks
209
210
212 solver = _solver(rk_order=4, boundary=None)
213 tasks = _construct_time_step(solver, tmp_path, periodic_BC=(True, True, True))
214 tableau = solver.numerical_scheme.butcher_tableau
215
216 def combination(task_name):
217 [action_set] = [
218 action_set
219 for action_set in tasks[task_name].action_sets
220 if isinstance(
222 )
223 ]
224 return action_set
225
226 for stage in range(1, 4):
227 action_set = combination(f"Probe_Prepare stage {stage}")
228 assert action_set.weights == tableau.weight_matrix()[stage]
229 assert action_set.output is solver._stage_input
230 final = combination("Probe_Complete time step")
231 assert final.weights == tableau.final_estimate_weights()
232 assert final.output is solver._patch
233
234
236 _construct_time_step(_solver(), tmp_path)
237 kernels = tmp_path / "kernels"
238 for name in [
239 "Probe_fd4_2d",
240 "Probe_fd4_3d",
241 "Probe_fd4_eigenvalue_2d",
242 "Probe_fd4_eigenvalue_3d",
243 "rungekutta_linear_combination",
244 ]:
245 assert (kernels / (name + ".h")).exists(), name
246 assert (kernels / (name + ".cpph")).exists(), name
247 wrapper = (kernels / "Probe_fd4.h").read_text()
248 assert '#include "Probe_fd4_2d.h"' in wrapper
249 assert '#include "Probe_fd4_eigenvalue_3d.h"' in wrapper
250 assert "Probe_rightHandSideWithFD4" in (kernels / "Probe_fd4_2d.cpph").read_text()
251
252
254 solver = _solver(time_step_size=0.01, boundary=None)
255 tasks = _construct_time_step(solver, tmp_path, periodic_BC=(True, True, True))
256 assert not (tmp_path / "kernels" / "Probe_fd4_eigenvalue_2d.h").exists()
257 assert not any(
259 for task in tasks.values()
260 for action_set in task.action_sets
261 )
262 [time_step_size] = tasks["Probe_Compute time step size"].action_sets
263 assert (
264 "const double admissibleTimeStepSize = 0.01;"
265 in time_step_size.get_body_of_unprepareTraversal()
266 )
267
268
270 solver = _solver(rk_order=4, boundary=None)
271 tasks = _construct_time_step(solver, tmp_path, periodic_BC=(True, True, True))
272 [action_set] = tasks["Probe_Evaluate right hand side stage 2"].action_sets
273 body = action_set.get_body_of_operation(
274 peano4.api.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME
275 )
276 # c_2 = 2/3 for Kutta's 3/8 rule, and each stage's estimate holds 2*4^d values
277 assert "getMinTimeStamp() + 0.6666666666666666 * timeStepSize" in body
278 assert "fineGridCellProbeQRhsEstimates.value + 64;" in body
279 assert "fineGridCellProbeQRhsEstimates.value + 256;" in body
280 assert (
281 "Probe_rightHandSideWithFD4<\n 4,\n 2,\n 2,\n 1\n>(\n patchData,\n [&]("
282 in body
283 )
284
285
287 solver = exahype2.api.Solver("Probe", 1, 0)
288 solver.add_pde_term(exahype2.api.Flux(implementation="F[0] = Q[0];"))
290 solver, patch_size=4, rk_order=2
291 )
292 with pytest.raises(AssertionError):
293 _construct_time_step(solver, tmp_path, periodic_BC=(True, True, True))
294
295
297 """!
298 Peano's stacks require that each stored datum is loaded by the next
299 sweep which uses it. The reconstruction of a later stage is the last
300 reader of the stage input, as the next combination writes it from
301 scratch, so it must not store it.
302 """
303 solver = _solver(rk_order=4, boundary=None)
304 tasks = _construct_time_step(solver, tmp_path, periodic_BC=(True, True, True))
305 Access = peano4.api.taskgraph.Task.DataAccess
306 assert (
307 tasks["Probe_Reconstruct stage 0"].cell_data_access[solver._patch]
308 == Access.READ_AND_WRITE
309 )
310 for stage in range(1, 4):
311 reconstruction = tasks[f"Probe_Reconstruct stage {stage}"]
312 assert (
313 reconstruction.cell_data_access[solver._stage_input]
314 == Access.READ_AND_DISCARD
315 )
316 assert (
317 tasks[f"Probe_Prepare stage {stage}"].cell_data_access[solver._stage_input]
318 == Access.WRITE
319 )
The conservative flux F(Q) of a hyperbolic PDE.
Definition Flux.py:8
The maximum absolute eigenvalue of the flux Jacobian in a given direction, i.e.
Abstract base class for all ExaHyPE 2 solvers.
Definition Solver.py:17
Fourth-order central Finite Differences with explicit Runge-Kutta time stepping, realised over a bloc...
Combine the solution with the right-hand side estimates of the Runge-Kutta stages.
Store the maximum eigenvalue over all grid cells of a patch.
Project a patch's data onto the overlap held on its surrounding faces, so the halo layers can be reco...
Copy patch_overlap_update - the face overlap exahype2.api.rkfd4.actionsets.ProjectPatchOntoFaces has ...
Represents a Peano 4 project.
Definition Project.py:23
Holds a sequence of Task instances.
Definition Graph.py:9
The Python-side model of "what happens during one mesh sweep".
Definition __init__.py:1
The task graph abstraction allows users to formalise their solver steps over small task graphs which ...
Definition __init__.py:1
test_dirichlet_accepts_decreasing_stage_time_stamps(tmp_path, monkeypatch)
test_stage_combinations_use_rows_of_butcher_tableau(tmp_path)
test_one_reconstruction_per_stage_and_chain_of_stages(tmp_path)
test_periodic_domain_needs_no_boundary_tasks(tmp_path)
test_fixed_time_step_size_skips_eigenvalue(tmp_path)
test_boundary_values_use_stage_time_stamp(tmp_path)
test_right_hand_side_uses_stage_time_stamp_and_estimate_slot(tmp_path)
test_stage_input_is_loaded_once_after_each_store(tmp_path)
Peano's stacks require that each stored datum is loaded by the next sweep which uses it.
test_sommerfeld_replaces_roll_over_and_omits_boundary_tasks(tmp_path)
test_stage_combinations_are_tagged_as_solution_updates(tmp_path)
test_adaptive_time_stepping_requires_max_eigenvalue(tmp_path)
test_periodic_domain_rejects_boundary_condition_term(tmp_path)
_solver(time_step_size=None, rk_order=3, boundary="dirichlet")
test_kernels_are_generated_for_both_dimensions(tmp_path)
test_sommerfeld_rejects_decreasing_stage_time_stamps(tmp_path, monkeypatch)
_construct_time_step(solver, directory, periodic_BC=(False, False, False))