diff --git a/test/materials/test_material_cache.jl b/test/materials/test_material_cache.jl deleted file mode 100644 index d6cdb7d..0000000 --- a/test/materials/test_material_cache.jl +++ /dev/null @@ -1,286 +0,0 @@ -# This file is a part of JuliaFEM. -# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md - -using Test -using JuliaFEM -using Tensors - -@testset "Material Cache" begin - @testset "State type inference" begin - # Test that material_state_type correctly infers from traits - - # Stateless material (LinearElastic) - mat_stateless = LinearElastic(E=210e9, ν=0.3) - StateType_stateless = JuliaFEM.material_state_type(mat_stateless) - @test StateType_stateless === NamedTuple{(),Tuple{}} - @test fieldnames(StateType_stateless) == () - - # Stateful material (PerfectPlasticity) - # Note: PerfectPlasticity not exported yet, so this test will be added later - # when PerfectPlasticity is integrated - end - - @testset "Zero state creation" begin - # Test empty state - StateType_empty = NamedTuple{(),Tuple{}} - zero_state = JuliaFEM.create_zero_state(StateType_empty) - @test zero_state === NamedTuple() - - # Test tensor state - StateType_tensor = NamedTuple{(:ε_p,), Tuple{SymmetricTensor{2,3,Float64,6}}} - zero_state = JuliaFEM.create_zero_state(StateType_tensor) - @test haskey(zero_state, :ε_p) - @test zero_state.ε_p == zero(SymmetricTensor{2,3}) - - # Test scalar state - StateType_scalar = NamedTuple{(:κ,), Tuple{Float64}} - zero_state = JuliaFEM.create_zero_state(StateType_scalar) - @test haskey(zero_state, :κ) - @test zero_state.κ == 0.0 - - # Test mixed state (like plasticity) - StateType_mixed = NamedTuple{(:ε_p, :α, :κ), Tuple{SymmetricTensor{2,3,Float64,6}, SymmetricTensor{2,3,Float64,6}, Float64}} - zero_state = JuliaFEM.create_zero_state(StateType_mixed) - @test haskey(zero_state, :ε_p) - @test haskey(zero_state, :α) - @test haskey(zero_state, :κ) - @test zero_state.ε_p == zero(SymmetricTensor{2,3}) - @test zero_state.α == zero(SymmetricTensor{2,3}) - @test zero_state.κ == 0.0 - end - - @testset "MaterialCache construction - stateless" begin - mat = LinearElastic(E=210e9, ν=0.3) - n_ips = 8 - - cache = JuliaFEM.create_global_material_cache(mat, n_ips=n_ips, n_elems=10) - - # Check type - @test cache isa JuliaFEM.GlobalMaterialCache{NamedTuple{(),Tuple{}}} - - # Check structure (Matrix: [nips, nelems]) - @test size(cache.states) == (n_ips, 10) - @test size(cache.states_old) == (n_ips, 10) - - # Check initialization - for elem_id in 1:10, ip in 1:n_ips - @test cache.states[ip, elem_id] === NamedTuple() - @test cache.states_old[ip, elem_id] === NamedTuple() - end - end - - @testset "MaterialCache construction - stateful" begin - # Define a simple stateful state type manually - StateType = NamedTuple{(:ε_p, :κ), Tuple{SymmetricTensor{2,3,Float64,6}, Float64}} - n_ips = 4 - n_elems = 3 - - cache = JuliaFEM.GlobalMaterialCache{StateType}(n_ips, n_elems) - - # Check type - @test cache isa JuliaFEM.GlobalMaterialCache{StateType} - - # Check structure (Matrix: [nips, nelems]) - @test size(cache.states) == (n_ips, n_elems) - @test size(cache.states_old) == (n_ips, n_elems) - - # Check initialization to zeros - for elem_id in 1:n_elems, ip in 1:n_ips - state = cache.states[ip, elem_id] - @test haskey(state, :ε_p) - @test haskey(state, :κ) - @test state.ε_p == zero(SymmetricTensor{2,3}) - @test state.κ == 0.0 - - state_old = cache.states_old[ip, elem_id] - @test haskey(state_old, :ε_p) - @test haskey(state_old, :κ) - @test state_old.ε_p == zero(SymmetricTensor{2,3}) - @test state_old.κ == 0.0 - end - end - - @testset "State access functions" begin - StateType = NamedTuple{(:ε_p, :κ), Tuple{SymmetricTensor{2,3,Float64,6}, Float64}} - n_ips = 4 - n_elems = 2 - cache = JuliaFEM.GlobalMaterialCache{StateType}(n_ips, n_elems) - - # Test get_state (requires ip and elem_id) - ip = 2 - elem_id = 1 - state = JuliaFEM.get_state(cache, ip, elem_id) - @test state isa NamedTuple - @test haskey(state, :ε_p) - @test haskey(state, :κ) - - # Test get_old_state - state_old = JuliaFEM.get_old_state(cache, ip, elem_id) - @test state_old isa NamedTuple - @test haskey(state_old, :ε_p) - @test haskey(state_old, :κ) - - # Test set_state! - new_ε_p = SymmetricTensor{2,3}((0.001, 0.0, 0.0, 0.0, 0.0, 0.0)) - new_κ = 0.5 - new_state = (ε_p=new_ε_p, κ=new_κ) - - JuliaFEM.set_state!(cache, ip, elem_id, new_state) - - state_updated = JuliaFEM.get_state(cache, ip, elem_id) - @test state_updated.ε_p == new_ε_p - @test state_updated.κ == new_κ - - # Verify old state unchanged - state_old_check = JuliaFEM.get_old_state(cache, ip, elem_id) - @test state_old_check.ε_p == zero(SymmetricTensor{2,3}) - @test state_old_check.κ == 0.0 - end - - @testset "update_cache! - time stepping" begin - StateType = NamedTuple{(:κ,), Tuple{Float64}} - n_ips = 3 - n_elems = 1 - cache = JuliaFEM.GlobalMaterialCache{StateType}(n_ips, n_elems) - elem_id = 1 - - # Set some current states - JuliaFEM.set_state!(cache, 1, elem_id, (κ=0.1,)) - JuliaFEM.set_state!(cache, 2, elem_id, (κ=0.2,)) - JuliaFEM.set_state!(cache, 3, elem_id, (κ=0.3,)) - - # Old states should still be zero - @test JuliaFEM.get_old_state(cache, 1, elem_id).κ == 0.0 - @test JuliaFEM.get_old_state(cache, 2, elem_id).κ == 0.0 - @test JuliaFEM.get_old_state(cache, 3, elem_id).κ == 0.0 - - # Update cache (copy current → old) - JuliaFEM.update_cache!(cache) - - # Now old states should match current - @test JuliaFEM.get_old_state(cache, 1, elem_id).κ == 0.1 - @test JuliaFEM.get_old_state(cache, 2, elem_id).κ == 0.2 - @test JuliaFEM.get_old_state(cache, 3, elem_id).κ == 0.3 - - # Current states unchanged - @test JuliaFEM.get_state(cache, 1, elem_id).κ == 0.1 - @test JuliaFEM.get_state(cache, 2, elem_id).κ == 0.2 - @test JuliaFEM.get_state(cache, 3, elem_id).κ == 0.3 - - # Modify current states - JuliaFEM.set_state!(cache, 1, elem_id, (κ=0.15,)) - JuliaFEM.set_state!(cache, 2, elem_id, (κ=0.25,)) - - # Old states should still be from previous step - @test JuliaFEM.get_old_state(cache, 1, elem_id).κ == 0.1 - @test JuliaFEM.get_old_state(cache, 2, elem_id).κ == 0.2 - - # Current states are new - @test JuliaFEM.get_state(cache, 1, elem_id).κ == 0.15 - @test JuliaFEM.get_state(cache, 2, elem_id).κ == 0.25 - end - - @testset "reset_cache!" begin - StateType = NamedTuple{(:ε_p, :κ), Tuple{SymmetricTensor{2,3,Float64,6}, Float64}} - n_ips = 2 - n_elems = 1 - cache = JuliaFEM.GlobalMaterialCache{StateType}(n_ips, n_elems) - elem_id = 1 - - # Set some non-zero states - ε_p = SymmetricTensor{2,3}((0.01, 0.0, 0.0, 0.0, 0.0, 0.0)) - JuliaFEM.set_state!(cache, 1, elem_id, (ε_p=ε_p, κ=0.5)) - JuliaFEM.set_state!(cache, 2, elem_id, (ε_p=ε_p, κ=0.8)) - - # Update to move to old - JuliaFEM.update_cache!(cache) - - # Verify non-zero - @test JuliaFEM.get_state(cache, 1, elem_id).κ != 0.0 - @test JuliaFEM.get_old_state(cache, 1, elem_id).κ != 0.0 - - # Reset - JuliaFEM.reset_cache!(cache) - - # All should be zero - for ip in 1:n_ips - state = JuliaFEM.get_state(cache, ip, elem_id) - state_old = JuliaFEM.get_old_state(cache, ip, elem_id) - - @test state.ε_p == zero(SymmetricTensor{2,3}) - @test state.κ == 0.0 - @test state_old.ε_p == zero(SymmetricTensor{2,3}) - @test state_old.κ == 0.0 - end - end - - @testset "State variable helpers" begin - # Create a state matching plasticity structure - StateType = NamedTuple{(:ε_p, :α, :κ), Tuple{SymmetricTensor{2,3,Float64,6}, SymmetricTensor{2,3,Float64,6}, Float64}} - - ε_p_val = SymmetricTensor{2,3}((0.001, 0.0, 0.0, 0.0, 0.0, 0.0)) - α_val = SymmetricTensor{2,3}((100e6, 0.0, 0.0, 0.0, 0.0, 0.0)) - κ_val = 0.01 - - state = (ε_p=ε_p_val, α=α_val, κ=κ_val) - - # Test get_state_variable - extracted_ε_p = JuliaFEM.get_state_variable(state, PlasticStrain) - @test extracted_ε_p == ε_p_val - - extracted_α = JuliaFEM.get_state_variable(state, Backstress) - @test extracted_α == α_val - - extracted_κ = JuliaFEM.get_state_variable(state, EquivalentPlasticStrain) - @test extracted_κ == κ_val - - # Test set_state_variable (immutable update) - new_κ_val = 0.02 - new_state = JuliaFEM.set_state_variable(state, EquivalentPlasticStrain, new_κ_val) - - # Original state unchanged (immutable) - @test state.κ == κ_val - - # New state has updated value - @test new_state.κ == new_κ_val - - # Other values unchanged - @test new_state.ε_p == ε_p_val - @test new_state.α == α_val - end - - @testset "Type stability" begin - # Verify that cache operations are type-stable - mat = LinearElastic(E=210e9, ν=0.3) - cache = JuliaFEM.create_global_material_cache(mat, n_ips=4, n_elems=2) - - # get_state should be type-stable (requires ip and elem_id) - @inferred JuliaFEM.get_state(cache, 1, 1) - @inferred JuliaFEM.get_old_state(cache, 1, 1) - - # For stateful cache - StateType = NamedTuple{(:κ,), Tuple{Float64}} - cache_stateful = JuliaFEM.GlobalMaterialCache{StateType}(4, 2) - - @inferred JuliaFEM.get_state(cache_stateful, 1, 1) - @inferred JuliaFEM.get_old_state(cache_stateful, 1, 1) - end - - @testset "Integration with material traits" begin - # Verify that the cache system integrates correctly with material traits - mat = LinearElastic(E=210e9, ν=0.3) - - # Material declares no state variables - vars = required_state_variables(mat) - @test vars === () - - # Cache should be empty - StateType = JuliaFEM.material_state_type(mat) - @test StateType === NamedTuple{(),Tuple{}} - - # Created cache should be stateless - cache = JuliaFEM.create_global_material_cache(mat, n_ips=2, n_elems=3) - @test cache isa JuliaFEM.GlobalMaterialCache{NamedTuple{(),Tuple{}}} - @test JuliaFEM.get_state(cache, 1, 1) === NamedTuple() - end -end