FETI-DP

100. FETI-DP#

There is a problem with the FETI method if the bilinear-form is just \(A(u,v) = \int \nabla u \nabla v\), since then sub-domains without Dirichlet boundary conditions lead to singular \(A\)-blocks (called floating sub-domains)

The FETI - DP (where DP stands for dual/primal) is not to break all continuity, but keep some degrees of freedom continuous. E.g. keep the functions connected at the vertices. Then, the \(A\)-matrix is regular, and it is still cheap to invert.

We implement the vertex by introducing additional primal variables, and gluing the sub-domain fields to the vertex variables by a penalty method.

from ngsolve import *
from ngsolve.webgui import Draw

from netgen.occ import *
shape = None
mx, my = 3,3
for i in range(mx): 
    for j in range(my):
        rect = MoveTo(i/mx, j/mx).Rectangle(1/mx, 1/my).Face()
        rect.faces.name='mat'+str(i)+str(j)
        shape = Glue([shape,rect]) if shape else rect

shape.edges[Y<1e-4].name = "bot"
mesh = Mesh(OCCGeometry(shape, dim=2).GenerateMesh(maxh=0.04))


print (mesh.GetMaterials())
print (mesh.GetBoundaries())
print (mesh.GetBBoundaries())
Draw (mesh);
('mat00', 'mat01', 'mat02', 'mat10', 'mat11', 'mat12', 'mat20', 'mat21', 'mat22')
('bot', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'bot', 'default', 'default', 'default', 'default', 'default', 'default', 'bot', 'default', 'default', 'default', 'default', 'default', 'default')
('default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default', 'default')

sub-domain spaces, plus a Vertex-space with degrees of freedom only in the sub-domain vertices. (unfortunately these freedoms are not activated per default and have to be set manually, will be fixed).

fes = None
for dom in mesh.Materials('.*').Split():
    fesi = Compress(H1(mesh, definedon=dom, dirichlet="bot"))
    fes = fes * fesi if fes else fesi

fesVertex = H1(mesh, definedon=mesh.BBoundaries('.*'))
fes = fes * fesVertex
print ("ndof =", fes.ndof)

freedofs = fes.FreeDofs()
for el in fes.Elements(BBND):
    freedofs.Set(el.dofs[-1])

u, v = fes.TnT()

domtrial = {} 
domtest = {}
for nr,dom in enumerate (mesh.Materials('.*').Split()):
    domtrial[dom] = u[nr]
    domtest[dom] = v[nr]
ndof = 1604
feslam = None
for inter in mesh.Boundaries('.*').Split():
    doms = inter.Neighbours(VOL).Split()
    if len(doms) == 2:
        feslami = Compress(H1(mesh, definedon=inter))
        feslam = feslam * feslami if feslam else feslami 
    
print ("ndof-lam:", feslam.ndof)

lam, mu = feslam.TnT()

intertrial = {} 
intertest = {}
nr = 0
for inter in mesh.Boundaries('.*').Split():
    doms = inter.Neighbours(VOL).Split()
    if len(doms) == 2:
        intertrial[inter] = lam[nr]
        intertest[inter] = mu[nr]
        nr = nr+1
ndof-lam: 108
a = BilinearForm(fes)
f = LinearForm(fes)
b = BilinearForm(trialspace=fes, testspace=feslam)

uvert,vvert = u[-1], v[-1]
import ngsolve.comp
dVert = ngsolve.comp.DifferentialSymbol(BBND)

for dom in mesh.Materials('.*').Split():
    print ("vertices of dom: ", dom.Boundaries().Boundaries().Mask())
    ui, vi = domtrial[dom], domtest[dom]
    a += grad(ui)*grad(vi)*dx 
    a += 1e6*(ui-uvert)*(vi-vvert)*dVert(dom.Boundaries().Boundaries())
    f += y*x*vi*dx
    

for inter in mesh.Boundaries('.*').Split():
    doms = inter.Neighbours(VOL).Split()
    if len(doms) == 2:
        dom1,dom2 = doms
        b += (domtrial[dom1]-domtrial[dom2]) * intertest[inter] * ds(inter)
        
a.Assemble()
b.Assemble()
f.Assemble()
vertices of dom:  0: 1111000000000000
vertices of dom:  0: 0011110000000000
vertices of dom:  0: 0000111100000000
vertices of dom:  0: 0110000011000000
vertices of dom:  0: 0010100001100000
vertices of dom:  0: 0000101000110000
vertices of dom:  0: 0000000011001100
vertices of dom:  0: 0000000001100110
vertices of dom:  0: 0000000000110011
<ngsolve.comp.LinearForm at 0x110e16670>
gfu = GridFunction(fes)
ainv = a.mat.Inverse(inverse="sparsecholesky", freedofs=fes.FreeDofs())
S = b.mat @ ainv @ b.mat.T
g = (b.mat @ ainv * f.vec).Evaluate()

from ngsolve.krylovspace import CGSolver
invS = CGSolver(S, pre=IdentityMatrix(feslam.ndof), printrates="\r", maxiter=500)

lam = g.CreateVector()
lam.data = invS * g
gfu.vec.data = ainv * (f.vec - b.mat.T * lam)
CG iteration 1, residual = 0.02465296027570176     
CG iteration 2, residual = 0.0010045080936531537     
CG iteration 3, residual = 0.0007480745192100791     
CG iteration 4, residual = 0.0006647371091368124     
CG iteration 5, residual = 0.0003811858600465746     
CG iteration 6, residual = 0.000169241279661563     
CG iteration 7, residual = 0.00017862216656462402     
CG iteration 8, residual = 0.0001139934738266756     
CG iteration 9, residual = 0.0001554495625644795     
CG iteration 10, residual = 0.000113768687735942     
CG iteration 11, residual = 2.9074188055829104e-05     
CG iteration 12, residual = 1.9459877943866364e-05     
CG iteration 13, residual = 2.6840486465394826e-05     
CG iteration 14, residual = 1.9755711867121734e-05     
CG iteration 15, residual = 1.4374652208058638e-05     
CG iteration 16, residual = 1.64519534848992e-05     
CG iteration 17, residual = 8.636931867963392e-06     
CG iteration 18, residual = 5.905016378463157e-06     
CG iteration 19, residual = 2.905220752696197e-06     
CG iteration 20, residual = 9.12863329296047e-07     
CG iteration 21, residual = 7.56247992551079e-07     
CG iteration 22, residual = 1.0041701037189113e-06     
CG iteration 23, residual = 3.1737634627058275e-07     
CG iteration 24, residual = 1.695553777750457e-07     
CG iteration 25, residual = 2.508863997641801e-07     
CG iteration 26, residual = 1.5045220307617923e-07     
CG iteration 27, residual = 7.020921520573842e-08     
CG iteration 28, residual = 4.785356420685145e-08     
CG iteration 29, residual = 3.866605392033105e-08     
CG iteration 30, residual = 2.1463017279576963e-08     
CG iteration 31, residual = 3.758459253704872e-08     
CG iteration 32, residual = 2.3586833155231812e-08     
CG iteration 33, residual = 2.5490104909186764e-08     
CG iteration 34, residual = 1.5314789582441124e-08     
CG iteration 35, residual = 5.548503269620179e-09     
CG iteration 36, residual = 3.3304662762145377e-09     
CG iteration 37, residual = 1.5999435118936547e-09     
CG iteration 38, residual = 1.9098074578346433e-09     
CG iteration 39, residual = 1.3599443467801386e-09     
CG iteration 40, residual = 8.600146982364828e-10     
CG iteration 41, residual = 9.38735155262725e-10     
CG iteration 42, residual = 5.28104194714278e-10     
CG iteration 43, residual = 3.2897454008927705e-10     
CG iteration 44, residual = 2.404058525544706e-10     
CG iteration 45, residual = 3.9906250186677225e-10     
CG iteration 46, residual = 5.337893713070697e-10     
CG iteration 47, residual = 4.0301728719558134e-10     
CG iteration 48, residual = 5.286622867929107e-10     
CG iteration 49, residual = 8.690222252824563e-10     
CG iteration 50, residual = 3.384417646519425e-09     
CG iteration 51, residual = 6.272912216339554e-09     
CG iteration 52, residual = 5.108420054998112e-09     
CG iteration 53, residual = 3.997745644108606e-09     
CG iteration 54, residual = 1.0801027340152564e-08     
CG iteration 55, residual = 8.430171598176871e-09     
CG iteration 56, residual = 8.122107471371668e-08     
CG iteration 57, residual = 5.586682956909551e-08     
CG iteration 58, residual = 6.565250197410478e-08     
CG iteration 59, residual = 9.136657458440983e-08     
CG iteration 60, residual = 8.249756181280925e-08     
CG iteration 61, residual = 1.866342862798106e-07     
CG iteration 62, residual = 1.1110095651762984e-07     
CG iteration 63, residual = 1.9794050710210138e-07     
CG iteration 64, residual = 1.54363894703238e-07     
CG iteration 65, residual = 1.2704836995365155e-07     
CG iteration 66, residual = 7.223397101208971e-08     
CG iteration 67, residual = 8.228098001464905e-08     
CG iteration 68, residual = 5.1548725281844306e-08     
CG iteration 69, residual = 2.686092528287761e-08     
CG iteration 70, residual = 8.367966401769728e-09     
CG iteration 71, residual = 7.797141147241187e-09     
CG iteration 72, residual = 5.7402344909959206e-09     
CG iteration 73, residual = 2.442091465549524e-09     
CG iteration 74, residual = 3.3710588214359143e-09     
CG iteration 75, residual = 1.4901864193034273e-09     
CG iteration 76, residual = 8.659191086951956e-10     
CG iteration 77, residual = 2.636229849355725e-10     
CG iteration 78, residual = 3.156099034312336e-10     
CG iteration 79, residual = 4.5140382881547446e-11     
CG iteration 80, residual = 1.5419782071106046e-10     
CG iteration 81, residual = 2.478080957647242e-11     
CG iteration 82, residual = 4.5891398649927797e-11     
CG iteration 83, residual = 1.59405336959108e-11     
CG iteration 84, residual = 2.945019688894114e-11     
CG iteration 85, residual = 9.804111119202031e-12     
CG iteration 86, residual = 1.2230265570371406e-11     
CG iteration 87, residual = 1.3985505538779264e-11     
CG iteration 88, residual = 4.231800553405579e-11     
CG iteration 89, residual = 2.7978007889251674e-11     
CG iteration 90, residual = 2.3626794717372264e-11     
CG iteration 91, residual = 1.1626125219562889e-10     
CG iteration 92, residual = 1.1189162193674461e-10     
CG iteration 93, residual = 5.946038351683978e-10     
CG iteration 94, residual = 9.33747991988654e-10     
CG iteration 95, residual = 1.8042842653812196e-09     
CG iteration 96, residual = 7.224245137858673e-10     
CG iteration 97, residual = 1.531974595959108e-09     
CG iteration 98, residual = 1.940887650616227e-09     
CG iteration 99, residual = 3.0438549992059032e-09     
CG iteration 100, residual = 2.231628307327755e-09     
CG iteration 101, residual = 3.313430476860122e-09     
CG iteration 102, residual = 3.3449818788895694e-09     
CG iteration 103, residual = 5.2602847475597e-09     
CG iteration 104, residual = 4.225500217695409e-09     
CG iteration 105, residual = 1.4946201253765835e-09     
CG iteration 106, residual = 1.1446064790074082e-09     
CG iteration 107, residual = 2.0174751214445433e-09     
CG iteration 108, residual = 2.7100477039474815e-09     
CG iteration 109, residual = 1.3851314670110915e-09     
CG iteration 110, residual = 3.238166250910595e-10     
CG iteration 111, residual = 2.350994115985948e-10     
CG iteration 112, residual = 1.8510294360799867e-10     
CG iteration 113, residual = 7.15901102690421e-11     
CG iteration 114, residual = 3.011034189724812e-11     
CG iteration 115, residual = 4.211033417892459e-11     
CG iteration 116, residual = 2.559910850119869e-11     
CG iteration 117, residual = 1.0099475941491787e-11     
CG iteration 118, residual = 6.357213654094172e-12     
CG iteration 119, residual = 2.003411552855566e-11     
CG iteration 120, residual = 2.0051771795038548e-12     
CG iteration 121, residual = 1.6961471486119564e-12     
CG iteration 122, residual = 1.0796321768019933e-12     
CG iteration 123, residual = 1.8268017189966915e-12     
CG iteration 124, residual = 5.010904037099035e-13     
CG iteration 125, residual = 6.238452384758804e-14     
CG iteration 126, residual = 6.667823201958212e-14     
CG iteration 127, residual = 3.78593217572049e-14     
CG iteration 128, residual = 8.617426748434672e-14     
CG iteration 129, residual = 2.0971158281186845e-14     
CG converged in 129 iterations to residual 2.0971158281186845e-14
bnddofs = fes.GetDofs(mesh.Boundaries(".*"))
innerdofs = ~bnddofs & fes.FreeDofs()

massbnd = BilinearForm(fes)
for (ui,vi) in zip(u,v):
    massbnd += ui*vi*ds
massbnd.Assemble()
invmassbnd = massbnd.mat.Inverse(inverse="sparsecholesky", freedofs=bnddofs)

massinter = BilinearForm(feslam)
for inter in mesh.Boundaries('.*').Split():
    doms = inter.Neighbours(VOL).Split()
    if len(doms) == 2:
        massinter += intertrial[inter]*intertest[inter]*ds(inter)
massinter.Assemble()

emb = invmassbnd@b.mat.T@massinter.mat.Inverse(inverse="sparsecholesky")
used dof inconsistency
(silence this warning by setting BilinearForm(...check_unused=False) )
SchurDir = a.mat - a.mat@a.mat.Inverse(inverse="sparsecholesky", freedofs=innerdofs)@a.mat
pre = emb.T @ SchurDir @ emb

invS = CGSolver(S, pre=pre, printrates="\r", maxiter=500)

lam = g.CreateVector()
lam.data = invS * g
gfu.vec.data = ainv * (f.vec - b.mat.T * lam)
CG iteration 1, residual = 0.5621483955975527     
CG iteration 2, residual = 0.025107066428877348     
CG iteration 3, residual = 0.010098600736521033     
CG iteration 4, residual = 0.0011940088258720724     
CG iteration 5, residual = 0.0003140988977799483     
CG iteration 6, residual = 4.7432636997533445e-05     
CG iteration 7, residual = 1.2451276566230542e-05     
CG iteration 8, residual = 4.135008596422449e-06     
CG iteration 9, residual = 3.1323396903690127e-06     
CG iteration 10, residual = 1.0755162319435827e-06     
CG iteration 11, residual = 2.0443270809198917e-07     
CG iteration 12, residual = 3.572652921442311e-08     
CG iteration 13, residual = 2.659552219378877e-09     
CG iteration 14, residual = 2.2763945191068638e-10     
CG iteration 15, residual = 1.4916852502521548e-11     
CG iteration 16, residual = 1.2237338394942944e-11     
CG iteration 17, residual = 6.5108492359015e-13     
CG iteration 18, residual = 4.462559659526368e-14     
CG converged in 18 iterations to residual 4.462559659526368e-14
gftot = CF ( list(gfu.components) )
Draw(gftot, mesh);