5Create the PNG illustrations of the tutorial 521_gauge-wave.ipynb
7The script builds the gauge wave setup of the tutorial for several volume
8and patch sizes, runs each configuration over one period of the wave,
9extracts the lapse along the x-axis with Peano's PointProbe, and writes all
10pictures into the directory of this script. It also prints the convergence
11table which the tutorial discusses. Call it from anywhere:
13 python3 tutorials/exahype2/exagrype/illustrations/521_gauge-wave.py
15All builds and outputs go into a temporary directory (or --work-directory).
16If you pass a work directory, runs which have completed before are reused,
17i.e. you can rerun the script to redraw the pictures without recomputing.
18The whole study takes around two hours on an eight-core workstation, most of
19it spent on the three finest runs.
32Here = os.path.dirname(os.path.abspath(__file__))
33PeanoRoot = os.path.abspath(os.path.join(Here,
"..",
"..",
"..",
".."))
34sys.path.insert(0, os.path.join(PeanoRoot,
"src"))
39import matplotlib.pyplot
as plt
49PatchFile =
"solution-CCZ4.peano-patch-file"
58MaxVolumeSizes = {9: 0.12, 27: 0.04, 81: 0.013}
77 """! Lapse of the gauge wave, i.e. sqrt(H) with H = 1 - A sin(2 pi (x-t))"""
78 return np.sqrt(1.0 - Amplitude * np.sin(2.0 * np.pi * (np.asarray(x) - t)))
81def build_and_run(volumes_per_axis, patch_size, directory, peano_installation):
84 Generate, compile and run the tutorial setup within directory
86 Returns the runtime of the executable in seconds. If the directory holds
87 the result of a previous run, we do not rerun but return the stored
88 runtime. peano_installation is the configured and built Peano root
89 directory which the generated Makefile links against.
92 runtime_file = os.path.join(directory,
"runtime.txt")
93 if os.path.exists(runtime_file):
94 with open(runtime_file)
as f:
95 return float(f.read())
97 os.makedirs(directory, exist_ok=
True)
99 "build and run {} volumes per axis with patch size {} in {}".format(
100 volumes_per_axis, patch_size, directory
104 namespace=[
"tutorials",
"exagrype",
"gaugewave"],
105 project_name=
"GaugeWave",
107 executable=
"gauge-wave",
116 solver, volume_size=MaxVolumeSizes[volumes_per_axis]
120 first_plot_time_stamp=0.0,
121 time_in_between_plots=0.05,
122 plotter_precision=10,
124 solver.plotter.add_plot_filter([0.0, 0.49, 0.49], [1.0, 0.02, 0.02])
125 project.add_solver(solver)
126 project.set_global_simulation_parameters(
128 offset=[0.0, 0.0, 0.0],
129 size=[1.0, 1.0, 1.0],
130 min_end_time=EndTime,
131 max_end_time=EndTime,
132 periodic_BC=[
True,
True,
True],
134 project.set_Peano4_installation(
135 peano_installation, peano4.api.output.CompileMode.RELEASE
137 with contextlib.redirect_stdout(io.StringIO()):
138 peano4_project = project.generate_Peano4_project(
139 verbosity=peano4.api.LoggerVerbosity.TEXT
141 peano4_project.generate()
147 stdout=subprocess.DEVNULL,
148 stderr=subprocess.DEVNULL,
151 with open(os.path.join(directory,
"run.log"),
"w")
as log:
152 subprocess.run([
"./gauge-wave"], cwd=directory, check=
True, stdout=log)
153 runtime = time.time() - start
154 with open(runtime_file,
"w")
as f:
155 f.write(str(runtime))
159def probe(directory, point, output_file):
162 Probe all snapshots at one point and return (time stamps, lapse)
164 This is the Python counterpart of src/tarch/plotter/api/probe.py.
168 file_name=os.path.join(directory, PatchFile),
170 output_file=os.path.join(directory, output_file),
174 with contextlib.redirect_stdout(io.StringIO()):
175 point_probe.display()
176 with open(os.path.join(directory, output_file))
as csv_file:
177 rows = list(csv.DictReader(csv_file))
179 np.array([float(row[
"time"])
for row
in rows]),
180 np.array([float(row[
"lapse"])
for row
in rows]),
187 Probe the lapse in the centre of each volume along the line y=z=0.5
189 Returns the x coordinates, the time stamps of all snapshots and a 2d
190 array lapse[snapshot,x].
193 x = (np.arange(volumes_per_axis) + 0.5) / volumes_per_axis
195 for i, x_i
in enumerate(x):
196 t, lapse_over_time =
probe(
197 directory, [x_i, 0.5, 0.5],
"profile-{}.csv".format(i)
199 lapse.append(lapse_over_time)
200 return x, t, np.array(lapse).T
204 """! Initial lapse along the x-axis with volumes and patches"""
206 x_fine = np.linspace(0.0, 1.0, 400)
207 h = 1.0 / volumes_per_axis
209 fig, ax = plt.subplots(figsize=(8, 4))
210 ax.plot(x_fine,
exact_lapse(x_fine, 0.0),
"k-", linewidth=1, label=
"exact lapse")
212 np.append(x - 0.5 * h, 1.0),
213 np.append(lapse[0], lapse[0][-1]),
216 label=
"volume averages",
218 for patch
in range(volumes_per_axis // patch_size + 1):
219 ax.axvline(patch * patch_size * h, color=
"tab:red", linewidth=1, linestyle=
"--")
221 [], [], color=
"tab:red", linewidth=1, linestyle=
"--", label=
"patch boundaries"
223 ax.set_xlabel(
"x (along y=z=0.5)")
224 ax.set_ylabel(
"lapse")
226 "Initial lapse: {} volumes per axis, patch size {}".format(
227 volumes_per_axis, patch_size
230 ax.legend(loc=
"lower right")
232 os.path.join(Here,
"521_gauge-wave-mesh.png"), dpi=100, bbox_inches=
"tight"
238 """! Lapse along the x-axis for three snapshots"""
240 x_fine = np.linspace(0.0, 1.0, 400)
242 fig, ax = plt.subplots(figsize=(8, 4))
243 for snapshot, colour
in zip(
244 [0, len(t) // 2, len(t) - 1], [
"tab:blue",
"tab:orange",
"tab:green"]
247 x_fine,
exact_lapse(x_fine, t[snapshot]),
"-", color=colour, linewidth=1
255 label=
"t={:.2f}".format(t[snapshot]),
257 ax.set_xlabel(
"x (along y=z=0.5)")
258 ax.set_ylabel(
"lapse")
259 ax.set_title(
"Lapse profile (lines: exact, dots: ExaGRyPE)")
260 ax.legend(loc=
"upper left", bbox_to_anchor=(1.01, 1.0))
262 os.path.join(Here,
"521_gauge-wave-profile.png"), dpi=100, bbox_inches=
"tight"
268 """! Lapse at the centre of the domain over time"""
269 t, lapse =
probe(directory, [0.5, 0.5, 0.5],
"probe.csv")
270 t_fine = np.linspace(0.0, EndTime, 400)
272 fig, ax = plt.subplots(figsize=(8, 4))
273 ax.plot(t_fine,
exact_lapse(0.5, t_fine),
"k-", linewidth=1, label=
"exact")
274 ax.plot(t, lapse,
"o", markersize=3, label=
"PointProbe at (0.5,0.5,0.5)")
276 ax.set_ylabel(
"lapse")
277 ax.set_title(
"Lapse at the centre of the domain over time")
278 ax.legend(loc=
"lower right")
280 os.path.join(Here,
"521_gauge-wave-probe.png"), dpi=100, bbox_inches=
"tight"
288 Maximum and L2 error of the lapse along the x-axis for all snapshots
290 Returns the time stamps plus the two errors per snapshot.
294 error = lapse -
exact_lapse(x[np.newaxis, :], t[:, np.newaxis])
295 return t, np.max(np.abs(error), axis=1), np.sqrt(np.mean(error**2, axis=1))
301 Maximum error over volume size for several points in time
303 The lines show patch size 3 at several time stamps. The markers for patch
304 sizes 9 and 27 are drawn for the final time stamp only.
307 fig, ax = plt.subplots(figsize=(7, 5))
308 times = [0.05, 0.2, 0.5, 1.0]
309 colours = [
"tab:blue",
"tab:orange",
"tab:green",
"tab:red"]
310 for time_stamp, colour
in zip(times, colours):
312 (volumes, values)
for (volumes, p), values
in results.items()
if p == 3
314 h = np.array([1.0 / volumes
for volumes, _
in data])
317 values[
"max errors"][np.argmin(np.abs(values[
"t"] - time_stamp))]
318 for _, values
in data
321 ax.loglog(h, error,
"o-", color=colour, label=
"t={}".format(time_stamp))
322 for patch_size, marker
in [(9,
"s"), (27,
"^")]:
324 (1.0 / volumes, values[
"max errors"][-1])
325 for (volumes, p), values
in results.items()
329 [h
for h, _
in data],
330 [e
for _, e
in data],
333 markerfacecolor=
"none",
335 label=
"t=1.0, patch size {}".format(patch_size),
337 h = np.array([1.0 / 81, 1.0 / 9])
338 reference = results[(81, 3)][
"max errors"][1]
339 ax.loglog(h, reference * h / h[0],
"k--", linewidth=1, label=
"first order")
340 ax.set_xlabel(
"volume size h = 1 / (volumes per axis)")
341 ax.set_ylabel(
"max error of the lapse")
342 ax.set_title(
"Convergence of the Finite Volume scheme (lines: patch size 3)")
343 ax.legend(loc=
"upper left", bbox_to_anchor=(1.01, 1.0))
345 os.path.join(Here,
"521_gauge-wave-convergence.png"),
353 """! Estimated convergence order from two meshes whose volume sizes differ by a factor of three"""
354 return np.log(coarse_error / fine_error) / np.log(3.0)
357if __name__ ==
"__main__":
358 parser = argparse.ArgumentParser(
359 description=
"Create the PNGs of the tutorial 521_gauge-wave.ipynb"
364 help=
"Directory for builds and outputs (default: temporary directory)",
369 help=
"Configured and built Peano installation to link against (default: the repository holding this script)",
371 args = parser.parse_args()
373 with contextlib.ExitStack()
as stack:
374 work_directory = args.work_directory
or stack.enter_context(
375 tempfile.TemporaryDirectory()
377 work_directory = os.path.abspath(work_directory)
381 work_directory,
"h{}-p{}".format(volumes_per_axis, patch_size)
385 for volumes_per_axis, patch_size
in Configurations:
391 os.path.abspath(args.peano_root),
394 results[(volumes_per_axis, patch_size)] = {
397 "max errors": max_errors,
398 "L2 errors": l2_errors,
402 plot_mesh(reference_directory, *Reference)
407 print(
"Errors after the last time step:")
409 "{:>8} {:>6} {:>8} {:>12} {:>8} {:>12} {:>8} {:>10}".format(
420 for (volumes_per_axis, patch_size), values
in sorted(
421 results.items(), key=
lambda item: (item[0][1], item[0][0])
423 coarser = results.get((volumes_per_axis // 3, patch_size))
424 order_max = order_l2 = float(
"nan")
425 if coarser
is not None:
426 order_max =
order(coarser[
"max errors"][-1], values[
"max errors"][-1])
427 order_l2 =
order(coarser[
"L2 errors"][-1], values[
"L2 errors"][-1])
429 "{:>8} {:>6} {:>8.4f} {:>12.4e} {:>8.2f} {:>12.4e} {:>8.2f} {:>9.1f}s".format(
433 values[
"max errors"][-1],
435 values[
"L2 errors"][-1],
441 print(
"Max errors of patch size 3 over time:")
443 "{:>8} {:>12} {:>12} {:>12} {:>8} {:>8}".format(
444 "t",
"h=1/9",
"h=1/27",
"h=1/81",
"order",
"order"
447 for snapshot
in [1, 2, 4, 10, 20]:
449 results[(volumes, 3)][
"max errors"][snapshot]
for volumes
in [9, 27, 81]
452 "{:>8.3f} {:>12.4e} {:>12.4e} {:>12.4e} {:>8.2f} {:>8.2f}".format(
453 results[(81, 3)][
"t"][snapshot],
455 order(errors[0], errors[1]),
456 order(errors[1], errors[2]),
Solver for the Einstein equations in the first-order CCZ4 formulation.
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...
Probe the data within a Peano patch file at one point over time.
wave_profile(directory, volumes_per_axis)
Probe the lapse in the centre of each volume along the line y=z=0.5.
plot_convergence(results)
Maximum error over volume size for several points in time.
plot_probe(directory)
Lapse at the centre of the domain over time.
exact_lapse(x, t)
Lapse of the gauge wave, i.e.
errors_over_time(directory, volumes_per_axis)
Maximum and L2 error of the lapse along the x-axis for all snapshots.
build_and_run(volumes_per_axis, patch_size, directory, peano_installation)
Generate, compile and run the tutorial setup within directory.
plot_mesh(directory, volumes_per_axis, patch_size)
Initial lapse along the x-axis with volumes and patches.
order(coarse_error, fine_error)
Estimated convergence order from two meshes whose volume sizes differ by a factor of three.
directory_of(volumes_per_axis, patch_size)
plot_profile(directory, volumes_per_axis)
Lapse along the x-axis for three snapshots.
exahype2.api.InitPointWisely gauge_wave()
Gauge wave along the x-axis,.
This file is part of the ExaGRyPE project.