use serde::{Deserialize, Serialize};
const RHO_ICE: f64 = 917.0;
const RHO_MANTLE: f64 = 3300.0;
const G: f64 = 9.81;
const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 3600.0;
const A_REF: f64 = 2.4e-24;
const T_REF_C: f64 = -10.0;
const DOUBLING_INTERVAL: f64 = 10.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GlacierType {
Alpine,
IceSheet,
IceCap,
Piedmont,
TideWater,
}
#[must_use]
fn flow_parameter_a(temperature_c: f64) -> f64 {
let exponent = (temperature_c - T_REF_C) / DOUBLING_INTERVAL;
A_REF * f64::exp2(exponent)
}
#[must_use]
pub fn glen_flow_law(stress_pa: f64, temperature_c: f64, n: f64) -> f64 {
let a = flow_parameter_a(temperature_c);
a * stress_pa.powf(n)
}
#[must_use]
pub fn basal_sliding_velocity(basal_shear_stress: f64, effective_pressure: f64) -> f64 {
const K: f64 = 1e-15;
const P: f64 = 3.0;
const Q: f64 = 1.0;
if effective_pressure <= 0.0 {
return 0.0;
}
K * basal_shear_stress.powf(P) / effective_pressure.powf(Q)
}
#[must_use]
pub fn mass_balance(accumulation_m_yr: f64, ablation_m_yr: f64) -> f64 {
accumulation_m_yr - ablation_m_yr
}
#[must_use]
pub fn equilibrium_line_altitude(summit_m: f64, terminus_m: f64) -> f64 {
(summit_m + terminus_m) / 2.0
}
#[must_use]
pub fn isostatic_depression(ice_thickness_m: f64) -> f64 {
ice_thickness_m * (RHO_ICE / RHO_MANTLE)
}
#[must_use]
pub fn isostatic_rebound_time(_depression_m: f64, viscosity_pa_s: f64) -> f64 {
let tau_seconds = viscosity_pa_s / (RHO_MANTLE * G * 100.0);
tau_seconds / SECONDS_PER_YEAR
}
#[must_use]
pub fn ice_velocity_depth_integrated(
surface_slope: f64,
thickness_m: f64,
temperature_c: f64,
) -> f64 {
let n: f64 = 3.0;
let a = flow_parameter_a(temperature_c);
let driving_stress = RHO_ICE * G * surface_slope.sin();
let velocity_m_s = (2.0 * a / (n + 1.0)) * driving_stress.powf(n) * thickness_m.powf(n + 1.0);
velocity_m_s * SECONDS_PER_YEAR
}
#[cfg(test)]
mod tests {
use super::*;
const EPS: f64 = 1e-12;
#[test]
fn glen_flow_law_increases_with_stress() {
let low = glen_flow_law(1e5, -10.0, 3.0);
let high = glen_flow_law(2e5, -10.0, 3.0);
assert!(high > low, "strain rate should increase with stress");
}
#[test]
fn glen_flow_law_increases_with_temperature() {
let cold = glen_flow_law(1e5, -20.0, 3.0);
let warm = glen_flow_law(1e5, -10.0, 3.0);
assert!(warm > cold, "strain rate should increase with temperature");
}
#[test]
fn glen_flow_law_reference_value() {
let rate = glen_flow_law(1.0, -10.0, 3.0);
assert!(
(rate - A_REF).abs() < 1e-40,
"A(T_ref) × 1^3 should equal A_REF"
);
}
#[test]
fn glen_flow_law_zero_stress() {
let rate = glen_flow_law(0.0, -10.0, 3.0);
assert!(
rate.abs() < EPS,
"zero stress should yield zero strain rate"
);
}
#[test]
fn basal_sliding_positive() {
let v = basal_sliding_velocity(1e5, 1e6);
assert!(v > 0.0, "sliding velocity should be positive");
}
#[test]
fn basal_sliding_increases_with_stress() {
let v_low = basal_sliding_velocity(1e5, 1e6);
let v_high = basal_sliding_velocity(2e5, 1e6);
assert!(
v_high > v_low,
"velocity should increase with basal shear stress"
);
}
#[test]
fn basal_sliding_zero_effective_pressure() {
let v = basal_sliding_velocity(1e5, 0.0);
assert!(v.abs() < EPS, "zero effective pressure should return 0");
}
#[test]
fn mass_balance_positive_when_accumulation_exceeds_ablation() {
let b = mass_balance(2.0, 1.5);
assert!((b - 0.5).abs() < EPS);
}
#[test]
fn mass_balance_negative_when_ablation_exceeds() {
let b = mass_balance(1.0, 3.0);
assert!(b < 0.0);
}
#[test]
fn ela_midpoint() {
let ela = equilibrium_line_altitude(4000.0, 2000.0);
assert!((ela - 3000.0).abs() < EPS);
}
#[test]
fn isostatic_depression_proportional() {
let d = isostatic_depression(3000.0);
let expected = 3000.0 * (917.0 / 3300.0);
assert!((d - expected).abs() < 1e-6);
}
#[test]
fn isostatic_depression_zero_ice() {
let d = isostatic_depression(0.0);
assert!(d.abs() < EPS);
}
#[test]
fn rebound_time_positive() {
let t = isostatic_rebound_time(100.0, 1e21);
assert!(t > 0.0, "rebound time should be positive");
}
#[test]
fn rebound_time_increases_with_viscosity() {
let t_low = isostatic_rebound_time(100.0, 1e20);
let t_high = isostatic_rebound_time(100.0, 1e21);
assert!(
t_high > t_low,
"higher viscosity should yield longer rebound time"
);
}
#[test]
fn velocity_positive_for_downslope() {
let v = ice_velocity_depth_integrated(0.05, 500.0, -10.0);
assert!(v > 0.0, "downslope flow should yield positive velocity");
}
#[test]
fn velocity_increases_with_thickness() {
let v_thin = ice_velocity_depth_integrated(0.05, 200.0, -10.0);
let v_thick = ice_velocity_depth_integrated(0.05, 500.0, -10.0);
assert!(v_thick > v_thin, "thicker ice should flow faster");
}
#[test]
fn glacier_type_roundtrip_serde() {
let gt = GlacierType::TideWater;
let json = serde_json::to_string(>).unwrap();
let back: GlacierType = serde_json::from_str(&json).unwrap();
assert_eq!(gt, back);
}
}