mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-20 20:22:03 +00:00
e55560a35a
Test file included in main test/runtests.jl Simple validation test for plasticity material behavior
192 lines
7.2 KiB
Julia
192 lines
7.2 KiB
Julia
# 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
|