19.1. Solver Layers#
Current situation:
We implement a time-stepper
inside the time-stepper we run some Newton-like method to solve a non-linear equation.
Newton’s method computes residual and tangent, and uses some direct solver to invert.
Layer approach:
We create a
Preconditoner
object for theBilinearForm
objectWe create a
LinearSolver
object from theBilinearForm.mat
and thePreconditioner
objectWe create a
NonlinearSolver
from theBilinearForm
and theLinearSolver
object (or LinearSolver-class+args)We create a
TimeStepper
from aBilinearForm
object andNonLinearSolver
class+args
19.1.1. Example:#
setting up the model:
from ngsolve import *
from ngsolve.webgui import Draw
from netgen.occ import *
shape = Rectangle(1,0.1).Face()
shape.edges.Max(X).name="right"
shape.edges.Min(X).name="left"
shape.edges.Max(Y).name="top"
shape.edges.Min(Y).name="bot"
# shape.vertices.Min(X+Y).maxh=0.01
# shape.vertices.Min(X-Y).maxh=0.01
shape.vertices.Min(X+Y).hpref=1
shape.vertices.Min(X-Y).hpref=1
mesh = Mesh(OCCGeometry(shape, dim=2).GenerateMesh(maxh=0.05))
mesh.RefineHP(2)
E, nu = 210, 0.2
mu = E / 2 / (1+nu)
lam = E * nu / ((1+nu)*(1-2*nu))
def C(u):
F = Id(2) + Grad(u)
return F.trans * F
def NeoHooke (C):
return 0.5*mu*(Trace(C-Id(2)) + 2*mu/lam*Det(C)**(-lam/2/mu)-1)
factor = Parameter(0.5)
force = CoefficientFunction( (0,factor) )
fes = H1(mesh, order=4, dirichlet="left", dim=mesh.dim)
u,v = fes.TnT()
a = BilinearForm(fes, symmetric=True)
a += Variation(NeoHooke(C(u)).Compile()*dx)
a += Variation((-InnerProduct(force,u)).Compile()*dx)
gfu = GridFunction(fes)
gfu.vec[:] = 0
19.1.1.1. Usual approach#
Call Newton’s method, with a couple of arguments:
solvers.Newton(a=a, u=gfu, inverse="sparsecholesky", printing=False)
scene = Draw (C(gfu)[0,0]-1, mesh, deformation=gfu, min=-0.1, max=0.1)
19.1.1.2. Solver layers approach:#
pre = preconditioners.MultiGrid(a)
linsolve = solvers.CGSolver(a.mat, pre, maxiter=20)
nlsolve = nonlinearsolvers.NewtonSolver(a=a, u=gfu, solver=linsolve)
gfu.vec[:] = 0
nlsolve.Solve(printing=False);
scene = Draw (C(gfu)[0,0]-1, mesh, deformation=gfu, min=-0.1, max=0.1);
Variation
keeps the expression for the energy. This is not yet supported for timestepping
. We can use symbolic differentiation to convert it to variational forms:
a = BilinearForm(NeoHooke(C(u)).Diff(u, v)*dx - force*v*dx)
gfu = GridFunction(fes)
a.Assemble() # we need that to construct a CGSolver object outside (TODO: solver_cls, solver_args)
pre = preconditioners.MultiGrid(a)
linsolve = solvers.CGSolver(a.mat, pre, maxiter=20)
nlsolve = nonlinearsolvers.NewtonSolver(a=a, u=gfu, solver=linsolve)
# nlsolve = nonlinearsolvers.NewtonSolver(a=a, u=gfu, lin_solver_cls=solvers.CGSolver, lin_solver_args={ "pre":pre })
gfu.vec[:] = 0
nlsolve.Solve(printing=False);
Draw (C(gfu)[0,0]-1, mesh, deformation=gfu, min=-0.1, max=0.1);
19.1.2. Timesteppers#
User provides the time-dependent equation as a variational form
Brand new: time-derivative
u.dt
force = CF( (0,-1))
eq = NeoHooke(C(u)).Diff(u,v) * dx - force * v * dx + u.dt.dt * v * dx
ts = timestepping.Newmark(equation=eq, dt=1e-1)
gfu = GridFunction(fes)
scene = Draw(gfu, deformation=True, center=(0,-0.3), radius=0.6)
def callback(t, gfu):
# print("t = ", t, "displacement = ", gfu(mesh(1,0.05)))
scene.Redraw()
ts.Integrate(gfu, start_time=0, end_time=10, callback=callback);
19.1.2.1. New features to work on exptression trees#
mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))
fes = H1(mesh, order=3, dirichlet=".*")
u,v = fes.TnT()
equ = u.dt*v*dx + 1e-3*u*v*dx + grad(u)*grad(v)*dx - v*dx
print (equ)
coef binary operation '*', real
coef trial-function diffop = Id order-dt=1, real
coef test-function diffop = Id, real
VOL
coef binary operation '*', real
coef scale 0.001, real
coef trial-function diffop = Id, real
coef test-function diffop = Id, real
VOL
coef innerproduct, fix size = 2, real
coef trial-function diffop = grad, real, dim=2
coef test-function diffop = grad, real, dim=2
VOL
-1*(coef test-function diffop = Id, real
) VOL
we can extract all proxies (trial- or test-functions) from the variational form:
allproxies = equ.GetProxies(trial=True)
print ("proxies:")
for proxy in allproxies: print(proxy, " python dtorder = ", proxy.dt_order)
proxies:
coef trial-function diffop = Id order-dt=1, real
python dtorder = 1
coef trial-function diffop = Id, real
python dtorder = 0
coef trial-function diffop = grad, real, dim=2
python dtorder = 0
proxiesdt0 = [ prox for prox in allproxies if prox.dt_order==0 ]
proxiesdt1 = [ prox for prox in allproxies if prox.dt_order==1 ]
print ("order 0 proxies:")
print (*proxiesdt0)
print ("order 1 proxies:")
print (*proxiesdt1)
order 0 proxies:
coef trial-function diffop = Id, real
coef trial-function diffop = grad, real, dim=2
order 1 proxies:
coef trial-function diffop = Id order-dt=1, real
The time-dependent equation is
An implicit Euler time-stepping method is to solve
where the unknown \(u\) is the value of the new time-step \(u_{n+1}\).
So we have to replace the
tau = Parameter(0.1)
gfuold = GridFunction(fes)
repl = {}
for prox in allproxies:
if prox.dt_order==1:
repl[prox] = 1/tau*(prox.anti_dt - prox.anti_dt.ReplaceFunction(gfuold))
for key,val in repl.items():
print ("replace:", key)
print ("by\n", val)
replace: coef trial-function diffop = Id order-dt=1, real
by
coef binary operation '*', real
coef binary operation '/', real
coef 1, real
coef N5ngfem28ParameterCoefficientFunctionIdEE, real
coef binary operation '-', real
coef trial-function diffop = Id, real
coef N6ngcomp31GridFunctionCoefficientFunctionE, real
ImplEuler_equ = equ.Replace(repl)
print (ImplEuler_equ)
coef binary operation '*', real
coef binary operation '*', real
coef binary operation '/', real
coef 1, real
coef N5ngfem28ParameterCoefficientFunctionIdEE, real
coef binary operation '-', real
coef trial-function diffop = Id, real
coef N6ngcomp31GridFunctionCoefficientFunctionE, real
coef test-function diffop = Id, real
VOL
coef binary operation '*', real
coef scale 0.001, real
coef trial-function diffop = Id, real
coef test-function diffop = Id, real
VOL
coef innerproduct, fix size = 2, real
coef trial-function diffop = grad, real, dim=2
coef test-function diffop = grad, real, dim=2
VOL
-1*(coef test-function diffop = Id, real
) VOL
bfIE = BilinearForm(ImplEuler_equ)
bfIE.Assemble()
pre = preconditioners.MultiGrid(bfIE)
linsolve = krylovspace.CGSolver(bfIE.mat, pre, maxiter=20)
gfu = GridFunction(fes)
nlsolve = nonlinearsolvers.NewtonSolver(a=bfIE, u=gfu, solver=linsolve)
scene = Draw(gfu, deformation=True, scale=10)
gfuold.vec[:] = 0
from time import sleep
for i in range(20):
nlsolve.Solve()
gfuold.vec[:] = gfu.vec
scene.Redraw()
sleep(0.2)