#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
use std::f64::consts::PI;
const R_GAS: f64 = 8.314_462_618;
#[derive(Debug, Clone)]
pub struct CeramicProperties {
pub young_modulus: f64,
pub poisson_ratio: f64,
pub thermal_conductivity: f64,
pub cte: f64,
pub hardness: f64,
pub fracture_toughness: f64,
pub flexural_strength: f64,
pub weibull_modulus: f64,
pub surface_energy: f64,
pub name: String,
}
impl CeramicProperties {
#[allow(clippy::too_many_arguments)]
pub fn new(
young_modulus: f64,
poisson_ratio: f64,
thermal_conductivity: f64,
cte: f64,
hardness: f64,
fracture_toughness: f64,
flexural_strength: f64,
weibull_modulus: f64,
surface_energy: f64,
name: impl Into<String>,
) -> Self {
Self {
young_modulus,
poisson_ratio,
thermal_conductivity,
cte,
hardness,
fracture_toughness,
flexural_strength,
weibull_modulus,
surface_energy,
name: name.into(),
}
}
pub fn alumina() -> Self {
Self::new(
380e9, 0.22, 30.0, 8.1e-6, 18.0, 4.0, 400e6, 10.0, 1.0, "Al2O3",
)
}
pub fn silicon_carbide() -> Self {
Self::new(
420e9, 0.17, 120.0, 4.5e-6, 27.0, 4.5, 500e6, 12.0, 1.5, "SiC",
)
}
pub fn silicon_nitride() -> Self {
Self::new(
310e9, 0.27, 30.0, 3.2e-6, 16.0, 7.0, 700e6, 15.0, 2.0, "Si3N4",
)
}
pub fn zirconia() -> Self {
Self::new(
200e9, 0.31, 2.5, 10.5e-6, 12.0, 10.0, 1000e6, 8.0, 1.2, "ZrO2",
)
}
pub fn titania() -> Self {
Self::new(
230e9, 0.27, 11.8, 8.4e-6, 10.0, 2.0, 200e6, 7.0, 0.5, "TiO2",
)
}
}
#[derive(Debug, Clone)]
pub struct BrittleFractureCeramic {
pub props: CeramicProperties,
pub geometry_factor: f64,
}
impl BrittleFractureCeramic {
pub fn new(props: CeramicProperties, geometry_factor: f64) -> Self {
Self {
props,
geometry_factor,
}
}
pub fn griffith_critical_stress(&self, crack_half_length: f64) -> f64 {
let e = self.props.young_modulus;
let gamma = self.props.surface_energy;
(2.0 * e * gamma / (PI * crack_half_length)).sqrt()
}
pub fn stress_intensity(&self, sigma: f64, crack_half_length: f64) -> f64 {
self.geometry_factor * sigma * (PI * crack_half_length).sqrt()
}
pub fn is_critical(&self, sigma: f64, crack_half_length: f64) -> bool {
let ki = self.stress_intensity(sigma, crack_half_length);
let kic = self.props.fracture_toughness * 1e6; ki >= kic
}
pub fn critical_crack_length(&self, sigma: f64) -> f64 {
let kic = self.props.fracture_toughness * 1e6;
let y_sigma = self.geometry_factor * sigma;
(kic / y_sigma).powi(2) / PI
}
pub fn weibull_survival_probability(&self, sigma: f64) -> f64 {
let sigma_0 = self.props.flexural_strength;
let m = self.props.weibull_modulus;
(-(sigma / sigma_0).powf(m)).exp()
}
pub fn weibull_failure_probability(&self, sigma: f64) -> f64 {
1.0 - self.weibull_survival_probability(sigma)
}
pub fn weibull_median_strength(&self) -> f64 {
let sigma_0 = self.props.flexural_strength;
let m = self.props.weibull_modulus;
sigma_0 * 2_f64.ln().powf(1.0 / m)
}
pub fn weibull_mean_strength(&self) -> f64 {
let sigma_0 = self.props.flexural_strength;
let m = self.props.weibull_modulus;
sigma_0 * gamma_approx(1.0 + 1.0 / m)
}
}
fn gamma_approx(x: f64) -> f64 {
let c = [
1.000_000_000_190_015,
76.180_091_729_471_46,
-86.505_320_329_416_77,
24.014_098_240_830_91,
-1.231_739_572_450_155,
1.208_650_973_866_179e-3,
-5.395_239_384_953_0e-6,
];
let x = x - 1.0;
let mut ser = c[0];
for (i, &ci) in c[1..].iter().enumerate() {
ser += ci / (x + i as f64 + 1.0);
}
let t = x + 5.5; std::f64::consts::TAU.sqrt() * t.powf(x + 0.5) * (-t).exp() * ser
}
#[derive(Debug, Clone)]
pub struct ThermalShockResistance {
pub props: CeramicProperties,
}
impl ThermalShockResistance {
pub fn new(props: CeramicProperties) -> Self {
Self { props }
}
pub fn r_parameter(&self) -> f64 {
let sigma_f = self.props.flexural_strength;
let nu = self.props.poisson_ratio;
let alpha = self.props.cte;
let e = self.props.young_modulus;
sigma_f * (1.0 - nu) / (alpha * e)
}
pub fn r_prime_parameter(&self) -> f64 {
self.props.thermal_conductivity * self.r_parameter()
}
pub fn critical_temperature_difference(&self) -> f64 {
self.r_parameter()
}
pub fn r_damage_parameter(&self) -> f64 {
let e = self.props.young_modulus;
let gamma = self.props.surface_energy;
let sigma_f = self.props.flexural_strength;
e * gamma / (sigma_f * sigma_f)
}
}
#[derive(Debug, Clone)]
pub struct SinteringModel {
pub neck_growth_prefactor: f64,
pub neck_growth_exponent: f64,
pub activation_energy_neck: f64,
pub particle_radius: f64,
pub densification_prefactor: f64,
pub densification_grain_exponent: f64,
pub activation_energy_densification: f64,
pub initial_density: f64,
}
impl SinteringModel {
#[allow(clippy::too_many_arguments)]
pub fn new(
neck_growth_prefactor: f64,
neck_growth_exponent: f64,
activation_energy_neck: f64,
particle_radius: f64,
densification_prefactor: f64,
densification_grain_exponent: f64,
activation_energy_densification: f64,
initial_density: f64,
) -> Self {
Self {
neck_growth_prefactor,
neck_growth_exponent,
activation_energy_neck,
particle_radius,
densification_prefactor,
densification_grain_exponent,
activation_energy_densification,
initial_density,
}
}
pub fn neck_ratio(&self, time: f64, temp: f64) -> f64 {
let n = self.neck_growth_exponent;
let rate = self.neck_growth_prefactor
* time
* (-self.activation_energy_neck / (R_GAS * temp)).exp();
rate.powf(1.0 / n)
}
pub fn densification_rate(&self, r_grain: f64, temp: f64) -> f64 {
let m = self.densification_grain_exponent;
self.densification_prefactor
* r_grain.powf(-m)
* (-self.activation_energy_densification / (R_GAS * temp)).exp()
}
pub fn density_at_time(&self, time: f64, r_grain: f64, temp: f64, steps: usize) -> f64 {
let dt = time / steps as f64;
let mut rho = self.initial_density;
let rate = self.densification_rate(r_grain, temp);
for _ in 0..steps {
rho += rate * dt;
if rho >= 1.0 {
return 1.0;
}
}
rho
}
}
#[derive(Debug, Clone)]
pub struct GrainGrowth {
pub exponent: f64,
pub prefactor: f64,
pub activation_energy: f64,
pub initial_grain_size: f64,
}
impl GrainGrowth {
pub fn new(
exponent: f64,
prefactor: f64,
activation_energy: f64,
initial_grain_size: f64,
) -> Self {
Self {
exponent,
prefactor,
activation_energy,
initial_grain_size,
}
}
pub fn grain_size(&self, time: f64, temp: f64) -> f64 {
let n = self.exponent;
let d0n = self.initial_grain_size.powf(n);
let kt = self.prefactor * time * (-self.activation_energy / (R_GAS * temp)).exp();
(d0n + kt).powf(1.0 / n)
}
pub fn growth_rate(&self, d: f64, temp: f64) -> f64 {
let n = self.exponent;
let k_eff = self.prefactor * (-self.activation_energy / (R_GAS * temp)).exp();
k_eff / (n * d.powf(n - 1.0))
}
pub fn is_saturated(&self, time: f64, temp: f64, tol: f64) -> bool {
let d = self.grain_size(time, temp);
let d_long = self.grain_size(time * 1e6, temp);
(d_long - d) / d_long < tol
}
}
#[derive(Debug, Clone)]
pub struct CreepCeramic {
pub prefactor: f64,
pub stress_exponent: f64,
pub activation_energy: f64,
}
impl CreepCeramic {
pub fn new(prefactor: f64, stress_exponent: f64, activation_energy: f64) -> Self {
Self {
prefactor,
stress_exponent,
activation_energy,
}
}
pub fn creep_rate(&self, sigma: f64, temp: f64) -> f64 {
self.prefactor
* sigma.powf(self.stress_exponent)
* (-self.activation_energy / (R_GAS * temp)).exp()
}
pub fn apparent_activation_energy(rate1: f64, temp1: f64, rate2: f64, temp2: f64) -> f64 {
R_GAS * temp1 * temp2 / (temp2 - temp1) * (rate2 / rate1).ln()
}
pub fn accumulated_strain(&self, sigma: f64, temp: f64, time: f64) -> f64 {
self.creep_rate(sigma, temp) * time
}
}
#[derive(Debug, Clone)]
pub struct DielectricCeramic {
pub curie_constant: f64,
pub curie_temperature: f64,
pub d33: f64,
pub d31: f64,
pub permittivity_ferroelectric: f64,
}
impl DielectricCeramic {
pub fn new(
curie_constant: f64,
curie_temperature: f64,
d33: f64,
d31: f64,
permittivity_ferroelectric: f64,
) -> Self {
Self {
curie_constant,
curie_temperature,
d33,
d31,
permittivity_ferroelectric,
}
}
pub fn barium_titanate() -> Self {
Self::new(1.7e5, 393.0, 190.0, -79.0, 4000.0)
}
pub fn permittivity_above_tc(&self, temp: f64) -> Option<f64> {
if temp <= self.curie_temperature {
None
} else {
Some(self.curie_constant / (temp - self.curie_temperature))
}
}
pub fn permittivity(&self, temp: f64) -> f64 {
self.permittivity_above_tc(temp)
.unwrap_or(self.permittivity_ferroelectric)
}
pub fn electric_displacement(&self, e_field: f64, temp: f64) -> f64 {
const EPS0: f64 = 8.854_187_817e-12;
EPS0 * self.permittivity(temp) * e_field
}
pub fn piezo_strain_33(&self, e3: f64) -> f64 {
self.d33 * 1e-12 * e3
}
}
#[derive(Debug, Clone)]
pub struct ZrO2Transformation {
pub transformation_strain: f64,
pub volume_fraction: f64,
pub shear_modulus: f64,
pub base_toughness: f64,
pub zone_height: f64,
}
impl ZrO2Transformation {
pub fn new(
transformation_strain: f64,
volume_fraction: f64,
shear_modulus: f64,
base_toughness: f64,
zone_height: f64,
) -> Self {
Self {
transformation_strain,
volume_fraction,
shear_modulus,
base_toughness,
zone_height,
}
}
pub fn toughening_increment(&self) -> f64 {
let e_eff = 2.0 * self.shear_modulus;
0.22 * e_eff * self.transformation_strain * self.volume_fraction * self.zone_height.sqrt()
}
pub fn effective_toughness(&self) -> f64 {
self.base_toughness + self.toughening_increment()
}
pub fn zone_size_estimate(&self) -> f64 {
let sigma_t =
self.shear_modulus * self.transformation_strain / (1.0 - self.volume_fraction);
let kic = self.effective_toughness();
(kic / sigma_t).powi(2) / (3.0 * PI)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alumina_young_modulus() {
let al2o3 = CeramicProperties::alumina();
assert!(
(al2o3.young_modulus - 380e9).abs() < 1e6,
"E(Al2O3) should be ~380 GPa"
);
}
#[test]
fn test_sic_fracture_toughness() {
let sic = CeramicProperties::silicon_carbide();
assert!((sic.fracture_toughness - 4.5).abs() < 0.01);
}
#[test]
fn test_zirconia_high_toughness() {
let zro2 = CeramicProperties::zirconia();
assert!(zro2.fracture_toughness > CeramicProperties::alumina().fracture_toughness);
}
#[test]
fn test_titania_properties_positive() {
let tio2 = CeramicProperties::titania();
assert!(tio2.young_modulus > 0.0);
assert!(tio2.thermal_conductivity > 0.0);
assert!(tio2.cte > 0.0);
}
#[test]
fn test_griffith_larger_crack_lower_strength() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props, 1.12);
let sigma_small = frac.griffith_critical_stress(1e-6);
let sigma_large = frac.griffith_critical_stress(100e-6);
assert!(
sigma_small > sigma_large,
"Larger crack → lower Griffith strength"
);
}
#[test]
fn test_stress_intensity_proportional_to_sigma() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props, 1.12);
let k1 = frac.stress_intensity(100e6, 10e-6);
let k2 = frac.stress_intensity(200e6, 10e-6);
assert!((k2 / k1 - 2.0).abs() < 1e-10);
}
#[test]
fn test_is_critical_small_crack_not_critical() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props, 1.12);
assert!(!frac.is_critical(100e6, 1e-9));
}
#[test]
fn test_is_critical_large_crack_is_critical() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props, 1.12);
assert!(frac.is_critical(400e6, 1e-3));
}
#[test]
fn test_critical_crack_length_decreases_with_stress() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props, 1.12);
let a1 = frac.critical_crack_length(100e6);
let a2 = frac.critical_crack_length(400e6);
assert!(a1 > a2, "Higher stress → shorter critical crack");
}
#[test]
fn test_weibull_survival_at_zero_stress() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props, 1.12);
let ps = frac.weibull_survival_probability(0.0);
assert!((ps - 1.0).abs() < 1e-12, "Survival at zero stress = 1");
}
#[test]
fn test_weibull_survival_at_characteristic_strength() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props.clone(), 1.12);
let ps = frac.weibull_survival_probability(props.flexural_strength);
assert!((ps - (-1.0_f64).exp()).abs() < 1e-10);
}
#[test]
fn test_weibull_failure_probability_complement() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props, 1.12);
let sigma = 300e6;
let ps = frac.weibull_survival_probability(sigma);
let pf = frac.weibull_failure_probability(sigma);
assert!((ps + pf - 1.0).abs() < 1e-12);
}
#[test]
fn test_weibull_high_modulus_steep_transition() {
let mut props = CeramicProperties::alumina();
props.weibull_modulus = 100.0;
let frac = BrittleFractureCeramic::new(props.clone(), 1.12);
let pf_below = frac.weibull_failure_probability(0.99 * props.flexural_strength);
let pf_above = frac.weibull_failure_probability(1.01 * props.flexural_strength);
assert!(pf_above - pf_below > 0.5, "High m → steep transition");
}
#[test]
fn test_weibull_median_less_than_characteristic() {
let props = CeramicProperties::alumina();
let frac = BrittleFractureCeramic::new(props.clone(), 1.12);
let median = frac.weibull_median_strength();
assert!(median < props.flexural_strength);
}
#[test]
fn test_weibull_mean_strength_positive() {
let props = CeramicProperties::silicon_nitride();
let frac = BrittleFractureCeramic::new(props, 1.12);
assert!(frac.weibull_mean_strength() > 0.0);
}
#[test]
fn test_r_parameter_increases_with_strength() {
let mut p1 = CeramicProperties::alumina();
let mut p2 = CeramicProperties::alumina();
p2.flexural_strength *= 2.0;
let r1 = ThermalShockResistance::new(p1.clone()).r_parameter();
let r2 = ThermalShockResistance::new(p2).r_parameter();
assert!(r2 > r1);
p1.name = p1.name.clone();
}
#[test]
fn test_r_parameter_decreases_with_youngs_modulus() {
let mut p1 = CeramicProperties::alumina();
let mut p2 = CeramicProperties::alumina();
p2.young_modulus *= 2.0;
let r1 = ThermalShockResistance::new(p1.clone()).r_parameter();
let r2 = ThermalShockResistance::new(p2).r_parameter();
assert!(r1 > r2, "Higher E → lower R");
p1.name = p1.name.clone();
}
#[test]
fn test_r_prime_greater_than_r_for_high_k() {
let props = CeramicProperties::silicon_carbide(); let tsr = ThermalShockResistance::new(props);
assert!(tsr.r_prime_parameter() > tsr.r_parameter());
}
#[test]
fn test_r_damage_parameter_positive() {
let props = CeramicProperties::zirconia();
let tsr = ThermalShockResistance::new(props);
assert!(tsr.r_damage_parameter() > 0.0);
}
#[test]
fn test_critical_temperature_difference_equals_r() {
let props = CeramicProperties::alumina();
let tsr = ThermalShockResistance::new(props);
assert!((tsr.critical_temperature_difference() - tsr.r_parameter()).abs() < 1e-12);
}
#[test]
fn test_density_increases_with_time() {
let model = SinteringModel::new(1e-5, 5.0, 300e3, 1e-6, 1e-8, 3.0, 450e3, 0.60);
let rho_early = model.density_at_time(100.0, 1e-6, 1800.0, 100);
let rho_late = model.density_at_time(10000.0, 1e-6, 1800.0, 100);
assert!(
rho_late > rho_early,
"Density should increase with sintering time"
);
}
#[test]
fn test_density_bounded_by_unity() {
let model = SinteringModel::new(1e-5, 5.0, 100e3, 1e-6, 1e-5, 3.0, 100e3, 0.60);
let rho = model.density_at_time(1e9, 1e-6, 2000.0, 1000);
assert!(rho <= 1.0, "Density cannot exceed 1");
}
#[test]
fn test_neck_ratio_increases_with_time() {
let model = SinteringModel::new(1e-10, 5.0, 300e3, 1e-6, 1e-8, 3.0, 450e3, 0.60);
let xr_early = model.neck_ratio(100.0, 1600.0);
let xr_late = model.neck_ratio(10000.0, 1600.0);
assert!(xr_late > xr_early);
}
#[test]
fn test_neck_ratio_increases_with_temperature() {
let model = SinteringModel::new(1e-10, 5.0, 300e3, 1e-6, 1e-8, 3.0, 450e3, 0.60);
let xr_low = model.neck_ratio(1000.0, 1400.0);
let xr_high = model.neck_ratio(1000.0, 1800.0);
assert!(xr_high > xr_low);
}
#[test]
fn test_densification_rate_positive() {
let model = SinteringModel::new(1e-10, 5.0, 300e3, 1e-6, 1e-8, 3.0, 450e3, 0.60);
let rate = model.densification_rate(1e-6, 1800.0);
assert!(rate > 0.0);
}
#[test]
fn test_grain_growth_increases_with_time() {
let gg = GrainGrowth::new(2.0, 1e-14, 300e3, 1e-6);
let d1 = gg.grain_size(100.0, 1500.0);
let d2 = gg.grain_size(10000.0, 1500.0);
assert!(d2 > d1, "Grain size should increase with annealing time");
}
#[test]
fn test_grain_size_at_zero_time_equals_initial() {
let d0 = 1.5e-6;
let gg = GrainGrowth::new(2.0, 1e-14, 300e3, d0);
let d = gg.grain_size(0.0, 1500.0);
assert!((d - d0).abs() < 1e-15, "d(t=0) should equal d0");
}
#[test]
fn test_grain_growth_faster_at_higher_temp() {
let gg = GrainGrowth::new(2.0, 1e-14, 300e3, 1e-6);
let d_low = gg.grain_size(3600.0, 1200.0);
let d_high = gg.grain_size(3600.0, 1600.0);
assert!(d_high > d_low);
}
#[test]
fn test_grain_growth_exponent_effect() {
let d0 = 1e-6_f64;
let gg_n2 = GrainGrowth::new(2.0, 1e-10, 0.0, d0); let gg_n3 = GrainGrowth::new(3.0, 1e-10, 0.0, d0);
let d_n2 = gg_n2.grain_size(1.0, 1000.0);
let d_n3 = gg_n3.grain_size(1.0, 1000.0);
assert!(d_n2 > d0, "n=2 grain size should grow beyond initial");
assert!(d_n3 > d0, "n=3 grain size should grow beyond initial");
assert!(d_n2 > d0 * 1.5 || d_n3 > d0 * 1.5);
}
#[test]
fn test_growth_rate_positive() {
let gg = GrainGrowth::new(2.0, 1e-14, 300e3, 1e-6);
assert!(gg.growth_rate(2e-6, 1500.0) > 0.0);
}
#[test]
fn test_creep_rate_exponential_temperature() {
let creep = CreepCeramic::new(1e-30, 3.0, 500e3);
let rate_low = creep.creep_rate(100e6, 1000.0);
let rate_high = creep.creep_rate(100e6, 1400.0);
assert!(
rate_high > rate_low * 100.0,
"Creep rate increases strongly with temperature"
);
}
#[test]
fn test_creep_rate_power_law_stress() {
let creep = CreepCeramic::new(1e-30, 3.0, 500e3);
let r1 = creep.creep_rate(100e6, 1200.0);
let r2 = creep.creep_rate(200e6, 1200.0);
assert!((r2 / r1 - 8.0).abs() < 1e-10);
}
#[test]
fn test_accumulated_strain_proportional_to_time() {
let creep = CreepCeramic::new(1e-30, 3.0, 500e3);
let eps1 = creep.accumulated_strain(100e6, 1200.0, 1000.0);
let eps2 = creep.accumulated_strain(100e6, 1200.0, 2000.0);
assert!((eps2 / eps1 - 2.0).abs() < 1e-12);
}
#[test]
fn test_apparent_activation_energy_estimate() {
let creep = CreepCeramic::new(1e-30, 3.0, 500e3);
let t1 = 1200.0_f64;
let t2 = 1400.0_f64;
let r1 = creep.creep_rate(100e6, t1);
let r2 = creep.creep_rate(100e6, t2);
let q_est = CreepCeramic::apparent_activation_energy(r1, t1, r2, t2);
assert!(
(q_est - 500e3).abs() / 500e3 < 0.01,
"Q estimate should be within 1% of true value"
);
}
#[test]
fn test_curie_weiss_diverges_near_tc() {
let bto = DielectricCeramic::barium_titanate();
let eps_near = bto
.permittivity_above_tc(bto.curie_temperature + 1.0)
.unwrap();
let eps_far = bto
.permittivity_above_tc(bto.curie_temperature + 100.0)
.unwrap();
assert!(eps_near > eps_far * 10.0, "ε_r diverges near T_C");
}
#[test]
fn test_permittivity_below_tc_returns_ferroelectric_value() {
let bto = DielectricCeramic::barium_titanate();
let eps = bto.permittivity(300.0); assert!((eps - bto.permittivity_ferroelectric).abs() < 1e-12);
}
#[test]
fn test_permittivity_above_tc_is_none_at_tc() {
let bto = DielectricCeramic::barium_titanate();
assert!(bto.permittivity_above_tc(bto.curie_temperature).is_none());
}
#[test]
fn test_electric_displacement_proportional_to_field() {
let bto = DielectricCeramic::barium_titanate();
let d1 = bto.electric_displacement(1e5, 500.0);
let d2 = bto.electric_displacement(2e5, 500.0);
assert!((d2 / d1 - 2.0).abs() < 1e-10);
}
#[test]
fn test_piezo_strain_33_sign() {
let bto = DielectricCeramic::barium_titanate();
assert!(bto.piezo_strain_33(1e6) > 0.0);
}
#[test]
fn test_toughening_increment_positive() {
let zro2 = ZrO2Transformation::new(0.04, 0.3, 80e9, 5e6, 1e-4);
assert!(zro2.toughening_increment() > 0.0);
}
#[test]
fn test_effective_toughness_greater_than_base() {
let zro2 = ZrO2Transformation::new(0.04, 0.3, 80e9, 5e6, 1e-4);
assert!(zro2.effective_toughness() > zro2.base_toughness);
}
#[test]
fn test_toughening_increases_with_volume_fraction() {
let zro2_low = ZrO2Transformation::new(0.04, 0.1, 80e9, 5e6, 1e-4);
let zro2_high = ZrO2Transformation::new(0.04, 0.4, 80e9, 5e6, 1e-4);
assert!(zro2_high.toughening_increment() > zro2_low.toughening_increment());
}
#[test]
fn test_zone_size_estimate_positive() {
let zro2 = ZrO2Transformation::new(0.04, 0.3, 80e9, 5e6, 1e-4);
assert!(zro2.zone_size_estimate() > 0.0);
}
#[test]
fn test_toughening_increases_with_strain() {
let zro2_low = ZrO2Transformation::new(0.02, 0.3, 80e9, 5e6, 1e-4);
let zro2_high = ZrO2Transformation::new(0.06, 0.3, 80e9, 5e6, 1e-4);
assert!(zro2_high.toughening_increment() > zro2_low.toughening_increment());
}
}