Skip to content

Commit fd46ca8

Browse files
mscroggssigvaldm
andauthored
Update docs (#118)
* Add reference to Ciarlet * DOI is for 2002 reprint * add some examples to readme * Add Nedelec demo Add text to demos Test snippets in readme * Add a LAgrange test * update demos * Add demos to docs * add more intro to readme * Corrected mapping in Lagrange demo (#119) Co-authored-by: Matthew Scroggs <[email protected]> * Add fast test running to readme * give reason for xfailing Co-authored-by: Sigvald Marholm <[email protected]>
1 parent ce15190 commit fd46ca8

20 files changed

+319
-76
lines changed

README.md

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@
99
[![status](https://joss.theoj.org/papers/95e093272d6555489b1f941aebd6494b/status.svg)](https://joss.theoj.org/papers/95e093272d6555489b1f941aebd6494b)
1010

1111
Symfem is a symbolic finite element definition library, that can be used to
12-
symbolically evaluate the basis functions of a finite element space.
12+
symbolically evaluate the basis functions of a finite element space. Symfem can:
13+
14+
- Symbolically compute the basis functions of a wide range of finite element spaces
15+
- Symbolically compute derivatives and vector products and substitute values into functions
16+
- Allow the user to define their own element using the Ciarlet definition of a finite element
17+
- Be used to verify that the basis functions of a given space have some desired properties
1318

1419
## Installing Symfem
15-
### Installing from repo
20+
### Installing from sourde
1621
Symfem can be installed by downloading the [GitHub repo](https://github.com/mscroggs/symfem)
1722
and running:
1823

@@ -41,10 +46,68 @@ To run the Symfem unit tests, clone the repository and run:
4146
python3 -m pytest test/
4247
```
4348

49+
You may instead like to run the following, as this will skip the slowest tests.
50+
51+
```bash
52+
python3 -m pytest test/ --speed fast
53+
```
54+
4455
## Using Symfem
45-
Documentation of the latest release version of Symfem can be found on
46-
[Read the Docs](https://symfem.readthedocs.io/en/latest/).
56+
Finite elements can be created in Symfem using the `symfem.create_element()`
57+
function. For example, some elements are created in the following snippet:
58+
59+
```python
60+
import symfem
4761

62+
lagrange = symfem.create_element("triangle", "Lagrange", 1)
63+
rt = symfem.create_element("tetrahedron", "Raviart-Thomas", 2)
64+
nedelec = symfem.create_element("triangle", "N2curl", 1)
65+
qcurl = symfem.create_element("quadrilateral", "Qcurl", 2)
66+
```
67+
68+
The polynomial basis of an element can be obtained by calling `get_polynomial_basis()`:
69+
70+
```python
71+
import symfem
72+
73+
lagrange = symfem.create_element("triangle", "Lagrange", 1)
74+
print(lagrange.get_basis_functions())
75+
```
76+
```
77+
[-x - y + 1, x, y]
78+
```
79+
80+
Each basis function will be a [Sympy](https://www.sympy.org) symbolic expression.
81+
82+
Derivative of these basis functions can be computed using the functions in
83+
[`symfem.calculus`](symfem/calculus.py). Vector-valued basis functions can
84+
be manipulated using the functions in [`symfem.vector`](symfem/vectors.py).
85+
86+
The function `map_to_cell` can be used to map the basis functions of a finite element
87+
to a non-default cell:
88+
89+
```python
90+
import symfem
91+
92+
lagrange = symfem.create_element("triangle", "Lagrange", 1)
93+
print(lagrange.get_basis_functions())
94+
print(lagrange.map_to_cell([(0,0), (2, 0), (2, 1)]))
95+
```
96+
```
97+
[-x - y + 1, x, y]
98+
[1 - x/2, x/2 - y, y]
99+
```
100+
101+
### Further documentation
102+
More detailed documentation of the latest release version of Symfem can be found on
103+
[Read the Docs](https://symfem.readthedocs.io/en/latest/). A series of example uses
104+
of Symfem can be found in the [`demo` folder](demo/) or viewed on
105+
[Read the Docs](https://symfem.readthedocs.io/en/latest/demos/index.html).
106+
107+
Details of the definition of each element can be found on [DefElement](https://defelement.com)
108+
alongside Symfem snippets for creating the element.
109+
110+
## Getting help
48111
You can ask questions about using Symfem by opening [an issue with the support label](https://github.com/mscroggs/symfem/issues/new?assignees=&labels=support&template=support.md&title=).
49112
You can view previously answered questions [here](https://github.com/mscroggs/symfem/issues?q=is%3Aclosed+label%3Asupport).
50113

@@ -175,4 +238,3 @@ You can find information about how to contribute to Symfem [here](CONTRIBUTING.m
175238
- Buffa-Christiansen
176239
- dual
177240
- rotated Buffa-Christiansen
178-

demo/custom_element.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
"""
2+
This demo shows how a custom element can be created in Symfem.
3+
"""
4+
15
import symfem
26
import sympy
37
from symfem.finite_element import CiarletElement
@@ -40,3 +44,6 @@ def __init__(self, reference, order):
4044
# Create the element and print its basis functions
4145
element = symfem.create_element("quadrilateral", "custom quad element", 1)
4246
print(element.get_basis_functions())
47+
48+
# Run the Symfem tests on the custom element
49+
element.test()

demo/lagrange.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
This demo shows how Symfem can be used to verify properties of the basis functions of
3+
an element.
4+
5+
The basis functions of a Lagrange element, when restricted to an edge of a cell,
6+
should be equal to the basis functions of a Lagrange space on that edge (or equal to 0).
7+
8+
In this demo, we verify that this is true for an order 5 Lagrange element on a triangle.
9+
"""
10+
11+
import symfem
12+
import sympy
13+
from symfem.symbolic import x, subs, symequal
14+
15+
element = symfem.create_element("triangle", "Lagrange", 5)
16+
edge_element = symfem.create_element("interval", "Lagrange", 5)
17+
18+
# Define a parameter that will go from 0 to 1 on the chosen edge
19+
a = sympy.Symbol("a")
20+
21+
# Get the basis functions of the Lagrange space and substitute the parameter into the
22+
# functions on the edge
23+
basis = element.get_basis_functions()
24+
edge_basis = [subs(f, x[0], a) for f in edge_element.get_basis_functions()]
25+
26+
# Get the DOFs on edge 0 (from vertex 1 (1,0) to vertex 2 (0,1))
27+
# (1 - a, a) is a parametrisation of this edge
28+
dofs = element.entity_dofs(0, 1) + element.entity_dofs(0, 2) + element.entity_dofs(1, 0)
29+
# Check that the basis functions on this edge are equal
30+
for d, edge_f in zip(dofs, edge_basis):
31+
# symequal will simplify the expressions then check that they are equal
32+
assert symequal(subs(basis[d], x[:2], (1 - a, a)), edge_f)
33+
34+
# Get the DOFs on edge 1 (from vertex 0 (0,0) to vertex 2 (0,1), parametrised (0, a))
35+
dofs = element.entity_dofs(0, 0) + element.entity_dofs(0, 2) + element.entity_dofs(1, 1)
36+
for d, edge_f in zip(dofs, edge_basis):
37+
assert symequal(subs(basis[d], x[:2], (0, a)), edge_f)
38+
39+
# Get the DOFs on edge 2 (from vertex 0 (0,0) to vertex 1 (1,0), parametrised (a, 0))
40+
dofs = element.entity_dofs(0, 0) + element.entity_dofs(0, 1) + element.entity_dofs(1, 2)
41+
for d, edge_f in zip(dofs, edge_basis):
42+
assert symequal(subs(basis[d], x[:2], (a, 0)), edge_f)

demo/nedelec.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
This demo shows how Symfem can be used to verify properties of the polynomial basis
3+
and basis functions of an element.
4+
5+
The polynomial set of a degree k Nedelec first kind space is:
6+
{polynomials of degree < k} UNION {polynomials of degree k such that p DOT x = 0}.
7+
8+
The basis functions of a Nedelec first kind that are associated with the interior of the cell
9+
have 0 tangential component on the facets of the cell.
10+
11+
In this demo, we verify that these properties hold for a degree 4 Nedelec first kind
12+
space on a triangle.
13+
"""
14+
15+
import symfem
16+
from symfem.polynomials import polynomial_set
17+
from symfem.symbolic import x, subs, symequal
18+
19+
# In this demo, we use the function vdot to compute the dot product of two vectors.
20+
# The module symfem.vectors contains many functions that can be used to manipulate
21+
# vectors.
22+
from symfem.vectors import vdot
23+
24+
element = symfem.create_element("triangle", "Nedelec1", 4)
25+
polys = element.get_polynomial_basis()
26+
27+
# Check that the first 20 polynomials in the polynomial basis are
28+
# the polynomials of degree 3
29+
p3 = polynomial_set(2, 2, 3)
30+
assert len(p3) == 20
31+
for i, j in zip(p3, polys[:20]):
32+
assert i == j
33+
34+
# Check that the rest of the polynomials in the polynomial basis
35+
# satisfy p DOT x = 0
36+
for p in polys[20:]:
37+
assert vdot(p, x) == 0
38+
39+
# Get the basis functions associated with the interior of the triangle
40+
basis = element.get_basis_functions()
41+
functions = [basis[d] for d in element.entity_dofs(2, 0)]
42+
43+
# Check that these functions have 0 tangential component on each edge
44+
# symequal will simplify the expressions then check that they are equal
45+
for f in functions:
46+
assert symequal(vdot(subs(f, x[0], 1 - x[1]), (1, -1)), 0)
47+
assert symequal(vdot(subs(f, x[0], 0), (0, 1)), 0)
48+
assert symequal(vdot(subs(f, x[1], 0), (1, 0)), 0)

demo/stiffness_matrix.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1+
"""
2+
This demo shows how Symfem can be used to compute a stiffness matrix.
3+
"""
4+
15
import symfem
26
from symfem.vectors import vdot
37
from symfem.calculus import grad
48

5-
matrix = [[0 for i in range(4)] for j in range(4)]
6-
9+
# Define the vertived and triangles of the mesh
710
vertices = [(0, 0), (1, 0), (0, 1), (1, 1)]
811
triangles = [[0, 1, 2], [1, 3, 2]]
912

13+
# Create a matrix of zeros with the correct shape
14+
matrix = [[0 for i in range(4)] for j in range(4)]
15+
16+
# Create a Lagrange element
1017
element = symfem.create_element("triangle", "Lagrange", 1)
18+
1119
for triangle in triangles:
20+
# Get the vertices of the triangle
1221
vs = [vertices[i] for i in triangle]
22+
# Create a reference cell with these vertices: this will be used
23+
# to compute the integral over the triangle
1324
ref = symfem.create_reference("triangle", vertices=vs)
25+
# Map the basis functions to the cell
1426
basis = element.map_to_cell(vs)
27+
1528
for test_i, test_f in zip(triangle, basis):
1629
for trial_i, trial_f in zip(triangle, basis):
30+
# Compute the integral of grad(u)-dot-grad(v) for each pair of basis
31+
# functions u and v
1732
integrand = vdot(grad(test_f, 2), grad(trial_f, 2))
1833
matrix[test_i][trial_i] += ref.integral(integrand)
1934

docs/demos/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Symfem demos
55
.. toctree::
66
:titlesonly:
77

8+
lagrange
9+
nedelec
810
stiffness_matrix
911
custom_element
10-

docs/demos/lagrange.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
###########################
2+
Checking a Lagrange element
3+
###########################
4+
5+
This demo shows how Symfem can be used to confirm that when the basis functions of a Lagrange
6+
element of a triangle are restricted to an edge, then they are equal to the basis functions
7+
of a Lagrange element on that edge.
8+
9+
.. literalinclude:: ../../demo/lagrange.py
10+
:language: Python

docs/demos/nedelec.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
##########################
2+
Checking a Nedelec element
3+
##########################
4+
5+
This demo shows how Symfem can be used to confirm that the polynomial set of a Nedelec
6+
first kind element are as expected.
7+
8+
.. literalinclude:: ../../demo/nedelec.py
9+
:language: Python

docs/index.rst

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
Symfem
77
======
88

9-
Welcome to the documention of Symfem: a symbolic finite element definition library
9+
Welcome to the documention of Symfem: a symbolic finite element definition library.
10+
11+
Symfem can be used to create a very wide range of finite element spaces. The basis functions
12+
of these spaces can be computed symbolically, allowing them to easily be further
13+
manipulated.
1014

1115
*****************
1216
Installing Symfem
@@ -27,7 +31,7 @@ Using Symfem
2731

2832
Finite elements
2933
---------------
30-
Finite elements can be created in symfem using the :func:`symfem.create_element` function.
34+
Finite elements can be created in Symfem using the :func:`symfem.create_element` function.
3135
For example, some elements are created in the following snippet:
3236

3337
.. code-block:: python
@@ -40,67 +44,69 @@ For example, some elements are created in the following snippet:
4044
qcurl = symfem.create_element("quadrilateral", "Qcurl", 2)
4145
4246
`create_element` will create a :class:`symfem.finite_element.FiniteElement` object.
43-
From this object, the polynomial basis of the element can be obtained:
47+
The basis functions spanning the finite element space can be obtained, or tabulated
48+
at a set of points:
4449

4550
.. code-block:: python
4651
4752
import symfem
4853
4954
lagrange = symfem.create_element("triangle", "Lagrange", 1)
50-
print(lagrange.get_polynomial_basis())
55+
print(lagrange.get_basis_functions())
56+
57+
points = [[0, 0], [0.5, 0], [1, 0], [0.25, 0.25]]
58+
print(lagrange.tabulate_basis(points))
5159
5260
::
5361

54-
[1, x, y]
62+
[-x - y + 1, x, y]
63+
[[1, 0, 0], [0.500000000000000, 0.500000000000000, 0], [0, 1, 0], [0.500000000000000, 0.250000000000000, 0.250000000000000]]
5564

56-
Each item in the polynomial basis will be a `Sympy <https://www.sympy.org>`_ symbolic expression.
65+
Each basis function will be a `Sympy <https://www.sympy.org>`_ symbolic expression.
5766

58-
The functionals that define the DOFs of the finite element space can be obtained with the following
59-
snippet.
67+
The majority of the elements in Symfem are defined using Ciarlet's [Ciarlet]_ definition of
68+
a finite element (:class:`symfem.finite_element.CiarletElement`): these elements are using a
69+
polynomial set, and a set of functionals. In Symfem, the polynomial set
70+
of an element can be obtained by:
6071

6172
.. code-block:: python
6273
6374
import symfem
6475
6576
lagrange = symfem.create_element("triangle", "Lagrange", 1)
66-
print(lagrange.dofs)
77+
print(lagrange.get_polynomial_basis())
6778
6879
::
6980

70-
[<symfem.functionals.PointEvaluation object at 0x{ADDRESS}>, <symfem.functionals.PointEvaluation object at 0x{ADDRESS}>, <symfem.functionals.PointEvaluation object at 0x{ADDRESS}>]
71-
72-
Each functional will be a functional defined in :mod:`symfem.functionals`.
81+
[1, x, y]
7382

74-
The basis functions spanning the finite element space can be obtained, or tabulated
75-
at a set of points:
83+
The functionals of the finite element space can be obtained with the following snippet.
7684

7785
.. code-block:: python
7886
7987
import symfem
8088
8189
lagrange = symfem.create_element("triangle", "Lagrange", 1)
82-
print(lagrange.get_basis_functions())
83-
84-
points = [[0, 0], [0.5, 0], [1, 0], [0.25, 0.25]]
85-
print(lagrange.tabulate_basis(points))
90+
print(lagrange.dofs)
8691
8792
::
8893

89-
[-x - y + 1, x, y]
90-
[[1, 0, 0], [0.500000000000000, 0.500000000000000, 0], [0, 1, 0], [0.500000000000000, 0.250000000000000, 0.250000000000000]]
94+
[<symfem.functionals.PointEvaluation object at 0x{ADDRESS}>, <symfem.functionals.PointEvaluation object at 0x{ADDRESS}>, <symfem.functionals.PointEvaluation object at 0x{ADDRESS}>]
95+
96+
Each functional will be a functional defined in :mod:`symfem.functionals`.
9197

92-
Reference elements
93-
------------------
94-
Reference elements can be obtained from a :class:`symfem.finite_element.FiniteElement`:
98+
Reference cells
99+
---------------
100+
Reference cells can be obtained from a :class:`symfem.finite_element.FiniteElement`:
95101

96102
.. code-block:: python
97103
98-
import symfem
104+
import symfem
99105
100106
lagrange = symfem.create_element("triangle", "Lagrange", 1)
101107
reference = lagrange.reference
102108
103-
Alternatively, reference elements can be created using the :func:`symfem.create_reference` function.
109+
Alternatively, reference cells can be created using the :func:`symfem.create_reference` function.
104110
For example:
105111

106112
.. code-block:: python
@@ -115,7 +121,7 @@ For example:
115121
In the final example, the vertices of the reference have been provided, so a reference
116122
with these three vertices will be created.
117123

118-
Various information about the reference can be accessed. The reference element's subentities
124+
Various information about the reference can be accessed. The reference cell's subentities
119125
can be obtained:
120126

121127
.. code-block:: python
@@ -196,3 +202,5 @@ Documentation index
196202

197203
demos/index
198204
docs/index
205+
206+
.. [Ciarlet] P. G. Ciarlet, The Finite Element Method for Elliptic Problems (2002, first published 1978) [DOI: 10.1137/1.9780898719208](https://doi.org/10.1137/1.9780898719208)

0 commit comments

Comments
 (0)