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
}
}