perf(materials): Use @generated for zero-allocation elasticity tensor

Convert elasticity_tensor() to compile-time generation.

Before (672 bytes in test context):
- Runtime array comprehension for 81 tensor components
- Tuple conversion caused allocations
- Type instability from generic Tensor{4,3} constructor

After (0 bytes):
- @generated function pre-computes all 81 components at compile time
- Returns concrete Tensor{4,3,Float64,81} type
- Zero runtime allocations

Algorithm:
- Compute symbolic expressions for C_{ijkl} at compile time
- Generate optimized code with only λ_val, μ_val runtime parameters
- Tensor construction happens entirely at compile time

Result: 672 bytes → 0 bytes (100% reduction)

Note: This was part of the optimization but not the primary fix.
The main issue was ips::Any type instability in ElementCache.
This commit is contained in:
Jukka Aho
2025-11-18 20:47:11 +02:00
parent 1539585964
commit 7ec65dd1bf
+31 -10
View File
@@ -209,17 +209,38 @@ C = elasticity_tensor(material)
Returns non-symmetric Tensor{4,3} for indexing convenience in assembly.
The tensor has minor and major symmetries: C_{ijkl} = C_{jikl} = C_{ijlk} = C_{klij}
"""
function elasticity_tensor(material::LinearElastic)
# Lamé parameters
λ_val = λ(material)
μ_val = μ(material)
# Kronecker delta
@generated function elasticity_tensor(material::LinearElastic)
# Generate tensor construction at compile time for zero allocations
# C_{ijkl} = λ δ_{ij} δ_{kl} + μ (δ_{ik} δ_{jl} + δ_{il} δ_{jk})
δ(i, j) = i == j ? 1.0 : 0.0
# Build tensor: C_{ijkl} = λ δ_{ij} δ_{kl} + μ (δ_{ik} δ_{jl} + δ_{il} δ_{jk})
C_ijkl = [(λ_val * δ(i, j) * δ(k, l) + μ_val * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k)))
for i in 1:3, j in 1:3, k in 1:3, l in 1:3]
# Pre-compute symbolic expressions for all 81 components
exprs = []
for i in 1:3, j in 1:3, k in 1:3, l in 1:3
if δ(i,j) != 0.0 && δ(k,l) != 0.0
# Has λ term
if δ(i,k) != 0.0 && δ(j,l) != 0.0
# λ + 2μ (diagonal component)
push!(exprs, :(λ_val + 2*μ_val))
else
# λ only (off-diagonal coupling)
push!(exprs, :(λ_val))
end
elseif δ(i,k) != 0.0 && δ(j,l) != 0.0 && i != j
# μ (shear component)
push!(exprs, :(μ_val))
elseif δ(i,l) != 0.0 && δ(j,k) != 0.0 && i != j
# μ (shear component, swapped indices)
push!(exprs, :(μ_val))
else
# Zero
push!(exprs, :(0.0))
end
end
return Tensor{4,3}(tuple(C_ijkl...))
return quote
λ_val = λ(material)
μ_val = μ(material)
Tensor{4,3,Float64,81}(($(exprs...),))
end
end