Series: Finite Element Method

Shape Functions on Reference Elements

The previous posts showed how each element type has a fixed reference domain T^\hat{T} and how integrals over T^\hat{T} are evaluated by quadrature. The next ingredient is the set of shape functions defined on T^\hat{T}: polynomial functions N1,…,Nn:T^→RN_1, \ldots, N_n : \hat{T} \to \mathbb{R} that form a basis for the local approximation space on the element. Each shape function is associated with one node, and is equal to one at that node and zero at all others.

A key property of shape functions is the partition of unity: at every point x^∈T^\hat{x} \in \hat{T} the shape functions sum to one,

∑i=1nNi(x^)=1.\sum_{i=1}^{n} N_i(\hat{x}) = 1.

This ensures that a constant function is represented exactly, which is a necessary condition for consistency of the approximation.

Beyond the shape function values themselves, the finite element method also needs their gradients. On the reference domain these are the partial derivatives ∂Ni/∂x^j\partial N_i / \partial \hat{x}_j. Transforming them into physical-space gradients on an actual element additionally requires the Jacobian of the element mapping FeF_e, which is covered in a later assembly post.

In the library, this structure is captured by the abstract class ReferenceElement in elements/element.py, which exposes three core methods. node_coords is a property returning an array of shape (num_nodes, topo_dim) with the reference-domain coordinates of each node. shape_functions(xi) takes an array xi of shape (num_points, topo_dim) and returns an array of shape (num_points, num_nodes); the ii-th column of the result is the ii-th shape function evaluated at every query point. shape_function_gradients(xi) returns an array of shape (num_points, num_nodes, topo_dim), where entry [q, i, j] is ∂Ni/∂x^j\partial N_i / \partial \hat{x}_j evaluated at query point qq.

Each element type has a dedicated post: