suns 0.0.1

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

use crate::{
    PROTON_MASS, SOLAR_CORE_DENSITY, SOLAR_CORE_TEMP, SOLAR_EFFECTIVE_TEMP,
    SOLAR_LUMINOSITY, SOLAR_MEAN_DENSITY, SOLAR_RADIUS,
};

pub const CORE_HYDROGEN_FRACTION: f64 = 0.34;
pub const ENVELOPE_HYDROGEN_FRACTION: f64 = 0.7346;
pub const ENVELOPE_HELIUM_FRACTION: f64 = 0.2485;
pub const ENVELOPE_METAL_FRACTION: f64 = 0.0169;
pub const MEAN_MOLECULAR_WEIGHT_CORE: f64 = 0.83;
pub const MEAN_MOLECULAR_WEIGHT_ENVELOPE: f64 = 0.61;
pub const ADIABATIC_INDEX: f64 = 5.0 / 3.0;
pub const EDDINGTON_LUMINOSITY_FRACTION: f64 = 5.1e-5;

pub struct StellarLayer {
    pub name: &'static str,
    pub inner_radius_frac: f64,
    pub outer_radius_frac: f64,
    pub temp_inner_k: f64,
    pub temp_outer_k: f64,
    pub density_inner: f64,
    pub density_outer: f64,
}

impl StellarLayer {
    pub fn mean_temperature(&self) -> f64 {
        (self.temp_inner_k + self.temp_outer_k) / 2.0
    }

    pub fn mean_density(&self) -> f64 {
        (self.density_inner + self.density_outer) / 2.0
    }

    pub fn thickness_m(&self) -> f64 {
        (self.outer_radius_frac - self.inner_radius_frac) * SOLAR_RADIUS
    }

    pub fn volume_m3(&self) -> f64 {
        let r_outer = self.outer_radius_frac * SOLAR_RADIUS;
        let r_inner = self.inner_radius_frac * SOLAR_RADIUS;
        (4.0 / 3.0) * std::f64::consts::PI * (r_outer.powi(3) - r_inner.powi(3))
    }

    pub fn mass_estimate_kg(&self) -> f64 {
        self.mean_density() * self.volume_m3()
    }

    pub fn pressure_at_base(&self) -> f64 {
        let rho = self.mean_density();
        let r = self.inner_radius_frac * SOLAR_RADIUS;
        let m_enclosed = (4.0 / 3.0) * std::f64::consts::PI * r.powi(3) * rho;
        G * m_enclosed * rho / r
    }

    pub fn sound_speed(&self) -> f64 {
        let t = self.mean_temperature();
        let mu = if self.inner_radius_frac < 0.3 {
            MEAN_MOLECULAR_WEIGHT_CORE
        } else {
            MEAN_MOLECULAR_WEIGHT_ENVELOPE
        };
        (ADIABATIC_INDEX * K_B * t / (mu * PROTON_MASS)).sqrt()
    }

    pub fn thermal_scale_height(&self) -> f64 {
        let t = self.mean_temperature();
        let mu = MEAN_MOLECULAR_WEIGHT_ENVELOPE;
        let r = ((self.inner_radius_frac + self.outer_radius_frac) / 2.0) * SOLAR_RADIUS;
        let g_local = G * SOLAR_MASS * (r / SOLAR_RADIUS).powi(3) / (r * r);
        K_B * t / (mu * PROTON_MASS * g_local.max(1e-10))
    }
}

pub fn core() -> StellarLayer {
    StellarLayer {
        name: "Core",
        inner_radius_frac: 0.0,
        outer_radius_frac: 0.25,
        temp_inner_k: SOLAR_CORE_TEMP,
        temp_outer_k: 7.0e6,
        density_inner: SOLAR_CORE_DENSITY,
        density_outer: 2.0e4,
    }
}

pub fn radiative_zone() -> StellarLayer {
    StellarLayer {
        name: "Radiative Zone",
        inner_radius_frac: 0.25,
        outer_radius_frac: 0.70,
        temp_inner_k: 7.0e6,
        temp_outer_k: 2.0e6,
        density_inner: 2.0e4,
        density_outer: 200.0,
    }
}

pub fn convective_zone() -> StellarLayer {
    StellarLayer {
        name: "Convective Zone",
        inner_radius_frac: 0.70,
        outer_radius_frac: 1.0,
        temp_inner_k: 2.0e6,
        temp_outer_k: SOLAR_EFFECTIVE_TEMP,
        density_inner: 200.0,
        density_outer: 2e-4,
    }
}

pub fn hydrostatic_pressure(depth_frac: f64) -> f64 {
    let rho = SOLAR_MEAN_DENSITY;
    let r = (1.0 - depth_frac) * SOLAR_RADIUS;
    let m_enclosed = SOLAR_MASS * (r / SOLAR_RADIUS).powi(3);
    G * m_enclosed * rho / r.max(1.0)
}

pub fn kelvin_helmholtz_timescale() -> f64 {
    G * SOLAR_MASS * SOLAR_MASS / (SOLAR_RADIUS * SOLAR_LUMINOSITY)
}

pub fn thermal_timescale() -> f64 {
    let e_thermal = 1.5 * K_B * SOLAR_CORE_TEMP / PROTON_MASS * SOLAR_MASS * 0.1;
    e_thermal / SOLAR_LUMINOSITY
}

pub fn nuclear_timescale() -> f64 {
    let efficiency = 0.007;
    let core_mass_frac = 0.1;
    let mc = SOLAR_MASS * core_mass_frac;
    efficiency * mc * crate::SPEED_OF_LIGHT * crate::SPEED_OF_LIGHT / SOLAR_LUMINOSITY
}

pub fn dynamical_timescale() -> f64 {
    1.0 / (G * SOLAR_MEAN_DENSITY).sqrt()
}

pub fn eddington_luminosity() -> f64 {
    let sigma_t = 6.6524e-29;
    4.0 * std::f64::consts::PI * G * SOLAR_MASS * crate::SPEED_OF_LIGHT / sigma_t
}

pub fn virial_temperature() -> f64 {
    let mu = MEAN_MOLECULAR_WEIGHT_CORE;
    G * SOLAR_MASS * mu * PROTON_MASS / (3.0 * K_B * SOLAR_RADIUS)
}

pub fn central_pressure_estimate() -> f64 {
    3.0 * G * SOLAR_MASS * SOLAR_MASS / (8.0 * std::f64::consts::PI * SOLAR_RADIUS.powi(4))
}

pub fn luminosity_from_temperature() -> f64 {
    4.0 * std::f64::consts::PI * SOLAR_RADIUS * SOLAR_RADIUS * SIGMA_SB
        * SOLAR_EFFECTIVE_TEMP.powi(4)
}

pub fn gravitational_binding_energy() -> f64 {
    3.0 * G * SOLAR_MASS * SOLAR_MASS / (5.0 * SOLAR_RADIUS)
}

pub fn surface_gravity() -> f64 {
    G * SOLAR_MASS / (SOLAR_RADIUS * SOLAR_RADIUS)
}

pub fn schwarzschild_radius() -> f64 {
    2.0 * G * SOLAR_MASS / (crate::SPEED_OF_LIGHT * crate::SPEED_OF_LIGHT)
}

pub fn roche_limit(body_density: f64) -> f64 {
    SOLAR_RADIUS * (2.0 * SOLAR_MEAN_DENSITY / body_density).powf(1.0 / 3.0)
}

pub fn jeans_mass(temperature: f64, density: f64) -> f64 {
    let cs = (K_B * temperature / (MEAN_MOLECULAR_WEIGHT_ENVELOPE * PROTON_MASS)).sqrt();
    let pi = std::f64::consts::PI;
    (pi / 6.0) * (pi * cs * cs / (G * density)).powf(1.5) * density
}