API reference#
Reading data#
- xhycom.open_dataset(path, grid=None, endian='big', chunks=None, variables=None, postprocess=False)[source]#
Open a HYCOM
.abfile pair as anxr.Dataset.Automatically detects the file type (archive, grid, or bathymetry) from the
.bheader, 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/.bextension is optional. Glob patterns (*,?,[) and directory paths are forwarded toopen_mfdataset().grid (str or xr.Dataset, optional) –
Path to
regional.grid(without extension), or a Dataset already returned by a previousopen_datasetcall on a grid file.For archive files: attaches
lon/latas 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 viaxhycom.postprocess()— e.g. sea-surface height and layer thicknesses in metres, plusarea/landmask. DefaultFalse(data are returned exactly as stored on disk).
- Returns:
Contents depend on file type:
- Archive (
archv.YYYY_DDD_HH) timedimension of size 1.2-D fields on
(time, y, x).Layered fields on
(time, k, y, x)withk(layer index, 1-based) anddens(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
depthvariable (metres) on(y, x).
lon/latnon-dimension coordinates are attached to every variable when grid is supplied (archive and bathymetry files).- Archive (
- 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
.abfile pairs as a singlexr.Dataset.Snapshots are concatenated along a
timedimension in chronological order.- Parameters:
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. DefaultFalse.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, applyxhycom.postprocess()to the combined Dataset (native-unit conversions + derived fields). DefaultFalse.
- Returns:
Combined Dataset with a
timedimension 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()andregrid_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=(solon/latexist).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 asopen_dataset’sgrid=), needed to rotate velocities (suppliespang) 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. Itsmask(1 = sea), if present, is applied to the final 3-D output unless apply_target_mask isFalse.method (str) – Horizontal interpolation method (xESMF). Default
"conservative"(requiresgrid=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 amask, 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.
Trueauto-keys a file by grid geometry under$XHYCOM_CACHE_DIR; a path names an explicit file;None(default) disables caching. Seeregrid_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. SetFalseto 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 agrid=so thatlon/lat2-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_datasetonregional.grid), or a path toregional.grid(same asopen_dataset’sgrid=). Required to rotate velocities — it suppliespang— and to build source cell bounds for conservative regridding (it suppliesqlon/qlat). Ifdsalready carries apangcoordinate, 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 viaregrid(),depth). Supplied instead of lon / lat. If it carries amaskvariable (1 = sea), land points are set to NaN in the output unless apply_target_mask isFalse.method (str) – xESMF interpolation method (
"conservative","bilinear","patch", …). Default"conservative", which requires cell bounds (source bounds come from the grid’sqlon/qlat— sogrid=must be passed — and target bounds are built from the regular target spacing) and thickness-weights layered fields so that the layer volume contentfield * thicknessis 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/thknssis used (finite = ocean).apply_target_mask (bool) – If
True(default) and target carries amask, 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.
Truekeys an auto-named file by source/target geometry (the grididm/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. SetFalseto 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:
: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/latdimension coordinates, on dims(time, k, lat, lon)for hybrid-layer input (thknssretained for a subsequent vertical step) or(time, depth, lat, lon)for depth-level input.- Return type:
xr.Dataset
- Parameters:
ds (Dataset)
lon (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str] | None)
lat (_Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str] | None)
method (str)
periodic (bool)
mask_var (str | None)
apply_target_mask (bool)
subset_target (bool)
nan_pole (bool)
- 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 viathknss / 9806, cumulative sum to interfaces, minus half-thickness to centres) and used as the source coordinate for anxgcmvertical transform onto the requesteddepthlevels.- Parameters:
ds (xr.Dataset) – Dataset containing
thknssand one or more variables onlayer_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(exceptthknssitself).
- Returns:
Dataset with
layer_dimreplaced by adepthdimension 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. GLORYSuo/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/latitudeorlon/lat), or a path to one.grid (xr.Dataset or str) – HYCOM grid (
regional.gridpath or a Dataset fromxhycom.open_dataset()). Supplies the target pointsplon/platand, for conservative remapping, the cell cornersqlon/qlat.method (str) – xESMF method. Default
"bilinear"(point interpolation of a coarser product onto a finer grid)."conservative"additionally needs the grid cornersqlon/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 oftemp/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().Truekeys 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-Dlon/latcoordinates, 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 anglepang: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 byregrid_to_hycom(): that function interpolates the product’s velocities (e.g. GLORYSuo/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.gridpath or a Dataset fromxhycom.open_dataset()) supplying the rotation anglepang. May be omitted ifdsalready carries apangcoordinate.
- 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/latcoordinates and keeping the HYCOM names. Each component’sstandard_namebecomeseastward/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 (forv) — which have no neighbour — become NaN boundary cells, exactly as insideregrid_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
.abfile pairs and return sorted basenames.Scans path for files whose names match
archv.YYYY_DDD_HHorarchv.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/.bextension, ordered chronologically by (year, day-of-year, hour).- Return type:
- Raises:
FileNotFoundError – If path is a non-existent directory.
ValueError – If no matching archive file pairs are found.
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.datetimeobject.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
.bheader.yrflag (int) – HYCOM year-flag (0-5). Read from the
.bheader automatically when usingxhycom.open_dataset().
- Returns:
Absolute date in the calendar implied by
yrflag.- Return type:
- Raises:
ValueError – If
yrflagis not one of the supported values (0-5).
Notes
The mapping from
yrflagto 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)