Bathymetry Workflow
Insights from Frank Bryan, Fred Castruccio, Gustavo Marques, Ashley Barnes, and others.
Topo.set_from_dataset (see Quickstart Guide) is the flagship function of the Topo class.
It takes a dataset and puts in on the Topo object. It is built out of several
smaller, independently-usable methods on Topo. This page documents each of
them — their arguments and the reasoning behind them — so they can be reached
for individually when the one-call workflow’s defaults aren’t the right fit.
For the regridding math itself (Cressman weights, xESMF), see Regridding & Mapping.
Resolution Diagnosis
Topo.diagnose_resolution(radius=6.378e6) compares the model grid’s median
cell spacing to the source dataset’s pixel spacing and recommends a strategy:
below roughly a 12x ratio, a direct xESMF regrid is recommended; at or above
it, sub-sampling statistics and Cressman interpolation are recommended
instead. The 12x threshold mirrors the criterion tx2_3’s interp_smooth.f90
uses — it’s the point past which a single interpolated value per cell starts
throwing away enough real bathymetric detail that a statistical/mask-aware
approach is worth the extra cost.
Masking Methods
generate_mask_from_stats_ocean_frac(mask_threshold=0.5) derives a land/ocean
mask from the per-cell statistics computed by compute_stats (see below):
a cell is ocean if at least mask_threshold of its sub-sampled points are
below the land cutoff depth. This mirrors tx2_3’s create_model_topo.f90 and
is the better choice when the source data is much finer than the grid,
because it’s a majority vote over many real source points rather than a
single interpolated elevation value.
generate_mask_from_naturalearth(resolution="10", version="v5_1_2") derives
a mask from Natural Earth land polygons (via regionmask) instead of the
source bathymetry’s own land/ocean values. Useful when substats isn’t warranted.
Depth Methods
compute_stats(nx_sub, ny_sub, mask_hmin) sub-samples each grid cell into
nx_sub × ny_sub points, snaps each to the nearest source pixel, and
returns per-cell OCN_FRAC, D_mean, D_min, D_max, and D2_mean.
mask_hmin is the land cutoff depth — typically 0 m for regional domains,
but -1 m for tx2_3, since what counts as “ocean” for masking purposes isn’t
always exactly sea level. This method is a Python port of code Frank Bryan
originally wrote in Fortran for tx2_3; computing all of these statistics in
one sub-sampling pass avoids re-reading the (often very large) source dataset
once per depth/mask decision.
set_depth_from_stats(statistic) sets depth directly from one of the D_*
statistics above ("mean", "min", "max", …) — the cheapest depth
option once statistics are already computed.
direct_cressman_interp(smooth_scl=2.0, cressman_exp=2.0, weights_path=None)
sets depth via mask-aware Cressman distance-weighted interpolation (see
Regridding & Mapping for the weighting formula). Because only source ocean points
contribute to each destination cell, it avoids the coastal land-contamination
that a plain interpolation produces — the main weakness a resolution-ratio
based choice (see above) is meant to correct for.
Cleanup
fill_inland_lakes_and_channels() fills one-cell-wide channels and inland
lakes left behind by either masking method. It takes no arguments and is
generally run as a last step, since masks derived from sub-sampling or land
polygons commonly leave small artifacts like these.
Decision Flow
set_from_dataset exists so the resolution-ratio heuristic above doesn’t
need to be applied by hand every time. Internally, it calls
diagnose_resolution() exactly once and reuses that single result to drive
two separate decisions — one for masking, one for depth — each of which
only falls back to the automatic pick if you didn’t pass that argument
yourself.
Allowed Values
Value |
What it does |
Extra requirements |
|---|---|---|
|
Natural Earth land polygons via |
none |
|
Threshold the sub-sampled ocean fraction from |
|
|
Derive the mask directly from the source dataset’s own raw depth sign |
none |
|
Use a |
|
|
Auto-pick |
— |
Value |
What it does |
Extra requirements |
|---|---|---|
|
Use the |
|
|
Mask-aware Cressman distance-weighted interpolation
( |
none |
|
Direct xESMF regrid of the source dataset ( |
honors |
|
Auto-pick |
— |
Automatic Selection
Only two of each argument’s values are ever chosen automatically, and which one depends on the same resolution-ratio check:
Argument |
Picked automatically when resolution ratio is below 12x (grid comparable to or finer than the source) |
Picked automatically when resolution ratio is 12x or above (source much finer than the grid) |
|---|---|---|
|
|
|
|
|
|
'dataset'/'manual' (mask) and 'stats' (depth) are never picked
automatically — they only run if you ask for them explicitly, since none of
them is a clear default for either resolution regime.
Because masking and depth are decided independently, you can override either
one on its own without touching the other — the two don’t have to move
together. Passing mask_method="naturalearth" on a high-ratio grid still
lets depth auto-pick Cressman; passing depth_method="stats" on a low-ratio
grid still lets the mask auto-pick Natural Earth.
# Force the Natural Earth mask, but still let depth auto-pick Cressman
# on a grid that's much coarser than the source.
topo.set_from_dataset(
bathymetry_path="GEBCO_2023.nc",
longitude_coordinate_name="lon",
latitude_coordinate_name="lat",
vertical_coordinate_name="elevation",
mask_method="naturalearth",
)
mask_method="ocean_frac" and depth_method="stats" both rely on the same
compute_stats call, so nx_sub/ny_sub/mask_hmin (passed through
set_from_dataset’s **kwargs) only need to be given once even if both are
in play. diagnose_resolution’s printout runs on every call regardless of
which methods end up chosen, so you always see the measured ratio and which
path it recommends, even when you’re overriding it. fill_channels=True runs
fill_inland_lakes_and_channels afterward regardless of which mask/depth
combination was used.
Channel Width Constraints
ChannelWidth/ChannelWidthList (see Quickstart Guide) exist alongside
the depth/mask methods above for a different reason: some straits and
channels are narrower than a single grid cell but still need to permit flow.
Rather than distorting the discretized bathymetry to force this, channel
width constraints record an effective width override that’s applied at
runtime, independent of the depth and mask fields.