Function spaces
Generic
Bcube.AbstractFunctionSpaceType — Type
Abstract structure for the different types of function space, for instance the Lagrange function space, the Taylor function space etc.
Devs notes
All subtypes should implement the following functions:
get_ndofs(::AbstractFunctionSpace, ::AbstractShape)_scalar_shape_functions(::AbstractFunctionSpace, ::AbstractShape, ξ)idof_by_vertex(::AbstractFunctionSpace, ::AbstractShape)idof_by_edge(::AbstractFunctionSpace, ::AbstractShape)idof_by_edge_with_bounds(::AbstractFunctionSpace, ::AbstractShape)idof_by_face(::AbstractFunctionSpace, ::AbstractShape)idof_by_face_with_bounds(::AbstractFunctionSpace, ::AbstractShape)
Bcube.FunctionSpace — Method
FunctionSpace(fstype::Symbol, degree::Integer)
FunctionSpace(fstype::AbstractFunctionSpaceType, degree::Integer)Build a FunctionSpace of the designated FunctionSpaceType and degree.
Examples
julia> FunctionSpace(:Lagrange, 2)
FunctionSpace{Bcube.Lagrange{:Uniform}, 2}()Bcube.basis_functions_style — Method
basis_functions_style(fs::AbstractFunctionSpace)Return the style (modal or nodal) corresponding to the basis functions of the 'fs'.
Bcube.get_coords — Method
get_coords(fs::AbstractFunctionSpace,::AbstractShape)Return node coordinates in the reference space for associated function space and shape.
Bcube.get_degree — Method
get_degree(::AbstractFunctionSpace{type, degree}) where{type, degree}Return the degree associated to the AbstractFunctionSpace.
Bcube.get_ndofs — Method
get_ndofs(fs::AbstractFunctionSpace, shape::AbstractShape)Number of dofs associated to the given interpolation.
Bcube.get_type — Method
get_type(::AbstractFunctionSpace{type})Getter for the type of the AbstractFunctionSpace
Bcube.idof_by_edge — Method
idof_by_edge(::AbstractFunctionSpace, ::AbstractShape)Return the local indices of the dofs lying on each edge of the Shape.
Dofs lying on the edge vertices are excluded.
The result is a Tuple of arrays of integers. Arrays maybe be empty. See Lagrange interpolation for simple examples.
Bcube.idof_by_edge_with_bounds — Method
idof_by_edge_with_bounds(::AbstractFunctionSpace, ::AbstractShape)Return the local indices of the dofs lying on each edge of the Shape.
Dofs lying on the edge vertices are included.
The result is a Tuple of arrays of integers. Arrays maybe be empty. See Lagrange interpolation for simple examples.
Bcube.idof_by_face — Method
idof_by_face(::AbstractFunctionSpace, ::AbstractShape)Return the local indices of the dofs lying on each face of the Shape.
Dofs lying on the face edges are excluded, only "face-interior" dofs are considered.
The result is a Tuple of arrays of integers. Arrays maybe be empty. See Lagrange interpolation for simple examples.
Bcube.idof_by_face_with_bounds — Method
idof_by_face_with_bounds(::AbstractFunctionSpace, ::AbstractShape)Return the local indices of the dofs lying on each face of the Shape.
Dofs lying on the face edges are included
The result is a Tuple of arrays of integers. Arrays maybe be empty. See Lagrange interpolation for simple examples.
Bcube.idof_by_vertex — Method
idof_by_vertex(::AbstractFunctionSpace, ::AbstractShape)Return the local indices of the dofs lying on each vertex of the Shape.
Beware that we are talking about the Shape, not the EntityType. So 'interior' vertices of the EntityType are not taken into account for instance. See Lagrange interpolation for simple examples.
Bcube.shape_functions — Method
shape_functions(::AbstractFunctionSpace, ::Val{N}, shape::AbstractShape, ξ) where N
shape_functions(::AbstractFunctionSpace, shape::AbstractShape, ξ)Return the list of shape functions corresponding to a FunctionSpace and a Shape. N is the size of the finite element space (default: N=1 if the argument is not provided).
The result is a vector of all the shape functions evaluated at position ξ, and not a tuple of the different shape functions. This choice is optimal for performance.
Note : λ = ξ -> shape_functions(fs, shape, ξ); λ(ξ)[i] is faster than λ =shape_functions(fs, shape); λ[i](ξ)
Implementation
Default version, should be overriden for each concrete FunctionSpace.
Bcube.shape_functions_vec — Method
shape_functions_vec(fs::AbstractFunctionSpace, ::Val{N}, shape::AbstractShape, ξ) where {N}Return all the shape functions of FunctionSpace on a Shape evaluated in ξ as a vector.
N is the the size (number of components) of the finite element space.
shape_functions_vec(fs::AbstractFunctionSpace{T,D}, n::Val{N}, shape::AbstractShape) where {T,D, N}The shape functions are returned as a vector of functions.
Implementation
This is implementation is not always valid, but it is for Lagrange and Taylor spaces (the only two spaces available up to 20/01/23).
Bcube.∂λξ_∂ξ — Function
∂λξ_∂ξ(::AbstractFunctionSpace, ::Val{N}, shape::AbstractShape) where NGradient, with respect to the reference coordinate system, of shape functions for any function space. The result is an array whose elements are the gradient of each shape functions. N is the size of the finite element space.
Implementation
Default version using automatic differentiation. Specialize to increase performance.
Lagrange
Bcube._scalar_shape_functions — Method
shape_functions(::FunctionSpace{<:Lagrange, 1}, ::Tetra, ξ)Shape functions for Tetra Lagrange element of degree 1 in a 3D space.
\[\hat{\lambda}_1(\xi, \eta, \zeta) = (1 - \xi - \eta - \zeta) \hspace{1cm} \hat{\lambda}_2(\xi, \eta, \zeta) = \xi \hspace{1cm} \hat{\lambda}_3(\xi, \eta, \zeta) = \eta \hspace{1cm} \hat{\lambda}_5(\xi, \eta, \zeta) = \zeta \hspace{1cm}\]
Bcube.shape_functions — Method
shape_functions(::FunctionSpace{<:Lagrange}, :: Val{N}, ::AbstractShape, ξ) where {N}Implementation
For N > 1, the default version consists in "replicating" the shape functions. If shape_functions returns the vector [λ₁; λ₂; λ₃], and if the FESpace is of size 2, then this default behaviour consists in returning the matrix [λ₁ 0; λ₂ 0; λ₃ 0; 0 λ₁; 0 λ₂; 0 λ₃].
Triangle
Order 1
\[\hat{\lambda}_1(\xi, \eta) = 1 - \xi - \eta \hspace{1cm} \hat{\lambda}_2(\xi, \eta) = \xi \hspace{1cm} \hat{\lambda}_3(\xi, \eta) = \eta\]
Order 2
\[\begin{aligned} & \hat{\lambda}_1(\xi, \eta) = (1 - \xi - \eta)(1 - 2 \xi - 2 \eta) \\ & \hat{\lambda}_2(\xi, \eta) = \xi (2\xi - 1) \\ & \hat{\lambda}_3(\xi, \eta) = \eta (2\eta - 1) \\ & \hat{\lambda}_{12}(\xi, \eta) = 4 \xi (1 - \xi - \eta) \\ & \hat{\lambda}_{23}(\xi, \eta) = 4 \xi \eta \\ & \hat{\lambda}_{31}(\xi, \eta) = 4 \eta (1 - \xi - \eta) \end{aligned}\]
Prism
Order 1
\[\begin{aligned} \hat{\lambda}_1(\xi, \eta, \zeta) = (1 - \xi - \eta)(1 - \zeta)/2 \hspace{1cm} \hat{\lambda}_2(\xi, \eta, \zeta) = \xi (1 - \zeta)/2 \hspace{1cm} \hat{\lambda}_3(\xi, \eta, \zeta) = \eta (1 - \zeta)/2 \hspace{1cm} \hat{\lambda}_5(\xi, \eta, \zeta) = (1 - \xi - \eta)(1 + \zeta)/2 \hspace{1cm} \hat{\lambda}_6(\xi, \eta, \zeta) = \xi (1 + \zeta)/2 \hspace{1cm} \hat{\lambda}_7(\xi, \eta, \zeta) = \eta (1 + \zeta)/2 \hspace{1cm} \end{aligned}\]
Bcube.shape_functions_symbolic — Method
shape_functions_symbolic(fs::FunctionSpace{<:Lagrange, D}, ::Shape, ξ) where {D, Shape<:Line}
∂λξ_∂ξ_symbolic(fs::FunctionSpace{<:Lagrange, D}, ::Shape, ξ) where {D, Shape<:Line}Implementation
Based on Symbolic.jl. First tests show that this version is slower than the implementation based on meta when D is greater. Further investigations are needed to understand this behavior.
shape_functions_symbolic uses a "generated" function named _shape_functions_symbolic. The core of the generated function is an Expression that is created by __shape_functions_symbolic. This latter function uses the Symbolics package and the lagrange polynomials (defined in _lagrange_poly).
Bcube.∂λξ_∂ξ — Method
∂λξ_∂ξ(::FunctionSpace{<:Lagrange}, ::Val{1}, ::AbstractShape, ξ)Triangle
Order 0
\[\nabla \hat{\lambda}(\xi, \eta) = \begin{pmatrix} 0 \\ 0 \end{pmatrix}\]
Order 1
\[\begin{aligned} & \nabla \hat{\lambda}_1(\xi, \eta) = \begin{pmatrix} -1 \\ -1 \end{pmatrix} \\ & \nabla \hat{\lambda}_2(\xi, \eta) = \begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ & \nabla \hat{\lambda}_3(\xi, \eta) = \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ \end{aligned}\]
Order 2
\[\begin{aligned} & \nabla \hat{\lambda}_1(\xi, \eta) = \begin{pmatrix} -3 + 4 (\xi + \eta) \\ -3 + 4 (\xi + \eta) \end{pmatrix} \\ & \nabla \hat{\lambda}_2(\xi, \eta) = \begin{pmatrix} -1 + 4 \xi \\ 0 \end{pmatrix} \\ & \nabla \hat{\lambda}_3(\xi, \eta) = \begin{pmatrix} 0 \\ -1 + 4 \eta \end{pmatrix} \\ & \nabla \hat{\lambda}_{12}(\xi, \eta) = 4 \begin{pmatrix} 1 - 2 \xi - \eta \\ - \xi \end{pmatrix} \\ & \nabla \hat{\lambda}_{23}(\xi, \eta) = 4 \begin{pmatrix} \eta \\ \xi \end{pmatrix} \\ & \nabla \hat{\lambda}_{31}(\xi, \eta) = 4 \begin{pmatrix} - \eta \\ 1 - 2 \eta - \xi \end{pmatrix} \\ \end{aligned}\]
Square
Order 0
\[\nabla \hat{\lambda}(\xi, \eta) = \begin{pmatrix} 0 \\ 0 \end{pmatrix}\]
Taylor
The Taylor function space corresponds to a function space where functions are approximated by a Taylor series expansion of order $n$ in each cell:
\[ \forall x \in \Omega_i,~g(x) = g(x_0) + (x - x_0) g'(x_0) + o(x)\]
where $x_0$ is the cell center.
Note that a Taylor-P0 is strictly equivalent to a 1st-order Finite Volume discretization (beware that "order" can have different meaning depending on whether one refers to the order of the function space basis or the order of the discretization method).
Recall that any function space implies that any function $g$ is interpolated by $g(x) = \sum g_i \lambda_i(x)$ where $\lambda_i$ are the shape functions. For a Taylor expansion, the definition of $\lambda_i$ is not unique. For instance for the Taylor expansion of order $1$ on a 1D line above, we may be tempted to set $\lambda_1(x) = 1$ and $\lambda_2(x) = (x - x_0)$. If you do so, what are the corresponding shape functions in the reference element, the $\hat{\lambda_i}$? We immediately recover $\hat{\lambda_1}(\hat{x}) = 1$. For $\hat{\lambda_2}$:
\[ \hat{\lambda_2}(\hat{x}) = (\lambda \circ F)(\hat{x}) = (x \rightarrow x - x_0) \circ (\hat{x} \rightarrow \frac{x_r - x_l}{2} \hat{x} + \frac{x_r + x_l}{2}) = \frac{x_r - x_l}{2} \hat{x}\]
So if you set $\lambda_2(x) = (x - x_0)$ then $\hat{\lambda_2}$ depends on the element length ($\Delta x = x_r-x_l$), which is pointless. So $\lambda_2$ must be proportional to the element length to obtain a universal definition for $\hat{\lambda_2}$. For instance, we may choose $\lambda_2(x) = (x - x_0) / \Delta x$, leading to $\hat{\lambda_2}(\hat{x}) = \hat{x} / 2$. But we could have chosen an other element length multiple.
Don't forget that choosing $\lambda_2(x) = (x - x_0) / \Delta x$ leads to $g(x) = g(x_0) + \frac{x - x_0}{\Delta x} g'(x_0) Δx$ hence $g_2 = g'(x_0) Δx$ in the interpolation.
Bcube.shape_functions — Method
shape_functions(::FunctionSpace{<:Taylor}, ::AbstractShape, ξ)Implementation
For N > 1, the default version consists in "replicating" the shape functions. If shape_functions returns the vector [λ₁; λ₂; λ₃], and if the FESpace is of size 2, then this default behaviour consists in returning the matrix [λ₁ 0; λ₂ 0; λ₃ 0; 0 λ₁; 0 λ₂; 0 λ₃].
Any shape, order 0
$\hat{\lambda}(\xi) = 1$
Line
Order 1
\[\hat{\lambda}_1(\xi) = 1 \hspace{1cm} \hat{\lambda}_1(\xi) = \frac{\xi}{2}\]
Square
Order 1
\[\begin{aligned} & \hat{\lambda}_1(\xi, \eta) = 0 \\ & \hat{\lambda}_2(\xi, \eta) = \frac{\xi}{2} \\ & \hat{\lambda}_3(\xi, \eta) = \frac{\eta}{2} \end{aligned}\]
Bcube.∂λξ_∂ξ — Method
∂λξ_∂ξ(::FunctionSpace{<:Taylor}, ::Val{1}, ::AbstractShape, ξ)Line
Order 0
$\nabla \hat{\lambda}(\xi) = 0$
Order 1
\[\nabla \hat{\lambda}_1(\xi) = 0 \hspace{1cm} \nabla \hat{\lambda}_1(\xi) = \frac{1}{2}\]
Square
Order 0
\[\hat{\lambda}_1(\xi, \eta) = \begin{pmatrix} 0 \\ 0 \end{pmatrix}\]
Order 1
\[\begin{aligned} & \nabla \hat{\lambda}_1(\xi, \eta) = \begin{pmatrix} 0 \\ 0 \end{pmatrix} \\ & \nabla \hat{\lambda}_2(\xi, \eta) = \begin{pmatrix} \frac{1}{2} \\ 0 \end{pmatrix} \\ & \nabla \hat{\lambda}_3(\xi, \eta) = \begin{pmatrix} 0 \\ \frac{1}{2} \end{pmatrix} \end{aligned}\]