Note
Go to the end to download the full example code.
How to create Animations#
Section author: Florian Zill (Helmholtz Centre for Environmental Research GmbH - UFZ)
To demonstrate the creation of an animated plot we use a component transport example from the ogs benchmark gallery (https://www.opengeosys.org/docs/benchmarks/hydro-component/elder/).
import matplotlib.pyplot as plt
import numpy as np
import ogstools as ogs
from ogstools import examples
mesh_series = examples.load_meshseries_CT_2D_XDMF()
To read your own data as a mesh series you can do:
from ogstools.meshlib import MeshSeries
mesh_series = MeshSeries("filepath/filename_pvd_or_xdmf")
You can also use a variable from the available presets instead of needing to create your own: Variable presets and data transformation
Let’s use fixed scale limits to prevent rescaling during the animation.
ogs.plot.setup.vmin = 0
ogs.plot.setup.vmax = 100
ogs.plot.setup.dpi = 50
You can choose which timesteps to render by passing either an int array corresponding to the indices, or a float array corresponding to the timevalues to render. If a requested timevalue is not part of the timeseries it will be interpolated. In this case every second frame will be interpolated.
timevalues = np.linspace(
mesh_series.timevalues()[0], mesh_series.timevalues()[-1], num=25
)
Now, let’s animate the saturation solution. A timescale at the top indicates existing timesteps and the position of the current timevalue. Note that rendering many frames in conjunction with large meshes might take a really long time. We can pass two functions to animate: mesh_func which transforms the mesh and plot_func which can apply custom formatting and / or plotting.
def mesh_func(mesh: ogs.Mesh) -> ogs.Mesh:
"Clip the left half of the mesh."
return mesh.clip("-x", [0, 0, 0])
def plot_func(ax: plt.Axes, timevalue: float) -> None:
"Add the time to the title."
ax.set_title(f"{timevalue/(365.25*86400):.1f} yrs", loc="center")
anim = mesh_series.animate(
ogs.variables.saturation,
timevalues,
mesh_func=mesh_func,
plot_func=plot_func,
)
0%| | 0/25 [00:00<?, ?it/s]
4%|▍ | 1/25 [00:00<00:03, 6.30it/s]
8%|▊ | 2/25 [00:00<00:03, 6.35it/s]
12%|█▏ | 3/25 [00:00<00:03, 6.71it/s]
16%|█▌ | 4/25 [00:00<00:03, 6.58it/s]
20%|██ | 5/25 [00:00<00:02, 6.78it/s]
24%|██▍ | 6/25 [00:00<00:02, 6.63it/s]
28%|██▊ | 7/25 [00:01<00:02, 6.79it/s]
32%|███▏ | 8/25 [00:01<00:02, 6.65it/s]
36%|███▌ | 9/25 [00:01<00:02, 6.80it/s]
40%|████ | 10/25 [00:01<00:02, 6.65it/s]
44%|████▍ | 11/25 [00:01<00:02, 6.80it/s]
48%|████▊ | 12/25 [00:01<00:01, 6.65it/s]
52%|█████▏ | 13/25 [00:01<00:01, 6.79it/s]
56%|█████▌ | 14/25 [00:02<00:01, 6.61it/s]
60%|██████ | 15/25 [00:02<00:01, 6.76it/s]
64%|██████▍ | 16/25 [00:02<00:01, 6.62it/s]
68%|██████▊ | 17/25 [00:02<00:01, 6.76it/s]
72%|███████▏ | 18/25 [00:02<00:01, 6.62it/s]
76%|███████▌ | 19/25 [00:02<00:00, 6.75it/s]
80%|████████ | 20/25 [00:03<00:00, 5.27it/s]
84%|████████▍ | 21/25 [00:03<00:00, 5.69it/s]
88%|████████▊ | 22/25 [00:03<00:00, 5.85it/s]
92%|█████████▏| 23/25 [00:03<00:00, 6.16it/s]
96%|█████████▌| 24/25 [00:03<00:00, 6.20it/s]
96%|█████████▌| 24/25 [00:03<00:00, 6.20it/s]
The animation can be saved (as mp4) like so:
ogs.plot.utils.save_animation(anim, "Saturation", fps=5)
Total running time of the script: (0 minutes 7.824 seconds)