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)
  1. Bias-correct CMIP6 atmospheric forcing against ERA5 (or equivalent) so its statistical properties match the observational reference over the calibration period.
  2. Run the ocean model with the corrected forcing for each SSP scenario.
  3. 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, and bc check have moved to ocean-prep. Install and run them from the ocean-prep repo. Config and guides are at ocean-prep/config/bc_correct_example.yaml and ocean-prep/guides/.

Commands (run from ocean-prep)

CommandRole
bc correctApply bias correction → write corrected NetCDF
bc diagnosticsInspect correction → maps, seasonal cycles, trend plots

Aliases: bc-correct, bc-diagnostics.

Specialised variable guides

Some variables require extra treatment beyond a straightforward QDM:

GuideVariableWhat it covers
Humidity bias correctionhussConvert ERA5 d2m + sp → specific humidity before QDM
Radiation bias correctionnet_sw, net_lwCorrect net (not downwelling-only) radiation using ERA5 ssr/str and CMIP6 rsds−rsus/rlds−rlus; confirms CMIP6 availability
River projectionriver flow & loadsDelta-change method for freshwater inputs using bias-corrected P/E

Correction methods

MethodFlagDescription
Quantile delta mappingqdmPreserves relative future trends; corrects full distribution
Quantile mappingqmCorrects distribution but suppresses climate change signal
Delta (additive)deltaFast; 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.dir and reused across variables and scenarios with the same grids.