mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-18 01:31:31 +00:00
feat(basis): Add nbasis() function for compile-time basis function count
Add zero-cost nbasis(topology, basis) -> Int function that returns the number
of basis functions at compile time. This is essential for validating Ciarlet
triplet (K, P, Σ) consistency and enables type-stable pre-allocations.
Key features:
- Zero-cost: compiles to single constant return (ret i64 N)
- Instance-based dispatch: nbasis(Triangle{3}(), Lagrange{1}())
- Comprehensive documentation with compile-time verification examples
- Replaces removed ndofs() function with clearer semantics
The function is generated automatically by basis_generator.jl alongside
basis function implementations, ensuring consistency.
This commit is contained in:
+74
-19
@@ -120,6 +120,79 @@ Equivalent to `get_basis_derivatives(topology, basis, xi)[i]`.
|
||||
return get_basis_derivatives(topology, basis, xi)[i]
|
||||
end
|
||||
|
||||
"""
|
||||
nbasis(topology::AbstractTopology, basis::AbstractBasis) -> Int
|
||||
|
||||
Return the number of basis functions for the given basis on the given topology.
|
||||
|
||||
**Zero-cost:** This function compiles to a constant integer for concrete types.
|
||||
The return value is compile-time known, enabling type-stable allocations and
|
||||
constant propagation throughout assembly code.
|
||||
|
||||
# Arguments
|
||||
- `topology::AbstractTopology`: Element topology instance (e.g., `Triangle{3}()`, `Tetrahedron{10}()`)
|
||||
- `basis::AbstractBasis`: Basis instance (e.g., `Lagrange{1}()`, `Lagrange{2}()`)
|
||||
|
||||
# Returns
|
||||
- `Int`: Number of basis functions (compile-time constant for generated bases)
|
||||
|
||||
# Examples
|
||||
```julia
|
||||
# Standard nodal bases
|
||||
nbasis(Triangle{3}(), Lagrange{1}()) # 3 (linear triangle)
|
||||
nbasis(Triangle{6}(), Lagrange{2}()) # 6 (quadratic triangle)
|
||||
nbasis(Tetrahedron{10}(), Lagrange{2}()) # 10 (quadratic tetrahedron)
|
||||
|
||||
# Exotic elements (nodes ≠ basis functions)
|
||||
nbasis(Triangle{3}(), DKT()) # 9 (3 nodes but 9 DOFs!)
|
||||
|
||||
# Serendipity families
|
||||
nbasis(Quadrilateral{8}(), Serendipity{2}()) # 8 (reduced quadratic quad)
|
||||
```
|
||||
|
||||
# Implementation Notes
|
||||
|
||||
For generated bases (Lagrange, Serendipity), `nbasis` functions are automatically
|
||||
generated by `basis_generator.jl` alongside the basis function implementations.
|
||||
This ensures consistency: the generator knows exactly how many basis functions
|
||||
it produced, so `nbasis` always returns the correct value.
|
||||
|
||||
For custom/special bases (DKT, hierarchical, etc.), implement manually:
|
||||
```julia
|
||||
@inline nbasis(::Triangle{3}, ::DKT) = 9
|
||||
```
|
||||
|
||||
For truly dynamic bases (adaptive refinement), use instance-based dispatch:
|
||||
```julia
|
||||
@inline nbasis(::K, basis::HierarchicalBasis) where {K<:AbstractTopology} = basis.active_modes
|
||||
```
|
||||
|
||||
# Compile-Time Verification
|
||||
|
||||
You can verify zero-cost compilation:
|
||||
```julia
|
||||
@code_llvm nbasis(Triangle{3}(), Lagrange{1}())
|
||||
# Should show: ret i64 3 (direct constant return)
|
||||
|
||||
@code_native nbasis(Triangle{3}(), Lagrange{1}())
|
||||
# Should show: mov rax, 3; ret (single instruction)
|
||||
```
|
||||
|
||||
# Usage in Validation
|
||||
|
||||
The primary use case is validating the Ciarlet triplet (K, P, Σ):
|
||||
```julia
|
||||
function validate_dof_consistency(dof, K, P)
|
||||
n_dofs = ndofs(dof, K)
|
||||
n_basis = nbasis(K(), P()) # ← Zero-cost!
|
||||
@assert n_dofs == n_basis "Inconsistent element: \$n_basis basis ≠ \$n_dofs DOFs"
|
||||
end
|
||||
```
|
||||
|
||||
See also: [`get_basis_functions`](@ref), [`validate_dof_consistency`](@ref)
|
||||
"""
|
||||
function nbasis end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Deprecated bridge (old API names)
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -140,25 +213,7 @@ Provided temporarily for migration.
|
||||
"""
|
||||
function eval_dbasis! end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Degrees of freedom utility
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
ndofs(basis::AbstractBasis)
|
||||
ndofs(::Type{<:AbstractBasis})
|
||||
|
||||
Total degrees of freedom for this basis. For standard nodal bases, this is
|
||||
usually equal to the number of basis functions; specialized bases (e.g., plates)
|
||||
can override to return multiple DOFs per node.
|
||||
"""
|
||||
function ndofs end
|
||||
|
||||
# Default: basis implementations should override; no assumption about nnodes here.
|
||||
ndofs(::AbstractBasis) = error("ndofs not implemented for this basis")
|
||||
ndofs(::Type{<:AbstractBasis}) = error("ndofs not implemented for this basis type")
|
||||
|
||||
export AbstractBasis, Lagrange, Serendipity
|
||||
export ndofs
|
||||
export get_basis_functions, get_basis_derivatives, get_basis_function, get_basis_derivative
|
||||
export nbasis
|
||||
export eval_basis!, eval_dbasis!
|
||||
|
||||
Reference in New Issue
Block a user