use core::f64::consts::{E, PI};
use metrology::{AMU, K_B, M_E, R_GAS};
#[derive(Clone, Copy, Debug)]
pub struct Townsend {
pub a: f64,
pub b: f64,
}
const TORR_CM: f64 = 133.322_368 * 0.01;
pub const AIR: Townsend = Townsend { a: 15.0 / TORR_CM, b: 365.0 / TORR_CM };
pub const AIR_DIAMETER: f64 = 3.711e-10;
pub fn paschen(gas: Townsend, gamma: f64, pd: f64) -> Option<f64> {
let denominator = (gas.a * pd).ln() - (1.0 + 1.0 / gamma).ln().ln();
(denominator > 0.0).then(|| gas.b * pd / denominator)
}
pub fn paschen_minimum(gas: Townsend, gamma: f64) -> (f64, f64) {
let pd = E * (1.0 + 1.0 / gamma).ln() / gas.a;
(pd, gas.b * pd)
}
pub fn mean_free_path(pressure: f64, temperature: f64, diameter: f64) -> f64 {
K_B * temperature / (2f64.sqrt() * PI * diameter * diameter * pressure)
}
pub fn peek_onset(radius: f64, relative_density: f64) -> f64 {
3.0e6 * relative_density * (1.0 + 0.0301 / (relative_density * radius).sqrt())
}
pub const AIR_MOLAR_MASS: f64 = 0.028_964_7;
pub const AIR_VISCOSITY: f64 = 1.81e-5;
pub const AIR_ION_MOBILITY: f64 = 2.0e-4;
pub fn gas_density(pressure: f64, temperature: f64, molar_mass: f64) -> f64 {
pressure * molar_mass / (R_GAS * temperature)
}
pub fn mean_speed(temperature: f64, molar_mass: f64) -> f64 {
(8.0 * R_GAS * temperature / (PI * molar_mass)).sqrt()
}
pub fn cunningham(knudsen: f64) -> f64 {
1.0 + knudsen * (1.257 + 0.400 * (-1.10 / knudsen).exp())
}
pub fn sphere_drag(radius: f64, viscosity: f64, knudsen: f64) -> f64 {
6.0 * PI * viscosity * radius / cunningham(knudsen)
}
pub fn sphere_spin_drag(radius: f64, viscosity: f64, density: f64, mean_speed: f64) -> f64 {
let dense = 8.0 * PI * viscosity * radius.powi(3);
let thin = 2.0 * PI / 3.0 * density * mean_speed * radius.powi(4);
1.0 / (1.0 / dense + 1.0 / thin)
}
pub fn ion_mobility(reduced: f64, pressure: f64, temperature: f64) -> f64 {
reduced * (101_325.0 / pressure) * (temperature / 273.15)
}
pub fn floating_potential(te: f64, ion_mass: f64) -> f64 {
-0.5 * te * (ion_mass / (2.0 * PI * M_E)).ln()
}
pub const N2_ION_MASS: f64 = 28.0 * AMU;
#[cfg(test)]
mod tests {
use super::*;
fn close(a: f64, b: f64, rel: f64) -> bool {
(a / b - 1.0).abs() < rel
}
#[test]
fn the_paschen_minimum_of_air() {
let (pd, v) = paschen_minimum(AIR, 0.01);
assert!(close(pd, 1.1150, 1e-4) && close(v, 305.27, 1e-4), "{pd} {v}");
assert!(close(pd / TORR_CM, 0.8363, 1e-3));
let at = |x: f64| paschen(AIR, 0.01, x).unwrap();
assert!(close(at(pd), v, 1e-12) && at(0.9 * pd) > v && at(1.1 * pd) > v);
let edge = (1.0 + 1.0 / 0.01f64).ln() / AIR.a;
assert!(paschen(AIR, 0.01, 0.99 * edge).is_none() && paschen(AIR, 0.01, 1.01 * edge).is_some());
}
#[test]
fn a_ten_centimetre_tank() {
assert!(close(paschen(AIR, 0.01, 100.0 * 0.1).unwrap(), 857.23, 1e-4));
assert!(close(paschen(AIR, 0.01, 10.0 * 0.1).unwrap(), 307.24, 1e-4));
assert!(paschen(AIR, 0.01, 1.0 * 0.1).is_none());
}
#[test]
fn mean_free_path_of_air() {
let lambda = mean_free_path(1.0, 293.15, AIR_DIAMETER);
assert!(close(lambda, 6.6e-3, 0.02), "{lambda}");
assert!(close(mean_free_path(101_325.0, 293.15, AIR_DIAMETER) / lambda, 1.0 / 101_325.0, 1e-12));
}
#[test]
fn the_gas_itself() {
assert!(close(gas_density(101_325.0, 293.15, AIR_MOLAR_MASS), 1.2041, 1e-4));
assert!(close(mean_speed(293.15, AIR_MOLAR_MASS), 462.9, 1e-3));
assert!(close(ion_mobility(AIR_ION_MOBILITY, 1.0, 273.15), 20.265, 1e-9));
assert!(close(floating_potential(2.0, N2_ION_MASS), -9.002, 1e-3), "{}", floating_potential(2.0, N2_ION_MASS));
}
#[test]
fn drag_across_the_knudsen_range() {
assert!(close(cunningham(1e-9), 1.0, 1e-8) && close(sphere_drag(1e-3, AIR_VISCOSITY, 0.0), 6.0 * PI * AIR_VISCOSITY * 1e-3, 1e-15));
assert!(close(cunningham(0.132), 1.166, 1e-3), "{}", cunningham(0.132));
let (a, rho, v) = (1e-3, 1e-8, 463.0); let lambda = 2.0 * AIR_VISCOSITY / (rho * v);
let epstein = 4.0 * PI / 3.0 * a * a * rho * v * (1.0 + PI / 8.0);
assert!(close(sphere_drag(a, AIR_VISCOSITY, lambda / a), epstein, 0.03));
assert!(close(sphere_spin_drag(a, AIR_VISCOSITY, 1e3, v), 8.0 * PI * AIR_VISCOSITY * a.powi(3), 1e-6));
assert!(close(sphere_spin_drag(a, AIR_VISCOSITY, 1e-12, v), 2.0 * PI / 3.0 * 1e-12 * v * a.powi(4), 1e-6));
}
#[test]
fn peek_corona_onset() {
assert!(close(peek_onset(0.01, 1.0), 3.903e6, 1e-3));
assert!(close(peek_onset(0.75e-3, 1.0), 6.297e6, 1e-3));
assert!(peek_onset(0.01, 0.5) < peek_onset(0.01, 1.0));
}
}