use suns::plasma::corona::{
CoronalRegion, active_region_corona, coronal_euv_flux, coronal_hole, coronal_loop_density,
coronal_loop_temperature, coronal_streamer, coronal_x_ray_luminosity, density_at_height,
hydrostatic_scale_height, quiet_corona, thompson_scattering_brightness,
type_iii_burst_frequency,
};
use suns::plasma::prominences::{
Prominence, coronal_rain_velocity, drain_rate, magnetic_dip_angle,
prominence_oscillation_period,
};
use suns::plasma::solar_flare::{
FlareClass, SolarFlare, flare_frequency_per_day, flare_ribbon_speed, goes_class_from_flux,
magnetic_energy_available,
};
use suns::plasma::solar_wind::{
SolarWindState, alfven_radius, bow_shock_standoff, corotation_radius,
heliospheric_magnetic_field_at, parker_critical_radius, parker_solution_speed,
solar_wind_mass_loss_rate,
};
#[test]
fn corona_temperatures_above_million_k() {
for r in [quiet_corona(), active_region_corona()] {
assert!(r.temperature_k >= 1e6, "{} T too low", r.name);
}
}
#[test]
fn active_region_hotter_than_quiet() {
assert!(active_region_corona().temperature_k > quiet_corona().temperature_k);
}
#[test]
fn coronal_hole_less_dense() {
assert!(coronal_hole().electron_density_m3 < quiet_corona().electron_density_m3);
}
#[test]
fn streamer_denser_than_quiet() {
assert!(coronal_streamer().electron_density_m3 > quiet_corona().electron_density_m3);
}
#[test]
fn plasma_beta_low_in_corona() {
let beta = quiet_corona().plasma_beta();
assert!(beta < 10.0, "coronal β={beta} too high");
}
#[test]
fn alfven_speed_supersonic_in_active_corona() {
let r = active_region_corona();
assert!(
r.alfven_speed() > r.sound_speed(),
"v_A should > c_s in active corona"
);
}
#[test]
fn debye_length_positive() {
assert!(quiet_corona().debye_length() > 0.0);
}
#[test]
fn plasma_frequency_positive() {
assert!(quiet_corona().plasma_frequency() > 0.0);
}
#[test]
fn density_decreases_with_height() {
let n0 = 1e14;
let n1 = density_at_height(n0, 1e8, 1.5e6);
let n2 = density_at_height(n0, 5e8, 1.5e6);
assert!(n1 > n2);
assert!(n2 > 0.0);
}
#[test]
fn hydrostatic_scale_height_proportional_to_temperature() {
let h1 = hydrostatic_scale_height(1e6);
let h2 = hydrostatic_scale_height(2e6);
assert!(h2 > h1);
}
#[test]
fn coronal_xray_luminosity_positive() {
assert!(coronal_x_ray_luminosity() > 0.0);
}
#[test]
fn coronal_loop_temperature_positive() {
let t = coronal_loop_temperature(1e8, 1e-4);
assert!(t > 0.0);
}
#[test]
fn thompson_scattering_decreases_with_distance() {
let b1 = thompson_scattering_brightness(1e14, 1.5);
let b2 = thompson_scattering_brightness(1e14, 3.0);
assert!(b1 > b2);
}
#[test]
fn type_iii_burst_frequency_proportional_to_density_sqrt() {
let f1 = type_iii_burst_frequency(1e14);
let f2 = type_iii_burst_frequency(4e14);
let ratio = f2 / f1;
assert!((ratio - 2.0).abs() < 0.1, "ratio={ratio}");
}
#[test]
fn x_class_more_energetic_than_c() {
let x = SolarFlare::from_class(FlareClass::X, 1.0);
let c = SolarFlare::from_class(FlareClass::C, 1.0);
assert!(x.total_energy_joules() > c.total_energy_joules());
}
#[test]
fn flare_peak_temperature_positive() {
let f = SolarFlare::from_class(FlareClass::M, 5.0);
assert!(f.peak_temperature() > 0.0);
}
#[test]
fn flare_reconnection_speed_positive() {
let f = SolarFlare::from_class(FlareClass::X, 1.0);
assert!(f.reconnection_inflow_speed() > 0.0);
}
#[test]
fn flare_frequency_decreases_with_class() {
let c_rate = flare_frequency_per_day(FlareClass::C);
let x_rate = flare_frequency_per_day(FlareClass::X);
assert!(c_rate > x_rate, "C-class more frequent than X-class");
}
#[test]
fn goes_classification_roundtrip() {
assert!(matches!(goes_class_from_flux(1e-6), FlareClass::C));
assert!(matches!(goes_class_from_flux(1e-5), FlareClass::M));
assert!(matches!(goes_class_from_flux(1e-4), FlareClass::X));
}
#[test]
fn magnetic_energy_available_positive() {
let e = magnetic_energy_available(0.1, 1e20);
assert!(e > 0.0);
}
#[test]
fn flare_confinement_time_positive() {
let f = SolarFlare::from_class(FlareClass::M, 1.0);
assert!(f.confinement_time() > 0.0);
}
#[test]
fn quiescent_prominence_cooler_than_corona() {
let p = Prominence::quiescent();
assert!(p.temperature_k < 1e6);
}
#[test]
fn active_prominence_hotter_than_quiescent() {
let q = Prominence::quiescent();
let a = Prominence::active();
assert!(a.temperature_k >= q.temperature_k);
}
#[test]
fn prominence_mass_positive() {
assert!(Prominence::quiescent().mass_kg() > 0.0);
}
#[test]
fn prominence_magnetic_energy_greater_than_thermal() {
let p = Prominence::quiescent();
assert!(
p.magnetic_energy() > p.thermal_energy(),
"Prominence magnetically dominated"
);
}
#[test]
fn kippenhahn_schluter_support_positive() {
assert!(Prominence::quiescent().kippenhahn_schluter_support() > 0.0);
}
#[test]
fn eruption_speed_positive() {
assert!(Prominence::active().eruption_speed() > 0.0);
}
#[test]
fn coronal_rain_velocity_increases_with_height() {
let v1 = coronal_rain_velocity(1e7);
let v2 = coronal_rain_velocity(5e7);
assert!(v2 > v1);
}
#[test]
fn prominence_oscillation_period_positive() {
let p = prominence_oscillation_period(1e8, 0.001, 1e-11);
assert!(p > 0.0);
}
#[test]
fn magnetic_dip_angle_range() {
let a = magnetic_dip_angle(0.001, 0.0005);
assert!(a > 0.0 && a < std::f64::consts::FRAC_PI_2);
}
#[test]
fn fast_wind_faster_than_slow() {
let slow = SolarWindState::slow_wind_1au();
let fast = SolarWindState::fast_wind_1au();
assert!(fast.speed_ms > slow.speed_ms);
}
#[test]
fn slow_wind_about_400_kms() {
let v = SolarWindState::slow_wind_1au().speed_ms;
assert!(v > 300e3 && v < 500e3, "slow wind v={v}");
}
#[test]
fn fast_wind_about_700_kms() {
let v = SolarWindState::fast_wind_1au().speed_ms;
assert!(v > 600e3 && v < 900e3, "fast wind v={v}");
}
#[test]
fn density_decreases_with_distance() {
let w = SolarWindState::slow_wind_1au();
let w2 = w.at_distance(2.0);
assert!(w2.density_m3 < w.density_m3);
}
#[test]
fn dynamic_pressure_positive() {
assert!(SolarWindState::slow_wind_1au().dynamic_pressure() > 0.0);
}
#[test]
fn parker_spiral_angle_at_1au_about_45() {
let angle = SolarWindState::slow_wind_1au().parker_spiral_angle_deg();
assert!(angle > 20.0 && angle < 70.0, "spiral angle={angle}°");
}
#[test]
fn alfven_mach_supersonic() {
assert!(SolarWindState::slow_wind_1au().alfven_mach_number() > 1.0);
}
#[test]
fn sonic_mach_supersonic() {
assert!(SolarWindState::slow_wind_1au().sonic_mach_number() > 1.0);
}
#[test]
fn parker_critical_radius_few_solar_radii() {
let r = parker_critical_radius() / suns::SOLAR_RADIUS;
assert!(r > 1.0 && r < 30.0, "r_c={r} R_sun");
}
#[test]
fn solar_wind_mass_loss_positive() {
let dm = solar_wind_mass_loss_rate();
assert!(dm > 0.0);
}
#[test]
fn alfven_radius_outside_sun() {
assert!(alfven_radius() > suns::SOLAR_RADIUS);
}
#[test]
fn corotation_radius_outside_sun() {
assert!(corotation_radius() > suns::SOLAR_RADIUS);
}
#[test]
fn heliospheric_field_decreases_with_distance() {
let b1 = heliospheric_magnetic_field_at(1.0);
let b2 = heliospheric_magnetic_field_at(5.0);
assert!(b1 > b2);
}
#[test]
fn travel_time_proportional_to_distance() {
let w = SolarWindState::slow_wind_1au();
let t1 = w.travel_time_to_au(2.0);
let t2 = w.travel_time_to_au(3.0);
assert!((t2 / t1 - 2.0).abs() < 0.1);
}
#[test]
fn coronal_region_struct_accessible() {
let r: CoronalRegion = quiet_corona();
assert!(r.thermal_pressure() > 0.0);
assert!(r.collision_frequency() > 0.0);
assert!(r.radiative_loss_rate() > 0.0);
}
#[test]
fn coronal_euv_flux_decreases_with_distance() {
let f1 = coronal_euv_flux(1.0);
let f2 = coronal_euv_flux(2.0);
assert!(f1 > f2);
assert!(f2 > 0.0);
}
#[test]
fn coronal_loop_density_positive() {
let d = coronal_loop_density(2e6, 1e8);
assert!(d > 0.0);
}
#[test]
fn drain_rate_positive() {
let r = drain_rate(1e-10, 1e6, 274.0, 5e7);
assert!(r > 0.0);
}
#[test]
fn flare_ribbon_speed_positive() {
let v = flare_ribbon_speed(0.1, 1e6);
assert!(v > 0.0);
}
#[test]
fn bow_shock_standoff_positive() {
let r = bow_shock_standoff(8e15, 1.0);
assert!(r > 0.0);
}
#[test]
fn parker_solution_speed_positive() {
let v = parker_solution_speed(parker_critical_radius() * 2.0);
assert!(v > 0.0);
}