#[allow(unused_imports)]
use super::functions::*;
use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub struct MagnetorheologicalFluid {
pub base_viscosity: f64,
pub tau0_base: f64,
pub tau_h_coeff: f64,
pub field_exponent: f64,
pub volume_fraction: f64,
}
impl MagnetorheologicalFluid {
pub fn new(
base_viscosity: f64,
tau0_base: f64,
tau_h_coeff: f64,
field_exponent: f64,
volume_fraction: f64,
) -> Self {
Self {
base_viscosity,
tau0_base,
tau_h_coeff,
field_exponent,
volume_fraction,
}
}
pub fn yield_stress(&self, h_field: f64) -> f64 {
self.tau0_base + self.tau_h_coeff * h_field.powf(self.field_exponent)
}
pub fn shear_stress(&self, shear_rate: f64, h_field: f64) -> f64 {
let tau_y = self.yield_stress(h_field);
if shear_rate.abs() < 1e-12 {
return tau_y;
}
tau_y + self.base_viscosity * shear_rate
}
pub fn mason_number(&self, shear_rate: f64, h_field: f64) -> f64 {
const MU0: f64 = 4.0 * PI * 1e-7;
if h_field < 1e-10 {
return f64::INFINITY;
}
self.base_viscosity * shear_rate / (MU0 * h_field * h_field)
}
pub fn apparent_viscosity(&self, shear_rate: f64, h_field: f64) -> f64 {
if shear_rate.abs() < 1e-12 {
return f64::INFINITY;
}
self.shear_stress(shear_rate, h_field) / shear_rate
}
}
#[derive(Debug, Clone)]
pub struct ShapeMemoryAlloy {
pub ms: f64,
pub mf: f64,
pub a_s: f64,
pub af: f64,
pub xi: f64,
pub e_a: f64,
pub e_m: f64,
pub max_strain: f64,
pub cm: f64,
pub ca: f64,
}
impl ShapeMemoryAlloy {
#[allow(clippy::too_many_arguments)]
pub fn new(
ms: f64,
mf: f64,
a_s: f64,
af: f64,
e_a: f64,
e_m: f64,
max_strain: f64,
cm: f64,
ca: f64,
) -> Self {
Self {
ms,
mf,
a_s,
af,
xi: 1.0,
e_a,
e_m,
max_strain,
cm,
ca,
}
}
pub fn nitinol() -> Self {
Self::new(291.0, 273.0, 307.0, 325.0, 75e9, 28e9, 0.08, 8e6, 13e6)
}
pub fn elastic_modulus(&self) -> f64 {
self.e_a + self.xi * (self.e_m - self.e_a)
}
pub fn update_phase(&mut self, temperature: f64, stress: f64) -> f64 {
let ms_stress = self.ms + stress / self.cm;
let mf_stress = self.mf + stress / self.cm;
if temperature <= ms_stress && temperature >= mf_stress {
let xi_m =
(1.0 + (PI * (temperature - ms_stress) / (mf_stress - ms_stress)).cos()) / 2.0;
self.xi = xi_m.clamp(self.xi, 1.0);
}
let as_stress = self.a_s + stress / self.ca;
let af_stress = self.af + stress / self.ca;
if temperature >= as_stress && temperature <= af_stress {
let xi_a = self.xi
* (1.0 + (PI * (temperature - as_stress) / (af_stress - as_stress)).cos())
/ 2.0;
self.xi = xi_a.clamp(0.0, self.xi);
}
if temperature < mf_stress {
self.xi = 1.0;
}
if temperature > af_stress {
self.xi = 0.0;
}
self.xi
}
pub fn phase(&self) -> SmaPhase {
if self.xi > 0.99 {
SmaPhase::Martensite
} else if self.xi < 0.01 {
SmaPhase::Austenite
} else {
SmaPhase::Mixed
}
}
pub fn recovery_stress(&self, strain: f64) -> f64 {
self.elastic_modulus() * (strain - self.xi * self.max_strain)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ElectrorheologicalFluid {
pub base_viscosity: f64,
pub field_coeff: f64,
pub saturation_field: f64,
pub epsilon_p: f64,
pub epsilon_f: f64,
}
impl ElectrorheologicalFluid {
pub fn new(
base_viscosity: f64,
field_coeff: f64,
saturation_field: f64,
epsilon_p: f64,
epsilon_f: f64,
) -> Self {
Self {
base_viscosity,
field_coeff,
saturation_field,
epsilon_p,
epsilon_f,
}
}
pub fn yield_stress(&self, e_field: f64) -> f64 {
let e_eff = e_field.min(self.saturation_field);
self.field_coeff * e_eff * e_eff
}
pub fn shear_stress(&self, shear_rate: f64, e_field: f64) -> f64 {
let tau_y = self.yield_stress(e_field);
if shear_rate.abs() < 1e-12 {
return tau_y;
}
tau_y + self.base_viscosity * shear_rate
}
pub fn beta_parameter(&self) -> f64 {
(self.epsilon_p - self.epsilon_f) / (self.epsilon_p + 2.0 * self.epsilon_f)
}
}
#[derive(Debug, Clone, Copy)]
pub struct PolymerActuator {
pub curvature_gain: f64,
pub time_constant: f64,
pub length: f64,
pub thickness: f64,
pub blocking_force_gain: f64,
pub bending_state: f64,
}
impl PolymerActuator {
pub fn new(
curvature_gain: f64,
time_constant: f64,
length: f64,
thickness: f64,
blocking_force_gain: f64,
) -> Self {
Self {
curvature_gain,
time_constant,
length,
thickness,
blocking_force_gain,
bending_state: 0.0,
}
}
pub fn steady_state_deflection(&self, voltage: f64) -> f64 {
let kappa = self.curvature_gain * voltage;
kappa * self.length * self.length / 2.0
}
pub fn transient_deflection(&self, voltage: f64, t: f64) -> f64 {
let delta_ss = self.steady_state_deflection(voltage);
delta_ss * (1.0 - (-t / self.time_constant).exp())
}
pub fn blocking_force(&self, voltage: f64) -> f64 {
self.blocking_force_gain * voltage
}
pub fn curvature(&self, voltage: f64) -> f64 {
self.curvature_gain * voltage
}
}
#[derive(Debug, Clone, Copy)]
pub struct DielectricElastomer {
pub epsilon_r: f64,
pub thickness: f64,
pub initial_area: f64,
pub elastic_modulus: f64,
pub breakdown_field: f64,
}
impl DielectricElastomer {
pub fn new(
epsilon_r: f64,
thickness: f64,
initial_area: f64,
elastic_modulus: f64,
breakdown_field: f64,
) -> Self {
Self {
epsilon_r,
thickness,
initial_area,
elastic_modulus,
breakdown_field,
}
}
pub fn maxwell_stress(&self, e_field: f64) -> f64 {
const EPS0: f64 = 8.854e-12;
self.epsilon_r * EPS0 * e_field * e_field
}
pub fn actuation_strain(&self, voltage: f64) -> f64 {
let e_field = voltage / self.thickness;
let e_eff = e_field.min(self.breakdown_field);
let p = self.maxwell_stress(e_eff);
(p / self.elastic_modulus).min(0.9)
}
pub fn area_strain(&self, voltage: f64) -> f64 {
2.0 * self.actuation_strain(voltage)
}
pub fn actuation_pressure(&self, voltage: f64) -> f64 {
let e_field = (voltage / self.thickness).min(self.breakdown_field);
self.maxwell_stress(e_field)
}
pub fn electric_field(&self, voltage: f64) -> f64 {
voltage / self.thickness
}
}
#[derive(Debug, Clone)]
pub struct SmaController {
pub sma: ShapeMemoryAlloy,
pub target_xi: f64,
pub t_hot: f64,
pub t_cold: f64,
pub kp: f64,
pub temperature: f64,
pub max_power: f64,
}
impl SmaController {
pub fn new(sma: ShapeMemoryAlloy, t_hot: f64, t_cold: f64, kp: f64, max_power: f64) -> Self {
let temperature = t_cold;
Self {
sma,
target_xi: 0.0,
t_hot,
t_cold,
kp,
temperature,
max_power,
}
}
pub fn set_target(&mut self, xi_target: f64) {
self.target_xi = xi_target.clamp(0.0, 1.0);
}
pub fn compute_power(&self) -> f64 {
let xi_err = self.target_xi - self.sma.xi;
if xi_err > 0.1 {
0.0
} else if xi_err < -0.1 {
self.max_power
} else {
(-self.kp * xi_err * self.max_power).clamp(0.0, self.max_power)
}
}
pub fn step(&mut self, dt: f64, r_thermal: f64, t_ambient: f64) {
let power = self.compute_power();
let thermal_mass = 1e-4;
let q_loss = (self.temperature - t_ambient) / r_thermal;
let d_temp = (power - q_loss) / thermal_mass * dt;
self.temperature = (self.temperature + d_temp).clamp(self.t_cold, self.t_hot);
self.sma.update_phase(self.temperature, 0.0);
}
}
#[derive(Debug, Clone)]
pub struct SmartStructure {
pub n_modes: usize,
pub modal_frequencies: Vec<f64>,
pub modal_damping: Vec<f64>,
pub actuator_positions: Vec<f64>,
pub sensor_positions: Vec<f64>,
pub modal_state: Vec<[f64; 2]>,
pub control_gains: Vec<f64>,
}
impl SmartStructure {
pub fn new(
n_modes: usize,
modal_frequencies: Vec<f64>,
modal_damping: Vec<f64>,
actuator_positions: Vec<f64>,
sensor_positions: Vec<f64>,
) -> Self {
let control_gains = vec![1.0; n_modes];
let modal_state = vec![[0.0; 2]; n_modes];
Self {
n_modes,
modal_frequencies,
modal_damping,
actuator_positions,
sensor_positions,
modal_state,
control_gains,
}
}
fn mode_shape(&self, mode: usize, position: f64) -> f64 {
((mode + 1) as f64 * PI * position).sin()
}
pub fn modal_control(&self, sensor_readings: &[f64]) -> Vec<f64> {
let mut u = vec![0.0; self.actuator_positions.len()];
for (k, &x_act) in self.actuator_positions.iter().enumerate() {
for m in 0..self.n_modes {
let obs: f64 = sensor_readings
.iter()
.zip(self.sensor_positions.iter())
.map(|(&y, &x_s)| y * self.mode_shape(m, x_s))
.sum();
let vel = self.modal_state[m][1];
let phi_act = self.mode_shape(m, x_act);
u[k] -= self.control_gains[m] * (obs + vel) * phi_act;
}
}
u
}
pub fn step(&mut self, forcing: &[f64], dt: f64) {
for m in 0..self.n_modes {
let omega = 2.0 * PI * self.modal_frequencies[m];
let zeta = self.modal_damping[m];
let q = self.modal_state[m][0];
let qdot = self.modal_state[m][1];
let f_gen: f64 = forcing
.iter()
.zip(self.actuator_positions.iter())
.map(|(&f, &x)| f * self.mode_shape(m, x))
.sum();
let qddot = f_gen - 2.0 * zeta * omega * qdot - omega * omega * q;
self.modal_state[m][0] = q + qdot * dt;
self.modal_state[m][1] = qdot + qddot * dt;
}
}
pub fn displacement_at(&self, x: f64) -> f64 {
(0..self.n_modes)
.map(|m| self.modal_state[m][0] * self.mode_shape(m, x))
.sum()
}
pub fn set_control_gains(&mut self, gains: Vec<f64>) {
let n = gains.len().min(self.n_modes);
self.control_gains[..n].copy_from_slice(&gains[..n]);
}
}
#[derive(Debug, Clone, Copy)]
pub struct HydrogelSwelling {
pub chi: f64,
pub phi0: f64,
pub n_c: f64,
pub v_s: f64,
pub temperature: f64,
}
impl HydrogelSwelling {
pub fn new(chi: f64, phi0: f64, n_c: f64, v_s: f64, temperature: f64) -> Self {
Self {
chi,
phi0,
n_c,
v_s,
temperature,
}
}
pub fn pnipam() -> Self {
Self::new(0.45, 0.05, 100.0, 18e-6, 298.0)
}
pub fn mixing_free_energy(&self, phi: f64) -> f64 {
const R: f64 = 8.314;
let phi = phi.clamp(1e-6, 1.0 - 1e-6);
let psi = 1.0 - phi;
(R * self.temperature / self.v_s) * (phi * phi.ln() + psi * psi.ln() + self.chi * phi * psi)
}
pub fn equilibrium_phi(&self) -> f64 {
let phi_guess = self.phi0;
let mut phi = phi_guess.clamp(0.01, 0.99);
for _ in 0..50 {
let psi = 1.0 - phi;
let mu_mix = phi.ln() + psi + self.chi * psi * psi;
let mu_el = (phi / (2.0 * self.n_c)) * (phi / self.phi0).powf(1.0 / 3.0);
let mu = mu_mix + mu_el;
let d_mu_mix = 1.0 / phi - 1.0 - 2.0 * self.chi * psi;
let d_mu_el = (1.0 / (2.0 * self.n_c))
* (phi / self.phi0).powf(1.0 / 3.0)
* (1.0 + phi / (3.0 * self.phi0));
let d_mu = d_mu_mix + d_mu_el;
if d_mu.abs() < 1e-15 {
break;
}
phi -= mu / d_mu;
phi = phi.clamp(0.001, 0.999);
}
phi
}
pub fn swelling_ratio(&self) -> f64 {
1.0 / self.equilibrium_phi().max(1e-6)
}
pub fn linear_strain(&self) -> f64 {
self.swelling_ratio().powf(1.0 / 3.0) - 1.0
}
pub fn osmotic_pressure(&self, phi: f64) -> f64 {
const R: f64 = 8.314;
let phi = phi.clamp(1e-6, 1.0 - 1e-6);
let psi = 1.0 - phi;
-(R * self.temperature / self.v_s) * (psi + phi.ln() + self.chi * phi * phi)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EapType {
Ionic,
Electronic,
}
#[derive(Debug, Clone)]
pub struct FerroelectricHysteresis {
pub p_sat: f64,
pub e_coercive: f64,
pub p_remanent: f64,
pub n_bins: usize,
pub(super) hysteron_states: Vec<i8>,
pub(super) hysteron_fields: Vec<[f64; 2]>,
pub polarization: f64,
}
impl FerroelectricHysteresis {
pub fn new(p_sat: f64, e_coercive: f64, p_remanent: f64, n_bins: usize) -> Self {
let mut hysteron_fields = Vec::with_capacity(n_bins);
let mut hysteron_states = Vec::with_capacity(n_bins);
for i in 0..n_bins {
let frac = (i as f64 + 0.5) / n_bins as f64;
let e_up = e_coercive * (0.5 + 1.5 * frac);
let e_down = -e_up * (p_remanent / p_sat).max(0.1);
hysteron_fields.push([e_down, e_up]);
hysteron_states.push(-1i8);
}
Self {
p_sat,
e_coercive,
p_remanent,
n_bins,
hysteron_states,
hysteron_fields,
polarization: -p_remanent,
}
}
pub fn barium_titanate() -> Self {
Self::new(0.26, 0.4e6, 0.18, 32)
}
pub fn update(&mut self, e_field: f64) {
for (i, &[e_down, e_up]) in self.hysteron_fields.iter().enumerate() {
if e_field > e_up {
self.hysteron_states[i] = 1;
} else if e_field < e_down {
self.hysteron_states[i] = -1;
}
}
let sum: i32 = self.hysteron_states.iter().map(|&s| s as i32).sum();
self.polarization = self.p_sat * sum as f64 / self.n_bins as f64;
}
pub fn susceptibility(&self, e_field: f64, delta_e: f64) -> f64 {
let mut twin = self.clone();
twin.update(e_field + delta_e);
let p1 = twin.polarization;
let mut twin2 = self.clone();
twin2.update(e_field - delta_e);
let p2 = twin2.polarization;
(p1 - p2) / (2.0 * delta_e)
}
pub fn stored_energy(&self, e_field: f64) -> f64 {
const EPS0: f64 = 8.854e-12;
EPS0 * e_field * e_field / 2.0 + self.polarization * e_field
}
}
#[derive(Debug, Clone, Copy)]
pub struct BimorphActuator {
pub length: f64,
pub t1: f64,
pub t2: f64,
pub e1: f64,
pub e2: f64,
pub alpha1: f64,
pub alpha2: f64,
}
impl BimorphActuator {
pub fn new(length: f64, t1: f64, t2: f64, e1: f64, e2: f64, alpha1: f64, alpha2: f64) -> Self {
Self {
length,
t1,
t2,
e1,
e2,
alpha1,
alpha2,
}
}
pub fn tip_deflection(&self, delta_t: f64) -> f64 {
let m = self.e1 * self.t1 / (self.e2 * self.t2);
let n = self.t1 / self.t2;
let t_total = self.t1 + self.t2;
let denom = 3.0 * (1.0 + m) * (1.0 + m) + (1.0 + m * n) * (m * m + 1.0 / (m * n));
if denom.abs() < 1e-20 {
return 0.0;
}
let curvature = 6.0 * (self.alpha1 - self.alpha2) * delta_t * (1.0 + m) / (t_total * denom);
curvature * self.length * self.length / 2.0
}
pub fn neutral_axis(&self) -> f64 {
let n1 = self.e1 * self.t1;
let n2 = self.e2 * self.t2;
(n1 * self.t1 / 2.0 + n2 * (self.t1 + self.t2 / 2.0)) / (n1 + n2)
}
pub fn curvature_radius(&self, delta_t: f64) -> f64 {
let deflection = self.tip_deflection(delta_t);
if deflection.abs() < 1e-20 {
return f64::INFINITY;
}
self.length * self.length / (2.0 * deflection)
}
}
#[derive(Debug, Clone)]
pub struct SmartComposite {
pub fiber_volume_fraction: f64,
pub sma: ShapeMemoryAlloy,
pub matrix_modulus: f64,
pub matrix_density: f64,
pub sma_density: f64,
pub thickness: f64,
}
impl SmartComposite {
pub fn new(
fiber_volume_fraction: f64,
sma: ShapeMemoryAlloy,
matrix_modulus: f64,
matrix_density: f64,
sma_density: f64,
thickness: f64,
) -> Self {
Self {
fiber_volume_fraction,
sma,
matrix_modulus,
matrix_density,
sma_density,
thickness,
}
}
pub fn longitudinal_modulus(&self) -> f64 {
let v_f = self.fiber_volume_fraction;
let v_m = 1.0 - v_f;
v_f * self.sma.elastic_modulus() + v_m * self.matrix_modulus
}
pub fn transverse_modulus(&self) -> f64 {
let v_f = self.fiber_volume_fraction;
let v_m = 1.0 - v_f;
1.0 / (v_f / self.sma.elastic_modulus() + v_m / self.matrix_modulus)
}
pub fn density(&self) -> f64 {
let v_f = self.fiber_volume_fraction;
let v_m = 1.0 - v_f;
v_f * self.sma_density + v_m * self.matrix_density
}
pub fn composite_recovery_stress(&self, strain: f64) -> f64 {
self.fiber_volume_fraction * self.sma.recovery_stress(strain)
}
pub fn actuation_curvature(&self, temperature: f64) -> f64 {
let e_sma = self.sma.elastic_modulus();
let e_mat = self.matrix_modulus;
let t_sma = self.thickness * self.fiber_volume_fraction;
let t_mat = self.thickness * (1.0 - self.fiber_volume_fraction);
let eps_sma = self.sma.max_strain * (1.0 - self.sma.xi);
let _ = temperature;
6.0 * e_sma * e_mat * t_sma * t_mat * (t_sma + t_mat) * eps_sma
/ (e_sma * t_sma.powi(2) * (4.0 * t_sma + 3.0 * t_mat)
+ e_mat * t_mat.powi(2) * (4.0 * t_mat + 3.0 * t_sma)
+ 6.0 * e_sma * e_mat * t_sma * t_mat * (t_sma + t_mat))
.max(1e-30)
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct BrinsonModel {
pub ms: f64,
pub mf: f64,
pub a_s: f64,
pub af: f64,
pub xi_s: f64,
pub xi_t: f64,
pub e_a: f64,
pub e_m: f64,
pub eps_l: f64,
pub cm: f64,
pub ca: f64,
pub theta: f64,
pub stress: f64,
pub strain: f64,
pub temperature: f64,
}
impl BrinsonModel {
#[allow(clippy::too_many_arguments)]
pub fn new(
ms: f64,
mf: f64,
a_s: f64,
af: f64,
e_a: f64,
e_m: f64,
eps_l: f64,
cm: f64,
ca: f64,
theta: f64,
) -> Self {
Self {
ms,
mf,
a_s,
af,
xi_s: 0.0,
xi_t: 1.0,
e_a,
e_m,
eps_l,
cm,
ca,
theta,
stress: 0.0,
strain: 0.0,
temperature: mf,
}
}
pub fn nitinol() -> Self {
Self::new(
291.0, 273.0, 307.0, 325.0, 75e9, 28e9, 0.08, 8e6, 13e6, 0.55e6,
)
}
pub fn total_xi(&self) -> f64 {
(self.xi_s + self.xi_t).clamp(0.0, 1.0)
}
pub fn elastic_modulus(&self) -> f64 {
self.e_a + self.total_xi() * (self.e_m - self.e_a)
}
pub fn update(&mut self, temperature: f64, stress: f64) -> (f64, f64, f64) {
self.temperature = temperature;
self.stress = stress;
let ms_s = self.ms + stress / self.cm;
let mf_s = self.mf + stress / self.cm;
if temperature <= ms_s && temperature >= mf_s {
let cos_arg = PI * (temperature - ms_s) / (mf_s - ms_s);
let xi_new = (1.0 + cos_arg.cos()) / 2.0;
if xi_new > self.xi_t {
self.xi_t = xi_new.min(1.0 - self.xi_s);
}
} else if temperature < mf_s {
self.xi_t = 1.0 - self.xi_s;
}
let sigma_ms = self.cm * (temperature - self.ms).max(0.0);
let sigma_mf = self.cm * (temperature - self.mf).max(0.0);
if stress >= sigma_ms && stress <= sigma_mf + 1e-3 && temperature > self.mf {
let cos_arg = PI * (stress - sigma_ms) / (sigma_mf - sigma_ms + 1e-6);
let xi_s_new = (1.0 - self.xi_s) / 2.0 * (1.0 - cos_arg.cos());
self.xi_s = (self.xi_s + xi_s_new).clamp(0.0, 1.0 - self.xi_t);
}
let as_s = self.a_s + stress / self.ca;
let af_s = self.af + stress / self.ca;
if temperature >= as_s && temperature <= af_s {
let cos_arg = PI * (temperature - as_s) / (af_s - as_s);
let xi_new = self.total_xi() / 2.0 * (1.0 + cos_arg.cos());
let xi_total = xi_new.clamp(0.0, self.total_xi());
let frac = if self.total_xi() > 1e-10 {
xi_total / self.total_xi()
} else {
0.0
};
self.xi_s = (self.xi_s * frac).clamp(0.0, 1.0);
self.xi_t = (self.xi_t * frac).clamp(0.0, 1.0);
} else if temperature > af_s {
self.xi_s = 0.0;
self.xi_t = 0.0;
}
(self.xi_s, self.xi_t, self.total_xi())
}
pub fn stress_from_strain(&self, strain: f64, t_ref: f64) -> f64 {
let e = self.elastic_modulus();
e * (strain - self.eps_l * self.xi_s) + self.theta * (self.temperature - t_ref)
}
pub fn recovery_stress(&self, strain: f64) -> f64 {
self.elastic_modulus() * (strain - self.eps_l * self.xi_s)
}
pub fn sme_stroke(&self, xi_initial: f64, xi_final: f64, wire_length: f64) -> f64 {
let delta_xi_s = xi_initial - xi_final;
delta_xi_s * self.eps_l * wire_length
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct Magnetorheological {
pub eta0: f64,
pub mu_c: f64,
pub m_sat: f64,
pub phi: f64,
pub n_exp: f64,
pub c_yield: f64,
pub phi_max: f64,
pub intrinsic_viscosity: f64,
}
impl Magnetorheological {
#[allow(clippy::too_many_arguments)]
pub fn new(
eta0: f64,
mu_c: f64,
m_sat: f64,
phi: f64,
n_exp: f64,
c_yield: f64,
phi_max: f64,
intrinsic_viscosity: f64,
) -> Self {
Self {
eta0,
mu_c,
m_sat,
phi,
n_exp,
c_yield,
phi_max,
intrinsic_viscosity,
}
}
pub fn carbonyl_iron() -> Self {
Self::new(0.1, 4.0 * PI * 1e-7, 1.36e6, 0.30, 1.5, 0.3e3, 0.74, 2.5)
}
pub fn effective_viscosity_off(&self) -> f64 {
let ratio = self.phi / self.phi_max;
let kd = (1.0 - ratio).powf(-self.intrinsic_viscosity * self.phi_max);
self.eta0 * kd.max(1.0)
}
pub fn yield_stress(&self, h_field: f64) -> f64 {
self.c_yield * h_field.powf(self.n_exp)
}
pub fn mason_number(&self, shear_rate: f64, h_field: f64) -> f64 {
const MU0: f64 = 1.2566370614e-6;
if h_field < 1e-10 {
return f64::INFINITY;
}
self.eta0 * shear_rate / (MU0 * h_field * h_field)
}
pub fn shear_stress(&self, shear_rate: f64, h_field: f64) -> f64 {
let tau_y = self.yield_stress(h_field);
let eta_eff = self.effective_viscosity_off();
if shear_rate.abs() < 1e-12 {
return tau_y;
}
tau_y + eta_eff * shear_rate
}
pub fn apparent_viscosity(&self, shear_rate: f64, h_field: f64) -> f64 {
if shear_rate.abs() < 1e-12 {
return f64::INFINITY;
}
self.shear_stress(shear_rate, h_field) / shear_rate
}
pub fn chain_fraction(&self, h_field: f64) -> f64 {
const MU0: f64 = 1.2566370614e-6;
let m = MU0 * self.m_sat * h_field;
let lambda = m / (self.eta0.max(1e-10) + 1.0);
(lambda * self.phi).tanh().clamp(0.0, 1.0)
}
pub fn viscosity_ratio(&self, h_field: f64) -> f64 {
let cf = self.chain_fraction(h_field);
1.0 + 100.0 * cf * self.phi
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SmaPhase {
Martensite,
Austenite,
Mixed,
}
#[derive(Debug, Clone)]
pub struct ElectroactivePoly {
pub eap_type: EapType,
pub max_strain: f64,
pub v_half: f64,
pub elastic_modulus: f64,
pub time_constant: f64,
pub state: f64,
}
impl ElectroactivePoly {
pub fn new(
eap_type: EapType,
max_strain: f64,
v_half: f64,
elastic_modulus: f64,
time_constant: f64,
) -> Self {
Self {
eap_type,
max_strain,
v_half,
elastic_modulus,
time_constant,
state: 0.0,
}
}
pub fn ipmc() -> Self {
Self::new(EapType::Ionic, 0.03, 1.5, 1e6, 0.1)
}
pub fn dielectric_elastomer() -> Self {
Self::new(EapType::Electronic, 0.45, 1000.0, 1e5, 0.001)
}
pub fn steady_state_strain(&self, voltage: f64) -> f64 {
let k = 4.0 / self.v_half;
let s = 1.0 / (1.0 + (-k * (voltage - self.v_half)).exp());
self.max_strain * s
}
pub fn transient_strain(&self, voltage: f64, t: f64) -> f64 {
let ss = self.steady_state_strain(voltage);
ss * (1.0 - (-t / self.time_constant).exp())
}
pub fn blocking_stress(&self, voltage: f64) -> f64 {
self.elastic_modulus * self.steady_state_strain(voltage)
}
pub fn update(&mut self, voltage: f64, dt: f64) {
let target = self.steady_state_strain(voltage) / self.max_strain.max(1e-12);
let tau = self.time_constant;
self.state += (target - self.state) * (1.0 - (-dt / tau).exp());
self.state = self.state.clamp(0.0, 1.0);
}
pub fn current_strain(&self) -> f64 {
self.state * self.max_strain
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct HydrogelFloryRehner {
pub chi: f64,
pub crosslink_density: f64,
pub phi0: f64,
pub v_s: f64,
pub temperature: f64,
pub dry_modulus: f64,
}
impl HydrogelFloryRehner {
pub fn new(
chi: f64,
crosslink_density: f64,
phi0: f64,
v_s: f64,
temperature: f64,
dry_modulus: f64,
) -> Self {
Self {
chi,
crosslink_density,
phi0,
v_s,
temperature,
dry_modulus,
}
}
pub fn polyacrylamide() -> Self {
Self::new(0.45, 10.0, 0.05, 18e-6, 298.0, 1e4)
}
pub fn mixing_free_energy(&self, phi: f64) -> f64 {
const R: f64 = 8.314;
let phi = phi.clamp(1e-6, 1.0 - 1e-6);
let psi = 1.0 - phi;
R * self.temperature / self.v_s * (phi * phi.ln() + psi * psi.ln() + self.chi * phi * psi)
}
pub fn elastic_free_energy(&self, phi: f64) -> f64 {
const R: f64 = 8.314;
let phi = phi.clamp(1e-6, 1.0);
let lambda = (self.phi0 / phi).powf(1.0 / 3.0);
let lambda2 = lambda * lambda;
let ln_lambda3 = 3.0 * lambda.ln();
self.crosslink_density * R * self.temperature / 2.0
* (3.0 * lambda2 - 3.0 - 2.0 * ln_lambda3)
}
pub fn total_free_energy(&self, phi: f64) -> f64 {
self.mixing_free_energy(phi) + self.elastic_free_energy(phi)
}
pub fn chemical_potential(&self, phi: f64) -> f64 {
let phi = phi.clamp(1e-6, 1.0 - 1e-6);
let psi = 1.0 - phi;
psi.ln() + phi + self.chi * phi * phi
}
pub fn elastic_chemical_potential(&self, phi: f64) -> f64 {
let phi = phi.clamp(1e-6, 1.0);
let n = (self.crosslink_density * self.v_s).max(1e-10);
(phi / (2.0 * n)) * (phi / self.phi0).powf(1.0 / 3.0)
}
pub fn equilibrium_swelling(&self) -> f64 {
let mut phi = self.phi0.clamp(0.01, 0.99);
for _ in 0..100 {
let mu_mix = self.chemical_potential(phi);
let mu_el = self.elastic_chemical_potential(phi);
let mu = mu_mix + mu_el;
let dphi = 1e-6;
let d_mu = (self.chemical_potential(phi + dphi)
+ self.elastic_chemical_potential(phi + dphi)
- self.chemical_potential(phi - dphi)
- self.elastic_chemical_potential(phi - dphi))
/ (2.0 * dphi);
if d_mu.abs() < 1e-15 {
break;
}
phi -= mu / d_mu;
phi = phi.clamp(0.001, 0.999);
}
1.0 / phi.max(1e-6)
}
pub fn linear_strain(&self) -> f64 {
self.equilibrium_swelling().powf(1.0 / 3.0) - 1.0
}
pub fn osmotic_pressure(&self, phi: f64) -> f64 {
const R: f64 = 8.314;
let phi = phi.clamp(1e-6, 1.0 - 1e-6);
let psi = 1.0 - phi;
let mu1 = psi.ln() + phi + self.chi * phi * phi;
-(R * self.temperature / self.v_s) * mu1
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct SelfHealingMaterial {
pub virgin_strength: f64,
pub damage: f64,
pub healing_efficiency: f64,
pub k_healing: f64,
pub activation_energy: f64,
pub t_ref: f64,
pub agent_fraction: f64,
pub damage_threshold: f64,
pub time_since_damage: f64,
pub max_efficiency: f64,
}
impl SelfHealingMaterial {
#[allow(clippy::too_many_arguments)]
pub fn new(
virgin_strength: f64,
k_healing: f64,
activation_energy: f64,
t_ref: f64,
agent_fraction: f64,
damage_threshold: f64,
max_efficiency: f64,
) -> Self {
Self {
virgin_strength,
damage: 0.0,
healing_efficiency: 0.0,
k_healing,
activation_energy,
t_ref,
agent_fraction,
damage_threshold,
time_since_damage: 0.0,
max_efficiency,
}
}
pub fn microencapsulated_epoxy() -> Self {
Self::new(60e6, 1e-4, 50e3, 298.0, 0.10, 0.05, 0.90)
}
pub fn healing_rate(&self, temperature: f64) -> f64 {
const R: f64 = 8.314;
self.k_healing
* (-self.activation_energy / (R * temperature)).exp()
* (-self.activation_energy / (R * self.t_ref)).exp().recip()
}
pub fn apply_damage(&mut self, damage: f64) {
self.damage = (self.damage + damage).clamp(0.0, 1.0);
if self.damage > self.damage_threshold {
self.time_since_damage = 0.0;
self.healing_efficiency = 0.0;
}
}
pub fn heal(&mut self, dt: f64, temperature: f64) {
if self.damage < self.damage_threshold {
return;
}
let k = self.healing_rate(temperature);
let rate = k * (self.max_efficiency - self.healing_efficiency) * self.agent_fraction;
self.healing_efficiency =
(self.healing_efficiency + rate * dt).clamp(0.0, self.max_efficiency);
self.time_since_damage += dt;
}
pub fn current_strength(&self) -> f64 {
let intact_fraction = 1.0 - self.damage;
let healed_contribution = self.healing_efficiency * self.damage;
self.virgin_strength * (intact_fraction + healed_contribution).clamp(0.0, 1.0)
}
pub fn stiffness_reduction(&self) -> f64 {
(1.0 - self.damage * (1.0 - self.healing_efficiency)).clamp(0.0, 1.0)
}
pub fn toughness_recovery(&self) -> f64 {
self.stiffness_reduction().sqrt()
}
pub fn healing_progress(&self) -> f64 {
if self.max_efficiency < 1e-10 {
0.0
} else {
self.healing_efficiency / self.max_efficiency
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct HydrogelActuator {
pub chi: f64,
pub crosslink_density: f64,
pub q_ref: f64,
pub chi_temp_coeff: f64,
pub ph_ref: f64,
pub chi_ph_coeff: f64,
pub elastic_modulus: f64,
}
impl HydrogelActuator {
pub fn new(
chi: f64,
crosslink_density: f64,
q_ref: f64,
chi_temp_coeff: f64,
ph_ref: f64,
chi_ph_coeff: f64,
elastic_modulus: f64,
) -> Self {
Self {
chi,
crosslink_density,
q_ref,
chi_temp_coeff,
ph_ref,
chi_ph_coeff,
elastic_modulus,
}
}
pub fn effective_chi(&self, temperature: f64, ph: f64) -> f64 {
self.chi
+ self.chi_temp_coeff * (temperature - 298.0)
+ self.chi_ph_coeff * (ph - self.ph_ref)
}
pub fn swelling_ratio(&self, temperature: f64, ph: f64) -> f64 {
let chi_eff = self.effective_chi(temperature, ph);
let q = self.q_ref * (-chi_eff * 0.5).exp();
q.max(1.0)
}
pub fn linear_strain(&self, temperature: f64, ph: f64) -> f64 {
let q = self.swelling_ratio(temperature, ph);
q.powf(1.0 / 3.0) - 1.0
}
pub fn swelling_pressure(&self, temperature: f64, ph: f64) -> f64 {
let strain = self.linear_strain(temperature, ph);
self.elastic_modulus * strain
}
pub fn force(&self, temperature: f64, ph: f64, area: f64) -> f64 {
self.swelling_pressure(temperature, ph) * area
}
}
#[derive(Debug, Clone, Copy)]
pub struct MagnetostrictiveMaterial {
pub lambda_sat: f64,
pub h_sat: f64,
pub elastic_modulus: f64,
pub d33: f64,
pub mu_r: f64,
}
impl MagnetostrictiveMaterial {
pub fn new(lambda_sat: f64, h_sat: f64, elastic_modulus: f64, d33: f64, mu_r: f64) -> Self {
Self {
lambda_sat,
h_sat,
elastic_modulus,
d33,
mu_r,
}
}
pub fn terfenol_d() -> Self {
Self::new(1600e-6, 60e3, 50e9, 20e-9, 3.0)
}
pub fn strain(&self, h_field: f64) -> f64 {
if h_field.abs() < 1e-10 {
return 0.0;
}
let x = h_field / self.h_sat;
let l = if x.abs() > 30.0 {
x.signum() * (1.0 - 1.0 / x.abs())
} else {
1.0 / x.tanh() - 1.0 / x
};
self.lambda_sat * l * l * h_field.signum().max(0.0)
+ self.lambda_sat * l * l * (-h_field.signum()).max(0.0)
}
pub fn strain_magnitude(&self, h_field: f64) -> f64 {
if h_field.abs() < 1e-10 {
return 0.0;
}
let x = h_field.abs() / self.h_sat;
let l = if x > 30.0 {
1.0 - 1.0 / x
} else {
1.0 / x.tanh() - 1.0 / x
};
self.lambda_sat * l * l
}
pub fn blocked_stress(&self, h_field: f64) -> f64 {
self.elastic_modulus * self.strain_magnitude(h_field)
}
pub fn piezomagnetic_coeff(&self, h_field: f64) -> f64 {
let dh = h_field * 1e-4 + 1.0;
(self.strain_magnitude(h_field + dh) - self.strain_magnitude(h_field)) / dh
}
}
#[derive(Debug, Clone, Copy)]
pub struct MagneticShape {
pub max_strain: f64,
pub k_u: f64,
pub m_sat: f64,
pub h_critical: f64,
pub eta: f64,
pub elastic_modulus: f64,
}
impl MagneticShape {
pub fn new(
max_strain: f64,
k_u: f64,
m_sat: f64,
h_critical: f64,
elastic_modulus: f64,
) -> Self {
Self {
max_strain,
k_u,
m_sat,
h_critical,
eta: 0.0,
elastic_modulus,
}
}
pub fn ni2mnga() -> Self {
Self::new(0.06, 1.65e5, 600e3, 300e3, 2.0e9)
}
pub fn zeeman_energy(&self, h_field: f64, eta: f64) -> f64 {
const MU0: f64 = 4.0 * PI * 1e-7;
-MU0 * self.m_sat * h_field * eta
}
pub fn anisotropy_energy(&self, eta: f64) -> f64 {
self.k_u * eta * (1.0 - eta)
}
pub fn update(&mut self, h_field: f64) {
if h_field.abs() > self.h_critical {
self.eta = if h_field > 0.0 { 1.0 } else { 0.0 };
} else {
let fraction = (h_field / self.h_critical).clamp(-1.0, 1.0);
self.eta = (0.5 + 0.5 * fraction).clamp(0.0, 1.0);
}
}
pub fn strain(&self) -> f64 {
self.eta * self.max_strain
}
pub fn blocking_stress(&self) -> f64 {
self.elastic_modulus * self.strain()
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct Electrostrictive {
pub epsilon_r: f64,
pub q33: f64,
pub q31: f64,
pub sat_field: f64,
pub elastic_modulus: f64,
pub thickness: f64,
}
impl Electrostrictive {
pub fn new(
epsilon_r: f64,
q33: f64,
q31: f64,
sat_field: f64,
elastic_modulus: f64,
thickness: f64,
) -> Self {
Self {
epsilon_r,
q33,
q31,
sat_field,
elastic_modulus,
thickness,
}
}
pub fn pmn_pt() -> Self {
Self::new(5000.0, 0.026, -0.010, 2e7, 6e9, 1e-3)
}
pub fn polarization(&self, e_field: f64) -> f64 {
const EPS0: f64 = 8.854e-12;
let chi = self.epsilon_r - 1.0;
let denom = 1.0 + e_field.abs() / self.sat_field;
EPS0 * chi * e_field / denom
}
pub fn maxwell_stress(&self, e_field: f64) -> f64 {
const EPS0: f64 = 8.854e-12;
0.5 * EPS0 * self.epsilon_r * e_field * e_field
}
pub fn strain_33(&self, e_field: f64) -> f64 {
let p = self.polarization(e_field);
self.q33 * p * p
}
pub fn strain_31(&self, e_field: f64) -> f64 {
let p = self.polarization(e_field);
self.q31 * p * p
}
pub fn thickness_change(&self, voltage: f64) -> f64 {
let e_field = voltage / self.thickness;
self.strain_33(e_field) * self.thickness
}
pub fn blocking_stress(&self, voltage: f64) -> f64 {
let e_field = voltage / self.thickness;
self.elastic_modulus * self.strain_33(e_field)
}
pub fn coupling_coefficient(&self, e_field: f64) -> f64 {
const EPS0: f64 = 8.854e-12;
let p = self.polarization(e_field);
let d33 = 2.0 * self.q33 * p * EPS0 * self.epsilon_r;
let s33 = 1.0 / self.elastic_modulus;
let eps33 = EPS0 * self.epsilon_r;
let k2 = (d33 * d33) / (s33 * eps33);
k2.sqrt().clamp(0.0, 1.0)
}
}
#[derive(Debug, Clone, Copy)]
pub struct PiezoActuator {
pub stroke_per_volt: f64,
pub force_per_volt: f64,
pub stiffness: f64,
pub resonance_freq: f64,
pub capacitance: f64,
pub hysteresis: f64,
}
impl PiezoActuator {
pub fn new(
stroke_per_volt: f64,
force_per_volt: f64,
stiffness: f64,
resonance_freq: f64,
capacitance: f64,
) -> Self {
Self {
stroke_per_volt,
force_per_volt,
stiffness,
resonance_freq,
capacitance,
hysteresis: 0.1,
}
}
pub fn free_stroke(&self, voltage: f64) -> f64 {
self.stroke_per_volt * voltage
}
pub fn blocking_force(&self, voltage: f64) -> f64 {
self.force_per_volt * voltage
}
pub fn output_force(&self, voltage: f64, displacement: f64) -> f64 {
let free = self.free_stroke(voltage);
self.stiffness * (free - displacement)
}
pub fn energy_per_cycle(&self, voltage: f64, freq: f64) -> f64 {
let _ = freq;
0.5 * self.capacitance * voltage * voltage
}
pub fn mechanical_work(&self, voltage: f64) -> f64 {
let f = self.blocking_force(voltage);
let d = self.free_stroke(voltage);
0.5 * f * d
}
}
#[derive(Debug, Clone)]
pub struct ThermochromicResponse {
pub t_transition: f64,
pub delta_t: f64,
pub color_low: [f64; 3],
pub color_high: [f64; 3],
pub latent_heat: f64,
}
impl ThermochromicResponse {
pub fn new(
t_transition: f64,
delta_t: f64,
color_low: [f64; 3],
color_high: [f64; 3],
latent_heat: f64,
) -> Self {
Self {
t_transition,
delta_t,
color_low,
color_high,
latent_heat,
}
}
pub fn transition_fraction(&self, temperature: f64) -> f64 {
let x = (temperature - self.t_transition) / self.delta_t;
1.0 / (1.0 + (-x).exp())
}
pub fn reflectance(&self, temperature: f64) -> [f64; 3] {
let s = self.transition_fraction(temperature);
[
self.color_low[0] + s * (self.color_high[0] - self.color_low[0]),
self.color_low[1] + s * (self.color_high[1] - self.color_low[1]),
self.color_low[2] + s * (self.color_high[2] - self.color_low[2]),
]
}
pub fn luminance(&self, temperature: f64) -> f64 {
let [r, g, b] = self.reflectance(temperature);
0.2126 * r + 0.7152 * g + 0.0722 * b
}
pub fn effective_heat_capacity(&self, temperature: f64, c_base: f64) -> f64 {
let s = self.transition_fraction(temperature);
let ds_dt = s * (1.0 - s) / self.delta_t;
c_base + self.latent_heat * ds_dt
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct PiezoelectricSmart {
pub d33: f64,
pub d31: f64,
pub d15: f64,
pub s33_e: f64,
pub s11_e: f64,
pub eps33_t: f64,
pub density: f64,
pub length: f64,
pub area: f64,
}
impl PiezoelectricSmart {
#[allow(clippy::too_many_arguments)]
pub fn new(
d33: f64,
d31: f64,
d15: f64,
s33_e: f64,
s11_e: f64,
eps33_t: f64,
density: f64,
length: f64,
area: f64,
) -> Self {
Self {
d33,
d31,
d15,
s33_e,
s11_e,
eps33_t,
density,
length,
area,
}
}
pub fn pzt5h() -> Self {
const EPS0: f64 = 8.854e-12;
Self::new(
593e-12,
-274e-12,
741e-12,
20.7e-12,
16.5e-12,
3400.0 * EPS0,
7500.0,
10e-3,
1e-4,
)
}
pub fn pzt4() -> Self {
const EPS0: f64 = 8.854e-12;
Self::new(
289e-12,
-123e-12,
496e-12,
15.5e-12,
12.3e-12,
1300.0 * EPS0,
7600.0,
10e-3,
1e-4,
)
}
pub fn k33(&self) -> f64 {
let k2 = self.d33 * self.d33 / (self.s33_e * self.eps33_t);
k2.sqrt().clamp(0.0, 1.0)
}
pub fn kp(&self) -> f64 {
let k31_2 = self.d31 * self.d31 / (self.s11_e * self.eps33_t);
let kp2 = 2.0 * k31_2 / 0.7_f64;
kp2.sqrt().clamp(0.0, 1.0)
}
pub fn resonance_frequency(&self) -> f64 {
let v_sound = (1.0 / (self.density * self.s33_e)).sqrt();
v_sound / (2.0 * self.length)
}
pub fn antiresonance_frequency(&self) -> f64 {
let fr = self.resonance_frequency();
let k = self.k33();
fr * (1.0 / (1.0 - k * k)).sqrt()
}
pub fn actuation_stroke(&self, voltage: f64) -> f64 {
self.d33 * voltage
}
pub fn actuation_stroke_31(&self, voltage: f64) -> f64 {
let thickness = self.length;
let width = self.area.sqrt();
self.d31 * voltage / thickness * width
}
pub fn blocking_force(&self, voltage: f64) -> f64 {
self.d33 * voltage * self.area / (self.s33_e * self.length)
}
pub fn charge_from_force(&self, force: f64) -> f64 {
self.d33 * force
}
pub fn voltage_from_force(&self, force: f64) -> f64 {
let stress = force / self.area;
self.d33 * stress * self.length / self.eps33_t
}
pub fn mechanical_q(&self) -> f64 {
let fr = self.resonance_frequency();
let fa = self.antiresonance_frequency();
if (fa - fr).abs() < 1e-3 {
1000.0
} else {
fr / (fa - fr)
}
}
}
#[derive(Debug, Clone)]
pub struct SmaActuator {
pub area: f64,
pub length: f64,
pub xi_s: f64,
pub xi_t: f64,
pub sma: ShapeMemoryAlloy,
pub strain: f64,
pub stress: f64,
}
impl SmaActuator {
pub fn new(sma: ShapeMemoryAlloy, area: f64, length: f64) -> Self {
Self {
area,
length,
xi_s: 0.0,
xi_t: sma.xi,
sma,
strain: 0.0,
stress: 0.0,
}
}
pub fn total_xi(&self) -> f64 {
(self.xi_s + self.xi_t).clamp(0.0, 1.0)
}
pub fn recovery_stress(&self) -> f64 {
let e = self.sma.e_a + self.total_xi() * (self.sma.e_m - self.sma.e_a);
e * (self.strain - self.sma.max_strain * self.xi_s)
}
pub fn stroke(&self, xi_final: f64) -> f64 {
let delta_xi = self.total_xi() - xi_final.clamp(0.0, 1.0);
delta_xi * self.sma.max_strain * self.length
}
pub fn force(&self) -> f64 {
self.recovery_stress() * self.area
}
pub fn update(&mut self, temperature: f64, applied_strain: f64) {
self.strain = applied_strain;
let xi_new = self.sma.update_phase(temperature, self.stress);
let xi_s_new = (applied_strain / self.sma.max_strain.max(1e-12)).clamp(0.0, xi_new);
self.xi_s = xi_s_new;
self.xi_t = (xi_new - xi_s_new).max(0.0);
self.stress = self.recovery_stress();
}
}
#[derive(Debug, Clone, Copy)]
pub struct PiezoelectricCoupling {
pub d33: f64,
pub d31: f64,
pub elastic_modulus_33: f64,
pub elastic_modulus_11: f64,
pub epsilon_r33: f64,
pub k33: f64,
}
impl PiezoelectricCoupling {
pub fn new(
d33: f64,
d31: f64,
elastic_modulus_33: f64,
elastic_modulus_11: f64,
epsilon_r33: f64,
) -> Self {
const EPS0: f64 = 8.854e-12;
let k33 = d33 * (elastic_modulus_33 / (EPS0 * epsilon_r33)).sqrt();
Self {
d33,
d31,
elastic_modulus_33,
elastic_modulus_11,
epsilon_r33,
k33,
}
}
pub fn pzt5a() -> Self {
Self::new(374e-12, -171e-12, 61e9, 61e9, 1700.0)
}
pub fn strain_from_voltage_33(&self, voltage: f64, thickness: f64) -> f64 {
self.d33 * voltage / thickness
}
pub fn strain_from_voltage_31(&self, voltage: f64, thickness: f64) -> f64 {
self.d31 * voltage / thickness
}
pub fn charge_from_stress_33(&self, stress: f64) -> f64 {
self.d33 * stress
}
pub fn voltage_from_stress(&self, stress: f64, thickness: f64) -> f64 {
const EPS0: f64 = 8.854e-12;
let p = self.charge_from_stress_33(stress);
p * thickness / (EPS0 * self.epsilon_r33)
}
pub fn harvested_energy_density(&self, stress_amplitude: f64) -> f64 {
0.5 * self.d33 * self.d33 * stress_amplitude * stress_amplitude
/ (self.epsilon_r33 * 8.854e-12)
}
}