mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-30 13:29:22 +00:00
refactor: Zero-allocation basis functions and immutable Element
MAJOR PERFORMANCE REFACTORING:
1. Shape functions return tuples instead of allocating vectors:
- eval_basis!(): Returns NTuple{N,T} directly (zero allocations)
- eval_dbasis!(): Returns NTuple{N,Vec{D}} directly (zero allocations)
- API boundary (get_basis/get_dbasis) still returns vectors for compat
2. Element is now immutable with compile-time known structure:
- connectivity: Vector{UInt} → NTuple{N,UInt}
- integration_points: Vector{IP} → NTuple{NIP,IP}
- Element{N,NIP,M,B} parametrized by connectivity/IP count
- Changed from 'mutable struct' to 'struct'
3. Helper function for immutability:
- with_integration_points(element, ips) returns new element
- get_integration_points() returns tuple directly
Benefits:
- Zero allocations in hot paths (basis evaluation)
- Compile-time sizes enable better optimization
- Type stability improvements
- Stack allocation instead of heap
Breaking changes:
- Element.connectivity is now tuple (use collect() for vector)
- Element is immutable (use with_integration_points for updates)
Tests: All 157 tests passing
This commit is contained in:
@@ -45,27 +45,28 @@ abstract type AbstractBasis{dim} end
|
||||
# This allows calling methods on both Seg2 and Seg2()
|
||||
Base.length(B::T) where {T<:AbstractBasis} = length(T)
|
||||
Base.size(B::T) where {T<:AbstractBasis} = size(T)
|
||||
eval_basis!(B::T, N, xi) where {T<:AbstractBasis} = eval_basis!(T, N, xi)
|
||||
eval_dbasis!(B::T, dN, xi) where {T<:AbstractBasis} = eval_dbasis!(T, dN, xi)
|
||||
# Updated signatures: eval_basis! and eval_dbasis! now return tuples
|
||||
eval_basis!(B::T, ::Type{U}, xi) where {T<:AbstractBasis,U} = eval_basis!(T, U, xi)
|
||||
eval_dbasis!(B::T, xi) where {T<:AbstractBasis} = eval_dbasis!(T, xi)
|
||||
|
||||
# Allocating versions (convenience wrappers)
|
||||
# Allocating versions (convenience wrappers) - now they just call and collect
|
||||
"""
|
||||
eval_basis(basis::AbstractBasis{dim}, xi) -> Vector{Float64}
|
||||
|
||||
Evaluate basis functions at point `xi`, allocating return vector.
|
||||
|
||||
See also: [`eval_basis!`](@ref) for non-allocating version.
|
||||
See also: [`eval_basis!`](@ref) for non-allocating version that returns tuple.
|
||||
"""
|
||||
eval_basis(B::AbstractBasis{dim}, xi) where {dim} = eval_basis!(B, zeros(length(B)), xi)
|
||||
eval_basis(B::AbstractBasis{dim}, ::Type{T}, xi) where {dim,T} = collect(eval_basis!(B, T, xi))
|
||||
|
||||
"""
|
||||
eval_dbasis(basis::AbstractBasis{dim}, xi) -> Vector{Vec{dim, Float64}}
|
||||
|
||||
Evaluate basis function derivatives at point `xi`, allocating return vector.
|
||||
|
||||
See also: [`eval_dbasis!`](@ref) for non-allocating version.
|
||||
See also: [`eval_dbasis!`](@ref) for non-allocating version that returns tuple.
|
||||
"""
|
||||
eval_dbasis(B::AbstractBasis{dim}, xi) where {dim} = eval_dbasis!(B, zeros(Vec{dim}, length(B)), xi)
|
||||
eval_dbasis(B::AbstractBasis{dim}, xi) where {dim} = collect(eval_dbasis!(B, xi))
|
||||
|
||||
# Declare interface functions (will be implemented by basis generator)
|
||||
function get_reference_element_coordinates end
|
||||
|
||||
+12
-16
@@ -148,15 +148,13 @@ function create_basis(name, description, X::Vector{<:Vecish{D,T}}, basis, dbasis
|
||||
N = length(X)
|
||||
@debug "create basis given basis functions and derivatives" name description X basis dbasis
|
||||
|
||||
Q = Expr(:block)
|
||||
for i = 1:N
|
||||
push!(Q.args, :(N[$i] = $(basis[i])))
|
||||
end
|
||||
# Build tuple expression for eval_basis! return: (N1, N2, N3, ...)
|
||||
basis_tuple_args = [basis[i] for i = 1:N]
|
||||
basis_tuple = Expr(:tuple, basis_tuple_args...)
|
||||
|
||||
V = Expr(:block)
|
||||
for i = 1:N
|
||||
push!(V.args, :(dN[$i] = Vec(float.(tuple($(dbasis[:, i]...))))))
|
||||
end
|
||||
# Build tuple expression for eval_dbasis! return: (dN1, dN2, dN3, ...)
|
||||
dbasis_tuple_args = [:(Vec(float.(tuple($(dbasis[:, i]...))))) for i = 1:N]
|
||||
dbasis_tuple = Expr(:tuple, dbasis_tuple_args...)
|
||||
|
||||
if D == 1
|
||||
unpack = :((u,) = xi)
|
||||
@@ -187,18 +185,16 @@ function create_basis(name, description, X::Vector{<:Vecish{D,T}}, basis, dbasis
|
||||
return $X
|
||||
end
|
||||
|
||||
@inline function eval_basis!(::Type{$name}, N::Vector{<:Number}, xi::Vec)
|
||||
@assert length(N) == $N
|
||||
# Return tuple directly - zero allocations!
|
||||
@inline function eval_basis!(::Type{$name}, ::Type{T}, xi::Vec) where T
|
||||
$unpack
|
||||
@inbounds $Q
|
||||
return N
|
||||
@inbounds return $basis_tuple
|
||||
end
|
||||
|
||||
@inline function eval_dbasis!(::Type{$name}, dN::Vector{<:Vec{$D}}, xi::Vec)
|
||||
@assert length(dN) == $N
|
||||
# Return NTuple{N,Vec{D}} directly - zero allocations!
|
||||
@inline function eval_dbasis!(::Type{$name}, xi::Vec)
|
||||
$unpack
|
||||
@inbounds $V
|
||||
return dN
|
||||
@inbounds return $dbasis_tuple
|
||||
end
|
||||
end
|
||||
return code
|
||||
|
||||
Reference in New Issue
Block a user