How to add a new element type
To add a new finite element there's a few things we need to do. First, we may need to define an abstract type if the element type is not a currently supported abstract element topology. The currently supported abstract element topologies are
AbstractEdge
AbstractFace
AbstractQuad
AbstractTri
AbstractVolume
AbstractHex
AbstractTet
We'll use a MyTri3
element as an example of how to add a new element type.
First, let's pretend AbstractTri
doesn't exist yet. The first thing we need to do is define the following abstract type
julia> using ReferenceFiniteElements
julia> abstract type AbstractTri{V, I, P, Q} <: ReferenceFiniteElements.AbstractFace{V, 3, I, P, Q} end
The four parameters V
, I
, P
, and Q
are the number of vertices, interpolation type, the polynomial degree, and the number of quadrature degree for an element implementation. For a given element topology, such as a three noded triangle in this example, V
will be fixed parameters while we allow for I
, P
, and Q
to be variable, i.e. have potentially different interpolation and quadrature rules on a triangular element. The number 3 above corresponds to the number of edges in the face topology.
We now need to define some methods for basics about the element toplogy such as number of vertices, edges, faces, etc.
TODO finish this part of the documention! TODO change nodal_coordinates
and surface_nodal_coordinates
to be vertex_coordinates
and surface_vertex_coordinates
respectively. TODO also surface_nodal_coordinates
is likely redundant.
The complete list of methods that need to be defined for a new abstract topology type include the following:
element_edge_nodes
element_face_nodes
element_interior_nodes
nodal_coordinates
num_edges
num_faces
num_interior_vertices
num_quadrature_points
num_vertices
num_vertices_per_edge
num_vertices_per_face
quadrature_points_and_weights
surface_element
surface_element_type
surface_nodal_coordinates
surface_quadrature_points_and_weights
Now we define a struct for specific implementations. Here we'll define the MyTri3 struct.
julia> struct MyTri3{I, Q} <: AbstractTri{3, I, 1, Q} end
julia> ReferenceFiniteElements.surface_element(e::MyTri3{Lagrange, Q}) where Q = Edge2{Lagrange, Q}()
julia> e = MyTri3{Lagrange, 1}()
MyTri3{Lagrange, 1}()
julia> ReferenceFiniteElements.num_edges(e)
3
Here we have fixed P
to be 1
respectively while I
and Q
are left as parametric types to correspond to a linear interpolation but with a generic interpolation and quadrature rule.
Now that we have a basic type to represent our element topology defined, we now need to implement our interpolation scheme. The methods needed for specific interpolation types include the following:
shape_function_values
shape_funciton_gradients
shape_function_hessians
Below is an example for the MyTri3
element above
function shape_function_value(e::MyTri3{Lagrange}, X, ξ, backend::ReferenceFiniteElements.ArrayBackend)
Ns = convert_to_vector(e, backend,
1. - ξ[1] - ξ[2],
ξ[1],
ξ[2]
)
return Ns
end
# output
shape_function_value (generic function with 1 method)
function shape_function_gradient(e::MyTri3{Lagrange}, X, ξ, backend::ReferenceFiniteElements.ArrayBackend)
Ns = convert_to_matrix(e, backend,
-1.,
1.,
0.,
#
-1.,
0.,
1.
)
return Ns
end
# output
shape_function_gradient (generic function with 1 method)
function shape_function_hessian(e::MyTri3{Lagrange}, X, ξ, backend::ReferenceFiniteElements.ArrayBackend)
Ns = convert_to_3d_array(e, backend,
0., 0.,
0., 0.,
0., 0.,
#
0., 0.,
0., 0.,
0., 0.
)
return Ns
end
# output
shape_function_hessian (generic function with 1 method)