Interpolations
All Interpolation
s should subtype Interpolation{shape, order}
, where shape <: AbstractRefShape
is the reference shape for which the interpolation is defined and order
is the characteristic interpolation order. The how-to at bottom of this page describes how to implement a new interpolation.
Methods to be implemented for a new interpolation
Always
Ferrite.reference_shape_value
— Methodreference_shape_value(ip::Interpolation, ξ::Vec, i::Int)
Evaluate the value of the i
th shape function of the interpolation ip
at a point ξ
on the reference element. The index i
must match the index in vertices(::Interpolation)
, faces(::Interpolation)
and edges(::Interpolation)
.
For nodal interpolations the indices also must match the indices of reference_coordinates(::Interpolation)
.
Ferrite.reference_coordinates
— Methodreference_coordinates(ip::Interpolation)
Returns a vector of coordinates with length getnbasefunctions(::Interpolation)
and indices corresponding to the indices of a dof in vertices
, faces
and edges
.
Only required for nodal interpolations.
TODO: Separate nodal and non-nodal interpolations.
Ferrite.vertexdof_indices
— Methodvertexdof_indices(ip::Interpolation)
A tuple containing tuples of local dof indices for the respective vertex in local enumeration on a cell defined by vertices(::Cell)
. The vertex enumeration must match the vertex enumeration of the corresponding geometrical cell.
The dofs appearing in the tuple must be continuous and increasing! The first dof must be the 1, as vertex dofs are enumerated first.
Ferrite.facedof_indices
— Methodfacedof_indices(ip::Interpolation)
A tuple containing tuples of all local dof indices for the respective face in local enumeration on a cell defined by faces(::Cell)
. The face enumeration must match the face enumeration of the corresponding geometrical cell.
Ferrite.facedof_interior_indices
— Methodfacedof_interior_indices(ip::Interpolation)
A tuple containing tuples of the local dof indices on the interior of the respective face in local enumeration on a cell defined by faces(::Cell)
. The face enumeration must match the face enumeration of the corresponding geometrical cell. Note that the vertex and edge dofs are included here.
The dofs appearing in the tuple must be continuous and increasing! The first dof must be the computed via "last edge interior dof index + 1", if face dofs exist.
Ferrite.edgedof_indices
— Methodedgedof_indices(ip::Interpolation)
A tuple containing tuples of local dof indices for the respective edge in local enumeration on a cell defined by edges(::Cell)
. The edge enumeration must match the edge enumeration of the corresponding geometrical cell.
The dofs are guaranteed to be aligned with the local ordering of the entities on the oriented edge. Here the first entries are the vertex dofs, followed by the edge interior dofs.
Ferrite.edgedof_interior_indices
— Methodedgedof_interior_indices(ip::Interpolation)
A tuple containing tuples of the local dof indices on the interior of the respective edge in local enumeration on a cell defined by edges(::Cell)
. The edge enumeration must match the edge enumeration of the corresponding geometrical cell. Note that the vertex dofs are included here.
The dofs appearing in the tuple must be continuous and increasing! The first dof must be computed via "last vertex dof index + 1", if edge dofs exist.
Ferrite.volumedof_interior_indices
— Methodvolumedof_interior_indices(ip::Interpolation)
Tuple containing the dof indices associated with the interior of a volume.
The dofs appearing in the tuple must be continuous and increasing, volumedofs are enumerated last.
Ferrite.getnbasefunctions
— MethodFerrite.getnbasefunctions(ip::Interpolation)
Return the number of base functions for the interpolation ip
.
Ferrite.adjust_dofs_during_distribution
— Methodadjust_dofs_during_distribution(::Interpolation)
This function must return true
if the dofs should be adjusted (i.e. permuted) during dof distribution. This is in contrast to i) adjusting the dofs during reinit!
in the assembly loop, or ii) not adjusting at all (which is not needed for low order interpolations, generally).
For special interpolations
Discontinuous interpolations
For discontinuous interpolations, implementing the following methods might be required to apply Dirichlet boundary conditions.
Ferrite.is_discontinuous
— Methodis_discontinuous(::Interpolation)
is_discontinuous(::Type{<:Interpolation})
Checks whether the interpolation is discontinuous (i.e. DiscontinuousLagrange
)
Ferrite.dirichlet_vertexdof_indices
— Methoddirichlet_vertexdof_indices(ip::Interpolation)
A tuple containing tuples of local dof indices for the respective vertex in local enumeration on a cell defined by vertices(::Cell)
. The vertex enumeration must match the vertex enumeration of the corresponding geometrical cell. Used internally in ConstraintHandler
and defaults to vertexdof_indices(ip::Interpolation)
for continuous interpolation.
The dofs appearing in the tuple must be continuous and increasing! The first dof must be the 1, as vertex dofs are enumerated first.
Ferrite.dirichlet_facedof_indices
— Methoddirichlet_facedof_indices(ip::Interpolation)
A tuple containing tuples of all local dof indices for the respective face in local enumeration on a cell defined by faces(::Cell)
. The face enumeration must match the face enumeration of the corresponding geometrical cell. Used internally in ConstraintHandler
and defaults to facedof_indices(ip::Interpolation)
for continuous interpolation.
Ferrite.dirichlet_edgedof_indices
— Methoddirichlet_edgedof_indices(ip::Interpolation)
A tuple containing tuples of local dof indices for the respective edge in local enumeration on a cell defined by edges(::Cell)
. The edge enumeration must match the edge enumeration of the corresponding geometrical cell. Used internally in ConstraintHandler
and defaults to edgedof_indices(ip::Interpolation)
for continuous interpolation.
The dofs are guaranteed to be aligned with the local ordering of the entities on the oriented edge. Here the first entries are the vertex dofs, followed by the edge interior dofs.
Non-identity mapping
For interpolations that have a non-identity mapping (see Mapping of finite elements), the mapping type must be specified.
Ferrite.mapping_type
— Functionmapping_type(ip::Interpolation)
Get the type of mapping from the reference cell to the real cell for an interpolation ip
. Subtypes of ScalarInterpolation
and VectorizedInterpolation
return IdentityMapping()
, but other non-scalar interpolations may request different mapping types.
Ferrite.get_direction
— Functionget_direction(interpolation::Interpolation, shape_nr::Int, cell::AbstractCell)
Return the direction, ±1
, of the cell entity (e.g. facet or edge) associated with the interpolation
's shape function nr. shape_nr
. This is only required for interpolations with non-identity mappings, where the direction is required during the mapping of the shape values.
TODO: Move the following description to get_edge_direction
and get_face_direction
following #1162
The direction of entities are defined as following the node numbers of the entity's vertices, vnodes
. For an edge, vnodes[2] > vnodes[1]
implies positive direction.
For a face, we first find index, i
, of the smallest value in vnodes
. Considering circular indexing, then a positive face has vnodes[i-1] > vnodes[i+1]
.
The defaults should always work
The following functions are defined such that they should work for any interpolations that defines the required functions specified above.
Ferrite.getrefshape
— MethodFerrite.getrefshape(::Interpolation)::AbstractRefShape
Return the reference element shape of the interpolation.
Ferrite.getorder
— MethodFerrite.getorder(::Interpolation)
Return order of the interpolation.
Ferrite.reference_shape_gradient
— Methodreference_shape_gradient(ip::Interpolation, ξ::Vec, i::Int)
Evaluate the gradient of the i
th shape function of the interpolation ip
in reference coordinate ξ
.
Ferrite.reference_shape_gradient_and_value
— Methodreference_shape_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int)
Optimized version combining the evaluation Ferrite.reference_shape_value(::Interpolation)
and Ferrite.reference_shape_gradient(::Interpolation)
.
Ferrite.reference_shape_hessian_gradient_and_value
— Methodreference_shape_hessian_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int)
Optimized version combining the evaluation Ferrite.reference_shape_value(::Interpolation)
, Ferrite.reference_shape_gradient(::Interpolation)
, and the gradient of the latter.
Ferrite.boundarydof_indices
— Functionboundarydof_indices(::Type{<:BoundaryIndex})
Helper function to generically dispatch on the correct dof sets of a boundary entity.
Ferrite.dirichlet_boundarydof_indices
— Functiondirichlet_boundarydof_indices(::Type{<:BoundaryIndex})
Helper function to generically dispatch on the correct dof sets of a boundary entity. Used internally in ConstraintHandler
and defaults to boundarydof_indices(ip::Interpolation)
for continuous interpolation.
Ferrite.reference_shape_values!
— Functionreference_shape_values!(values::AbstractArray{T}, ip::Interpolation, ξ::Vec)
Evaluate all shape functions of ip
at once at the reference point ξ
and store them in values
.
Ferrite.reference_shape_gradients!
— Functionreference_shape_gradients!(gradients::AbstractArray, ip::Interpolation, ξ::Vec)
Evaluate all shape function gradients of ip
at once at the reference point ξ
and store them in gradients
.
Ferrite.reference_shape_gradients_and_values!
— Functionreference_shape_gradients_and_values!(gradients::AbstractArray, values::AbstractArray, ip::Interpolation, ξ::Vec)
Evaluate all shape function gradients and values of ip
at once at the reference point ξ
and store them in values
.
Ferrite.reference_shape_hessians_gradients_and_values!
— Functionreference_shape_hessians_gradients_and_values!(hessians::AbstractVector, gradients::AbstractVector, values::AbstractVector, ip::Interpolation, ξ::Vec)
Evaluate all shape function hessians, gradients and values of ip
at once at the reference point ξ
and store them in hessians
, gradients
, and values
.
Ferrite.shape_value_type
— Methodshape_value_type(ip::Interpolation, ::Type{T}) where T<:Number
Return the type of shape_value(ip::Interpolation, ξ::Vec, ib::Int)
.
How to implement a new interpolation
The API for implementing a new interpolation is not fully stable yet.
As an example, we will implement a Q
uadratic T
riangle I
nterpolation (QTI
), corresponding to Lagrange{RefTriangle, 2}()
. Since this interpolation gives scalar-valued shape functions, it is a subtype of ScalarInterpolation
,
struct QTI <: ScalarInterpolation{RefTriangle, 2}
end
Before we jump in and define the shape functions associated with this interpolation, we first need to consider the RefTriangle
entities,
Ferrite.RefTriangle
— TypeRefTriangle <: AbstractRefShape{2}
Reference triangle, reference dimension 2.
----------------+--------------------
Vertex numbers: | Vertex coordinates:
2 |
| \ | v1: 𝛏 = (1.0, 0.0)
| \ | v2: 𝛏 = (0.0, 1.0)
ξ₂^ | \ | v3: 𝛏 = (0.0, 0.0)
| 3-------1 |
+--> ξ₁ |
----------------+--------------------
Edge numbers: | Edge identifiers:
+ |
| \ | e1: (v1, v2)
2 1 | e2: (v2, v3)
| \ | e3: (v3, v1)
+---3---+ |
----------------+--------------------
Face numbers: | Face identifiers:
+ |
| \ |
| \ | f1: (v1, v2, v3)
| 1 \ |
+-------+ |
----------------+--------------------
For this particular interpolation, we have one degree of freedom associated with each vertex, and one degree of freedom associated with each edge. Following the Ferrite numbering rules, we start by enumerating the vertices first, followed by the edges. The numbering is based on the RefTriangle
shown above, and the actual shape functions are taken from defelement.org.
function Ferrite.reference_shape_value(ip::QTI, ξ::Vec{2}, shape_number::Int)
ξ₁ = ξ[1]
ξ₂ = ξ[2]
γ = 1 - ξ₁ - ξ₂ # Helper
shape_number == 1 && return ξ₁ * (2ξ₁ - 1) # v1: 1 at ξ = (1, 0)
shape_number == 2 && return ξ₂ * (2ξ₂ - 1) # v2: 1 at ξ = (0, 1)
shape_number == 3 && return γ * (2γ - 1) # v3: 1 at ξ = (0, 0)
shape_number == 4 && return 4ξ₁ * ξ₂ # e1: 1 at ξ = (½, ½)
shape_number == 5 && return 4ξ₂ * γ # e2: 1 at ξ = (0, ½)
shape_number == 6 && return 4ξ₁ * γ # e3: 1 at ξ = (½, 0)
throw(ArgumentError("no shape function $shape_number for interpolation $ip"))
end
Having defined the actual interpolation function, we must now provide the information in comments above to Ferrite. We start by providing the reference coordinate for each shape number,
function Ferrite.reference_coordinates(::QTI)
return [
Vec{2, Float64}((1.0, 0.0)), # v1
Vec{2, Float64}((0.0, 1.0)), # v2
Vec{2, Float64}((0.0, 0.0)), # v3
Vec{2, Float64}((0.5, 0.5)), # center of e1
Vec{2, Float64}((0.0, 0.5)), # center of e2
Vec{2, Float64}((0.5, 0.0)), # center of e3
]
end
We move on to defining which dof indices belong to each vertex. As we have 3 vertices on the RefTriangle
, this should be a tuple with length 3, and each element contains all dof indices for the particular vertex. In this case, we only have a single dof per vertex,
Ferrite.vertexdof_indices(::QTI) = ((1,), (2,), (3,))
Note that the dof index numbering must be in order, first all in the first vertex, then all in the next vertex, and so on. (If we had two indices per vertex, we would have ((1,2), (3,4), (5,6))
). Next, we number the edge dofs, following the same convention, matching how we defined it above,
Ferrite.edgedof_interior_indices(::QTI) = ((4,), (5,), (6,))
Ferrite.edgedof_indices(::QTI) = ((1, 2, 4,), (2, 3, 5,), (3, 1, 6,))
But here we need two functions, one for the interior
indices (those that have not yet been included in lower-dimensional entities (vertices in this case)), and one for all indices for dofs that belong to the edge.
For the triangle, we only have a single face. However, all the dofs that belong to the face, also belongs to either the vertices or edges, hence we have no "interior" face dofs. So we get,
Ferrite.facedof_interior_indices(::QTI) = ((),)
Ferrite.facedof_indices(::QTI) = ((1, 2, 3, 4, 5, 6),)
Finally, since this is a 2d element, we have no volumedofs
, and thus
Ferrite.volumedof_interior_indices(::QTI) = ()
It is necessary to tell Ferrite the total number of base functions, e.g.,
Ferrite.getnbasefunctions(::QTI) = 6
For distributing the degrees of freedom, higher order interpolations require that we account for the ordering on their entity. For example, if we have two interior dofs associated with an edge, we must match them the edges of the for the cells that share the edge, to make sure we consider the same ordering. Since we only have a single interior dof per edge, we don't need to adjust these, hence,
Ferrite.adjust_dofs_during_distribution(::QTI) = false
The function test_interpolation_properties
in test/test_interpolations.jl
can be used when implementation to check that some basic properties are fullfilled.