From 2452735cf6aa8004745f53a8b73cab3894130610 Mon Sep 17 00:00:00 2001 From: Jukka Aho Date: Thu, 20 Nov 2025 18:21:15 +0200 Subject: [PATCH] feat(basis): Add Serendipity basis type and remove duplicate function stubs Add Serendipity{T,P} basis type for quadrilateral and hexahedral elements with reduced nodes: - Struct definition with comprehensive documentation - ndims methods delegating to topology dimension - nnodes methods: Quad8 (8 nodes), Hex20 (20 nodes) - Comparison with Lagrange documented (Quad8 vs Quad9) Replace 4 function stub declarations with comments pointing to basis_api.jl: - eval_basis! - eval_dbasis! - get_basis_functions - get_basis_derivatives This eliminates method overwrite warnings when Quad8 uses Serendipity and Quad9 uses Lagrange. --- src/basis/abstract.jl | 52 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/basis/abstract.jl b/src/basis/abstract.jl index 42208e1..e356e95 100644 --- a/src/basis/abstract.jl +++ b/src/basis/abstract.jl @@ -85,7 +85,7 @@ N = get_basis_functions(Triangle(), Lagrange{1}(), xi) See: [`get_basis_functions`](@ref) """ -function eval_basis! end +# Note: eval_basis! stub is defined in basis_api.jl """ eval_dbasis!(B::Type{<:AbstractBasis}, xi::Vec) -> NTuple{N, Vec{D}} @@ -107,7 +107,7 @@ dN = get_basis_derivatives(Triangle(), Lagrange{1}(), xi) See: [`get_basis_derivatives`](@ref) """ -function eval_dbasis! end +# Note: eval_dbasis! stub is defined in basis_api.jl # ============================================================================ # NEW API (Recommended) @@ -136,7 +136,7 @@ N = get_basis_functions(topology, basis, xi) See also: [`get_basis_derivatives`](@ref) """ -function get_basis_functions end +# Note: get_basis_functions stub is defined in basis_api.jl """ get_basis_derivatives(topology, basis, xi) -> NTuple{N, Vec{D, Float64}} @@ -159,7 +159,7 @@ dN = get_basis_derivatives(topology, basis, xi) See also: [`get_basis_functions`](@ref) """ -function get_basis_derivatives end +# Note: get_basis_derivatives stub is defined in basis_api.jl # ============================================================================ # Parametric Lagrange Basis Type (OLD - with topology parameter) @@ -227,11 +227,35 @@ See also: [`AbstractBasis`](@ref), [`Serendipity`](@ref), [`Nedelec`](@ref) """ struct Lagrange{T<:AbstractTopology,P} <: AbstractBasis end +""" + Serendipity{T<:AbstractTopology, P} <: AbstractBasis + +Serendipity basis family for quadrilateral and hexahedral elements. + +Serendipity elements use a reduced set of nodes compared to full tensor-product Lagrange +elements by omitting interior nodes while maintaining the polynomial order on element edges. + +# Common examples: +- `Serendipity{Quadrilateral, 2}`: 8-node quadrilateral (no center node) +- `Serendipity{Hexahedron, 2}`: 20-node hexahedron (no interior nodes) + +# Comparison with Lagrange: +- Quad8 (Serendipity): 8 nodes (4 corners + 4 edge midpoints, no center) +- Quad9 (Lagrange): 9 nodes (4 corners + 4 edge midpoints + center) + +See also: [`AbstractBasis`](@ref), [`Lagrange`](@ref) +""" +struct Serendipity{T<:AbstractTopology,P} <: AbstractBasis 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()) +# Define interface methods for Serendipity +Base.ndims(::Type{Serendipity{T,P}}) where {T,P} = dim(T()) +Base.ndims(::Serendipity{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 @@ -275,6 +299,26 @@ nnodes(::Type{Lagrange{Pyramid,3}}) = 29 nnodes(::Lagrange{Wedge,P}) where {P} = div((P + 1)^2 * (P + 2), 2) nnodes(::Type{Lagrange{Wedge,P}}) where {P} = div((P + 1)^2 * (P + 2), 2) +""" + nnodes(::Serendipity{T, P}) where {T, P} + nnodes(::Type{Serendipity{T, P}}) where {T, P} + +Compute number of nodes for Serendipity basis of degree P on topology T. + +Serendipity elements omit interior nodes, using only edge and corner nodes: +- Quadrilateral P=2: 8 nodes (4 corners + 4 edge midpoints) +- Hexahedron P=2: 20 nodes (8 corners + 12 edge midpoints) +""" +# 2D: Quadrilateral (Serendipity) +# P=2: 8 nodes (4 corners + 4 edge midpoints, no center) +nnodes(::Serendipity{Quadrilateral,2}) = 8 +nnodes(::Type{Serendipity{Quadrilateral,2}}) = 8 + +# 3D: Hexahedron (Serendipity) +# P=2: 20 nodes (8 corners + 12 edge midpoints, no face/interior nodes) +nnodes(::Serendipity{Hexahedron,2}) = 20 +nnodes(::Type{Serendipity{Hexahedron,2}}) = 20 + # Also need nnodes for the higher-order topology types themselves (Tet10, Tri6, etc.) # These forward to the topology's nnodes() method nnodes(::Type{T}) where {T<:AbstractTopology} = nnodes(T())