[petsc-users] Stokes-Brinkmann equation preconditioner

Salazar De Troya, Miguel salazardetro1 at llnl.gov
Mon Oct 7 15:09:43 CDT 2019


From: Matthew Knepley <knepley at gmail.com>
Date: Friday, October 4, 2019 at 3:19 AM
To: Lawrence Mitchell <wence at gmx.li>
Cc: "Salazar De Troya, Miguel" <salazardetro1 at llnl.gov>, "petsc-users at mcs.anl.gov" <petsc-users at mcs.anl.gov>
Subject: Re: [petsc-users] Stokes-Brinkmann equation preconditioner

On Fri, Oct 4, 2019 at 6:04 AM Lawrence Mitchell <wence at gmx.li<mailto:wence at gmx.li>> wrote:


> On 4 Oct 2019, at 10:46, Matthew Knepley via petsc-users <petsc-users at mcs.anl.gov<mailto:petsc-users at mcs.anl.gov>> wrote:
>
> On Thu, Oct 3, 2019 at 6:34 PM Salazar De Troya, Miguel via petsc-users <petsc-users at mcs.anl.gov<mailto:petsc-users at mcs.anl.gov>> wrote:
> I am trying to solve the Stokes equation with the Brinkman term to simulate a solid material. My intention is to implement the preconditioner in this paper:https://onlinelibrary.wiley.com/doi/epdf/10.1002/fld.426 (section 2.6)
>
>
> Link does not work for me.

Try https://onlinelibrary.wiley.com/doi/epdf/10.1002/fld.426 (mail clients are awful)

>
> where they solve for the velocity and substitute that expression in the pressure equation. They end up solving a system of the type B K^-1 B^T, i.e. the Schur complement of the problem. For this system of equations, they argue that the preconditioner in page 11 is perfect for a given constant Brinkman penalty term.
>
>
>
> Because I am solving for velocity and pressure without doing any substitution, I thought I could use a PC fieldsplit type Schur (full factorization)
>
>
> Yes, this will form the exact factorization and a matrix-free form of the Schur complement.
>
> and provide the preconditioner in the paper to solve the Schur complement.
>
>
> Yes, you can provide a user-defined PC for the Schur complement.
>
> My question is, should I provide this preconditioner through PCFieldSplitSetSchurPre or through fieldsplit_1_pc_type (probably through the Firedrake interface as inhttps://www.firedrakeproject.org/demos/stokes.py.html<http://www.firedrakeproject.org/demos/stokes.py.html>) ?
>
>
> The name PCFieldSplitSetSchurPre seems to be very misleading. You do not use it to provide a _preconditioner_. You use it to determine
> the _preconditioning matrix_ from which the actual preconditioner is built. The preconditioner itself is defined using -fieldpslit_1_pc_type.
> Since I do not know what the preconditioner looks like, I cannot say what preconditioner matrix you would want. Since Firedrake can construct
> any operator for you, you might not care about the matrix we pass to you.

The action of the PC is defined by:

M^{-1} q = \phi_q + \mu q

\mu is some number, and \phi_q solves the BVP

-div \alpha^{-1} grad \phi_q = q

grad \phi_q \dot n = 0
\int \phi_q = 0

So your PC for the fieldsplit_1 needs to:

1. Discretise and solve this BVP to compute \phi_q
2. Add \mu q.

I.e. the action of PCApply is:

y <- \mu x + Laplace^{-1} x

(Aside, surely a primal-dual error has been made in the analysis here)

You could, I think, do this by providing the discretisation of this laplacian and using an additive PCComposite (although I don't know what PC you would use to just scale the input, it's easy to write one though).

Richardson R(x, y) is

   y += mu (b - Ax)

so if b = 0 and A = I, you get that. It seems easier just to make a PCSHELL with VecAXPY I guess.

  Thanks,

      Matt

Cheers,

Lawrence


Why is there a need to use a PCComposite instead of applying “y <- \mu x + Laplace^{-1} x” directly in the same preconditioner? I have tried to do the latter without success, maybe that is why. I have used Firedrake and followed the example in https://www.firedrakeproject.org/_modules/firedrake/preconditioners/pcd.html . I hope it is ok to include it here given that it uses a lot of the petsc4py routines. The code is attached below. The implementation of the preconditioner for the Schur complement must be wrong somewhere because the fieldsplit_1_ solve takes many iterations to converge whenever a Gaussian bump or a jump function models the \alpha parameter. It behaves better for \alpha constant (an assumption in the paper for the preconditioner to be perfect). I am solving the pure Neumann problem by passing the nullspace to the KSP. I hope it is okay to ask here if there is something wrong in how I am applying “y <- \mu x + Laplace^{-1} x”

Thanks
Miguel

from firedrake import *
from firedrake.petsc import PETSc
from firedrake.preconditioners.base import PCBase

class BrinkmannPC(PCBase):
    r""" Preconditioner for the Stokes-Brinkmann problem
        Fill details
    """
    def initialize(self, pc):
        from firedrake import TrialFunction, TestFunction, dx, \
            assemble, inner, grad, split, Constant, parameters
        from firedrake.assemble import allocate_matrix, create_assembly_callable
        if pc.getType() != "python":
            raise ValueError("Expecting PC type python")
        prefix = pc.getOptionsPrefix() + "brink_"
         # we assume P has things stuffed inside of it
        _, P = pc.getOperators()
        context = P.getPythonContext()

        test, trial = context.a.arguments()
        if test.function_space() != trial.function_space():
            raise ValueError("Pressure space test and trial space differ")

        Q = test.function_space()

        p = TrialFunction(Q)
        q = TestFunction(Q)

        alpha = context.appctx.get("alpha", Constant(1.0))
        stiffness = inner(1.0 / alpha * grad(p), grad(q))*dx

        opts = PETSc.Options()

        # we're inverting Kp, so default them to assembled.
        # Mp only needs its action, so default it to mat-free.
        # These can of course be overridden.
        default = parameters["default_matrix_type"]
        Kp_mat_type = opts.getString(prefix+"Kp_mat_type", default)
        self.Mp_mat_type = opts.getString(prefix+"Mp_mat_type", "matfree")

        Kp = assemble(stiffness, form_compiler_parameters=context.fc_params,
                  mat_type=Kp_mat_type,
                  options_prefix=prefix + "Kp_")
        Kp_petsc = Kp.M.handle


        L =  q * dx
        self.rhs = assemble(L)

        from firedrake import VectorSpaceBasis, Function
        ctsp = Function(Q).interpolate(Constant(1.0))
        nullspace = VectorSpaceBasis(vecs=[ctsp])
        nullspace.orthonormalize()
        nullspace_petsc = nullspace.nullspace()
        Kp_petsc.setNearNullSpace(nullspace_petsc)
        self.nullspace = nullspace

        Kksp = PETSc.KSP().create(comm=pc.comm)
        Kksp.incrementTabLevel(1, parent=pc)
        Kksp.setOptionsPrefix(prefix + "Kp_")
        Kksp.setOperators(Kp_petsc)
        Kksp.setFromOptions()
        Kksp.setUp()
        self.Kksp = Kksp

        mu = context.appctx.get("mu", Constant(1.0))
        mass = mu*p*q*dx
        self.Mp = allocate_matrix(mass, form_compiler_parameters=context.fc_params,
                                  mat_type=self.Mp_mat_type,
                                  options_prefix=prefix + "Mp_")
        self._assemble_Mp = create_assembly_callable(mass, tensor=self.Mp,
                                                     form_compiler_parameters=context.fc_params,
                                                     mat_type=self.Mp_mat_type)
        self._assemble_Mp()
        Mpmat = self.Mp.petscmat
        self.workspace = [Mpmat.createVecLeft() for i in (0, 1)]


    def update(self, pc):
        pass

    def apply(self, pc, x, y):
        a, b = self.workspace
        with self.rhs.dat.vec_wo as rhs_dat:
            x.copy(rhs_dat)

        self.nullspace.orthogonalize(self.rhs)

        with self.rhs.dat.vec_wo as rhs_dat:
            self.Kksp.solve(rhs_dat, a)

        self.Mp.petscmat.mult(x, y)
        y.axpy(1.0, a)

    def applyTranspose(self, pc, x, y):
        a, b = self.workspace
        with self.rhs.dat.vec_wo as rhs_dat:
            x.copy(rhs_dat)

        self.nullspace.orthogonalize(self.rhs)

        with self.rhs.dat.vec_wo as rhs_dat:
            self.Kksp.solveTranspose(rhs_dat, a)

        self.Mp.petscmat.mult(x, y)
        y.axpy(1.0, a)

    def view(self, pc, viewer=None):
        super(BrinkmannPC, self).view(pc, viewer)
        viewer.printfASCII("KSP solver for K^-1:\n")
        self.Kksp.view(viewer)

N = 100

mesh = UnitSquareMesh(N, N)

V = VectorFunctionSpace(mesh, "CG", 2)
W = FunctionSpace(mesh, "CG", 1)
Z = V * W
print("# DOFS {}".format(Z.dim()))


w = Function(Z)
u, p = split(w)
v, q = TestFunctions(Z)

mu = Constant(1.0)

x, y = SpatialCoordinate(mesh)

from ufl import And
alpha = conditional(And(y > 0.4, And(x < 0.2, y < 0.8)), Constant(1e5), Constant(1e5))
alpha = Constant(1e3)
alpha = 1e2*exp((0.2 - (x - 0.5)*(x - 0.5) - (y - 0.5)*(y - 0.5)) / 0.05)
alpha_interp = Function(W)
alpha_interp.interpolate(alpha)
File("alpha.pvd").write(alpha_interp)
F = mu*inner(grad(u), grad(v))*dx - p*div(v)*dx - div(u)*q*dx + alpha*inner(u, v)*dx


bc_value = as_vector([0, x * (x - 1)])

bcs = [DirichletBC(Z.sub(0), bc_value, (4,)),
       DirichletBC(Z.sub(0), zero(mesh.geometric_dimension()), (1, 2)),
       DirichletBC(Z.sub(0), bc_value, (3,))]

def create_solver(solver_parameters, appctx=None):
    p = {}
    if solver_parameters is not None:
        p.update(solver_parameters)
    # Default to linear SNES
    p.setdefault("snes_type", "ksponly")
    p.setdefault("ksp_atol", 1e-6)
    problem = NonlinearVariationalProblem(F, w, bcs=bcs)
    solver = NonlinearVariationalSolver(problem,
                                        solver_parameters=p, appctx=appctx)
    return solver

fieldsplit_0_lu = {
    "ksp_type": "preonly",
    "ksp_max_it": 1,
    "pc_type": "lu",
    "pc_factor_mat_solver_type": "mumps",
}

fieldsplit_1_lu = fieldsplit_0_lu

fieldsplit_0_python = {
    "ksp_type": "preonly",
    "pc_type": "python",
    "pc_python_type": "firedrake.AssembledPC",
    "assembled_pc_type": "hypre",
}

fieldsplit_1_python = {
    "ksp_type" : "cg",
    "ksp_atol" : 1e-2,
    "pc_type": "python",
    "pc_python_type": "__main__.BrinkmannPC",
    "ksp_converged_reason" : None,
    "brink_Kp_ksp_type": "preonly",
    "brink_Kp_pc_type": "lu",
    "brink_Kp_pc_factor_mat_solver_type" : "mumps",
}

iterative = True
params = {
        "mat_type" : "matfree" if iterative else "aij",
        "ksp_monitor_true_residual": None,
        "ksp_converged_reason": None,
        "ksp_max_it" : 100,
        "ksp_atol" : 1e-6,
        "ksp_type" : "fgmres",
        "pc_type" : "fieldsplit",
        "pc_fieldsplit_type" : "schur",
        "pc_fieldsplit_schur_fact_type": "full",
        "pc_fieldsplit_schur_precondition": "selfp" if not iterative else "a11",
        "fieldsplit_0": fieldsplit_0_python if iterative else fieldsplit_0_lu,
        "fieldsplit_1" : fieldsplit_1_python if iterative else fieldsplit_1_lu,
}

w.assign(0)
appctx = {"alpha" : alpha, "mu" : mu}
solver = create_solver(params, appctx=appctx)
solver.solve()
u, p = w.split()
u.rename("velocity")
p.rename("pressure")
File("out.pvd").write(u, p)

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mcs.anl.gov/pipermail/petsc-users/attachments/20191007/5bb90c56/attachment-0001.html>


More information about the petsc-users mailing list