Skip to content

Repository files navigation

ClimaCalibrate.jl

Calibrate model parameters against observations, from a laptop to a supercomputer with the same code.

ClimaCalibrate.jl runs the calibration loop around your forward model: it launches an ensemble of model runs, collects their output, hands it to EnsembleKalmanProcesses.jl to produce the next set of parameters, and repeats. EnsembleKalmanProcesses chooses the parameters; ClimaCalibrate runs your model with them, checkpoints the results, and resumes where it left off if the run is interrupted.

Documentation stable dev
Version version
License license
Tests gha ci
Code Coverage codecov
Downloads Downloads

Features

  • One calibration, several places to run it: the same code runs members one at a time in the current process, across Distributed.jl workers, or as one scheduler job per ensemble member. You choose by swapping the backend.
  • Ready-made HPC backends: Slurm and PBS support, with configurations for the Resnick High Performance Computing Center, NSF NCAR Derecho, Google Cloud, and CliMA's GPU server.
  • Restarts: completed iterations and completed forward models are checkpointed and skipped, so an interrupted calibration picks up where it stopped.
  • Observations from model output: recipes that turn ClimaAnalysis.jl OutputVars into observations with estimated noise covariances.
  • A G ensemble builder that checks itself: it places each variable using the observation's own metadata and validates short names, units, dimension names, dimension units, and dimension values, so model output that does not line up with the observation raises an error instead of being calibrated against silently.
  • Diagnostics: residual analysis that reports how much of the unfitted residual is structured rather than noise-like, and Makie plots of ensemble output against observations.

Installation

ClimaCalibrate.jl is a registered Julia package. Install it with:

julia> ] add ClimaCalibrate

Julia 1.10 or newer is required.

Quick Example

A calibration needs three things from you: a struct holding your configuration, a forward model, and an observation map. Here the "model" is exp(-rate), and the calibration recovers rate from a single observation.

import ClimaCalibrate as CAL
import EnsembleKalmanProcesses as EKP
import EnsembleKalmanProcesses.ParameterDistributions:
    combine_distributions, constrained_gaussian
import TOML

struct Decay <: CAL.AbstractModelInterface
    output_dir::String
    ensemble_size::Int
end

# Run one ensemble member: read the parameters written for it, write its output
function CAL.forward_model(model::Decay, iteration, member)
    (; output_dir) = model
    member_dir = CAL.path_to_ensemble_member(output_dir, iteration, member)
    rate = TOML.parsefile(CAL.parameter_path(output_dir, iteration, member))
    write(joinpath(member_dir, "out"), string(exp(-rate["rate"]["value"])))
end

# Collect the whole ensemble's output, one column per member
function CAL.observation_map(model::Decay, iteration)
    (; output_dir, ensemble_size) = model
    G_ensemble = Matrix{Float64}(undef, 1, ensemble_size)
    for member in 1:ensemble_size
        member_dir = CAL.path_to_ensemble_member(output_dir, iteration, member)
        G_ensemble[1, member] =
            parse(Float64, read(joinpath(member_dir, "out"), String))
    end
    return G_ensemble
end

prior = combine_distributions([constrained_gaussian("rate", 1.0, 0.5, 0, Inf)])
observation = [exp(-0.7)]                # generated with rate = 0.7
noise = reshape([1e-4], 1, 1)
ensemble_size, n_iterations = 10, 5
output_dir = mktempdir()

ekp = EKP.EnsembleKalmanProcess(
    EKP.construct_initial_ensemble(prior, ensemble_size),
    observation,
    noise,
    EKP.Inversion(),
)

ekp = CAL.calibrate(
    CAL.JuliaBackend(),
    ekp,
    Decay(output_dir, ensemble_size),
    n_iterations,
    prior,
    output_dir,
)

EKP.get_ϕ_mean_final(prior, ekp)          # ≈ [0.7]

To run the ensemble in parallel instead, swap CAL.JuliaBackend() for CAL.WorkerBackend() or one of the HPC backends. Nothing else changes.

Documentation

The documentation is at stable and dev. Useful entry points:

  • How a calibration works: the loop, where your code is called, what lands in the output directory, and how restarts work.
  • Getting Started: the pieces a calibration needs and how to put them together.
  • Calibration Tutorial: a complete worked example you can run locally.
  • Backends: choosing where the ensemble runs, and moving a working calibration onto a cluster.
  • Observations: building observations and noise covariances from data.
  • Troubleshooting: what the errors mean, and what to check when a calibration does not converge.

Integration with CliMA models

ClimaCalibrate.jl is the calibration layer of the CliMA Earth System Model:

Contributing

Contributions of any size are welcome, and fresh eyes catch errors that regular developers miss. If you would like to work on a new feature, let us know by opening an issue.

Development follows the shared CliMA DeveloperGuides, which cover code style, the documentation policy, testing, and the changelog conventions this repository uses. Before opening a pull request, run the test suite and the formatter:

julia --project -e 'using Pkg; Pkg.test()'
julia -e 'using JuliaFormatter; format(".")'

User-visible changes go in NEWS.md.

License

Apache License 2.0. See LICENSE and NOTICE.

Releases

Packages

Used by

Contributors

Languages