This page was generated from wta/fembem.ipynb.
FEM-BEM Coupling¶
The ngbem boundary element addon project initiated by Lucy Weggeler (see https://weggler.github.io/docu-ngsbem/intro.html) is now partly integrated into core NGSolve. Find a short and sweet introduction to the boundary element method there.
In this demo we simulate a plate capacitor on an unbounded domain.
[1]:
from ngsolve import *
from netgen.occ import *
from ngsolve.solvers import GMRes
from ngsolve.webgui import Draw
from ngsolve.bem import *
[2]:
largebox = Box ((-2,-2,-2), (2,2,2) )
eltop = Box ( (-1,-1,0.5), (1,1,1) )
elbot = Box ( (-1,-1,-1), (1,1,-0.5))
largebox.faces.name = "outer" # coupling boundary
eltop.faces.name = "topface" # Dirichlet boundary
elbot.faces.name = "botface" # Dirichlet boundary
eltop.edges.hpref = 1
elbot.edges.hpref = 1
shell = largebox-eltop-elbot # FEM domain
shell.solids.name = "air"
mesh = shell.GenerateMesh(maxh=0.8)
mesh.RefineHP(2)
ea = { "euler_angles" : (-67, 0, 110) }
Draw (mesh, clipping={"x":1, "y":0, "z":0, "dist" : 1.1}, **ea);
On the exterior domain \(\Omega^c\), the solution can be expressed by the representation formula:
where \(\gamma_0 u = u\) and \(\gamma_1 u = \frac{\partial u}{\partial n}\) are Dirichlet and Neumann traces. These traces are related by the Calderon projector
.
The \(V\), \(K\) are the single layer and double layer potential operators, and \(D\) is the hypersingular operator.
On the FEM domain we have the variational formulation
We use Calderon’s represenataion formula for the Neumann trace:
To get a closed system, we use also the first equation of the Calderon equations. To see the structure of the discretized system, the dofs are split into degrees of freedom inside \(\Omega\), and those on the boundary \(\Gamma\). The FEM matrix \(A\) is split accordingly. We see, the coupled system is symmetric, but indefinite:
Generate the finite element space for \(H^1(\Omega)\) and set the given Dirichlet boundary conditions on the surfaces of the plates:
[3]:
order = 4
fesH1 = H1(mesh, order=order, dirichlet="topface|botface")
print ("H1-ndof = ", fesH1.ndof)
H1-ndof = 90703
The finite element space \(\verb-fesH1-\) provides \(H^{\frac12}(\Gamma)\) conforming element to discretize the Dirichlet trace on the coupling boundary \(\Gamma\). However we still need \(H^{-\frac12}(\Gamma)\) conforming elements to discretize the Neumann trace of \(u\) on the coupling boundary. Here it is:
[4]:
fesL2 = SurfaceL2(mesh, order=order-1, dual_mapping=True, definedon=mesh.Boundaries("outer"))
print ("L2-ndof = ", fesL2.ndof)
L2-ndof = 4035
[5]:
fes = fesH1 * fesL2
u,dudn = fes.TrialFunction()
v,dvdn = fes.TestFunction()
a = BilinearForm(grad(u)*grad(v)*dx, check_unused=False).Assemble()
gfudir = GridFunction(fes)
gfudir.components[0].Set ( mesh.BoundaryCF( { "topface" : 1, "botface" : -1 }), BND)
f = LinearForm(fes).Assemble()
res = (f.vec - a.mat * gfudir.vec).Evaluate()
Generate the the single layer potential \(V\), double layer potential \(K\) and hypersingular operator \(D\):
[6]:
n = specialcf.normal(3)
with TaskManager():
V = LaplaceSL(dudn*ds("outer"))*dvdn*ds("outer")
K = LaplaceDL(u*ds("outer"))*dvdn*ds("outer")
D = LaplaceSL(Cross(grad(u).Trace(),n)*ds("outer"))*Cross(grad(v).Trace(),n)*ds("outer")
M = BilinearForm(u*dvdn*ds("outer"), check_unused=False).Assemble()
Setup the coupled system matrix and the right hand side:
[7]:
sym = a.mat+D.mat - (0.5*M.mat+K.mat).T - (0.5*M.mat+K.mat) - V.mat
rhs = res
bfpre = BilinearForm(grad(u)*grad(v)*dx+1e-10*u*v*dx + dudn*dvdn*ds("outer") ).Assemble()
pre = bfpre.mat.Inverse(freedofs=fes.FreeDofs(), inverse="sparsecholesky")
Compute the solution of the coupled system:
[8]:
with TaskManager():
sol_sym = GMRes(A=sym, b=rhs, pre=pre, tol=1e-6, maxsteps=200, printrates=True)
GMRES iteration 1, residual = 47.94329927058715
GMRES iteration 2, residual = 10.547738042883847
GMRES iteration 3, residual = 2.6122774443197745
GMRES iteration 4, residual = 1.9193598537429275
GMRES iteration 5, residual = 0.4151312135075399
GMRES iteration 6, residual = 0.39044487437532716
GMRES iteration 7, residual = 0.17719618613372207
GMRES iteration 8, residual = 0.13791749556363272
GMRES iteration 9, residual = 0.08865763341014755
GMRES iteration 10, residual = 0.047706297584150896
GMRES iteration 11, residual = 0.04662109067760234
GMRES iteration 12, residual = 0.044970971312911556
GMRES iteration 13, residual = 0.02080386902570217
GMRES iteration 14, residual = 0.013029445657638054
GMRES iteration 15, residual = 0.012428849092459162
GMRES iteration 16, residual = 0.007375363713899776
GMRES iteration 17, residual = 0.007328671491551012
GMRES iteration 18, residual = 0.006116844119538037
GMRES iteration 19, residual = 0.005574750443211928
GMRES iteration 20, residual = 0.0036699773556882038
GMRES iteration 21, residual = 0.0035919757620183714
GMRES iteration 22, residual = 0.0029943239014823242
GMRES iteration 23, residual = 0.002946929400832018
GMRES iteration 24, residual = 0.00197926792717994
GMRES iteration 25, residual = 0.0019408664885523486
GMRES iteration 26, residual = 0.0015575489596837638
GMRES iteration 27, residual = 0.0014246204451268786
GMRES iteration 28, residual = 0.0012204425120834106
GMRES iteration 29, residual = 0.0010847044561307403
GMRES iteration 30, residual = 0.0009837865748349686
GMRES iteration 31, residual = 0.0007490400519513562
GMRES iteration 32, residual = 0.0007488559795358786
GMRES iteration 33, residual = 0.0006134924989706602
GMRES iteration 34, residual = 0.000612410958497477
GMRES iteration 35, residual = 0.0004939642689658551
GMRES iteration 36, residual = 0.00047858686153427917
GMRES iteration 37, residual = 0.00037174187491896744
GMRES iteration 38, residual = 0.00037097048958194864
GMRES iteration 39, residual = 0.0003142110253330339
GMRES iteration 40, residual = 0.00031040251752161854
GMRES iteration 41, residual = 0.00021837357938609556
GMRES iteration 42, residual = 0.00021449524401340406
GMRES iteration 43, residual = 0.00019217128038114028
GMRES iteration 44, residual = 0.00017880247625871726
GMRES iteration 45, residual = 0.0001623609208677372
GMRES iteration 46, residual = 0.00013935854239635
GMRES iteration 47, residual = 0.0001387778589841212
GMRES iteration 48, residual = 0.00011238293933995522
GMRES iteration 49, residual = 0.00011226422935024775
GMRES iteration 50, residual = 8.69252185992582e-05
GMRES iteration 51, residual = 8.664928870808155e-05
GMRES iteration 52, residual = 7.291903342567916e-05
GMRES iteration 53, residual = 7.253473755164517e-05
GMRES iteration 54, residual = 5.7047639456677345e-05
GMRES iteration 55, residual = 4.796454440298464e-05
GMRES iteration 56, residual = 4.6725280152901864e-05
GMRES iteration 57, residual = 3.819925845916461e-05
GMRES iteration 58, residual = 3.807476465322355e-05
GMRES iteration 59, residual = 2.8152110707778168e-05
GMRES iteration 60, residual = 2.772481587205001e-05
GMRES iteration 61, residual = 2.4337832355438186e-05
GMRES iteration 62, residual = 2.4272918485293868e-05
GMRES iteration 63, residual = 2.036836915923449e-05
GMRES iteration 64, residual = 1.987315616737015e-05
GMRES iteration 65, residual = 1.603807165281029e-05
GMRES iteration 66, residual = 1.4477036736568047e-05
GMRES iteration 67, residual = 1.4163716892485606e-05
GMRES iteration 68, residual = 1.0409809092586067e-05
GMRES iteration 69, residual = 1.0328804525369803e-05
GMRES iteration 70, residual = 8.19011269437267e-06
GMRES iteration 71, residual = 7.817834137256319e-06
GMRES iteration 72, residual = 7.018532913149956e-06
GMRES iteration 73, residual = 5.89781983997394e-06
GMRES iteration 74, residual = 5.54547113237165e-06
GMRES iteration 75, residual = 4.159607886541806e-06
GMRES iteration 76, residual = 4.128810893438155e-06
GMRES iteration 77, residual = 3.6120159949964026e-06
GMRES iteration 78, residual = 3.351577833290155e-06
GMRES iteration 79, residual = 2.984677507723779e-06
GMRES iteration 80, residual = 2.5627033677135334e-06
GMRES iteration 81, residual = 2.4522366803363107e-06
GMRES iteration 82, residual = 2.1344262311815452e-06
GMRES iteration 83, residual = 2.077497442685054e-06
GMRES iteration 84, residual = 1.6116734857501248e-06
GMRES iteration 85, residual = 1.5868596633604372e-06
GMRES iteration 86, residual = 1.3487735710229494e-06
GMRES iteration 87, residual = 1.2296439783298127e-06
GMRES iteration 88, residual = 1.1847029845519198e-06
GMRES iteration 89, residual = 9.075403547162275e-07
[9]:
gfu = GridFunction(fes)
gfu.vec[:] = sol_sym + gfudir.vec
Draw(gfu.components[0], clipping={"x" : 1, "y":0, "z":0, "dist":0.0, "function" : True }, **ea, order=2);
The Neumann data:
[10]:
Draw (gfu.components[1], **ea);
References:
M. Costabel: Principles of boundary element methods
[ ]: