use crate::math::constants::{C, E_CHARGE, EPSILON_0, K_B, M_ELECTRON, MU_0, PI};
#[must_use]
pub fn debye_length(temperature: f64, density: f64, charge: f64) -> f64 {
assert!(density > 0.0, "density must be positive");
assert!(charge != 0.0, "charge must not be zero");
(EPSILON_0 * K_B * temperature / (density * charge * charge)).sqrt()
}
#[must_use]
pub fn plasma_frequency_electron(density: f64) -> f64 {
(density * E_CHARGE * E_CHARGE / (M_ELECTRON * EPSILON_0)).sqrt()
}
#[must_use]
pub fn plasma_frequency_ion(density: f64, ion_mass: f64) -> f64 {
assert!(ion_mass > 0.0, "ion_mass must be positive");
(density * E_CHARGE * E_CHARGE / (ion_mass * EPSILON_0)).sqrt()
}
#[must_use]
pub fn cyclotron_frequency_electron(b_field: f64) -> f64 {
E_CHARGE * b_field / M_ELECTRON
}
#[must_use]
pub fn cyclotron_frequency_ion(b_field: f64, ion_mass: f64, charge: f64) -> f64 {
assert!(ion_mass > 0.0, "ion_mass must be positive");
charge * b_field / ion_mass
}
#[must_use]
pub fn larmor_radius(velocity_perp: f64, mass: f64, charge: f64, b_field: f64) -> f64 {
assert!(charge != 0.0, "charge must not be zero");
assert!(b_field > 0.0, "b_field must be positive");
mass * velocity_perp / (charge.abs() * b_field)
}
#[must_use]
pub fn plasma_beta(pressure: f64, b_field: f64) -> f64 {
assert!(b_field != 0.0, "b_field must not be zero");
2.0 * MU_0 * pressure / (b_field * b_field)
}
#[must_use]
pub fn alfven_speed(b_field: f64, density: f64) -> f64 {
assert!(density > 0.0, "density must be positive");
b_field / (MU_0 * density).sqrt()
}
#[must_use]
pub fn sound_speed_plasma(gamma: f64, temperature: f64, ion_mass: f64) -> f64 {
assert!(ion_mass > 0.0, "ion_mass must be positive");
(gamma * K_B * temperature / ion_mass).sqrt()
}
#[must_use]
pub fn thermal_velocity(temperature: f64, mass: f64) -> f64 {
assert!(mass > 0.0, "mass must be positive");
(2.0 * K_B * temperature / mass).sqrt()
}
#[must_use]
pub fn debye_number(density: f64, debye_len: f64) -> f64 {
density * (4.0 * PI / 3.0) * debye_len.powi(3)
}
#[must_use]
pub fn coulomb_logarithm(temperature: f64, density: f64) -> f64 {
let lambda_d = debye_length(temperature, density, E_CHARGE);
let nd = debye_number(density, lambda_d);
(12.0 * PI * nd).ln()
}
#[must_use]
pub fn magnetic_pressure(b_field: f64) -> f64 {
b_field * b_field / (2.0 * MU_0)
}
#[must_use]
pub fn magnetosonic_speed(alfven: f64, sound: f64) -> f64 {
(alfven * alfven + sound * sound).sqrt()
}
#[must_use]
pub fn skin_depth_plasma(plasma_freq: f64) -> f64 {
assert!(plasma_freq > 0.0, "plasma_freq must be positive");
C / plasma_freq
}
#[must_use]
pub fn collision_frequency(
density: f64,
temperature: f64,
coulomb_log: f64,
mass: f64,
charge: f64,
) -> f64 {
let vth = thermal_velocity(temperature, mass);
let numerator = density * charge.powi(4) * coulomb_log;
let denominator = 4.0 * PI * EPSILON_0 * EPSILON_0 * mass * mass * vth.powi(3);
numerator / denominator
}
#[cfg(test)]
mod tests {
use super::*;
use crate::math::constants::M_PROTON;
fn approx_rel(a: f64, b: f64, tol: f64) -> bool {
if b == 0.0 {
return a.abs() < tol;
}
((a - b) / b).abs() < tol
}
#[test]
fn test_debye_length() {
let temp = 1.16e7;
let n = 1e20;
let ld = debye_length(temp, n, E_CHARGE);
assert!(approx_rel(ld, 2.35e-5, 0.05));
}
#[test]
fn test_plasma_frequency_electron() {
let n = 1e18;
let wp = plasma_frequency_electron(n);
assert!(approx_rel(wp, 5.64e10, 0.02));
}
#[test]
fn test_plasma_frequency_ion() {
let n = 1e18;
let wp_ion = plasma_frequency_ion(n, M_PROTON);
let wp_e = plasma_frequency_electron(n);
let ratio = wp_ion / wp_e;
let expected_ratio = (M_ELECTRON / M_PROTON).sqrt();
assert!(approx_rel(ratio, expected_ratio, 1e-6));
}
#[test]
fn test_cyclotron_frequency_electron() {
let b = 1.0; let wc = cyclotron_frequency_electron(b);
assert!(approx_rel(wc, 1.76e11, 0.01));
}
#[test]
fn test_cyclotron_frequency_ion() {
let b = 1.0;
let wc = cyclotron_frequency_ion(b, M_PROTON, E_CHARGE);
assert!(approx_rel(wc, 9.58e7, 0.01));
}
#[test]
fn test_larmor_radius() {
let v_perp = 1e6; let b = 1.0;
let rl = larmor_radius(v_perp, M_ELECTRON, E_CHARGE, b);
assert!(approx_rel(rl, 5.69e-6, 0.01));
}
#[test]
fn test_plasma_beta() {
let pressure = 1e5; let b = 1.0;
let beta = plasma_beta(pressure, b);
assert!(approx_rel(beta, 0.2514, 0.01));
}
#[test]
fn test_alfven_speed() {
let b = 1e-3; let rho = 1e-12; let va = alfven_speed(b, rho);
assert!(approx_rel(va, 8.92e5, 0.02));
}
#[test]
fn test_sound_speed_plasma() {
let gamma = 5.0 / 3.0;
let temp = 1e6; let cs = sound_speed_plasma(gamma, temp, M_PROTON);
assert!(approx_rel(cs, 1.17e5, 0.02));
}
#[test]
fn test_thermal_velocity() {
let temp = 1e6;
let vth = thermal_velocity(temp, M_ELECTRON);
assert!(approx_rel(vth, 5.51e6, 0.02));
}
#[test]
fn test_debye_number() {
let n = 1e20;
let ld = 2.35e-5;
let nd = debye_number(n, ld);
assert!(approx_rel(nd, 5.44e6, 0.05));
}
#[test]
fn test_coulomb_logarithm() {
let temp = 1.16e7;
let n = 1e20;
let ln_lambda = coulomb_logarithm(temp, n);
assert!(ln_lambda > 10.0 && ln_lambda < 30.0);
}
#[test]
fn test_magnetic_pressure() {
let b = 1.0;
let pm = magnetic_pressure(b);
assert!(approx_rel(pm, 3.98e5, 0.01));
}
#[test]
fn test_magnetosonic_speed() {
let va = 1000.0;
let cs = 500.0;
let vms = magnetosonic_speed(va, cs);
let expected = 1118.033988749895;
assert!(approx_rel(vms, expected, 1e-10));
}
#[test]
fn test_skin_depth_plasma() {
let wp = 5.64e10;
let delta = skin_depth_plasma(wp);
assert!(approx_rel(delta, 5.31e-3, 0.02));
}
#[test]
fn test_collision_frequency() {
let n = 1e20;
let temp = 1.16e7;
let ln_lambda = 17.0;
let nu = collision_frequency(n, temp, ln_lambda, M_ELECTRON, E_CHARGE);
assert!(nu > 0.0 && nu.is_finite());
}
#[test]
fn test_approx_rel_zero_b() {
assert!(approx_rel(0.0, 0.0, 1e-6));
assert!(!approx_rel(1.0, 0.0, 0.5));
}
}