Files
JuliaFEM.jl/test/materials/test_traits.jl
T
Jukka Aho 2502707f10 test(materials): snapshot material_behavior inventory across model families
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.
2026-05-09 18:46:20 +03:00

162 lines
5.9 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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