Analysis Overview — all analysis types in one page
Boundary Conditions — future open-boundary forcing (delta-change + tidal)
Experiments
- CMEMS/spinup — validation
- CMEMS/tidal — tidal
- CMEMS/v01P — validation
- CMIP6/tidal — tidal
- CMIP6_raw/GFDL-ESM4-ssp126/run01 — scenario
- WOA/tidal — tidal
NSe boundary condition generation — runbook
Practical how-to. For dataset-level details (CMEMS product IDs, coverage
dates, Baltic override rationale) see ocean-prep/docs/nse_boundary_sources.md
— that doc explains what data; this one explains what to run, in what
order, from where.
0. The boundary point file — read this first
Canonical copy: NSe/Bathymetry/nse_bdy_lonlat.txt (currently 311
points). Moved here from NSe/nse_bdy_lonlat.txt on 2026-08-17 — this
file’s entire validity is defined by wet/land status against
NSe/Bathymetry/bathymetry_nse.nc, needs re-verification after every
bathymetry regeneration (see below), and lives alongside the other
grid/mask artifacts there, not the YAML domain configs in NSe/config/.
This is the file the running model actually uses — confirmed empirically by
comparing it against lon_bdy/lat_bdy in a getm-dump.nc crash dump
written by a live run: exact match, 0 points different. Treat any other
copy as untrustworthy until proven otherwise (see “Known copies” below).
Policy: this file belongs to the setup. It must live under
NSe/Bathymetry/, not in ocean-prep/bdy_coords/ or
boundaries.old/bdy_coords/ or the oceanicu_3d/ repo root, and not as a
symlink from those locations either — every consuming config references
this one real path directly (fixed 2026-08-17; see §3). Verify with
ls -la / grep -rln nse_bdy_lonlat before trusting any copy if this ever
looks inconsistent again.
Verifying it against the bathymetry
Every boundary point must land on a wet cell in the current
NSe/Bathymetry/bathymetry_nse.nc. Check with:
import netCDF4, numpy as np
with netCDF4.Dataset("NSe/Bathymetry/bathymetry_nse.nc") as nc:
lon = np.array(nc["lon"][:]); lat = np.array(nc["lat"][:])
mask = np.array(nc["ocean_mask"][:]).astype(bool)
pts = []
with open("NSe/Bathymetry/nse_bdy_lonlat.txt") as f:
for line in f.readlines()[2:]:
line = line.strip()
if not line: continue
lo, la = map(float, line.split(","))
pts.append((lo, la))
n_land = 0
for lo, la in pts:
i = int(np.argmin(np.abs(lon - lo)))
j = int(np.argmin(np.abs(lat - la)))
if not mask[j, i]:
n_land += 1
print("ON LAND:", lo, la)
print(f"{len(pts)} points, {n_land} on land")
Run this after every bathymetry regeneration. generate_nse_bathymetry.py
(mask_regions, rx0 smoothing, thalweg fixes) can change which cells are wet —
a point that was fine yesterday can end up on land today with no error or
warning anywhere else. This actually happened on 2026-08-03: a fix to
mask_regions rectangle-boundary floating-point tolerance (for an unrelated
single-row “Humber” closure) retroactively closed the eastern edge of the
pre-existing “West of Orkney” region, which had been silently not closing
due to the same rounding issue. 123 of 311 points ended up on land as a
side effect of a bathymetry fix that had nothing to do with boundaries.
Update, 2026-08-17: re-checked against the current bathymetry_nse.nc
— 0 of 311 points on land. The bathymetry has evidently been
regenerated/corrected again since the above regression, independently of
any boundary-point edit. A partial fix for the 123-point regression was
attempted the same day it happened (121 of the 123 points moved to nearby
wet cells, 2 dropped as a redundant corner vertex once both adjacent
segments shifted) but landed on the wrong file (oceanicu_3d/nse_bdy_lonlat.txt
at the repo root, not the canonical copy) and was never actually applied —
now archived at NSe/Bathymetry/nse_bdy_lonlat.txt.mislanded_edit_20260803
for reference in case this regression (or a similar one) recurs. Always
re-run the check above before trusting either “0 on land” or an old
point-count from this doc — bathymetry regenerations happen independently
of this file and can silently invalidate either state.
If a point needs to move or be dropped
- Back up first — always:
.bakcopies with a timestamp suffix if a second round of fixes is likely (seeNSe/Bathymetry/nse_bdy_lonlat.txt.bak*for the pattern used so far). - Edit
NSe/Bathymetry/nse_bdy_lonlat.txtdirectly (plainlon,latper line, headerT-grid/lon,lat). - If boundary_data NC files already exist (see §2 below) for the old
point list, they must be re-cut to match — the point count/order in the
NC files’
nbdypdimension must stay in lockstep with the text file, or pygetm will refuse to load them ("length 312 ... actual extent 317"type errors) or — worse — silently misalign points if counts happen to match by coincidence. UseNSe/trim_bdy.py <src.nc> <dst.nc> <idx0> ...to drop specific 0-basednbdypindices from hourly/daily reference files; verify the index againstboundary_lon/boundary_latin the NC file first, not just the text file (they can drift independently — see “Known copies”). - If more than a couple of points changed, it may be simpler to fully regenerate the reference data from source (§2) than to patch the NC files by hand.
1. The three-stage pipeline
NSe boundary conditions are built in three independent stages, each with its
own config and CLI tool (all ocean-prep, pip-installed editable — works
from any cwd):
| Stage | Produces | Tool | Config |
|---|---|---|---|
| 1. Historical reference | boundary_data/nse/{hourly,daily}/*.nc — real CMEMS reanalysis + near-real-time forecast, 2015–present | run-cmems-boundaries | NSe/config/nse_bdy_create.yaml |
| 2a. Future scenario (T/S) | CMIP6/{model}/{experiment}/bdy_3d_{var}_*.nc — delta-change projection | run-delta-boundaries | NSe/config/nse_delta_bdy.yaml |
| 2b. Future scenario (SSH/currents) | Tidal + CMIP6 mean SSH/transport, hourly | run-tidal-boundaries | NSe/config/nse_tidal_bdy.yaml |
Stage 1 must exist before stage 2a can run — run-delta-boundaries reads
the historical hourly/daily files as its cycling reference (it has no
independent boundary-point list; it inherits points from those NC files'
own boundary_lon/boundary_lat/segment_id variables). Stage 2b
(tidal) is independent of stage 1 — it reads TPXO9 + CMIP6 directly, using
NSe/Bathymetry/nse_bdy_lonlat.txt for its own point list.
Stage 1 — historical reference (CMEMS)
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe # nse_bdy_create.yaml lives here (NSe-specific, not in ocean-prep)
run-cmems-boundaries --config config/nse_bdy_create.yaml --dryrun
run-cmems-boundaries --config config/nse_bdy_create.yaml
# selectively:
run-cmems-boundaries --config config/nse_bdy_create.yaml --dataset temperature salinity
run-cmems-boundaries --config config/nse_bdy_create.yaml --category physics
nse_bdy_create.yaml’s boundary_points.file_path is a real absolute path
directly at NSe/Bathymetry/nse_bdy_lonlat.txt — no symlink indirection
(fixed 2026-08-17; every domain setup follows this same pattern, its own
canonical copy in its own repo, referenced directly — a shared symlink
target doesn’t generalize across setups).
Stage 2a — future scenario, temperature/salinity (delta-change)
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe
run-delta-boundaries --config config/nse_delta_bdy.yaml --dryrun
run-delta-boundaries --config config/nse_delta_bdy.yaml --scenario ssp126 ssp370 ssp585
run-delta-boundaries --config config/nse_delta_bdy.yaml \
--future-start 2070-01-01 --future-end 2099-12-31
run-delta-boundaries --config config/nse_delta_bdy.yaml --variable thetao so
Method: corrected(t) = AMM7/AMM15_ref(t_analog) + [CMIP6_future_clim(month) − CMIP6_hist_clim(month)],
where t_analog cycles through the historical reference period (same
calendar day/hour, year mapped modulo the reference length). Safe to run as
long as stage 1’s hourly/daily files are current and correctly aligned with
NSe/Bathymetry/nse_bdy_lonlat.txt (they inherit its point layout
automatically from their own boundary_lon/boundary_lat/segment_id).
Stage 2b — future scenario, SSH/currents (tidal + CMIP6)
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe
run-tidal-boundaries --config config/nse_tidal_bdy.yaml --dryrun
run-tidal-boundaries --config config/nse_tidal_bdy.yaml
run-tidal-boundaries --config config/nse_tidal_bdy.yaml --start 2060-01-01 --end 2060-12-31
run-tidal-boundaries --config config/nse_tidal_bdy.yaml --model UKESM1-0-LL --scenario ssp126
NSe/config/nse_tidal_bdy.yaml’s file_path is ./Bathymetry/nse_bdy_lonlat.txt,
a relative path resolved against the working directory — must still be run
with cwd = NSe/.
2. Post-generation sanity checks
- Re-run the land/water check in §0 if the bathymetry changed since the NC files were last built.
nc_nan_scan.py(repo root,oceanicu_3d/nc_nan_scan.py) can scan any resulting or model-output NetCDF for unexpected NaNs inside the computational domain, distinguishing genuine bugs from NaN that’s expected at open-boundary points for terms likeadvU/advV. See its own docstring — in short:(boundary mask codes are model/file-specific — check the “values=” list the tool prints per mask; this NSe setup’spython nc_nan_scan.py some_output.nc --vars advU,advV \ --exclude-boundary-vars advU,advV --boundary-value 3,4masku/maskvuse 3 and 4 for boundary orientation, not the GETM-textbook single code 2, which only the T-maskmasktuses here.)
3. Known copies of nse_bdy_lonlat.txt — resolved 2026-08-17
As of 2026-08-03 there were four divergent copies of this file across three
repos (NSe/, the oceanicu_3d repo root, boundaries (now
boundaries.old), and ocean-prep), with most configs pointing at the
wrong one — this section used to document that mess in detail. It’s now
resolved:
- Canonical, single copy:
NSe/Bathymetry/nse_bdy_lonlat.txt(moved fromNSe/nse_bdy_lonlat.txt; still verified againstgetm-dump.nc, 0 points different). - Every config (
NSe/config/{nse_bdy_create,nse_init_create,nse_tidal_bdy}.yaml, andboundaries.old/config/{nse_bdy_create,nse_init_create}.yaml) references this one copy directly — a real absolute path in every case exceptNSe/config/nse_tidal_bdy.yaml, which uses a relative path resolved againstcwd=NSe/(see stage 2b).nse_bdy_create.yamlandnse_init_create.yamlmoved here fromocean-prep/config/on 2026-08-24 (NSe-specific configs belong with the NSe setup, not inside the genericocean-preptool repo). No symlinks anywhere — deliberate: this file changes per domain setup, not just NSe, so a shared symlink target wouldn’t generalize. - The old
oceanicu_3d/nse_bdy_lonlat.txt(repo-root, 309-point mis-landed edit — see §0’s “Update, 2026-08-17” note) is archived atNSe/Bathymetry/nse_bdy_lonlat.txt.mislanded_edit_20260803, not left at the repo root where it could be mistaken for a live copy again.
If this ever drifts again: grep -rln nse_bdy_lonlat across
OceanICU/oceanicu_3d, ocean-prep, and boundaries.old to find every
reference, then verify each with ls -la (should show a real file at
NSe/Bathymetry/nse_bdy_lonlat.txt and nothing else with that basename
outside .bak*/archived copies) before trusting any of them.
NSe
North Sea/eastern Atlantic domain setup. This file is the entry point for “what do I run to (re)generate the inputs” — each topic below links to a deeper doc where one already exists. Expected to grow as more of the setup gets documented (river discharge, meteo, running the model itself, …).
Bathymetry
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe
./generate_nse_bathymetry.py # full run (fixes applied by default), writes Bathymetry/bathymetry_nse.nc
./generate_nse_bathymetry.py --dryrun # print resolved config, write nothing
./generate_nse_bathymetry.py --no-fixes # skip --fixes-file/--accept-fixes
./generate_nse_bathymetry.py --extract-only # (re)run the ncks GEBCO extraction step only
Behaviour (coarse source, depth/mask variable names, fixes:,
mask_regions:, thalweg: waypoints, smooth.local_filters,
smooth.local_rx0) is controlled by NSe/config/nse_bathymetry.yaml — edit
that and re-run, no flags needed for config changes. Overwrites
bathymetry_nse.nc in place, no automatic backup — copy it first if you
want to diff against the previous version.
After any regeneration, re-verify NSe/Bathymetry/nse_bdy_lonlat.txt
against the new mask — a bathymetry fix can silently put boundary points on
land with no error anywhere else. See
BOUNDARY_GENERATION.md’s “§0 — read this first”
for the check to run and why it matters.
Runs fine on either orca or bb-server1 — no external data
dependency beyond the local GEBCO source and ocean-prep’s bathymetry
tooling.
Boundaries
Run on bb-server1, not orca (2026-08-24): the CMEMS/CMIP6/TPXO9
download caches these tools need (/data/cache/cmems, /data/cache/cmip6,
/data/cache/tpxo9, plus the /data/CMIP6 and /data/TPXO9 source
archives) live on bb-server1, not orca — orca’s copies were migrated over
and removed. All output.base_directory paths below are bb-server1
absolute paths (/data/OceanICU/oceanicu_3d/data/NSe/...) accordingly.
Full runbook: BOUNDARY_GENERATION.md (point-file
provenance, the three-stage pipeline, post-generation sanity checks, known
copies of nse_bdy_lonlat.txt). Short version:
# Stage 1 — historical reference (CMEMS)
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe
run-cmems-boundaries --config config/nse_bdy_create.yaml --dryrun
run-cmems-boundaries --config config/nse_bdy_create.yaml
# Stage 2a — future scenario, temperature/salinity (delta-change)
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe
run-delta-boundaries --config config/nse_delta_bdy.yaml --dryrun
run-delta-boundaries --config config/nse_delta_bdy.yaml --scenario ssp126 ssp370 ssp585
# Stage 2b — future scenario, SSH/currents (tidal + CMIP6)
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe
run-tidal-boundaries --config config/nse_tidal_bdy.yaml --dryrun
run-tidal-boundaries --config config/nse_tidal_bdy.yaml
Stage 1 must exist before stage 2a can run (2a inherits its boundary-point layout from stage 1’s own NC files). Stage 2b is independent of stage 1.
Initial conditions
Run on bb-server1, not orca — same reason as boundaries above (CMEMS download cache lives there now).
Two source options — pick with --source; AMM7 is the default/primary
(1993–2026-04-30 coverage), AMM15 covers 2024-03-01 onward:
cd /home/kb/source/repos/OceanICU/oceanicu_3d/NSe
download-init-conditions --config config/nse_init_create.yaml --source AMM7
download-init-conditions --config config/nse_init_create.yaml --source AMM15
download-init-conditions --config config/nse_init_create.yaml --source AMM7 --year 2015
download-init-conditions --config config/nse_init_create.yaml --source AMM7 --dryrun
download-init-conditions --config config/nse_init_create.yaml --source AMM7 --check-coverage
Downloads temperature/salinity on the source product’s native grid and
writes 12 monthly snapshots (1st of each month) per variable to
/data/OceanICU/oceanicu_3d/data/NSe/CMEMS/init/ (output.base_directory
in config/nse_init_create.yaml). The NWS product (AMM7/AMM15) is primary and
defines the output grid; the corresponding Baltic product fills the masked
southern Kattegat/Belt Sea halo. flood_fill: true in the config also
writes a {variable}_ff flood-filled variant of each field.
Model config
See config/README.md for how nse_from_oceanicu.yaml
(the pygetm-config-schema domain config actually used to run the model)
relates to nse_model_config.yaml, and
driver/README.md for the driver itself
(--print-config, --dump-python, data-path portability, the TUI editor).
More to follow
River discharge, meteo forcing, and running the model itself aren’t documented here yet.
North Sea Extension (NSe)
Domain Description
The NSe domain is a high-resolution regional configuration covering the northwest European shelf and the southern North Sea, from the English Channel in the south to the Norwegian coast in the north. It extends eastward into the Kattegat and the entrance to the Baltic Sea.
Geographic Coverage
- Latitude: 47.5°N to 62.0°N
- Longitude: 8°W to 13°E
- Resolution: ~1/12° spherical grid (~7–9 km)
- Vertical: 40 generalised vertical coordinate (GVC) levels
Key Features
- 9 open boundary segments, auto-detected from coordinate file (min gap 0.5°); segment 7 (eastern Kattegat) uses Baltic product override throughout because the NWS domain is masked in that region
- Bathymetry from GEBCO 2025 with Beckmann–Haidvogel smoothing (rx0 ≤ 0.2) and manual corrections for Belt Sea and Skagen straits
- Future climate projections use TPXO9 tidal forcing + CMIP6 mean-state correction (zos, uo, vo) plus a 3-D delta-change signal for temperature and salinity
Observation Datasets
| Dataset | Variables | Period | Type |
|---|---|---|---|
| ICES hydrographic database | Temperature, Salinity | 1993– | Cruise CTD / bottle |
| ARGO floats (Ifremer/GDAC) | Temperature, Salinity | 2000– | Autonomous profilers |
| OSTIA / CMEMS SST | Sea surface temperature | 2003– | Level 4 satellite analysis |
| FES2014 / TPXO9 | Tidal constituents | — | Barotropic tidal model |
Observations
ARGO — 2010-2013
Station Map
Observation Density
CRUISE — 2010-2013
Station Map
Observation Density
ICES — 2010-2013
Station Map
Observation Density
PSAL Hovmoller
TEMP Hovmoller
PLATFORM — 2010-2013
Station Map
Observation Density
Cross-experiment Summary
Surface

How Input Files Were Generated
Future ocean boundary conditions use CMIP6 model output as the climate-change signal on top of the historical CMEMS reference (AMM7/AMM15, same for all scenarios).
Scenario Summary
GFDL-ESM4
| Boundary type | SSP1-2.6 | SSP3-7.0 |
|---|---|---|
| Tidal mean state (2-D) | zos uo/vo absent from ESGF — tidal only | zos, uo, vo |
| 3-D delta-change | thetao, so uo/vo absent from ESGF | — |
MPI-ESM1-2-HR
| Boundary type | SSP3-7.0 |
|---|---|
| 3-D delta-change | thetao, so, uo, vo |
Bathymetry
Bathymetry
Regridded conservatively from GEBCO 2025 onto the NSe ~1/12° spherical grid using bathymetry-regrid (ocean-prep), with Beckmann-Haidvogel smoothing (rx0 ≤ 0.2) and manual depth fixes for Belt Sea straits and Skagen. Output: bathymetry_nse.nc with variables lon, lat, mask, H.
Config not yet committed — will be added once NSe grid parameters are finalised.
bathymetry-regrid --config config/nse_bathy_create.yaml
Boundary Conditions
CMEMS historical boundaries (T, S, SSH, currents)
AMM7 NWS MY reanalysis (1993–2026-04-30) blended with AMM15 NWS ANFC (2026-05-01 onwards). Segment 7 (eastern Kattegat) overridden with Baltic products for all variables; SSH uses Baltic detided product from 2022-12-02. Processed with ocean-prep run_cmems_boundaries.
python cli/run_cmems_boundaries.py --config config/nse_bdy_create.yaml
Tidal boundaries (TPXO9 + CMIP6 mean SSH / barotropic transport)
TPXO9-atlas (13 constituents) tidal prediction for zos, uo, vo combined with CMIP6 monthly mean sea-surface height and depth-integrated barotropic transport. Scenario-specific; see Scenario Summary table below.
run-tidal-boundaries --config config/nse_tidal_bdy.yaml
3-D delta-change boundaries (T, S and optionally currents)
Future 3-D boundary conditions via the delta-change method: AMM7/AMM15 historical CMEMS reference cycled over the output period, with a CMIP6 monthly change signal (future 20-yr climatology minus historical 1985–2014 climatology) added at each boundary point. Scenario-specific; see Scenario Summary table below.
NSe delta-change boundary config
run-delta-boundaries --config config/nse_delta_bdy.yaml --scenario ssp126
Initial Conditions
Initial conditions (temperature, salinity)
Monthly snapshots (1st of each month) from CMEMS NWS products — AMM7 MY reanalysis or AMM15 ANFC — with the Baltic ANFC product filling the southern Kattegat masked halo. Flood-fill propagates valid values into any remaining NaN cells on the model grid.
python cli/download_init_conditions.py --config config/nse_init_create.yaml --source AMM7 --year 2015