earths 0.0.1

High-fidelity Earth simulation engine — orbit, atmosphere, geology, hydrology, biosphere, terrain, lighting, rendering, satellites, and temporal systems with full scientific coupling
Documentation
pub struct TropicalCyclone {
    pub max_sustained_wind_m_s: f64,
    pub central_pressure_hpa: f64,
    pub radius_max_wind_km: f64,
    pub latitude_deg: f64,
}
impl TropicalCyclone {
    pub fn saffir_simpson_category(&self) -> u8 {
        let v_kt = self.max_sustained_wind_m_s * 1.94384;
        if v_kt >= 137.0 {
            5
        } else if v_kt >= 113.0 {
            4
        } else if v_kt >= 96.0 {
            3
        } else if v_kt >= 83.0 {
            2
        } else if v_kt >= 64.0 {
            1
        } else {
            0
        }
    }
    pub fn accumulated_cyclone_energy(&self, duration_hours: f64) -> f64 {
        let v_kt = self.max_sustained_wind_m_s * 1.94384;
        v_kt * v_kt * duration_hours / 6.0 * 1e-4
    }
    pub fn potential_intensity_m_s(sst_k: f64, tropopause_temp_k: f64, enthalpy_diff: f64) -> f64 {
        let efficiency = (sst_k - tropopause_temp_k) / sst_k;
        let ck_cd = 0.9;
        (ck_cd * efficiency * enthalpy_diff).sqrt()
    }
    pub fn rossby_deformation_radius(&self) -> f64 {
        let f = 2.0 * crate::OMEGA_EARTH * (self.latitude_deg.to_radians()).sin();
        let nh = 50.0;
        if f.abs() < 1e-10 {
            return f64::INFINITY;
        }
        nh / f.abs()
    }
    pub fn wind_at_radius(&self, r_km: f64) -> f64 {
        let rm = self.radius_max_wind_km;
        let vm = self.max_sustained_wind_m_s;
        vm * (rm / r_km) * (-(r_km - rm).powi(2) / (2.0 * rm * rm)).exp()
    }
}
pub fn fujita_scale(wind_speed_m_s: f64) -> &'static str {
    let mph = wind_speed_m_s * 2.23694;
    if mph >= 261.0 {
        "EF5"
    } else if mph >= 201.0 {
        "EF4"
    } else if mph >= 136.0 {
        "EF3"
    } else if mph >= 111.0 {
        "EF2"
    } else if mph >= 86.0 {
        "EF1"
    } else if mph >= 65.0 {
        "EF0"
    } else {
        "Sub-EF"
    }
}
pub fn cape_layer(parcel_temp_k: f64, env_temp_k: f64, dz: f64) -> f64 {
    if env_temp_k.abs() < 1e-10 {
        return 0.0;
    }
    *crate::SURFACE_GRAVITY * ((parcel_temp_k - env_temp_k) / env_temp_k).max(0.0) * dz
}
pub fn cape_integrated(
    parcel_temp_k: f64,
    env_surface_temp_k: f64,
    env_lapse_rate_k_per_m: f64,
    dz_m: f64,
    n_layers: u32,
) -> f64 {
    let moist_lapse = 0.0065;
    let mut cape_total = 0.0;
    let mut t_parcel = parcel_temp_k;
    let mut t_env = env_surface_temp_k;
    let mut above_lfc = false;
    for _ in 0..n_layers {
        t_parcel -= moist_lapse * dz_m;
        t_env -= env_lapse_rate_k_per_m * dz_m;
        if t_parcel > t_env {
            above_lfc = true;
            cape_total += cape_layer(t_parcel, t_env, dz_m);
        } else if above_lfc {
            break;
        }
    }
    cape_total
}