rdb_ocean_bathymetry_inject Module

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.


Uses

  • module~~rdb_ocean_bathymetry_inject~~UsesGraph module~rdb_ocean_bathymetry_inject rdb_ocean_bathymetry_inject module~rdb_constants rdb_constants module~rdb_ocean_bathymetry_inject->module~rdb_constants module~rdb_error_ring rdb_error_ring module~rdb_ocean_bathymetry_inject->module~rdb_error_ring module~rdb_grid rdb_grid module~rdb_ocean_bathymetry_inject->module~rdb_grid module~rdb_ocean_status rdb_ocean_status module~rdb_ocean_bathymetry_inject->module~rdb_ocean_status pic_strings pic_strings module~rdb_ocean_bathymetry_inject->pic_strings pic_types pic_types module~rdb_constants->pic_types pic_logger pic_logger module~rdb_error_ring->pic_logger module~rdb_grid->module~rdb_constants

Used by

  • module~~rdb_ocean_bathymetry_inject~~UsedByGraph module~rdb_ocean_bathymetry_inject rdb_ocean_bathymetry_inject module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_bathymetry_inject module~rdb_handle rdb_handle module~rdb_ocean_api->module~rdb_handle module~rdb_ocean_diag_derived rdb_ocean_diag_derived module~rdb_ocean_api->module~rdb_ocean_diag_derived module~rdb_ocean_diag_fills rdb_ocean_diag_fills module~rdb_ocean_api->module~rdb_ocean_diag_fills module~rdb_ocean_engine rdb_ocean_engine module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_state rdb_ocean_state module~rdb_ocean_state->module~rdb_ocean_bathymetry_inject module~rdb_driver rdb_driver module~rdb_driver->module~rdb_ocean_state module~rdb_driver->module~rdb_ocean_engine module~rdb_handle->module~rdb_ocean_state module~rdb_handle->module~rdb_ocean_engine module~rdb_ocean_diag_derived->module~rdb_ocean_state module~rdb_ocean_diag_derived->module~rdb_ocean_diag_fills module~rdb_ocean_diag_fills->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_diag_derived module~rdb_ocean_engine->module~rdb_ocean_diag_fills module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_setup module~rdb_ocean_setup->module~rdb_ocean_state

Variables

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 +4000. No sign flip.

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 -4000. Flipped (negated) to positive-down.


Functions

private pure function bathymetry_median(b) result(med)

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).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: b(:,:)

Return Value real(kind=wp)


Subroutines

public subroutine bathymetry_fill_ghosts_array(b, grid)

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.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: b(:,:)
type(hgrid_t), intent(in) :: grid

public subroutine bathymetry_normalise_sign(b, convention, ierr)

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).

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: b(:,:)
integer, intent(in) :: convention

One of BATHY_CONVENTION_DEPTH_POSITIVE_DOWN / _HEIGHT_POSITIVE_UP. No default — any other value is OCEAN_STATUS_ERR_SETUP.

integer, intent(out), optional :: ierr

Non-zero (OCEAN_STATUS_ERR_SETUP on an unrecognised convention, OCEAN_STATUS_ERR_BATHYMETRY_SIGN on a zero-wet-cell array) when present; absent behaves as today (error stop).

private pure subroutine sort_real(a)

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.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: a(:)