use sciforge::hub::domain::common::constants::AU;
use crate::{
CME_MASS_RANGE_HIGH, CME_MASS_RANGE_LOW, CME_SPEED_FAST, CME_SPEED_SLOW, MU_0,
};
pub const CME_RATE_SOLAR_MIN_PER_DAY: f64 = 0.5;
pub const CME_RATE_SOLAR_MAX_PER_DAY: f64 = 4.0;
pub const CME_ANGULAR_WIDTH_DEG: f64 = 60.0;
pub const HALO_CME_FRACTION: f64 = 0.04;
pub const CME_MAGNETIC_CLOUD_FIELD_T: f64 = 2e-8;
pub const CME_SHEATH_COMPRESSION: f64 = 4.0;
pub const ICME_DURATION_HOURS: f64 = 24.0;
pub const DST_STORM_THRESHOLD_NT: f64 = -50.0;
pub struct CoronalMassEjection {
pub speed_ms: f64,
pub mass_kg: f64,
pub angular_width_deg: f64,
pub magnetic_field_t: f64,
pub source_latitude_deg: f64,
}
impl CoronalMassEjection {
pub fn slow() -> Self {
Self {
speed_ms: CME_SPEED_SLOW,
mass_kg: CME_MASS_RANGE_LOW,
angular_width_deg: 40.0,
magnetic_field_t: 1e-8,
source_latitude_deg: 30.0,
}
}
pub fn fast() -> Self {
Self {
speed_ms: CME_SPEED_FAST,
mass_kg: CME_MASS_RANGE_HIGH,
angular_width_deg: 90.0,
magnetic_field_t: 5e-8,
source_latitude_deg: 15.0,
}
}
pub fn halo() -> Self {
Self {
speed_ms: 2.0e6,
mass_kg: 5e12,
angular_width_deg: 360.0,
magnetic_field_t: 3e-8,
source_latitude_deg: 10.0,
}
}
pub fn kinetic_energy(&self) -> f64 {
0.5 * self.mass_kg * self.speed_ms * self.speed_ms
}
pub fn magnetic_energy(&self) -> f64 {
let volume = self.volume_at_1au();
self.magnetic_field_t.powi(2) * volume / (2.0 * MU_0)
}
pub fn total_energy(&self) -> f64 {
self.kinetic_energy() + self.magnetic_energy()
}
pub fn travel_time_to_earth(&self) -> f64 {
AU / self.speed_ms
}
pub fn travel_time_to_au(&self, distance_au: f64) -> f64 {
distance_au * AU / self.speed_ms
}
pub fn momentum(&self) -> f64 {
self.mass_kg * self.speed_ms
}
pub fn solid_angle_sr(&self) -> f64 {
2.0 * std::f64::consts::PI
* (1.0 - (self.angular_width_deg / 2.0).to_radians().cos())
}
pub fn volume_at_1au(&self) -> f64 {
let r = AU;
self.solid_angle_sr() * r.powi(3) / 3.0
}
pub fn density_at_1au(&self) -> f64 {
self.mass_kg / self.volume_at_1au()
}
pub fn dynamic_pressure_at_1au(&self) -> f64 {
0.5 * self.density_at_1au() * self.speed_ms * self.speed_ms
}
pub fn alfven_mach_number(&self) -> f64 {
let rho = self.density_at_1au();
let va = self.magnetic_field_t / (MU_0 * rho).sqrt();
self.speed_ms / va
}
pub fn is_supercritical(&self) -> bool {
self.alfven_mach_number() > 1.0
}
pub fn sheath_thickness_m(&self) -> f64 {
AU * 0.01
}
pub fn magnetic_cloud_radius_m(&self) -> f64 {
0.1 * AU
}
pub fn dst_index_estimate(&self) -> f64 {
let v = self.speed_ms / 1e3;
let bz = self.magnetic_field_t * 1e9;
-0.01 * v * bz
}
pub fn is_geoeffective(&self) -> bool {
self.dst_index_estimate() < DST_STORM_THRESHOLD_NT
}
pub fn forbush_decrease_percent(&self) -> f64 {
let e = self.kinetic_energy();
(e / 1e24).min(20.0)
}
}
pub fn cme_rate_at_cycle_phase(phase: f64) -> f64 {
let min = CME_RATE_SOLAR_MIN_PER_DAY;
let max = CME_RATE_SOLAR_MAX_PER_DAY;
min + (max - min) * (std::f64::consts::PI * phase).sin().powi(2)
}
pub fn snow_plow_speed(cme_mass: f64, cme_speed: f64, ambient_density: f64, distance: f64) -> f64 {
let swept_mass = ambient_density * 4.0 * std::f64::consts::PI * distance.powi(3) / 3.0;
cme_mass * cme_speed / (cme_mass + swept_mass)
}
pub fn drag_based_cme_speed(
initial_speed: f64,
solar_wind_speed: f64,
drag_coeff: f64,
time_s: f64,
) -> f64 {
let dv = initial_speed - solar_wind_speed;
solar_wind_speed + dv * (-drag_coeff * time_s).exp()
}
pub fn cme_arrival_probability(angular_width_deg: f64) -> f64 {
(angular_width_deg / 360.0).min(1.0)
}
pub fn interplanetary_shock_compression(mach: f64) -> f64 {
let gamma = 5.0 / 3.0;
(gamma + 1.0) * mach * mach / ((gamma - 1.0) * mach * mach + 2.0)
}