diff --git a/src/basis/abstract.jl b/src/basis/abstract.jl index 65ad2da..af73f5a 100644 --- a/src/basis/abstract.jl +++ b/src/basis/abstract.jl @@ -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