use sciforge::hub::prelude::constants::elements::atomic_mass;
pub struct ExosphereSpecies {
pub name: &'static str,
pub symbol: &'static str,
pub molar_mass_kg_mol: f64,
pub column_density_m2: f64,
pub scale_height_m: f64,
}
pub struct VolcanicPlumeEndpoint {
pub plume_height_m: f64,
pub ejection_velocity_m_s: f64,
pub so2_mass_rate_kg_s: f64,
pub temperature_k: f64,
pub optical_depth_at_vent: f64,
}
pub struct ExosphereEndpoint {
pub body_radius_m: f64,
pub species: Vec<ExosphereSpecies>,
}
fn so2_molar() -> f64 {
(atomic_mass(16) + 2.0 * atomic_mass(8)) * 1e-3
}
fn so_molar() -> f64 {
(atomic_mass(16) + atomic_mass(8)) * 1e-3
}
fn s_molar() -> f64 {
atomic_mass(16) * 1e-3
}
fn o_molar() -> f64 {
atomic_mass(8) * 1e-3
}
fn na_molar() -> f64 {
atomic_mass(11) * 1e-3
}
fn k_molar() -> f64 {
atomic_mass(19) * 1e-3
}
fn cl_molar() -> f64 {
atomic_mass(17) * 1e-3
}
fn nacl_molar() -> f64 {
(atomic_mass(11) + atomic_mass(17)) * 1e-3
}
impl ExosphereEndpoint {
pub fn io() -> Self {
Self {
body_radius_m: crate::IO_RADIUS_M,
species: vec![
ExosphereSpecies {
name: "Sulfur dioxide",
symbol: "SO2",
molar_mass_kg_mol: so2_molar(),
column_density_m2: 1e20,
scale_height_m: 12_000.0,
},
ExosphereSpecies {
name: "Sulfur monoxide",
symbol: "SO",
molar_mass_kg_mol: so_molar(),
column_density_m2: 1e18,
scale_height_m: 15_000.0,
},
ExosphereSpecies {
name: "Atomic sulfur",
symbol: "S",
molar_mass_kg_mol: s_molar(),
column_density_m2: 5e17,
scale_height_m: 40_000.0,
},
ExosphereSpecies {
name: "Atomic oxygen",
symbol: "O",
molar_mass_kg_mol: o_molar(),
column_density_m2: 1e18,
scale_height_m: 50_000.0,
},
ExosphereSpecies {
name: "Sodium",
symbol: "Na",
molar_mass_kg_mol: na_molar(),
column_density_m2: 1e14,
scale_height_m: 100_000.0,
},
ExosphereSpecies {
name: "Potassium",
symbol: "K",
molar_mass_kg_mol: k_molar(),
column_density_m2: 1e12,
scale_height_m: 80_000.0,
},
ExosphereSpecies {
name: "Chlorine",
symbol: "Cl",
molar_mass_kg_mol: cl_molar(),
column_density_m2: 5e13,
scale_height_m: 30_000.0,
},
ExosphereSpecies {
name: "Sodium chloride",
symbol: "NaCl",
molar_mass_kg_mol: nacl_molar(),
column_density_m2: 1e13,
scale_height_m: 10_000.0,
},
],
}
}
pub fn density_at_altitude(&self, species_symbol: &str, altitude_m: f64) -> f64 {
self.species
.iter()
.find(|s| s.symbol == species_symbol)
.map(|s| {
let n_surface = s.column_density_m2 / s.scale_height_m;
n_surface * (-altitude_m / s.scale_height_m).exp()
})
.unwrap_or(0.0)
}
pub fn total_column_density(&self) -> f64 {
self.species.iter().map(|s| s.column_density_m2).sum()
}
pub fn sodium_glow_intensity(&self) -> f64 {
self.species
.iter()
.find(|s| s.symbol == "Na")
.map(|s| s.column_density_m2 * 1e-14)
.unwrap_or(0.0)
}
}
impl VolcanicPlumeEndpoint {
pub fn pele_type() -> Self {
Self {
plume_height_m: 300_000.0,
ejection_velocity_m_s: 1_000.0,
so2_mass_rate_kg_s: 1e4,
temperature_k: 1_500.0,
optical_depth_at_vent: 5.0,
}
}
pub fn prometheus_type() -> Self {
Self {
plume_height_m: 75_000.0,
ejection_velocity_m_s: 500.0,
so2_mass_rate_kg_s: 1e2,
temperature_k: 450.0,
optical_depth_at_vent: 1.5,
}
}
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.optical_depth_at_vent * (1.0 - frac).powi(2)
}
pub fn thermal_emission_w_m2(&self) -> f64 {
5.670374419e-8 * self.temperature_k.powi(4)
}
}