Peano
Loading...
Searching...
No Matches
test_boundary_conditions.py
Go to the documentation of this file.
1"""Boundary condition terms of exahype2.api and how the numerical schemes
2realise them (@ref exahype2.api.blockstructured.BoundaryConditions)."""
3import types
4
5import pytest
6
7import peano4.api
10
11import exahype2.api
12
13
14TOUCH_FACE_LAST_TIME = peano4.api.solversteps.ActionSet.OPERATION_TOUCH_FACE_LAST_TIME
15
16
17def _project(periodic_BC):
18 return types.SimpleNamespace(periodic_BC=list(periodic_BC))
19
20
21def _solver(*boundary_terms, max_eigenvalue=True):
22 solver = exahype2.api.Solver("Probe", 2, 1)
23 solver.add_pde_term(exahype2.api.Flux(implementation="F[0] = Q[0]; F[1] = Q[1];"))
24 solver.add_pde_term(
26 implementation="Q[0] = 1.0; Q[1] = 0.0; Q[2] = 0.0;"
27 )
28 )
29 if max_eigenvalue:
30 solver.add_pde_term(
31 exahype2.api.MaxEigenvalue(implementation="*lambdaMax = 1.0;")
32 )
33 for term in boundary_terms:
34 solver.add_pde_term(term)
35 return solver
36
37
38def _sommerfeld(**kwargs):
39 return exahype2.api.SommerfeldBoundaryConditions(
40 implementation="Q[0] = 1.0; Q[1] = 0.0;", **kwargs
41 )
42
43
45 return exahype2.api.DirichletBoundaryConditions(implementation="Q[0] = 1.0;")
46
47
49 has_domain_boundary = exahype2.api.blockstructured.BoundaryConditions.has_domain_boundary
50 assert not has_domain_boundary(_project([True, True, True]))
51 assert has_domain_boundary(_project([True, False, True]))
52 assert has_domain_boundary(_project([False, False, False]))
53
54
56 get_boundary_conditions = exahype2.api.blockstructured.BoundaryConditions.get_boundary_conditions
57 dirichlet = _dirichlet()
58 assert (
59 get_boundary_conditions(_solver(dirichlet), _project([False] * 3)) is dirichlet
60 )
61 sommerfeld = _sommerfeld()
62 assert (
63 get_boundary_conditions(_solver(sommerfeld), _project([True, False]))
64 is sommerfeld
65 )
66 assert get_boundary_conditions(_solver(), _project([True] * 3)) is None
67
68
69@pytest.mark.parametrize(
70 "solver, periodic_BC, message",
71 [
72 (lambda: _solver(_dirichlet()), [True] * 3, "all axes are periodic"),
73 (lambda: _solver(_sommerfeld()), [True] * 3, "all axes are periodic"),
74 (lambda: _solver(), [False] * 3, "no boundary condition PDE term"),
75 (
76 lambda: _solver(_dirichlet(), _sommerfeld()),
77 [False] * 3,
78 "more than one boundary condition term",
79 ),
80 (
81 lambda: _solver(_sommerfeld(), max_eigenvalue=False),
82 [False] * 3,
83 "no non-stateless MaxEigenvalue",
84 ),
85 (
86 lambda: _solver(_sommerfeld(origin=[0.0, 0.0])),
87 [False] * 3,
88 "has 2 entries, but the domain has 3 dimensions",
89 ),
90 ],
91)
93 solver, periodic_BC, message
94):
95 with pytest.raises(ValueError, match=message):
97 solver(), _project(periodic_BC)
98 )
99
100
102 term = _sommerfeld(origin=[1.0, 2.0])
103 assert term.name == "sommerfeldFarFieldSolution"
104 assert term.get_origin(2) == [1.0, 2.0]
105 assert _sommerfeld().get_origin(3) == [0.0, 0.0, 0.0]
106 declaration = term.generator.generate_declaration()
107 assert "virtual void sommerfeldFarFieldSolution(" in declaration
108 assert "const tarch::la::Vector<Dimensions, double>& x" in declaration
109
110
112 scheme_factory, grid_construction_factory, solver, periodic_BC, tmp_path
113):
114 """! Setup and time step task graphs of solver over the unit cube"""
115 solver.numerical_scheme = scheme_factory(solver)
116 solver.grid_construction = grid_construction_factory(solver)
117 exahype2_project = exahype2.api.Project(
118 ["probe"], "probe", "probe", directory=str(tmp_path)
119 )
120 exahype2_project.set_global_simulation_parameters(
121 dimensions=3,
122 offset=[0.0, 0.0, 0.0],
123 size=[1.0, 1.0, 1.0],
124 min_end_time=1.0,
125 periodic_BC=periodic_BC,
126 )
127 peano_project = peano4.api.Project(["probe"], "probe", str(tmp_path))
128 graphs = [peano4.api.taskgraph.Graph() for _ in range(4)]
129 solver.generate_solver_realisation(exahype2_project, peano_project, *graphs)
130 return [{task.name: task for task in graph.tasks} for graph in graphs[:2]]
131
132
133SCHEMES = {
134 "fv": (
135 lambda solver: exahype2.api.fv.RusanovWithExplicitEuler(solver, 4),
137 solver, volume_size=0.1
138 ),
140 ),
141 "optimistic fv": (
144 solver, volume_size=0.1
145 ),
147 ),
148 "rkfd4": (
150 solver, patch_size=4, rk_order=2
151 ),
153 solver, volume_size=0.1
154 ),
156 ),
157}
158
159
160def _roll_over_bodies(tasks, actionsets):
161 return [
162 action_set.get_body_of_operation(TOUCH_FACE_LAST_TIME)
163 for task in tasks.values()
164 for action_set in task.action_sets
165 if isinstance(action_set, actionsets.RollOverUpdatedFace)
166 ]
167
168
169@pytest.mark.parametrize("scheme", SCHEMES)
171 scheme_factory, grid_construction_factory, actionsets = SCHEMES[scheme]
172 setup, time_step = _realisation(
173 scheme_factory,
174 grid_construction_factory,
175 _solver(_sommerfeld(origin=[0.5, 0.5, 0.5])),
176 [False, False, False],
177 tmp_path,
178 )
179 assert not any("Dirichlet" in name for name in time_step)
180
181 # The setup initialises the ghost cells with the initial condition
182 [setup_roll_over] = _roll_over_bodies(setup, actionsets)
183 assert "applyDirichletBoundaryConditions(" in setup_roll_over
184 assert "initPointWisely(x, Q)" in setup_roll_over
185 assert "applySommerfeldBoundaryConditions(" not in setup_roll_over
186
187 # Every roll over of the time step advances the ghost cells first
188 roll_overs = _roll_over_bodies(time_step, actionsets)
189 assert roll_overs
190 for body in roll_overs:
191 assert "applySommerfeldBoundaryConditions(" in body
192 assert "sommerfeldFarFieldSolution(x, Q)" in body
193 assert "maxEigenvalue(Q, x, normal, &lambdaMax)" in body
194 assert "tarch::la::Vector<Dimensions, double>({0.5, 0.5, 0.5})" in body
195 assert body.index("applySommerfeldBoundaryConditions") < body.index(
196 "copyHalfOfHalo"
197 )
198
199
201 return {
202 task.face_data_access[solver._patch_overlap_current_time_step]
203 for task in tasks.values()
204 if solver._patch_overlap_current_time_step in task.face_data
205 }
206
207
208@pytest.mark.parametrize("scheme", SCHEMES)
210 """! The roll over reads the old face data, so no task may discard it,
211 and the projection has to load it"""
212 scheme_factory, grid_construction_factory, actionsets = SCHEMES[scheme]
213 solver = _solver(_sommerfeld())
214 setup, time_step = _realisation(
215 scheme_factory, grid_construction_factory, solver, [False] * 3, tmp_path
216 )
217 assert _accesses_to_persistent_face_data(solver, time_step) == {
218 peano4.api.taskgraph.Task.DataAccess.READ_AND_WRITE
219 }
220
221
222@pytest.mark.parametrize("scheme", SCHEMES)
224 scheme_factory, grid_construction_factory, actionsets = SCHEMES[scheme]
225 solver = _solver(_dirichlet())
226 setup, time_step = _realisation(
227 scheme_factory, grid_construction_factory, solver, [False] * 3, tmp_path
228 )
229 assert (
230 peano4.api.taskgraph.Task.DataAccess.READ_AND_DISCARD
231 in _accesses_to_persistent_face_data(solver, time_step)
232 )
233
234
235@pytest.mark.parametrize("scheme", SCHEMES)
237 scheme_factory, grid_construction_factory, actionsets = SCHEMES[scheme]
238 setup, time_step = _realisation(
239 scheme_factory,
240 grid_construction_factory,
242 [False, False, False],
243 tmp_path,
244 )
245 assert any("Apply Dirichlet BCs" in name for name in time_step)
246 for body in _roll_over_bodies(setup, actionsets) + _roll_over_bodies(
247 time_step, actionsets
248 ):
249 assert "BoundaryConditions(" not in body
250
251
252@pytest.mark.parametrize("scheme", SCHEMES)
254 scheme_factory, grid_construction_factory, actionsets = SCHEMES[scheme]
255 setup, time_step = _realisation(
256 scheme_factory,
257 grid_construction_factory,
258 _solver(),
259 [True, True, True],
260 tmp_path,
261 )
262 assert not any("Dirichlet" in name for name in time_step)
263 for body in _roll_over_bodies(setup, actionsets) + _roll_over_bodies(
264 time_step, actionsets
265 ):
266 assert "BoundaryConditions(" not in body
267
268
269@pytest.mark.parametrize("scheme", SCHEMES)
271 scheme_factory, grid_construction_factory, actionsets = SCHEMES[scheme]
272 with pytest.raises(ValueError, match="all axes are periodic"):
274 scheme_factory,
275 grid_construction_factory,
277 [True, True, True],
278 tmp_path,
279 )
The conservative flux F(Q) of a hyperbolic PDE.
Definition Flux.py:8
Point-wise initial condition: given the physical position of a single degree of freedom,...
The maximum absolute eigenvalue of the flux Jacobian in a given direction, i.e.
ExaHyPE 2 project.
Definition Project.py:15
Abstract base class for all ExaHyPE 2 solvers.
Definition Solver.py:17
Finite Volumes with a Rusanov Riemann solver and explicit Euler time stepping, realised over a block-...
Finite Volumes with a Rusanov Riemann solver and optimistic explicit Euler time stepping,...
Setup phase counterpart to RusanovWithExplicitEuler: sets the initial condition for a Finite Volume s...
Fourth-order central Finite Differences with explicit Runge-Kutta time stepping, realised over a bloc...
Setup phase of the fourth-order Finite Differences with Runge-Kutta time stepping (exahype2....
Represents a Peano 4 project.
Definition Project.py:23
Holds a sequence of Task instances.
Definition Graph.py:9
get_boundary_conditions(solver, exahype2_project)
The solver's boundary condition term, or None if the domain has no boundary.
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_sommerfeld_keeps_old_face_data_alive(scheme, tmp_path)
The roll over reads the old face data, so no task may discard it, and the projection has to load it.
test_sommerfeld_replaces_roll_over_of_setup_and_time_step(scheme, tmp_path)
test_dirichlet_keeps_boundary_task_and_plain_roll_over(scheme, tmp_path)
_realisation(scheme_factory, grid_construction_factory, solver, periodic_BC, tmp_path)
Setup and time step task graphs of solver over the unit cube.
test_periodic_domain_rejects_boundary_condition_term(scheme, tmp_path)
test_dirichlet_discards_face_data_after_reconstruction(scheme, tmp_path)
test_periodic_domain_has_no_boundary_treatment(scheme, tmp_path)
test_get_boundary_conditions_rejects_inconsistent_terms(solver, periodic_BC, message)
_solver(*boundary_terms, max_eigenvalue=True)