SKILL.md
Climate Modeling Guide
A skill for working with climate models and climate data in research contexts. Covers accessing CMIP archives, processing NetCDF data, running idealized climate simulations, statistical downscaling, and analyzing climate projections with Python tools.
Climate Data Standards
NetCDF and CF Conventions
Climate data is stored in NetCDF (Network Common Data Form) files following CF (Climate and Forecast) conventions:
import xarray as xr
import numpy as np
# Open a CMIP6 temperature dataset
ds = xr.open_dataset("tas_Amon_CESM2_ssp585_r1i1p1f1_gn_201501-210012.nc")
print(ds)
# Dimensions: (time: 1032, lat: 192, lon: 288)
# Variables: tas (surface air temperature, K)
# Attributes: CF-1.6 compliant, CMIP6 metadata
# Basic inspection
print(f"Variable: {ds.tas.long_name}")
print(f"Units: {ds.tas.units}")
print(f"Time range: {ds.time.values[0]} to {ds.time.values[-1]}")
print(f"Spatial resolution: {np.diff(ds.lat.values[:2])[0]:.2f} deg")
CMIP6 Data Access
The Coupled Model Intercomparison Project Phase 6 provides standardized multi-model climate projections:
# Using intake-esm to search the CMIP6 catalog
import intake
# Open the Pangeo CMIP6 catalog (cloud-hosted on Google Cloud)
url = "https://storage.googleapis.com/cmip6/pangeo-cmip6.json"
col = intake.open_esm_datastore(url)
# Search for monthly surface temperature under SSP5-8.5
query = col.search(
experiment_id="ssp585",
variable_id="tas",
table_id="Amon",
source_id=["CESM2", "GFDL-ESM4", "UKESM1-0-LL", "MPI-ESM1-2-HR"],
member_id="r1i1p1f1",
)
print(f"Found {len(query)} datasets from {query.nunique()['source_id']} models")
# Load as xarray datasets (lazy, Zarr-backed)
dsets = query.to_dataset_dict(zarr_kwargs={"consolidated": True})
Climate Analysis Techniques
Global Mean Temperature Anomaly
def compute_global_mean_anomaly(ds, baseline_start="1850-01-01",
baseline_end="1900-12-31"):
"""
Compute area-weighted global mean temperature anomaly
relative to a baseline period.
"""
# Area weighting by cosine of latitude
weights = np.cos(np.deg2rad(ds.lat))
weights.name = "weights"
# Weighted global mean time series
global_mean = ds.tas.weighted(weights).mean(dim=["lat", "lon"])
# Compute baseline climatology
baseline = global_mean.sel(time=slice(baseline_start, baseline_end))
climatology = baseline.groupby("time.month").mean("time")
# Compute anomalies
anomaly = global_mean.groupby("time.month") - climatology
# Annual mean anomaly
annual_anomaly = anomaly.resample(time="YE").mean()
return annual_anomaly
def multi_model_ensemble(datasets: dict, baseline_period: tuple):
"""
Compute multi-model ensemble mean and spread for temperature projections.
datasets: dict of {model_name: xarray.Dataset}
Returns ensemble mean and 5th/95th percentile bounds.
"""
anomalies = []
for name, ds in datasets.items():
anom = compute_global_mean_anomaly(ds, *baseline_period)
anom = anom.assign_coords(model=name)
anomalies.append(anom)
ensemble = xr.concat(anomalies, dim="model")
return {
"mean": ensemble.mean(dim="model"),
"p05": ensemble.quantile(0.05, dim="model"),
"p95": ensemble.quantile(0.95, dim="model"),
}
