Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Real-Basin Data Sources: Geometry + Forcing

Authors
Affiliations
University of Valencia

TL;DR. Phase 4 / Phase 5 of the scenario x model refactor (epic #72) needs land masks, bathymetry, and surface forcing for four basins (north_atlantic, med_sea, gulf_stream, southern_ocean). This note pins the decisions so #78 and #79 can proceed:

  1. Bathymetry: GEBCO 2024 (15 arc-sec global grid).

  2. Land mask: derived from GEBCO (depth >= 0).

  3. Forcing: ERA5 monthly-mean surface stresses + net heat flux, averaged over 1993-2023 into an annual-mean climatology. Seasonal cycles are a follow-up.

  4. Format: one Zarr v3 store per basin (data/basin/<name>.zarr) containing mask, bathymetry, tau_x, tau_y, heat, and lon/lat coordinate variables.

  5. Pipeline: a DVC stage per basin (build-basin-<name>) in dvc.yaml, invoking scripts/data/build_basin.py. dvc repro regenerates when source data or the script changes.

  6. Storage: bring-your-own-remote. somax ships the pipeline and CLI helpers; each user points DVC at their own remote (Google Drive, S3, Azure Blob, local disk, etc.). No canonical somax-hosted bundle.

  7. Helpers: somax-sim data {init,fetch,build,status} wraps the DVC flow so users never have to learn DVC’s command surface just to get basin bundles.

  8. Coordinates: Cartesian beta-plane (Mercator-projected) for north_atlantic, med_sea, gulf_stream. Lat/lon native for southern_ocean.

This note closes the “open questions” in #74 (geometry) and #75 (forcing). The loader and per-basin _build() fillers belong to Phase 4 (#78) / Phase 5 (#79); this PR scaffolds the pipeline + helpers and ships a synthetic-data placeholder build so the wiring is exercised end-to-end today.

Why both questions in one note

#74 and #75 were filed separately because geometry and forcing are physically distinct. But the engineering questions they raise — distribution, format, resolution, versioning, licensing — are identical. Answering them separately risks diverging on arbitrary details. One decision, one bundle, one pipeline.

Per-basin canonical grids

Each basin ships at one canonical grid chosen to keep smoke tests fast and production runs tractable on a single GPU:

BasinShapeExtentReason
med_sea128x64~3500 x 1500 km, ~25 km/cellSmallest basin — fast smoke target
gulf_stream256x128~4000 x 2000 km, ~15 km/cellEddy-resolving WBC reference
north_atlantic256x256~7000 x 7000 km, ~25-30 km/cellFull-basin QG reference
southern_ocean360x900-360E, 70S-30S (spherical cap)ACC-relevant; eddy-permitting on sphere

Users who want the canonical bundle invoke somax-sim data build <name>. For a non-default resolution, invoke python scripts/data/build_basin.py <name> --nx <N> --ny <M> directly — the DVC stage and the CLI helper both bake in the canonical shape. The committed pipeline output is one canonical grid per basin.

Bathymetry: GEBCO 2024

Source

GEBCO 2024 is a 15 arc-second (~450 m at the equator) global bathymetry grid. De facto community standard for ocean modeling, freely redistributable with attribution.

Rejected alternatives

Derived mask

The wet/dry mask for each basin is derived from GEBCO at build time: mask = (bathymetry < 0). Natural Earth 10m coastlines are used only as a QC overlay, not as the mask itself. Rationale: a single dataset eliminates mask/bathymetry inconsistency bugs — land cells with finite ocean depth, or vice versa — which are the most common kind of real-basin configuration error.

Minimum depth floor

Shallow shelves (depth < 20 m) are raised to a configurable floor min_depth before regridding to avoid sub-grid-scale topography destabilizing the time step. Default floor is 20 m; --min-depth 0 disables it. The floor is recorded in the Zarr store’s attrs["history"].

Surface forcing: ERA5

Source

ERA5 (ECMWF Reanalysis v5) provides 0.25° x 0.25° global monthly-mean fields from 1940-present. Variables we consume:

ERA5 variableMeaningUnit
metssMean eastward turbulent surface stressN/m²
mntssMean northward turbulent surface stressN/m²
msnlwrfMean surface net longwave radiation fluxW/m²
msnswrfMean surface net shortwave radiation fluxW/m²
mslhfMean surface latent heat fluxW/m²
msshfMean surface sensible heat fluxW/m²

Net heat flux stored in the bundle is heat = msnlwrf + msnswrf + mslhf + msshf (CF sign convention: positive downward into the ocean).

Temporal aggregation

Annual mean over 1993-2023 (31 years). Rationale:

Rejected alternatives

Licensing

ERA5 is distributed under the Copernicus License, which permits redistribution of derived products with attribution. Each basin’s Zarr store carries a source attr citing “Copernicus Climate Change Service (C3S) — ERA5 monthly-mean single levels” and a DOI pointer.

Spatial regridding

Regridding happens once, at build time (inside the DVC stage). Runtime loads the pre-regridded Zarr directly — no regridding dependencies at runtime.

Regridding code lives in scripts/data/build_basin.py (build-time only, not imported at runtime). somax’s runtime deps stay xarray + numpy + jax — no xesmf, no pyproj.

Coordinate handling

Cartesian basins (north_atlantic, med_sea, gulf_stream)

Each basin is projected onto a local Mercator-like Cartesian grid at build time, using a basin-central reference latitude:

The Zarr store records (Lx, Ly) in metres plus lon/lat coordinate variables for every T-cell (for plotting against a map). Geometry.kind == "real_basin" signals the Cartesian beta-plane regime to the compatibility checker.

Spherical basin (southern_ocean)

southern_ocean stays in native lat/lon (spherical cap, 70°S-30°S). Geometry.kind == "spherical_cap". Consumed by Phase 5 (#79) + spherical models (#73).

Distribution: DVC + bring-your-own-remote

Decision

somax does not host a canonical basin bundle. The repo ships:

  1. The pipeline. dvc.yaml stages (build-basin-med-sea, ...) that regenerate each basin from source data.

  2. The build script. scripts/data/build_basin.py — reads GEBCO + ERA5, regrids, writes data/basin/<name>.zarr.

  3. DVC tracking. Each bundle is a DVC output; the .dvc pointer is committed to git; the Zarr store itself is cached by DVC (not in git).

  4. CLI helpers. somax-sim data {init,fetch,build,status} — see below.

The user brings their own remote. First-time setup:

$ somax-sim data init
Pick a DVC remote backend:
  [1] gdrive
  [2] s3
  [3] azure
  [4] gcs
  [5] local
> 1
Google Drive folder URL format: gdrive://<folder-id>
remote URL: gdrive://1a2b3c...
$ somax-sim data fetch med_sea
$ somax-sim data build gulf_stream --push

Install the corresponding DVC extra manually (pip install 'dvc[gdrive]' for gdrive, and similarly for s3 / azure / gs). data init prints an advisory hint but does not install anything on your behalf.

Rejected alternatives

Versioning

No canonical bundle means no canonical versioning. Each user’s remote is independently versioned via DVC’s content-hash mechanism. The build script is versioned in git — reproducing a bundle from a given git commit is deterministic if the user’s source GEBCO/ERA5 inputs are pinned.

File format: Zarr v3, one store per basin

Layout (one .zarr dir per basin under data/basin/)

data/basin/med_sea.zarr/
  .zarray, .zgroup, .zattrs
  lon/         (y, x) float32  degrees_east
  lat/         (y, x) float32  degrees_north
  bathymetry/  (y, x) float32  m, positive downward
  mask/        (y, x) int8     1 = ocean, 0 = land
  tau_x/       (y, x) float32  Pa, eastward
  tau_y/       (y, x) float32  Pa, northward
  heat/        (y, x) float32  W/m^2, positive into ocean

root attrs:
  title: "somax basin bundle: med_sea"
  source: "GEBCO 2024 + ERA5 1993-2023 annual mean"
  history: "built by scripts/data/build_basin.py <commit>"
  conventions: "CF-1.10"
  somax_basin_version: "<semver>"

Rejected alternatives

Synthetic-data placeholder (this PR)

#78 is where real GEBCO/ERA5 integration lands. Until then, build_basin.py runs in a --synthetic mode that constructs:

These are placeholders — the DVC pipeline is real, the Zarr schema is real, the CLI is real. Only the physical content is synthetic. Swapping in real data in #78 is a one-function change inside build_basin.py.

Out of scope (explicit non-goals)

Deferred to future issues:

Summary — answers to the open questions

Cross-referenced to #74 and #75:

QuestionDecisionSection
#74 Q1 — bathymetry sourceGEBCO 2024Bathymetry
#74 Q2 / #75 Q4 — distributionDVC + BYO remoteDistribution
#74 Q3 / #75 Q2 — formatZarr v3, one store per basinFile format
#74 Q4 — resolution policyone canonical grid per basin, rebuildable via CLIPer-basin grids
#74 Q5 — coordinate handlingCartesian beta-plane per basin; spherical for southern_oceanCoordinates
#75 Q1 — forcing sourceERA5 monthly-means, 1993-2023Surface forcing
#75 Q2 — time aggregationannual mean (Phase 4); seasonal deferredSurface forcing
#75 Q3 — spatial regriddingbilinear for stress/heat, conservative for bathymetry, precomputedRegridding
#75 Q5 — stationary vs time-varyingstationary only; time-varying is a follow-upSurface forcing

Real-data loader and per-basin _build() implementations belong to Phase 4 (#78) and Phase 5 (#79).