use super::functions::R_UNIVERSAL;
#[allow(unused_imports)]
use super::functions::*;
#[derive(Debug, Clone, Copy)]
pub struct ShockHugoniot {
pub rho0: f64,
pub c0: f64,
pub s: f64,
pub p0: f64,
}
impl ShockHugoniot {
pub fn new(rho0: f64, c0: f64, s: f64) -> Self {
Self {
rho0,
c0,
s,
p0: 0.0,
}
}
pub fn aluminum() -> Self {
Self::new(2700.0, 5240.0, 1.4)
}
pub fn iron() -> Self {
Self::new(7874.0, 3574.0, 1.92)
}
pub fn shock_velocity(&self, u_p: f64) -> f64 {
self.c0 + self.s * u_p
}
pub fn hugoniot_pressure(&self, u_p: f64) -> f64 {
let u_s = self.shock_velocity(u_p);
self.rho0 * u_s * u_p + self.p0
}
pub fn post_shock_density(&self, u_p: f64) -> f64 {
let u_s = self.shock_velocity(u_p);
if (u_s - u_p).abs() < 1e-10 {
return f64::INFINITY;
}
self.rho0 * u_s / (u_s - u_p)
}
pub fn hugoniot_energy(&self, u_p: f64) -> f64 {
let p_h = self.hugoniot_pressure(u_p);
let rho_h = self.post_shock_density(u_p);
0.5 * (self.p0 + p_h) * (1.0 / self.rho0 - 1.0 / rho_h)
}
}
#[derive(Debug, Clone, Copy)]
pub struct VinetEos {
pub v0: f64,
pub k0: f64,
pub k0_prime: f64,
}
impl VinetEos {
pub fn new(v0: f64, k0: f64, k0_prime: f64) -> Self {
Self { v0, k0, k0_prime }
}
pub fn diamond() -> Self {
Self::new(1.0 / 3515.0, 443.0e9, 3.6)
}
pub fn copper() -> Self {
Self::new(1.0 / 8960.0, 136.7e9, 5.3)
}
pub fn pressure_from_volume(&self, v: f64) -> f64 {
let x = (v / self.v0).powf(1.0 / 3.0);
if (x - 1.0).abs() < 1e-15 {
return 0.0;
}
let eta = 1.5 * (self.k0_prime - 1.0);
3.0 * self.k0 * (1.0 - x) / (x * x) * (eta * (1.0 - x)).exp()
}
pub fn volume(&self, pressure: f64) -> f64 {
let mut lo = self.v0 * 0.3;
let mut hi = self.v0 * 2.0;
for _ in 0..80 {
let mid = 0.5 * (lo + hi);
if self.pressure_from_volume(mid) > pressure {
lo = mid;
} else {
hi = mid;
}
}
0.5 * (lo + hi)
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct JwlEos {
pub big_a: f64,
pub big_b: f64,
pub r1: f64,
pub r2: f64,
pub omega: f64,
pub e0: f64,
pub rho0: f64,
}
#[allow(dead_code)]
impl JwlEos {
#[allow(clippy::too_many_arguments)]
pub fn new(big_a: f64, big_b: f64, r1: f64, r2: f64, omega: f64, e0: f64, rho0: f64) -> Self {
Self {
big_a,
big_b,
r1,
r2,
omega,
e0,
rho0,
}
}
pub fn tnt() -> Self {
Self::new(3.712e11, 3.231e9, 4.15, 0.95, 0.30, 7.0e9, 1630.0)
}
pub fn pressure(&self, rho: f64, e: f64) -> f64 {
let v = self.rho0 / rho.max(1e-30);
let term1 = self.big_a * (1.0 - self.omega / (self.r1 * v)) * (-self.r1 * v).exp();
let term2 = self.big_b * (1.0 - self.omega / (self.r2 * v)) * (-self.r2 * v).exp();
let term3 = self.omega * e / v;
term1 + term2 + term3
}
}
#[derive(Debug, Clone, Copy)]
pub struct PolynomialEos {
pub rho0: f64,
pub a: [f64; 3],
pub b: [f64; 3],
pub t: [f64; 2],
}
impl PolynomialEos {
pub fn new(rho0: f64, a: [f64; 3], b: [f64; 3], t: [f64; 2]) -> Self {
Self { rho0, a, b, t }
}
pub fn mu(&self, density: f64) -> f64 {
density / self.rho0 - 1.0
}
pub fn pressure_energy(&self, density: f64, energy: f64) -> f64 {
let mu = self.mu(density);
if mu >= 0.0 {
let poly = self.a[0] * mu + self.a[1] * mu * mu + self.a[2] * mu * mu * mu;
let energy_term = (self.b[0] + self.b[1] * mu + self.b[2] * mu * mu) * density * energy;
poly + energy_term
} else {
self.t[0] * mu + self.t[1] * mu * mu
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct GruneisenParameter {
pub gamma_0: f64,
pub v0: f64,
pub q: f64,
}
impl GruneisenParameter {
pub fn new(gamma_0: f64, v0: f64, q: f64) -> Self {
Self { gamma_0, v0, q }
}
pub fn evaluate(&self, v: f64) -> f64 {
self.gamma_0 * (v / self.v0).powf(self.q)
}
pub fn thermal_pressure(&self, density: f64, cv: f64, temperature: f64, t0: f64) -> f64 {
let v = 1.0 / density;
self.evaluate(v) * density * cv * (temperature - t0)
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct PengRobinson {
pub tc: f64,
pub pc: f64,
pub omega: f64,
}
#[allow(dead_code)]
impl PengRobinson {
pub fn new(tc: f64, pc: f64, omega: f64) -> Self {
Self { tc, pc, omega }
}
pub fn b_param(&self) -> f64 {
0.07780 * R_UNIVERSAL * self.tc / self.pc
}
pub fn a_critical(&self) -> f64 {
0.45724 * R_UNIVERSAL * R_UNIVERSAL * self.tc * self.tc / self.pc
}
pub fn acentric_factor_correction(&self, t: f64) -> f64 {
let kappa = 0.37464 + 1.54226 * self.omega - 0.26992 * self.omega * self.omega;
(1.0 + kappa * (1.0 - (t / self.tc).sqrt())).powi(2)
}
pub fn a_param(&self, t: f64) -> f64 {
self.a_critical() * self.acentric_factor_correction(t)
}
pub fn pressure(&self, t: f64, v_mol: f64) -> f64 {
let b = self.b_param();
let a_t = self.a_param(t);
if (v_mol - b).abs() < 1e-20 {
return f64::INFINITY;
}
R_UNIVERSAL * t / (v_mol - b) - a_t / (v_mol * (v_mol + b) + b * (v_mol - b))
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct MurnaghanEos {
pub v0: f64,
pub k0: f64,
pub n: f64,
}
#[allow(dead_code)]
impl MurnaghanEos {
pub fn new(v0: f64, k0: f64, n: f64) -> Self {
Self { v0, k0, n }
}
pub fn aluminum() -> Self {
Self::new(1.0 / 2700.0, 76.0e9, 4.5)
}
pub fn iron() -> Self {
Self::new(1.0 / 7874.0, 170.0e9, 4.9)
}
pub fn pressure_from_volume(&self, v: f64) -> f64 {
if self.n.abs() < f64::EPSILON {
return 0.0;
}
(self.k0 / self.n) * ((self.v0 / v).powf(self.n) - 1.0)
}
pub fn volume_from_pressure(&self, pressure: f64) -> f64 {
let x = 1.0 + self.n * pressure / self.k0;
if x <= 0.0 {
return self.v0 * 0.01;
}
self.v0 / x.powf(1.0 / self.n)
}
pub fn bulk_modulus(&self, v: f64) -> f64 {
self.k0 * (self.v0 / v).powf(self.n)
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct TaitBulkEos {
pub k0: f64,
pub k0_prime: f64,
pub rho0: f64,
}
#[allow(dead_code)]
impl TaitBulkEos {
pub fn new(k0: f64, k0_prime: f64, rho0: f64) -> Self {
Self { k0, k0_prime, rho0 }
}
pub fn water() -> Self {
Self::new(2.2e9, 6.0, 997.0)
}
pub fn pressure(&self, rho: f64) -> f64 {
let ratio = rho / self.rho0;
self.k0 / self.k0_prime * (ratio.powf(self.k0_prime) - 1.0)
}
#[allow(dead_code)]
pub fn sound_speed(&self, rho: f64) -> f64 {
let ratio = rho / self.rho0;
(self.k0 * ratio.powf(self.k0_prime - 1.0) / self.rho0).sqrt()
}
}
#[derive(Debug, Clone, Copy)]
pub struct IdealGasEos {
pub gamma: f64,
pub specific_gas_constant: f64,
}
impl IdealGasEos {
pub fn new(gamma: f64, specific_gas_constant: f64) -> Self {
Self {
gamma,
specific_gas_constant,
}
}
pub fn air() -> Self {
Self::new(1.4, 287.0)
}
pub fn hydrogen() -> Self {
Self::new(1.4, 4157.0)
}
pub fn pressure_at_temperature(&self, density: f64, temperature: f64) -> f64 {
density * self.specific_gas_constant * temperature
}
pub fn temperature(&self, density: f64, pressure: f64) -> f64 {
if density.abs() < f64::EPSILON {
return 0.0;
}
pressure / (density * self.specific_gas_constant)
}
}
#[derive(Debug, Clone, Copy)]
pub struct BirchMurnaghan3Eos {
pub v0: f64,
pub k0: f64,
pub k0_prime: f64,
}
impl BirchMurnaghan3Eos {
pub fn new(v0: f64, k0: f64, k0_prime: f64) -> Self {
Self { v0, k0, k0_prime }
}
pub fn iron() -> Self {
Self::new(1.0 / 7874.0, 170.0e9, 4.9)
}
pub fn mgo() -> Self {
Self::new(1.0 / 3580.0, 160.2e9, 3.99)
}
pub fn pressure_from_volume(&self, v: f64) -> f64 {
let x = self.v0 / v;
let x23 = x.powf(2.0 / 3.0);
let x73 = x.powf(7.0 / 3.0);
let x53 = x.powf(5.0 / 3.0);
let correction = 1.0 + 0.75 * (self.k0_prime - 4.0) * (x23 - 1.0);
1.5 * self.k0 * (x73 - x53) * correction
}
pub fn volume(&self, pressure: f64) -> f64 {
let mut lo = self.v0 * 0.1;
let mut hi = self.v0 * 10.0;
for _ in 0..80 {
let mid = 0.5 * (lo + hi);
if self.pressure_from_volume(mid) > pressure {
lo = mid;
} else {
hi = mid;
}
}
0.5 * (lo + hi)
}
pub fn bulk_modulus(&self, v: f64) -> f64 {
let h = v * 1e-6;
let dp = self.pressure_from_volume(v - h) - self.pressure_from_volume(v + h);
v * dp / (2.0 * h)
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct VanDerWaals {
pub a: f64,
pub b: f64,
pub r_gas: f64,
}
#[allow(dead_code)]
impl VanDerWaals {
pub fn new(a: f64, b: f64, r_gas: f64) -> Self {
Self { a, b, r_gas }
}
pub fn with_r(a: f64, b: f64) -> Self {
Self::new(a, b, 8.314_462_618)
}
pub fn pressure(&self, t: f64, v: f64, _n: f64) -> f64 {
if (v - self.b).abs() < 1e-20 {
return f64::INFINITY;
}
self.r_gas * t / (v - self.b) - self.a / (v * v)
}
pub fn compressibility(&self, t: f64, p: f64, _n: f64) -> f64 {
let rt = self.r_gas * t;
if rt.abs() < 1e-30 {
return 0.0;
}
let mut lo = self.b + 1e-10;
let mut hi = rt / p.max(1.0) * 100.0 + 1.0;
hi = hi.max(lo * 1000.0);
for _ in 0..80 {
let mid = 0.5 * (lo + hi);
if self.pressure(t, mid, 1.0) > p {
lo = mid;
} else {
hi = mid;
}
}
let v_m = 0.5 * (lo + hi);
p * v_m / rt
}
}
#[derive(Debug, Clone, Copy)]
pub struct MieGruneisenEos {
pub rho0: f64,
pub gamma_0: f64,
pub c0: f64,
pub s: f64,
}
impl MieGruneisenEos {
pub fn new(rho0: f64, gamma_0: f64, c0: f64, s: f64) -> Self {
Self {
rho0,
gamma_0,
c0,
s,
}
}
pub fn copper() -> Self {
Self::new(8930.0, 2.0, 3933.0, 1.5)
}
pub fn aluminum() -> Self {
Self::new(2785.0, 2.0, 5386.0, 1.339)
}
pub fn iron() -> Self {
Self::new(7896.0, 1.81, 3574.0, 1.92)
}
pub fn compression(&self, density: f64) -> f64 {
1.0 - self.rho0 / density
}
pub fn hugoniot_pressure(&self, density: f64) -> f64 {
let mu = density / self.rho0 - 1.0;
if mu <= 0.0 {
return 0.0;
}
let denom = (1.0 - self.s * mu).powi(2);
if denom.abs() < f64::EPSILON {
return 0.0;
}
self.rho0 * self.c0 * self.c0 * mu / denom
}
pub fn pressure_from_energy(&self, density: f64, energy: f64) -> f64 {
let ph = self.hugoniot_pressure(density);
let eh = if density > self.rho0 {
ph * 0.5 * (1.0 / self.rho0 - 1.0 / density)
} else {
0.0
};
ph + self.gamma_0 * density * (energy - eh)
}
}
#[derive(Debug, Clone, Copy)]
pub struct TaitEos {
pub reference_density: f64,
pub reference_pressure: f64,
pub speed_of_sound: f64,
pub gamma: f64,
}
impl TaitEos {
pub fn new(
reference_density: f64,
reference_pressure: f64,
speed_of_sound: f64,
gamma: f64,
) -> Self {
Self {
reference_density,
reference_pressure,
speed_of_sound,
gamma,
}
}
pub fn water() -> Self {
Self::new(1000.0, 0.0, 1500.0, 7.0)
}
pub fn b_coefficient(&self) -> f64 {
self.reference_density * self.speed_of_sound.powi(2) / self.gamma
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct RedlichKwong {
pub a: f64,
pub b: f64,
}
#[allow(dead_code)]
impl RedlichKwong {
pub fn new(a: f64, b: f64) -> Self {
Self { a, b }
}
pub fn from_critical(tc: f64, pc: f64) -> Self {
let a = 0.42748 * R_UNIVERSAL * R_UNIVERSAL * tc.powf(2.5) / pc;
let b = 0.08664 * R_UNIVERSAL * tc / pc;
Self { a, b }
}
pub fn pressure(&self, t: f64, v_mol: f64) -> f64 {
if (v_mol - self.b).abs() < 1e-20 {
return f64::INFINITY;
}
R_UNIVERSAL * t / (v_mol - self.b) - self.a / (t.sqrt() * v_mol * (v_mol + self.b))
}
}
#[derive(Debug, Clone, Copy)]
pub struct VanDerWaalsEos {
pub a: f64,
pub b: f64,
pub molar_mass: f64,
pub r_gas: f64,
}
impl VanDerWaalsEos {
pub fn new(a: f64, b: f64, molar_mass: f64) -> Self {
Self {
a,
b,
molar_mass,
r_gas: 8.314,
}
}
pub fn co2() -> Self {
Self::new(0.3658, 42.9e-6, 0.044)
}
pub fn water_vapor() -> Self {
Self::new(0.5537, 30.5e-6, 0.018)
}
pub fn nitrogen() -> Self {
Self::new(0.1370, 38.7e-6, 0.028)
}
pub fn pressure_at_temperature(&self, density: f64, temperature: f64) -> f64 {
let specific_volume = 1.0 / density;
let molar_volume = specific_volume * self.molar_mass;
if (molar_volume - self.b).abs() < 1e-15 {
return f64::INFINITY;
}
self.r_gas * temperature / (molar_volume - self.b) - self.a / (molar_volume * molar_volume)
}
}
#[derive(Debug, Clone, Copy)]
pub struct TillotsonEos {
pub rho0: f64,
pub e0: f64,
pub a: f64,
pub b: f64,
pub big_a: f64,
pub big_b: f64,
pub e_iv: f64,
pub e_cv: f64,
pub alpha: f64,
pub beta: f64,
}
impl TillotsonEos {
#[allow(clippy::too_many_arguments)]
pub fn new(
rho0: f64,
e0: f64,
a: f64,
b: f64,
big_a: f64,
big_b: f64,
e_iv: f64,
e_cv: f64,
alpha: f64,
beta: f64,
) -> Self {
Self {
rho0,
e0,
a,
b,
big_a,
big_b,
e_iv,
e_cv,
alpha,
beta,
}
}
pub fn granite() -> Self {
Self::new(
2680.0, 16.0e6, 0.5, 1.3, 18.0e9, 18.0e9, 3.5e6, 18.0e6, 5.0, 5.0,
)
}
pub fn basalt() -> Self {
Self::new(
2860.0, 49.0e6, 0.49, 1.39, 26.7e9, 26.7e9, 4.72e6, 14.0e6, 5.0, 5.0,
)
}
pub fn eta(&self, density: f64) -> f64 {
density / self.rho0
}
pub fn pressure_compressed(&self, density: f64, energy: f64) -> f64 {
let eta = self.eta(density);
let mu = eta - 1.0;
let z = energy / (self.e0 * eta * eta);
(self.a + self.b / (z + 1.0)) * density * energy + self.big_a * mu + self.big_b * mu * mu
}
pub fn pressure_expanded(&self, density: f64, energy: f64) -> f64 {
let eta = self.eta(density);
let mu = eta - 1.0;
let z = energy / (self.e0 * eta * eta);
let term1 = self.a * density * energy;
let term2 = (self.b * density * energy / (z + 1.0)
+ self.big_a * mu * (-self.beta * (1.0 / eta - 1.0)).exp())
* (-self.alpha * (1.0 / eta - 1.0).powi(2)).exp();
term1 + term2
}
pub fn pressure_from_energy(&self, density: f64, energy: f64) -> f64 {
let eta = self.eta(density);
if eta >= 1.0 || energy <= self.e_iv {
self.pressure_compressed(density, energy)
} else if energy >= self.e_cv {
self.pressure_expanded(density, energy)
} else {
let t = (energy - self.e_iv) / (self.e_cv - self.e_iv);
let pc = self.pressure_compressed(density, energy);
let pe = self.pressure_expanded(density, energy);
(1.0 - t) * pc + t * pe
}
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct SoaveRedlichKwong {
pub a: f64,
pub b: f64,
pub omega: f64,
pub tc: f64,
}
#[allow(dead_code)]
impl SoaveRedlichKwong {
pub fn new(a: f64, b: f64, omega: f64, tc: f64) -> Self {
Self { a, b, omega, tc }
}
pub fn from_critical(tc: f64, pc: f64, omega: f64) -> Self {
let a = 0.42748 * R_UNIVERSAL * R_UNIVERSAL * tc * tc / pc;
let b = 0.08664 * R_UNIVERSAL * tc / pc;
Self { a, b, omega, tc }
}
fn alpha(&self, t: f64) -> f64 {
let m = 0.480 + 1.574 * self.omega - 0.176 * self.omega * self.omega;
(1.0 + m * (1.0 - (t / self.tc).sqrt())).powi(2)
}
pub fn pressure(&self, t: f64, v_mol: f64) -> f64 {
let a_t = self.a * self.alpha(t);
if (v_mol - self.b).abs() < 1e-20 {
return f64::INFINITY;
}
R_UNIVERSAL * t / (v_mol - self.b) - a_t / (v_mol * (v_mol + self.b))
}
}
#[derive(Debug, Clone, Copy)]
pub struct StiffenedGasEos {
pub gamma: f64,
pub p_inf: f64,
pub reference_density: f64,
pub reference_energy: f64,
}
impl StiffenedGasEos {
pub fn new(gamma: f64, p_inf: f64, reference_density: f64, reference_energy: f64) -> Self {
Self {
gamma,
p_inf,
reference_density,
reference_energy,
}
}
pub fn water() -> Self {
Self::new(6.59, 4.02e8, 1000.0, 0.0)
}
pub fn air() -> Self {
Self::new(1.4, 0.0, 1.225, 0.0)
}
pub fn pressure_from_energy(&self, density: f64, energy: f64) -> f64 {
(self.gamma - 1.0) * density * (energy - self.reference_energy) - self.p_inf
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct NobleAbelEos {
pub r_specific: f64,
pub b_covolume: f64,
}
#[allow(dead_code)]
impl NobleAbelEos {
pub fn new(r_specific: f64, b_covolume: f64) -> Self {
Self {
r_specific,
b_covolume,
}
}
pub fn propellant() -> Self {
Self::new(290.0, 0.001)
}
pub fn pressure_at_temperature(&self, density: f64, temperature: f64) -> f64 {
let denom = 1.0 - self.b_covolume * density;
if denom <= 0.0 {
return f64::INFINITY;
}
density * self.r_specific * temperature / denom
}
pub fn density_at_temperature(&self, pressure: f64, temperature: f64) -> f64 {
let rt = self.r_specific * temperature;
if rt.abs() < 1e-30 {
return 0.0;
}
pressure / (rt + pressure * self.b_covolume)
}
}
pub struct PvtEos {
pub cold_eos: BirchMurnaghan3Eos,
pub gruneisen: GruneisenParameter,
pub cv: f64,
pub t0: f64,
}
impl PvtEos {
pub fn new(
cold_eos: BirchMurnaghan3Eos,
gruneisen: GruneisenParameter,
cv: f64,
t0: f64,
) -> Self {
Self {
cold_eos,
gruneisen,
cv,
t0,
}
}
pub fn iron() -> Self {
let cold = BirchMurnaghan3Eos::iron();
let v0 = cold.v0;
let gruneisen = GruneisenParameter::new(1.81, v0, 1.0);
Self::new(cold, gruneisen, 450.0, 300.0)
}
pub fn pressure(&self, v: f64, temperature: f64) -> f64 {
let density = 1.0 / v;
let p_cold = self.cold_eos.pressure_from_volume(v);
let p_th = self
.gruneisen
.thermal_pressure(density, self.cv, temperature, self.t0);
p_cold + p_th
}
pub fn density(&self, pressure: f64, temperature: f64) -> f64 {
let mut lo = 1.0 / (self.cold_eos.v0 * 3.0);
let mut hi = 1.0 / (self.cold_eos.v0 * 0.3);
for _ in 0..80 {
let mid = 0.5 * (lo + hi);
if self.pressure(1.0 / mid, temperature) < pressure {
lo = mid;
} else {
hi = mid;
}
}
0.5 * (lo + hi)
}
}