Skip to content

Commit 132ad98

Browse files
committed
docs
1 parent 463c496 commit 132ad98

File tree

1 file changed

+96
-61
lines changed
  • firedrake/preconditioners

1 file changed

+96
-61
lines changed

firedrake/preconditioners/fdm.py

Lines changed: 96 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -380,21 +380,21 @@ def condense(self, A, J, bcs, fcp, pc_type="icc"):
380380
The matrix to statically condense.
381381
J : ufl.Form
382382
The bilinear form to statically condense.
383-
bcs :
384-
An iterable of boundary conditions.
385-
fcp :
386-
A dict with the form compiler parameters.
387-
pc_type :
383+
bcs : BCBase[]
384+
An iterable of boundary conditions to apply on ``A``.
385+
fcp : dict
386+
The form compiler parameters.
387+
pc_type : PETSc.PC.Type
388388
The preconditioner type for the interior solver.
389389
390390
Returns
391391
-------
392-
Smat :
393-
A :class:`PETSc.Mat` with the original blocks of A, except that
392+
Smat : PETSc.Mat
393+
A matrix with the original blocks of ``A``, except that
394394
the matrix-free Schur complement replaces the interface-interface block.
395-
Pmat :
396-
A `dict` mapping pairs of function spaces to the preconditioner blocks
397-
[[inv(A00), A01], [A10, inv(S)]].
395+
Pmat : dict
396+
A dict mapping pairs of function spaces to the preconditioner blocks
397+
``[[inv(A00), A01], [A10, inv(S)]]``.
398398
"""
399399
Smats = {}
400400
V = J.arguments()[0].function_space()
@@ -437,7 +437,7 @@ def condense(self, A, J, bcs, fcp, pc_type="icc"):
437437
*args_acc,
438438
x.dat(op2.READ, x.cell_node_map()),
439439
y.dat(op2.INC, y.cell_node_map()))
440-
ctx = ParloopMatrixContext(parloop, x, y, bcs=bcs)
440+
ctx = PythonMatrixContext(parloop, x, y, bcs=bcs)
441441
Smats[Vsub, Vsub] = PETSc.Mat().createPython(sizes, context=ctx, comm=comm)
442442
if Vsub == V0:
443443
Pmats[Vsub, Vsub] = Smats[Vsub, Vsub]
@@ -668,15 +668,22 @@ def _element_mass_matrix(self):
668668

669669
@PETSc.Log.EventDecorator("FDMSetValues")
670670
def set_values(self, A, Vrow, Vcol, addv, mat_type="aij"):
671-
"""
672-
Assemble the auxiliary operator in the FDM basis using sparse reference
673-
tensors and diagonal mass matrices.
674-
675-
:arg A: the :class:`PETSc.Mat` to assemble
676-
:arg Vrow: the :class:`.FunctionSpace` test space
677-
:arg Vcol: the :class:`.FunctionSpace` trial space
678-
:arg addv: a `PETSc.Mat.InsertMode`
679-
:arg mat_type: the `PETSc.Mat.Type` of the auxiliary operator
671+
"""Assemble the auxiliary operator in the FDM basis using sparse
672+
reference tensors and diagonal mass matrices.
673+
674+
Parameters
675+
----------
676+
A : PETSc.Mat
677+
The (initialized) matrix to assemble.
678+
Vrow : FunctionSpace
679+
The test space.
680+
Vcol : FunctionSpace
681+
The trial space.
682+
addv : PETSc.Mat.InsertMode
683+
Flag indicating if we want to insert or add matrix values.
684+
mat_type : PETSc.Mat.Type
685+
The matrix type of auxiliary operator. This only used when ``A`` is a preallocator
686+
to determine the nonzeros on the upper triangual part of an ``'sbaij'`` matrix.
680687
"""
681688
key = (Vrow.ufl_element(), Vcol.ufl_element())
682689
on_diag = Vrow == Vcol
@@ -795,7 +802,7 @@ def kernel(self, mat_type="aij", on_diag=False, addv=None):
795802

796803

797804
class TripleProductKernel(ElementKernel):
798-
"""Kernel builder that inserts a triple product of the form L * C * R,
805+
"""Kernel builder to assemble a triple product of the form L * C * R for each cell,
799806
where L, C, R are sparse matrices and the entries of C are updated on each cell."""
800807
code = dedent("""
801808
PetscErrorCode %(name)s(const Mat A, const Mat B,
@@ -810,8 +817,8 @@ class TripleProductKernel(ElementKernel):
810817
PetscFunctionReturn(PETSC_SUCCESS);
811818
}""")
812819

813-
def __init__(self, A, B, C, name=None):
814-
self.product = partial(A.matMatMult, B, C)
820+
def __init__(self, L, C, R, name=None):
821+
self.product = partial(L.matMatMult, C, R)
815822
super().__init__(self.product(), name=name)
816823

817824

@@ -1169,15 +1176,15 @@ def matmult_kernel_code(a, prefix="form", fcp=None, matshell=False):
11691176
11701177
Returns
11711178
-------
1172-
A 4-tuple of C code strings/string generators,
1173-
matmult_struct :
1179+
matmult_struct : str
11741180
The C code to compute the matrix-vector product.
1175-
matmult_call :
1176-
A lambda to call the matrix-vector product on the first argument,
1177-
writing the output on the second argument.
1178-
ctx_struct :
1181+
matmult_call : callable
1182+
- ``x``: the pointer name of the input vector (`str`).
1183+
- ``y``: the pointer name of the output vector (`str`).
1184+
A lambda to generate the C code calling the matrix-vector product.
1185+
ctx_struct : str
11791186
The signature of the kernel.
1180-
ctx_pack :
1187+
ctx_pack : str
11811188
Code to update the coefficient array pointers to be called before
11821189
applying the matshell.
11831190
"""
@@ -1193,14 +1200,18 @@ def matmult_kernel_code(a, prefix="form", fcp=None, matshell=False):
11931200
kernel = kernels[-1].kinfo.kernel
11941201
nargs = len(kernel.arguments) - len(a.arguments())
11951202
ncoef = nargs - len(extract_firedrake_constants(F))
1203+
1204+
matmult_struct = cache_generate_code(kernel, V._comm)
1205+
matmult_struct = matmult_struct.replace("void "+kernel.name, "static void "+kernel.name)
1206+
1207+
ctx_coeff = "".join(f"appctx[{i}], " for i in range(ncoef))
1208+
ctx_const = "".join(f", appctx[{i}]" for i in range(ncoef, nargs))
1209+
matmult_call = lambda x, y: f"{kernel.name}({y}, {ctx_coeff}{x}{ctx_const});"
1210+
11961211
ctx_struct = "".join(f"const PetscScalar *restrict c{i}, " for i in range(nargs))
11971212
ctx_pointers = ", ".join(f"c{i}" for i in range(nargs))
11981213
ctx_pack = f"const PetscScalar *appctx[{nargs}] = {{ {ctx_pointers} }};"
1199-
ctx_coefficients = "".join("appctx[%d], " % i for i in range(ncoef))
1200-
ctx_constants = "".join(", appctx[%d]" % i for i in range(ncoef, nargs))
1201-
matmult_call = lambda x, y: f"{kernel.name}({y}, {ctx_coefficients}{x}{ctx_constants});"
1202-
matmult_struct = cache_generate_code(kernel, V._comm)
1203-
matmult_struct = matmult_struct.replace("void "+kernel.name, "static void "+kernel.name)
1214+
12041215
cache[key] = (matmult_struct, matmult_call, ctx_struct, ctx_pack)
12051216

12061217
if matshell:
@@ -1262,24 +1273,20 @@ def __init__(self, kernel, form, name=None, prefix="interior_", fcp=None, pc_typ
12621273
A.setSizes(B.getSizes())
12631274
A.setUp()
12641275

1265-
use_cg = False
1266-
if use_cg:
1267-
ksp_type = PETSc.KSP.Type.CG
1268-
norm_type = PETSc.KSP.NormType.NATURAL
1269-
else:
1270-
ksp_type = PETSc.KSP.Type.MINRES
1271-
norm_type = PETSc.KSP.NormType.PRECONDITIONED
1272-
knoll = False
1273-
knoll = True
1276+
# Set up the local KSP for the cell interiors
12741277
ksp = PETSc.KSP().create(comm=comm)
12751278
ksp.setOptionsPrefix(prefix)
12761279
ksp.setOperators(A, B)
1280+
1281+
# Default solver options, these can be overriden via -interior_ksp_type, etc.
1282+
rtol = 1E-8
1283+
atol = 1E-14
1284+
ksp_type = PETSc.KSP.Type.MINRES
1285+
norm_type = PETSc.KSP.NormType.PRECONDITIONED
12771286
ksp.pc.setType(pc_type)
12781287
ksp.setType(ksp_type)
12791288
ksp.setNormType(norm_type)
1280-
ksp.setTolerances(rtol=1E-8, atol=0)
1281-
ksp.setInitialGuessNonzero(knoll)
1282-
ksp.setInitialGuessKnoll(knoll)
1289+
ksp.setTolerances(rtol=rtol, atol=atol)
12831290
ksp.setFromOptions()
12841291
ksp.setUp()
12851292
super().__init__(ksp, name=name)
@@ -1352,12 +1359,18 @@ def __init__(self, kernel, name=None):
13521359
fdofs = PETSc.IS().createBlock(V.value_size, restricted_dofs(V1.finat_element, V.finat_element), comm=comm)
13531360
size = idofs.size + fdofs.size
13541361
assert size == V.finat_element.space_dimension() * V.value_size
1362+
# Bilinear form on the space with interior and interface
13551363
a = form if Q == V else form(*(t.reconstruct(function_space=V) for t in args))
1356-
a00 = form if Q == V0 else form(*(t.reconstruct(function_space=V0) for t in args))
1364+
# Generate code to apply the action of A for computing the Schur complement action
13571365
A_struct, A_call, ctx_struct, ctx_pack = matmult_kernel_code(a, prefix="A", fcp=fcp)
1366+
1367+
# Bilinear form on the interior
1368+
a00 = form if Q == V0 else form(*(t.reconstruct(function_space=V0) for t in args))
1369+
# Generate code to apply A00 as a PETSc.Mat of type shell within the interior KSP
13581370
A00_struct, *_ = matmult_kernel_code(a00, prefix="A_interior", fcp=fcp, matshell=True)
13591371
A00_struct = A00_struct.replace("#include <stdint.h>", "")
13601372

1373+
# Replacement rules to use idofs, fdofs, A, and A00 on self.code
13611374
rules = dict(A_struct=A_struct, A_call=A_call("x", "y"), ctx_struct=ctx_struct, ctx_pack=ctx_pack,
13621375
A00_struct=A00_struct, size=size, isize=idofs.size, fsize=fdofs.size,
13631376
idofs=", ".join(map(str, idofs.indices)),
@@ -1367,10 +1380,23 @@ def __init__(self, kernel, name=None):
13671380
fdofs.destroy()
13681381

13691382

1370-
class ParloopMatrixContext:
1383+
class PythonMatrixContext:
1384+
"""Python matrix context that handles boundary conditions."""
13711385

1372-
def __init__(self, parloop, x, y, bcs=None):
1373-
self._mult_parloop = parloop
1386+
def __init__(self, mult_callable, x, y, bcs=None):
1387+
"""
1388+
Parameters
1389+
----------
1390+
mult_callable : callable
1391+
The callable performing the matrix-vector product.
1392+
x : Function
1393+
The tensor holding the input to the matrix-vector product.
1394+
y : Function
1395+
The tensor holding the output to the matrix-vector product.
1396+
bcs : BCBase[] or None
1397+
An iterable of boundary conditions to apply on ``x`` and ``y``.
1398+
"""
1399+
self._mult_callable = mult_callable
13741400
self._x = x
13751401
self._y = y
13761402
Vrow = y.function_space()
@@ -1408,11 +1434,11 @@ def _op(self, action, X, Y, W=None):
14081434

14091435
@PETSc.Log.EventDecorator()
14101436
def mult(self, mat, X, Y):
1411-
self._op(self._mult_parloop, X, Y)
1437+
self._op(self._mult_callable, X, Y)
14121438

14131439
@PETSc.Log.EventDecorator()
14141440
def multAdd(self, mat, X, Y, W):
1415-
self._op(self._mult_parloop, X, Y, W)
1441+
self._op(self._mult_callable, X, Y, W)
14161442

14171443

14181444
def is_restricted(finat_element):
@@ -1672,7 +1698,8 @@ def get_base_elements(e):
16721698

16731699

16741700
class SparseAssembler:
1675-
1701+
"""Class to generate and cache python wrappers to insert sparse element
1702+
matrices directly with PETSc C code."""
16761703
_cache = {}
16771704

16781705
@staticmethod
@@ -1807,14 +1834,22 @@ def assemble_reference_tensor(self, V):
18071834

18081835
@PETSc.Log.EventDecorator("FDMSetValues")
18091836
def set_values(self, A, Vrow, Vcol, addv, mat_type="aij"):
1810-
"""
1811-
Assemble the stiffness matrix in the FDM basis using Kronecker products of interval matrices
1837+
"""Assemble the stiffness matrix in the FDM basis using Kronecker
1838+
products of interval matrices.
18121839
1813-
:arg A: the :class:`PETSc.Mat` to assemble
1814-
:arg Vrow: the :class:`.FunctionSpace` test space
1815-
:arg Vcol: the :class:`.FunctionSpace` trial space
1816-
:arg addv: a `PETSc.Mat.InsertMode`
1817-
:arg mat_type: a `PETSc.Mat.Type`
1840+
Parameters
1841+
----------
1842+
A : PETSc.Mat
1843+
The (initialized) matrix to assemble.
1844+
Vrow : FunctionSpace
1845+
The test space.
1846+
Vcol : FunctionSpace
1847+
The trial space.
1848+
addv : PETSc.Mat.InsertMode
1849+
Flag indicating if we want to insert or add matrix values.
1850+
mat_type : PETSc.Mat.Type
1851+
The matrix type of auxiliary operator. This only used when ``A`` is a preallocator
1852+
to determine the nonzeros on the upper triangual part of an ``'sbaij'`` matrix.
18181853
"""
18191854
triu = A.getType() == "preallocator" and mat_type.endswith("sbaij")
18201855
set_submat = SparseAssembler.setSubMatCSR(PETSc.COMM_SELF, triu=triu)

0 commit comments

Comments
 (0)