use crate::stress::StressTensor;
#[must_use]
#[inline]
pub fn von_mises_check(stress: &StressTensor, yield_strength: f64) -> bool {
stress.von_mises() >= yield_strength
}
#[must_use]
#[inline]
pub fn tresca_check(stress: &StressTensor, yield_strength: f64) -> bool {
stress.max_shear() >= yield_strength / 2.0
}
#[must_use]
#[inline]
pub fn safety_factor(stress: &StressTensor, yield_strength: f64) -> f64 {
let vm = stress.von_mises();
if vm.abs() < hisab::EPSILON_F64 {
return f64::INFINITY;
}
yield_strength / vm
}
#[must_use]
#[inline]
pub fn safety_factor_tresca(stress: &StressTensor, yield_strength: f64) -> f64 {
let tau = stress.max_shear();
if tau.abs() < hisab::EPSILON_F64 {
return f64::INFINITY;
}
(yield_strength / 2.0) / tau
}
#[must_use]
#[inline]
pub fn drucker_prager_check(stress: &StressTensor, alpha: f64, k: f64) -> bool {
stress.j2().sqrt() + alpha * stress.i1() >= k
}
#[must_use]
pub fn drucker_prager_from_mohr_coulomb(friction_angle_rad: f64, cohesion: f64) -> (f64, f64) {
let sin_phi = friction_angle_rad.sin();
let cos_phi = friction_angle_rad.cos();
let denom = 3.0_f64.sqrt() * (3.0 - sin_phi);
let alpha = 2.0 * sin_phi / denom;
let k = 6.0 * cohesion * cos_phi / denom;
(alpha, k)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn below_yield() {
let s = StressTensor::uniaxial(100e6);
assert!(
!von_mises_check(&s, 250e6),
"100 MPa should not yield at 250 MPa"
);
}
#[test]
fn above_yield() {
let s = StressTensor::uniaxial(300e6);
assert!(
von_mises_check(&s, 250e6),
"300 MPa should yield at 250 MPa"
);
}
#[test]
fn tresca_uniaxial() {
let s = StressTensor::uniaxial(260e6);
assert!(tresca_check(&s, 250e6));
}
#[test]
fn safety_factor_basic() {
let s = StressTensor::uniaxial(100e6);
let sf = safety_factor(&s, 250e6);
assert!(
(sf - 2.5).abs() < 0.01,
"safety factor should be 2.5, got {sf}"
);
}
#[test]
fn safety_factor_zero_stress() {
let s = StressTensor::uniaxial(0.0);
assert!(safety_factor(&s, 250e6).is_infinite());
}
#[test]
fn tresca_safety_factor_uniaxial() {
let s = StressTensor::uniaxial(100e6);
let sf = safety_factor_tresca(&s, 250e6);
assert!(
(sf - 2.5).abs() < 0.01,
"Tresca SF should be 2.5 for uniaxial, got {sf}"
);
}
#[test]
fn tresca_safety_factor_zero_stress() {
let s = StressTensor::uniaxial(0.0);
assert!(safety_factor_tresca(&s, 250e6).is_infinite());
}
#[test]
fn von_mises_tresca_ordering() {
let s = StressTensor::new(100e6, 50e6, 0.0, 30e6, 0.0, 0.0);
let sf_vm = safety_factor(&s, 500e6);
let sf_tr = safety_factor_tresca(&s, 500e6);
assert!(
sf_tr <= sf_vm + 1e-6,
"Tresca should be more conservative: VM={sf_vm}, Tresca={sf_tr}"
);
}
#[test]
fn drucker_prager_hydrostatic() {
let s = StressTensor::hydrostatic_state(-10e6); let phi = std::f64::consts::FRAC_PI_6; let (alpha, k) = drucker_prager_from_mohr_coulomb(phi, 5e6);
assert!(
!drucker_prager_check(&s, alpha, k),
"hydrostatic compression should not yield"
);
}
#[test]
fn drucker_prager_parameters() {
let phi = std::f64::consts::FRAC_PI_6; let (alpha, k) = drucker_prager_from_mohr_coulomb(phi, 5e6);
assert!(alpha > 0.0, "alpha should be positive");
assert!(k > 0.0, "k should be positive");
}
}