Skip to content

Commit fe7428e

Browse files
committed
add docs
1 parent fcf7704 commit fe7428e

2 files changed

Lines changed: 106 additions & 1 deletion

File tree

docs/source/interpolation.rst

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ This also works when interpolating into a space defined on the facets of the mes
109109

110110

111111
Semantics of symbolic interpolation
112-
--------------------------------------------
112+
-----------------------------------
113113

114114
Let :math:`U` and :math:`V` be finite element spaces with DoFs :math:`\{\psi^{*}_{i}\}` and :math:`\{\phi^{*}_{i}\}`
115115
and basis functions :math:`\{\psi_{i}\}` and :math:`\{\phi_{i}\}`, respectively.
@@ -430,6 +430,63 @@ the next operation using ``f``.
430430
For interaction with external point data, see the
431431
:ref:`corresponding manual section <external-point-data>`.
432432

433+
Interpolation between mixed function spaces
434+
-------------------------------------------
435+
436+
Assembly of interpolation operators between mixed function spaces is also supported.
437+
Each component of the mixed space may be on different meshes.
438+
For example, consider the following mixed finite element spaces:
439+
440+
.. math::
441+
442+
W &= V_1 \times V_2 \\
443+
U &= V_3 \times V_4
444+
445+
where each :math:`V_i` is a finite element space defined on possibly different meshes.
446+
We can assemble the interpolation matrix from :math:`U` to :math:`W` in Firedrake as follows:
447+
448+
.. literalinclude:: ../../tests/firedrake/regression/test_interpolation_manual.py
449+
:language: python3
450+
:dedent:
451+
:start-after: [test_mixed_space_interpolation 1]
452+
:end-before: [test_mixed_space_interpolation 2]
453+
454+
We specified ``mat_type="nest"`` here to obtain a PETSc MatNest matrix, but Firedrake also
455+
supports assembly of ``mat_type="aij"`` and ``mat_type="matfree"`` interpolation matrices
456+
between mixed function spaces. In this example ``I`` is a block diagonal matrix, with
457+
each block given by
458+
459+
.. math::
460+
461+
\begin{pmatrix}
462+
V_3 \rightarrow V_1 & 0 \\
463+
0 & V_4 \rightarrow V_2
464+
\end{pmatrix}
465+
466+
The off-diagonal blocks are zero since the dofs are applied component-wise. Firedrake's form
467+
compiler recognises this and avoids assembling the zero blocks.
468+
469+
We can assemble more general interpolation matrices between mixed function spaces by interpolating
470+
vector expressions with arguments. For example, by doing
471+
472+
.. literalinclude:: ../../tests/firedrake/regression/test_interpolation_manual.py
473+
:language: python3
474+
:dedent:
475+
:start-after: [test_mixed_space_interpolation 3]
476+
:end-before: [test_mixed_space_interpolation 4]
477+
478+
we can assemble the interpolation matrix with block structure
479+
480+
.. math::
481+
482+
\begin{pmatrix}
483+
V_3 \rightarrow V_1 & V_4 \rightarrow V_1 \\
484+
V_3 \rightarrow V_2 & V_4 \rightarrow V_2
485+
\end{pmatrix}
486+
487+
Here we obtain non-zero off-diagonal blocks by including both components of the trial function
488+
in each component of the expression.
489+
433490
Generating Functions with randomised values
434491
-------------------------------------------
435492

tests/firedrake/regression/test_interpolation_manual.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from firedrake import *
2+
from firedrake.formmanipulation import split_form
23
import pytest
34
import numpy as np
45

@@ -235,3 +236,50 @@ def correct_indent():
235236

236237
assert np.isclose(dest_eval05.evaluate(f_dest), 0.5) # x_src^2 + y_src^2 = 0.5
237238
assert np.isclose(dest_eval15.evaluate(f_dest), 3.0) # x_dest + y_dest = 3.0
239+
240+
241+
def test_mixed_space_interpolation():
242+
mesh = UnitSquareMesh(2, 2)
243+
V1 = FunctionSpace(mesh, "CG", 1)
244+
V2 = FunctionSpace(mesh, "CG", 2)
245+
V3 = FunctionSpace(mesh, "CG", 3)
246+
V4 = FunctionSpace(mesh, "CG", 4)
247+
W = V1 * V2
248+
U = V3 * V4
249+
250+
# [test_mixed_space_interpolation 1]
251+
interp = interpolate(TrialFunction(U), W)
252+
I = assemble(interp, mat_type="nest")
253+
# [test_mixed_space_interpolation 2]
254+
255+
# The block matrix structure is
256+
# | V3 -> V1 0 |
257+
# | 0 V4 -> V2 |
258+
for i in range(2):
259+
for j in range(2):
260+
sub_mat = I.petscmat.getNestSubMatrix(i, j)
261+
if i != j:
262+
assert not sub_mat
263+
continue
264+
else:
265+
res_block = assemble(interpolate(TrialFunction(U.sub(j)), W.sub(i)))
266+
assert np.allclose(sub_mat[:, :], res_block.petscmat[:, :])
267+
assert sub_mat.type == "seqaij"
268+
269+
# [test_mixed_space_interpolation 3]
270+
u0, u1 = TrialFunctions(U)
271+
expr = as_vector([u0 + u1, u0 + u1])
272+
interp = interpolate(expr, W)
273+
I2 = assemble(interp, mat_type="nest")
274+
# [test_mixed_space_interpolation 4]
275+
276+
# The block matrix structure is
277+
# | V3 -> V1 V4 -> V1 |
278+
# | V3 -> V2 V4 -> V2 |
279+
split_interp = dict(split_form(interp))
280+
for i in range(2):
281+
for j in range(2):
282+
interp_ij = split_interp[(i, j)]
283+
assert isinstance(interp_ij, Interpolate)
284+
res_block = assemble(interpolate(TrialFunction(U.sub(j)), W.sub(i), allow_missing_dofs=True))
285+
assert np.allclose(I2.petscmat.getNestSubMatrix(i, j)[:, :], res_block.petscmat[:, :])

0 commit comments

Comments
 (0)