Peano
Loading...
Searching...
No Matches
20_additional-solver-steps.py
Go to the documentation of this file.
1# This file is part of the Peano project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3"""!
4
5Create the PNG illustrations of the tutorial 20_additional-solver-steps.ipynb
6
7The script writes the following pictures into the directory of this script:
8
9- 20_additional-solver-steps-fv-task-graph.png: the time stepping task graph
10 of the Finite Volumes with a density floor as additional solver step;
11- 20_additional-solver-steps-rk-task-graph.png: the same for the
12 fourth-order Finite Differences with a Runge-Kutta scheme of order two,
13 where the floor follows each Runge-Kutta stage;
14- 20_additional-solver-steps-profile.png: the density along y=0 after one
15 period with and without the density floor.
16
17Call it from anywhere:
18
19 python3 tutorials/exahype2/illustrations/20_additional-solver-steps.py
20
21All builds and outputs go into a temporary directory (or --work-directory).
22A run whose directory already holds the output is not repeated. The script
23builds four executables, i.e. it runs for a few minutes.
24
25"""
26import argparse
27import os
28import subprocess
29import sys
30import tempfile
31import types
32
33Here = os.path.dirname(os.path.abspath(__file__))
34PeanoRoot = os.path.abspath(os.path.join(Here, "..", "..", ".."))
35sys.path.insert(0, os.path.join(PeanoRoot, "src"))
36
37import matplotlib
38
39matplotlib.use("Agg")
40import matplotlib.pyplot as plt
41import numpy as np
42
43import peano4.api
45import exahype2.api
48
49
50Flux = """
51 const double irho = 1.0 / Q[0];
52 constexpr double gamma = 1.4;
53 const double p = (gamma - 1.0) * (Q[3] - 0.5 * irho * (Q[1] * Q[1] + Q[2] * Q[2]));
54 switch (normal) {
55 case 0:
56 F[0] = Q[1];
57 F[1] = irho * Q[1] * Q[1] + p;
58 F[2] = irho * Q[1] * Q[2];
59 F[3] = irho * Q[1] * (Q[3] + p);
60 break;
61 case 1:
62 F[0] = Q[2];
63 F[1] = irho * Q[2] * Q[1];
64 F[2] = irho * Q[2] * Q[2] + p;
65 F[3] = irho * Q[2] * (Q[3] + p);
66 break;
67 }
68"""
69
70MaxEigenvalue = """
71 const double irho = 1.0 / Q[0];
72 constexpr double gamma = 1.4;
73 const double p = (gamma - 1.0) * (Q[3] - 0.5 * irho * (Q[1] * Q[1] + Q[2] * Q[2]));
74 const double c = std::sqrt(gamma * p * irho);
75 const double u = Q[1 + normal] * irho;
76 lambdaMax[0] = std::max(std::abs(u + c), std::abs(u - c));
77"""
78
79InitPointWisely = """
80 constexpr double gamma = 1.4;
81 constexpr double p = 1.0;
82 constexpr double u = 1.0;
83 const double rho = 1.0 + 0.2 * std::sin(2.0 * M_PI * (x[0] + 0.1) / 0.2);
84 Q[0] = rho;
85 Q[1] = rho * u;
86 Q[2] = 0.0;
87 Q[3] = p / (gamma - 1) + 0.5 * rho * u * u;
88"""
89
90DensityFloor = """
91 constexpr double gamma = 1.4;
92 constexpr double minDensity = 0.9;
93 if (Q[0] < minDensity) {
94 const double u = Q[1] / Q[0];
95 const double v = Q[2] / Q[0];
96 const double p = (gamma - 1.0) * (Q[3] - 0.5 * Q[0] * (u * u + v * v));
97 Q[0] = minDensity;
98 Q[1] = minDensity * u;
99 Q[2] = minDensity * v;
100 Q[3] = p / (gamma - 1.0) + 0.5 * minDensity * (u * u + v * v);
101 }
102"""
103
104
105
106def insert_density_floor(solver, time_step, tag, patch):
107 """!
108
109 Insert the density floor after each task of solver which carries tag
110
111 Mirrors the routine of the tutorial: The numerical scheme knows which
112 patch its tagged tasks write and passes it in.
113
114 """
115 for anchor in time_step.find_tasks(tag):
116 if not anchor.name.startswith(solver.name + "_"):
117 continue
119 name=f"{solver.name}_Density floor after {anchor.name[len(solver.name) + 1:]}",
120 entity=peano4.api.taskgraph.Task.GridEntity.CELL,
121 )
122 task.use_cell(patch, peano4.api.taskgraph.Task.DataAccess.READ_AND_WRITE)
123 task.add_action_set(
124 toolbox.blockstructured.api.ApplyPointWiselyToPatch(
125 patch=patch,
126 implementation=DensityFloor,
127 guard=solver.grid_selector.createCellGuard(),
128 additional_includes=solver._get_default_includes()
129 + solver.user_solver_includes,
130 )
131 )
132 time_step.insert_after(anchor, task)
133
134
136 """! Finite Volumes which insert the density floor after each update"""
137
138 def construct_output(self, exahype2_project, peano_project, time_step):
139 super().construct_output(exahype2_project, peano_project, time_step)
142 time_step,
143 exahype2.api.Solver.SolutionUpdateTaskTag,
144 self._solver_solver_solver._patch,
145 )
146
147
149 """! Runge-Kutta Finite Differences which insert the density floor after each stage"""
150
151 def construct_output(self, exahype2_project, peano_project, time_step):
152 super().construct_output(exahype2_project, peano_project, time_step)
155 time_step,
156 exahype2.api.Solver.SolutionUpdateTaskTag,
157 self._solver_solver_solver._patch,
158 )
161 time_step,
162 exahype2.api.Solver.IntermediateSolutionUpdateTag,
163 self._solver_solver_solver._stage_input,
164 )
165
166
167DomainOffset = -0.1
168DomainWidth = 0.2
169VolumeSize = 0.0075
170EndTime = 0.2
171
172
174 return 1.0 + 0.2 * np.sin(
175 2.0 * np.pi * (np.asarray(x) - t - DomainOffset) / DomainWidth
176 )
177
178
179def create_solver(name, rk_order, density_floor):
180 """!
181
182 Create the Euler solver of the tutorial
183
184 @param rk_order: int or None
185 None selects the Finite Volumes, an integer the fourth-order Finite
186 Differences with the Runge-Kutta scheme of this order.
187
188 """
189 solver = exahype2.api.Solver(name, 4, 0)
190 solver.add_user_solver_includes("#include <cmath>\n#include <algorithm>\n")
191 solver.add_pde_term(exahype2.api.Flux(implementation=Flux, stateless=False))
192 solver.add_pde_term(
193 exahype2.api.MaxEigenvalue(implementation=MaxEigenvalue, stateless=False)
194 )
195 solver.add_pde_term(
196 exahype2.api.InitPointWisely(implementation=InitPointWisely, stateless=False)
197 )
198 if rk_order is None:
199 scheme = (
200 RusanovWithDensityFloor
201 if density_floor
203 )
204 solver.numerical_scheme = scheme(solver, 9)
205 solver.grid_construction = exahype2.api.fv.SetupSimulationOverRegularGrid(
206 solver, volume_size=VolumeSize
207 )
208 solver.plotter = exahype2.api.fv.PlotAsVTKFile(
209 solver, first_plot_time_stamp=0.0, time_in_between_plots=0.05
210 )
211 else:
212 scheme = (
213 RungeKuttaWithDensityFloor
214 if density_floor
216 )
217 solver.numerical_scheme = scheme(solver, patch_size=9, rk_order=rk_order)
218 solver.grid_construction = exahype2.api.rkfd4.SetupSimulationOverRegularGrid(
219 solver, volume_size=VolumeSize
220 )
221 solver.plotter = exahype2.api.rkfd4.PlotAsVTKFile(
222 solver, first_plot_time_stamp=0.0, time_in_between_plots=0.05
223 )
224 return solver
225
226
227def plot_task_graph(rk_order, file_name, title):
228 """! Construct the time stepping task graph including the density floor and draw it"""
229 peano4.api.Logger().file_name = os.path.join(
230 tempfile.gettempdir(), "20-additional-solver-steps-log.md"
231 )
232 with tempfile.TemporaryDirectory() as directory:
233 peano_project = peano4.api.Project(
234 ["illustration"], "illustration", directory=directory
235 )
236 solver = create_solver("Euler", rk_order, density_floor=True)
237 exahype2_project = types.SimpleNamespace(periodic_BC=[True, True])
238 time_step = peano4.api.taskgraph.Graph()
239 solver.numerical_scheme.construct_output(
240 exahype2_project, peano_project, time_step
241 )
242 time_step.visualise(title=title)
243 output = os.path.join(Here, file_name)
244 plt.gcf().savefig(output, dpi=90)
245 plt.close(plt.gcf())
246 print("Wrote", output)
247
248
249def build_and_run(rk_order, density_floor, directory, peano_root):
250 name = "EulerFV" if rk_order is None else "EulerFD4"
251 patch_file = os.path.join(directory, "solution-{}.peano-patch-file".format(name))
252 if os.path.exists(patch_file):
253 print("reuse {}".format(directory))
254 return patch_file
255 print("build and run {}".format(directory))
256 os.makedirs(directory, exist_ok=True)
257 project = exahype2.api.Project(
258 namespace=["tutorials", "exahype2", "additionalsteps"],
259 project_name="AdditionalSteps",
260 directory=directory,
261 executable="additional-steps",
262 )
263 project.add_solver(create_solver(name, rk_order, density_floor))
264 project.set_global_simulation_parameters(
265 dimensions=2,
266 offset=[DomainOffset, DomainOffset],
267 size=[DomainWidth, DomainWidth],
268 min_end_time=EndTime,
269 max_end_time=EndTime,
270 periodic_BC=[True, True, True],
271 )
272 project.set_Peano4_installation(peano_root, peano4.api.output.CompileMode.RELEASE)
273 peano4_project = project.generate_Peano4_project(
274 verbosity=peano4.api.LoggerVerbosity.TEXT
275 )
276 peano4_project.generate()
277 subprocess.run(
278 ["make", "-j4"], cwd=directory, check=True, stdout=subprocess.DEVNULL
279 )
280 subprocess.run(
281 ["./additional-steps"], cwd=directory, check=True, stdout=subprocess.DEVNULL
282 )
283 return patch_file
284
285
286def density_along_x_axis(patch_file):
287 """! Return time stamp plus grid cell centres and densities along y=0 of the last snapshot"""
288 time_stamp, files = tarch.plotter.api.notebook.read_snapshots(patch_file)[-1]
289 x, rho = [], []
290 for file_name in files:
291 for offset, size, values in tarch.plotter.api.notebook.read_patches(file_name):
292 size = np.array([DomainWidth / round(DomainWidth / s) for s in size])
293 offset = (
294 np.round((np.array(offset) - DomainOffset) / size) * size + DomainOffset
295 )
296 dofs = values.shape[0]
297 if offset[1] <= 0.0 < offset[1] + size[1]:
298 row = int((0.0 - offset[1]) / (size[1] / dofs))
299 x += list(offset[0] + (np.arange(dofs) + 0.5) * size[0] / dofs)
300 rho += list(values[row, :, 0])
301 order = np.argsort(x)
302 return time_stamp, np.array(x)[order], np.array(rho)[order]
303
304
305if __name__ == "__main__":
306 parser = argparse.ArgumentParser(
307 description="Create the PNGs of the tutorial 20_additional-solver-steps.ipynb"
308 )
309 parser.add_argument(
310 "--work-directory",
311 default=None,
312 help="Directory for builds and outputs (default: temporary directory)",
313 )
314 parser.add_argument(
315 "--peano-root",
316 default=PeanoRoot,
317 help="Configured and built Peano installation to link against",
318 )
319 args = parser.parse_args()
320
322 None,
323 "20_additional-solver-steps-fv-task-graph.png",
324 "Finite Volumes with a density floor",
325 )
327 2,
328 "20_additional-solver-steps-rk-task-graph.png",
329 "Runge-Kutta of order two with a density floor",
330 )
331
332 import contextlib
333
334 with contextlib.ExitStack() as stack:
335 work_directory = os.path.abspath(
336 args.work_directory or stack.enter_context(tempfile.TemporaryDirectory())
337 )
338 fig, axes = plt.subplots(1, 2, figsize=(11, 4), sharey=True)
339 for ax, rk_order, title in [
340 (axes[0], None, "Finite Volumes"),
341 (axes[1], 4, "Finite Differences, Runge-Kutta 4"),
342 ]:
343 x_fine = np.linspace(DomainOffset, DomainOffset + DomainWidth, 400)
344 ax.plot(
345 x_fine, exact_density(x_fine, EndTime), "k-", linewidth=1, label="exact"
346 )
347 for density_floor, marker in [(False, "o"), (True, "s")]:
348 directory = os.path.join(
349 work_directory,
350 "{}-{}".format(
351 "fv" if rk_order is None else "rk4",
352 "floor" if density_floor else "plain",
353 ),
354 )
355 patch_file = build_and_run(
356 rk_order, density_floor, directory, os.path.abspath(args.peano_root)
357 )
358 t, x, rho = density_along_x_axis(patch_file)
359 print(
360 "{}: t={:.4f}, min density={:.4f}, max density={:.4f}".format(
361 directory, t, rho.min(), rho.max()
362 )
363 )
364 ax.plot(
365 x,
366 rho,
367 marker,
368 markersize=3,
369 label="with density floor" if density_floor else "without",
370 )
371 ax.axhline(0.9, color="tab:red", linestyle="--", linewidth=1, label="floor")
372 ax.set_title(title)
373 ax.set_xlabel("x (along y=0)")
374 ax.set_xticks([-0.1, -0.05, 0.0, 0.05, 0.1])
375 axes[0].set_ylabel("density after one period")
376 axes[1].legend(loc="upper left", bbox_to_anchor=(1.01, 1.0))
377 output = os.path.join(Here, "20_additional-solver-steps-profile.png")
378 fig.savefig(output, dpi=100, bbox_inches="tight")
379 plt.close(fig)
380 print("Wrote", output)
Runge-Kutta Finite Differences which insert the density floor after each stage.
construct_output(self, exahype2_project, peano_project, time_step)
Add the data to the Peano 4 project, generate the kernels and add the tasks of one Runge-Kutta step t...
Finite Volumes which insert the density floor after each update.
construct_output(self, exahype2_project, peano_project, time_step)
Has to be implemented by subclass.
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
Plotting counterpart to RusanovWithExplicitEuler: maps the patch data of a Finite Volume scheme with ...
Finite Volumes with a Rusanov Riemann solver and explicit Euler time stepping, realised over a block-...
_solver
Build the reconstruction tasks befilling the patch_with_halo, which is logically temporary.
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...
Plotting counterpart of the fourth-order Finite Differences with Runge-Kutta time stepping (exahype2....
Setup phase of the fourth-order Finite Differences with Runge-Kutta time stepping (exahype2....
Singleton logger for the Peano 4 Python API.
Definition Logger.py:37
Represents a Peano 4 project.
Definition Project.py:23
Holds a sequence of Task instances.
Definition Graph.py:9
One task within a peano4.api.taskgraph.Graph.
Definition Task.py:8
build_and_run(rk_order, density_floor, directory, peano_root)
plot_task_graph(rk_order, file_name, title)
Construct the time stepping task graph including the density floor and draw it.
create_solver(name, rk_order, density_floor)
Create the Euler solver of the tutorial.
density_along_x_axis(patch_file)
Return time stamp plus grid cell centres and densities along y=0 of the last snapshot.
insert_density_floor(solver, time_step, tag, patch)
Insert the density floor after each task of solver which carries tag.
The task graph abstraction allows users to formalise their solver steps over small task graphs which ...
Definition __init__.py:1
Interactive visualisation of Peano patch files within Jupyter notebooks.
Definition notebook.py:1
read_patches(patch_file_name)
Return list of (offset, size, values) for all patches of one patch file.
Definition notebook.py:49
read_snapshots(file_name)
Return list of (time stamp, list of patch files) from a patch meta file.
Definition notebook.py:30