Oceananigans-style geometry construction (docs/ocean_python_api_plan.md
S5b, 06_python_surface_design.md D6.2) hands bathymetry to the
library as an interior-sized array instead of a topo_config formula
or a NetCDF file. Two pieces live here, deliberately in a NetCDF-FREE
module (unlike rdb_bathymetry, which requires
RDB_ENABLE_NETCDF=ON purely because it also happens to contain the
NetCDF reader): sign normalisation/validation, and ghost fill. Array
injection is explicitly meant to need no filesystem/NetCDF at all — a
caller building a grid in memory and handing it straight to create()
must not be forced onto a NetCDF build just to get its ghosts filled.
Sign is the single most dangerous argument in this API (D6.2).
Roundabout’s %barotropic%b is a POSITIVE-DOWN depth (a 4000 m-deep cell
is +4000); GEBCO/ETOPO/Oceananigans GridFittedBottom ship a
NEGATIVE-DOWN height (the same cell is -4000). Passing a GEBCO array
straight through un-flipped makes every cell read as land (b < 2.0 =
LAND_DEPTH_THRESHOLD), producing a clean, crash-free, entirely-wrong
quiescent run — and a quiescent uniform-rho zero-velocity run is this
project’s OWN correctness check (memory quiescent-ic), so the sign
error would look like a passing validation. The guard:
1. convention is REQUIRED — no default (bathymetry_normalise_sign
has no default branch; an unrecognised value is
OCEAN_STATUS_ERR_SETUP, not a silent fall-through).
2. after normalising to positive-down, the WET FRACTION is checked
(b > LAND_DEPTH_THRESHOLD) — zero wet cells is
OCEAN_STATUS_ERR_BATHYMETRY_SIGN, naming the array’s median and
the convention that was asked for.
3. this is NOT a “no negatives” test: b < 0 is legal under
wet/dry (rdb_config.F90 &ocean_wetdry_nml), so the check is on
the wet fraction, never on the presence of a negative value.
Ghost fill (bathymetry_fill_ghosts_array) mirrors
rdb_bathymetry::fill_bathymetry_ghosts_array (constant extrapolation
from the nearest interior cell) — CLAUDE.md’s formula-bathymetry
ghost-fill gotcha: an unfilled ghost row leaves b=0 there, the EOS
falls back to rho=rho_0, and the spurious density jump at the
wall-adjacent face e-folds the basin in ~12 h. Deliberately
duplicated (not used from rdb_bathymetry) rather than pulled in
through a NetCDF-gated module — see the module docstring above.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | public, | parameter | :: | BATHY_CONVENTION_DEPTH_POSITIVE_DOWN | = | 1 |
Caller’s array already matches Roundabout’s internal convention: a
4000 m-deep cell is |
| integer, | public, | parameter | :: | BATHY_CONVENTION_HEIGHT_POSITIVE_UP | = | 2 |
Caller’s array is a GEBCO/ETOPO/Oceananigans-style height: a
4000 m-deep cell is |
Approximate median via a full sort — b is a setup-time array
(called once per create(), never per-step), so O(n log n) is
fine; this exists purely to make the sign-error message name a
representative depth rather than minval/maxval (which a single
outlier cell would distort).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | b(:,:) |
Fill ghost-cell bathymetry by constant extrapolation from the
nearest interior cell. Deliberate duplicate of
rdb_bathymetry::fill_bathymetry_ghosts_array — see the module
docstring for why this module cannot use that one.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(inout) | :: | b(:,:) | |||
| type(hgrid_t), | intent(in) | :: | grid |
In-place sign-normalise b (any shape — interior or full array,
the caller decides what it passes) to Roundabout’s positive-down depth
convention, then validate on the NORMALISED array: zero wet cells
(b > LAND_DEPTH_THRESHOLD) is rejected as
OCEAN_STATUS_ERR_BATHYMETRY_SIGN, naming the median depth and the
convention that was requested, so the caller can see at a glance
that the OTHER convention was probably meant. NOT a “no negatives”
check (see module docstring) — a majority-negative but non-empty
wet fraction is accepted (legal under wet/dry).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(inout) | :: | b(:,:) | |||
| integer, | intent(in) | :: | convention |
One of |
||
| integer, | intent(out), | optional | :: | ierr |
Non-zero ( |
Plain insertion sort. a is at most a few hundred cells in every
realistic setup-time call (and correctness, not speed, is what
matters for a diagnostic median) — no need for anything fancier.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(inout) | :: | a(:) |