Embedded Trefftz-DG: Stokes problem
\(\newcommand{\Th}{\mathcal{T}_h} \newcommand{\Vhp}{V^p(\Th)} \newcommand{\bT}{\mathbf{T}} \newcommand{\bW}{\mathbf{W}} \newcommand{\bl}{\mathbf{l}} \newcommand{\bM}{\mathbf{M}} \newcommand{\bL}{\mathbf{L}} \newcommand{\bA}{\mathbf{A}} \newcommand{\bU}{\mathbf{U}} \newcommand{\bV}{\mathbf{V}} \newcommand{\calL}{\mathcal{L}} \newcommand{\bu}{\mathbf{u}} \newcommand{\IT}{\mathbb{T}} \newcommand{\calG}{\mathcal{G}} \newcommand{\be}{\mathbf{e}} \newcommand{\bx}{{\mathbf x}} \newcommand{\inner}[1]{\langle #1 \rangle} \DeclareMathOperator\Ker{ker} \DeclareMathOperator{\Div}{\mathrm{div}}\)
The Stokes equations finds a velocity \(u\) and a pressure \(p\) such that
[1]:
from ngsolve import *
from ngstrefftz import *
from netgen.occ import *
from ngsolve.webgui import Draw
SetNumThreads(4)
nu=1
The Trefftz embedding
We use the function \(\texttt{TrefftzEmbedding}\) and pass the operator
with test functions
We are missing a Hesse operator for \(u\) therefore, assuming \(\nu\) constant, we use the equivalent formulation
where we used the notation \(\partial_n w := \nabla w \cdot n\).
[2]:
def TrefftzStokesEmb(fes,rhs=CF((0,0))):
mesh = fes.components[0].mesh
k = fes.components[0].globalorder
Vs = VectorL2(mesh, order=k - 2)
Qs = L2(mesh, order=k - 1)
test_fes = Vs * Qs
u, p = fes.TrialFunction()
wu, wp = test_fes.TestFunction()[0:2]
n = specialcf.normal(mesh.dim)
op = nu*InnerProduct( Grad(u),Grad(wu) ) * dx \
- nu*InnerProduct(Grad(u)*n,wu) * dx(element_boundary=True) \
+ InnerProduct(grad(p),wu) * dx + div(u)*wp*dx
lop = rhs * wu * dx(bonus_intorder=5)
PP, uf = TrefftzEmbedding(op,fes,lop,eps=10**-6,test_fes=test_fes)
return PP, uf
We can visualize the Trefftz basis functions via the embedding and drawing the \(\texttt{GridFunction}\) of the corresponding polynomial space
[3]:
def EmbeddedBasisFunctionsAsMultiDim(Emb, fes):
gfshow = GridFunction(fes, multidim=0)
gf = GridFunction(fes)
coefvec = Emb.CreateRowVector()
for i in range(Emb.width):
coefvec[:] = 0
coefvec[i] = 1
gf.vec.data = Emb * coefvec
gfshow.AddMultiDimComponent(gf.vec)
return gfshow
[4]:
order = 3
mesh = Mesh(unit_square.GenerateMesh(maxh=1))
V = VectorL2(mesh, order=order, dgjumps=True)
Q = L2(mesh, order=order - 1, dgjumps=True)
fes = V * Q
PP,uf = TrefftzStokesEmb(fes)
gfshow = EmbeddedBasisFunctionsAsMultiDim(PP,fes)
Draw (gfshow.components[0], mesh, interpolate_multidim=False, animate=False, autoscale=True, vectors = {"grid_size" : 20}, settings={"subdivision":20})
[4]:
BaseWebGuiScene
Stokes-DG formulation
\(\newcommand{\Th}{{\mathcal{T}_h}} \newcommand{\Fh}{{\mathcal{F}_h}} \newcommand{\Fhi}{{\mathcal{F}_h^{\text{i}}}} \newcommand{\Fhb}{{\mathcal{F}_h^{\text{b}}}} \newcommand{\TT}{\mathbb{T}} \newcommand{\X}{\mathbb{X}} \renewcommand{\SS}{\mathbb{S}} \newcommand{\RR}{\mathbb{R}} \newcommand{\PP}{\mathbb{P}} \newcommand{\BDM}{\mathbb{B}\mathbb{D}\mathbb{M}} \newcommand{\RT}{\mathbb{R}\mathbb{T}} \newcommand{\PiN}[1]{\Pi_{\mathcal{N}}^{#1}} \newcommand{\cP}{\mathcal{P}} \DeclareMathOperator{\range}{range} \DeclareMathOperator{\dist}{dist} \DeclareMathOperator{\id}{id} \DeclareMathOperator{\Int}{Int} \DeclareMathOperator{\meas}{meas} \DeclareMathOperator{\Div}{\mathrm{div}} \DeclareMathOperator{\Grad}{\nabla} \DeclareMathOperator\Supp{Supp} \DeclareMathOperator\Span{span} \DeclareMathOperator\Rank{rank} \DeclareMathOperator\Ker{ker} \DeclareMathOperator\Range{range} \DeclareMathOperator\dt{\frac{\partial}{\partial t}} \newcommand\restr[2]{{\left.\kern-\nulldelimiterspace #1 \right|_{#2} }} \newcommand{\avg}[1]{\{\!\!\{ #1\}\!\!\}} \newcommand{\jmp}[1]{[\![#1]\!]} \newcommand{\jump}[1]{[\![#1]\!]}\) As a basis for the following we consider the established DG discretization of the Stokes problem: Find \((u_h, p_h) \in [\PP^k]^d \times \PP^{k-1}\setminus \RR\), s.t.
with the bilinear forms
where the interior penalty parameter \(\alpha = \mathcal{O}(k^2)\) is chosen sufficiently large enough.
[5]:
def StokesDG(fes, nu, rhs):
k = fes.components[0].globalorder
u, v = fes.TrialFunction()[0], fes.TestFunction()[0]
p, q = fes.TrialFunction()[1], fes.TestFunction()[1]
alpha = 10 # interior penalty param
n = specialcf.normal(mesh.dim)
h = specialcf.mesh_size
jump_u = u - u.Other()
jump_v = v - v.Other()
mean_dudn = 0.5 * (grad(u) + grad(u.Other())) * n
mean_dvdn = 0.5 * (grad(v) + grad(v.Other())) * n
mean_q = 0.5 * n * (q + q.Other())
mean_p = 0.5 * n * (p + p.Other())
a = BilinearForm(fes)
a += nu * InnerProduct(grad(u), grad(v)) * dx
a += nu * alpha * k**2 / h * jump_u * jump_v * dx(skeleton=True)
a += nu * (-mean_dudn * jump_v - mean_dvdn * jump_u) * dx(skeleton=True)
a += nu * alpha * k**2 / h * u * v * ds(skeleton=True)
a += nu * (-grad(u) * n * v - grad(v) * n * u) * ds(skeleton=True)
a += (-div(u) * q - div(v) * p) * dx
a += (mean_p * jump_v + mean_q * jump_u) * dx(skeleton=True)
a += (p * v * n + q * u * n) * ds(skeleton=True)
a += 1e-8 * (p * q) * dx
a.Assemble()
f = LinearForm(fes)
f += rhs * v * dx(bonus_intorder=5)
f.Assemble()
return a,f
We consider the following exact solution
[6]:
zeta = x**2 * (1-x)**2 * y**2 * (1-y)**2
uexact = CF((zeta.Diff(y), - zeta.Diff(x)))
graduexact = CF((uexact.Diff(x),uexact.Diff(y)),dims=(2,2)).trans
pexact = x**6+y**6 - 2/7
f1 = - nu*uexact[0].Diff(x).Diff(x) - nu*uexact[0].Diff(y).Diff(y) + pexact.Diff(x)
f2 = - nu*uexact[1].Diff(x).Diff(x) - nu*uexact[1].Diff(y).Diff(y) + pexact.Diff(y)
rhs = CF((f1,f2))
[7]:
order = 3
mesh = Mesh(unit_square.GenerateMesh(maxh=.1))
V = VectorL2(mesh, order=order, dgjumps=True)
Q = L2(mesh, order=order - 1, dgjumps=True)
fes = V * Q
a,f = StokesDG(fes, 1, rhs)
PP, uf = TrefftzStokesEmb(fes, rhs)
PPT = PP.CreateTranspose()
TA = PPT @ a.mat @ PP
Tgfu = TA.Inverse(inverse="pardiso") * (PPT * (f.vec - a.mat * uf))
gfu = GridFunction(fes)
gfu.vec.data = PP * Tgfu + uf
[8]:
uh = gfu.components[0]
Draw(uh,mesh,"u")
print(sqrt(Integrate(InnerProduct(uh-uexact,uh-uexact),mesh)))
9.39961407472032e-07