81. Overlapping Domain Decomposition Methods#

Let {Ωi} be a non-overlapping decomposition of Ω into open sub-domains, i.e.

Ω= Ωi

with

ΩiΩj=0 for ij.

Let Hi=diamΩi. To simplify notation we assume that all Hi are of the same order of magnitude, and write H.

In addition to Ωi, we define enlarged sub-domains

Ω~i=UcH(Ωi)={xΩ:dist(x,Ωi)<cH},

where the constant c is of order one.

The overlapping domain decomposition method is a sub-space decomposition of the global finite element space VhH0,D1(Ω) into local spaces supported on the overlapping sub-domains Ω~i, i.e.

Vi={vVh:v=0 on ΩΩ~i}.

The overlapping domain decomposition method is an additive Schwarz method with the space splitting

Vh=Vi

We are solving Dirichlet problems on the overlapping sub-domains, and adding up the solutions: The preconditioning action

C1:rw

is defined as

w=wiwithwiVi:A(wi,vi)=r(vi)viVi

81.1. Experiments with overlapping DD#

from ngsolve import *
from ngsolve.webgui import Draw

We start from a coarse grid, where we define the overlapping sub-domains as vertex patches: All elements connected with vertex i form the ith sub-domain Ω~i. To visualize it, we define an L2 finite element function with order=0.

mesh = Mesh(unit_square.GenerateMesh(maxh=0.3))
fesdom = L2(mesh, order=0, autoupdate=True)
fes = H1(mesh, order=1, dirichlet="bottom", autoupdate=True)
gfdom = GridFunction(fesdom, multidim=mesh.nv, nested=True, autoupdate=True)
for el in mesh.Elements(VOL):
    for v in el.vertices:
        gfdom.vecs[v.nr][el.nr] = 1
        
for l in range(2):
    mesh.Refine()

Draw (gfdom, animate=True);
copy mesh complete

Setup the problem. The sub-spaces are now finite element functions which are in H01(Ωi). The sub-spaces are represented via BitArrays with cleared bits for dofs not belonging to elements outside Ω~i:

u,v = fes.TnT()
a = BilinearForm(grad(u)*grad(v)*dx).Assemble()

pre = None
for v in gfdom.vecs:
    mask = BitArray(fes.FreeDofs())
    for el in fes.Elements(VOL):
        if v[el.nr] == 0:
            for d in el.dofs:
                mask[d] = False
    # print (mask)
    invi = a.mat.Inverse(freedofs=mask, inverse="sparsecholesky")
    pre = pre+invi if pre else invi     
from ngsolve.la import EigenValues_Preconditioner
lam = list(EigenValues_Preconditioner(a.mat, pre))
print ("lammin, lammax=", lam[0], lam[-1])
print ("kappa=", lam[-1]/lam[0])
lammin, lammax= 0.06687894741407863 2.99999999999935
kappa= 44.857165311304264

We observe the following behaviour:

  • the condition number is independent of the refinement level

  • the condition number grows with the number of sub-domains

81.2. Analysis of the DD preconditioner#

Theorem: The eigenvalues of the domain decomposition preconditioner are bounded as

σ(CDD1A)[c1H2,c2]

Outline of the proof: We apply the Additive Schwarz Lemma. The ASM lemma provides the representation

uTCDDu=infu=uiuiViiuiA2

By assuming shape regularity of the domains, only a small, fixed number of sub-domains overlap. From that we get the constant upper bound c2 for the spectrum. For the lower bound c1H2 we have to define explicitly a decomposition of a finite element function u into sub-space finite element functions. This requires some technical tools.

Partition on unity:

We call the set of functions {ψi} a partition of unity for the domain decomposition Ω~i iff

support{ψi}Ω~i
0ψi1

and

ψi=1

For the definition of the overlapping domain decomposition we can define a partition of unity as follows:

ψi0(x)=max{11cHidist{x,Ωi},0}

These functions are 1 inside Ωi, 0 outside Ω~i, and decay with a maximal derivative

|ψi0|1cHi.

However, these function do not sum up to 1, so we rescale by their sum to obtain a partition of unity:

ψi(x):=ψi0(x)ψi0(x)

H1-stable quasi-interpolation operators:

We need local interpolation-like operators $πh:H1Vh$

which are projectors onto Vh, i.e. Ihvh=vh for vhVh, are continuous in the H1 semi-norm

πhvL2(Ω)vL2(Ω),

and satisfy the approximation estimate $1h(vπhv)L2(Ω)vL2(Ω).$

We cannot use simple nodal interpolation since this is not well-defined for H1 functions (for two or more space dimensions), but we can use a Clément-type quasi-interpolation operator: Such an operator defines nodal values by some local averaging.

We have now the tools to conduct the proof for the sub-space decomposition:

For a given uVh we define

ui=πh(ψiu)

These ui are in Vi, i.e. are localized finite element functions.

By linearity of the quasi-interpolation, the partition of unity, and the projection property they are a decomposition of the given function u:

ui=πh(ψiu)=πh(ψiu)=πhu=u

Finally, we have to bound the norm of the decomposition. First, we use the boundedness of the quasi-interpolation in the H1 semi-norm:

uiL2=πh(ψiu)L2(ψiu)L2

We continue with the product rule, and bounds for the pu-functions and their derivatives:

uiL2(ψi)u+ψiuL2ψiLuL2(Ω~i)+ψiLuL2(Ω~i)H1uL2(Ω~i)+uL2(Ω~i)

Finally, we sum over all sub-domains. We use that only a small, fixed number of sub-domains overlap:

uiL22i{Hi2uL2(Ω~i)2+uL2(Ω~i)2}H2uL2(Ω)2+uL2(Ω)2H2uA2

In the last step we used Friedrichs’ inequality to bound the L2-norm by the H1 semi-norm. The factor H2 we paid for the derivative of the pu-functions shows up in the final result.

81.3. Overlapping DD Methods with coarse grid#

Now we add an additional coarse grid correction step. We assume that we have an additional finite element space VH, where the element-size is in the order of the sub-domain size H. We assume that VHVh.

We define this two-level preconditioner C2L by the preconditioning action in matrix notation

C2L1=CDD1+PAH1PT,

where AH is the discretization matrix on the coarse grid, and P is the so called prolongation matrix. If vH is some finite element function in the coarse space VH with coefficient vector vH, then the vector PvH are the coefficients of the same function represented on the fine grid. The matrix P of dimension dimVh×dimVH will not be stored as a dense matrix, but the matrix vector product and its transpose are implemented as linear operators. They are called prolongation and restriction operators.

from ngsolve import *
from ngsolve.webgui import Draw

Same as before, but now we assemble the bilinear-form already before mesh refinement, and invert the (small) coarse grid matrix by a direct solver:

mesh = Mesh(unit_square.GenerateMesh(maxh=0.3))
fesdom = L2(mesh, order=0, autoupdate=True)
fes = H1(mesh, order=1, dirichlet="bottom", autoupdate=True)
gfdom = GridFunction(fesdom, multidim=mesh.nv, nested=True, autoupdate=True)
for el in mesh.Elements(VOL):
    for v in el.vertices:
        gfdom.vecs[v.nr][el.nr] = 1
 
u,v = fes.TnT()
a = BilinearForm(grad(u)*grad(v)*dx).Assemble()
a0inv = a.mat.Inverse(fes.FreeDofs())

levels = 2
for l in range(levels):
    mesh.Refine()
copy mesh complete

the local inverses:

a.Assemble()

pre = None
for v in gfdom.vecs:
    mask = BitArray(fes.FreeDofs())
    for el in fes.Elements(VOL):
        if v[el.nr] == 0:
            for d in el.dofs:
                mask[d] = False

    invi = a.mat.Inverse(freedofs=mask, inverse="sparsecholesky")
    pre = pre+invi if pre else invi     

We have the prolongation as a function call, prolongating a vector in-place. Prolongate(l) prolongates a vector from level l-1 to level l. The transpose of the prolongation matrix is the restriction matrix, again implemented as an in-place operation:

class ProlongationOp(BaseMatrix):
    def __init__ (self, fes, levels, nc):
        super(ProlongationOp, self).__init__()
        self.fes = fes
        self.levels = levels
        self.nc = nc
        
    def Mult (self, x, y):
        y[IntRange(0, self.nc)] = x
        for l in range(self.levels):
            fes.Prolongation().Prolongate(l+1, y)

    def MultTrans (self, y, x):
        hy = y.CreateVector()
        hy.data = y
        for l in range(self.levels, 0, -1):
            fes.Prolongation().Restrict(l, hy)
        x.data = hy[IntRange(0, self.nc)]

    def Height (self):
        return self.fes.ndof
    def Width (self):
        return self.fes.ndof
P = ProlongationOp (fes, levels, a0inv.height)

C2l = P @ a0inv @ P.T + pre

lam = list(EigenValues_Preconditioner(a.mat, C2l))
print ("lammin, lammax=", lam[0], lam[-1])
print ("kappa=", lam[-1]/lam[0])
lammin, lammax= 1.0427258429588369 3.692782966558738
kappa= 3.5414706478167877

81.4. Analysis of the 2-level method:#

We apply now the ASM - theory to the sub-space splitting

Vh=VH+Vi

Theorem: The condition number of the 2-level method is bounded, i.e.

σ{C2L1A}[c1,c2]

Proof: By the ASM Lemma we have

uTC2Lu=infu=uH+uiuHVH,uiViuHA2+iuiA2.

Again, only a fixed number of sub-spaces overlap, which immediately proves the constant c2.

We have to come up with an explicit decomposition u=uH+ui. First we fix the coarse grid function uH by Clément interpolation onto the coarse-grid:

uH:=πHu

By semi-norm continuity of πH there holds

uHL2uL2,

and for the rest uf=uuH there holds H1-stability as well as good approximation in L2-norm:

ufL22+H1ufL22uL22

We proceed as above to decompose uf=ui=πh(ψiuf), which satisfy the estimates from above

iui2H2ufL22+ufL22

The inverse factor H2 we pay due to the partition of unity, we get back from the approximation error estimate of the coarse grid interpolant.

81.5. Comparison to DD with minimal overlap#

In comparison to the small overlap in the previous section, we have now an overlap of a fixed width, independent of the mesh-size. It is more expensive, but the condition number does not deteriorate for smaller mesh-size. The second difference is that we have now a coarse space containing the continuous, piecewise linear finite element functions. Thanks to this coarse space, we have a good approximation property of coarse grid functions.