suns 0.0.3

Sun celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
use crate::{
    FLARE_CLASS_C_FLUX, FLARE_CLASS_M_FLUX, FLARE_CLASS_X_FLUX, SOLAR_LUMINOSITY, SOLAR_RADIUS,
};

pub const FLARE_POWER_LAW_INDEX: f64 = 1.8;
pub const NANOFLARE_ENERGY_J: f64 = 1e17;
pub const MICROFLARE_ENERGY_J: f64 = 1e20;
pub const SMALL_FLARE_ENERGY_J: f64 = 1e23;
pub const LARGE_FLARE_ENERGY_J: f64 = 1e25;
pub const SUPERFLARE_ENERGY_J: f64 = 1e27;
pub const CARRINGTON_EVENT_ENERGY_J: f64 = 5e25;
pub const FLARE_IMPULSIVE_DURATION_S: f64 = 60.0;
pub const FLARE_GRADUAL_DURATION_S: f64 = 3600.0;
pub const SEP_PROTON_ENERGY_MEV: f64 = 100.0;

#[derive(Clone, Copy)]
pub enum FlareCategory {
    Nanoflare,
    Microflare,
    CClass,
    MClass,
    XClass,
    Superflare,
}

impl FlareCategory {
    pub fn typical_energy_j(&self) -> f64 {
        match self {
            FlareCategory::Nanoflare => NANOFLARE_ENERGY_J,
            FlareCategory::Microflare => MICROFLARE_ENERGY_J,
            FlareCategory::CClass => SMALL_FLARE_ENERGY_J,
            FlareCategory::MClass => LARGE_FLARE_ENERGY_J,
            FlareCategory::XClass => 1e26,
            FlareCategory::Superflare => SUPERFLARE_ENERGY_J,
        }
    }

    pub fn frequency_per_day(&self) -> f64 {
        match self {
            FlareCategory::Nanoflare => 1e6,
            FlareCategory::Microflare => 1e4,
            FlareCategory::CClass => 8.0,
            FlareCategory::MClass => 1.0,
            FlareCategory::XClass => 0.1,
            FlareCategory::Superflare => 1e-4,
        }
    }

    pub fn label(&self) -> &'static str {
        match self {
            FlareCategory::Nanoflare => "Nanoflare",
            FlareCategory::Microflare => "Microflare",
            FlareCategory::CClass => "C-class",
            FlareCategory::MClass => "M-class",
            FlareCategory::XClass => "X-class",
            FlareCategory::Superflare => "Superflare",
        }
    }

    pub fn typical_duration_s(&self) -> f64 {
        match self {
            FlareCategory::Nanoflare | FlareCategory::Microflare => 10.0,
            FlareCategory::CClass => 300.0,
            FlareCategory::MClass => 1800.0,
            FlareCategory::XClass => 3600.0,
            FlareCategory::Superflare => 7200.0,
        }
    }
}

pub struct SolarFlareEvent {
    pub category: FlareCategory,
    pub energy_j: f64,
    pub duration_s: f64,
    pub latitude_deg: f64,
    pub longitude_deg: f64,
    pub cycle_phase: f64,
}

impl SolarFlareEvent {
    pub fn peak_luminosity(&self) -> f64 {
        self.energy_j / self.duration_s
    }

    pub fn peak_flux_at_earth(&self) -> f64 {
        let d = 1.496e11;
        self.peak_luminosity() / (4.0 * std::f64::consts::PI * d * d)
    }

    pub fn goes_classification(&self) -> &'static str {
        let flux = self.peak_flux_at_earth();
        if flux >= FLARE_CLASS_X_FLUX {
            "X"
        } else if flux >= FLARE_CLASS_M_FLUX {
            "M"
        } else if flux >= FLARE_CLASS_C_FLUX {
            "C"
        } else if flux >= 1e-7 {
            "B"
        } else {
            "A"
        }
    }

    pub fn sep_flux_protons_cm2(&self) -> f64 {
        match self.category {
            FlareCategory::XClass | FlareCategory::Superflare => 1e4,
            FlareCategory::MClass => 1e2,
            _ => 0.0,
        }
    }

    pub fn radio_burst_expected(&self) -> bool {
        matches!(
            self.category,
            FlareCategory::MClass | FlareCategory::XClass | FlareCategory::Superflare
        )
    }

    pub fn total_radiated_fraction(&self) -> f64 {
        self.energy_j / (SOLAR_LUMINOSITY * self.duration_s)
    }
}

pub fn flare_frequency_distribution(energy_j: f64) -> f64 {
    let a = 1e50;
    a * energy_j.powf(-FLARE_POWER_LAW_INDEX)
}

pub fn nanoflare_coronal_heating_rate() -> f64 {
    let n = 1e6;
    let e = NANOFLARE_ENERGY_J;
    let area = 4.0 * std::f64::consts::PI * SOLAR_RADIUS * SOLAR_RADIUS;
    n * e / (86400.0 * area)
}

pub fn carrington_class_event() -> SolarFlareEvent {
    SolarFlareEvent {
        category: FlareCategory::Superflare,
        energy_j: CARRINGTON_EVENT_ENERGY_J,
        duration_s: 300.0,
        latitude_deg: 20.0,
        longitude_deg: 0.0,
        cycle_phase: 0.5,
    }
}

pub fn bastille_day_flare() -> SolarFlareEvent {
    SolarFlareEvent {
        category: FlareCategory::XClass,
        energy_j: 1e25,
        duration_s: 1200.0,
        latitude_deg: 18.0,
        longitude_deg: -10.0,
        cycle_phase: 0.9,
    }
}

pub fn total_nanoflare_luminosity() -> f64 {
    1e6 * NANOFLARE_ENERGY_J / 86400.0
}

pub fn flare_waiting_time(rate_per_day: f64) -> f64 {
    86400.0 / rate_per_day
}