API reference#

Reading data#

xhycom.open_dataset(path, grid=None, endian='big', chunks=None, variables=None, postprocess=False)[source]#

Open a HYCOM .ab file pair as an xr.Dataset.

Automatically detects the file type (archive, grid, or bathymetry) from the .b header, so the same function works for all HYCOM output files.

If path is a glob pattern or directory, it is forwarded to open_mfdataset() automatically.

Parameters:
  • path (str) – Path to the file. The .a / .b extension is optional. Glob patterns (*, ?, [) and directory paths are forwarded to open_mfdataset().

  • grid (str or xr.Dataset, optional) –

    Path to regional.grid (without extension), or a Dataset already returned by a previous open_dataset call on a grid file.

    • For archive files: attaches lon / lat as non-dimension coordinates on every variable.

    • For bathymetry files: required (grid dimensions and coordinates are not stored in the bathymetry file itself).

    • For grid files: ignored.

  • endian (str) – Byte order: "big" (default), "little", or "native".

  • chunks (int, dict, or "auto", optional) – If provided, the returned Dataset is chunked with Dask. Passed directly to ds.chunk(). Example: chunks={"k": 1} to chunk one layer at a time.

  • postprocess (bool) – If True, convert native units to physical ones and add derived fields via xhycom.postprocess() — e.g. sea-surface height and layer thicknesses in metres, plus area / landmask. Default False (data are returned exactly as stored on disk).

  • variables (list[str] | None)

Returns:

Contents depend on file type:

Archive (archv.YYYY_DDD_HH)
  • time dimension of size 1.

  • 2-D fields on (time, y, x).

  • Layered fields on (time, k, y, x) with k (layer index, 1-based) and dens (target sigma-2 density) coordinates.

  • Global attributes iversn, iexpt, yrflag.

Grid (regional.grid)
  • All 19 grid variables on (y, x): plon, plat, ulon, ulat, vlon, vlat, qlon, qlat, pang, scpx, scpy, scqx, scqy, scux, scuy, scvx, scvy, cori, pasp.

Bathymetry (depth_*)
  • Single depth variable (metres) on (y, x).

lon / lat non-dimension coordinates are attached to every variable when grid is supplied (archive and bathymetry files).

Return type:

xr.Dataset

Raises:

ValueError – If the file type cannot be detected, or if grid is not provided for a bathymetry file.

Examples

Open the grid:

>>> grid = xhycom.open_dataset("topo/regional.grid")

Open the bathymetry (grid required for dimensions and coordinates):

>>> bathy = xhycom.open_dataset("topo/depth_TP2a0.10_04",
...                             grid="topo/regional.grid")

Open a single archive snapshot with grid coordinates:

>>> ds = xhycom.open_dataset("data/archv.2020_001_00",
...                          grid="topo/regional.grid")

Re-use a pre-loaded grid Dataset to avoid reading the file twice:

>>> grid = xhycom.open_dataset("topo/regional.grid")
>>> bathy = xhycom.open_dataset("topo/depth_TP2a0.10_04", grid=grid)
>>> ds    = xhycom.open_dataset("data/archv.2020_001_00", grid=grid)
xhycom.open_mfdataset(paths, grid=None, endian='big', skip_errors=False, chunks=None, variables=None, postprocess=False)[source]#

Open multiple HYCOM archive .ab file pairs as a single xr.Dataset.

Snapshots are concatenated along a time dimension in chronological order.

Parameters:
  • paths (str or list of str) –

    One of:

    • A directory path — all archv. / archm.YYYY_DDD_HH.[ab] pairs found inside are used.

    • A glob pattern such as "data/archm.1993_*.a".

    • An explicit list of archive basenames or filenames.

  • grid (str or xr.Dataset, optional) – Grid file path or pre-loaded Dataset. Loaded once and shared across all files.

  • endian (str) – Byte order.

  • skip_errors (bool) – If True, files that fail to open are skipped with a warning rather than raising an exception. Default False.

  • chunks (int, dict, or "auto", optional) – If provided, the returned Dataset is chunked with Dask. Passed directly to ds.chunk(). Example: chunks={"time": 1}.

  • variables (list of str, optional) – If provided, only these variables are included in the returned Dataset. Reduces the Dask graph size proportionally — useful when working with large archives that contain many variables (e.g. BGC runs). Variables not present in the archive are skipped with a warning.

  • postprocess (bool) – If True, apply xhycom.postprocess() to the combined Dataset (native-unit conversions + derived fields). Default False.

Returns:

Combined Dataset with a time dimension spanning all snapshots.

Return type:

xr.Dataset

Examples

Open all snapshots in a directory:

>>> ds = xhycom.open_mfdataset("data/", grid="topo/regional.grid")

Open a subset using a glob:

>>> ds = xhycom.open_mfdataset("data/archv.2020_*.a",
...                            grid="topo/regional.grid")

Compute time-mean surface salinity:

>>> ds["saln"].isel(k=0).mean("time").plot(x="lon", y="lat")

Post-processing#

Convert native HYCOM units to physical ones and add derived fields. Applied automatically with open_dataset(..., postprocess=True), or called directly on an existing Dataset.

xhycom.postprocess(ds)[source]#

Return a copy of ds with native units converted and derived fields added.

Idempotent-ish: a field already carrying units='m' is not re-scaled, and derived fields are not recomputed if already present.

Parameters:

ds (xr.Dataset) – A Dataset from xhycom.open_dataset() / open_mfdataset (archive, grid, or bathymetry).

Returns:

New Dataset; lazy/Dask-backed inputs stay lazy.

Return type:

xr.Dataset

Regridding#

Map HYCOM output onto a regular lon/lat/depth grid (e.g. for comparison with GLORYS). See the Regridding notebook. The vertical step needs only xgcm (pip); the lateral step also needs xesmf (conda — see ci/environment-regrid.yml).

xhycom.regrid(ds, lon=None, lat=None, depth=None, grid=None, target=None, method='conservative', z_method='conservative', periodic=False, mask_edges=True, apply_target_mask=True, subset_target=True, weights=None, nan_pole=True, order='horizontal_first', variables=None)[source]#

Regrid HYCOM output to a regular lon/lat/depth grid (lateral + vertical).

Chains regrid_horizontal() and regrid_vertical(); order picks which runs first, and the two are not equivalent near topography and fronts:

  • "horizontal_first" (default) blends neighbouring cells within each hybrid layer before collapsing to depth levels. In HYCOM’s stratified interior the layers are isopycnals, so this mixes water of the same density — it is along-isopycnal and preserves water masses and the T–S relationship, the way tracers actually mix.

  • "vertical_first" puts every native column on the depth levels first — honouring its own bathymetry and giving better coverage on shelves and slopes — then blends horizontally at constant depth. Where isopycnals tilt this mixes across density surfaces, which can smear water masses, so prefer it when geometric/bathymetric fidelity to a z-level product matters more than water-mass integrity.

Both orders conserve the global integral (each step is conservative); the difference is local fidelity.

Parameters:
  • ds (xr.Dataset) – HYCOM Dataset opened with a grid= (so lon / lat exist).

  • lon (array-like, optional) – Target 1-D longitudes / latitudes (degrees). Omit when target is given.

  • lat (array-like, optional) – Target 1-D longitudes / latitudes (degrees). Omit when target is given.

  • depth (array-like, optional) – Target 1-D depths (metres, positive down). Omit when target supplies depth.

  • grid (xr.Dataset or str, optional) – Grid Dataset, or a path to regional.grid (same as open_dataset’s grid=), needed to rotate velocities (supplies pang) and to build source cell bounds for conservative regridding (qlon/qlat).

  • target (xr.Dataset or str, optional) – A regular target grid (e.g. GLORYS), or a path to one, providing longitude / latitude / depth — supplied instead of lon / lat / depth. Its mask (1 = sea), if present, is applied to the final 3-D output unless apply_target_mask is False.

  • method (str) – Horizontal interpolation method (xESMF). Default "conservative" (requires grid= for source cell corners).

  • z_method (str) – Vertical interpolation method (xgcm). Default "conservative".

  • periodic (bool) – Source grid periodic in longitude. Default False.

  • mask_edges (bool) – Mask target depths outside the source column range. Default True.

  • apply_target_mask (bool) – If True (default) and target carries a mask, apply it to the output.

  • subset_target (bool) – If True (default) and lon/lat are taken from target, trim the target grid to the source’s bounding box (plus a small pad) before regridding — the main speed-up for a regional source over a global target (e.g. TOPAZ2 → GLORYS). Ignored when explicit lon / lat are passed.

  • weights (str, path-like, or bool, optional) – Cache the xESMF remap weights so they are built once per (source grid, target grid, method) and reused across files. True auto-keys a file by grid geometry under $XHYCOM_CACHE_DIR; a path names an explicit file; None (default) disables caching. See regrid_horizontal().

  • nan_pole (bool) – If True (default), set the exact geographic-pole rows (|lat| = 90) to NaN — singular on a regular lat/lon grid and unused by products like GLORYS. The row is kept, so the grid is unchanged and stays aligned with the target for differencing. Set False to keep the raw remapped pole value.

  • order ({"horizontal_first", "vertical_first"}) – Which step runs first (see above). Default "horizontal_first" (along-isopycnal, water-mass preserving).

  • variables (list of str, optional) – Restrict the vertical step to these layered variables.

Returns:

Dataset on dims (time, depth, lat, lon).

Return type:

xr.Dataset

xhycom.regrid_horizontal(ds, lon=None, lat=None, grid=None, target=None, method='conservative', periodic=False, mask_var=None, apply_target_mask=True, subset_target=True, weights=None, nan_pole=True)[source]#

Regrid a HYCOM Dataset from its curvilinear grid to a regular lon/lat grid.

Velocities (if present) are first de-staggered to T-points and rotated to true east/north (requires the grid angle pang); everything is then interpolated with a single T-grid xESMF regridder.

Parameters:
  • ds (xr.Dataset) – Dataset from xhycom.open_dataset() / open_mfdataset, opened with a grid= so that lon / lat 2-D coords are attached.

  • lon (array-like, optional) – 1-D target longitudes and latitudes (degrees). Omit when target is given.

  • lat (array-like, optional) – 1-D target longitudes and latitudes (degrees). Omit when target is given.

  • grid (xr.Dataset or str, optional) – Grid Dataset (from open_dataset on regional.grid), or a path to regional.grid (same as open_dataset’s grid=). Required to rotate velocities — it supplies pang — and to build source cell bounds for conservative regridding (it supplies qlon / qlat). If ds already carries a pang coordinate, this may be omitted.

  • target (xr.Dataset or str, optional) – A regular target grid (e.g. GLORYS), or a path to one, providing longitude / latitude (and, when used via regrid(), depth). Supplied instead of lon / lat. If it carries a mask variable (1 = sea), land points are set to NaN in the output unless apply_target_mask is False.

  • method (str) – xESMF interpolation method ("conservative", "bilinear", "patch", …). Default "conservative", which requires cell bounds (source bounds come from the grid’s qlon / qlat — so grid= must be passed — and target bounds are built from the regular target spacing) and thickness-weights layered fields so that the layer volume content field * thickness is conserved.

  • periodic (bool) – Whether the source grid is periodic in longitude. Default False.

  • mask_var (str, optional) – Name of the variable used to derive the source land/sea mask. By default the first available of temp / thknss is used (finite = ocean).

  • apply_target_mask (bool) – If True (default) and target carries a mask, apply it to the output.

  • subset_target (bool) – If True (default) and the target lon/lat are derived from target, trim the target to the source’s bounding box (plus a small pad) before building the regridder. A regional source over a global target (e.g. TOPAZ2 → GLORYS) otherwise pays to remap every global cell, almost all of which receive no data. No effect on the result inside the covered region; ignored when explicit lon / lat are passed.

  • weights (str, path-like, or bool, optional) – Cache for the xESMF remap weights, which are the slow part of a remap and depend only on the two grids and method — not on the field or time. True keys an auto-named file by source/target geometry (the grid idm/jdm + lon/lat extent, the GLORYS shape/extent) under $XHYCOM_CACHE_DIR (default ~/.cache/xhycom/regrid_weights), so TP0/TP2/TP5 × target × method each get their own and are reused across files. A path names an explicit file (reused if it exists, else created). None (default) disables caching.

  • nan_pole (bool) – If True (default), set the exact geographic-pole rows (|lat| = 90) to NaN. A regular lat/lon grid is singular there — a remap deposits a single, meaningless value — and products like GLORYS carry no data at 90 N (yet still mark it sea in their mask, so masking alone won’t remove it). The row is kept (not dropped), so the grid is unchanged and stays aligned with the target for a like-for-like difference. Set False to keep the raw remapped pole value.

  • on (Accepts a field either on hybrid layers (with thknss) or already)

  • dimension (fixed depth levels (a depth)

  • output (no thknss — e.g. the)

Return type:

Dataset

:param of regrid_vertical()). In the latter case the static 2-D land mask is: :param skipped and NaN source cells are dropped per level: :param so depth-varying: :param bathymetry is honoured during the lateral remap.:

Returns:

Dataset with 1-D lon / lat dimension coordinates, on dims (time, k, lat, lon) for hybrid-layer input (thknss retained for a subsequent vertical step) or (time, depth, lat, lon) for depth-level input.

Return type:

xr.Dataset

Parameters:
xhycom.regrid_vertical(ds, depth, method='conservative', mask_edges=True, layer_dim='k', variables=None)[source]#

Regrid HYCOM layered variables onto fixed depth levels.

Layer-centre depths are reconstructed from thknss (Pa -> m via thknss / 9806, cumulative sum to interfaces, minus half-thickness to centres) and used as the source coordinate for an xgcm vertical transform onto the requested depth levels.

Parameters:
  • ds (xr.Dataset) – Dataset containing thknss and one or more variables on layer_dim. May be on the native or a regular horizontal grid.

  • depth (array-like) – 1-D target depths in metres, positive down (e.g. GLORYS levels).

  • method (str) – xgcm transform method. "conservative" (default) conserves the depth-integral of each field: it builds depth bins centred on depth and returns the thickness-weighted layer mean in each. "linear" instead interpolates each field onto depth from the layer centres. Either way the output lands on the depth levels.

  • mask_edges (bool) – If True, target depths outside the source column range are NaN.

  • layer_dim (str) – Name of the HYCOM layer dimension. Default "k".

  • variables (list of str, optional) – Which layered variables to regrid. Default: all variables that have layer_dim (except thknss itself).

Returns:

Dataset with layer_dim replaced by a depth dimension coordinate (positive='down'). 2-D fields are carried through unchanged.

Return type:

xr.Dataset

xhycom.regrid_to_hycom(product, grid, *, method='bilinear', periodic=False, like=None, weights=None)[source]#

Regrid a regular lon/lat product onto the HYCOM curvilinear (y, x) grid.

The lateral inverse of regrid_horizontal(): a regular product such as GLORYS is interpolated onto HYCOM’s native curvilinear grid, so it can be compared with the model in the model’s own space. This is the natural direction when the model grid is coarser than the product (regridding HYCOM up onto a finer product mostly interpolates, adding no information).

Only the horizontal grid is changed: fields keep their own vertical coordinate (depth). All fields are treated as scalars — vector components (e.g. GLORYS uo/vo) are interpolated as-is and stay on geographic (east/north) axes; they are not rotated onto the model axes or re-staggered to the C-grid.

Parameters:
  • product (xr.Dataset, xr.DataArray, or str) – Regular lon/lat[/depth] source (longitude/latitude or lon/lat), or a path to one.

  • grid (xr.Dataset or str) – HYCOM grid (regional.grid path or a Dataset from xhycom.open_dataset()). Supplies the target points plon/plat and, for conservative remapping, the cell corners qlon/qlat.

  • method (str) – xESMF method. Default "bilinear" (point interpolation of a coarser product onto a finer grid). "conservative" additionally needs the grid corners qlon/qlat.

  • periodic (bool) – Whether the product is periodic in longitude (e.g. a global grid). Default False.

  • like (xr.Dataset, optional) – A HYCOM field on the same (y, x) grid; its land/sea mask (finite = ocean, via the first of temp/thknss) is applied to the output so product values are not carried onto HYCOM land.

  • weights (str, path-like, or bool, optional) – Cache for the remap weights, as in regrid_horizontal(). True keys an auto-named file by product/HYCOM geometry under $XHYCOM_CACHE_DIR; a path names an explicit file; None (default) disables caching.

Returns:

Product fields on HYCOM dims (..., y, x) with 2-D lon/lat coordinates, lined up with a HYCOM Dataset for a like-for-like difference.

Return type:

xr.Dataset

xhycom.velocities_east_north(ds, grid=None)[source]#

De-stagger HYCOM C-grid velocities to T-points and rotate to true east/north.

HYCOM stores velocities on a staggered Arakawa C-grid with components along the model grid axes (u-vel. along x, v-vel. along y). On a curvilinear grid those axes are not east/north — they rotate across the domain (sharply near the grid’s poles). This averages each (u, v) pair onto the cell centre (T-point) and rotates the components onto the geographic axes using the grid angle pang:

east  = u * cos(pang) - v * sin(pang)
north = u * sin(pang) + v * cos(pang)

Unlike regrid_horizontal(), the native curvilinear grid is kept — only the velocity components are de-staggered and rotated. This is the piece needed to compare model velocities against a regular product brought onto the HYCOM grid by regrid_to_hycom(): that function interpolates the product’s velocities (e.g. GLORYS uo/vo) as scalars, so they stay on geographic east/north axes. Rotating the model side here puts both on the same axes on the same (y, x) grid, so they difference directly.

Parameters:
  • ds (xr.Dataset) – HYCOM Dataset that may contain one or more (u, v) pairs (u-vel. / v-vel., u_btrop / v_btrop, umix / vmix, si_u / si_v). A Dataset with no velocity pair is returned unchanged.

  • grid (xr.Dataset or str, optional) – HYCOM grid (regional.grid path or a Dataset from xhycom.open_dataset()) supplying the rotation angle pang. May be omitted if ds already carries a pang coordinate.

Returns:

Copy of ds with each velocity pair de-staggered to the T-points and rotated to true eastward / northward, re-homed onto the T-point lon / lat coordinates and keeping the HYCOM names. Each component’s standard_name becomes eastward / northward_sea_water_velocity.

Return type:

xr.Dataset

Notes

The de-stagger averages each edge value with its neighbour, so the last column (for u) and last row (for v) — which have no neighbour — become NaN boundary cells, exactly as inside regrid_horizontal().

Internal utilities#

These are not part of the public API but are documented here for contributors.

File discovery#

xhycom._discovery.find_archv_files(path)[source]#

Find HYCOM archive .ab file pairs and return sorted basenames.

Scans path for files whose names match archv.YYYY_DDD_HH or archv.YYYY_DDD. The argument can be a directory or a glob pattern.

Parameters:

path (str) – A directory path or a glob pattern (e.g. "data/archv.2020_*.a").

Returns:

Sorted list of basenames without the .a / .b extension, ordered chronologically by (year, day-of-year, hour).

Return type:

list of str

Raises:

Examples

>>> files = find_archv_files("data/")
>>> files[0]
'data/archv.2020_001_00'

Time conversion#

xhycom._time.model_day_to_datetime(model_day, yrflag)[source]#

Convert a HYCOM model day (float) to a cftime.datetime object.

HYCOM stores time as a single floating-point “model day” whose meaning depends on yrflag (see HYCOM blkdat documentation).

Parameters:
  • model_day (float) – HYCOM model day as stored in the archive .b header.

  • yrflag (int) – HYCOM year-flag (0-5). Read from the .b header automatically when using xhycom.open_dataset().

Returns:

Absolute date in the calendar implied by yrflag.

Return type:

cftime.datetime

Raises:

ValueError – If yrflag is not one of the supported values (0-5).

Notes

The mapping from yrflag to CF calendar name is:

yrflag

Calendar

Epoch

0 1 2 3 4 5

360_day 366_day 366_day standard 365_day 365_day

Jan 16, year 1 Jan 16, year 1 Jan 1, year 1 Jan 1, 1901 Jan 1, year 1 Jan 1, 1901

Examples

>>> model_day_to_datetime(40909.5, yrflag=3)
cftime.datetime(2013, 1, 1, 12, 0, 0, 0, has_year_zero=False)