Implementation of approach 3. using
- modified demo
neumann-poisson
by Marie,
- MINRES (problem is symmetric, indefinite),
- BoomerAMG with Jacobi coarsener by Nico,
- preconditioning operator $\int ( \nabla u \cdot \nabla v + c\,d ) \,\mathrm{d}x$ inspired by
stokes-iterative
demo by Garth.
Unfortunately, PETSc's MINRES does not support KSP_NORM_UNPRECONDITIONED
or PC_RIGHT
so residuals/cost cannot be compared to other implementations. Comparison could be done using errornorm of some manufactured solution.
Deprecation warning: Nico's approach should be preferred when Krylov solver is supposed to be employed. There is no need introduce Lagrange multiplier when Krylov solver can handle null space of constants. Big advantage of Nico's approach is that it keeps the problem positive definite.
from dolfin import *
# Create mesh and define function space
mesh = UnitSquareMesh(64, 64)
V = FunctionSpace(mesh, "CG", 1)
R = FunctionSpace(mesh, "R", 0)
W = V * R
# Define variational problem
(u, c) = TrialFunction(W)
(v, d) = TestFunctions(W)
f = Expression("10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)")
g = Expression("-sin(5*x[0])")
a = (inner(grad(u), grad(v)) + c*v + u*d)*dx
ap = (inner(grad(u), grad(v)) + c*d)*dx
L = f*v*dx + g*v*ds
# Assemble
A = assemble(a)
Ap = assemble(ap)
b = assemble(L)
# Create Krylov solver
#PETScOptions.set("ksp_norm_type", "unpreconditioned")
#PETScOptions.set("ksp_pc_side", "right")
prec = PETScPreconditioner('hypre_amg')
prec.parameters['hypre']['BoomerAMG']['relax_type_coarse'] = 'jacobi'
solver = PETScKrylovSolver('minres', prec)
#solver.parameters['convergence_norm_type'] = 'true'
solver.parameters['absolute_tolerance'] = 0.0
solver.parameters['relative_tolerance'] = 1e-15
solver.parameters['maximum_iterations'] = 100
solver.parameters['monitor_convergence'] = True
# Solve system
w = Function(W)
solver.set_operators(A, Ap)
solver.solve(w.vector(), b)
(u, c) = w.split()
# Plot solution
plot(u, interactive=True)