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:
Jukka Aho
2025-11-09 03:29:36 +02:00
parent 065156b40a
commit 907ec0b183
4 changed files with 61 additions and 46 deletions
@@ -87,7 +87,9 @@ element = Element(Quad4, [1, 2, 3, 4])
@testset "Element Creation" begin
@test typeof(element.properties) == Quad4
@test element.connectivity == [1, 2, 3, 4]
# connectivity is now a tuple of UInt, not Vector{Int}
@test element.connectivity == (UInt(1), UInt(2), UInt(3), UInt(4))
@test collect(element.connectivity) == [1, 2, 3, 4] # Can still collect to vector
end
# ## Step 3: Update Element Fields