🌍

Climate Science

Climate modeling with uncertainty propagation for robust predictions

Climate Science with Sounio

Sounio enables climate modeling with rigorous uncertainty quantification for policy-relevant projections.

The Uncertainty Challenge

Climate science involves multiple uncertainty sources:

  • Initial condition sensitivity
  • Model structural uncertainty
  • Parameter uncertainty
  • Scenario uncertainty
  • Observation errors

Communicating these uncertainties clearly is critical for policy decisions.

Temperature Projections with Uncertainty

use std::units::{celsius, year, W_per_m2}
use std::epistemic::{Knowledge, measure}

struct ClimateProjection {
    temperature: Knowledge<celsius>,
    forcing: Knowledge<W_per_m2>,
    year: year,
    scenario: String,
}

fn project_temperature(
    baseline: Knowledge<celsius>,
    forcing: Knowledge<W_per_m2>,
    sensitivity: Knowledge<celsius_per_doubling>
) -> Knowledge<celsius> {
    // Simple energy balance model
    let delta_t = sensitivity * ln(forcing / 280.0) / ln(2.0)

    baseline + delta_t
    // Uncertainty from all inputs propagates automatically
}

Ensemble Analysis

fn analyze_ensemble(
    models: Vec<ClimateModel>,
    scenario: EmissionScenario
) -> EnsembleResult with IO {
    var projections = vec![]

    for model in models {
        let projection = model.run(scenario)
        projections.push(Knowledge {
            value: projection.temperature,
            uncertainty: model.internal_variability,
            provenance: vec![model.name, model.version],
            confidence: model.validation_score,
        })

        perform IO::log(
            model.name + ": " +
            projection.temperature.to_string() + " K"
        )
    }

    // Multi-model mean with uncertainty
    let ensemble_mean = weighted_mean(projections, |p| p.confidence)

    // Include model spread in uncertainty
    let model_spread = projections.std_dev()
    let total_uncertainty = (
        ensemble_mean.uncertainty.pow(2) +
        model_spread.pow(2)
    ).sqrt()

    EnsembleResult {
        mean: Knowledge {
            value: ensemble_mean.value,
            uncertainty: total_uncertainty,
            confidence: ensemble_mean.confidence,
            provenance: projections.flat_map(|p| p.provenance).collect(),
        },
        range: (projections.min(), projections.max()),
        n_models: models.len(),
    }
}

Confidence-Gated Policy Advice

fn assess_risk(
    projection: Knowledge<celsius>,
    threshold: celsius
) -> RiskAssessment with IO {
    // Probability of exceeding threshold
    let exceedance_prob = 1.0 - normal_cdf(
        threshold,
        projection.value,
        projection.uncertainty
    )

    if projection.confidence > 0.90 {
        if exceedance_prob > 0.66 {
            RiskAssessment::Likely("Threshold likely to be exceeded")
        } else if exceedance_prob > 0.33 {
            RiskAssessment::Possible("Threshold may be exceeded")
        } else {
            RiskAssessment::Unlikely("Threshold unlikely to be exceeded")
        }
    } else {
        perform IO::warn("Low model confidence - assessment uncertain")
        RiskAssessment::Uncertain("Insufficient model agreement")
    }
}

Sea Level Rise Modeling

fn project_sea_level(
    thermal_expansion: Knowledge<mm>,
    ice_sheet_contribution: Knowledge<mm>,
    glacier_melt: Knowledge<mm>
) -> Knowledge<mm> {
    // Components sum with correlated uncertainties
    let total = thermal_expansion + ice_sheet_contribution + glacier_melt

    // Ice sheet has fat-tailed uncertainty
    let adjusted = total.with_distribution(
        Distribution::SkewNormal(
            location: total.value,
            scale: total.uncertainty,
            shape: 2.0  // Right-skewed for ice sheet instability
        )
    )

    adjusted
}

Regional Downscaling

fn downscale_projection(
    global: Knowledge<celsius>,
    region: Region,
    historical: Vec<Observation>
) -> Knowledge<celsius> with IO {
    // Statistical downscaling with uncertainty
    let pattern = compute_scaling_pattern(historical, region)

    let regional = global * pattern.scaling_factor

    // Add downscaling uncertainty
    let total_uncertainty = (
        regional.uncertainty.pow(2) +
        pattern.uncertainty.pow(2)
    ).sqrt()

    if pattern.confidence < 0.80 {
        perform IO::warn(
            "Limited historical data for " + region.name +
            " - downscaling uncertainty may be underestimated"
        )
    }

    Knowledge {
        value: regional.value,
        uncertainty: total_uncertainty,
        confidence: regional.confidence * pattern.confidence,
        provenance: regional.provenance.append("downscaled_" + region.name),
    }
}

Features for Climate Science

  • Ensemble handling: Combine multiple model outputs
  • IPCC-compatible: Likelihood language support
  • Uncertainty decomposition: Separate sources of uncertainty
  • Provenance tracking: Full model lineage
  • Unit safety: Prevents common unit errors

Data Format Support

  • NetCDF native support
  • CMIP6 data structures
  • BIDS-like metadata conventions

Get Started

sounio new climate-project --template climate
cd climate-project
sounio run examples/temperature_projection.sio