mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-06 04:21:33 +00:00
2502707f10
Add one representative model per `material_behavior` kind so refactors to traits cannot silently orphan newly added constitutive laws. - Cover constant tangents (elastic/thermal/hydraulic/moisture/diffusion/orthotropic). - Cover hyperelastic, plasticity, damage, Chaboche, creep, and eigenstrain-augmented elasticity.
162 lines
5.9 KiB
Julia
162 lines
5.9 KiB
Julia
# 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 Traits" begin
|
||
@testset "LinearElastic traits" begin
|
||
mat = LinearElastic(E=210e9, ν=0.3)
|
||
|
||
# Test supported_physics
|
||
physics = supported_physics(mat)
|
||
@test physics isa Tuple
|
||
@test length(physics) == 1
|
||
@test physics[1] isa Elasticity{3}
|
||
|
||
# Test required_field_types (derived from supported_physics)
|
||
fields = required_field_types(mat)
|
||
@test fields isa Tuple
|
||
@test length(fields) == 1
|
||
@test fields[1] === Displacement{3}
|
||
|
||
# Test required_state_variables (stateless)
|
||
state_vars = required_state_variables(mat)
|
||
@test state_vars isa Tuple
|
||
@test length(state_vars) == 0
|
||
@test isempty(state_vars)
|
||
|
||
# Test helper functions
|
||
@test is_stateful(mat) == false
|
||
|
||
state_types = get_state_variable_types(mat)
|
||
@test state_types isa Tuple
|
||
@test length(state_types) == 0
|
||
|
||
state_symbols = get_state_variable_symbols(mat)
|
||
@test state_symbols isa Tuple
|
||
@test length(state_symbols) == 0
|
||
end
|
||
|
||
@testset "Trait function existence" begin
|
||
# Test that trait functions are exported and callable
|
||
@test isdefined(JuliaFEM, :supported_physics)
|
||
@test isdefined(JuliaFEM, :required_field_types)
|
||
@test isdefined(JuliaFEM, :required_state_variables)
|
||
@test isdefined(JuliaFEM, :get_state_variable_types)
|
||
@test isdefined(JuliaFEM, :get_state_variable_symbols)
|
||
@test isdefined(JuliaFEM, :is_stateful)
|
||
end
|
||
|
||
@testset "Helper function consistency" begin
|
||
mat = LinearElastic(E=210e9, ν=0.3)
|
||
|
||
# For stateless material
|
||
@test is_stateful(mat) == false
|
||
@test isempty(required_state_variables(mat))
|
||
@test isempty(get_state_variable_types(mat))
|
||
@test isempty(get_state_variable_symbols(mat))
|
||
end
|
||
|
||
@testset "Compositional design verification" begin
|
||
# This test verifies the compositional design philosophy:
|
||
# Materials declare tuples of state variable types,
|
||
# and each state variable has its own traits
|
||
|
||
mat = LinearElastic(E=210e9, ν=0.3)
|
||
|
||
# LinearElastic should have no state variables
|
||
vars = required_state_variables(mat)
|
||
@test vars === ()
|
||
|
||
# If a material HAD state variables (e.g., PerfectPlasticity),
|
||
# we could query each variable's traits like this:
|
||
# vars = required_state_variables(material)
|
||
# for var in vars
|
||
# concrete_type = state_variable_type(var)
|
||
# symbol = default_symbol(var)
|
||
# end
|
||
|
||
# The key insight: state variables are building blocks,
|
||
# materials compose them to declare requirements
|
||
end
|
||
|
||
@testset "material_behavior trait inventory (one example per kind)" begin
|
||
# StatelessConstantTangent
|
||
@test material_behavior(LinearElastic(E = 210e9, ν = 0.3)) isa StatelessConstantTangent
|
||
@test material_behavior(HeatConductivity(k = 200.0)) isa StatelessConstantTangent
|
||
@test material_behavior(HydraulicConductivity(K = 1e-5)) isa StatelessConstantTangent
|
||
@test material_behavior(MoistureDiffusivity(D_w = 1e-9)) isa StatelessConstantTangent
|
||
@test material_behavior(ElementWiseScalarDiffusion([1.0])) isa StatelessConstantTangent
|
||
E = 210e9
|
||
ν = 0.3
|
||
G = E / (2(1 + ν))
|
||
@test material_behavior(
|
||
OrthotropicLinearElastic(
|
||
E1 = E,
|
||
E2 = E,
|
||
E3 = E,
|
||
G12 = G,
|
||
G23 = G,
|
||
G31 = G,
|
||
ν12 = ν,
|
||
ν23 = ν,
|
||
ν31 = ν,
|
||
),
|
||
) isa StatelessConstantTangent
|
||
|
||
# StatelessStrainDependent
|
||
@test material_behavior(NeoHookean(E_mod = 1e6, nu = 0.45)) isa StatelessStrainDependent
|
||
@test material_behavior(MooneyRivlin(C10 = 80e3, C01 = 20e3, κ_bulk = 1e9)) isa StatelessStrainDependent
|
||
@test material_behavior(Yeoh3(C10 = 1e5, κ_bulk = 1e9)) isa StatelessStrainDependent
|
||
@test material_behavior(Gent(μ = 1e5, Jm = 100.0, κ_bulk = 1e9)) isa StatelessStrainDependent
|
||
|
||
# StatefulStrainDependent
|
||
@test material_behavior(
|
||
PerfectPlasticity(E = 210e9, ν = 0.3, σ_y = 250e6, H = 1e9),
|
||
) isa StatefulStrainDependent
|
||
@test material_behavior(
|
||
J2LinearIsotropicPlasticity(E = 210e9, ν = 0.3, σ_y0 = 250e6, H_iso = 1e9),
|
||
) isa StatefulStrainDependent
|
||
@test material_behavior(
|
||
StVenantKirchhoffJ2Plasticity(E = 210e9, ν = 0.3, σ_y = 250e6, H = 0.0),
|
||
) isa StatefulStrainDependent
|
||
@test material_behavior(
|
||
ScalarDamageLinearElastic(E = 210e9, ν = 0.3, r = 10.0),
|
||
) isa StatefulStrainDependent
|
||
@test material_behavior(
|
||
ChabocheJ2Plasticity(
|
||
E = 210e9,
|
||
ν = 0.3,
|
||
σ_y = 300e6,
|
||
C1 = 1e9,
|
||
γ1 = 100.0,
|
||
C2 = 1e9,
|
||
γ2 = 100.0,
|
||
),
|
||
) isa StatefulStrainDependent
|
||
@test material_behavior(
|
||
NortonCreepElastic(E = 210e9, ν = 0.3, A = 1e-20, n = 3.0),
|
||
) isa StatefulStrainDependent
|
||
@test material_behavior(
|
||
LinearElasticWithEigenstrain(E = 210e9, ν = 0.3),
|
||
) isa StatefulStrainDependent
|
||
end
|
||
|
||
@testset "Physics to field type derivation" begin
|
||
mat = LinearElastic(E=210e9, ν=0.3)
|
||
|
||
# Test that required_field_types correctly derives from supported_physics
|
||
physics = supported_physics(mat)
|
||
fields = required_field_types(mat)
|
||
|
||
@test length(physics) == length(fields)
|
||
|
||
for (p, f) in zip(physics, fields)
|
||
# Each physics should map to its required field
|
||
@test f === required_field_type(p)
|
||
end
|
||
end
|
||
end
|