use sciforge::hub::domain::common::constants::{AU, G, K_B, SOLAR_MASS};
use crate::{ELECTRON_CHARGE, ELECTRON_MASS, EPSILON_0, MU_0, PROTON_MASS, SOLAR_RADIUS};
pub const SLOW_WIND_SPEED: f64 = 4.0e5;
pub const FAST_WIND_SPEED: f64 = 8.0e5;
pub const WIND_DENSITY_1AU: f64 = 7e6;
pub const WIND_TEMPERATURE_1AU: f64 = 1e5;
pub const WIND_MAGNETIC_FIELD_1AU: f64 = 5e-9;
pub const PARKER_SPIRAL_OMEGA: f64 = 2.87e-6;
pub const SOLAR_WIND_MASS_LOSS_RATE: f64 = 2e-14;
pub const HELIOSPHERIC_CURRENT_SHEET_TILT_DEG: f64 = 7.25;
pub const TERMINATION_SHOCK_AU: f64 = 85.0;
pub const HELIOPAUSE_AU: f64 = 120.0;
pub struct SolarWindState {
pub speed_ms: f64,
pub density_m3: f64,
pub temperature_k: f64,
pub magnetic_field_t: f64,
pub distance_au: f64,
}
impl SolarWindState {
pub fn slow_wind_1au() -> Self {
Self {
speed_ms: SLOW_WIND_SPEED,
density_m3: WIND_DENSITY_1AU,
temperature_k: WIND_TEMPERATURE_1AU,
magnetic_field_t: WIND_MAGNETIC_FIELD_1AU,
distance_au: 1.0,
}
}
pub fn fast_wind_1au() -> Self {
Self {
speed_ms: FAST_WIND_SPEED,
density_m3: 3e6,
temperature_k: 2e5,
magnetic_field_t: WIND_MAGNETIC_FIELD_1AU,
distance_au: 1.0,
}
}
pub fn at_distance(&self, distance_au: f64) -> Self {
let r_ratio = self.distance_au / distance_au;
Self {
speed_ms: self.speed_ms,
density_m3: self.density_m3 * r_ratio * r_ratio,
temperature_k: self.temperature_k * r_ratio.powf(0.7),
magnetic_field_t: self.magnetic_field_t * r_ratio,
distance_au,
}
}
pub fn dynamic_pressure(&self) -> f64 {
0.5 * self.density_m3 * PROTON_MASS * self.speed_ms * self.speed_ms
}
pub fn ram_pressure(&self) -> f64 {
self.dynamic_pressure()
}
pub fn thermal_pressure(&self) -> f64 {
self.density_m3 * K_B * self.temperature_k
}
pub fn magnetic_pressure(&self) -> f64 {
self.magnetic_field_t.powi(2) / (2.0 * MU_0)
}
pub fn total_pressure(&self) -> f64 {
self.dynamic_pressure() + self.thermal_pressure() + self.magnetic_pressure()
}
pub fn kinetic_energy_flux(&self) -> f64 {
0.5 * self.density_m3 * PROTON_MASS * self.speed_ms.powi(3)
}
pub fn mass_flux(&self) -> f64 {
self.density_m3 * PROTON_MASS * self.speed_ms
}
pub fn alfven_mach_number(&self) -> f64 {
let va = self.magnetic_field_t / (MU_0 * self.density_m3 * PROTON_MASS).sqrt();
self.speed_ms / va
}
pub fn sonic_mach_number(&self) -> f64 {
let cs = (5.0 / 3.0 * K_B * self.temperature_k / PROTON_MASS).sqrt();
self.speed_ms / cs
}
pub fn parker_spiral_angle_deg(&self) -> f64 {
let r = self.distance_au * AU;
let angle = (PARKER_SPIRAL_OMEGA * r / self.speed_ms).atan();
angle.to_degrees()
}
pub fn proton_gyroradius(&self) -> f64 {
let v_th = (2.0 * K_B * self.temperature_k / PROTON_MASS).sqrt();
PROTON_MASS * v_th / (ELECTRON_CHARGE * self.magnetic_field_t)
}
pub fn debye_length(&self) -> f64 {
(EPSILON_0 * K_B * self.temperature_k
/ (self.density_m3 * ELECTRON_CHARGE * ELECTRON_CHARGE))
.sqrt()
}
pub fn plasma_frequency(&self) -> f64 {
(self.density_m3 * ELECTRON_CHARGE * ELECTRON_CHARGE / (EPSILON_0 * ELECTRON_MASS)).sqrt()
}
pub fn travel_time_to_au(&self, target_au: f64) -> f64 {
let distance = (target_au - self.distance_au).abs() * AU;
distance / self.speed_ms
}
}
pub fn parker_critical_radius() -> f64 {
let mu = 0.6;
let cs = (K_B * 1e6 / (mu * PROTON_MASS)).sqrt();
G * SOLAR_MASS / (2.0 * cs * cs)
}
pub fn parker_solution_speed(r_m: f64) -> f64 {
let rc = parker_critical_radius();
let cs = (K_B * 1e6 / (0.6 * PROTON_MASS)).sqrt();
let ratio = r_m / rc;
if ratio <= 1.0 {
cs * (ratio).powf(0.5)
} else {
cs * (1.0 + 2.0 * (ratio.ln())).sqrt()
}
}
pub fn solar_wind_mass_loss_rate() -> f64 {
let r_1au = AU;
let area = 4.0 * std::f64::consts::PI * r_1au * r_1au;
WIND_DENSITY_1AU * PROTON_MASS * SLOW_WIND_SPEED * area
}
pub fn alfven_radius() -> f64 {
15.0 * SOLAR_RADIUS
}
pub fn heliospheric_magnetic_field_at(distance_au: f64) -> f64 {
WIND_MAGNETIC_FIELD_1AU / distance_au
}
pub fn bow_shock_standoff(planet_dipole_moment: f64, distance_au: f64) -> f64 {
let wind = SolarWindState::slow_wind_1au().at_distance(distance_au);
let p_dyn = wind.dynamic_pressure();
(planet_dipole_moment / (4.0 * std::f64::consts::PI * p_dyn / MU_0)).powf(1.0 / 6.0)
}
pub fn interplanetary_shock_speed(upstream_speed: f64, compression_ratio: f64) -> f64 {
upstream_speed * compression_ratio / (compression_ratio - 1.0)
}
pub fn corotation_radius() -> f64 {
let omega = PARKER_SPIRAL_OMEGA;
let v = SLOW_WIND_SPEED;
v / omega
}