Peano
Loading...
Searching...
No Matches
10_euler.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 10_euler.ipynb
6
7The script builds the tutorial's Euler setup for two volume sizes, runs it,
8probes the solution at the origin and writes all pictures into the
9directory of this script. The PDE terms are injected from Python, i.e. we
10do not need any hand-written C++ file. Call it from anywhere:
11
12 python3 tutorials/exahype2/illustrations/10_euler.py
13
14All builds and outputs go into a temporary directory (or --work-directory).
15
16"""
17import argparse
18import csv
19import math
20import os
21import subprocess
22import sys
23import tempfile
24
25Here = os.path.dirname(os.path.abspath(__file__))
26PeanoRoot = os.path.abspath(os.path.join(Here, "..", "..", ".."))
27sys.path.insert(0, os.path.join(PeanoRoot, "src"))
28
29import matplotlib
30
31matplotlib.use("Agg")
32import matplotlib.patches
33import matplotlib.pyplot as plt
34import numpy as np
35
36import peano4.api
37import exahype2
39
40
41Flux = """
42 const double irho = 1.0 / Q[0];
43 constexpr double gamma = 1.4;
44 const double p = (gamma - 1.0) * (Q[3] - 0.5 * irho * (Q[1] * Q[1] + Q[2] * Q[2]));
45 switch (normal) {
46 case 0:
47 F[0] = Q[1];
48 F[1] = irho * Q[1] * Q[1] + p;
49 F[2] = irho * Q[1] * Q[2];
50 F[3] = irho * Q[1] * (Q[3] + p);
51 break;
52 case 1:
53 F[0] = Q[2];
54 F[1] = irho * Q[2] * Q[1];
55 F[2] = irho * Q[2] * Q[2] + p;
56 F[3] = irho * Q[2] * (Q[3] + p);
57 break;
58 }
59"""
60
61MaxEigenvalue = """
62 const double irho = 1.0 / Q[0];
63 constexpr double gamma = 1.4;
64 const double p = (gamma - 1.0) * (Q[3] - 0.5 * irho * (Q[1] * Q[1] + Q[2] * Q[2]));
65 const double c = std::sqrt(gamma * p * irho);
66 const double u = Q[1 + normal] * irho;
67 lambdaMax[0] = std::max(std::abs(u + c), std::abs(u - c));
68"""
69
70InitPointWisely = """
71 constexpr double gamma = 1.4;
72 constexpr double p = 1.0;
73 constexpr double u = 1.0;
74 const double rho = 1.0 + 0.2 * std::sin(2.0 * M_PI * (x[0] + 0.1) / 0.2);
75 Q[0] = rho;
76 Q[1] = rho * u;
77 Q[2] = 0.0;
78 Q[3] = p / (gamma - 1) + 0.5 * rho * u * u;
79"""
80
81EndTime = 0.2
82TimeInBetweenPlots = 0.005
83PatchFile = "solution-EulerFV.peano-patch-file"
84
85
86def exact_density(x, t):
87 """! Density of the entropy wave advected with u=1 over [-0.1,0.1]"""
88 return 1.0 + 0.2 * np.sin(2.0 * np.pi * (np.asarray(x) - t + 0.1) / 0.2)
89
90
91def build_and_run(volume_size, directory):
92 """! Generate, compile and run the tutorial setup within directory"""
93 project = exahype2.api.Project(
94 namespace=["tutorials", "exahype2", "euler"],
95 project_name="Euler",
96 directory=directory,
97 executable="euler",
98 )
99 solver = exahype2.api.Solver("EulerFV", 4, 0)
100 solver.add_user_solver_includes("#include <cmath>\n#include <algorithm>\n")
101 solver.add_pde_term(exahype2.api.Flux(implementation=Flux, stateless=False))
102 solver.add_pde_term(
103 exahype2.api.MaxEigenvalue(implementation=MaxEigenvalue, stateless=False)
104 )
105 solver.add_pde_term(
106 exahype2.api.InitPointWisely(implementation=InitPointWisely, stateless=False)
107 )
108 solver.numerical_scheme = exahype2.api.fv.RusanovWithExplicitEuler(solver, 9)
109 solver.grid_construction = exahype2.api.fv.SetupSimulationOverRegularGrid(
110 solver, volume_size=volume_size
111 )
112 solver.plotter = exahype2.api.fv.PlotAsVTKFile(
113 solver, first_plot_time_stamp=0.0, time_in_between_plots=TimeInBetweenPlots
114 )
115 project.add_solver(solver)
116 project.set_global_simulation_parameters(
117 dimensions=2,
118 offset=[-0.1, -0.1],
119 size=[0.2, 0.2],
120 min_end_time=EndTime,
121 max_end_time=EndTime,
122 periodic_BC=[True, True, True],
123 )
124 project.set_Peano4_installation(PeanoRoot, peano4.api.output.CompileMode.RELEASE)
125 peano4_project = project.generate_Peano4_project()
126 peano4_project.generate()
127 subprocess.run(["make", "-j"], cwd=directory, check=True, stdout=subprocess.DEVNULL)
128 subprocess.run(["./euler"], cwd=directory, check=True, stdout=subprocess.DEVNULL)
129 subprocess.run(
130 [
131 sys.executable,
132 os.path.join(PeanoRoot, "src", "tarch", "plotter", "api", "probe.py"),
133 "--point",
134 "0.0",
135 "0.0",
136 "-o",
137 "probe.csv",
138 "-j",
139 str(os.cpu_count()),
140 PatchFile,
141 ],
142 cwd=directory,
143 check=True,
144 stdout=subprocess.DEVNULL,
145 env=dict(os.environ, PYTHONPATH=os.path.join(PeanoRoot, "src")),
146 )
147
148
149def read_probe(directory):
150 """! Return arrays time, density from the probe's csv file"""
151 with open(os.path.join(directory, "probe.csv")) as csv_file:
152 rows = list(csv.DictReader(csv_file))
153 return (
154 np.array([float(row["time"]) for row in rows]),
155 np.array([float(row["EulerFVQ_0"]) for row in rows]),
156 )
157
158
159def density_along_x_axis(directory, snapshot):
160 """! Return time stamp plus volume centres and densities along y=0"""
161 time_stamp, patch_files = tarch.plotter.api.notebook.read_snapshots(
162 os.path.join(directory, PatchFile)
163 )[snapshot]
164 x, rho = [], []
165 for patch_file in patch_files:
166 for offset, size, values in tarch.plotter.api.notebook.read_patches(patch_file):
167 if offset[1] <= 0.0 < offset[1] + size[1]:
168 dof = values.shape[0]
169 h = size[0] / dof
170 row = int((0.0 - offset[1]) / (size[1] / dof))
171 for i in range(dof):
172 x.append(offset[0] + (i + 0.5) * h)
173 rho.append(values[row, i, 0])
174 order = np.argsort(x)
175 return time_stamp, np.array(x)[order], np.array(rho)[order]
176
177
178def plot_mesh(directory):
179 """! Initial density plus patch boundaries and the volumes of one patch"""
180 file_name = os.path.join(directory, PatchFile)
181 ax = tarch.plotter.api.notebook.plot_snapshot(file_name, 0, 0, label="density")
182 _, patch_files = tarch.plotter.api.notebook.read_snapshots(file_name)[0]
183 patches = [
184 p for f in patch_files for p in tarch.plotter.api.notebook.read_patches(f)
185 ]
186 for offset, size, values in patches:
187 ax.add_patch(
188 matplotlib.patches.Rectangle(
189 offset, size[0], size[1], fill=False, edgecolor="white", linewidth=1.2
190 )
191 )
192 # highlight the volumes of one patch
193 offset, size, values = min(patches, key=lambda p: (p[0][0] ** 2 + p[0][1] ** 2))
194 dof = values.shape[0]
195 for i in range(1, dof):
196 ax.plot(
197 [offset[0] + i * size[0] / dof] * 2,
198 [offset[1], offset[1] + size[1]],
199 color="red",
200 linewidth=0.5,
201 )
202 ax.plot(
203 [offset[0], offset[0] + size[0]],
204 [offset[1] + i * size[1] / dof] * 2,
205 color="red",
206 linewidth=0.5,
207 )
208 ax.add_patch(
209 matplotlib.patches.Rectangle(
210 offset, size[0], size[1], fill=False, edgecolor="red", linewidth=1.5
211 )
212 )
213 ax.set_title(
214 "Initial density\n{0}x{0} patches, {1}x{1} volumes per patch".format(
215 round(0.2 / size[0]), dof
216 )
217 )
218 ax.figure.savefig(
219 os.path.join(Here, "10_euler-mesh.png"), dpi=100, bbox_inches="tight"
220 )
221 plt.close(ax.figure)
222
223
224def plot_advection(directory):
225 """! Density along y=0 at a few time stamps against the exact solution"""
226 fig, ax = plt.subplots(figsize=(7, 4))
227 x_exact = np.linspace(-0.1, 0.1, 400)
228 snapshots = len(
229 tarch.plotter.api.notebook.read_snapshots(os.path.join(directory, PatchFile))
230 )
231 for snapshot, colour in zip(
232 [0, (snapshots - 1) // 4, (snapshots - 1) // 2], ["C0", "C1", "C2"]
233 ):
234 t, x, rho = density_along_x_axis(directory, snapshot)
235 ax.plot(x_exact, exact_density(x_exact, t), color=colour, linewidth=1)
236 ax.plot(x, rho, "o", color=colour, markersize=2.5, label="t={:.3f}".format(t))
237 ax.set_xlabel("x")
238 ax.set_ylabel("density")
239 ax.set_title("Density along y=0: ExaHyPE (dots) vs exact solution (lines)")
240 ax.legend()
241 fig.savefig(
242 os.path.join(Here, "10_euler-advection.png"), dpi=100, bbox_inches="tight"
243 )
244 plt.close(fig)
245
246
247def plot_probe(directory):
248 """! Probe at the origin over time against the exact sine"""
249 t, rho = read_probe(directory)
250 t_exact = np.linspace(0, EndTime, 400)
251 fig, ax = plt.subplots(figsize=(7, 4))
252 ax.plot(
253 t_exact,
254 exact_density(0.0, t_exact),
255 "k-",
256 linewidth=1,
257 label="exact: 1+0.2 sin(10 pi t)",
258 )
259 ax.plot(t, rho, "o", markersize=3, label="probe.py at (0,0)")
260 ax.set_xlabel("t")
261 ax.set_ylabel("density")
262 ax.set_title("Density at the origin over time")
263 ax.legend()
264 fig.savefig(os.path.join(Here, "10_euler-probe.png"), dpi=100, bbox_inches="tight")
265 plt.close(fig)
266
267
268def plot_convergence(directories):
269 """! Probe error over time for several volume sizes"""
270 fig, ax = plt.subplots(figsize=(7, 4))
271 for volume_size, directory in directories:
272 t, rho = read_probe(directory)
273 error = np.abs(rho - exact_density(0.0, t))
274 ax.plot(
275 t,
276 error,
277 "o-",
278 markersize=3,
279 label="volume_size={}: max error {:.4f}".format(volume_size, error.max()),
280 )
281 ax.set_xlabel("t")
282 ax.set_ylabel("|density - exact density| at (0,0)")
283 ax.set_title("Error at the probe point for two mesh resolutions")
284 ax.legend()
285 fig.savefig(
286 os.path.join(Here, "10_euler-convergence.png"), dpi=100, bbox_inches="tight"
287 )
288 plt.close(fig)
289
290
291if __name__ == "__main__":
292 parser = argparse.ArgumentParser(
293 description="Create the PNGs of the tutorial 10_euler.ipynb"
294 )
295 parser.add_argument(
296 "--work-directory",
297 default=None,
298 help="Directory for builds and outputs (default: temporary directory)",
299 )
300 args = parser.parse_args()
301
302 work_directory = args.work_directory or tempfile.mkdtemp(prefix="10_euler-")
303 runs = []
304 for volume_size in [0.005, 0.001]:
305 directory = os.path.join(work_directory, "volume-size-{}".format(volume_size))
306 os.makedirs(directory, exist_ok=True)
307 print("build and run volume size {} in {}".format(volume_size, directory))
308 build_and_run(volume_size, directory)
309 runs.append((volume_size, directory))
310
311 plot_mesh(runs[0][1])
312 plot_advection(runs[0][1])
313 plot_probe(runs[0][1])
314 plot_convergence(runs)
315 print("wrote PNGs into {}".format(Here))
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-...
Setup phase counterpart to RusanovWithExplicitEuler: sets the initial condition for a Finite Volume s...
plot_convergence(directories)
Probe error over time for several volume sizes.
Definition 10_euler.py:268
density_along_x_axis(directory, snapshot)
Return time stamp plus volume centres and densities along y=0.
Definition 10_euler.py:159
build_and_run(volume_size, directory)
Generate, compile and run the tutorial setup within directory.
Definition 10_euler.py:91
plot_probe(directory)
Probe at the origin over time against the exact sine.
Definition 10_euler.py:247
plot_advection(directory)
Density along y=0 at a few time stamps against the exact solution.
Definition 10_euler.py:224
plot_mesh(directory)
Initial density plus patch boundaries and the volumes of one patch.
Definition 10_euler.py:178
exact_density(x, t)
Density of the entropy wave advected with u=1 over [-0.1,0.1].
Definition 10_euler.py:86
read_probe(directory)
Return arrays time, density from the probe's csv file.
Definition 10_euler.py:149
Interactive visualisation of Peano patch files within Jupyter notebooks.
Definition notebook.py:1
plot_snapshot(file_name, snapshot=-1, unknown=0, clim=None, ax=None, cmap="viridis", label=None)
Plot one unknown of one snapshot.
Definition notebook.py:100
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