This page was generated from unit-2.1.6-highorder-mg/highorder-multigrid.ipynb.
2.1.6. Multigrid for high order finite element spaces¶
Officially released with NGSolve-2504, most high order spaces provide high order polynomial preserving prolongation operators. Current restrictions:
require simplicial meshes
refinement by bisection
require uniform polynomial order
[1]:
from ngsolve import *
from ngsolve.webgui import Draw
from ngsolve.la import EigenValues_Preconditioner
Many FESpaces provide now a high-order accurate prolongation operator. It has to be enabled by the flag hoprolongation=True, maybe becoming default in future.
[2]:
mesh = Mesh(unit_square.GenerateMesh(maxh=0.2))
gfu = GridFunction(H1(mesh, order=10, hoprolongation=True))
gfu.Set (sin(50*x*y))
Draw (gfu, order=10);
[3]:
for l in range(2):
mesh.Refine()
Draw (gfu, order=5);
Multigrid preconditioners for high order spaces¶
If the high order prolongation is enabled, the multigrid preconditioner uses the high order discretization on the mesh hierarchy. If not, the coarse grid spaces use the lowest order spaces in the background.
[4]:
mesh = Mesh(unit_square.GenerateMesh(maxh=0.2))
fes = H1(mesh, order=5, hoprolongation=True, dirichlet=".*")
u,v = fes.TnT()
a = BilinearForm(grad(u)*grad(v)*dx).Assemble()
pre = preconditioners.MultiGrid(a, blocktype=["vertexpatch"])
# pre = preconditioners.MultiGrid(a, smoother="block", blocktype=["vertexedge","face"])
# pre = preconditioners.MultiGrid(a, smoother="block", blocktype=["vertpatch"])
for l in range(4):
mesh.Refine()
a.Assemble()
lam = EigenValues_Preconditioner(a.mat, pre)
print (mesh.levels, fes.ndof, lam[0], lam[-1])
2 2801 0.9737767047975869 0.9999650900439694
3 11001 0.973189334176511 0.9999569893695426
4 43601 0.9738508901099723 0.9999593243775629
5 173601 0.9740047338416953 0.9999581308397345
[5]:
f = LinearForm(x*v*dx).Assemble()
gfu = GridFunction(fes)
Solve(a * gfu == f, dirichlet=x*(1-x), lin_solver=solvers.CGSolver, pre=pre, printrates=True)
CG iteration 1, residual = 2.500665990770516
CG iteration 2, residual = 0.024383987764233173
CG iteration 3, residual = 0.00018799791819350977
CG iteration 4, residual = 1.148479427788205e-06
CG iteration 5, residual = 9.252209913652392e-09
CG iteration 6, residual = 6.018344822159841e-11
CG iteration 7, residual = 4.580210360037326e-13
[6]:
ea = { "euler_angles" : (-70, 0,-55) }
Draw (gfu, deformation=True, **ea);
High order with static condensation¶
For high order methods, static condensation may save a lot of computation. However, canonical prolongation for the skeleton variables only does not preserve high order polynomials. Here the HarmonicProlongation comes into play: It prolongates functions on the boundaries of the coarse grid elements, and then solves local Dirichlet problems for the dofs on the fine-grid skeleton inside the coarse grid elements.
The Dirichlet problem is solved for the problem-specific bilinear form, which has to be provided when enabling the HarmonicProlongation:
[7]:
mesh = Mesh(unit_square.GenerateMesh(maxh=0.2))
fes = H1(mesh, order=5, hoprolongation=True, dirichlet=".*")
u,v = fes.TnT()
a = BilinearForm(grad(u)*grad(v)*dx, condense=True).Assemble()
fes.SetHarmonicProlongation(a)
pre = preconditioners.MultiGrid(a, blocktype="vertexpatch")
for l in range(5):
mesh.Refine()
a.Assemble()
lam = EigenValues_Preconditioner(a.mat, pre)
print (mesh.levels, fes.ndof, lam[0], lam[-1])
2 2801 0.887345553976028 0.9998975910633989
3 11001 0.8957401274225061 0.999889401455988
4 43601 0.8945786078982361 0.9998863285007149
5 173601 0.8943861692924866 0.9998960435342537
6 692801 0.8953852278217913 0.9998858093863467
[8]:
f = LinearForm(x*v*dx).Assemble()
gfu = GridFunction(fes)
Solve(a * gfu == f, dirichlet=x*(1-x), lin_solver=solvers.CGSolver, pre=pre, printrates=True)
CG iteration 1, residual = 3.488608797786568
CG iteration 2, residual = 0.12345444219082138
CG iteration 3, residual = 0.003639088091883743
CG iteration 4, residual = 9.080367025015532e-05
CG iteration 5, residual = 2.7831282126522843e-06
CG iteration 6, residual = 7.696308282558474e-08
CG iteration 7, residual = 2.172768027790448e-09
CG iteration 8, residual = 6.173415107275916e-11
CG iteration 9, residual = 1.8887182888427297e-12
[9]:
Draw (gfu, deformation=True, **ea);
This example shows the result of a HarmonicProlongation:
[10]:
mesh = Mesh(unit_square.GenerateMesh(maxh=2))
fes = H1(mesh, order=8, hoprolongation=True)
u,v = fes.TnT()
a = BilinearForm(grad(u)*grad(v)*dx, condense=True).Assemble()
fes.SetHarmonicProlongation(a)
pre = preconditioners.MultiGrid(a, smoother="block", blocktype="vertexpatch")
[11]:
gfu = GridFunction(fes)
gfu.Set(sin(10*x))
Draw(gfu, order=5)
mesh.Refine()
Draw(gfu, order=5)
gfu.vec.data += a.harmonic_extension * gfu.vec
Draw (gfu, order=5);
Nearly incompressible elasticity and Stokes¶
The Scott-Vogelius element:
[12]:
mesh = Mesh(unit_square.GenerateMesh(maxh=0.2))
fes = VectorH1(mesh, order=4, hoprolongation=True, dirichlet=".*")
u,v = fes.TnT()
eq = InnerProduct(Grad(u),Grad(v)) * dx + 1e4 * div(u)*div(v)* dx
a = BilinearForm(eq).Assemble()
# a = BilinearForm( (Grad(u)|Grad(v)) * dx).Assemble()
pre = preconditioners.MultiGrid(a, blocktype=["vertexpatch"])
with TaskManager():
for l in range(5):
mesh.Refine()
a.Assemble()
lam = EigenValues_Preconditioner(a.mat, pre)
print (mesh.levels, fes.ndof, lam[0], lam[-1])
2 3618 0.3132568089248118 0.9999007841869558
3 14146 0.19750181032355046 0.9999100509630188
4 55938 0.1954916126781437 0.9999047978307452
5 222466 0.19549539298184632 0.9998999587479178
6 887298 0.19556232072452207 0.9998955800582809
A robust method with reduced integration¶
We define the bilinear form as
where \(P_{L_2}^Q\) is an element-wise \(L_2\)-projector into a lower order space.
[13]:
mesh = Mesh(unit_square.GenerateMesh(maxh=0.2))
fesp = L2(mesh, order=0)
fes = VectorH1(mesh, order=2, hoprolongation=True, dirichlet=".*")
u,v = fes.TnT()
eq = InnerProduct(Grad(u),Grad(v)) * dx + 1e4 * Interpolate(div(u),fesp)*div(v)* dx
a = BilinearForm(eq).Assemble()
# a = BilinearForm( (Grad(u)|Grad(v)) * dx).Assemble()
fes.SetHarmonicProlongation(a)
pre = preconditioners.MultiGrid(a, blocktype=["vertexpatch"])
for l in range(4):
mesh.Refine()
a.Assemble()
lam = EigenValues_Preconditioner(a.mat, pre)
print (mesh.levels, fes.ndof, lam[0], lam[-1])
2 946 0.5451163631462679 1.2567450360094843
3 3618 0.35880823306096116 1.4293139465315245
4 14146 0.30554010192371317 1.5320915176935432
5 55938 0.27985898251763053 1.6946834108508888
[14]:
f = LinearForm((x-0.5)*v[1]*dx).Assemble()
gfu = GridFunction(fes)
Solve (a*gfu==f, pre, solvers.CGSolver, printrates=True)
CG iteration 1, residual = 0.02055357090151336
CG iteration 2, residual = 0.0024505017135058762
CG iteration 3, residual = 0.0006507984597112339
CG iteration 4, residual = 0.0002003886621036486
CG iteration 5, residual = 7.105863986300889e-05
CG iteration 6, residual = 3.0287581801079647e-05
CG iteration 7, residual = 1.395742125126224e-05
CG iteration 8, residual = 6.026536683784559e-06
CG iteration 9, residual = 2.743921037373031e-06
CG iteration 10, residual = 1.2186305324699465e-06
CG iteration 11, residual = 4.964351575268852e-07
CG iteration 12, residual = 1.939838808357378e-07
CG iteration 13, residual = 7.686678702298934e-08
CG iteration 14, residual = 3.304683227224703e-08
CG iteration 15, residual = 1.435254930536677e-08
CG iteration 16, residual = 6.021165087788604e-09
CG iteration 17, residual = 2.5562823884504946e-09
CG iteration 18, residual = 1.0915843646621335e-09
CG iteration 19, residual = 4.5195816561544285e-10
CG iteration 20, residual = 1.7588626620261285e-10
CG iteration 21, residual = 6.957906924108333e-11
CG iteration 22, residual = 2.8502107548643943e-11
CG iteration 23, residual = 1.1345979806470996e-11
CG iteration 24, residual = 4.1691621193247045e-12
CG iteration 25, residual = 1.5090611126962246e-12
CG iteration 26, residual = 5.394782139812124e-13
CG iteration 27, residual = 1.9871234166850976e-13
CG iteration 28, residual = 7.249987931596829e-14
CG iteration 29, residual = 2.7920178282554352e-14
CG iteration 30, residual = 1.1077828822476012e-14
[15]:
Draw (gfu);
[16]:
from netgen.occ import *
shape = MoveTo(0,0).LineTo(1,0,"in").LineTo(1,1).LineTo(2,1).LineTo(3,0).LineTo(4,1).LineTo(5,1) \
.LineTo(5,2,"out").LineTo(4,2).LineTo(3,1).LineTo(2,2).LineTo(1,2).Rotate(180).Arc(1,90).Close().Face()
mesh = shape.GenerateMesh(dim=2, maxh=0.25).Curve(3)
Draw (mesh)
print (mesh.GetBoundaries())
fesp = L2(mesh, order=0)
fes = VectorH1(mesh, order=2, hoprolongation=True, dirichlet="in|default")
u,v = fes.TnT()
eq = InnerProduct(Grad(u),Grad(v)) * dx + 1e4 * Interpolate(div(u),fesp)*div(v)* dx
a = BilinearForm(eq).Assemble()
# a = BilinearForm( (Grad(u)|Grad(v)) * dx).Assemble()
fes.SetHarmonicProlongation(a)
pre = preconditioners.MultiGrid(a, blocktype=["vertexpatch"])
for l in range(4):
mesh.Refine()
a.Assemble()
print (mesh.levels, fes.ndof)
('in', 'default', 'default', 'default', 'default', 'default', 'out', 'default', 'default', 'default', 'default', 'default', 'default')
2 3482
3 13426
4 52706
5 208834
from Dissertation J. Schöberl, page 116.
[17]:
f = LinearForm(fes)
gfu = GridFunction(fes)
Solve (a*gfu==f, pre, solvers.CGSolver, dirichlet=CF((0,x*(1-x))) | mesh.Boundaries("in"), printrates=True)
CG iteration 1, residual = 156.02169771864808
CG iteration 2, residual = 4.696993518462748
CG iteration 3, residual = 0.44126979160214613
CG iteration 4, residual = 0.06908617512284203
CG iteration 5, residual = 0.0294334448000792
CG iteration 6, residual = 0.014480167237871143
CG iteration 7, residual = 0.008734149812435171
CG iteration 8, residual = 0.005471780853183333
CG iteration 9, residual = 0.002040640466471899
CG iteration 10, residual = 0.0008675227735582531
CG iteration 11, residual = 0.00030334263942783475
CG iteration 12, residual = 0.0001495584892527998
CG iteration 13, residual = 5.951615094315918e-05
CG iteration 14, residual = 2.711796490131768e-05
CG iteration 15, residual = 1.04423570214311e-05
CG iteration 16, residual = 5.095531021655106e-06
CG iteration 17, residual = 2.3425876326473943e-06
CG iteration 18, residual = 9.93517656239655e-07
CG iteration 19, residual = 3.816539545946586e-07
CG iteration 20, residual = 1.5921102516442616e-07
CG iteration 21, residual = 7.002462032065595e-08
CG iteration 22, residual = 3.024142833377104e-08
CG iteration 23, residual = 1.4336012132171723e-08
CG iteration 24, residual = 6.296140098408158e-09
CG iteration 25, residual = 2.50903955418891e-09
CG iteration 26, residual = 1.0887999973225502e-09
CG iteration 27, residual = 4.765297115249496e-10
CG iteration 28, residual = 2.1085634863324408e-10
CG iteration 29, residual = 1.1199062914583449e-10
[18]:
Draw (gfu);
[ ]: