5Create the PNG illustrations of the tutorial 12_higher-order-solvers.ipynb
7The script builds the Euler setup of the tutorials 10_euler and
812_higher-order-solvers with the first-order Finite Volumes and with the
9fourth-order Finite Differences plus Runge-Kutta schemes of order one to
10four, runs them on three meshes, and writes the following pictures into
11the directory of this script:
13- 12_higher-order-solvers-convergence.png: error over mesh width;
14- 12_higher-order-solvers-profile.png: density along y=0 after one period;
15- 12_higher-order-solvers-instability.png: the error over time of the
16 explicit Euler time stepping (Runge-Kutta of order one).
18The PDE terms are injected from Python, i.e. we do not need any hand-written
19C++ file. Call it from anywhere:
21 python3 tutorials/exahype2/illustrations/12_higher-order-solvers.py
23All builds and outputs go into a temporary directory (or --work-directory).
24A run whose directory already holds the output is not repeated, so you can
25rerun the script with the same --work-directory after you have changed a
26plot. The script builds 20 executables, i.e. it runs for roughly 40 minutes.
36Here = os.path.dirname(os.path.abspath(__file__))
37PeanoRoot = os.path.abspath(os.path.join(Here,
"..",
"..",
".."))
38sys.path.insert(0, os.path.join(PeanoRoot,
"src"))
43import matplotlib.pyplot
as plt
52 const double irho = 1.0 / Q[0];
53 constexpr double gamma = 1.4;
54 const double p = (gamma - 1.0) * (Q[3] - 0.5 * irho * (Q[1] * Q[1] + Q[2] * Q[2]));
58 F[1] = irho * Q[1] * Q[1] + p;
59 F[2] = irho * Q[1] * Q[2];
60 F[3] = irho * Q[1] * (Q[3] + p);
64 F[1] = irho * Q[2] * Q[1];
65 F[2] = irho * Q[2] * Q[2] + p;
66 F[3] = irho * Q[2] * (Q[3] + p);
72 const double irho = 1.0 / Q[0];
73 constexpr double gamma = 1.4;
74 const double p = (gamma - 1.0) * (Q[3] - 0.5 * irho * (Q[1] * Q[1] + Q[2] * Q[2]));
75 const double c = std::sqrt(gamma * p * irho);
76 const double u = Q[1 + normal] * irho;
77 lambdaMax[0] = std::max(std::abs(u + c), std::abs(u - c));
81 constexpr double gamma = 1.4;
82 constexpr double p = 1.0;
83 constexpr double u = 1.0;
84 const double rho = 1.0 + 0.2 * std::sin(2.0 * M_PI * (x[0] + 0.1) / 0.2);
88 Q[3] = p / (gamma - 1) + 0.5 * rho * u * u;
96VolumeSizes = [(0.0075, 27), (0.0025, 81), (0.00083, 243)]
98ConvergenceEndTime = 0.05
102RungeKuttaColours = {1:
"C3", 2:
"C1", 3:
"C2", 4:
"C0"}
106 """! Density of the entropy wave advected with u=1 over [-0.1,0.1]"""
107 return 1.0 + 0.2 * np.sin(
108 2.0 * np.pi * (np.asarray(x) - t - DomainOffset) / DomainWidth
113 """! Name of the solver: EulerFV for rk_order None, EulerFD4 otherwise"""
114 return "EulerFV" if rk_order
is None else "EulerFD4"
117def patch_file(directory, rk_order):
119 directory,
"solution-{}.peano-patch-file".format(
solver_name(rk_order))
124 rk_order, volume_size, end_time, time_in_between_plots, directory, peano_root
128 Generate, compile and run one setup within directory
130 @param rk_order: int or None
131 None selects the Finite Volumes of the tutorial 10_euler, an integer
132 the fourth-order Finite Differences with the Runge-Kutta scheme of this
135 @param peano_root: str
136 Configured and built Peano directory the executable links against.
139 if os.path.exists(patch_file(directory, rk_order)):
140 print(
"reuse {}".format(directory))
142 print(
"build and run {}".format(directory))
143 os.makedirs(directory, exist_ok=
True)
145 namespace=[
"tutorials",
"exahype2",
"higherorder"],
146 project_name=
"HigherOrder",
148 executable=
"higher-order",
151 solver.add_user_solver_includes(
"#include <cmath>\n#include <algorithm>\n")
162 solver, volume_size=volume_size
166 first_plot_time_stamp=0.0,
167 time_in_between_plots=time_in_between_plots,
171 solver, patch_size=9, rk_order=rk_order
174 solver, volume_size=volume_size
179 first_plot_time_stamp=0.0,
180 time_in_between_plots=time_in_between_plots,
181 plotter_precision=14,
183 project.add_solver(solver)
184 project.set_global_simulation_parameters(
186 offset=[DomainOffset, DomainOffset],
187 size=[DomainWidth, DomainWidth],
188 min_end_time=end_time,
189 max_end_time=end_time,
190 periodic_BC=[
True,
True,
True],
192 project.set_Peano4_installation(peano_root, peano4.api.output.CompileMode.RELEASE)
193 peano4_project = project.generate_Peano4_project(
194 verbosity=peano4.api.LoggerVerbosity.TEXT
196 peano4_project.generate()
197 subprocess.run([
"make",
"-j"], cwd=directory, check=
True, stdout=subprocess.DEVNULL)
199 [
"./higher-order"], cwd=directory, check=
True, stdout=subprocess.DEVNULL
206 Snap a patch's offset and size onto the regular mesh over the domain
208 The patch files hold offsets and sizes with seven significant digits
209 only. Below an error of roughly 1e-7, this would dominate the error.
212 size = np.array([DomainWidth / round(DomainWidth / s)
for s
in size])
213 offset = np.round((np.array(offset) - DomainOffset) / size) * size + DomainOffset
218 """! Return a list of (time stamp, max error, rms error) of the density per snapshot"""
221 patch_file(directory, rk_order)
224 for file_name
in files:
228 offset, size =
snap(offset, size)
229 dofs = values.shape[0]
230 x = offset[0] + (np.arange(dofs) + 0.5) * size[0] / dofs
231 squared_errors.append(
234 squared_errors = np.concatenate([e.flatten()
for e
in squared_errors])
238 math.sqrt(np.max(squared_errors)),
239 math.sqrt(np.mean(squared_errors)),
246 """! Return time stamp plus grid cell centres and densities along y=0"""
248 patch_file(directory, rk_order)
251 for file_name
in files:
253 offset, size =
snap(offset, size)
254 dofs = values.shape[0]
255 if offset[1] <= 0.0 < offset[1] + size[1]:
256 row = int((0.0 - offset[1]) / (size[1] / dofs))
257 x += list(offset[0] + (np.arange(dofs) + 0.5) * size[0] / dofs)
258 rho += list(values[row, :, 0])
259 order = np.argsort(x)
260 return time_stamp, np.array(x)[order], np.array(rho)[order]
265 return "Finite Volumes (Rusanov, explicit Euler)"
266 return "FD4 with Runge-Kutta of order {}".format(rk_order)
272 Max error at t=ConvergenceEndTime over the grid cells per axis
275 Maps rk_order (None for Finite Volumes) onto a list of
276 (grid cells per axis, directory).
279 fig, ax = plt.subplots(figsize=(7, 5))
280 for rk_order, directories
in runs.items():
281 cells = [c
for c, _
in directories]
282 max_errors = [
errors(d, rk_order)[-1][1]
for _, d
in directories]
283 style = {
"marker":
"o",
"linestyle":
"-"}
285 style = {
"marker":
"s",
"linestyle":
"--"}
293 "markerfacecolor":
"none",
298 color=
"k" if rk_order
is None else RungeKuttaColours[rk_order],
299 label=
label(rk_order),
303 cells = np.array([c
for _, c
in VolumeSizes], dtype=float)
304 for order, anchor
in [(1, 1.5e-1), (2, 1e-5), (4, 1e-4)]:
307 anchor * (cells[0] / cells) ** order,
313 "order {}".format(order),
314 (cells[-1], anchor * (cells[0] / cells[-1]) ** order),
315 textcoords=
"offset points",
320 ax.set_xticklabels([str(int(c))
for c
in cells])
322 ax.set_xlabel(
"grid cells (volumes) per axis")
323 ax.set_ylabel(
"max |density - exact density| at t={}".format(ConvergenceEndTime))
324 ax.set_title(
"Convergence for the entropy wave")
325 ax.legend(loc=
"lower left", fontsize=
"small")
327 os.path.join(Here,
"12_higher-order-solvers-convergence.png"),
337 Density along y=0 after one period on the coarsest mesh
340 List of (rk_order, directory).
343 fig, ax = plt.subplots(figsize=(7, 4))
344 x_exact = np.linspace(DomainOffset, DomainOffset + DomainWidth, 400)
345 ax.plot(x_exact,
exact_density(x_exact, 0.0),
"k-", linewidth=1, label=
"exact")
346 for rk_order, directory
in runs:
351 "s" if rk_order
is None else "o",
352 color=
"grey" if rk_order
is None else RungeKuttaColours[rk_order],
354 label=
label(rk_order),
357 ax.set_ylabel(
"density")
359 "Density along y=0 at t={} on {}x{} grid cells".format(
360 PeriodEndTime, VolumeSizes[0][1], VolumeSizes[0][1]
363 ax.legend(loc=
"lower left", fontsize=
"small")
365 os.path.join(Here,
"12_higher-order-solvers-profile.png"),
375 Max error over time for the explicit Euler time stepping vs the others
378 List of (rk_order, directory) on the same mesh.
381 fig, (left, right) = plt.subplots(1, 2, figsize=(12, 4))
382 for rk_order, directory
in runs:
383 e = np.array(
errors(directory, rk_order))
388 color=RungeKuttaColours[rk_order],
389 label=
label(rk_order),
392 left.set_ylabel(
"max |density - exact density|")
393 left.set_title(
"Error over time on {0}x{0} grid cells".format(VolumeSizes[1][1]))
394 left.legend(loc=
"upper left", fontsize=
"small")
396 rk_order, directory = runs[0]
402 color=RungeKuttaColours[rk_order],
405 right.set_xlabel(
"x")
406 right.set_ylabel(
"density - exact density")
407 right.set_title(
"Error along y=0 at t={:.3f}, {}".format(t,
label(rk_order)))
409 os.path.join(Here,
"12_higher-order-solvers-instability.png"),
416if __name__ ==
"__main__":
417 parser = argparse.ArgumentParser(
418 description=
"Create the PNGs of the tutorial 12_higher-order-solvers.ipynb"
423 help=
"Directory for builds and outputs (default: temporary directory)",
428 help=
"Configured and built Peano directory (default: the repository this script resides in)",
433 default=[
"convergence",
"profile",
"instability"],
434 choices=[
"convergence",
"profile",
"instability"],
435 help=
"Pictures to create (default: all)",
437 args = parser.parse_args()
439 work_directory = args.work_directory
or tempfile.mkdtemp(
440 prefix=
"12_higher-order-solvers-"
444 return os.path.join(work_directory, name)
446 if "convergence" in args.pictures:
448 for rk_order
in [
None, 1, 2, 3, 4]:
450 for volume_size, cells
in VolumeSizes:
452 "convergence-{}-{}".format(
453 "fv" if rk_order
is None else "rk{}".format(rk_order), cells
464 runs[rk_order].append((cells, d))
467 if "profile" in args.pictures:
469 for rk_order
in [
None, 4]:
472 "fv" if rk_order
is None else "rk{}".format(rk_order)
483 runs.append((rk_order, d))
486 if "instability" in args.pictures:
488 for rk_order
in [1, 2, 4]:
489 d =
directory(
"instability-rk{}".format(rk_order))
491 rk_order, VolumeSizes[1][0], PeriodEndTime, 0.005, d, args.peano_root
493 runs.append((rk_order, d))
496 print(
"wrote PNGs into {}".format(Here))
The conservative flux F(Q) of a hyperbolic PDE.
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.
Abstract base class for all ExaHyPE 2 solvers.
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-...
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....
exact_density(x, t)
Density of the entropy wave advected with u=1 over [-0.1,0.1].
density_along_x_axis(directory, rk_order, snapshot)
Return time stamp plus grid cell centres and densities along y=0.
plot_profile(runs)
Density along y=0 after one period on the coarsest mesh.
snap(offset, size)
Snap a patch's offset and size onto the regular mesh over the domain.
build_and_run(rk_order, volume_size, end_time, time_in_between_plots, directory, peano_root)
Generate, compile and run one setup within directory.
plot_convergence(runs)
Max error at t=ConvergenceEndTime over the grid cells per axis.
solver_name(rk_order)
Name of the solver: EulerFV for rk_order None, EulerFD4 otherwise.
plot_instability(runs)
Max error over time for the explicit Euler time stepping vs the others.
errors(directory, rk_order)
Return a list of (time stamp, max error, rms error) of the density per snapshot.
Interactive visualisation of Peano patch files within Jupyter notebooks.
read_patches(patch_file_name)
Return list of (offset, size, values) for all patches of one patch file.
read_snapshots(file_name)
Return list of (time stamp, list of patch files) from a patch meta file.