#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub struct ContactInterface {
pub roughness_rms: f64,
pub young_modulus: f64,
pub poisson_ratio: f64,
pub hardness: f64,
}
impl ContactInterface {
pub fn new(roughness_rms: f64, young_modulus: f64, poisson_ratio: f64, hardness: f64) -> Self {
Self {
roughness_rms,
young_modulus,
poisson_ratio,
hardness,
}
}
}
pub fn reduced_modulus(e1: f64, nu1: f64, e2: f64, nu2: f64) -> f64 {
1.0 / ((1.0 - nu1 * nu1) / e1 + (1.0 - nu2 * nu2) / e2)
}
pub fn hertz_contact_radius(r1: f64, r2: f64, e_star: f64, load: f64) -> f64 {
let r_eff = r1 * r2 / (r1 + r2);
(3.0 * load * r_eff / (4.0 * e_star)).cbrt()
}
pub fn hertz_max_pressure(load: f64, contact_radius: f64) -> f64 {
3.0 * load / (2.0 * PI * contact_radius * contact_radius)
}
pub fn stribeck_curve(speed: f64, viscosity: f64, pressure: f64, r: f64) -> f64 {
viscosity * speed / (pressure * r)
}
pub fn hydrodynamic_lift(viscosity: f64, speed: f64, gap: f64, length: f64, width: f64) -> f64 {
viscosity * speed * length * width / (gap * gap)
}
#[derive(Debug, Clone, Copy)]
pub struct ArchardWear {
pub k: f64,
pub hardness: f64,
}
impl ArchardWear {
pub fn new(k: f64, hardness: f64) -> Self {
Self { k, hardness }
}
pub fn wear_volume(&self, load: f64, sliding_dist: f64) -> f64 {
self.k * load * sliding_dist / self.hardness
}
pub fn wear_depth(&self, load: f64, sliding_dist: f64, area: f64) -> f64 {
self.wear_volume(load, sliding_dist) / area
}
}
pub fn sommerfeld_number(viscosity: f64, speed: f64, load: f64, r: f64, l: f64, c: f64) -> f64 {
let c_over_r = c / r;
viscosity * speed * r * l / (load * c_over_r * c_over_r)
}
pub fn elasto_hydrodynamic_film_thickness(
r_eff: f64,
e_star: f64,
u: f64,
eta: f64,
load: f64,
) -> f64 {
let e_prime = 2.0 * e_star;
let w_star = load / (e_prime * r_eff * r_eff);
let u_star = eta * u / (e_prime * r_eff);
r_eff * 2.69 * u_star.powf(0.67) * w_star.powf(-0.067)
}
pub fn friction_power_dissipation(friction_force: f64, sliding_velocity: f64) -> f64 {
friction_force * sliding_velocity
}
pub fn flash_temperature(q_friction: f64, r_contact: f64, kappa: f64, rho_cp: f64, v: f64) -> f64 {
let kappa_diff = kappa / rho_cp;
let peclet_term = (PI * r_contact / (v * kappa_diff)).sqrt();
q_friction * peclet_term / (2.0 * kappa)
}
#[cfg(test)]
mod tests {
use super::*;
const EPS: f64 = 1e-9;
#[test]
fn test_reduced_modulus_positive() {
let e_star = reduced_modulus(200e9, 0.3, 200e9, 0.3);
assert!(
e_star > 0.0,
"Reduced modulus must be positive, got {e_star}"
);
}
#[test]
fn test_reduced_modulus_equal_materials() {
let e = 200e9_f64;
let nu = 0.3_f64;
let e_star = reduced_modulus(e, nu, e, nu);
let expected = e / (2.0 * (1.0 - nu * nu));
assert!(
(e_star - expected).abs() / expected < 1e-9,
"Equal materials: E* = {e_star} vs expected {expected}"
);
}
#[test]
fn test_reduced_modulus_increases_with_e() {
let e1 = reduced_modulus(100e9, 0.3, 200e9, 0.3);
let e2 = reduced_modulus(200e9, 0.3, 200e9, 0.3);
assert!(e2 > e1, "Larger E should give larger E*");
}
#[test]
fn test_hertz_contact_radius_positive() {
let a = hertz_contact_radius(0.01, 0.01, 100e9, 100.0);
assert!(a > 0.0, "Contact radius must be positive, got {a}");
}
#[test]
fn test_hertz_contact_radius_increases_with_load() {
let a1 = hertz_contact_radius(0.01, 0.01, 100e9, 100.0);
let a2 = hertz_contact_radius(0.01, 0.01, 100e9, 1000.0);
assert!(
a2 > a1,
"Contact radius should increase with load: a1={a1}, a2={a2}"
);
}
#[test]
fn test_hertz_contact_radius_scaling() {
let r1 = 0.01_f64;
let e_star = 100e9_f64;
let a1 = hertz_contact_radius(r1, r1, e_star, 100.0);
let a8 = hertz_contact_radius(r1, r1, e_star, 800.0);
let ratio = a8 / a1;
assert!(
(ratio - 2.0).abs() < 1e-6,
"Contact radius scales as F^1/3, got ratio={ratio}"
);
}
#[test]
fn test_hertz_max_pressure_formula() {
let load = 100.0_f64;
let a = 1e-3_f64;
let p0 = hertz_max_pressure(load, a);
let expected = 3.0 * load / (2.0 * PI * a * a);
assert!(
(p0 - expected).abs() < EPS,
"Max pressure formula mismatch: {p0} vs {expected}"
);
}
#[test]
fn test_hertz_max_pressure_positive() {
let p0 = hertz_max_pressure(50.0, 5e-4);
assert!(p0 > 0.0, "Max pressure should be positive, got {p0}");
}
#[test]
fn test_hertz_max_pressure_increases_with_load() {
let a = 1e-3_f64;
let p1 = hertz_max_pressure(100.0, a);
let p2 = hertz_max_pressure(200.0, a);
assert!(p2 > p1, "Higher load → higher max pressure");
}
#[test]
fn test_stribeck_positive() {
let s = stribeck_curve(1.0, 0.001, 1e6, 1e-3);
assert!(s > 0.0, "Stribeck number must be positive, got {s}");
}
#[test]
fn test_stribeck_increases_with_speed() {
let s1 = stribeck_curve(1.0, 0.001, 1e6, 1e-3);
let s2 = stribeck_curve(10.0, 0.001, 1e6, 1e-3);
assert!(s2 > s1, "Stribeck number should increase with speed");
}
#[test]
fn test_stribeck_scales_with_viscosity() {
let s1 = stribeck_curve(1.0, 0.001, 1e6, 1e-3);
let s2 = stribeck_curve(1.0, 0.002, 1e6, 1e-3);
assert!(
(s2 / s1 - 2.0).abs() < EPS,
"Stribeck scales with viscosity: {s2} vs 2*{s1}"
);
}
#[test]
fn test_hydro_lift_positive() {
let f = hydrodynamic_lift(0.001, 5.0, 1e-5, 0.1, 0.05);
assert!(f > 0.0, "Hydrodynamic lift must be positive, got {f}");
}
#[test]
fn test_hydro_lift_increases_with_speed() {
let f1 = hydrodynamic_lift(0.001, 1.0, 1e-5, 0.1, 0.05);
let f2 = hydrodynamic_lift(0.001, 2.0, 1e-5, 0.1, 0.05);
assert!(f2 > f1, "Lift should increase with speed");
}
#[test]
fn test_hydro_lift_gap_scaling() {
let f1 = hydrodynamic_lift(0.001, 1.0, 1e-5, 0.1, 0.05);
let f2 = hydrodynamic_lift(0.001, 1.0, 2e-5, 0.1, 0.05);
let ratio = f1 / f2;
assert!(
(ratio - 4.0).abs() < 1e-6,
"Lift scales as 1/h^2: got ratio={ratio}"
);
}
#[test]
fn test_archard_wear_scales_with_load() {
let wear = ArchardWear::new(1e-4, 1e9);
let v1 = wear.wear_volume(100.0, 1.0);
let v2 = wear.wear_volume(200.0, 1.0);
assert!(
(v2 / v1 - 2.0).abs() < EPS,
"Wear volume should scale with load: v1={v1}, v2={v2}"
);
}
#[test]
fn test_archard_wear_scales_with_distance() {
let wear = ArchardWear::new(1e-4, 1e9);
let v1 = wear.wear_volume(100.0, 1.0);
let v2 = wear.wear_volume(100.0, 5.0);
assert!(
(v2 / v1 - 5.0).abs() < EPS,
"Wear volume should scale with sliding distance: {v2} vs 5*{v1}"
);
}
#[test]
fn test_archard_wear_positive() {
let wear = ArchardWear::new(1e-5, 5e9);
let v = wear.wear_volume(500.0, 10.0);
assert!(v > 0.0, "Wear volume must be positive, got {v}");
}
#[test]
fn test_archard_wear_depth_consistent() {
let wear = ArchardWear::new(1e-4, 1e9);
let area = 1e-4_f64; let vol = wear.wear_volume(100.0, 0.5);
let depth = wear.wear_depth(100.0, 0.5, area);
assert!(
(depth - vol / area).abs() < EPS,
"Wear depth must be vol/area: {depth} vs {}",
vol / area
);
}
#[test]
fn test_archard_harder_less_wear() {
let soft = ArchardWear::new(1e-4, 1e9);
let hard = ArchardWear::new(1e-4, 5e9);
let v_soft = soft.wear_volume(100.0, 1.0);
let v_hard = hard.wear_volume(100.0, 1.0);
assert!(v_hard < v_soft, "Harder material should wear less");
}
#[test]
fn test_sommerfeld_positive() {
let s = sommerfeld_number(0.01, 100.0, 1000.0, 0.05, 0.05, 0.0001);
assert!(s > 0.0, "Sommerfeld number must be positive, got {s}");
}
#[test]
fn test_sommerfeld_increases_with_viscosity() {
let s1 = sommerfeld_number(0.01, 100.0, 1000.0, 0.05, 0.05, 0.0001);
let s2 = sommerfeld_number(0.02, 100.0, 1000.0, 0.05, 0.05, 0.0001);
assert!(s2 > s1, "Sommerfeld increases with viscosity");
}
#[test]
fn test_sommerfeld_scales_with_speed() {
let s1 = sommerfeld_number(0.01, 100.0, 1000.0, 0.05, 0.05, 0.0001);
let s2 = sommerfeld_number(0.01, 200.0, 1000.0, 0.05, 0.05, 0.0001);
assert!(
(s2 / s1 - 2.0).abs() < EPS,
"Sommerfeld scales with speed: got ratio {}",
s2 / s1
);
}
#[test]
fn test_ehl_film_positive() {
let h = elasto_hydrodynamic_film_thickness(0.01, 100e9, 1.0, 0.01, 1000.0);
assert!(h > 0.0, "EHL film thickness must be positive, got {h}");
}
#[test]
fn test_ehl_film_increases_with_speed() {
let h1 = elasto_hydrodynamic_film_thickness(0.01, 100e9, 0.5, 0.01, 1000.0);
let h2 = elasto_hydrodynamic_film_thickness(0.01, 100e9, 1.0, 0.01, 1000.0);
assert!(h2 > h1, "EHL film increases with speed: h1={h1}, h2={h2}");
}
#[test]
fn test_friction_power_positive() {
let p = friction_power_dissipation(50.0, 2.0);
assert!(p > 0.0, "Friction power must be positive, got {p}");
}
#[test]
fn test_friction_power_formula() {
let f = 75.0_f64;
let v = 3.5_f64;
let p = friction_power_dissipation(f, v);
assert!(
(p - f * v).abs() < EPS,
"P = F*v: got {p}, expected {}",
f * v
);
}
#[test]
fn test_friction_power_scales_with_force() {
let p1 = friction_power_dissipation(10.0, 2.0);
let p2 = friction_power_dissipation(20.0, 2.0);
assert!(
(p2 / p1 - 2.0).abs() < EPS,
"Friction power scales with force"
);
}
#[test]
fn test_flash_temperature_positive() {
let dt = flash_temperature(1e6, 1e-4, 50.0, 3e6, 1.0);
assert!(dt > 0.0, "Flash temperature must be positive, got {dt}");
}
#[test]
fn test_flash_temperature_increases_with_flux() {
let dt1 = flash_temperature(1e6, 1e-4, 50.0, 3e6, 1.0);
let dt2 = flash_temperature(2e6, 1e-4, 50.0, 3e6, 1.0);
assert!(dt2 > dt1, "Higher heat flux → higher flash temperature");
}
#[test]
fn test_flash_temperature_decreases_with_conductivity() {
let dt1 = flash_temperature(1e6, 1e-4, 50.0, 3e6, 1.0);
let dt2 = flash_temperature(1e6, 1e-4, 100.0, 3e6, 1.0);
assert!(dt2 < dt1, "Higher conductivity → lower flash temperature");
}
#[test]
fn test_contact_interface_fields() {
let ci = ContactInterface::new(1e-6, 200e9, 0.3, 2e9);
assert!((ci.roughness_rms - 1e-6).abs() < EPS);
assert!((ci.young_modulus - 200e9).abs() < 1.0);
assert!((ci.poisson_ratio - 0.3).abs() < EPS);
assert!((ci.hardness - 2e9).abs() < 1.0);
}
#[test]
fn test_hertz_contact_radius_larger_sphere() {
let a1 = hertz_contact_radius(0.01, 0.01, 100e9, 100.0);
let a2 = hertz_contact_radius(0.02, 0.02, 100e9, 100.0);
assert!(a2 > a1, "Larger spheres → larger contact radius");
}
#[test]
fn test_archard_new_fields() {
let w = ArchardWear::new(2e-4, 3e9);
assert!((w.k - 2e-4).abs() < EPS);
assert!((w.hardness - 3e9).abs() < 1.0);
}
#[test]
fn test_stribeck_zero_speed() {
let s = stribeck_curve(0.0, 0.001, 1e6, 1e-3);
assert_eq!(s, 0.0, "Stribeck = 0 at zero speed");
}
#[test]
fn test_hertz_pressure_decreases_with_radius() {
let p1 = hertz_max_pressure(100.0, 1e-3);
let p2 = hertz_max_pressure(100.0, 2e-3);
assert!(
p2 < p1,
"Larger contact radius → lower pressure for same load"
);
}
#[test]
fn test_sommerfeld_decreases_with_load() {
let s1 = sommerfeld_number(0.01, 100.0, 500.0, 0.05, 0.05, 0.0001);
let s2 = sommerfeld_number(0.01, 100.0, 1000.0, 0.05, 0.05, 0.0001);
assert!(s2 < s1, "Higher load → lower Sommerfeld number");
}
#[test]
fn test_ehl_film_increases_with_viscosity() {
let h1 = elasto_hydrodynamic_film_thickness(0.01, 100e9, 1.0, 0.01, 1000.0);
let h2 = elasto_hydrodynamic_film_thickness(0.01, 100e9, 1.0, 0.02, 1000.0);
assert!(h2 > h1, "Higher viscosity → thicker EHL film");
}
}