# This file is a part of JuliaFEM. # License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md """ Simple Plasticity Test: Single Hex8 Element with PerfectPlasticity Demonstrates end-to-end workflow with compositional material cache: 1. Create GlobalMaterialCache from material traits 2. Assemble with state tracking 3. Verify plastic strain accumulates correctly This is a UNIT TEST, not a validation test. Goal is to verify the material cache integration works correctly. """ using Test using JuliaFEM using LinearAlgebra using SparseArrays using Tensors @testset "Simple Plasticity - GlobalMaterialCache Integration" begin println("\n" * "="^70) println("SIMPLE PLASTICITY TEST - GlobalMaterialCache Integration") println("="^70) # ======================================================================== # 1. Create Single Element Mesh # ======================================================================== println("\n[1] Creating single element mesh...") # Unit cube Hex8 element nodes = [ Vec{3}((0.0, 0.0, 0.0)), # 1 Vec{3}((1.0, 0.0, 0.0)), # 2 Vec{3}((1.0, 1.0, 0.0)), # 3 Vec{3}((0.0, 1.0, 0.0)), # 4 Vec{3}((0.0, 0.0, 1.0)), # 5 Vec{3}((1.0, 0.0, 1.0)), # 6 Vec{3}((1.0, 1.0, 1.0)), # 7 Vec{3}((0.0, 1.0, 1.0)), # 8 ] connectivity = [NTuple{8,UInt32}((1, 2, 3, 4, 5, 6, 7, 8))] element_sets = Dict{Symbol,Set{UInt32}}(:all => Set(UInt32[1])) mesh = Mesh{8,Hexahedron{8}}(nodes, connectivity, element_sets) println(" Nodes: $(length(nodes))") println(" Elements: $(length(connectivity))") # ======================================================================== # 2. Material and GlobalMaterialCache # ======================================================================== println("\n[2] Creating material and global cache...") # Plasticity material E = 210e9 # Pa ν = 0.3 σ_y = 250e6 # Pa (250 MPa yield stress) H = 1e9 # Pa (hardening modulus) material = PerfectPlasticity(E=E, ν=ν, σ_y=σ_y, H=H) println(" Material: PerfectPlasticity") println(" E = $(E/1e9) GPa") println(" σ_y = $(σ_y/1e6) MPa") println(" H = $(H/1e9) GPa") # Verify material traits state_vars = required_state_variables(material) @test state_vars == (PlasticStrain, Backstress, EquivalentPlasticStrain) println(" State variables: $state_vars") # Create global material cache nips = 8 # Hex8 has 8 integration points (2x2x2 Gauss) nelems = 1 global_cache = create_global_material_cache(material, n_ips=nips, n_elems=nelems) @test global_cache isa GlobalMaterialCache @test size(global_cache.states) == (nips, nelems) println(" Created GlobalMaterialCache: $(size(global_cache.states))") # Verify initial state is zero for ip in 1:nips state = get_state(global_cache, ip, 1) @test state.ε_p == zero(SymmetricTensor{2,3}) @test state.α == zero(SymmetricTensor{2,3}) @test state.κ == 0.0 end println(" ✓ All states initialized to zero") # ======================================================================== # 3. Demonstrate Material Response # ======================================================================== println("\n[3] Testing material response...") # Test 1: Elastic response (small strain) ε_elastic = SymmetricTensor{2,3}((1e-4, 0.0, 0.0, 0.0, 0.0, 0.0)) state_old_1 = NamedTuple() # Empty state = initial σ_1, 𝔻_1, state_new_1 = compute_stress(material, ε_elastic, state_old_1, 0.0) @test state_new_1.κ == 0.0 # No plastic strain println(" ✓ Elastic response: κ = $(state_new_1.κ)") # Test 2: Plastic response (large strain) ε_plastic = SymmetricTensor{2,3}((0.005, 0.0, 0.0, 0.0, 0.0, 0.0)) state_old_2 = NamedTuple() # Empty state = initial σ_2, 𝔻_2, state_new_2 = compute_stress(material, ε_plastic, state_old_2, 0.0) @test state_new_2.κ > 0.0 # Plastic strain accumulated println(" ✓ Plastic response: κ = $(state_new_2.κ)") # ======================================================================== # 4. Direct NamedTuple Workflow (No Bridge!) # ======================================================================== println("\n[4] Testing direct NamedTuple workflow...") # State is already a NamedTuple - use directly! @test state_new_2 isa NamedTuple @test haskey(state_new_2, :ε_p) @test haskey(state_new_2, :α) @test haskey(state_new_2, :κ) println(" ✓ State is native NamedTuple (no conversion needed)") # Store directly in global cache set_state!(global_cache, 1, 1, state_new_2) retrieved_state = get_state(global_cache, 1, 1) @test retrieved_state.κ == state_new_2.κ println(" ✓ State stored directly in GlobalMaterialCache") # Access directly from cache @test get_state(global_cache, 1, 1) isa NamedTuple println(" ✓ Direct access from cache (no extraction needed)") # ======================================================================== # 5. Time-Stepping Workflow # ======================================================================== println("\n[5] Testing time-stepping workflow...") # Reset cache reset_cache!(global_cache) # Step 1: Small load (elastic) println(" Step 1: Elastic loading...") state_step1_old = get_state(global_cache, 1, 1) # Get directly σ_step1, 𝔻_step1, state_step1 = compute_stress(material, ε_elastic, state_step1_old, 0.0) set_state!(global_cache, 1, 1, state_step1) # Set directly @test get_state(global_cache, 1, 1).κ == 0.0 println(" κ = $(get_state(global_cache, 1, 1).κ) (elastic)") # Update cache for next step update_cache!(global_cache) # Step 2: Large load (plastic) println(" Step 2: Plastic loading...") state_step2_old = get_state(global_cache, 1, 1) # Get directly σ_step2, 𝔻_step2, state_step2 = compute_stress(material, ε_plastic, state_step2_old, 0.0) set_state!(global_cache, 1, 1, state_step2) # Set directly κ_current = get_state(global_cache, 1, 1).κ κ_old = get_old_state(global_cache, 1, 1).κ @test κ_current > 0.0 # Plastic strain accumulated @test κ_old == 0.0 # Old state still elastic println(" κ_current = $κ_current (plastic)") println(" κ_old = $κ_old (from previous step)") # ======================================================================== # 6. Summary # ======================================================================== println("\n" * "="^70) println("SUMMARY - Compositional Material Cache (No Bridge!)") println("="^70) println(" ✓ GlobalMaterialCache created from material traits") println(" ✓ NamedTuple states used directly (no conversion)") println(" ✓ compute_stress accepts/returns NamedTuple natively") println(" ✓ States stored and retrieved without any bridge") println(" ✓ Time-stepping workflow simplified") println(" ✓ Plastic strain accumulation tracked") println(" ✓ Pure compositional design - elegant and fast!") println("="^70) @test true # If we got here, everything worked! end