Skip to content

Commit 6c38a6f

Browse files
committed
FunctionSpace: enable quad_scheme and docs
1 parent 0b8184a commit 6c38a6f

File tree

9 files changed

+248
-32
lines changed

9 files changed

+248
-32
lines changed

docs/source/element_list.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from finat.ufl.elementlist import ufl_elements
2-
# ~ from ufl.finiteelement.elementlist import ufl_elements
32
from finat.element_factory import supported_elements
43
import csv
54

docs/source/extruded-meshes.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ supports a more general syntax:
304304
305305
V = FunctionSpace(mesh, element)
306306
307-
where ``element`` is a UFL :py:class:`~ufl.finiteelement.finiteelement.FiniteElement` object. This
307+
where ``element`` is a UFL :py:class:`~finat.ufl.finiteelement.FiniteElement` object. This
308308
requires generation and manipulation of ``FiniteElement`` objects.
309309

310310
Geometrically, an extruded mesh cell is the *product* of a base, "horizontal",
@@ -319,7 +319,7 @@ The ``TensorProductElement`` operator
319319
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
320320

321321
To create an element compatible with an extruded mesh, one should use
322-
the :py:class:`~ufl.finiteelement.tensorproductelement.TensorProductElement`
322+
the :py:class:`~finat.ufl.tensorproductelement.TensorProductElement`
323323
operator. For example,
324324

325325
.. code-block:: python3
@@ -359,7 +359,7 @@ but is piecewise constant horizontally.
359359

360360
A more complicated element, like a Mini horizontal element with linear
361361
variation in the vertical direction, may be built using the
362-
:py:class:`~ufl.finiteelement.enrichedelement.EnrichedElement` functionality
362+
:py:class:`~finat.ufl.enrichedelement.EnrichedElement` functionality
363363
in either of the following ways:
364364

365365
.. code-block:: python3
@@ -392,7 +392,7 @@ The ``HDivElement`` and ``HCurlElement`` operators
392392
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
393393

394394
For moderately complicated vector-valued elements,
395-
:py:class:`~ufl.finiteelement.tensorproductelement.TensorProductElement`
395+
:py:class:`~finat.ufl.tensorproductelement.TensorProductElement`
396396
does not give enough information to unambiguously produce the desired
397397
space. As an example, consider the lowest-order *Raviart-Thomas* element on a
398398
quadrilateral. The degrees of freedom live on the facets, and consist of
@@ -417,7 +417,7 @@ However, this is only scalar-valued. There are two natural vector-valued
417417
elements that can be generated from this: one of them preserves tangential
418418
continuity between elements, and the other preserves normal continuity
419419
between elements. To obtain the Raviart-Thomas element, we must use the
420-
:py:class:`~ufl.finiteelement.hdivcurl.HDivElement` operator:
420+
:py:class:`~finat.ufl.hdivcurl.HDivElement` operator:
421421

422422
.. code-block:: python3
423423
@@ -433,7 +433,7 @@ between elements. To obtain the Raviart-Thomas element, we must use the
433433
:align: center
434434

435435
The RT quadrilateral element, requiring the use
436-
of :py:class:`~ufl.finiteelement.hdivcurl.HDivElement`
436+
of :py:class:`~finat.ufl.hdivcurl.HDivElement`
437437

438438
Another reason to use these operators is when expanding a vector into a
439439
higher dimensional space. Consider the lowest-order Nedelec element of the
@@ -453,7 +453,7 @@ the product of this with a continuous element on an interval:
453453
454454
This element still only has two components. To expand this into a
455455
three-dimensional curl-conforming element, we must use the
456-
:py:class:`~ufl.finiteelement.hdivcurl.HCurlElement` operator; the syntax is:
456+
:py:class:`~finat.ufl.hdivcurl.HCurlElement` operator; the syntax is:
457457

458458
.. code-block:: python3
459459

docs/source/manual.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Manual
1919
duals
2020
interpolation
2121
point-evaluation
22+
quadrature
2223
external_operators
2324
adjoint
2425
visualisation

docs/source/quadrature.rst

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
.. only:: html
2+
3+
.. contents::
4+
5+
Quadrature rules
6+
================
7+
8+
To numerically compute the integrals in the variational formulation,
9+
a quadrature rule is required.
10+
By default, Firedrake obtains a quadrature rule by estimating the polynomial
11+
degree of the integrands within a :py:class:`ufl.Form`. Sometimes
12+
this estimate might be quite large, and a warning like this one will be raised:
13+
14+
.. code-block::
15+
16+
tsfc:WARNING Estimated quadrature degree 13 more than tenfold greater than any argument/coefficient degree (max 1)
17+
18+
For integrals with very complicated nonlinearities, the estimated quadrature
19+
degree might be in the hundreds or thousands, rendering the integration
20+
prohibitively expensive, or leading to segfaults.
21+
22+
Specifying the quadrature rule in the variational formulation
23+
-------------------------------------------------------------
24+
25+
To manually override the default, the
26+
quadrature degree can be prescribed on each integral :py:class:`~ufl.measure.Measure`,
27+
28+
.. code-block:: python3
29+
30+
inner(sin(u)**4, v) * dx(degree=4)
31+
32+
Setting ``degree=4`` means that the quadrature rule will be exact only for integrands
33+
of total polynomial degree up to 4. This, of course, will introduce a greater numerical error than the default.
34+
35+
For integrals that do not specify a quadrature degree, the default may be keyed as
36+
``"quadrature_degree"`` in the ``form_compiler_parameters`` dictionary passed on to
37+
:py:func:`~.solve`, :py:func:`~.project`, or :py:class:`~.NonlinearVariationalProblem`.
38+
39+
.. code-block:: python3
40+
41+
F = inner(grad(u), grad(v))*dx(degree=0) + inner(exp(u), v)*dx - inner(1, v)*dx
42+
43+
solve(F == 0, u, form_compiler_parameters={"quadrature_degree": 4})
44+
45+
In the example above, only the integrals with unspecified quadrature degree
46+
will be computed on a quadrature rule that exactly integrates polynomials of
47+
the degree set in ``form_compiler_parameters``.
48+
49+
Another way to specify the quadrature rule is through the ``scheme`` keyword. This could be
50+
either a :py:class:`~finat.quadrature.QuadratureRule`, or a string. Supported string values
51+
are ``"default"``, ``"canonical"``, and ``"KMV"``. For more details see
52+
:py:func:`~FIAT.quadrature_schemes.create_quadrature`.
53+
54+
Lumped quadrature schemes
55+
-------------------------
56+
57+
Spectral elements, such as Gauss-Legendre-Lobatto and `KMV`_, may be used with
58+
lumped quadrature schemes to produce a diagonal mass matrix.
59+
60+
.. literalinclude:: ../../tests/firedrake/regression/test_quadrature_manual.py
61+
:language: python3
62+
:dedent:
63+
:start-after: [test_lump_scheme 1]
64+
:end-before: [test_lump_scheme 2]
65+
66+
.. Note::
67+
68+
To obtain the lumped mass matrix with ``scheme="KMV"``,
69+
the ``degree`` argument should match the degree of the :py:class:`~finat.ufl.finiteelement.FiniteElement`.
70+
71+
The Quadrature space
72+
--------------------
73+
74+
It is possible to define a finite element :py:class:`~.Function` on a quadrature rule.
75+
The ``"Quadrature"`` and ``"Boundary Quadrature"`` spaces are useful to
76+
interpolate data at quadrature points on cell interiors and cell boundaries,
77+
respectively.
78+
79+
.. literalinclude:: ../../tests/firedrake/regression/test_quadrature_manual.py
80+
:language: python3
81+
:dedent:
82+
:start-after: [test_quadrature_space 1]
83+
:end-before: [test_quadrature_space 2]
84+
85+
The ``quad_scheme`` keyword argument again may be either
86+
:py:class:`~finat.quadrature.QuadratureRule` or a string.
87+
If a :py:class:`~.Function` in the ``"Quadrature"`` space appears within an
88+
integral, Firedrake will automatically select the quadrature rule that corresponds
89+
to ``dx(degree=quad_degree, scheme=quad_scheme)`` to match the one associated
90+
with the quadrature space.
91+
92+
.. _element_quad_scheme:
93+
94+
Specifying the quadrature for integral-type degrees of freedom
95+
--------------------------------------------------------------
96+
97+
Finite element spaces with :ref:`integral-type degrees of freedom <element_variants>`
98+
support different quadrature rules.
99+
These are selected by passing a string to the ``"quad_scheme"`` keyword argument of
100+
the :py:class:`~finat.ufl.finiteelement.FiniteElement` or
101+
:py:func:`~.FunctionSpace` constructors. For example, to construct a
102+
Crouzeix-Raviart space with degrees of freedom consisting of integrals along the edges
103+
computed from a 2-point average at the endpoints, one can set ``quad_scheme="KMV"``:
104+
105+
.. code-block:: python3
106+
107+
fe = FiniteElement("Crouzeix-Raviart", triangle, 1, variant="integral", quad_scheme="KMV")
108+
109+
.. Note::
110+
111+
Finite elements with integral-type degrees of freedom only accept string
112+
values for ``quad_scheme``, since it does not make sense to specify a
113+
concrete :py:class:`~finat.quadrature.QuadratureRule` when the degrees of
114+
freedom are defined on cell entities of different dimensions.
115+
116+
117+
.. _KMV : https://defelement.org/elements/kong-mulder-veldhuizen.html

docs/source/variational-problems.rst

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ family such as ``"Raviart-Thomas"``. Secondly, you may use the
180180
family, which gives a vector-valued space where each component is
181181
identical to the appropriate scalar-valued
182182
:py:class:`~.FunctionSpace`. Thirdly, you can create a
183-
:py:class:`~ufl.classes.VectorElement` directly (which is itself
183+
:py:class:`~finat.ufl.mixedelement.VectorElement` directly (which is itself
184184
*vector-valued* and pass that to the :py:func:`~.FunctionSpace`
185185
constructor).
186186

@@ -275,9 +275,11 @@ Firedrake supports the use of the following finite elements.
275275
:file: element_list.csv
276276

277277
In addition, the
278-
:py:class:`~ufl.finiteelement.tensorproductelement.TensorProductElement`
278+
:py:class:`~finat.ufl.tensorproductelement.TensorProductElement`
279279
operator can be used to create product elements on extruded meshes.
280280

281+
.. _element_variants:
282+
281283
Element variants
282284
~~~~~~~~~~~~~~~~
283285

@@ -288,15 +290,49 @@ For discontinuous elements these are the Gauss-Legendre points, and for
288290
continuous elements these are the Gauss-Lobatto-Legendre points.
289291
For CG and DG spaces on simplices, Firedrake offers both equispaced points and
290292
the better conditioned recursive Legendre points from :cite:`Isaac2020` via the
291-
`recursivenodes`_ module. These are selected by passing `variant="equispaced"`
292-
or `variant="spectral"` to the :py:class:`~ufl.classes.FiniteElement` or
293+
`recursivenodes`_ module. These are selected by passing ``variant="equispaced"``
294+
or ``variant="spectral"`` to the :py:class:`~finat.ufl.finiteelement.FiniteElement` or
293295
:py:func:`~.FunctionSpace` constructors. For example:
294296

295297
.. code-block:: python3
296298
297299
fe = FiniteElement("RTCE", quadrilateral, 2, variant="equispaced")
298300
299-
The default is the spectral variant.
301+
For CG and DG spaces, and most tensor-product elements, the default is ``variant="spectral"``.
302+
303+
Integral-type degrees of freedom are also available through ``variant="integral"``. These
304+
enable orthogonal bases for DG on any cell type:
305+
306+
.. code-block:: python3
307+
308+
V = FunctionSpace(mesh, "DG", 1, variant="integral")
309+
310+
In this case, the degrees of freedom are integral moments against a basis of polynomials,
311+
which need to be computed with a quadrature rule. The degree of accuracy of the quadrature
312+
can be increased also through the variant option. For instance, ``variant="integral(4)"``
313+
will use a quadrature that exactly evaluates the degrees of freedom on polynomials of
314+
4 degrees higher than that of the space.
315+
Furthermore, the quadrature rule can be specified via the ``"quad_scheme"`` keyword argument.
316+
For more details, see the manual section on :ref:`quadrature rules <element_quad_scheme>`.
317+
318+
Integral-type degrees of freedom are useful to nearly preserve curl-free or
319+
divergence-free functions when interpolating into the finite element space.
320+
H(curl) and H(div) elements, such as Nédélec, Brezzi-Douglas-Marini, and Raviart-Thomas,
321+
have ``variant="integral"`` as default. These elements also support ``variant="point"``,
322+
where the degrees of freedom are vector components evaluated at equispaced points.
323+
324+
Macroelements can be constructed by splitting each cell into subcells and tiling
325+
the element on each subcell, without refining the mesh. For example,
326+
the continuous Lagrange space where each simplex is subdivided
327+
by connecting each of its vertices to the barycenter is constructed as
328+
329+
.. code-block:: python3
330+
331+
V = FunctionSpace(mesh, "CG", 1, variant="alfeld")
332+
333+
The space P1-iso-P2 may be similarly constructed with ``variant="iso(2)"``.
334+
Macroelements also support integral-type degrees of freedom,
335+
for example ``variant="alfeld,integral(2)"``.
300336

301337

302338
Expressing a variational problem
@@ -685,7 +721,7 @@ More complicated forms
685721

686722
UFL is a fully-fledged language for expressing variational problems,
687723
and hence has operators for all appropriate vector calculus operations
688-
along with special support for discontinuous galerkin methods in the
724+
along with special support for discontinuous Galerkin methods in the
689725
form of symbolic expressions for facet averages and jumps. For an
690726
introduction to these concepts we refer the user to the `UFL manual
691727
<UFL_package_>`_ as well as the :ref:`Firedrake tutorials

firedrake/cofunction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def interpolate(self,
344344
block on the Pyadjoint tape.
345345
**kwargs
346346
Any extra kwargs are passed on to the interpolate function.
347-
For details see `firedrake.interpolation.interpolate`.
347+
For details see :func:`firedrake.interpolation.interpolate`.
348348
349349
Returns
350350
-------

firedrake/function.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ def interpolate(self,
373373
block on the Pyadjoint tape.
374374
**kwargs
375375
Any extra kwargs are passed on to the interpolate function.
376-
For details see `firedrake.interpolation.interpolate`.
376+
For details see :func:`firedrake.interpolation.interpolate`.
377377
378378
Returns
379379
-------

0 commit comments

Comments
 (0)