#[allow(unused_imports)]
use super::functions::*;
use std::f64::consts::PI;
#[derive(Debug, Clone)]
pub struct MeniscusModel {
pub e_circumferential: f64,
pub e_radial: f64,
pub shear_modulus: f64,
pub permeability: f64,
pub fluid_fraction: f64,
}
impl MeniscusModel {
pub fn new(
e_circumferential: f64,
e_radial: f64,
shear_modulus: f64,
permeability: f64,
fluid_fraction: f64,
) -> Self {
Self {
e_circumferential,
e_radial,
shear_modulus,
permeability,
fluid_fraction,
}
}
pub fn medial_meniscus() -> Self {
Self::new(150e6, 1.4e6, 0.12e6, 1.2e-15, 0.72)
}
pub fn hoop_stress(&self, joint_force: f64, area: f64) -> f64 {
joint_force / area
}
}
#[derive(Debug, Clone)]
pub struct CorticalBone {
pub density: f64,
pub ash_fraction: f64,
}
impl CorticalBone {
pub fn new(density: f64, ash_fraction: f64) -> Self {
Self {
density,
ash_fraction,
}
}
pub fn femur_cortical() -> Self {
Self::new(1.85, 0.62)
}
pub fn elastic_modulus(&self) -> f64 {
2017.0 * self.density.powf(2.5)
}
pub fn strength_compressive(&self) -> f64 {
68.0 * self.density.powf(1.6)
}
pub fn strength_tensile(&self) -> f64 {
131.0 * self.density
}
pub fn mineral_stiffness_contribution(&self) -> f64 {
self.elastic_modulus() * (0.6 + 0.4 * self.ash_fraction)
}
}
#[derive(Debug, Clone)]
pub struct MuscleModel {
pub f_max: f64,
pub l_opt: f64,
pub v_max: f64,
pub pennation_angle: f64,
}
impl MuscleModel {
pub fn new(f_max: f64, l_opt: f64, v_max: f64, pennation_angle: f64) -> Self {
Self {
f_max,
l_opt,
v_max,
pennation_angle,
}
}
pub fn tibialis_anterior() -> Self {
Self::new(600.0, 0.068, 0.51, 0.17)
}
pub fn quadriceps() -> Self {
Self::new(1169.0, 0.084, 0.63, 0.17)
}
pub fn active_force_length(&self, l_normalized: f64) -> f64 {
let omega = 0.45;
let x = (l_normalized - 1.0) / omega;
(-x * x).exp()
}
pub fn passive_force_length(&self, l_normalized: f64) -> f64 {
if l_normalized <= 1.0 {
return 0.0;
}
let k_p = 5.0;
let eps_0 = 0.6;
let numerator = (k_p * (l_normalized - 1.0) / eps_0).exp() - 1.0;
let denominator = k_p.exp() - 1.0;
(numerator / denominator).min(1.0)
}
pub fn force_velocity(&self, v_normalized: f64) -> f64 {
if v_normalized <= 0.0 {
let a_v = 0.25;
let v = v_normalized.max(-1.0 + 1e-9);
((1.0 + v) / (1.0 - v / a_v)).max(0.0)
} else {
let v = v_normalized.min(1.0);
(1.8 - 0.8 * (1.0 + v) / (1.0 - 7.56 * v / 6.0)).max(0.0)
}
}
pub fn muscle_force(&self, l: f64, v: f64, activation: f64) -> f64 {
let l_norm = l / self.l_opt;
let v_norm = v / self.v_max;
let fl = self.active_force_length(l_norm);
let fp = self.passive_force_length(l_norm);
let fv = self.force_velocity(v_norm);
let a = activation.clamp(0.0, 1.0);
let cos_psi = self.pennation_angle.cos();
(a * fl * fv + fp) * self.f_max * cos_psi
}
pub fn max_power(&self) -> f64 {
self.f_max * self.v_max / 4.0
}
}
#[derive(Debug, Clone)]
pub struct BloodVesselModel {
pub r0: f64,
pub h0: f64,
pub e_wall: f64,
pub nu_wall: f64,
}
impl BloodVesselModel {
pub fn new(r0: f64, h0: f64, e_wall: f64, nu_wall: f64) -> Self {
Self {
r0,
h0,
e_wall,
nu_wall,
}
}
pub fn aorta() -> Self {
Self::new(12.5e-3, 2.0e-3, 500e3, 0.45)
}
pub fn femoral_artery() -> Self {
Self::new(3.5e-3, 0.6e-3, 800e3, 0.45)
}
pub fn laplace_wall_stress(&self, p_transmural: f64) -> f64 {
p_transmural * self.r0 / self.h0
}
pub fn compliance(&self, _p: f64) -> f64 {
self.r0 * (1.0 - self.nu_wall * self.nu_wall) / (self.e_wall * self.h0 / self.r0)
}
pub fn pulse_wave_velocity(&self, rho: f64) -> f64 {
(self.e_wall * self.h0 / (2.0 * rho * self.r0)).sqrt()
}
pub fn radius_at_pressure(&self, p: f64) -> f64 {
let c = self.compliance(p);
self.r0 + c * p
}
}
#[derive(Debug, Clone)]
pub struct CorneaModel {
pub e_small: f64,
pub e_large: f64,
pub transition_strain: f64,
pub cct: f64,
pub iop: f64,
}
impl CorneaModel {
pub fn new(e_small: f64, e_large: f64, transition_strain: f64, cct: f64, iop: f64) -> Self {
Self {
e_small,
e_large,
transition_strain,
cct,
iop,
}
}
pub fn healthy_cornea() -> Self {
Self::new(0.01e6, 0.5e6, 0.05, 550e-6, 2000.0)
}
pub fn hoop_stress_iop(&self) -> f64 {
let r = 7.8e-3;
self.iop * r / (2.0 * self.cct)
}
pub fn tangent_modulus(&self, strain: f64) -> f64 {
if strain < self.transition_strain {
self.e_small
} else {
self.e_large
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum FailureMode {
NoFailure,
TensileFailure,
CompressiveFailure,
ShearFailure,
FatigueFailure,
FractureFailure,
}
#[derive(Debug, Clone)]
pub struct HeartValveTissue {
pub e_circ: f64,
pub e_radial: f64,
pub flexural_rigidity: f64,
pub thickness: f64,
}
impl HeartValveTissue {
pub fn new(e_circ: f64, e_radial: f64, flexural_rigidity: f64, thickness: f64) -> Self {
Self {
e_circ,
e_radial,
flexural_rigidity,
thickness,
}
}
pub fn aortic_valve() -> Self {
Self::new(15e6, 2e6, 0.001, 0.5e-3)
}
pub fn bending_stiffness(&self, nu: f64) -> f64 {
self.e_circ * self.thickness.powi(3) / (12.0 * (1.0 - nu * nu))
}
pub fn anisotropy_ratio(&self) -> f64 {
self.e_circ / self.e_radial
}
}
#[derive(Debug, Clone)]
pub struct SoftTissueMaterial {
pub c1: f64,
pub c2: f64,
pub alpha: f64,
pub beta: f64,
}
impl SoftTissueMaterial {
pub fn new(c1: f64, c2: f64, alpha: f64, beta: f64) -> Self {
Self {
c1,
c2,
alpha,
beta,
}
}
pub fn liver() -> Self {
Self::new(350.0, 0.57, 0.1, 1000.0)
}
pub fn brain() -> Self {
Self::new(264.0, 0.4, 0.05, 500.0)
}
pub fn q_exponent(&self, i1: f64, i2: f64, j: f64) -> f64 {
self.c2 * (i1 - 3.0) + self.alpha * (i2 - 3.0) + self.beta * (j - 1.0).powi(2)
}
pub fn strain_energy_density(&self, i1: f64, i2: f64, j: f64) -> f64 {
let q = self.q_exponent(i1, i2, j);
self.c1 * (q.exp() - 1.0)
}
pub fn cauchy_stress_green_lagrange(&self, strain: [[f64; 3]; 3]) -> [[f64; 3]; 3] {
let id = identity3();
let mut f = [[0.0f64; 3]; 3];
for i in 0..3 {
for j in 0..3 {
f[i][j] = id[i][j] + strain[i][j];
}
}
let c = right_cauchy_green(&f);
let i1 = trace3(&c);
let i2 = second_invariant_c(&c);
let j = det3(&f).abs().max(1e-14);
let q = self.q_exponent(i1, i2, j);
let exp_q = q.exp();
let dw_di1 = self.c1 * self.c2 * exp_q;
let dw_di2 = self.c1 * self.alpha * exp_q;
let dw_dj = self.c1 * self.beta * 2.0 * (j - 1.0) * exp_q;
let ft = transpose3(&f);
let b = matmul3(&f, &ft);
let b2 = matmul3(&b, &b);
let i1_b = trace3(&b);
let term1 = scale3(2.0 / j * dw_di1, &b);
let term2_inner = add3(&scale3(i1_b, &b), &scale3(-1.0, &b2));
let term2 = scale3(2.0 / j * dw_di2, &term2_inner);
let term3 = scale3(dw_dj, &id);
let sigma1 = add3(&term1, &term2);
add3(&sigma1, &term3)
}
}
#[derive(Debug, Clone)]
pub struct TendonLigamentModel {
pub toe_region_modulus: f64,
pub linear_modulus: f64,
pub transition_strain: f64,
pub c_relax: f64,
pub tau1: f64,
pub tau2: f64,
}
impl TendonLigamentModel {
pub fn new(
toe_region_modulus: f64,
linear_modulus: f64,
transition_strain: f64,
c_relax: f64,
tau1: f64,
tau2: f64,
) -> Self {
Self {
toe_region_modulus,
linear_modulus,
transition_strain,
c_relax,
tau1,
tau2,
}
}
pub fn patellar_tendon() -> Self {
Self::new(200e6, 1500e6, 0.02, 0.4, 0.01, 10.0)
}
pub fn acl() -> Self {
Self::new(50e6, 300e6, 0.03, 0.35, 0.02, 15.0)
}
pub fn stress_strain_nonlinear(&self, strain: f64) -> f64 {
if strain < 0.0 {
return 0.0;
}
if strain < self.transition_strain {
self.toe_region_modulus * strain * strain / self.transition_strain
} else {
let sigma_transition = self.toe_region_modulus * self.transition_strain;
sigma_transition + self.linear_modulus * (strain - self.transition_strain)
}
}
pub fn relaxation_function(&self, t: f64) -> f64 {
if t <= 0.0 {
return 1.0;
}
let g1 = (-t / self.tau1).exp();
let g2 = (-t / self.tau2).exp();
let denom = 1.0 + self.c_relax * (self.tau2 / self.tau1).ln();
let amplitude = self.c_relax / denom;
let g_inf = 1.0 - amplitude;
g_inf + 0.5 * amplitude * g1 + 0.5 * amplitude * g2
}
pub fn viscoelastic_stress(&self, strain: f64, t: f64) -> f64 {
let sigma_elastic = self.stress_strain_nonlinear(strain);
let g_t = self.relaxation_function(t);
sigma_elastic * g_t
}
pub fn ultimate_stress(&self, failure_strain: f64) -> f64 {
self.stress_strain_nonlinear(failure_strain)
}
}
#[derive(Debug, Clone)]
pub struct TrabecularBone {
pub bv_tv: f64,
pub ash_fraction: f64,
}
impl TrabecularBone {
pub fn new(bv_tv: f64, ash_fraction: f64) -> Self {
Self {
bv_tv,
ash_fraction,
}
}
pub fn vertebral() -> Self {
Self::new(0.15, 0.55)
}
pub fn elastic_modulus_trabecular(&self) -> f64 {
1904.0 * self.bv_tv.powf(1.64)
}
pub fn yield_stress_trabecular(&self) -> f64 {
24.8 * self.bv_tv.powf(1.6)
}
pub fn isotropy_ratio(&self) -> f64 {
(self.bv_tv / 0.35).min(1.0)
}
}
#[derive(Debug, Clone)]
pub struct LigamentWrapModel {
pub rest_length: f64,
pub stiffness: f64,
pub slack_length: f64,
}
impl LigamentWrapModel {
pub fn new(rest_length: f64, stiffness: f64, slack_length: f64) -> Self {
Self {
rest_length,
stiffness,
slack_length,
}
}
pub fn restraint_force(&self, current_length: f64) -> f64 {
if current_length <= self.slack_length {
0.0
} else {
self.stiffness * (current_length - self.slack_length)
}
}
pub fn strain(&self, current_length: f64) -> f64 {
(current_length - self.rest_length) / self.rest_length
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct TissueFailureCriteria {
pub uts: f64,
pub ucs: f64,
pub uss: f64,
pub fatigue_coeff: f64,
pub fatigue_exp: f64,
pub k_ic: f64,
pub g_c: f64,
pub tsai_wu_f12_star: f64,
}
impl TissueFailureCriteria {
pub fn cortical_bone() -> Self {
Self {
uts: 133e6,
ucs: 193e6,
uss: 68e6,
fatigue_coeff: 140e6,
fatigue_exp: -0.08,
k_ic: 2.2e6,
g_c: 600.0,
tsai_wu_f12_star: -0.5,
}
}
pub fn articular_cartilage() -> Self {
Self {
uts: 15e6,
ucs: 5e6,
uss: 4e6,
fatigue_coeff: 10e6,
fatigue_exp: -0.10,
k_ic: 0.2e6,
g_c: 100.0,
tsai_wu_f12_star: -0.5,
}
}
pub fn ligament() -> Self {
Self {
uts: 38e6,
ucs: 1e6,
uss: 10e6,
fatigue_coeff: 30e6,
fatigue_exp: -0.09,
k_ic: 0.5e6,
g_c: 200.0,
tsai_wu_f12_star: -0.5,
}
}
pub fn max_principal_stress(&self, s1: f64, s2: f64, s3: f64) -> FailureMode {
let max_tensile = s1.max(s2).max(s3);
let max_compressive = s1.min(s2).min(s3);
if max_tensile >= self.uts {
FailureMode::TensileFailure
} else if max_compressive <= -self.ucs {
FailureMode::CompressiveFailure
} else {
FailureMode::NoFailure
}
}
pub fn tensile_safety_factor(&self, sigma_max: f64) -> f64 {
if sigma_max <= 0.0 {
f64::INFINITY
} else {
self.uts / sigma_max
}
}
pub fn tsai_wu_index(&self, sigma1: f64, sigma2: f64, tau12: f64) -> f64 {
let f1 = 1.0 / self.uts - 1.0 / self.ucs;
let f2 = f1;
let f11 = 1.0 / (self.uts * self.ucs);
let f22 = f11;
let f66 = 1.0 / (self.uss * self.uss);
let f12 = self.tsai_wu_f12_star * (f11 * f22).sqrt();
f1 * sigma1
+ f2 * sigma2
+ f11 * sigma1 * sigma1
+ f22 * sigma2 * sigma2
+ 2.0 * f12 * sigma1 * sigma2
+ f66 * tau12 * tau12
}
pub fn fatigue_life_cycles(&self, stress_amplitude: f64) -> f64 {
if stress_amplitude <= 0.0 {
return f64::INFINITY;
}
let ratio = stress_amplitude / self.fatigue_coeff;
if ratio <= 0.0 {
return f64::INFINITY;
}
let two_n = ratio.powf(1.0 / self.fatigue_exp);
two_n / 2.0
}
pub fn fracture_criterion(&self, k_i: f64) -> FailureMode {
if k_i >= self.k_ic {
FailureMode::FractureFailure
} else {
FailureMode::NoFailure
}
}
pub fn energy_release_rate(&self, k_i: f64, e_modulus: f64, poisson: f64) -> f64 {
k_i * k_i * (1.0 - poisson * poisson) / e_modulus
}
pub fn toughness_criterion(&self, k_i: f64, e_modulus: f64, poisson: f64) -> FailureMode {
let g = self.energy_release_rate(k_i, e_modulus, poisson);
if g >= self.g_c {
FailureMode::FractureFailure
} else {
FailureMode::NoFailure
}
}
}
#[derive(Debug, Clone)]
pub struct CartilageModel {
pub solid_modulus_h_a: f64,
pub permeability: f64,
pub fluid_fraction: f64,
pub thickness: f64,
}
impl CartilageModel {
pub fn new(
solid_modulus_h_a: f64,
permeability: f64,
fluid_fraction: f64,
thickness: f64,
) -> Self {
Self {
solid_modulus_h_a,
permeability,
fluid_fraction,
thickness,
}
}
pub fn articular_cartilage() -> Self {
Self::new(0.5e6, 1e-15, 0.75, 2e-3)
}
pub fn consolidation_coefficient(&self) -> f64 {
self.solid_modulus_h_a * self.permeability
}
pub fn creep_compliance(&self, t: f64) -> f64 {
let cv = self.consolidation_coefficient();
let h = self.thickness;
let mut sum = 0.0;
for n in 1..=20usize {
let m = (2 * n - 1) as f64;
let coeff = 8.0 / (PI * PI * m * m);
let exponent = -cv * m * m * PI * PI * t / (4.0 * h * h);
sum += coeff * exponent.exp();
}
(1.0 / self.solid_modulus_h_a) * (1.0 - sum)
}
pub fn stress_relaxation(&self, t: f64) -> f64 {
let cv = self.consolidation_coefficient();
let h = self.thickness;
let mut sum = 0.0;
for n in 1..=20usize {
let m = (2 * n - 1) as f64;
let coeff = 8.0 / (PI * PI * m * m);
let exponent = -cv * m * m * PI * PI * t / (4.0 * h * h);
sum += coeff * exponent.exp();
}
sum
}
}
#[derive(Debug, Clone)]
pub struct SkinModel {
pub e_epidermis: f64,
pub e_dermis: f64,
pub nu_epidermis: f64,
pub nu_dermis: f64,
pub thickness_e: f64,
pub thickness_d: f64,
}
impl SkinModel {
pub fn new(
e_epidermis: f64,
e_dermis: f64,
nu_epidermis: f64,
nu_dermis: f64,
thickness_e: f64,
thickness_d: f64,
) -> Self {
Self {
e_epidermis,
e_dermis,
nu_epidermis,
nu_dermis,
thickness_e,
thickness_d,
}
}
pub fn human_forearm() -> Self {
Self::new(1e6, 0.08e6, 0.48, 0.49, 0.1e-3, 1.2e-3)
}
pub fn effective_modulus(&self, indentation: f64) -> f64 {
let h_e = self.thickness_e;
let w = (indentation / h_e).min(1.0);
(1.0 - w) * self.e_epidermis + w * self.e_dermis
}
pub fn effective_poisson(&self, indentation: f64) -> f64 {
let w = (indentation / self.thickness_e).min(1.0);
(1.0 - w) * self.nu_epidermis + w * self.nu_dermis
}
pub fn indentation_force_hertz(&self, r_indenter: f64, e_eff: f64, delta: f64) -> f64 {
let nu = self.effective_poisson(delta);
let e_reduced = e_eff / (1.0 - nu * nu);
(4.0 / 3.0) * e_reduced * r_indenter.sqrt() * delta.abs().powf(1.5)
}
pub fn total_thickness(&self) -> f64 {
self.thickness_e + self.thickness_d
}
}
#[derive(Debug, Clone)]
pub struct IntervertebralDiscModel {
pub nucleus_bulk_modulus: f64,
pub annulus_shear_modulus: f64,
pub height: f64,
pub outer_radius: f64,
pub inner_radius: f64,
}
impl IntervertebralDiscModel {
pub fn new(
nucleus_bulk_modulus: f64,
annulus_shear_modulus: f64,
height: f64,
outer_radius: f64,
inner_radius: f64,
) -> Self {
Self {
nucleus_bulk_modulus,
annulus_shear_modulus,
height,
outer_radius,
inner_radius,
}
}
pub fn lumbar_l4_l5() -> Self {
Self::new(1.72e6, 0.17e6, 11e-3, 23e-3, 12e-3)
}
pub fn compressive_stiffness(&self) -> f64 {
let area_n = PI * self.inner_radius * self.inner_radius;
let k_nucleus = self.nucleus_bulk_modulus * area_n / self.height;
let area_a =
PI * (self.outer_radius * self.outer_radius - self.inner_radius * self.inner_radius);
let k_annulus = self.annulus_shear_modulus * area_a / self.height;
k_nucleus + k_annulus
}
pub fn intradiscal_pressure(&self, axial_force: f64) -> f64 {
let area_n = PI * self.inner_radius * self.inner_radius;
axial_force / area_n
}
}
#[derive(Debug, Clone)]
pub struct FiberReinforcedTissue {
pub c_matrix: f64,
pub a0: [f64; 3],
pub kappa: f64,
pub k1: f64,
pub k2: f64,
}
impl FiberReinforcedTissue {
pub fn new(c_matrix: f64, a0: [f64; 3], kappa: f64, k1: f64, k2: f64) -> Self {
Self {
c_matrix,
a0,
kappa,
k1,
k2,
}
}
pub fn aortic_tissue() -> Self {
let a0 = [0.0, 1.0, 0.0];
Self::new(18.4e3, a0, 0.226, 996.6e3, 524.6)
}
pub fn fiber_strain_energy(&self, i4: f64) -> f64 {
if i4 < 1.0 {
return 0.0;
}
let e = i4 - 1.0;
(self.k1 / (2.0 * self.k2)) * ((self.k2 * e * e).exp() - 1.0)
}
pub fn total_strain_energy(&self, i1: f64, i4: f64) -> f64 {
let w_matrix = self.c_matrix * (i1 - 3.0);
let w_fiber = self.fiber_strain_energy(i4);
w_matrix + w_fiber
}
#[allow(clippy::needless_range_loop)]
pub fn compute_i4(&self, f: &[[f64; 3]; 3]) -> f64 {
let c = right_cauchy_green(f);
let mut ca = [0.0f64; 3];
for i in 0..3 {
for j in 0..3 {
ca[i] += c[i][j] * self.a0[j];
}
}
let mut i4 = 0.0;
for i in 0..3 {
i4 += self.a0[i] * ca[i];
}
i4
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CardiacMuscleModel {
pub c_passive: f64,
pub bf: f64,
pub bt: f64,
pub bfs: f64,
pub t_max: f64,
pub ca50: f64,
pub n_hill: f64,
pub l0: f64,
}
impl CardiacMuscleModel {
pub fn left_ventricle() -> Self {
Self {
c_passive: 880.0,
bf: 18.48,
bt: 3.58,
bfs: 1.627,
t_max: 135_480.0,
ca50: 0.805,
n_hill: 2.0,
l0: 1.58,
}
}
pub fn passive_strain_energy(&self, e_ff: f64, e_ss: f64, e_nn: f64, e_fs: f64) -> f64 {
let q = self.bf * e_ff * e_ff
+ self.bt * (e_ss * e_ss + e_nn * e_nn)
+ self.bfs * (2.0 * e_fs * e_fs);
self.c_passive / 2.0 * (q.exp() - 1.0)
}
pub fn active_fiber_stress(&self, l: f64, ca: f64) -> f64 {
if l <= self.l0 {
return 0.0;
}
let ca50_n = self.ca50.powf(self.n_hill);
let ca_n = ca.powf(self.n_hill);
let activation = ca_n / (ca_n + ca50_n);
let l_factor = ((l - self.l0) / (2.3 - self.l0)).clamp(0.0, 1.0);
self.t_max * activation * l_factor
}
pub fn total_fiber_stress(
&self,
e_ff: f64,
e_ss: f64,
e_nn: f64,
e_fs: f64,
l: f64,
ca: f64,
) -> f64 {
let w = self.passive_strain_energy(e_ff, e_ss, e_nn, e_fs);
let dw = {
let delta = 1e-6;
let w2 = self.passive_strain_energy(e_ff + delta, e_ss, e_nn, e_fs);
(w2 - w) / delta
};
dw + self.active_fiber_stress(l, ca)
}
pub fn elastance_at_peak(&self, volume: f64, v0: f64) -> f64 {
self.t_max / (volume - v0).abs().max(1e-6)
}
pub fn stroke_work(&self, edv: f64, esv: f64, _map: f64) -> f64 {
let sv = edv - esv;
let ees = self.elastance_at_peak(edv, esv);
ees * sv * sv / 2.0
}
}
#[derive(Debug, Clone)]
pub struct BoneModel {
pub cortical: CorticalBone,
pub trabecular: TrabecularBone,
pub cortical_thickness: f64,
pub total_width: f64,
}
impl BoneModel {
pub fn new(
cortical: CorticalBone,
trabecular: TrabecularBone,
cortical_thickness: f64,
total_width: f64,
) -> Self {
Self {
cortical,
trabecular,
cortical_thickness,
total_width,
}
}
pub fn femoral_neck() -> Self {
Self::new(
CorticalBone::femur_cortical(),
TrabecularBone::vertebral(),
2e-3,
30e-3,
)
}
pub fn effective_axial_modulus(&self) -> f64 {
let r_total = self.total_width / 2.0;
let r_inner = r_total - self.cortical_thickness;
let a_cortical = PI * (r_total * r_total - r_inner * r_inner);
let a_trabecular = PI * r_inner * r_inner;
let a_total = PI * r_total * r_total;
(self.cortical.elastic_modulus() * a_cortical
+ self.trabecular.elastic_modulus_trabecular() * a_trabecular)
/ a_total
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HuxleyModel {
pub crossbridge_stiffness: f64,
pub h: f64,
pub f1: f64,
pub g1: f64,
pub g2: f64,
pub crossbridge_density: f64,
}
impl HuxleyModel {
pub fn new(
crossbridge_stiffness: f64,
h: f64,
f1: f64,
g1: f64,
g2: f64,
crossbridge_density: f64,
) -> Self {
Self {
crossbridge_stiffness,
h,
f1,
g1,
g2,
crossbridge_density,
}
}
pub fn fast_twitch() -> Self {
Self::new(2.0e-3, 11e-9, 3.68e9, 1.0e9, 3.68e9, 5.0e14)
}
pub fn steady_state_fraction(&self, x: f64) -> f64 {
if x > 0.0 && x < self.h {
self.f1 / (self.f1 + self.g1)
} else {
0.0
}
}
pub fn isometric_force(&self) -> f64 {
let n_ss = self.f1 / (self.f1 + self.g1);
self.crossbridge_density * n_ss * self.crossbridge_stiffness * self.h * self.h / 2.0
}
pub fn force_velocity_ratio(&self, v: f64) -> f64 {
if v.abs() < 1e-30 {
return 1.0;
}
let f1 = self.f1;
let g1 = self.g1;
let g2 = self.g2;
let h = self.h;
if v > 0.0 {
let phi = v / (h * (f1 + g1));
let numerator = 1.0 - phi * (g2 / (f1 + g2));
numerator.max(0.0)
} else {
let phi = (-v) / (h * (f1 + g1));
(1.0 + phi * g2 / (f1 + g2)).min(1.8)
}
}
pub fn power_output(&self, v: f64) -> f64 {
let f0 = self.isometric_force();
f0 * self.force_velocity_ratio(v) * v
}
pub fn optimal_velocity(&self, v_max: f64) -> f64 {
let mut a = 0.0f64;
let mut b = v_max;
let phi = (5.0_f64.sqrt() - 1.0) / 2.0;
let tol = 1e-9;
let mut c = b - phi * (b - a);
let mut d = a + phi * (b - a);
while (b - a).abs() > tol {
if self.power_output(c) < self.power_output(d) {
a = c;
} else {
b = d;
}
c = b - phi * (b - a);
d = a + phi * (b - a);
}
(a + b) / 2.0
}
}