suns 0.0.1

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

use crate::{
    ELECTRON_CHARGE, ELECTRON_MASS, MU_0, PROTON_MASS, SOLAR_CYCLE_YEARS,
    SOLAR_RADIUS, SOLAR_ROTATION_EQUATORIAL_DAYS, SOLAR_ROTATION_POLAR_DAYS, SECONDS_PER_DAY,
};

pub const QUIET_SUN_FIELD_T: f64 = 1e-4;
pub const ACTIVE_REGION_FIELD_T: f64 = 0.1;
pub const SUNSPOT_FIELD_T: f64 = 0.3;
pub const CORE_FIELD_ESTIMATE_T: f64 = 30.0;
pub const TACHOCLINE_FIELD_T: f64 = 10.0;
pub const MAGNETIC_REYNOLDS_NUMBER: f64 = 1e10;
pub const PHOTOSPHERIC_DIFFUSIVITY: f64 = 1e3;

pub struct MagneticFieldRegion {
    pub name: &'static str,
    pub field_strength_t: f64,
    pub temperature_k: f64,
    pub density_kgm3: f64,
    pub scale_length_m: f64,
}

impl MagneticFieldRegion {
    pub fn magnetic_pressure(&self) -> f64 {
        self.field_strength_t.powi(2) / (2.0 * MU_0)
    }

    pub fn thermal_pressure(&self) -> f64 {
        let n = self.density_kgm3 / PROTON_MASS;
        n * K_B * self.temperature_k
    }

    pub fn plasma_beta(&self) -> f64 {
        self.thermal_pressure() / self.magnetic_pressure().max(1e-30)
    }

    pub fn alfven_speed(&self) -> f64 {
        self.field_strength_t / (MU_0 * self.density_kgm3).sqrt()
    }

    pub fn magnetic_energy_density(&self) -> f64 {
        self.field_strength_t.powi(2) / (2.0 * MU_0)
    }

    pub fn magnetic_diffusion_time(&self) -> f64 {
        MU_0 * self.scale_length_m.powi(2) / PHOTOSPHERIC_DIFFUSIVITY
    }

    pub fn magnetic_reynolds_at_scale(&self, velocity: f64) -> f64 {
        velocity * self.scale_length_m / PHOTOSPHERIC_DIFFUSIVITY
    }
}

pub fn quiet_sun() -> MagneticFieldRegion {
    MagneticFieldRegion {
        name: "Quiet Sun",
        field_strength_t: QUIET_SUN_FIELD_T,
        temperature_k: 5778.0,
        density_kgm3: 2e-4,
        scale_length_m: 1e6,
    }
}

pub fn active_region() -> MagneticFieldRegion {
    MagneticFieldRegion {
        name: "Active Region",
        field_strength_t: ACTIVE_REGION_FIELD_T,
        temperature_k: 5000.0,
        density_kgm3: 3e-4,
        scale_length_m: 3e7,
    }
}

pub fn sunspot_umbra() -> MagneticFieldRegion {
    MagneticFieldRegion {
        name: "Sunspot Umbra",
        field_strength_t: SUNSPOT_FIELD_T,
        temperature_k: 3700.0,
        density_kgm3: 5e-4,
        scale_length_m: 1e7,
    }
}

pub fn coronal_field() -> MagneticFieldRegion {
    MagneticFieldRegion {
        name: "Corona",
        field_strength_t: 1e-4,
        temperature_k: 1e6,
        density_kgm3: 1e-12,
        scale_length_m: 1e8,
    }
}

pub fn differential_rotation_rate(latitude_deg: f64) -> f64 {
    let a = 14.713;
    let b = -2.396;
    let c = -1.787;
    let sin_lat = latitude_deg.to_radians().sin();
    let sin2 = sin_lat * sin_lat;
    let sin4 = sin2 * sin2;
    (a + b * sin2 + c * sin4) * std::f64::consts::PI / 180.0 / SECONDS_PER_DAY
}

pub fn differential_rotation_period_days(latitude_deg: f64) -> f64 {
    let omega = differential_rotation_rate(latitude_deg);
    2.0 * std::f64::consts::PI / (omega * SECONDS_PER_DAY)
}

pub fn omega_effect_shear() -> f64 {
    let omega_eq = 2.0 * std::f64::consts::PI / (SOLAR_ROTATION_EQUATORIAL_DAYS * SECONDS_PER_DAY);
    let omega_pole =
        2.0 * std::f64::consts::PI / (SOLAR_ROTATION_POLAR_DAYS * SECONDS_PER_DAY);
    (omega_eq - omega_pole).abs()
}

pub fn dynamo_number(alpha: f64, shear: f64, diffusivity: f64, length: f64) -> f64 {
    alpha * shear * length.powi(3) / (diffusivity * diffusivity)
}

pub fn solar_dynamo_number_estimate() -> f64 {
    let alpha = 1.0;
    let shear = omega_effect_shear();
    let eta = PHOTOSPHERIC_DIFFUSIVITY;
    let l = 0.3 * SOLAR_RADIUS;
    dynamo_number(alpha, shear, eta, l)
}

pub fn magnetic_flux(field_t: f64, area_m2: f64) -> f64 {
    field_t * area_m2
}

pub fn magnetic_energy(field_t: f64, volume_m3: f64) -> f64 {
    field_t.powi(2) * volume_m3 / (2.0 * MU_0)
}

pub fn larmor_radius(velocity: f64, mass: f64, charge: f64, field_t: f64) -> f64 {
    mass * velocity / (charge.abs() * field_t)
}

pub fn proton_larmor_radius(temperature_k: f64, field_t: f64) -> f64 {
    let v_thermal = (2.0 * K_B * temperature_k / PROTON_MASS).sqrt();
    larmor_radius(v_thermal, PROTON_MASS, ELECTRON_CHARGE, field_t)
}

pub fn electron_larmor_radius(temperature_k: f64, field_t: f64) -> f64 {
    let v_thermal = (2.0 * K_B * temperature_k / ELECTRON_MASS).sqrt();
    larmor_radius(v_thermal, ELECTRON_MASS, ELECTRON_CHARGE, field_t)
}

pub fn cyclotron_frequency(charge: f64, mass: f64, field_t: f64) -> f64 {
    charge.abs() * field_t / mass
}

pub fn proton_cyclotron_frequency(field_t: f64) -> f64 {
    cyclotron_frequency(ELECTRON_CHARGE, PROTON_MASS, field_t)
}

pub fn electron_cyclotron_frequency(field_t: f64) -> f64 {
    cyclotron_frequency(ELECTRON_CHARGE, ELECTRON_MASS, field_t)
}

pub fn sweet_parker_reconnection_rate(alfven_speed: f64, lundquist_number: f64) -> f64 {
    alfven_speed / lundquist_number.sqrt()
}

pub fn magnetic_helicity(field_t: f64, length_m: f64) -> f64 {
    field_t * field_t * length_m.powi(3)
}

pub fn force_free_parameter(current_density: f64, field_t: f64) -> f64 {
    MU_0 * current_density / field_t.max(1e-30)
}

pub fn hale_cycle_period() -> f64 {
    2.0 * SOLAR_CYCLE_YEARS
}