Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Embedded Trefftz

Classical Trefftz methods need an explicit local basis of functions satisfying the PDE. These are difficult to construct. Embedded Trefftz works differently:

  1. start with a standard discontinuous polynomial space,

  2. assemble elementwise a local version of the PDE,

  3. compute the elementwise nullspace of that residual,

  4. assemble the usual DG formulation on that nullspace.

Moreover, we relax the Trefftz condition in a way that generalizes to operators that do not have a kernel in the polynomials.

Constructing an Embedding

The embedding represents the unknown Trefftz basis inside the standard discontinuous polynomial basis {ϕi}i=1N\{\phi_i\}_{i=1}^N.

ψj=i=1NTijϕi,TRN×M,MN.\psi_j=\sum_{i=1}^N \mathbf{T}_{ij}\phi_i, \qquad \mathbf T\in\mathbb R^{N\times M},\qquad M\ll N.

If AA is the ordinary DG matrix on the full polynomial space, then we solve is the projected system

TTATuT=TTb.\mathbf T^T \mathbf A \mathbf T\,u_T = \mathbf T^T b.

The embedding is constructed from the residual equations.

(WK)ji=(LKϕi,ξj)K.(\mathbf W_K)_{ji}=(\mathcal L_K\phi_i,\xi_j)_K .

The local Trefftz coefficients are the nullspace of WKW_K. Numerically, TrefftzEmbedding computes that nullspace element by element with an SVD (or equivalently a QR):

T=ker(W),Wji=KThLϕi,ξjK\boxed{ {\color{red}\mathbf{T}} = \ker(\mathbf{W}), \qquad \mathbf W_{ji} = \sum_{K\in\mathcal{T}_h} \left\langle \mathcal{L}\,{\color{blue}\phi_i}, {\color{purple}\xi_j} \right\rangle_K }

On each mesh element, use an SVD (or QR decomposition)\textcolor{gray}{\text{(or QR decomposition)}}:

WK=(u1uL)UK  (σ10σL0)ΣK  (v1TvLTvL+1TvNT)VKT.\left.\mathbf{W}\right|_K = {\color{gray} \underbrace{ \begin{pmatrix} | & & | \\ \mathbf{u}_1 & \cdots & \mathbf{u}_L \\ | & & | \end{pmatrix} }_{\mathbf{U}_K} } \; \underbrace{ \begin{pmatrix} \sigma_1 & & & {\color{green}0} \\ & \ddots & & \vdots \\ & & \sigma_L & {\color{green}0} \end{pmatrix} }_{\boldsymbol{\Sigma}_K} \; \underbrace{ \begin{pmatrix} - & \mathbf{v}_1^{T} & - \\ & \vdots & \\ - & \mathbf{v}_L^{T} & - \\[0.4em] {\color{red}-} & {\color{red}\mathbf{v}_{L+1}^{T}} & {\color{red}-} \\ & {\color{red}\vdots} & \\ {\color{red}-} & {\color{red}\mathbf{v}_N^{T}} & {\color{red}-} \end{pmatrix} }_{\mathbf{V}_K^{T}} .

Hence, the local embedding is given by

TK=(vL+1vN).{\color{red} \mathbf{T}_K = \begin{pmatrix} | & & | \\ \mathbf{v}_{L+1} & \cdots & \mathbf{v}_N \\ | & & | \end{pmatrix}. }

Conditioning of the embedded Trefftz method.

κ2 ⁣(TTAT)κ2 ⁣(A)\boxed{ \kappa_2\!\left( {\color{red}\mathbf{T}}^{T} {\color{blue}\mathbf{A}} {\color{red}\mathbf{T}} \right) \leq \kappa_2\!\left( {\color{blue}\mathbf{A}} \right) }
Source
from ngsolve import *
from netgen.occ import *
from ngsolve_book import Draw
from ngstrefftz import *
SetNumThreads(4)

import matplotlib.pyplot as plt
import scipy.sparse as sp

Lap = lambda u: sum(Trace(u.Operator("hesse")))

A first Embedding

Since

Δ:Pp(K)Pp2(K),-\Delta:\mathbb P^p(K)\to \mathbb P^{p-2}(K),

choosing Q(K)=Pp2(K)Q(K)=\mathbb P^{p-2}(K) yields

vTp(K)    (Δv,q)K=0qPp2(K)    Δv=0.v\in \mathbb T^p(K) \iff (-\Delta v,q)_K=0 \quad \forall q\in \mathbb P^{p-2}(K) \iff \Delta v=0.
mesh = Mesh(unit_square.GenerateMesh(maxh=0.35))
p = 4

base = L2(mesh, order=p, dgjumps=True)
test = L2(mesh, order=p - 2, dgjumps=True)

u = base.TrialFunction()
q = test.TestFunction()

op = (-Lap(u)) * q * dx
emb = TrefftzEmbedding(op)

print(f"full polynomial dofs: {base.ndof}")
print(f"embedded Trefftz dofs: {emb.GetEmbedding().shape[1]}")
full polynomial dofs: 270
embedded Trefftz dofs: 162

The embedded unknown lives in the small Trefftz space. emb.Embed(...) converts it back to the polynomial space for visualization or post-processing.

ut = emb.GetEmbedding().CreateRowVector()
up = GridFunction(base, multidim=len(ut))

for i, vec in enumerate(up.vecs):
    ut[:] = 0
    ut[i] = 1
    vec.data = emb.Embed(ut)

Draw(up,mesh,"basis",animate=True,interpolate_multidim=False,min=-1, max=1, deformation=True, scale=.3, euler_angles=[-70,0.4,2],)
Loading...
WebGLScene

Setting up the global embedded DG system

We can use again the standard SIPDG

ah(u,v)=ΩuvdxFhint({ ⁣{u} ⁣}[ ⁣[v] ⁣]+{ ⁣{v} ⁣}[ ⁣[u] ⁣]αp2h[ ⁣[u] ⁣][ ⁣[v] ⁣])dsFhbnd(nuv+nvuαp2huv)ds,(v)=Fhbnd(αp2hgvnvg)ds.\begin{aligned} a_h(u,v) &= \int_{\Omega} \nabla u \cdot \nabla v \,\mathrm{d}x - \int_{\mathcal{F}_h^{\mathrm{int}}} \left( \{\!\{\nabla u\}\!\} \cdot [\![ v ]\!] + \{\!\{\nabla v\}\!\} \cdot [\![ u ]\!] - \frac{\alpha p^2}{h} [\![ u ]\!] \cdot [\![ v ]\!] \right) \,\mathrm{d}s \\ &\quad - \int_{\mathcal{F}_h^{\mathrm{bnd}}} \left( n\cdot \nabla u\,v + n\cdot \nabla v\,u - \frac{\alpha p^2}{h}\,uv \right) \,\mathrm{d}s, \\ \ell(v) &= \int_{\mathcal{F}_h^{\mathrm{bnd}}} \left( \frac{\alpha p^2}{h}\,gv - n\cdot\nabla v\,g \right) \,\mathrm{d}s . \end{aligned}
Source
def sip_laplace(fes, bndc, rhs=0, alpha=4):
    mesh = fes.mesh
    order = fes.globalorder
    n = specialcf.normal(mesh.dim)
    h = specialcf.mesh_size
    u = fes.TrialFunction()
    v = fes.TestFunction()

    jump_u = (u - u.Other()) * n
    jump_v = (v - v.Other()) * n
    mean_dudn = 0.5 * (grad(u) + grad(u.Other()))
    mean_dvdn = 0.5 * (grad(v) + grad(v.Other()))

    a = BilinearForm(fes, symmetric=True)
    a += grad(u) * grad(v) * dx
    a += alpha * order**2 / h * jump_u * jump_v * dx(skeleton=True)
    a += (-mean_dudn * jump_v - mean_dvdn * jump_u) * dx(skeleton=True)
    a += alpha * order**2 / h * u * v * ds(skeleton=True)
    a += (-n * grad(u) * v - n * grad(v) * u) * ds(skeleton=True)

    f = LinearForm(fes)
    f += rhs * v * dx
    f += alpha * order**2 / h * bndc * v * ds(skeleton=True)
    f += -n * grad(v) * bndc * ds(skeleton=True)

    with TaskManager():
        a.Assemble()
        f.Assemble()
    return a, f

First, the explicit matrix projection mirrors the derivation:

exact = exp(x) * sin(y)

a_full, f_full = sip_laplace(base, exact)
T = emb.GetEmbedding()
TT = T.CreateTranspose()

with TaskManager():
    A_t = TT @ a_full.mat @ T
    b_t = TT * f_full.vec
    sol_t = A_t.Inverse(inverse="sparsecholesky") * b_t

gfu = GridFunction(base)
gfu.vec.data = T * sol_t

print(f"error: {sqrt(Integrate((gfu - exact) ** 2, mesh)):.3e}")

Draw(gfu, mesh, "projected embedded solve");
error: 9.955e-07
Loading...

EmbeddedTrefftzFES

For production code, EmbeddedTrefftzFES avoids assembling the larger full matrix first:

tfes = EmbeddedTrefftzFES(emb)

a_emb, f_emb = sip_laplace(tfes, exact)
gfu_t = GridFunction(tfes)

with TaskManager():
    gfu_t.vec.data = a_emb.mat.Inverse(inverse="sparsecholesky") * f_emb.vec

gfu = GridFunction(base)
gfu.vec.data = emb.Embed(gfu_t.vec)

print(f"error: {sqrt(Integrate((gfu - exact) ** 2, mesh)):.3e}")
error: 9.955e-07

Inhomogeneous Problems

For Lu=f\mathcal L u=f the unknown is split into a local particular part and a homogeneous Trefftz correction:

uh=uh,f+TuT.u_h = u_{h,f} + \mathbf T u_T.

The local equations determine uh,fu_{h,f} locally, while the global DG solve determines only uTu_T:

TTATuT=TT(bAuh,f).\mathbf T^T \mathbf A \mathbf T u_T = \mathbf T^T(b - \mathbf A u_{h,f}).
p = 4

base = L2(mesh, order=p, dgjumps=True)
test = L2(mesh, order=p - 2, dgjumps=True)

u = base.TrialFunction()
q = test.TestFunction()

exact = sin(pi * x) * sin(pi * y)
rhs = 2 * pi**2 * exact

op = Lap(u) * q * dx
lop = -rhs * q * dx

emb = TrefftzEmbedding(op, lop)
up = emb.GetParticularSolution()

a, f = sip_laplace(base, exact, rhs=rhs)
T = emb.GetEmbedding()
TT = T.CreateTranspose()

with TaskManager():
    A_t = TT @ a.mat @ T
    b_t = TT * (f.vec - a.mat * up)
    sol_t = A_t.Inverse(inverse="sparsecholesky") * b_t

gfu = GridFunction(base)
gfu.vec.data = T * sol_t + up

print(f"Poisson embedded error: {sqrt(Integrate((gfu - exact) ** 2, mesh)):.3e}")
print(f"full dofs: {base.ndof}, Trefftz dofs: {T.width}")

Draw(gfu, mesh, "gfu");
Poisson embedded error: 1.021e-04
full dofs: 270, Trefftz dofs: 162
Loading...

Local-Global Coupling

The embedded view also separates the global DG coupling from the local residual constraints. Recall the weak Trefftz space

Th={vhVh:(LKvh,qh)K=0qhQh(K), KTh}.\mathbb T_h = \left\{ v_h\in V_h: (\mathcal L_K v_h,q_h)_K=0 \quad \forall q_h\in Q_h(K),\ K\in\mathcal T_h \right\}.

For inhomogeneous problems, the algebraic problem can be written as a coupled local-global system: find uhVhu_h\in V_h such that

{ah(uh,vh)=h(vh)vhTh,(LKuh,qh)K=(f,qh)KqhQh(K), KTh.\begin{cases} a_h(u_h,v_h)=\ell_h(v_h) &\forall v_h\in \mathbb T_h,\\[0.4em] (\mathcal L_K u_h,q_h)_K=(f,q_h)_K &\forall q_h\in Q_h(K),\ K\in\mathcal T_h. \end{cases}

The crucial decomposition is

Vh=ThLh.V_h=\mathbb T_h\oplus \mathbb L_h.

Here Lh\mathbb L_h is a local lifting complement. In the block picture below, the lower-left block is zero because Trefftz trial functions satisfy the local residual equations. The lower-right block is local and determines the lifting or particular part; the upper blocks are the globally coupled DG equations.