Skip to content

Commit c245abc

Browse files
dhamJDBetteridge
andauthored
Update interpolate docs for new usage. (#3505)
* Update interpolate docs for new usage. * Use the new version of interpolate throughout the manual page. * Move live code to tests to avoid future rot. * Delete very old documentation about long dead features. Co-authored-by: Jack Betteridge <[email protected]> --------- Co-authored-by: Jack Betteridge <[email protected]>
1 parent 3942166 commit c245abc

File tree

2 files changed

+151
-146
lines changed

2 files changed

+151
-146
lines changed

docs/source/interpolation.rst

Lines changed: 84 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,77 @@
55
Interpolation
66
=============
77

8-
Firedrake offers various ways to interpolate expressions onto fields
9-
(:py:class:`~.Function`\s). Interpolation is often used to set up
10-
initial conditions and/or boundary conditions. The basic syntax for
11-
interpolation is:
8+
Firedrake offers highly flexible capabilities for interpolating expressions
9+
(functions of space) into finite element :py:class:`~.Function`\s.
10+
Interpolation is often used to set up initial conditions and/or boundary
11+
conditions. Mathematically, if :math:`e(x)` is a function of space and
12+
:math:`V` is a finite element functionspace then
13+
:math:`\operatorname{interpolate}(e, V)` is the :py:class:`~.Function`
14+
:math:`v_i \phi_i\in V` such that:
1215

13-
.. code-block:: python3
14-
15-
# create new function f on function space V
16-
f = interpolate(expression, V)
16+
.. math::
1717
18-
# alternatively:
19-
f = Function(V).interpolate(expression)
18+
v_i = \bar{\phi}^*_i(e)
2019
21-
# setting the values of an existing function
22-
f.interpolate(expression)
20+
where :math:`\bar{\phi}^*_i` is the :math:`i`-th dual basis function to
21+
:math:`V` suitably extended such that its domain encompasses :math:`e`.
2322

2423
.. note::
2524

26-
Interpolation is supported for most, but not all, of the elements
27-
that Firedrake provides. In particular, higher-continuity elements
28-
such as Argyris and Hermite do not presently support interpolation.
25+
The extension of dual basis functions to :math:`e` usually follows from the
26+
definition of the dual basis. For example, point evaluation and integral
27+
nodes can naturally be extended to any expression which is evaluatable at
28+
the relevant points, or integrable over that domain.
29+
30+
Firedrake will not impose any constraints on the expression to be
31+
interpolated beyond that its value shape matches that of the space into
32+
which it is interpolated. If the user interpolates an expression for which
33+
the nodes are not well defined (for example point evaluation at a
34+
discontinuity), the result is implementation-dependent.
35+
36+
The interpolate operator
37+
------------------------
38+
39+
.. note::
40+
The semantics for interpolation in Firedrake are in the course of changing.
41+
The documentation provided here is for the new behaviour, in which the
42+
`interpolate` operator is symbolic. In order to access the behaviour
43+
documented here (which is recommended), users need to use the following
44+
import line:
45+
46+
.. code-block:: python3
47+
48+
from firedrake.__future__ import interpolate
49+
50+
51+
The basic syntax for interpolation is:
52+
53+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
54+
:language: python3
55+
:dedent:
56+
:start-after: [test_interpolate_operator 1]
57+
:end-before: [test_interpolate_operator 2]
58+
59+
It is also possible to interpolate an expression directly into an existing
60+
:py:class:`~.Function`:
61+
62+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
63+
:language: python3
64+
:dedent:
65+
:start-after: [test_interpolate_operator 3]
66+
:end-before: [test_interpolate_operator 4]
67+
68+
This is a numerical operation, equivalent to:
69+
70+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
71+
:language: python3
72+
:dedent:
73+
:start-after: [test_interpolate_operator 5]
74+
:end-before: [test_interpolate_operator 6]
75+
2976

30-
The recommended way to specify the source expression is UFL. UFL_
31-
produces clear error messages in case of syntax or type errors, yet
77+
The source expression can be any UFL_ expression with the correct shape.
78+
UFL produces clear error messages in case of syntax or type errors, yet
3279
UFL expressions have good run-time performance, since they are
3380
translated to C interpolation kernels using TSFC_ technology.
3481
Moreover, UFL offers a rich language for describing expressions,
@@ -46,67 +93,27 @@ including:
4693

4794
Here is an example demonstrating some of these features:
4895

49-
.. code-block:: python3
50-
51-
# g is a vector-valued Function, e.g. on an H(div) function space
52-
f = interpolate(sqrt(3.2 * div(g)), V)
96+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
97+
:language: python3
98+
:dedent:
99+
:start-after: [test_interpolate_operator 7]
100+
:end-before: [test_interpolate_operator 8]
53101

54102
This also works as expected when interpolating into a a space defined on the facets
55103
of the mesh:
56104

57-
.. code-block:: python3
58-
59-
# where trace is a trace space on the current mesh:
60-
f = interpolate(expression, trace)
61-
62-
63-
Interpolator objects
64-
--------------------
65-
66-
Firedrake is also able to generate reusable :py:class:`~.Interpolator`
67-
objects which provide caching of the interpolation operation. The
68-
following line creates an interpolator which will interpolate the
69-
current value of `expression` into the space `V`:
70-
71-
.. code-block:: python3
72-
73-
interpolator = Interpolator(expression, V)
74-
75-
If `expression` does not contain a :py:func:`~ufl.TestFunction` then
76-
the interpolation can be performed with:
77-
78-
.. code-block:: python3
79-
80-
f = interpolator.interpolate()
81-
82-
Alternatively, one can use the interpolator to set the value of an existing :py:class:`~.Function`:
83-
84-
.. code-block:: python3
85-
86-
f = Function(V)
87-
interpolator.interpolate(output=f)
88-
89-
If `expression` contains a :py:func:`~ufl.TestFunction` then
90-
the interpolator acts to interpolate :py:class:`~.Function`\s in the
91-
test space to those in the target space. For example:
92-
93-
.. code-block:: python3
94-
95-
w = TestFunction(W)
96-
interpolator = Interpolator(w, V)
105+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
106+
:language: python3
107+
:dedent:
108+
:start-after: [test_interpolate_operator 9]
109+
:end-before: [test_interpolate_operator 10]
97110

98-
Here, `interpolator` acts as the interpolation matrix from the
99-
:py:func:`~.FunctionSpace` W into the
100-
:py:func:`~.FunctionSpace` V. Such that if `f` is a
101-
:py:class:`~.Function` in `W` then `g = interpolator.interpolate(f)` is its
102-
interpolation into a function `g` in `V`. As before, the `output` parameter can
103-
be used to write into an existing :py:class:`~.Function`. Passing the
104-
`transpose=True` option to :py:meth:`~.Interpolator.interpolate` will
105-
cause the transpose interpolation to occur. This is equivalent to the
106-
multigrid restriction operation which interpolates assembled 1-forms
107-
in the dual space to `V` to assembled 1-forms in the dual space to
108-
`W`.
111+
.. note::
109112

113+
Interpolation is supported into most, but not all, of the elements that
114+
Firedrake provides. In particular it is not currently possible to
115+
interpolate into spaces defined by higher-continuity elements such as
116+
Argyris and Hermite.
110117

111118
Interpolation across meshes
112119
---------------------------
@@ -286,22 +293,12 @@ the external data source, but the precise details are not relevant
286293
now. In this case, interpolation into a target function space ``V``
287294
proceeds as follows:
288295

289-
.. code-block:: python3
290296

291-
# First, grab the mesh.
292-
m = V.mesh()
293-
294-
# Now make the VectorFunctionSpace corresponding to V.
295-
W = VectorFunctionSpace(m, V.ufl_element())
296-
297-
# Next, interpolate the coordinates onto the nodes of W.
298-
X = interpolate(m.coordinates, W)
299-
300-
# Make an output function.
301-
f = Function(V)
302-
303-
# Use the external data function to interpolate the values of f.
304-
f.dat.data[:] = mydata(X.dat.data_ro)
297+
.. literalinclude:: ../../tests/regression/test_interpolation_manual.py
298+
:language: python3
299+
:dedent:
300+
:start-after: [test_interpolate_external 1]
301+
:end-before: [test_interpolate_external 2]
305302

306303
This will also work in parallel, as the interpolation will occur on
307304
each process, and Firedrake will take care of the halo updates before
@@ -310,65 +307,6 @@ the next operation using ``f``.
310307
For interaction with external point data, see the
311308
:ref:`corresponding manual section <external-point-data>`.
312309

313-
314-
C string expressions
315-
--------------------
316-
317-
.. warning::
318-
319-
C string expressions were a FEniCS compatibility feature which has
320-
now been removed. Users should use UFL expressions instead. This
321-
section only remains to assist in the transition of existing code.
322-
323-
Here are a couple of old-style C string expressions, and their modern replacements.
324-
325-
.. code-block:: python3
326-
327-
# Expression:
328-
f = interpolate(Expression("sin(x[0]*pi)"), V)
329-
330-
# UFL equivalent:
331-
x = SpatialCoordinate(V.mesh())
332-
f = interpolate(sin(x[0] * math.pi), V)
333-
334-
# Expression with a Constant parameter:
335-
f = interpolate(Expression('sin(x[0]*t)', t=t), V)
336-
337-
# UFL equivalent:
338-
x = SpatialCoordinate(V.mesh())
339-
f = interpolate(sin(x[0] * t), V)
340-
341-
342-
Python expression classes
343-
-------------------------
344-
345-
.. warning::
346-
347-
Python expression classes were a FEniCS compatibility feature which has
348-
now been removed. Users should use UFL expressions instead. This
349-
section only remains to assist in the transition of existing code.
350-
351-
Since Python ``Expression`` classes expressions are
352-
deprecated, below are a few examples on how to replace them with UFL
353-
expressions:
354-
355-
.. code-block:: python3
356-
357-
# Python expression:
358-
class MyExpression(Expression):
359-
def eval(self, value, x):
360-
value[:] = numpy.dot(x, x)
361-
362-
def value_shape(self):
363-
return ()
364-
365-
f.interpolate(MyExpression())
366-
367-
# UFL equivalent:
368-
x = SpatialCoordinate(f.function_space().mesh())
369-
f.interpolate(dot(x, x))
370-
371-
372310
Generating Functions with randomised values
373311
-------------------------------------------
374312

tests/regression/test_interpolation_manual.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,73 @@
44
import numpy as np
55

66

7+
def test_interpolate_operator():
8+
mesh = UnitSquareMesh(2, 2)
9+
V = FunctionSpace(mesh, "CG", 2)
10+
x, y = SpatialCoordinate(mesh)
11+
expression = x * y
12+
# [test_interpolate_operator 1]
13+
# create a UFL expression for the interpolation operation.
14+
f_i = interpolate(expression, V)
15+
16+
# numerically evaluate the interpolation to create a new Function
17+
f = assemble(f_i)
18+
# [test_interpolate_operator 2]
19+
assert isinstance(f, Function)
20+
21+
# [test_interpolate_operator 3]
22+
f = Function(V)
23+
f.interpolate(expression)
24+
# [test_interpolate_operator 4]
25+
f2 = f
26+
# [test_interpolate_operator 5]
27+
f = Function(V)
28+
f.assign(assemble(interpolate(expression, V)))
29+
# [test_interpolate_operator 6]
30+
assert np.allclose(f.dat.data_ro, f2.dat.data_ro)
31+
32+
W = FunctionSpace(mesh, "RT", 1)
33+
g = project(as_vector((sin(x), cos(y))), W)
34+
# [test_interpolate_operator 7]
35+
# g is a vector-valued Function, e.g. on an H(div) function space
36+
f = assemble(interpolate(sqrt(3.2 * div(g)), V))
37+
# [test_interpolate_operator 8]
38+
39+
# [test_interpolate_operator 9]
40+
trace = FunctionSpace(mesh, "HDiv Trace", 0)
41+
f = assemble(interpolate(expression, trace))
42+
# [test_interpolate_operator 10]
43+
44+
45+
def test_interpolate_external():
46+
m = UnitSquareMesh(2, 2)
47+
V = FunctionSpace(m, "CG", 2)
48+
x, y = SpatialCoordinate(m)
49+
expression = x * y
50+
51+
def mydata(points):
52+
return [x*y for x, y in points]
53+
54+
# [test_interpolate_external 1]
55+
# First, grab the mesh.
56+
m = V.mesh()
57+
58+
# Now make the VectorFunctionSpace corresponding to V.
59+
W = VectorFunctionSpace(m, V.ufl_element())
60+
61+
# Next, interpolate the coordinates onto the nodes of W.
62+
X = assemble(interpolate(m.coordinates, W))
63+
64+
# Make an output function.
65+
f = Function(V)
66+
67+
# Use the external data function to interpolate the values of f.
68+
f.dat.data[:] = mydata(X.dat.data_ro)
69+
# [test_interpolate_external 2]
70+
f2 = assemble(interpolate(expression, V))
71+
assert np.allclose(f.dat.data_ro, f2.dat.data_ro)
72+
73+
774
def test_line_integral():
875
# [test_line_integral 1]
976
# Start with a simple field exactly represented in the function space over

0 commit comments

Comments
 (0)