86. Multi-level Extension#
The goal is to extend boundary data (e.g. Dirichlet values) onto the domain, such that the resulting energy norm is optimal (up to a constant factor). We do so by a multi-level decomposition of the boundary data.
86.1. Efficiently computable multi-level decomposition#
We are given a nested sequence of spaces
with
We know that there exist such decompositions, but do not yet have a cheap algorithm for computing one. This we define next.
Let
If
For
which is a decomposition
Theorem There holds
Proof: We use that there exists a stable decomposition
For
for
Using the triangle inequality, the proven estimates and Cauchy-Schwarz:
86.2. Algorithm#
The quasi-interpolant of
can be implemented as follows. Let
The values
are the entries of the vector , where .
86.3. Extending boundary data#
We use this coarse-level interpolants to extend Dirichlet data onto the domain. On every level we have a trivial extension
by just setting all interior nodal values to zero. Thus, the function decays very quickly from the boundary. If the mesh gets more and more refined, the decay gets steeper and steeper.
from ngsolve import *
from ngsolve.webgui import Draw
mesh = Mesh(unit_square.GenerateMesh(maxh=0.3))
fes = H1(mesh, order=1, dirichlet="left|bottom", autoupdate=True)
u,v = fes.TnT()
gfu = GridFunction(fes, autoupdate=True)
for i in range(4):
mesh.Refine()
a = BilinearForm(grad(u)*grad(v)*dx).Assemble()
copy mesh complete
bnd = mesh.Boundaries("left|bottom")
gfu.Set (1-x-y+0.3*sin(10*pi*x), definedon=bnd)
Draw (gfu, deformation=True)
print ("Norm(u) = ", InnerProduct((a.mat*gfu.vec).Evaluate(), gfu.vec))
Norm(u) = 38.983324464566756
The method is now to decompose the function into different levels, and use the trivial extension on every level to obtain a better extension:
class MLExtension:
def __init__ (self, fes, level, bndmass, dofs):
self.fes = fes
self.level = level
self.bndmass = bndmass
self.dofs = dofs
ones = bndmass.CreateRowVector()
ones[:] = 1
Mones = (bndmass*ones).Evaluate()
self.inv = DiagonalMatrix(Mones).Inverse(dofs)
if level > 0:
self.prol = fes.Prolongation().CreateMatrix(level)
self.rest = self.prol.CreateTranspose()
coarsebndmass = self.rest @ bndmass @ self.prol # multiply matrices
coarsedofs = BitArray(self.prol.width)
coarsedofs[:] = False
for i in range(len(coarsedofs)):
coarsedofs[i] = dofs[i]
self.coarseext = MLExtension(fes, level-1, coarsebndmass, coarsedofs)
def Extend (self, x):
Mx = (self.bndmass * x).Evaluate()
ext = self.ExtendRec(Mx)
x[~self.dofs] = ext
def ExtendRec (self, Mx):
sol = (self.inv * Mx).Evaluate()
if self.level == 0:
return sol
if self.level > 0:
self.fes.Prolongation().Restrict(self.level, Mx)
xc = self.coarseext.ExtendRec(Mx)
pxc = (self.fes.Prolongation().Operator(self.level) * xc).Evaluate()
pxc[self.dofs] = sol
return pxc
bndmass = BilinearForm(u*v*ds(bnd), check_unused=False).Assemble().mat
ext = MLExtension(fes, fes.mesh.levels-1, bndmass, fes.GetDofs(bnd))
ext.Extend(gfu.vec)
Draw (gfu, deformation=True)
print ("Norm(uext) = ", InnerProduct(a.mat*gfu.vec, gfu.vec))
Norm(uext) = 3.541248535042474