Skip to content

Commit d434426

Browse files
authored
PCPatch: validate mesh overlap (#4684)
* PCPatch: validate mesh overlap
1 parent cbf7b00 commit d434426

File tree

7 files changed

+96
-17
lines changed

7 files changed

+96
-17
lines changed

demos/patch/hcurl_riesz_star.py.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ Contributed by `Robert Kirby <https://sites.baylor.edu/robert_kirby/>`_
55
and `Pablo Brubeck <https://www.maths.ox.ac.uk/people/pablo.brubeckmartinez/>`_.
66

77
Multigrid in H(div) and H(curl) also requires relaxation based on topological patches.
8-
Here, we demonstrate how to do this in the latter case. ::
8+
Here, we demonstrate how to do this in the latter case.
9+
10+
We start by importing firedrake and setting up a :func:`.MeshHierarchy` and the
11+
exact solution and forcing data. Crucially, the meshes must have an overlapping
12+
parallel domain decomposition that supports the vertex star patches. This is set
13+
via the `distribution_parameters` kwarg of the :func:`.Mesh` constructor. ::
914

1015
from firedrake import *
1116

12-
base = UnitCubeMesh(2, 2, 2)
17+
dparams = {"overlap_type": (DistributedMeshOverlapType.VERTEX, 1)}
18+
base = UnitCubeMesh(2, 2, 2, distribution_parameters=dparams)
1319
mh = MeshHierarchy(base, 3)
1420
mesh = mh[-1]
1521

demos/patch/hdiv_riesz_star.py.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ Contributed by `Robert Kirby <https://sites.baylor.edu/robert_kirby/>`_
55
and `Pablo Brubeck <https://www.maths.ox.ac.uk/people/pablo.brubeckmartinez/>`_.
66

77
Multigrid in H(div) and H(curl) also requires relaxation based on topological patches.
8-
Here, we demonstrate how to do this in the former case. ::
8+
Here, we demonstrate how to do this in the former case.
9+
10+
We start by importing firedrake and setting up a :func:`.MeshHierarchy` and the
11+
exact solution and forcing data. Crucially, the meshes must have an overlapping
12+
parallel domain decomposition that supports the vertex star patches. This is set
13+
via the `distribution_parameters` kwarg of the :func:`.Mesh` constructor. ::
914

1015
from firedrake import *
1116

12-
base = UnitCubeMesh(2, 2, 2)
17+
dparams = {"overlap_type": (DistributedMeshOverlapType.VERTEX, 1)}
18+
base = UnitCubeMesh(2, 2, 2, distribution_parameters=dparams)
1319
mh = MeshHierarchy(base, 3)
1420
mesh = mh[-1]
1521

demos/patch/poisson_mg_patches.py.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Using patch relaxation for multigrid
44
Contributed by `Robert Kirby <https://sites.baylor.edu/robert_kirby/>`_
55
and `Pablo Brubeck <https://www.maths.ox.ac.uk/people/pablo.brubeckmartinez/>`_.
66

7-
Simple relaxation like point Jacobi are not optimal or even suitable
7+
Simple multigrid relaxation methods like point Jacobi are not optimal or even suitable
88
smoothers for all applications. Firedrake supports additive Schwarz methods
99
based on local patch-based decompositions through two different paths.
1010

@@ -20,12 +20,15 @@ degree-independent iteration counts. Here, all the degrees of freedom in the ce
2020
For many problems, point Jacobi is even worse, and patches are required even to
2121
get a convergent method. We refer the reader to other demos.
2222

23-
We start by importing firedrake and setting up a mesh hierarchy and the
24-
exact solution and forcing data. ::
23+
We start by importing firedrake and setting up a :func:`.MeshHierarchy` and the
24+
exact solution and forcing data. Crucially, the meshes must have an overlapping
25+
parallel domain decomposition that supports the vertex star patches. This is set
26+
via the `distribution_parameters` kwarg of the :func:`.Mesh` constructor. ::
2527

2628
from firedrake import *
2729

28-
base = UnitSquareMesh(4, 4)
30+
dparams = {"overlap_type": (DistributedMeshOverlapType.VERTEX, 1)}
31+
base = UnitSquareMesh(4, 4, distribution_parameters=dparams)
2932
mh = MeshHierarchy(base, 1)
3033
mesh = mh[-1]
3134

demos/patch/stokes_vanka_patches.py.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@ of the patch but not pressures.
1616
In practice, we arrive at mesh-independent multigrid convergence using these relaxation.
1717
We can construct Vanka patches either through :class:`~.PatchPC`, in which the bilinear form
1818
is assembled on each vertex patch, or through :class:`~.ASMVankaPC`, in which the patch
19-
operators are extracted from the globally assembled stiffness matrix. ::
19+
operators are extracted from the globally assembled stiffness matrix.
20+
21+
We start by importing firedrake and setting up a :func:`.MeshHierarchy` and the
22+
exact solution and forcing data. Crucially, the meshes must have an overlapping
23+
parallel domain decomposition that supports the Vanka patches. This is set
24+
via the `distribution_parameters` kwarg of the :func:`.Mesh` constructor. ::
2025

2126
from firedrake import *
2227

23-
base = UnitSquareMesh(4, 4)
28+
dparams = {"overlap_type": (DistributedMeshOverlapType.VERTEX, 2)}
29+
base = UnitSquareMesh(4, 4, distribution_parameters=dparams)
2430
mh = MeshHierarchy(base, 3)
2531
mesh = mh[-1]
2632

docs/source/preconditioning.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,19 @@ multiplicatively within an MPI rank and additively between ranks.
6969
entities into lines or planes (useful for advection-dominated
7070
problems).
7171

72+
.. note::
73+
The additive Schwarz preconditioners listed here construct patches around
74+
mesh entities. Crucially, the mesh must have an overlapping parallel domain
75+
decomposition that supports the patches. This is set via the
76+
`distribution_parameters` kwarg of the :func:`.Mesh` constructor. For
77+
instance, vertex-star patches require ::
78+
79+
distribution_parameters["overlap_type"] = (DistributedMeshOverlapType.VERTEX, 1)
80+
81+
while Vanka patches require ::
82+
83+
distribution_parameters["overlap_type"] = (DistributedMeshOverlapType.VERTEX, 2)
84+
7285
Multigrid methods
7386
=================
7487

firedrake/preconditioners/asm.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from firedrake.preconditioners.base import PCBase
55
from firedrake.petsc import PETSc
66
from firedrake.dmhooks import get_function_space
7+
from firedrake.mesh import DistributedMeshOverlapType
78
from firedrake.logging import warning
89
from tinyasm import _tinyasm as tinyasm
910
from mpi4py import MPI
@@ -160,6 +161,8 @@ def get_patches(self, V):
160161
opts = PETSc.Options(self.prefix)
161162
depth = opts.getInt("construct_dim", default=0)
162163
ordering = opts.getString("mat_ordering_type", default="natural")
164+
validate_overlap(mesh, depth, "star")
165+
163166
# Accessing .indices causes the allocation of a global array,
164167
# so we need to cache these for efficiency
165168
V_local_ises_indices = tuple(iset.indices for iset in V.dof_dset.local_ises)
@@ -233,8 +236,11 @@ def get_patches(self, V):
233236
ises = []
234237
if depth != -1:
235238
(start, end) = mesh_dm.getDepthStratum(depth)
239+
patch_dim = depth
236240
else:
237241
(start, end) = mesh_dm.getHeightStratum(height)
242+
patch_dim = mesh_dm.getDimension() - height
243+
validate_overlap(mesh, patch_dim, "vanka")
238244

239245
for seed in range(start, end):
240246
# Only build patches over owned DoFs
@@ -453,6 +459,7 @@ def get_patches(self, V):
453459
else:
454460
continue
455461

462+
validate_overlap(mesh, base_depth, "star")
456463
start, end = mesh_dm.getDepthStratum(base_depth)
457464
for seed in range(start, end):
458465
# Only build patches over owned DoFs
@@ -508,3 +515,26 @@ def get_patches(self, V):
508515
iset = PETSc.IS().createGeneral(indices, comm=PETSc.COMM_SELF)
509516
ises.append(iset)
510517
return ises
518+
519+
520+
def validate_overlap(mesh, patch_dim, patch_type):
521+
if patch_type == "python":
522+
return
523+
patch_depth = {"pardecomp": 0, "star": 1, "vanka": 2}[patch_type]
524+
525+
tdim = mesh.topology_dm.getDimension()
526+
overlap_entity, overlap_depth = mesh._distribution_parameters["overlap_type"]
527+
overlap_dim = {
528+
DistributedMeshOverlapType.VERTEX: 0,
529+
DistributedMeshOverlapType.FACET: tdim-1,
530+
DistributedMeshOverlapType.NONE: tdim,
531+
}[overlap_entity]
532+
533+
if mesh.comm.size > 1:
534+
if overlap_dim > patch_dim:
535+
patch_entity = {0: "vertex", 1: "edge", 2: "face", tdim: "cell"}[patch_dim]
536+
warning(f"{overlap_entity} does not support {patch_entity}-patches. "
537+
"Did you forget to set overlap_type in your mesh's distribution_parameters?")
538+
if overlap_depth < patch_depth:
539+
warning(f"Mesh overlap depth of {overlap_depth} does not support {patch_type}-patches. "
540+
"Did you forget to set overlap_type in your mesh's distribution_parameters?")

firedrake/preconditioners/patch.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from firedrake.preconditioners.base import PCBase, SNESBase, PCSNESBase
2+
from firedrake.preconditioners.asm import validate_overlap
23
from firedrake.petsc import PETSc
34
from firedrake.cython.patchimpl import set_patch_residual, set_patch_jacobian
45
from firedrake.solving_utils import _SNESContext
@@ -772,15 +773,24 @@ def initialize(self, obj):
772773
if mesh.cell_set._extruded:
773774
raise NotImplementedError("Not implemented on extruded meshes")
774775

775-
if "overlap_type" not in mesh._distribution_parameters:
776-
if mesh.comm.size > 1:
777-
# Want to do
778-
# warnings.warn("You almost surely want to set an overlap_type in your mesh's distribution_parameters.")
779-
# but doesn't warn!
780-
PETSc.Sys.Print("Warning: you almost surely want to set an overlap_type in your mesh's distribution_parameters.")
776+
# Validate the mesh overlap
777+
prefix = (obj.getOptionsPrefix() or "") + "patch_"
778+
opts = PETSc.Options(prefix)
779+
petsc_prefix = self._petsc_prefix
780+
patch_type = opts.getString(f"{petsc_prefix}construct_type")
781+
patch_dim = opts.getInt(f"{petsc_prefix}construct_dim", -1)
782+
patch_codim = opts.getInt(f"{petsc_prefix}construct_codim", -1)
783+
if patch_dim != -1:
784+
assert patch_codim == -1, "Cannot set both dim and codim"
785+
elif patch_codim != -1:
786+
assert patch_dim == -1, "Cannot set both dim and codim"
787+
patch_dim = self.plex.getDimension() - patch_codim
788+
else:
789+
patch_dim = 0
790+
validate_overlap(mesh, patch_dim, patch_type)
781791

782792
patch = obj.__class__().create(comm=mesh.comm)
783-
patch.setOptionsPrefix((obj.getOptionsPrefix() or "") + "patch_")
793+
patch.setOptionsPrefix(prefix)
784794
self.configure_patch(patch, obj)
785795
patch.setType("patch")
786796

@@ -937,6 +947,8 @@ def view(self, pc, viewer=None):
937947

938948
class PatchPC(PCBase, PatchBase):
939949

950+
_petsc_prefix = "pc_patch_"
951+
940952
def configure_patch(self, patch, pc):
941953
(A, P) = pc.getOperators()
942954
patch.setOperators(A, P)
@@ -949,6 +961,9 @@ def applyTranspose(self, pc, x, y):
949961

950962

951963
class PatchSNES(SNESBase, PatchBase):
964+
965+
_petsc_prefix = "snes_patch_"
966+
952967
def configure_patch(self, patch, snes):
953968
patch.setTolerances(max_it=1)
954969
patch.setConvergenceTest("skip")

0 commit comments

Comments
 (0)