use super::functions::*;
pub struct ArrudaBoyce {
pub mu: f64,
pub bulk_modulus: f64,
pub lambda_m: f64,
}
impl ArrudaBoyce {
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let c = right_cauchy_green(f);
let (i1, _, _) = invariants(c);
let j = mat3_det(f);
let lm2 = self.lambda_m * self.lambda_m;
let coeffs = [
0.5,
1.0 / 20.0,
11.0 / 1050.0,
19.0 / 7000.0,
519.0 / 673750.0,
];
let mut w_dev = 0.0;
let mut i1_pow = i1;
let mut three_pow = 3.0_f64;
let mut lm_pow = 1.0_f64;
for (k, &ck) in coeffs.iter().enumerate() {
w_dev += self.mu * ck / lm_pow * (i1_pow - three_pow);
i1_pow *= i1;
three_pow *= 3.0;
if k < coeffs.len() - 1 {
lm_pow *= lm2;
}
}
let w_vol = self.bulk_modulus / 2.0 * (j - 1.0).powi(2);
w_dev + w_vol
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
numerical_pk1(f, |ff| self.strain_energy(ff))
}
}
pub struct MooneyRivlin {
pub c10: f64,
pub c01: f64,
pub d1: f64,
}
impl MooneyRivlin {
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let j = mat3_det(f);
let j_neg13 = j.powf(-1.0 / 3.0);
let f_bar = mat3_scale(f, j_neg13);
let c_bar = right_cauchy_green(f_bar);
let (i1_bar, i2_bar, _) = invariants(c_bar);
self.c10 * (i1_bar - 3.0) + self.c01 * (i2_bar - 3.0) + (j - 1.0).powi(2) / self.d1
}
pub fn pk2_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
let c = right_cauchy_green(f);
let h = 1e-7_f64;
let mut s = [[0.0_f64; 3]; 3];
for i in 0..3 {
for j_idx in 0..3 {
let mut c_plus = c;
let mut c_minus = c;
c_plus[i][j_idx] += h;
c_plus[j_idx][i] += h;
c_minus[i][j_idx] -= h;
c_minus[j_idx][i] -= h;
let w_plus = self.strain_energy_from_c(c_plus);
let w_minus = self.strain_energy_from_c(c_minus);
s[i][j_idx] = (w_plus - w_minus) / (2.0 * h);
}
}
s
}
fn strain_energy_from_c(&self, c: [[f64; 3]; 3]) -> f64 {
let j2 = mat3_det(c);
let j = j2.sqrt().max(1e-15);
let j_neg23 = j.powf(-2.0 / 3.0);
let (i1, i2, _) = invariants(c);
let i1_bar = i1 * j_neg23;
let i2_bar = i2 * j.powf(-4.0 / 3.0);
self.c10 * (i1_bar - 3.0) + self.c01 * (i2_bar - 3.0) + (j - 1.0).powi(2) / self.d1
}
}
pub struct HolzapfelOgden {
pub c10: f64,
pub bulk_modulus: f64,
pub k1: f64,
pub k2: f64,
pub a: [f64; 3],
}
impl HolzapfelOgden {
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let j = mat3_det(f);
let j_neg23 = j.powf(-2.0 / 3.0);
let c = right_cauchy_green(f);
let (i1, _, _) = invariants(c);
let i1_bar = i1 * j_neg23;
let w_iso = self.c10 * (i1_bar - 3.0);
let a = self.a;
let mut ca = [0.0_f64; 3];
for i in 0..3 {
for k in 0..3 {
ca[i] += c[i][k] * a[k];
}
}
let i4_full = a[0] * ca[0] + a[1] * ca[1] + a[2] * ca[2];
let i4 = i4_full * j_neg23;
let w_fibre = if i4 > 1.0 {
let di4 = i4 - 1.0;
self.k1 / (2.0 * self.k2) * ((self.k2 * di4 * di4).exp() - 1.0)
} else {
0.0
};
let w_vol = self.bulk_modulus / 2.0 * (j - 1.0).powi(2);
w_iso + w_fibre + w_vol
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
numerical_pk1(f, |ff| self.strain_energy(ff))
}
pub fn cauchy_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
pk1_to_cauchy(f, self.pk1_stress(f))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PenaltyType {
Quadratic,
SimoTaylor,
Logarithmic,
}
pub struct Fung {
pub c: f64,
pub b1: f64,
pub bulk_modulus: f64,
}
impl Fung {
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let c_tensor = right_cauchy_green(f);
let (i1, _, _) = invariants(c_tensor);
let j = mat3_det(f);
let di = i1 - 3.0;
let q = self.b1 * di * di;
let w_dev = self.c / 2.0 * (q.exp() - 1.0);
let w_vol = self.bulk_modulus / 2.0 * (j - 1.0).powi(2);
w_dev + w_vol
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
numerical_pk1(f, |ff| self.strain_energy(ff))
}
}
pub struct Yeoh {
pub c1: f64,
pub c2: f64,
pub c3: f64,
pub bulk_modulus: f64,
}
impl Yeoh {
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let c = right_cauchy_green(f);
let (i1, _, _) = invariants(c);
let j = mat3_det(f);
let di = i1 - 3.0;
let w_dev = self.c1 * di + self.c2 * di * di + self.c3 * di * di * di;
let w_vol = self.bulk_modulus / 2.0 * (j - 1.0).powi(2);
w_dev + w_vol
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
numerical_pk1(f, |ff| self.strain_energy(ff))
}
}
pub struct Gent {
pub mu: f64,
pub bulk_modulus: f64,
pub j_m: f64,
}
impl Gent {
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let c = right_cauchy_green(f);
let (i1, _, _) = invariants(c);
let j = mat3_det(f);
let arg = 1.0 - (i1 - 3.0) / self.j_m;
let w_dev = if arg > 1e-15 {
-self.mu * self.j_m / 2.0 * arg.ln()
} else {
f64::INFINITY
};
let w_vol = self.bulk_modulus / 2.0 * (j - 1.0).powi(2);
w_dev + w_vol
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
numerical_pk1(f, |ff| self.strain_energy(ff))
}
}
#[derive(Debug, Clone)]
pub struct NearlyIncompressibleNeoHookean {
pub mu: f64,
pub kappa: f64,
pub pressure: f64,
}
impl NearlyIncompressibleNeoHookean {
pub fn from_young_poisson(e: f64, nu: f64) -> Self {
let mu = e / (2.0 * (1.0 + nu));
let kappa = e / (3.0 * (1.0 - 2.0 * nu));
Self {
mu,
kappa,
pressure: 0.0,
}
}
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let c_bar = deviatoric_right_cauchy_green(f);
let (i1_bar, _, _) = invariants(c_bar);
let j = mat3_det(f);
let w_dev = self.mu / 2.0 * (i1_bar - 3.0);
let w_vol = self.pressure * (j - 1.0) - self.pressure * self.pressure / (2.0 * self.kappa);
w_dev + w_vol
}
pub fn update_pressure(&mut self, f: [[f64; 3]; 3]) {
let j = mat3_det(f);
self.pressure = self.kappa * (j - 1.0);
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
let p_dev = neo_hookean_deviatoric_pk1(self.mu, f);
let j = mat3_det(f);
let f_inv = mat3_inverse(f).unwrap_or(mat3_identity());
let f_inv_t = mat3_transpose(f_inv);
let mut result = [[0.0_f64; 3]; 3];
for i in 0..3 {
for k in 0..3 {
result[i][k] = p_dev[i][k] + self.pressure * j * f_inv_t[i][k];
}
}
result
}
pub fn cauchy_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
let p = self.pk1_stress(f);
pk1_to_cauchy(f, p)
}
}
pub struct NeoHookean {
pub mu: f64,
pub lambda: f64,
}
impl NeoHookean {
pub fn new(mu: f64, lambda: f64) -> Self {
Self { mu, lambda }
}
pub fn from_young_poisson(e: f64, nu: f64) -> Self {
let mu = e / (2.0 * (1.0 + nu));
let lambda = e * nu / ((1.0 + nu) * (1.0 - 2.0 * nu));
Self { mu, lambda }
}
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let c = right_cauchy_green(f);
let (i1, _, _) = invariants(c);
let j = mat3_det(f);
let ln_j = j.ln();
self.mu / 2.0 * (i1 - 3.0) - self.mu * ln_j + self.lambda / 2.0 * ln_j * ln_j
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
let j = mat3_det(f);
let ln_j = j.ln();
let f_inv = mat3_inverse(f).unwrap_or(mat3_identity());
let f_inv_t = mat3_transpose(f_inv);
let mut p = [[0.0_f64; 3]; 3];
for i in 0..3 {
for j_idx in 0..3 {
p[i][j_idx] = self.mu * (f[i][j_idx] - f_inv_t[i][j_idx])
+ self.lambda * ln_j * f_inv_t[i][j_idx];
}
}
p
}
pub fn cauchy_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
let j = mat3_det(f);
let p = self.pk1_stress(f);
let ft = mat3_transpose(f);
let pft = mat3_mul(p, ft);
let mut sigma = [[0.0_f64; 3]; 3];
for i in 0..3 {
for k in 0..3 {
sigma[i][k] = pft[i][k] / j;
}
}
sigma
}
}
pub struct OgdenTerm {
pub mu: f64,
pub alpha: f64,
}
impl OgdenTerm {
pub fn strain_energy_uniaxial(&self, lambda1: f64) -> f64 {
self.mu / self.alpha
* (lambda1.powf(self.alpha) + 2.0 * lambda1.powf(-self.alpha / 2.0) - 3.0)
}
}
pub struct Varga {
pub mu: f64,
pub bulk_modulus: f64,
}
impl Varga {
pub fn strain_energy(&self, f: [[f64; 3]; 3]) -> f64 {
let j = mat3_det(f);
let j_neg13 = j.powf(-1.0 / 3.0);
let f_bar = mat3_scale(f, j_neg13);
let c_bar = right_cauchy_green(f_bar);
let (i1_bar, _, _) = invariants(c_bar);
let w_dev = self.mu * (i1_bar.sqrt() * (3.0_f64.sqrt()) - 3.0);
let w_vol = self.bulk_modulus / 2.0 * (j - 1.0).powi(2);
w_dev + w_vol
}
pub fn pk1_stress(&self, f: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
numerical_pk1(f, |ff| self.strain_energy(ff))
}
}
#[derive(Debug, Clone, Copy)]
pub struct VolumetricPenalty {
pub kappa: f64,
pub penalty_type: PenaltyType,
}
impl VolumetricPenalty {
pub fn quadratic(kappa: f64) -> Self {
Self {
kappa,
penalty_type: PenaltyType::Quadratic,
}
}
pub fn simo_taylor(kappa: f64) -> Self {
Self {
kappa,
penalty_type: PenaltyType::SimoTaylor,
}
}
pub fn logarithmic(kappa: f64) -> Self {
Self {
kappa,
penalty_type: PenaltyType::Logarithmic,
}
}
pub fn energy(&self, j: f64) -> f64 {
match self.penalty_type {
PenaltyType::Quadratic => self.kappa / 2.0 * (j - 1.0).powi(2),
PenaltyType::SimoTaylor => self.kappa / 4.0 * (j * j - 1.0 - 2.0 * j.ln()),
PenaltyType::Logarithmic => self.kappa / 2.0 * j.ln().powi(2),
}
}
pub fn pressure(&self, j: f64) -> f64 {
match self.penalty_type {
PenaltyType::Quadratic => self.kappa * (j - 1.0),
PenaltyType::SimoTaylor => self.kappa / 2.0 * (j - 1.0 / j),
PenaltyType::Logarithmic => self.kappa * j.ln() / j,
}
}
pub fn bulk_contribution(&self, j: f64) -> f64 {
match self.penalty_type {
PenaltyType::Quadratic => self.kappa,
PenaltyType::SimoTaylor => self.kappa / 2.0 * (1.0 + 1.0 / (j * j)),
PenaltyType::Logarithmic => self.kappa * (1.0 - j.ln()) / (j * j),
}
}
}