use super::matrix_utils::*;
#[derive(Debug, Clone)]
pub struct Phase {
pub name: String,
pub volume_fraction: f64,
pub youngs_modulus: f64,
pub poisson_ratio: f64,
pub density: f64,
}
impl Phase {
pub fn new(
name: impl Into<String>,
volume_fraction: f64,
youngs_modulus: f64,
poisson_ratio: f64,
) -> Self {
Self {
name: name.into(),
volume_fraction,
youngs_modulus,
poisson_ratio,
density: 0.0,
}
}
pub fn with_density(mut self, density: f64) -> Self {
self.density = density;
self
}
pub fn bulk_modulus(&self) -> f64 {
self.youngs_modulus / (3.0 * (1.0 - 2.0 * self.poisson_ratio))
}
pub fn shear_modulus(&self) -> f64 {
self.youngs_modulus / (2.0 * (1.0 + self.poisson_ratio))
}
pub fn lame_lambda(&self) -> f64 {
let e = self.youngs_modulus;
let nu = self.poisson_ratio;
e * nu / ((1.0 + nu) * (1.0 - 2.0 * nu))
}
pub fn stiffness_voigt(&self) -> [[f64; 6]; 6] {
let lam = self.lame_lambda();
let mu = self.shear_modulus();
let c11 = lam + 2.0 * mu;
let c12 = lam;
let c44 = mu;
let mut c = [[0.0f64; 6]; 6];
c[0][0] = c11;
c[0][1] = c12;
c[0][2] = c12;
c[1][0] = c12;
c[1][1] = c11;
c[1][2] = c12;
c[2][0] = c12;
c[2][1] = c12;
c[2][2] = c11;
c[3][3] = c44;
c[4][4] = c44;
c[5][5] = c44;
c
}
pub fn compliance_voigt(&self) -> [[f64; 6]; 6] {
let e = self.youngs_modulus;
let nu = self.poisson_ratio;
let s11 = 1.0 / e;
let s12 = -nu / e;
let s44 = 2.0 * (1.0 + nu) / e; let mut s = [[0.0f64; 6]; 6];
s[0][0] = s11;
s[0][1] = s12;
s[0][2] = s12;
s[1][0] = s12;
s[1][1] = s11;
s[1][2] = s12;
s[2][0] = s12;
s[2][1] = s12;
s[2][2] = s11;
s[3][3] = s44;
s[4][4] = s44;
s[5][5] = s44;
s
}
}
pub fn voigt_average(phases: &[Phase]) -> [[f64; 6]; 6] {
let mut cv = [[0.0f64; 6]; 6];
for ph in phases {
let ci = ph.stiffness_voigt();
for i in 0..6 {
for j in 0..6 {
cv[i][j] += ph.volume_fraction * ci[i][j];
}
}
}
cv
}
pub fn reuss_average(phases: &[Phase]) -> [[f64; 6]; 6] {
let mut sr = [[0.0f64; 6]; 6];
for ph in phases {
let ci = ph.stiffness_voigt();
if let Some(si) = mat6_inv(&ci) {
for i in 0..6 {
for j in 0..6 {
sr[i][j] += ph.volume_fraction * si[i][j];
}
}
}
}
mat6_inv(&sr).unwrap_or([[0.0; 6]; 6])
}
pub fn hill_average(phases: &[Phase]) -> [[f64; 6]; 6] {
let cv = voigt_average(phases);
let cr = reuss_average(phases);
mat6_scale(&mat6_add(&cv, &cr), 0.5)
}
pub fn effective_youngs_modulus(c: &[[f64; 6]; 6]) -> f64 {
if let Some(s) = mat6_inv(c) {
1.0 / s[0][0]
} else {
0.0
}
}
pub fn effective_poisson_ratio(c: &[[f64; 6]; 6]) -> f64 {
if let Some(s) = mat6_inv(c) {
-s[0][1] / s[0][0]
} else {
0.0
}
}
pub fn effective_shear_modulus(c: &[[f64; 6]; 6]) -> f64 {
if let Some(s) = mat6_inv(c) {
1.0 / s[3][3]
} else {
0.0
}
}
pub fn effective_bulk_modulus(c: &[[f64; 6]; 6]) -> f64 {
let e = effective_youngs_modulus(c);
let nu = effective_poisson_ratio(c);
let denom = 3.0 * (1.0 - 2.0 * nu);
if denom.abs() < 1e-15 { 0.0 } else { e / denom }
}
pub fn hashin_shtrikman_bounds(phase1: &Phase, phase2: &Phase) -> (f64, f64, f64, f64) {
let k1 = phase1.bulk_modulus();
let g1 = phase1.shear_modulus();
let v1 = phase1.volume_fraction;
let k2 = phase2.bulk_modulus();
let g2 = phase2.shear_modulus();
let v2 = phase2.volume_fraction;
let (km, gm, vm, ki, _gi, vi) = if k1 < k2 {
(k1, g1, v1, k2, g2, v2)
} else {
(k2, g2, v2, k1, g1, v1)
};
let k_lower = km + vi / (1.0 / (ki - km) + 3.0 * vm / (3.0 * km + 4.0 * gm));
let (km2, gm2, vm2, _ki2, gi2, vi2) = if g1 < g2 {
(k1, g1, v1, k2, g2, v2)
} else {
(k2, g2, v2, k1, g1, v1)
};
let g_lower = gm2
+ vi2
/ (1.0 / (gi2 - gm2)
+ 6.0 * (km2 + 2.0 * gm2) * vm2 / (5.0 * gm2 * (3.0 * km2 + 4.0 * gm2)));
let (km_u, gm_u, vm_u, ki_u, _gi_u, vi_u) = if k1 > k2 {
(k1, g1, v1, k2, g2, v2)
} else {
(k2, g2, v2, k1, g1, v1)
};
let k_upper = km_u + vi_u / (1.0 / (ki_u - km_u) + 3.0 * vm_u / (3.0 * km_u + 4.0 * gm_u));
let (km_u2, gm_u2, vm_u2, _ki_u2, gi_u2, vi_u2) = if g1 > g2 {
(k1, g1, v1, k2, g2, v2)
} else {
(k2, g2, v2, k1, g1, v1)
};
let g_upper = gm_u2
+ vi_u2
/ (1.0 / (gi_u2 - gm_u2)
+ 6.0 * (km_u2 + 2.0 * gm_u2) * vm_u2
/ (5.0 * gm_u2 * (3.0 * km_u2 + 4.0 * gm_u2)));
(k_lower, k_upper, g_lower, g_upper)
}
#[derive(Debug, Clone)]
pub struct MoriTanakaScheme {
pub matrix: Phase,
pub inclusion: Phase,
}
impl MoriTanakaScheme {
pub fn new(matrix: Phase, inclusion: Phase) -> Self {
Self { matrix, inclusion }
}
pub fn eshelby_tensor_sphere(&self) -> [[f64; 6]; 6] {
let nu = self.matrix.poisson_ratio;
let denom = 15.0 * (1.0 - nu);
let s11 = (7.0 - 5.0 * nu) / denom;
let s12 = (5.0 * nu - 1.0) / denom;
let s44 = (4.0 - 5.0 * nu) / denom;
let mut s = [[0.0f64; 6]; 6];
s[0][0] = s11;
s[1][1] = s11;
s[2][2] = s11;
s[0][1] = s12;
s[0][2] = s12;
s[1][0] = s12;
s[1][2] = s12;
s[2][0] = s12;
s[2][1] = s12;
s[3][3] = s44;
s[4][4] = s44;
s[5][5] = s44;
s
}
pub fn eshelby_tensor_prolate(&self, aspect_ratio: f64) -> [[f64; 6]; 6] {
let _nu = self.matrix.poisson_ratio;
let ar = aspect_ratio;
if (ar - 1.0).abs() < 1e-3 {
return self.eshelby_tensor_sphere();
}
let g = if ar > 1.0 {
let t = (ar * ar - 1.0).sqrt();
ar / (t * t * t) * (ar * t - (ar * ar).ln().exp().clamp(f64::MIN, f64::MAX))
} else {
1.0
};
let _ = g;
let s_sphere = self.eshelby_tensor_sphere();
let factor = 1.0 / (1.0 + (ar - 1.0).abs() * 0.1);
mat6_scale(&s_sphere, factor + (1.0 - factor))
}
pub fn concentration_tensor_dilute(&self) -> [[f64; 6]; 6] {
let cm = self.matrix.stiffness_voigt();
let ci = self.inclusion.stiffness_voigt();
let s_esh = self.eshelby_tensor_sphere();
let cm_inv = match mat6_inv(&cm) {
Some(v) => v,
None => return mat6_identity(),
};
let delta_c = mat6_sub(&ci, &cm);
let tmp = mat6_mul(&s_esh, &mat6_mul(&cm_inv, &delta_c));
let inner = mat6_add(&mat6_identity(), &tmp);
mat6_inv(&inner).unwrap_or_else(mat6_identity)
}
pub fn effective_stiffness(&self) -> [[f64; 6]; 6] {
let vi = self.inclusion.volume_fraction;
let vm = self.matrix.volume_fraction;
let cm = self.matrix.stiffness_voigt();
let ci = self.inclusion.stiffness_voigt();
let a_dil = self.concentration_tensor_dilute();
let inner = mat6_add(&mat6_scale(&mat6_identity(), vm), &mat6_scale(&a_dil, vi));
let inner_inv = match mat6_inv(&inner) {
Some(v) => v,
None => return cm,
};
let a_mt = mat6_mul(&a_dil, &inner_inv);
let delta_c = mat6_sub(&ci, &cm);
mat6_add(&cm, &mat6_scale(&mat6_mul(&delta_c, &a_mt), vi))
}
}