use crate::math::constants::{K_B, MU_0, PI};
const FROZEN_IN_THRESHOLD: f64 = 100.0;
#[must_use]
pub fn magnetic_reynolds_number(
velocity: f64,
length: f64,
conductivity: f64,
) -> f64 {
MU_0 * conductivity * velocity * length
}
#[must_use]
pub fn magnetic_diffusivity(conductivity: f64) -> f64 {
assert!(conductivity > 0.0, "conductivity must be positive");
1.0 / (MU_0 * conductivity)
}
#[must_use]
pub fn lundquist_number(alfven_speed: f64, length: f64, diffusivity: f64) -> f64 {
assert!(diffusivity > 0.0, "diffusivity must be positive");
alfven_speed * length / diffusivity
}
#[must_use]
pub fn hartmann_number(
b_field: f64,
length: f64,
conductivity: f64,
dynamic_viscosity: f64,
) -> f64 {
assert!(dynamic_viscosity > 0.0, "dynamic_viscosity must be positive");
b_field * length * (conductivity / dynamic_viscosity).sqrt()
}
#[must_use]
pub fn magnetic_pressure(b_field: f64) -> f64 {
b_field * b_field / (2.0 * MU_0)
}
#[must_use]
pub fn total_pressure(gas_pressure: f64, b_field: f64) -> f64 {
gas_pressure + magnetic_pressure(b_field)
}
#[must_use]
pub fn plasma_beta(gas_pressure: f64, b_field: f64) -> f64 {
assert!(b_field != 0.0, "magnetic field must be non-zero");
2.0 * MU_0 * gas_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 slow_magnetosonic_speed(alfven: f64, sound: f64) -> f64 {
alfven.min(sound)
}
#[must_use]
pub fn fast_magnetosonic_speed(alfven: f64, sound: f64) -> f64 {
(alfven * alfven + sound * sound).sqrt()
}
#[must_use]
pub fn magnetosonic_mach(velocity: f64, alfven: f64, sound: f64) -> f64 {
let v_fast = fast_magnetosonic_speed(alfven, sound);
velocity / v_fast
}
#[must_use]
pub fn pinch_pressure_balance(current: f64, radius: f64) -> f64 {
assert!(radius > 0.0, "radius must be positive");
let b_theta = MU_0 * current / (2.0 * PI * radius);
magnetic_pressure(b_theta)
}
#[must_use]
pub fn bennett_pinch_condition(
current: f64,
line_density: f64,
temperature: f64,
) -> bool {
let rhs = 8.0 * PI * line_density * K_B * temperature / MU_0;
let lhs = current * current;
let ratio = lhs / rhs;
(0.9..=1.1).contains(&ratio)
}
#[must_use]
pub fn grad_shafranov_beta_limit(aspect_ratio: f64) -> f64 {
assert!(aspect_ratio > 0.0, "aspect_ratio must be positive");
1.0 / aspect_ratio
}
#[must_use]
pub fn sweet_parker_rate(alfven_speed: f64, lundquist: f64) -> f64 {
assert!(lundquist > 0.0, "Lundquist number must be positive");
alfven_speed / lundquist.sqrt()
}
#[must_use]
pub fn reconnection_electric_field(b_field: f64, inflow_velocity: f64) -> f64 {
inflow_velocity * b_field
}
#[must_use]
pub fn magnetic_diffusion_time(length: f64, diffusivity: f64) -> f64 {
assert!(diffusivity > 0.0, "diffusivity must be positive");
length * length / diffusivity
}
#[must_use]
pub fn advection_time(length: f64, velocity: f64) -> f64 {
assert!(velocity > 0.0, "velocity must be positive");
length / velocity
}
#[must_use]
pub fn is_frozen_in(reynolds_mag: f64) -> bool {
reynolds_mag > FROZEN_IN_THRESHOLD
}
#[cfg(test)]
mod tests {
use super::*;
fn rel_err(a: f64, b: f64) -> f64 {
(a - b).abs() / b.abs()
}
#[test]
fn test_alfven_speed_solar_corona() {
let va = alfven_speed(1e-4, 1e-12);
assert!(va > 1e4, "Alfvén speed in solar corona should be > 10 km/s, got {va}");
assert!(va < 1e6, "Alfvén speed in solar corona should be < 1000 km/s, got {va}");
assert!(rel_err(va, 89_206.2) < 1e-3);
}
#[test]
fn test_fast_magnetosonic_exceeds_components() {
let va = 500.0;
let cs = 300.0;
let v_fast = fast_magnetosonic_speed(va, cs);
let v_slow = slow_magnetosonic_speed(va, cs);
assert!(v_fast > va, "Fast magnetosonic must exceed Alfvén speed");
assert!(v_fast > cs, "Fast magnetosonic must exceed sound speed");
assert!(v_slow <= va && v_slow <= cs, "Slow magnetosonic must not exceed either component");
assert!(rel_err(v_fast, 583.095_189_484_530_05) < 1e-10);
}
#[test]
fn test_magnetic_pressure_positive() {
for &b in &[1e-6, 1e-3, 1.0, 10.0, 100.0] {
let p = magnetic_pressure(b);
assert!(p > 0.0, "Magnetic pressure must be positive for B = {b}");
}
let p = magnetic_pressure(1.0);
assert!(rel_err(p, 397_887.36) < 1e-3);
}
#[test]
fn test_sweet_parker_rate_decreases_with_lundquist() {
let va = 1e5;
let s1 = 1e4;
let s2 = 1e8;
let rate1 = sweet_parker_rate(va, s1);
let rate2 = sweet_parker_rate(va, s2);
assert!(
rate2 < rate1,
"Sweet-Parker rate must decrease with increasing Lundquist number"
);
assert!(rel_err(rate1, 1000.0) < 1e-12);
}
#[test]
fn test_magnetic_reynolds_scales_with_conductivity() {
let v = 1e3;
let l = 1.0;
let sigma1 = 1e3;
let sigma2 = 1e6;
let rm1 = magnetic_reynolds_number(v, l, sigma1);
let rm2 = magnetic_reynolds_number(v, l, sigma2);
assert!(rm2 > rm1, "Rm must increase with conductivity");
assert!(
rel_err(rm2 / rm1, sigma2 / sigma1) < 1e-10,
"Rm must scale linearly with conductivity"
);
}
#[test]
fn test_plasma_beta() {
let p_gas = 1e5;
let b = 0.01;
let beta = plasma_beta(p_gas, b);
assert!(rel_err(beta, 2513.274_124_24) < 1e-6);
}
#[test]
fn test_total_pressure() {
let p_gas = 1e5;
let b = 0.1;
let p_total = total_pressure(p_gas, b);
assert!(p_total > p_gas, "Total pressure must exceed gas pressure");
assert!(rel_err(p_total, 103_978.873_6) < 1e-3);
}
#[test]
fn test_magnetic_diffusivity() {
let sigma = 1e6;
let eta = magnetic_diffusivity(sigma);
assert!(rel_err(eta, 0.795_774_715_459_477) < 1e-6);
}
#[test]
fn test_hartmann_number() {
let ha = hartmann_number(1.0, 0.1, 1e6, 1e-3);
assert!(rel_err(ha, 3162.277_660_168_38) < 1e-6);
}
#[test]
fn test_lundquist_number() {
let s = lundquist_number(1e5, 1.0, 0.1);
assert!(rel_err(s, 1e6) < 1e-10);
}
#[test]
fn test_frozen_in_condition() {
assert!(is_frozen_in(1e6), "Rm = 1e6 should be frozen-in");
assert!(is_frozen_in(101.0), "Rm = 101 should be frozen-in");
assert!(!is_frozen_in(50.0), "Rm = 50 should NOT be frozen-in");
assert!(!is_frozen_in(100.0), "Rm = 100 (boundary) should NOT be frozen-in");
}
#[test]
fn test_diffusion_and_advection_times() {
let l = 1e6;
let eta = 1.0;
let v = 1e3;
let tau_d = magnetic_diffusion_time(l, eta);
let tau_a = advection_time(l, v);
assert!(rel_err(tau_d, 1e12) < 1e-12);
assert!(rel_err(tau_a, 1e3) < 1e-12);
}
#[test]
fn test_bennett_pinch_condition() {
let n_line = 1e20;
let t = 1e7;
let i_squared = 8.0 * PI * n_line * K_B * t / MU_0;
let current = i_squared.sqrt();
assert!(
bennett_pinch_condition(current, n_line, t),
"Exact Bennett current should satisfy condition"
);
assert!(
!bennett_pinch_condition(current * 2.0, n_line, t),
"Double current should violate condition"
);
}
#[test]
fn test_pinch_pressure_balance() {
let current = 1e6;
let radius = 0.1;
let p = pinch_pressure_balance(current, radius);
assert!(rel_err(p, 1_591_549.430_918_95) < 1e-3);
}
#[test]
fn test_reconnection_electric_field() {
let e = reconnection_electric_field(1e-4, 1e3);
assert!(rel_err(e, 0.1) < 1e-12);
}
#[test]
fn test_magnetosonic_mach() {
let v = 1000.0;
let va = 500.0;
let cs = 300.0;
let m = magnetosonic_mach(v, va, cs);
assert!(rel_err(m, 1.714_985_850_992_862_5) < 1e-6);
}
#[test]
fn test_grad_shafranov_beta_limit() {
let a_r = 3.0;
let beta_max = grad_shafranov_beta_limit(a_r);
assert!(rel_err(beta_max, 1.0 / 3.0) < 1e-12);
}
}