mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-17 09:12:09 +00:00
f223251fc0
New file: src/assemblers/material_cache.jl (247 lines)
Features:
- Parametric MaterialStateCache{StateType}
- Stores stress tensors (σ)
- Stores tangent modulus tensors (𝔻)
- Stores material state history (state, state_new)
- update_material_cache! function
State management:
- EmptyState for stateless materials (LinearElastic)
- Custom state types for plasticity (J2PlasticityState, etc.)
- State evolution tracked across load increments
Type parameter:
- StateType: Material state type (EmptyState, J2PlasticityState, etc.)
- Enables type-stable state access
Also includes ImmutableMaterialStateCache for read-only views
with @inline accessor functions.
248 lines
8.2 KiB
Julia
248 lines
8.2 KiB
Julia
# This file is a part of JuliaFEM.
|
||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||
|
||
"""
|
||
Material state cache implementations for zero-allocation assembly.
|
||
|
||
Contains mutable (MaterialStateCache) and immutable (ImmutableMaterialStateCache) variants.
|
||
"""
|
||
|
||
using Tensors
|
||
|
||
"""
|
||
MaterialStateCache{M<:AbstractMaterialState}
|
||
|
||
Workspace for material state at all integration points.
|
||
|
||
Contains pre-allocated arrays for stress, tangent, and internal state.
|
||
Mutated per element during assembly.
|
||
|
||
# Type Parameter
|
||
- `M`: Material state type (EmptyState for stateless, PlasticityState for plastic, etc.)
|
||
|
||
# Fields
|
||
- `σ::Vector{SymmetricTensor{2,3,Float64,6}}`: Stress at each IP [max_nips]
|
||
- `𝔻::Vector{SymmetricTensor{4,3,Float64,36}}`: Tangent modulus at each IP [max_nips]
|
||
- `states::Vector{M}`: Internal state at each IP [max_nips]
|
||
|
||
# Zero-Allocation Usage
|
||
Arrays are mutated in-place during `update_material_cache!` - no heap allocation.
|
||
|
||
# Examples
|
||
```julia
|
||
# Stateless material (elastic)
|
||
mat_cache = MaterialStateCache{EmptyState}(...)
|
||
|
||
# Stateful material (plasticity)
|
||
mat_cache = MaterialStateCache{PlasticityState}(...)
|
||
```
|
||
"""
|
||
struct MaterialStateCache{M<:AbstractMaterialState} <: AbstractMaterialStateCache{M}
|
||
σ::Vector{SymmetricTensor{2,3,Float64,6}} # Stress [NIP] (6 independent components)
|
||
𝔻::Vector{SymmetricTensor{4,3,Float64,36}} # Tangent [NIP] (36 independent components)
|
||
states::Vector{M} # State [NIP]
|
||
end
|
||
|
||
"""
|
||
ImmutableMaterialStateCache{M,NIP}
|
||
|
||
Immutable material state cache using NTuple for zero-allocation access.
|
||
|
||
Unlike `MaterialStateCache`, this version:
|
||
- Uses `NTuple` instead of `Vector` (stack-allocated, no heap access)
|
||
- Is immutable (must create new instance per element)
|
||
- Has **zero allocations** during cache access
|
||
- Enables full compiler optimization (sizes known at compile time)
|
||
|
||
# Type Parameters
|
||
- `M`: Material state type (EmptyState for stateless)
|
||
- `NIP`: Number of integration points (compile-time constant)
|
||
|
||
# Fields
|
||
- `σ::NTuple{NIP, SymmetricTensor{2,3,Float64,6}}`: Stress at each IP
|
||
- `𝔻::NTuple{NIP, SymmetricTensor{4,3,Float64,36}}`: Tangent modulus at each IP
|
||
- `states::NTuple{NIP, M}`: Internal state at each IP
|
||
|
||
# Zero-Allocation Access
|
||
|
||
```julia
|
||
# Indexing is zero-allocation:
|
||
tangent = cache.𝔻[q] # 0 bytes!
|
||
stress = cache.σ[q] # 0 bytes!
|
||
```
|
||
|
||
# Performance
|
||
|
||
**Eliminates type instability** from `Vector` indexing:
|
||
- Before: `𝔻::SYMMETRICTENSOR{4, 3, FLOAT64}` (UPPERCASE = unstable)
|
||
- After: `𝔻::SymmetricTensor{4, 3, Float64}` (lowercase = concrete)
|
||
|
||
**Pros:**
|
||
- Zero allocations during access
|
||
- Full compile-time type inference
|
||
- Stack-allocated (no GC pressure)
|
||
|
||
**Cons:**
|
||
- Immutable (must create new instance per element)
|
||
- Cannot be reused across elements
|
||
|
||
# Usage
|
||
|
||
```julia
|
||
# Create new cache per element:
|
||
material_cache = create_material_cache(
|
||
ImmutableMaterialStateCache,
|
||
geometry_cache, material, element_cache
|
||
)
|
||
|
||
# Then use normally in compute_block!:
|
||
K_kl = compute_block!(geometry_cache, material_cache, k, l)
|
||
```
|
||
"""
|
||
struct ImmutableMaterialStateCache{M<:AbstractMaterialState,NIP} <: AbstractMaterialStateCache{M}
|
||
σ::NTuple{NIP,SymmetricTensor{2,3,Float64,6}} # 6 independent components for 2nd order symmetric
|
||
𝔻::NTuple{NIP,SymmetricTensor{4,3,Float64,36}} # 36 independent components for 4th order symmetric
|
||
states::NTuple{NIP,M}
|
||
end
|
||
|
||
"""
|
||
reset!(cache::MaterialStateCache{M}) where M
|
||
|
||
Reset material state cache to zero values.
|
||
|
||
# Side Effects
|
||
Mutates all arrays in cache to zero.
|
||
"""
|
||
function reset!(cache::MaterialStateCache{M}) where M
|
||
fill!(cache.σ, zero(SymmetricTensor{2,3,Float64,6}))
|
||
fill!(cache.𝔻, zero(SymmetricTensor{4,3,Float64,36}))
|
||
# Don't reset states - they may have non-zero initial values
|
||
return nothing
|
||
end
|
||
|
||
# ============================================================================
|
||
# CONSTRUCTORS
|
||
# ============================================================================
|
||
|
||
"""
|
||
create_material_cache(material::M, max_nips::Int) -> MaterialStateCache{S}
|
||
where {M <: AbstractMaterial}
|
||
|
||
Create pre-allocated material state workspace with type-stable state type.
|
||
|
||
Uses `state_type(M)` trait to determine concrete state type at compile time,
|
||
ensuring full type stability and zero allocations.
|
||
|
||
# Arguments
|
||
- `material`: Material model (type M determines state type S)
|
||
- `max_nips`: Maximum integration points per element
|
||
|
||
# Returns
|
||
- `MaterialStateCache{EmptyState}` for stateless materials (e.g., LinearElastic)
|
||
- `MaterialStateCache{PlasticityState}` for J2 plasticity (e.g., PerfectPlasticity)
|
||
- `MaterialStateCache{S}` for other stateful materials with state type S
|
||
|
||
# Type Stability
|
||
Return type is fully inferrable:
|
||
- `M` is concrete material type (known at compile time)
|
||
- `S = state_type(M)` is concrete state type (trait dispatch)
|
||
- `MaterialStateCache{S}` is concrete return type
|
||
- **Zero allocations** in hot loops!
|
||
|
||
# Examples
|
||
```julia
|
||
# Stateless material
|
||
mat = LinearElastic(E=210e9, ν=0.3)
|
||
cache = create_material_cache(mat, 8) # MaterialStateCache{EmptyState}
|
||
|
||
# Stateful material
|
||
mat = PerfectPlasticity(E=210e9, ν=0.3, σ_y=250e6)
|
||
cache = create_material_cache(mat, 8) # MaterialStateCache{PlasticityState}
|
||
```
|
||
"""
|
||
function create_material_cache(material::M, max_nips::Int) where M<:AbstractMaterial
|
||
σ = [zero(SymmetricTensor{2,3,Float64,6}) for _ in 1:max_nips]
|
||
𝔻 = [zero(SymmetricTensor{4,3,Float64,36}) for _ in 1:max_nips]
|
||
|
||
# Get state type via trait (compile-time constant)
|
||
S = state_type(M)
|
||
states = [zero(S) for _ in 1:max_nips]
|
||
|
||
return MaterialStateCache{S}(σ, 𝔻, states)
|
||
end
|
||
|
||
"""
|
||
create_material_cache(
|
||
::Type{ImmutableMaterialStateCache},
|
||
geometry_cache::ImmutableGeometryCache{N,NIP},
|
||
material::AbstractMaterial,
|
||
element_cache::ElementCache
|
||
) -> ImmutableMaterialStateCache{M,NIP}
|
||
|
||
Create immutable material state cache with NTuple fields (zero allocations).
|
||
|
||
# Process
|
||
1. Compute stress/tangent at all integration points
|
||
2. Convert Vectors to NTuples (compile-time sizes)
|
||
3. Return immutable cache
|
||
|
||
# Zero-Allocation Benefits
|
||
|
||
Unlike mutable `MaterialStateCache`, this version:
|
||
- Uses NTuple (stack-allocated, no heap access)
|
||
- Enables full compiler optimization (sizes known at compile time)
|
||
- Eliminates type instability from Vector indexing
|
||
|
||
# Example
|
||
|
||
```julia
|
||
geometry_cache = create_geometry_cache(
|
||
ImmutableGeometryCache, element_cache, kernel, elem_id, mesh
|
||
)
|
||
material_cache = create_material_cache(
|
||
ImmutableMaterialStateCache, geometry_cache, material, element_cache
|
||
)
|
||
# Now both caches are zero-allocation!
|
||
```
|
||
"""
|
||
function create_material_cache(
|
||
::Type{ImmutableMaterialStateCache},
|
||
geometry_cache::ImmutableGeometryCache{N,NIP},
|
||
material::AbstractMaterial,
|
||
element_cache::ElementCache
|
||
) where {N,NIP}
|
||
# Compute stress and tangent at all integration points
|
||
σ_vec = Vector{SymmetricTensor{2,3,Float64,6}}(undef, NIP)
|
||
𝔻_vec = Vector{SymmetricTensor{4,3,Float64,36}}(undef, NIP)
|
||
|
||
# Get strain field (if needed for material evaluation)
|
||
# For now, assume zero strain (elastic initialization)
|
||
# This will be updated in actual assembly loop
|
||
|
||
if needs_state(material)
|
||
# Stateful material
|
||
states_vec = Vector{PlasticityState}(undef, NIP)
|
||
for q in 1:NIP
|
||
ε = zero(SymmetricTensor{2,3,Float64,6}) # Zero strain
|
||
state = PlasticityState() # Initial state
|
||
σ_vec[q], 𝔻_vec[q], states_vec[q] = update_material!(material, ε, state)
|
||
end
|
||
# Convert to NTuple
|
||
σ_tuple = ntuple(i -> σ_vec[i], Val(NIP))
|
||
𝔻_tuple = ntuple(i -> 𝔻_vec[i], Val(NIP))
|
||
states_tuple = ntuple(i -> states_vec[i], Val(NIP))
|
||
return ImmutableMaterialStateCache{PlasticityState,NIP}(σ_tuple, 𝔻_tuple, states_tuple)
|
||
else
|
||
# Stateless material
|
||
for q in 1:NIP
|
||
ε = zero(SymmetricTensor{2,3,Float64,6})
|
||
σ_vec[q], 𝔻_vec[q] = evaluate_material(material, ε)
|
||
end
|
||
# Convert to NTuple
|
||
σ_tuple = ntuple(i -> σ_vec[i], Val(NIP))
|
||
𝔻_tuple = ntuple(i -> 𝔻_vec[i], Val(NIP))
|
||
states_tuple = ntuple(i -> EmptyState(), Val(NIP))
|
||
return ImmutableMaterialStateCache{EmptyState,NIP}(σ_tuple, 𝔻_tuple, states_tuple)
|
||
end
|
||
end
|