Skip to content

Commit 2aee5bb

Browse files
ZiyaSelimconnorjwardJHopeCollins
authored
Change test_extrusion_lsw python demo to rst demo (#4852)
--------- Co-authored-by: Connor Ward <c.ward20@imperial.ac.uk> Co-authored-by: Josh Hope-Collins <jhc.jss@gmail.com>
1 parent 54677cb commit 2aee5bb

4 files changed

Lines changed: 180 additions & 80 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
Linear Shallow Water Equations on an Extruded Mesh using a Strang timestepping scheme
2+
=====================================================================================
3+
4+
This demo solves the linear shallow water equations on an extruded mesh
5+
using a Strang timestepping scheme.
6+
7+
The equations are solved in a domain :math:`\Omega` utilizing a 2D base mesh
8+
that is extruded vertically to form a 3D volume.
9+
10+
As usual, we start by importing Firedrake: ::
11+
12+
from firedrake import *
13+
14+
Mesh Generation
15+
----------------
16+
17+
We use an *extruded* mesh, where the base mesh is a :math:`2^5 \times 2^5` unit square
18+
with 5 evenly-spaced vertical layers. This results in a 3D volume composed of
19+
prisms. ::
20+
21+
power = 5
22+
m = UnitSquareMesh(2 ** power, 2 ** power)
23+
layers = 5
24+
25+
mesh = ExtrudedMesh(m, layers, layer_height=0.25)
26+
27+
Function Spaces
28+
----------------
29+
For the velocity field, we use an :math:`H(\mathrm{div})`-conforming function space
30+
constructed as the outer product of a 2D BDM space and a 1D DG space. This ensures
31+
that the normal component of the velocity is continuous across element boundaries
32+
in the horizontal directions, which is important for accurately capturing fluxes. ::
33+
34+
horiz = FiniteElement("BDM", "triangle", 1)
35+
vert = FiniteElement("DG", "interval", 0)
36+
prod = HDiv(TensorProductElement(horiz, vert))
37+
W = FunctionSpace(mesh, prod)
38+
39+
We also define a pressure space :math:`X` using piecewise constant discontinuous
40+
Galerkin elements, and a plotting space :math:`X_{\text{plot}}` using continuous
41+
Galerkin elements for better visualization. ::
42+
43+
X = FunctionSpace(mesh, "DG", 0, vfamily="DG", vdegree=0)
44+
Xplot = FunctionSpace(mesh, "CG", 1, vfamily="Lagrange", vdegree=1)
45+
46+
Initial Conditions
47+
-------------------
48+
49+
We define our functions for velocity and pressure fields. The initial pressure field
50+
is set to a prescribed sine function to create a wave-like disturbance. ::
51+
52+
# Define starting field
53+
u_0 = Function(W)
54+
u_h = Function(W)
55+
u_1 = Function(W)
56+
p_0 = Function(X)
57+
p_1 = Function(X)
58+
p_plot = Function(Xplot)
59+
x, y, z = SpatialCoordinate(mesh)
60+
p_0.interpolate(sin(4*pi*x)*sin(2*pi*x))
61+
62+
T = 0.5
63+
t = 0
64+
dt = 0.0025
65+
66+
file = VTKFile("lsw3d.pvd")
67+
68+
Before starting the time-stepping loop, we project the initial pressure field
69+
into the plotting space for visualization. ::
70+
71+
p_trial = TrialFunction(Xplot)
72+
p_test = TestFunction(Xplot)
73+
solve(p_trial * p_test * dx == p_0 * p_test * dx, p_plot)
74+
file.write(p_plot, time=t)
75+
76+
E_0 = assemble(0.5 * p_0 * p_0 * dx + 0.5 * dot(u_0, u_0) * dx)
77+
78+
Time-Stepping Loop
79+
--------------------
80+
81+
We evolve the system in time using a Strang splitting method. In each time step,
82+
we perform a half-step update of the velocity field, a full-step update of the pressure field,
83+
and then another half-step update of the velocity field. This approach helps to
84+
maintain stability and accuracy.
85+
86+
**Step 1: Velocity Half-Step**
87+
First, we solve for an intermediate velocity :math:`u_h` using the pressure
88+
from the start of the step :math:`p_0`. Mathematically, we find :math:`u_h \in W` such that:
89+
90+
.. math::
91+
92+
\int_{\Omega} w \cdot u_h \, dx = \int_{\Omega} w \cdot u_0 \, dx + \frac{\Delta t}{2} \int_{\Omega} (\nabla \cdot w) p_0 \, dx \quad \forall w \in W
93+
94+
.. code-block:: python
95+
96+
a_1 = dot(w, u) * dx
97+
L_1 = dot(w, u_0) * dx + 0.5 * dt * div(w) * p_0 * dx
98+
solve(a_1 == L_1, u_h)
99+
100+
**Step 2: Pressure Full-Step**
101+
Next, we update the pressure to :math:`p_1` using the divergence of the
102+
intermediate velocity :math:`u_h`. We find :math:`p_1 \in X` such that:
103+
104+
.. math::
105+
106+
\int_{\Omega} \phi \, p_1 \, dx = \int_{\Omega} \phi \, p_0 \, dx - \Delta t \int_{\Omega} \phi (\nabla \cdot u_h) \, dx \quad \forall \phi \in X
107+
108+
.. code-block:: python
109+
110+
a_2 = phi * p * dx
111+
L_2 = phi * p_0 * dx - dt * phi * div(u_h) * dx
112+
solve(a_2 == L_2, p_1)
113+
114+
**Step 3: Velocity Final Half-Step**
115+
Finally, we compute the velocity at the end of the time step :math:`u_1` using
116+
the updated pressure :math:`p_1`. We find :math:`u_1 \in W` such that:
117+
118+
.. math::
119+
120+
\int_{\Omega} w \cdot u_1 \, dx = \int_{\Omega} w \cdot u_h \, dx + \frac{\Delta t}{2} \int_{\Omega} (\nabla \cdot w) p_1 \, dx \quad \forall w \in W
121+
122+
.. code-block:: python
123+
124+
a_3 = dot(w, u) * dx
125+
L_3 = dot(w, u_h) * dx + 0.5 * dt * div(w) * p_1 * dx
126+
solve(a_3 == L_3, u_1)
127+
128+
Implementation in the Simulation Loop
129+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130+
131+
Here follows the complete implementation of the time-stepping loop, including the updates of the velocity and pressure fields,
132+
as well as the projection of the pressure field for visualization at each time step.
133+
We also print the current simulation time at each step for tracking progress.
134+
135+
.. code-block:: python
136+
137+
while t < T:
138+
u = TrialFunction(W)
139+
w = TestFunction(W)
140+
a_1 = dot(w, u) * dx
141+
L_1 = dot(w, u_0) * dx + 0.5 * dt * div(w) * p_0 * dx
142+
solve(a_1 == L_1, u_h)
143+
144+
p = TrialFunction(X)
145+
phi = TestFunction(X)
146+
a_2 = phi * p * dx
147+
L_2 = phi * p_0 * dx - dt * phi * div(u_h) * dx
148+
solve(a_2 == L_2, p_1)
149+
150+
u = TrialFunction(W)
151+
w = TestFunction(W)
152+
a_3 = dot(w, u) * dx
153+
L_3 = dot(w, u_h) * dx + 0.5 * dt * div(w) * p_1 * dx
154+
solve(a_3 == L_3, u_1)
155+
156+
u_0.assign(u_1)
157+
p_0.assign(p_1)
158+
t += dt
159+
160+
# project into P1 x P1 for plotting
161+
p_trial = TrialFunction(Xplot)
162+
p_test = TestFunction(Xplot)
163+
solve(p_trial * p_test * dx == p_0 * p_test * dx, p_plot)
164+
file.write(p_plot, time=t)
165+
print(t)
166+
167+
Energy Calculation
168+
------------------
169+
170+
Finally, we compute and print the total energy of the system at the end of the simulation
171+
and compare it to the initial energy to assess conservation properties. ::
172+
173+
E_1 = assemble(0.5 * p_0 * p_0 * dx + 0.5 * dot(u_0, u_0) * dx)
174+
print('Initial energy', E_0)
175+
print('Final energy', E_1)
176+
177+
This demo can be found as a script in
178+
:demo:`extruded_shallow_water.py <extruded_shallow_water.py>`.

demos/extruded_shallow_water/test_extrusion_lsw.py

Lines changed: 0 additions & 80 deletions
This file was deleted.

docs/source/intro_tut.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ Introductory Tutorials
1414
A mixed formulation of the Poisson equation.<demos/poisson_mixed.py>
1515
A time-dependent DG advection equation using upwinding.<demos/DG_advection.py>
1616
An extruded mesh example, using a steady-state continuity equation.<demos/extruded_continuity.py>
17+
A linear shallow water equations example using a Strang timestepping scheme.<demos/extruded_shallow_water.py>
1718
A linear wave equation with optional mass lumping.<demos/linear_wave_equation.py>
1819
Creating Firedrake-compatible meshes in Gmsh.<demos/immersed_fem.py>

tests/firedrake/demos/test_demos_run.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
Demo(("DG_advection", "DG_advection"), ["matplotlib"]),
2727
Demo(("eigenvalues_QG_basinmodes", "qgbasinmodes"), ["matplotlib", "slepc", "vtk"]),
2828
Demo(("extruded_continuity", "extruded_continuity"), []),
29+
Demo(("extruded_shallow_water", "extruded_shallow_water"), []),
2930
Demo(("helmholtz", "helmholtz"), ["vtk"]),
3031
Demo(("higher_order_mass_lumping", "higher_order_mass_lumping"), ["vtk"]),
3132
Demo(("immersed_fem", "immersed_fem"), []),

0 commit comments

Comments
 (0)