This page was generated from unit-3.4-simplehyp/shallow2D.ipynb.
3.4 A Nonlinear conservation law: shallow water equation in 2D¶
We consider the shallow water equations as an example of a nonlinear conservation law, i.e. we consider
with
and
Jacobian of the flux for shallow water:¶
\begin{align*} \mathbf{A}_1 & = \left(\begin{array}{ccc} 0 & 1 & 0 \\ - \frac{\mathbf{u}_2^2}{\mathbf{u}_1^2} + g \mathbf{u}_1 & 2 \frac{\mathbf{u}_2}{\mathbf{u}_1} & 0 \\ - \frac{\mathbf{u}_2 \mathbf{u}_3}{\mathbf{u}_1^2} & \frac{\mathbf{u}_3}{\mathbf{u}_1} & \frac{\mathbf{u}_2}{\mathbf{u}_1} \end{array} \right) = \left( \begin{array}{ccc} 0 & 1 & 0 \\ - u^2 + g h & 2 u & 0 \\ - u v & v & u \end{array} \right) \end{align*}
\begin{align*} \mathbf{A}_2 & = \left( \begin{array}{ccc} 0 & 0 & 1 \\ - \frac{\mathbf{u}_2\mathbf{u}_3}{\mathbf{u}_1^2} & \frac{\mathbf{u}_3}{\mathbf{u}_1} & \frac{\mathbf{u}_2}{\mathbf{u}_1} \\ - \frac{\mathbf{u}_3^2}{\mathbf{u}_1^2} + g \mathbf{u}_1 & 0 & 2\frac{\mathbf{u}_3}{\mathbf{u}_1} \end{array} \right) = \left( \begin{array}{ccc} 0 & 0 & 1 \\ - uv & v & u \\ - v^2 + gh & 0 & 2 v \end{array} \right) \end{align*}
\begin{align*} \mathbf{A}(\mathbf{u}, \mathbf{n}) & = n_1 \mathbf{A}_1 + n_2 \mathbf{A}_2 = \left( \begin{array}{ccc} 0 & n_1 & n_2 \\ - u \alpha - g h n_1 & \alpha + u n_1 & u n_2 \\ - v \alpha - g h n_2 & v n_1 & \alpha + v n_2 \end{array} \right), \quad \text{ with } \alpha = (\mathbf{u}, \mathbf{n}), \end{align*}
spectrum:
[1]:
from ngsolve import * # sloppy
import netgen.gui
The dam break problem (geometry)¶
[2]:
from netgen.geom2d import SplineGeometry
geo = SplineGeometry()
pnts =[ (-12,-5), (-7,-5), (-5,-5), (-3,-5), (-3,-3),
(-3,-1), (-1,-1), ( 0,-1), ( 1,-1), ( 3,-1),
( 3,-3), ( 3,-5), ( 5,-5), ( 7,-5), ( 12,-5),
( 12, 5), ( 7, 5), ( 5, 5), ( 3, 5), ( 3, 3),
( 3, 1), ( 1, 1), ( 0, 1), (-1, 1), (-3, 1),
(-3, 3), (-3, 5), (-5, 5), (-7, 5), (-12, 5)]
pnts = [geo.AppendPoint(*pnt) for pnt in pnts]
[3]:
curves = [[["line",0,1],"wall",1, 0], [["line",1,2],"wall",1, 0],
[["spline3",2,3,4],"wall",1, 0],[["spline3",4,5,6],"wall",1, 0],[["line",6,7],"wall",1, 0],
[["line",7,22],"dam",1, 2], # <--- dam interface
[["line",7,8],"wall",2, 0],[["spline3",8,9,10],"wall",2, 0],
[["spline3",10,11,12],"wall",2, 0],[["line",12,13],"wall",2, 0],[["line",13,14],"wall",2, 0],
[["line",14,15],"wall",2, 0], # <--- right boundary
[["line",15,16],"wall",2, 0],[["line",16,17],"wall",2, 0],
[["spline3",17,18,19],"wall",2, 0],[["spline3",19,20,21],"wall",2, 0],
[["line",21,22],"wall",2, 0],[["line",22,23],"wall",1, 0],
[["spline3",23,24,25],"wall",1, 0],[["spline3",25,26,27],"wall",1, 0],
[["line",27,28],"wall",1, 0],[["line",28,29],"wall",1, 0],
[["line",29,0],"wall",1, 0]] # <--- left boundary
for c,bc,l,r in curves:
geo.Append(c,bc=bc,leftdomain=l, rightdomain=r)
geo.SetMaterial(1,"upperlevel")
geo.SetMaterial(2,"lowerlevel")
[4]:
mesh = Mesh(geo.GenerateMesh(maxh=2))
mesh.Curve(3)
Draw(mesh)
Generate Mesh from spline geometry
Boundary mesh done, np = 59
CalcLocalH: 59 Points 0 Elements 0 Surface Elements
Meshing domain 1 / 2
load internal triangle rules
Surface meshing done
Meshing domain 2 / 2
Surface meshing done
Edgeswapping, topological
Smoothing
Split improve
Combine improve
Smoothing
Edgeswapping, metric
Smoothing
Split improve
Combine improve
Smoothing
Edgeswapping, metric
Smoothing
Split improve
Combine improve
Smoothing
Update mesh topology
Update clusters
Curve elements, order = 3
Update clusters
Curving edges
Curving faces
Vectorial (dim=3
) approximation space:¶
[5]:
order = 2
fes = L2(mesh,order=order,dim=3)
initial and boundary conditions¶
[6]:
U,V = fes.TnT() # "Trial" and "Test" function
h, hu, hv = U
# initial conditions
h0mat = {"upperlevel" : 10, "lowerlevel" : 2}
U0 = CoefficientFunction((CoefficientFunction([h0mat[mat] for mat in mesh.GetMaterials()]),0,0))
# boundary conditions
hbndreg = CoefficientFunction([{"wall" : h, "dam" : 0}[rg] for rg in mesh.GetBoundaries()])
hubndreg = CoefficientFunction([{"wall" : -hu, "dam" : 0}[rg] for rg in mesh.GetBoundaries()])
hvbndreg = CoefficientFunction([{"wall" : -hv, "dam" : 0}[rg] for rg in mesh.GetBoundaries()])
Ubnd = CoefficientFunction((hbndreg,hubndreg,hvbndreg))
# constant for gravitational force
g=1
Flux definition and numerical flux choice (Lax-Friedrich)¶
[7]:
def F(U):
h, hvx, hvy = U
vx = hvx/h
vy = hvy/h
return CoefficientFunction(((hvx,hvy),
(hvx*vx + 0.5*g*h**2, hvx*vy),
(hvx*vy, hvy*vy + 0.5*g*h**2)),dims=(3,2))
[8]:
n = specialcf.normal(mesh.dim)
def Max(u,v):
return IfPos(u-v,u,v)
def Fmax(A,B): # max. characteristic speed:
ha, hua, hva = A
hb, hub, hvb = B
vnorma = sqrt(hua**2+hva**2)/ha
vnormb = sqrt(hub**2+hvb**2)/hb
return Max(vnorma+sqrt(g*A[0]),vnormb+sqrt(g*B[0]))
def Fhatn(U): # numerical flux
Uhat = U.Other(bnd=Ubnd)
return (0.5*F(U)+0.5*F(Uhat))*n + Fmax(U,Uhat)/2*(U-Uhat)
DG formulation¶
We recall that a BilinearForm
is allowed to be nonlinear in the first argument.
[9]:
def DGBilinearForm(fes,F,Fhatn,Ubnd):
a = BilinearForm(fes, nonassemble=True)
a += - InnerProduct(F(U),Grad(V)) * dx
a += InnerProduct(Fhatn(U),V) * dx(element_boundary=True)
return a
a = DGBilinearForm(fes,F,Fhatn,Ubnd)
Simple fix to deal with shocks: artificial diffusion:¶
[10]:
from DGdiffusion import AddArtificialDiffusion
artvisc = Parameter(1.0)
if order > 0:
AddArtificialDiffusion(a,Ubnd,artvisc,compile=True)
Compiled CF:
N5ngfem28ParameterCoefficientFunctionIdEE
N5ngfem24ScaleCoefficientFunctionE
Z25ExportCoefficientFunctionRN8pybind117module_EE10MeshSizeCF
N5ngfem13cl_BinaryOpCFINS_11GenericMultEEE
N5ngfem27ConstantCoefficientFunctionE
N5ngfem13cl_BinaryOpCFINS_10GenericDivEEE
N5ngfem13ProxyFunctionE
N5ngfem13ProxyFunctionE
N5ngfem31T_MultVecVecCoefficientFunctionILi6EEE
N5ngfem13cl_BinaryOpCFINS_11GenericMultEEE
inputs =
0:
1: 0
2:
3: 1 2
4:
5: 3 4
6:
7:
8: 6 7
9: 5 8
Compiled CF:
N5ngfem28ParameterCoefficientFunctionIdEE
N5ngfem24ScaleCoefficientFunctionE
Z25ExportCoefficientFunctionRN8pybind117module_EE10MeshSizeCF
N5ngfem13cl_BinaryOpCFINS_11GenericMultEEE
N5ngfem27ConstantCoefficientFunctionE
N5ngfem13cl_BinaryOpCFINS_10GenericDivEEE
N5ngfem13ProxyFunctionE
N5ngfem28TransposeCoefficientFunctionE
N5ngfem24ScaleCoefficientFunctionE
N5ngfem13ProxyFunctionE
N5ngfem28TransposeCoefficientFunctionE
N5ngfem24ScaleCoefficientFunctionE
N5ngfem13cl_BinaryOpCFINS_11GenericPlusEEE
N5ngfem17cl_NormalVectorCFILi2EEE
N5ngfem29MultMatVecCoefficientFunctionE
N5ngfem13ProxyFunctionE
N5ngfem13ProxyFunctionE
N5ngfem13cl_BinaryOpCFINS_12GenericMinusEEE
N5ngfem31T_MultVecVecCoefficientFunctionILi3EEE
N5ngfem24ScaleCoefficientFunctionE
N5ngfem13ProxyFunctionE
N5ngfem28TransposeCoefficientFunctionE
N5ngfem24ScaleCoefficientFunctionE
N5ngfem13ProxyFunctionE
N5ngfem28TransposeCoefficientFunctionE
N5ngfem24ScaleCoefficientFunctionE
N5ngfem13cl_BinaryOpCFINS_11GenericPlusEEE
N5ngfem29MultMatVecCoefficientFunctionE
N5ngfem13ProxyFunctionE
N5ngfem13ProxyFunctionE
N5ngfem13cl_BinaryOpCFINS_12GenericMinusEEE
N5ngfem31T_MultVecVecCoefficientFunctionILi3EEE
N5ngfem13cl_BinaryOpCFINS_12GenericMinusEEE
N5ngfem27ConstantCoefficientFunctionE
N5ngfem13cl_BinaryOpCFINS_10GenericDivEEE
N5ngfem13cl_BinaryOpCFINS_12GenericMinusEEE
N5ngfem13ProxyFunctionE
N5ngfem13cl_BinaryOpCFINS_12GenericMinusEEE
N5ngfem31T_MultVecVecCoefficientFunctionILi3EEE
N5ngfem13cl_BinaryOpCFINS_11GenericMultEEE
N5ngfem13cl_BinaryOpCFINS_11GenericPlusEEE
N5ngfem13cl_BinaryOpCFINS_11GenericMultEEE
inputs =
0:
1: 0
2:
3: 1 2
4:
5: 3 4
6:
7: 6
8: 7
9:
10: 9
11: 10
12: 8 11
13:
14: 12 13
15:
16:
17: 15 16
18: 14 17
19: 18
20:
21: 20
22: 21
23:
24: 23
25: 24
26: 22 25
27: 26 13
28:
29:
30: 28 29
31: 27 30
32: 19 31
33:
34: 33 2
35: 28 29
36:
37: 15 36
38: 35 37
39: 34 38
40: 32 39
41: 5 40
Visualization of solution quantities¶
[11]:
gfu = GridFunction(fes)
gfh, gfhu, gfhv = gfu
gfvu = gfhu/gfh
gfvv = gfhv/gfh
momentum = CoefficientFunction((gfhu,gfhv))
velocity = CoefficientFunction((gfvu,gfvv))
gfu.Set(U0)
Draw(momentum,mesh,"mom")
Draw(velocity,mesh,"vel")
Draw(gfh,mesh,"h")
setvalues element 206/206
Explicit Euler time stepping¶
[12]:
def TimeLoop(a,gfu,dt,T,nsamplings=100):
#gfu.Set(U0)
res = gfu.vec.CreateVector()
fes = a.space
t = 0; i = 0
nsteps = int(ceil(T/dt))
invma = fes.Mass(1).Inverse() @ a.mat
with TaskManager():
while t <= T - 0.5*dt:
res = invma * gfu.vec
gfu.vec.data -= dt * res
t += dt
if (i+1) % int(nsteps/nsamplings) == 0:
Redraw()
i+=1
print("\rt = {:.10}".format(t),end="")
Redraw()
[13]:
TimeLoop(a,gfu,dt=0.0004,T=3)
t = 3.0
You may play around with this example.
change the artificial diffusion parameter: How does it influence the solution
change boundary conditions: left boundary -> fixed height and non-reflecting
change the initial heights
introduce a (circular) obstacle below the dam
Generate output to create a video: To this end take a look at the final unit of this section.
[14]:
%%HTML
<video width="600" height="400" controls>
<source src="../../_static/shallow2D.mov">
</video>