Skip to content

Commit 35f2b3c

Browse files
authored
Merge pull request #3067 from firedrakeproject/ReubenHill/interpolation-mesh-to-mesh
Parallel Compatible Cross-Mesh Interpolation
2 parents c0999bd + 396cd76 commit 35f2b3c

12 files changed

+1577
-45
lines changed

docs/source/interpolation.rst

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,120 @@ in the dual space to `V` to assembled 1-forms in the dual space to
108108
`W`.
109109

110110

111+
Interpolation across meshes
112+
---------------------------
113+
114+
The interpolation API supports interpolation between meshes where the target
115+
function space has finite elements (as given in the list of
116+
:ref:`supported elements <supported_elements>`)
117+
118+
* **Lagrange/CG** (also known a Continuous Galerkin or P elements),
119+
* **Q** (i.e. Lagrange/CG on lines, quadrilaterals and hexahedra),
120+
* **Discontinuous Lagrange/DG** (also known as Discontinuous Galerkin or DP elements) and
121+
* **DQ** (i.e. Discontinuous Lagrange/DG on lines, quadrilaterals and hexahedra).
122+
123+
Vector, tensor and mixed function spaces can also be interpolated into from
124+
other meshes as long as they are constructed from these spaces.
125+
126+
.. note::
127+
128+
The list of supported elements above is only for *target* function spaces.
129+
Function spaces on the *source* mesh can be built from most of the supported
130+
elements.
131+
132+
There are few constraints on the meshes involved: the target mesh can have a
133+
different cell shape, topological dimension, or resolution to the source mesh.
134+
There are many use cases for this: For example, two solutions to the same
135+
problem calculated on meshes with different resolutions or cell shapes can be
136+
interpolated onto one another, or onto a third, finer mesh, and be directly
137+
compared.
138+
139+
140+
Interpolating onto sub-domain meshes
141+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
142+
143+
The target mesh for a cross-mesh interpolation need not cover the full domain
144+
of the source mesh. Volume, surface and line integrals can therefore be
145+
calculated by interpolating onto the mesh or
146+
:ref:`immersed manifold <immersed_manifolds>` which defines the volume,
147+
surface or line of interest in the domain. The integral itself is calculated
148+
by calling :py:func:`~.assemble` on an approriate form over the target mesh
149+
function space:
150+
151+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
152+
:language: python3
153+
:dedent:
154+
:lines: 11-18, 31-40
155+
156+
For more on forms, see :ref:`this section of the manual <more_complicated_forms>`.
157+
158+
159+
Interpolating onto other meshes
160+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
161+
162+
.. note::
163+
164+
Interpolation *from* :ref:`immersed manifolds <immersed_manifolds>` and
165+
:ref:`high-order meshes <changing_coordinate_fs>` is currently not supported.
166+
167+
If the target mesh extends outside the source mesh domain, then cross-mesh
168+
interpolation will raise a :py:class:`~.DofNotDefinedError`.
169+
170+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
171+
:language: python3
172+
:dedent:
173+
:lines: 46-58, 64-65
174+
175+
This can be overriden with the optional ``allow_missing_dofs`` keyword
176+
argument:
177+
178+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
179+
:language: python3
180+
:dedent:
181+
:lines: 72-73, 80
182+
183+
By default the missing degrees of freedom (DoFs, the global basis function
184+
coefficients which could not be set) are zero:
185+
186+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
187+
:language: python3
188+
:dedent:
189+
:lines: 85
190+
191+
We can optionally specify a value to use for our missing DoFs. Here
192+
we set them to be ``nan`` ('not a number') for easy identification:
193+
194+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
195+
:language: python3
196+
:dedent:
197+
:lines: 90-93
198+
199+
When using :py:class:`~.Interpolator`\s, the ``allow_missing_dofs`` keyword
200+
argument is set at construction:
201+
202+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
203+
:language: python3
204+
:dedent:
205+
:lines: 100
206+
207+
The ``default_missing_val`` keyword argument is then set whenever we call
208+
:py:meth:`~.Interpolator.interpolate`:
209+
210+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
211+
:language: python3
212+
:dedent:
213+
:lines: 103
214+
215+
If we supply an output :py:class:`~.Function` and don't set
216+
``default_missing_val`` then any missing DoFs are left as they were prior to
217+
interpolation:
218+
219+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
220+
:language: python3
221+
:dedent:
222+
:lines: 107-110, 113-115, 124-126
223+
224+
111225
Interpolation from external data
112226
--------------------------------
113227

docs/source/mesh-coordinates.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ This can also be used if `f` is a solution to a PDE.
3636
This will be fixed in a future Firedrake update.
3737

3838

39+
.. _changing_coordinate_fs:
40+
3941
Changing the coordinate function space
4042
--------------------------------------
4143

docs/source/point-evaluation.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,10 @@ duplication.
217217
Points outside the domain
218218
~~~~~~~~~~~~~~~~~~~~~~~~~
219219

220-
Be default points outside the domain by more than the :ref:`specified
221-
tolerance <tolerance>` will generate a ``ValueError``. This can be switched
222-
to a warning or switched off entirely:
220+
Be default points outside the domain by more than the :ref:`specified
221+
tolerance <tolerance>` will generate
222+
a :class:`~.VertexOnlyMeshMissingPointsError`. This can be switched to a
223+
warning or switched off entirely:
223224

224225
.. literalinclude:: ../../tests/vertexonly/test_vertex_only_manual.py
225226
:language: python3

docs/source/variational-problems.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ of standard shapes. 1-dimensional intervals may be constructed with
7777
example to build unit square meshes). See
7878
:mod:`~firedrake.utility_meshes` for full details.
7979

80+
.. _immersed_manifolds:
8081

8182
Immersed manifolds
8283
~~~~~~~~~~~~~~~~~~
@@ -260,6 +261,9 @@ the horizontal and vertical spaces when building a function space on
260261
an extruded mesh. We refer the reader to the :doc:`manual section on
261262
extrusion <extruded-meshes>` for details.
262263

264+
265+
.. _supported_elements:
266+
263267
Supported finite elements
264268
-------------------------
265269

@@ -632,6 +636,8 @@ we can write:
632636
t += dt
633637
c.assign(t)
634638
639+
.. _more_complicated_forms:
640+
635641
More complicated forms
636642
----------------------
637643

firedrake/function.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,14 +367,47 @@ def vector(self):
367367
return vector.Vector(self)
368368

369369
@PETSc.Log.EventDecorator()
370-
def interpolate(self, expression, subset=None, ad_block_tag=None):
370+
def interpolate(
371+
self,
372+
expression,
373+
subset=None,
374+
allow_missing_dofs=False,
375+
default_missing_val=None,
376+
ad_block_tag=None,
377+
):
371378
r"""Interpolate an expression onto this :class:`Function`.
372379
373380
:param expression: a UFL expression to interpolate
374-
:param ad_block_tag: string for tagging the resulting block on the Pyadjoint tape
381+
:kwarg subset: An optional :class:`pyop2.types.set.Subset` to apply the
382+
interpolation over. Cannot, at present, be used when interpolating
383+
across meshes unless the target mesh is a :func:`.VertexOnlyMesh`.
384+
:kwarg allow_missing_dofs: For interpolation across meshes: allow
385+
degrees of freedom (aka DoFs/nodes) in the target mesh that cannot be
386+
defined on the source mesh. For example, where nodes are point
387+
evaluations, points in the target mesh that are not in the source mesh.
388+
When ``False`` this raises a ``ValueError`` should this occur. When
389+
``True`` the corresponding values are set to zero or to the value
390+
``default_missing_val`` if given. Ignored if interpolating within the
391+
same mesh or onto a :func:`.VertexOnlyMesh` (the behaviour of a
392+
:func:`.VertexOnlyMesh` in this scenario is, at present, set when
393+
it is created).
394+
:kwarg default_missing_val: For interpolation across meshes: the optional
395+
value to assign to DoFs in the target mesh that are outside the source
396+
mesh. If this is not set then zero is used. Ignored if interpolating
397+
within the same mesh or onto a :func:`.VertexOnlyMesh`.
398+
:kwarg ad_block_tag: An optional string for tagging the resulting block on
399+
the Pyadjoint tape.
375400
:returns: this :class:`Function` object"""
376401
from firedrake import interpolation
377-
return interpolation.interpolate(expression, self, subset=subset, ad_block_tag=ad_block_tag)
402+
403+
return interpolation.interpolate(
404+
expression,
405+
self,
406+
subset=subset,
407+
allow_missing_dofs=allow_missing_dofs,
408+
default_missing_val=default_missing_val,
409+
ad_block_tag=ad_block_tag,
410+
)
378411

379412
def zero(self, subset=None):
380413
"""Set all values to zero.

0 commit comments

Comments
 (0)