Scenario Analysis Guide
Overview
The scenario workflow has three stages:
CMIP6 forcing ──► bc correct ──► bias-corrected forcing
│
ocean model (future run)
│
┌─────────────┴─────────────┐
single-scenario multi-scenario
(trends, changes) (SSP comparison)
- Bias-correct CMIP6 atmospheric forcing against ERA5 (or equivalent) so its statistical properties match the observational reference over the calibration period.
- Run the ocean model with the corrected forcing for each SSP scenario.
- Analyse the ocean output — trend maps, time series, regional means — for one scenario at a time or across multiple scenarios simultaneously.
Step 1 — Bias correction
Note:
bc correct,bc diagnostics, andbc checkhave moved to ocean-prep. Install and run them from theocean-preprepo. Config and guides are atocean-prep/config/bc_correct_example.yamlandocean-prep/guides/.
Commands (run from ocean-prep)
| Command | Role |
|---|---|
bc correct | Apply bias correction → write corrected NetCDF |
bc diagnostics | Inspect correction → maps, seasonal cycles, trend plots |
Aliases: bc-correct, bc-diagnostics.
Specialised variable guides
Some variables require extra treatment beyond a straightforward QDM:
| Guide | Variable | What it covers |
|---|---|---|
| Humidity bias correction | huss | Convert ERA5 d2m + sp → specific humidity before QDM |
| Radiation bias correction | net_sw, net_lw | Correct net (not downwelling-only) radiation using ERA5 ssr/str and CMIP6 rsds−rsus/rlds−rlus; confirms CMIP6 availability |
| River projection | river flow & loads | Delta-change method for freshwater inputs using bias-corrected P/E |
Correction methods
| Method | Flag | Description |
|---|---|---|
| Quantile delta mapping | qdm | Preserves relative future trends; corrects full distribution |
| Quantile mapping | qm | Corrects distribution but suppresses climate change signal |
| Delta (additive) | delta | Fast; corrects mean only |
qdm is the recommended default for most variables.
Config structure
# config/bc.yaml
metadata:
author: "K. Bolding"
institute: "BB"
project: "NorthSea-MFC"
area: NS
experiment: ssp245
scenario: ssp245
domain:
lon_min: -5.0; lon_max: 10.0
lat_min: 50.0; lat_max: 62.0
calibration:
start: "1990-01-01"
end: "2014-12-31"
future:
start: "2015-01-01"
end: "2100-12-31"
correction:
method: qdm
n_quantiles: 100
variables:
- name: tas
kind: additive # additive or multiplicative
- name: pr
kind: multiplicative
reference: # ERA5 or equivalent observational reference
tas:
source: local
path: /data/era5/tas_{year}.nc
variable: t2m
pr:
source: local
path: /data/era5/pr_{year}.nc
variable: tp
historical: # CMIP6 historical member
tas:
uri: "cmip6://MPI-ESM1-2-HR/historical/tas/day/r1i1p1f1"
pr:
uri: "cmip6://MPI-ESM1-2-HR/historical/pr/day/r1i1p1f1"
future_model: # CMIP6 future scenario member
tas:
uri: "cmip6://MPI-ESM1-2-HR/ssp245/tas/day/r1i1p1f1"
pr:
uri: "cmip6://MPI-ESM1-2-HR/ssp245/pr/day/r1i1p1f1"
output:
dir: ./bc_output/{scenario}
filename_pattern: "{variable}_bc_{year}.nc"
compress: true
Config in ocean-prep: config/bc_correct_example.yaml
Running the correction
# Full run
bc correct --config config/bc.yaml
# Single variable / override method / override scenario
bc correct --config config/bc.yaml --variable tas
bc correct --config config/bc.yaml --method qdm
bc correct --config config/bc.yaml --scenario ssp585
# Stages: correction only or disaggregation only
bc correct --config config/bc.yaml --steps correct
bc correct --config config/bc.yaml --steps disaggregate
Disaggregation distributes daily corrected output to sub-daily resolution
using a diurnal ERA5 climatology. Requires a disaggregation: block in the
config.
Correction output
bc_output/ssp245/
tas_bc_2015.nc … tas_bc_2100.nc
pr_bc_2015.nc … pr_bc_2100.nc
Running diagnostics
bc diagnostics --config config/bc.yaml
bc diagnostics --config config/bc.yaml --variable tas
bc diagnostics --config config/bc.yaml --output-dir ./diag/ssp245/
Results land under analyses/areas/<area>/scenarios/<scenario>/:
analyses/areas/NS/scenarios/ssp245/
└── tas/
├── plots/
│ ├── calibration_maps.png — reference | CMIP6 raw | bias
│ ├── seasonal_cycle.png — monthly climatology comparison
│ ├── future_trend.png — annual means + linear trend to 2100
│ └── monthly_breakdown.png — decadal heatmap
└── tables/
└── statistics.txt — per-month RMSE / bias / correlation
Multiple scenarios
for scenario in ssp245 ssp370 ssp585; do
bc correct --config config/bc.yaml --scenario $scenario
bc diagnostics --config config/bc.yaml --scenario $scenario
done
Step 2 — Single scenario analysis
single-scenario analyses ocean model output from one future projection:
trends (linear or Theil-Sen), anomalies relative to a reference period, and
period-to-period changes over one or more future periods.
Variables may be physics (sst, sss, ssh, temp, salt, …) or
biogeochemical (chl, doxy, ph, ntra, phos, …) — mix them freely
in one run. Each variable’s domain (physics/bio) is detected automatically
and routes its own plots/tables under the matching physics/bio subtree
(see Output below); nothing extra needs configuring to include bio
variables alongside physics ones.
Config
# config/single_scenario.yaml
area: NS
scenario:
name: SSP2-4.5
experiment: ssp245
period: "2020-2100"
variables:
- sst
- sss
- ssh
- chl # bio
- doxy # bio
- ph # bio
model:
base_path: ${MODEL_DATA_ROOT}/{user}/{area}/{experiment}
filename_pattern: "*.nc"
reference_period:
start: "1985-01-01"
end: "2014-12-31"
future_periods:
- name: "Near-term (2030s)"
start: "2030-01-01"
end: "2039-12-31"
- name: "Mid-century (2050s)"
start: "2050-01-01"
end: "2059-12-31"
- name: "End-century (2090s)"
start: "2090-01-01"
end: "2099-12-31"
analysis:
compute_trends: true
trend_method: linear # linear | theil-sen
compute_anomalies: true
output:
analyses_dir: "${OCEANICU_ANALYSES_FOLDER}" # no default — see tidal-analysis.md
Config: config/single_scenario.yaml
Usage
single-scenario --config config/single_scenario.yaml
single-scenario --config config/single_scenario.yaml --scenario ssp585
Output
analyses/areas/NS/validations/ssp245/
plots/physics/model/2020-2100/scenario/
sst_timeseries.png — area-mean annual time series
sst_trend_ssp245.png — trend map with significance stippling
sst_change_maps.png — one panel per future period vs. reference
plots/bio/model/2020-2100/scenario/
chl_timeseries.png
chl_trend_ssp245.png
chl_change_maps.png
tables/physics/model/scenario/
sst_scenario_statistics.txt
sst_scenario_statistics.yaml — same content, machine-readable sidecar
tables/bio/model/scenario/
chl_scenario_statistics.txt
chl_scenario_statistics.yaml
<model> and the 2020-2100 period label come from model.name (defaults
to model if unset) and the span from reference_period.start to the last
future_periods[].end — same AnalysesLayout convention
gridded-2d-validation/tidal-analysis use, so the results feed into the
same Hugo page generation and statistics.db as every other analysis type.
Step 3 — Multi-scenario comparison
multi-scenario loads output from multiple SSP runs and overlays them —
time series comparison and a per-variable comparison table across
scenarios. Same physics/bio variable mixing as single-scenario above.
Config
# config/multi_scenario.yaml
area: NS
scenarios:
- name: SSP1-2.6
experiment: ssp126
- name: SSP2-4.5
experiment: ssp245
- name: SSP5-8.5
experiment: ssp585
variables:
- sst
- sss
- ssh
- chl # bio
- doxy # bio
- ph # bio
model:
base_path: ${MODEL_DATA_ROOT}/{user}/{area}/{experiment}
filename_pattern: "*.nc"
reference_period:
start: "1985-01-01"
end: "2014-12-31"
comparison_name: ssp_comparison # optional; defaults to the joined experiment ids
analysis:
trend_method: linear # linear | theil-sen
output:
analyses_dir: "${OCEANICU_ANALYSES_FOLDER}" # no default — see tidal-analysis.md
Config: config/multi_scenario.yaml
Usage
multi-scenario --config config/multi_scenario.yaml
Output
analyses/areas/NS/scenarios/ssp_comparison/
plots/
sst_multi_scenario_timeseries.png — all SSPs on one axis
chl_multi_scenario_timeseries.png
tables/
sst_multi_scenario_statistics.txt
sst_comparison.csv — mean/std/trend/p-value per scenario
chl_multi_scenario_statistics.txt
chl_comparison.csv
Unlike single-scenario’s output, this comparison tree isn’t picked up automatically by Hugo page generation yet — there’s no per-experiment directory a multi-scenario comparison maps to in the same way a single scenario run does.
Performance notes
- Bias correction uses Dask lazy loading; memory stays bounded because output is written year by year.
- Diagnostics use a time-only chunk path — typically ~2 s/year.
- Regridding weights are cached under
cache.dirand reused across variables and scenarios with the same grids.