use sciforge::hub::domain::common::constants::K_B;
use crate::{FLARE_CLASS_C_FLUX, FLARE_CLASS_M_FLUX, FLARE_CLASS_X_FLUX, MU_0};
pub const FLARE_DURATION_IMPULSIVE_S: f64 = 60.0;
pub const FLARE_DURATION_GRADUAL_S: f64 = 3600.0;
pub const FLARE_RISE_TIME_S: f64 = 300.0;
pub const FLARE_RECONNECTION_RATE: f64 = 0.1;
pub const NONTHERMAL_ELECTRON_FRACTION: f64 = 0.1;
pub const FLARE_LOOP_LENGTH_M: f64 = 5e7;
pub const FLARE_LOOP_FOOTPOINT_AREA_M2: f64 = 1e14;
pub struct SolarFlare {
pub classification: FlareClass,
pub peak_flux_wm2: f64,
pub duration_s: f64,
pub loop_length_m: f64,
pub magnetic_field_t: f64,
pub area_m2: f64,
}
#[derive(Clone, Copy, PartialEq)]
pub enum FlareClass {
A,
B,
C,
M,
X,
}
impl FlareClass {
pub fn base_flux(&self) -> f64 {
match self {
FlareClass::A => 1e-8,
FlareClass::B => 1e-7,
FlareClass::C => FLARE_CLASS_C_FLUX,
FlareClass::M => FLARE_CLASS_M_FLUX,
FlareClass::X => FLARE_CLASS_X_FLUX,
}
}
pub fn label(&self) -> &'static str {
match self {
FlareClass::A => "A",
FlareClass::B => "B",
FlareClass::C => "C",
FlareClass::M => "M",
FlareClass::X => "X",
}
}
}
impl SolarFlare {
pub fn from_class(class: FlareClass, multiplier: f64) -> Self {
let base = class.base_flux();
let peak = base * multiplier;
let (duration, b, area) = match class {
FlareClass::A | FlareClass::B => (300.0, 0.01, 1e13),
FlareClass::C => (600.0, 0.05, 5e13),
FlareClass::M => (1800.0, 0.1, 1e14),
FlareClass::X => (3600.0, 0.3, 5e14),
};
Self {
classification: class,
peak_flux_wm2: peak,
duration_s: duration,
loop_length_m: FLARE_LOOP_LENGTH_M,
magnetic_field_t: b,
area_m2: area,
}
}
pub fn total_energy_joules(&self) -> f64 {
let volume = self.area_m2 * self.loop_length_m;
self.magnetic_field_t.powi(2) * volume / (2.0 * MU_0)
}
pub fn peak_luminosity(&self) -> f64 {
self.peak_flux_wm2 * 4.0 * std::f64::consts::PI * (1.496e11_f64).powi(2)
}
pub fn peak_temperature(&self) -> f64 {
let e = self.total_energy_joules();
let volume = self.area_m2 * self.loop_length_m;
let n_e = 1e16;
e / (3.0 * n_e * K_B * volume)
}
pub fn reconnection_inflow_speed(&self) -> f64 {
let rho = 1e-11;
let v_a = self.magnetic_field_t / (MU_0 * rho).sqrt();
FLARE_RECONNECTION_RATE * v_a
}
pub fn reconnection_electric_field(&self) -> f64 {
self.reconnection_inflow_speed() * self.magnetic_field_t
}
pub fn nonthermal_electron_energy(&self) -> f64 {
NONTHERMAL_ELECTRON_FRACTION * self.total_energy_joules()
}
pub fn hard_xray_flux_estimate(&self) -> f64 {
0.01 * self.peak_flux_wm2
}
pub fn chromospheric_evaporation_speed(&self) -> f64 {
let energy_flux = self.total_energy_joules() / (self.area_m2 * self.duration_s);
let rho_chromo = 1e-8;
(2.0 * energy_flux / rho_chromo).powf(1.0 / 3.0)
}
pub fn confinement_time(&self) -> f64 {
let n_e = 1e16;
let volume = self.area_m2 * self.loop_length_m;
3.0 * n_e * K_B * self.peak_temperature() * volume / self.peak_luminosity().max(1.0)
}
pub fn neupert_effect_ratio(&self) -> f64 {
self.peak_flux_wm2 * self.duration_s
}
}
pub fn flare_frequency_per_day(class: FlareClass) -> f64 {
match class {
FlareClass::A => 100.0,
FlareClass::B => 30.0,
FlareClass::C => 8.0,
FlareClass::M => 1.0,
FlareClass::X => 0.1,
}
}
pub fn goes_class_from_flux(flux_wm2: f64) -> FlareClass {
if flux_wm2 >= FLARE_CLASS_X_FLUX {
FlareClass::X
} else if flux_wm2 >= FLARE_CLASS_M_FLUX {
FlareClass::M
} else if flux_wm2 >= FLARE_CLASS_C_FLUX {
FlareClass::C
} else if flux_wm2 >= 1e-7 {
FlareClass::B
} else {
FlareClass::A
}
}
pub fn magnetic_energy_available(field_t: f64, volume_m3: f64) -> f64 {
field_t.powi(2) * volume_m3 / (2.0 * MU_0)
}
pub fn flare_ribbon_speed(reconnection_rate: f64, alfven_speed: f64) -> f64 {
reconnection_rate * alfven_speed
}
pub fn white_light_flare_contrast(flare_luminosity: f64, photosphere_flux: f64) -> f64 {
flare_luminosity / (photosphere_flux * FLARE_LOOP_FOOTPOINT_AREA_M2)
}