Embedded Trefftz method
\(\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}\)
Let \(\{\phi_i\}\) be the set of basis functions of \(\Vhp\), \(N =\operatorname{dim}(\Vhp)\) and \(\calG: \mathbb{R}^n \to \Vhp\) be the Galerkin isomorphism, \(\calG(\bx) = \sum_{i=1}^N \bx_i \phi_i\). With \(\be_i,~i=1,..,N\) the canonical unit vectors in \(\mathbb{R}^n\), so that \(\calG(\be_i) = \phi_i\), we define the following matrices and vector for \(i,j = 1,\dots, N\)
where \(\inner{\cdot,\cdot}_{0,h}=\sum_{K\in\Th}\inner{\cdot,\cdot}_K\) is the element-wise \(L^2\)-inner product. We are interested in the kernel of \(\calL\) (in an element-wise and pointwise sense) in \(\Vhp\) as this is the part where the Trefftz DG method operates. We note that
and hence are looking for a basis of \(\Ker(\bW)\) which . We can determine the kernel of \(\bW\) element-wise, for \(\bW_K\) collect a set of orthogonal basis vectors in a matrix \(\bT_K\in\mathbb{R}^{N_{\!K}\!\times\! M_{\!K}}\) so that \(\ker(\bW_K) = \bT_K \cdot \mathbb{R}^{M_K}\).
We can then implicitly solve using Trefftz test and trial functions by solving:
Find \(\bu_\IT\) so that
The solution in the full polynomial space is then given by \(\bu=\bT\bu_\IT\)
[1]:
from ngsolve import *
from ngstrefftz import *
from netgen.occ import *
SetNumThreads(4)
Laplace problem
We are looking to solve
with the embedded Trefftz method, now $ \calL `= :nbsphinx-math:Delta`$.
[2]:
exact = exp(x+y)*sin(sqrt(2)*z)
order = 4
mesh = Mesh(unit_cube.GenerateMesh(maxh=.2))
fes = L2(mesh, order=order, dgjumps=True)
A suiteable DG method is given by
[3]:
def lapdg(fes,bndc):
alpha = 4
n = specialcf.normal(mesh.dim)
h = specialcf.mesh_size
u = fes.TrialFunction()
v = fes.TestFunction()
jump = lambda u: u-u.Other()
mean_dn = lambda u: 0.5*n * (grad(u)+grad(u).Other())
a = BilinearForm(fes,symmetric=True)
a += grad(u)*grad(v) * dx \
+alpha*order**2/h*jump(u)*jump(v) * dx(skeleton=True) \
+(-mean_dn(u)*jump(v)-mean_dn(v)*jump(u)) * dx(skeleton=True) \
+alpha*order**2/h*u*v * ds(skeleton=True) \
+(-n*grad(u)*v-n*grad(v)*u)* ds(skeleton=True)
a.Assemble()
f = LinearForm(fes)
f += alpha*order**2/h*bndc*v * ds(skeleton=True) \
+(-n*grad(v)*bndc)* ds(skeleton=True)
f.Assemble()
return a,f
a,f = lapdg(fes,exact)
with TaskManager():
a.Assemble()
f.Assemble()
Trefftz embedding
We use the function \(\texttt{TrefftzEmbedding}\) and pass the operator
When numerically computing the kernel of a matrix, we use a SVD. The diagonal matrix of the SVD would have \(M_K\) zeros assuming exact arithmetics. Due to inexact computer arithmetics this will not be the case exactly and hence we use a truncation parameter \(\varepsilon>0\) to determine which values are considered as (numerical) zeros.
[4]:
eps=10**-9
Lap = lambda u : sum(Trace(u.Operator('hesse')))
u,v = fes.TnT()
op = Lap(u)*Lap(v)*dx
with TaskManager():
PP = TrefftzEmbedding(op,fes,eps)
PPT = PP.CreateTranspose()
with TaskManager():
TA = PPT@a.mat@PP
TU = TA.Inverse(inverse='sparsecholesky')*(PPT*f.vec)
tgfu = GridFunction(fes)
tgfu.vec.data = PP*TU
[5]:
error = sqrt(Integrate((tgfu-exact)**2, mesh))
print("embedded Trefftz error ",error)
embedded Trefftz error 6.901461110293332e-07
Trefftz embedding type 2
We can also construct the embedding by finding the kernel of
[6]:
eps=10**-9
Lap = lambda u : sum(Trace(u.Operator('hesse')))
u = fes.TrialFunction()
fes2 = L2(mesh, order=order-2, dgjumps=True)
v = fes2.TestFunction()
op = Lap(u)*v*dx
with TaskManager():
PP = TrefftzEmbedding(op,fes,test_fes=fes2)
PPT = PP.CreateTranspose()
with TaskManager():
TA = PPT@a.mat@PP
TU = TA.Inverse(inverse='sparsecholesky')*(PPT*f.vec)
tgfu = GridFunction(fes)
tgfu.vec.data = PP*TU
[7]:
error = sqrt(Integrate((tgfu-exact)**2, mesh))
print("embedded Trefftz error ",error)
embedded Trefftz error 6.901461114480883e-07
Trefftz embedding via FESpace
The following improves the runtime of the embedded Trefftz method by avoiding the full assembly of the larger system matrix. Instead we directly assemble the matrix \(\bT^T\bA\bT\) using the local embedding matrices.
We use EmbeddedTrefftzFES
to construct an embedded space from an underlying L2
space.
[8]:
with TaskManager():
fes = L2(mesh, order=order, dgjumps=True)
u,v = fes.TnT()
op = Lap(u)*Lap(v)*dx
etfes = EmbeddedTrefftzFES(fes)
etfes.SetOp(op,eps=eps)
a,f = lapdg(etfes,exact)
a.Assemble()
f.Assemble()
inv = a.mat.Inverse(inverse="sparsecholesky")
gfu = GridFunction(etfes)
gfu.vec.data = inv * f.vec
[9]:
print("embedded Trefftz error ",sqrt(Integrate((gfu-exact)**2, mesh)))
embedded Trefftz error 6.901461113074624e-07