enceladuss 0.0.3

Enceladus celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
use sciforge::hub::prelude::constants::elements::atomic_mass;

pub struct PlumeSpecies {
    pub name: &'static str,
    pub symbol: &'static str,
    pub molar_mass_kg_mol: f64,
    pub mass_fraction: f64,
}

pub struct CryovolcanicEndpoint {
    pub body_radius_m: f64,
    pub plume_height_m: f64,
    pub ejection_velocity_m_s: f64,
    pub total_mass_rate_kg_s: f64,
    pub vent_temperature_k: f64,
    pub species: Vec<PlumeSpecies>,
}

pub struct TigerStripeEndpoint {
    pub stripe_count: u32,
    pub avg_length_m: f64,
    pub avg_width_m: f64,
    pub surface_temperature_k: f64,
    pub vent_temperature_k: f64,
    pub thermal_output_gw: f64,
}

fn h2o_molar() -> f64 {
    (2.0 * atomic_mass(1) + atomic_mass(8)) * 1e-3
}
fn co2_molar() -> f64 {
    (atomic_mass(6) + 2.0 * atomic_mass(8)) * 1e-3
}
fn ch4_molar() -> f64 {
    (atomic_mass(6) + 4.0 * atomic_mass(1)) * 1e-3
}
fn nh3_molar() -> f64 {
    (atomic_mass(7) + 3.0 * atomic_mass(1)) * 1e-3
}
fn h2_molar() -> f64 {
    2.0 * atomic_mass(1) * 1e-3
}
fn n2_molar() -> f64 {
    2.0 * atomic_mass(7) * 1e-3
}
fn nacl_molar() -> f64 {
    (atomic_mass(11) + atomic_mass(17)) * 1e-3
}

impl CryovolcanicEndpoint {
    pub fn enceladus() -> Self {
        Self {
            body_radius_m: crate::ENCELADUS_RADIUS_M,
            plume_height_m: 500_000.0,
            ejection_velocity_m_s: 1_350.0,
            total_mass_rate_kg_s: 200.0,
            vent_temperature_k: 197.0,
            species: vec![
                PlumeSpecies {
                    name: "Water vapor",
                    symbol: "H2O",
                    molar_mass_kg_mol: h2o_molar(),
                    mass_fraction: 0.96,
                },
                PlumeSpecies {
                    name: "Carbon dioxide",
                    symbol: "CO2",
                    molar_mass_kg_mol: co2_molar(),
                    mass_fraction: 0.033,
                },
                PlumeSpecies {
                    name: "Methane",
                    symbol: "CH4",
                    molar_mass_kg_mol: ch4_molar(),
                    mass_fraction: 0.002,
                },
                PlumeSpecies {
                    name: "Ammonia",
                    symbol: "NH3",
                    molar_mass_kg_mol: nh3_molar(),
                    mass_fraction: 0.001,
                },
                PlumeSpecies {
                    name: "Molecular hydrogen",
                    symbol: "H2",
                    molar_mass_kg_mol: h2_molar(),
                    mass_fraction: 0.001,
                },
                PlumeSpecies {
                    name: "Dinitrogen",
                    symbol: "N2",
                    molar_mass_kg_mol: n2_molar(),
                    mass_fraction: 0.002,
                },
                PlumeSpecies {
                    name: "Sodium chloride",
                    symbol: "NaCl",
                    molar_mass_kg_mol: nacl_molar(),
                    mass_fraction: 0.001,
                },
            ],
        }
    }

    pub fn plume_density_at_height(&self, height_m: f64) -> f64 {
        if height_m > self.plume_height_m {
            return 0.0;
        }
        let frac = height_m / self.plume_height_m;
        self.total_mass_rate_kg_s * (1.0 - frac).powi(2)
    }

    pub fn water_mass_rate_kg_s(&self) -> f64 {
        self.species
            .iter()
            .find(|s| s.symbol == "H2O")
            .map(|s| self.total_mass_rate_kg_s * s.mass_fraction)
            .unwrap_or(0.0)
    }

    pub fn particle_escape_fraction(&self) -> f64 {
        let escape_v = crate::ESCAPE_VELOCITY_M_S;
        if self.ejection_velocity_m_s >= escape_v {
            1.0
        } else {
            (self.ejection_velocity_m_s / escape_v).powi(2)
        }
    }
}

impl TigerStripeEndpoint {
    pub fn south_polar() -> Self {
        Self {
            stripe_count: 4,
            avg_length_m: 130_000.0,
            avg_width_m: 2_000.0,
            surface_temperature_k: 85.0,
            vent_temperature_k: 197.0,
            thermal_output_gw: 15.8,
        }
    }

    pub fn total_active_area_m2(&self) -> f64 {
        self.stripe_count as f64 * self.avg_length_m * self.avg_width_m
    }

    pub fn thermal_flux_w_m2(&self) -> f64 {
        self.thermal_output_gw * 1e9 / self.total_active_area_m2()
    }

    pub fn temperature_contrast_k(&self) -> f64 {
        self.vent_temperature_k - self.surface_temperature_k
    }
}