suns 0.0.3

Sun celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
use sciforge::hub::domain::common::constants::{AU, K_B};

use crate::plasma::solar_wind::{PARKER_SPIRAL_OMEGA, SolarWindState, WIND_MAGNETIC_FIELD_1AU};
use crate::{MU_0, PROTON_MASS};

pub const EARTH_DIPOLE_MOMENT: f64 = 8.22e22;
pub const JUPITER_DIPOLE_MOMENT: f64 = 1.56e27;
pub const SATURN_DIPOLE_MOMENT: f64 = 4.6e25;
pub const MERCURY_DIPOLE_MOMENT: f64 = 4.0e19;
pub const URANUS_DIPOLE_MOMENT: f64 = 3.9e24;
pub const NEPTUNE_DIPOLE_MOMENT: f64 = 2.2e24;
pub const GANYMEDE_DIPOLE_MOMENT: f64 = 1.3e20;

pub const EARTH_MAGNETOPAUSE_R: f64 = 10.0;
pub const JUPITER_MAGNETOPAUSE_R: f64 = 75.0;

pub struct SolarWindInteraction {
    pub planet_dipole_moment: f64,
    pub planet_radius_m: f64,
    pub distance_au: f64,
}

impl SolarWindInteraction {
    pub fn new(planet_dipole_moment: f64, planet_radius_m: f64, distance_au: f64) -> Self {
        Self {
            planet_dipole_moment,
            planet_radius_m,
            distance_au,
        }
    }

    pub fn earth() -> Self {
        Self::new(EARTH_DIPOLE_MOMENT, 6.371e6, 1.0)
    }

    pub fn jupiter() -> Self {
        Self::new(JUPITER_DIPOLE_MOMENT, 7.1492e7, 5.2)
    }

    pub fn mercury() -> Self {
        Self::new(MERCURY_DIPOLE_MOMENT, 2.4397e6, 0.387)
    }

    pub fn wind_state(&self) -> SolarWindState {
        SolarWindState::slow_wind_1au().at_distance(self.distance_au)
    }

    pub fn ram_pressure(&self) -> f64 {
        self.wind_state().dynamic_pressure()
    }

    pub fn magnetopause_distance(&self) -> f64 {
        let p_dyn = self.ram_pressure();
        (self.planet_dipole_moment.powi(2) * MU_0 / (8.0 * std::f64::consts::PI.powi(2) * p_dyn))
            .powf(1.0 / 6.0)
    }

    pub fn magnetopause_distance_in_radii(&self) -> f64 {
        self.magnetopause_distance() / self.planet_radius_m
    }

    pub fn bow_shock_distance(&self) -> f64 {
        1.3 * self.magnetopause_distance()
    }

    pub fn bow_shock_distance_in_radii(&self) -> f64 {
        self.bow_shock_distance() / self.planet_radius_m
    }

    pub fn magnetic_pressure_at_magnetopause(&self) -> f64 {
        let r_mp = self.magnetopause_distance();
        let b = self.planet_dipole_moment * MU_0 / (4.0 * std::f64::consts::PI * r_mp.powi(3));
        b * b / (2.0 * MU_0)
    }

    pub fn magnetosphere_cross_section(&self) -> f64 {
        let r_mp = self.magnetopause_distance();
        std::f64::consts::PI * r_mp * r_mp
    }

    pub fn intercepted_power(&self) -> f64 {
        let wind = self.wind_state();
        let ke_flux = wind.kinetic_energy_flux();
        ke_flux * self.magnetosphere_cross_section()
    }

    pub fn reconnection_rate(&self) -> f64 {
        let wind = self.wind_state();
        let v_a = wind.magnetic_field_t / (MU_0 * wind.density_m3 * PROTON_MASS).sqrt();
        0.1 * v_a
    }

    pub fn polar_cap_potential(&self) -> f64 {
        let wind = self.wind_state();
        let r_mp = self.magnetopause_distance();
        wind.speed_ms * wind.magnetic_field_t * 2.0 * r_mp * 0.1
    }

    pub fn imf_strength(&self) -> f64 {
        WIND_MAGNETIC_FIELD_1AU / self.distance_au
    }

    pub fn parker_spiral_angle_deg(&self) -> f64 {
        let r_m = self.distance_au * AU;
        let wind = self.wind_state();
        (PARKER_SPIRAL_OMEGA * r_m / wind.speed_ms)
            .atan()
            .to_degrees()
    }
}

pub fn magnetopause_standoff(dipole_moment: f64, dynamic_pressure: f64) -> f64 {
    (dipole_moment.powi(2) * MU_0 / (8.0 * std::f64::consts::PI.powi(2) * dynamic_pressure))
        .powf(1.0 / 6.0)
}

pub fn solar_wind_ram_pressure_at(distance_au: f64) -> f64 {
    SolarWindState::slow_wind_1au()
        .at_distance(distance_au)
        .dynamic_pressure()
}

pub fn has_magnetosphere(dipole_moment: f64, planet_radius_m: f64, distance_au: f64) -> bool {
    let p_dyn = solar_wind_ram_pressure_at(distance_au);
    let r_mp = magnetopause_standoff(dipole_moment, p_dyn);
    r_mp > planet_radius_m
}

pub fn atmospheric_ion_escape_rate(
    exosphere_temp_k: f64,
    particle_mass_kg: f64,
    planet_radius_m: f64,
    escape_velocity_ms: f64,
    distance_au: f64,
) -> f64 {
    let v_th = (2.0 * K_B * exosphere_temp_k / particle_mass_kg).sqrt();
    let lambda = (escape_velocity_ms / v_th).powi(2);
    let jeans = (1.0 + lambda) * (-lambda).exp();
    let wind = SolarWindState::slow_wind_1au().at_distance(distance_au);
    let sputtering_factor = wind.density_m3 * wind.speed_ms / 1e10;
    let area = 4.0 * std::f64::consts::PI * planet_radius_m * planet_radius_m;
    let n_exo = 1e12;
    n_exo * v_th * area * (jeans + sputtering_factor)
}

pub fn energy_input_to_magnetosphere(dipole_moment: f64, distance_au: f64) -> f64 {
    let wind = SolarWindState::slow_wind_1au().at_distance(distance_au);
    let r_mp = magnetopause_standoff(dipole_moment, wind.dynamic_pressure());
    let area = std::f64::consts::PI * r_mp * r_mp;
    let epsilon = 0.1;
    epsilon * wind.kinetic_energy_flux() * area
}