feat(basis): Add Lagrange{Topology, P} parametric basis type

- Added Lagrange{T<:AbstractTopology, P} struct for parametric basis
- Implemented nnodes() formulas for all 7 topologies:
  - Segment: P+1 nodes
  - Triangle: (P+1)(P+2)/2 nodes (simplex formula)
  - Quadrilateral: (P+1)² nodes (tensor product)
  - Tetrahedron: (P+1)(P+2)(P+3)/6 nodes (simplex formula)
  - Hexahedron: (P+1)³ nodes (tensor product)
  - Pyramid: hardcoded for P=1,2,3 (no simple formula)
  - Wedge: (P+1)²(P+2)/2 nodes (triangle × segment)
- Added comprehensive documentation with examples
- Exported Lagrange and nnodes
- Node count now comes from basis, not topology
This commit is contained in:
Jukka Aho
2025-11-09 09:28:45 +02:00
parent dcc7f69a75
commit 9f42575cf2
+105
View File
@@ -72,3 +72,108 @@ eval_dbasis(B::AbstractBasis{dim}, xi) where {dim} = collect(eval_dbasis!(B, xi)
function get_reference_element_coordinates end
function eval_basis! end
function eval_dbasis! end
# ============================================================================
# Parametric Lagrange Basis Type
# ============================================================================
"""
Lagrange{T<:AbstractTopology, P} <: AbstractBasis
Parametric Lagrange basis functions for topology `T` with polynomial degree `P`.
The node count is automatically derived from the topology and polynomial degree:
- `Lagrange{Triangle, 1}`: P1 → 3 nodes (vertices)
- `Lagrange{Triangle, 2}`: P2 → 6 nodes (vertices + edge midpoints)
- `Lagrange{Quadrilateral, 1}`: Q1 → 4 nodes (corners)
- `Lagrange{Quadrilateral, 2}`: Q2 → 9 nodes (full tensor product)
- `Lagrange{Tetrahedron, 1}`: P1 → 4 nodes (vertices)
- `Lagrange{Tetrahedron, 2}`: P2 → 10 nodes (vertices + edge midpoints)
- `Lagrange{Hexahedron, 1}`: Q1 → 8 nodes (corners)
- `Lagrange{Hexahedron, 2}`: Q2 → 27 nodes (full tensor product)
# Type Parameters
- `T`: Topology type (Triangle, Quadrilateral, Tetrahedron, Hexahedron, etc.)
- `P`: Polynomial degree (1 = linear, 2 = quadratic, 3 = cubic, ...)
# Mathematical Background
Lagrange basis functions satisfy the cardinal property:
```
Nᵢ(xⱼ) = δᵢⱼ (Kronecker delta)
```
where `xⱼ` are the interpolation nodes.
For polynomial degree P:
- 1D: P+1 nodes
- Triangle: (P+1)(P+2)/2 nodes
- Quadrilateral: (P+1)² nodes (tensor product)
- Tetrahedron: (P+1)(P+2)(P+3)/6 nodes
- Hexahedron: (P+1)³ nodes (tensor product)
# Example
```julia
# Linear triangle (P1)
basis = Lagrange{Triangle, 1}()
nnodes(basis) # → 3
# Quadratic triangle (P2)
basis = Lagrange{Triangle, 2}()
nnodes(basis) # → 6
# Bilinear quadrilateral (Q1)
basis = Lagrange{Quadrilateral, 1}()
nnodes(basis) # → 4
# Biquadratic quadrilateral (Q2)
basis = Lagrange{Quadrilateral, 2}()
nnodes(basis) # → 9
```
See also: [`AbstractBasis`](@ref), [`Serendipity`](@ref), [`Nedelec`](@ref)
"""
# Lagrange inherits from AbstractBasis but dimension is determined by topology
# We cannot compute dim(T()) at type definition time, so we use methods instead
struct Lagrange{T<:AbstractTopology,P} end
# Define interface methods for Lagrange
# Dimension comes from topology
Base.ndims(::Type{Lagrange{T,P}}) where {T,P} = dim(T())
Base.ndims(::Lagrange{T,P}) where {T,P} = dim(T())
# Node count formulas for different topologies and polynomial degrees
# These replace the hardcoded node counts in old Tri3, Quad4, etc. types
"""
nnodes(::Lagrange{T, P}) where {T, P}
Compute number of nodes for Lagrange basis of degree P on topology T.
"""
# 1D: Segment
nnodes(::Lagrange{Segment,P}) where {P} = P + 1
# 2D: Triangle (simplex)
nnodes(::Lagrange{Triangle,P}) where {P} = div((P + 1) * (P + 2), 2)
# 2D: Quadrilateral (tensor product)
nnodes(::Lagrange{Quadrilateral,P}) where {P} = (P + 1)^2
# 3D: Tetrahedron (simplex)
nnodes(::Lagrange{Tetrahedron,P}) where {P} = div((P + 1) * (P + 2) * (P + 3), 6)
# 3D: Hexahedron (tensor product)
nnodes(::Lagrange{Hexahedron,P}) where {P} = (P + 1)^3
# 3D: Pyramid (mixed)
# Pyramids don't follow a simple formula, so hardcode for known degrees
nnodes(::Lagrange{Pyramid,1}) = 5
nnodes(::Lagrange{Pyramid,2}) = 13
nnodes(::Lagrange{Pyramid,3}) = 29
# 3D: Wedge/Prism (triangle × segment tensor product)
nnodes(::Lagrange{Wedge,P}) where {P} = div((P + 1)^2 * (P + 2), 2)
# Export the new parametric type and node count function
export Lagrange, nnodes