Source code for thetis.assembledschur
from firedrake import *
from petsc4py import PETSc
[docs]
class AssembledSchurPC(PCBase):
    """
    Preconditioner for the Schur complement
    The preconditioner matrix is assembled by explicitly matrix multiplying
    :math:`A10*Minv*A10`.
    Here:
    - :math:`A01`, :math:`A10` are the assembled sub-blocks of the saddle point
        system. The form of this system needs to be supplied in the
        :code:`appctx` argument to the solver as :code:`appctx['a']`.
    - :math:`Minv` is the inverse of the mass-matrix which is assembled as
        :code:`assemble(Tensor(v*u*dx).inv)`, i.e. the element-wise inverse,
        where :math:`v` and :math:`u` are the test and trial of the :math:`A00`
        block. This gives the exact inverse of the mass matrix for a DG
        discretisation.
    """
[docs]
    def initialize(self, pc):
        _, P = pc.getOperators()
        ctx = P.getPythonContext()
        a = ctx.appctx['a']
        options_prefix = pc.getOptionsPrefix()
        test, trial = a.arguments()
        W = test.function_space()
        V, Q = W.subspaces
        v = TestFunction(V)
        u = TrialFunction(V)
        mass = dot(v, u)*dx
        self.A00_inv = assemble(Tensor(mass).inv, mat_type='aij').M.handle
        self.A10_A00_inv = None
        self.schur = None
        self.schur_plus = None
        fs = dict(formmanipulation.split_form(a))
        self.a01 = fs[(0, 1)]
        self.a10 = fs[(1, 0)]
        self.a11 = fs[(1, 1)]
        self.A01 = None
        self.A10 = None
        self.A11 = None
        self.ksp = PETSc.KSP().create()
        self.ksp.setOptionsPrefix(options_prefix + 'schur_')
        self.ksp.setFromOptions()
        self.update(pc) 
[docs]
    def update(self, pc):
        self.A01 = assemble(self.a01, tensor=self.A01)
        self.A10 = assemble(self.a10, tensor=self.A10)
        self.A11 = assemble(self.a11, tensor=self.A11)
        A01 = self.A01.M.handle
        A10 = self.A10.M.handle
        A11 = self.A11.M.handle
        self.A10_A00_inv = A10.matMult(self.A00_inv, self.A10_A00_inv, 2.0)
        self.schur = self.A10_A00_inv.matMult(A01, self.schur, 2.0)
        if self.schur_plus is None:
            self.schur_plus = self.schur.duplicate(copy=True)
            self.schur_plus.aypx(-1.0, A11, PETSc.Mat.Structure.DIFFERENT_NONZERO_PATTERN)
        else:
            self.schur_plus = self.schur.copy(self.schur_plus, PETSc.Mat.Structure.DIFFERENT_NONZERO_PATTERN)
            self.schur_plus.aypx(-1.0, A11, PETSc.Mat.Structure.SAME_NONZERO_PATTERN)
        self.ksp.setOperators(self.schur_plus) 
[docs]
    def apply(self, pc, X, Y):
        self.ksp.solve(X, Y)
        r = self.ksp.getConvergedReason()
        if r < 0:
            raise RuntimeError("LinearSolver failed to converge after %d iterations with reason: %s", self.ksp.getIterationNumber(), solving_utils.KSPReasons[r]) 
[docs]
    def view(self, pc, viewer=None):
        super(AssembledSchurPC, self).view(pc, viewer)
        if viewer is None:
            return
        if viewer.getType() != PETSc.Viewer.Type.ASCII:
            return
        viewer.pushASCIITab()
        viewer.printfASCII("Solves assembled Schur system D - C M.inv C^T\n")
        self.ksp.view(viewer)
        viewer.popASCIITab() 
[docs]
    def applyTranspose(self, pc, X, Y):
        raise NotImplementedError("applyTranspose not implemented for AssembledSchurPC")