Source code for firedrake.preconditioners.bddc

from firedrake.preconditioners.base import PCBase
from firedrake.preconditioners.patch import bcdofs
from firedrake.preconditioners.facet_split import restrict, get_restriction_indices
from firedrake.petsc import PETSc
from firedrake.dmhooks import get_function_space, get_appctx
from firedrake.ufl_expr import TestFunction, TrialFunction
from firedrake.functionspace import FunctionSpace, VectorFunctionSpace, TensorFunctionSpace
from ufl import curl, div, HCurl, HDiv, inner, dx
from pyop2.utils import as_tuple
import numpy

__all__ = ("BDDCPC",)


[docs] class BDDCPC(PCBase): """PC for PETSc PCBDDC (Balancing Domain Decomposition by Constraints). This is a domain decomposition method using subdomains defined by the blocks in a Mat of type IS. Internally, this PC creates a PETSc PCBDDC object that can be controlled by the options: - ``'bddc_pc_bddc_neumann'`` to set sub-KSPs on subdomains excluding corners, - ``'bddc_pc_bddc_dirichlet'`` to set sub-KSPs on subdomain interiors, - ``'bddc_pc_bddc_coarse'`` to set the coarse solver KSP. This PC also inspects optional arguments supplied in the application context: - ``'discrete_gradient'`` for problems in H(curl), this sets the arguments (a Mat tabulating the gradient of the auxiliary H1 space) and keyword arguments supplied to ``PETSc.PC.setBDDCDiscreteGradient``. - ``'divergence_mat'`` for 3D problems in H(div), this sets the Mat with the assembled bilinear form testing the divergence against an L2 space. Notes ----- Currently the Mat type IS is only supported by FDMPC. """ _prefix = "bddc_"
[docs] def initialize(self, pc): # Get context from pc _, P = pc.getOperators() dm = pc.getDM() self.prefix = pc.getOptionsPrefix() + self._prefix V = get_function_space(dm) # Create new PC object as BDDC type bddcpc = PETSc.PC().create(comm=pc.comm) bddcpc.incrementTabLevel(1, parent=pc) bddcpc.setOptionsPrefix(self.prefix) bddcpc.setOperators(*pc.getOperators()) bddcpc.setType(PETSc.PC.Type.BDDC) opts = PETSc.Options(bddcpc.getOptionsPrefix()) if V.ufl_element().variant() == "fdm" and "pc_bddc_use_local_mat_graph" not in opts: # Disable computation of disconected components of subdomain interfaces opts["pc_bddc_use_local_mat_graph"] = False ctx = get_appctx(dm) bcs = tuple(ctx._problem.bcs) if V.extruded: boundary_nodes = numpy.unique(numpy.concatenate(list(map(V.boundary_nodes, ("on_boundary", "top", "bottom"))))) else: boundary_nodes = V.boundary_nodes("on_boundary") if len(bcs) == 0: dir_nodes = numpy.empty(0, dtype=boundary_nodes.dtype) else: dir_nodes = numpy.unique(numpy.concatenate([bcdofs(bc, ghost=False) for bc in bcs])) neu_nodes = numpy.setdiff1d(boundary_nodes, dir_nodes) V.dof_dset.lgmap.apply(dir_nodes, result=dir_nodes) dir_bndr = PETSc.IS().createGeneral(dir_nodes, comm=pc.comm) bddcpc.setBDDCDirichletBoundaries(dir_bndr) V.dof_dset.lgmap.apply(neu_nodes, result=neu_nodes) neu_bndr = PETSc.IS().createGeneral(neu_nodes, comm=pc.comm) bddcpc.setBDDCNeumannBoundaries(neu_bndr) appctx = self.get_appctx(pc) sobolev_space = V.ufl_element().sobolev_space tdim = V.mesh().topological_dimension() degree = max(as_tuple(V.ufl_element().degree())) if tdim >= 2 and V.finat_element.formdegree == tdim-1: B = appctx.get("divergence_mat", None) if B is None: from firedrake.assemble import assemble d = {HCurl: curl, HDiv: div}[sobolev_space] if V.shape == (): make_function_space = FunctionSpace elif len(V.shape) == 1: make_function_space = VectorFunctionSpace else: make_function_space = TensorFunctionSpace Q = make_function_space(V.mesh(), "DG", degree-1) b = inner(d(TrialFunction(V)), TestFunction(Q)) * dx(degree=2*(degree-1)) B = assemble(b, mat_type="matfree") bddcpc.setBDDCDivergenceMat(B.petscmat) elif sobolev_space == HCurl: gradient = appctx.get("discrete_gradient", None) if gradient is None: from firedrake.preconditioners.fdm import tabulate_exterior_derivative from firedrake.preconditioners.hiptmair import curl_to_grad Q = FunctionSpace(V.mesh(), curl_to_grad(V.ufl_element())) gradient = tabulate_exterior_derivative(Q, V) corners = get_vertex_dofs(Q) gradient.compose('_elements_corners', corners) grad_args = (gradient,) grad_kwargs = {'order': degree} else: try: grad_args, grad_kwargs = gradient except ValueError: grad_args = (gradient,) grad_kwargs = dict() bddcpc.setBDDCDiscreteGradient(*grad_args, **grad_kwargs) bddcpc.setFromOptions() self.pc = bddcpc
[docs] def view(self, pc, viewer=None): self.pc.view(viewer=viewer)
[docs] def update(self, pc): pass
[docs] def apply(self, pc, x, y): self.pc.apply(x, y)
[docs] def applyTranspose(self, pc, x, y): self.pc.applyTranspose(x, y)
def get_vertex_dofs(V): W = FunctionSpace(V.mesh(), restrict(V.ufl_element(), "vertex")) indices = get_restriction_indices(V, W) V.dof_dset.lgmap.apply(indices, result=indices) vertex_dofs = PETSc.IS().createGeneral(indices, comm=V.comm) return vertex_dofs