Skip to main content

hydromend

Learnable lag operators for post-processing hydrodynamic model output against observations.

hydromend turns a co-located pair of a model series (e.g. a GTSM / tide–surge reanalysis point) and an observed series (e.g. a tide gauge) into a small, interpretable lag operator that maps a window of recent model values onto the observation. Operators can be

  • linear — a lag kernel w(τ), or
  • bilinear — a second-order Volterra kernel w(τ₁,τ₂) that captures tide–surge interaction and shallow-water non-linearity.

The default estimator is a variational-Bayes ARD regressor (vb_ard): it sparsifies irrelevant lags and returns coefficient uncertainties. Ordinary least squares, a GAM, and Gaussian-process-prior variants are available as options.

pip install hydromend            # fit + apply
pip install "hydromend[gam]"     # + statsmodels GAM backend

Fit your own operator — train, then test

Give hydromend a per-site DataFrame indexed by time with an observations column and a model column. Build the feature set, hold out a period, fit on the train part with the default VB-ARD estimator, and evaluate on the unseen test part.

import hydromend as hm
import pandas as pd

# --- feature recipe: 0–24 h memory, full bilinear (Volterra) kernel -----------
recipe = dict(lags_hours=range(1, 25), feature_set="bilinear",
              include_current=True, include_squares=True)

X = hm.build_feature_set(site_df, **recipe)
d = pd.concat([X, site_df["observations"]], axis=1).dropna()

# --- temporal train/test split ------------------------------------------------
split = int(len(d) * 0.7)
Xtr, ytr = d.iloc[:split][X.columns], d.iloc[:split]["observations"]
Xte, yte = d.iloc[split:][X.columns], d.iloc[split:]["observations"]

# --- fit (VB-ARD by default) and evaluate on the held-out period --------------
reg  = hm.make_regressor().fit(Xtr, ytr)              # make_regressor("ols"|"gp_volterra"|... to switch
pred = pd.Series(reg.predict(Xte), index=Xte.index)

mae_raw = (yte - site_df["model"].reindex(Xte.index)).abs().mean()
mae_op  = (yte - pred).abs().mean()
print(f"held-out MAE: {mae_raw:.3f} -> {mae_op:.3f} m")

Reuse the fitted operator anywhere

Wrap the fitted regressor in a portable Operator and apply it to any future model series for that site:

op = hm.Operator.from_regressor(reg, feature_names=list(X.columns), **recipe,
                                metadata={"site": "MySite", "lat": 51.5, "lon": 1.4})
op.predict(new_model_series)          # -> corrected Series

See what it learned

w = hm.extract_linear_lag_weights(reg, X.columns)
hm.plot_lag_weight_kernel(w)                          # linear kernel w(τ), with 95% bands

surf = hm.extract_bilinear_lag_surface(reg, X.columns)
hm.plot_bilinear_kernel_surface(surf)                 # bilinear kernel w(τ1,τ2)

Or in the frequency domain — the admittance and the input-weighted quadratic transfer function:

f, W = hm.weighted_quadratic_transfer(reg, model_series, X.columns)  # |H2|·|X(f1)||X(f2)|
hm.plot_quadratic_transfer(f, W)                       # which frequency pairs interact

Worked examples (examples/):

  • quickstart.ipynb — end-to-end on real GESLA + GTSM data (load the pair → fit → inspect → evaluate → save/reload).
  • synthetic_volterra.ipynb — arbitrary models on synthetic data: impose a known Volterra operator, recover it, compare linear vs bilinear, see the overtides in the spectrum, and use several predictors at once.
  • 1-d_estuary.ipynb — recreate the paper's 1-D shallow-water mechanism figures: a numba estuary solver generates baseline vs target series, and hydromend learns the linear impulse response, the bilinear Volterra kernel K(τ₁,τ₂), and the weighted QTF for each mechanism.

Apply a released operator (ERA5-GTSM)

Once a weight library exists (e.g. the ERA5-GTSM tide-gauge release on Zenodo), correcting a model series needs no fitting at all:

import hydromend as hm

lib = hm.OperatorLibrary.from_parquet("era5gtsm_operators.parquet")

op = lib.nearest(lat=51.5, lon=1.4)      # or lib.get("Sheerness")
corrected = op.predict(gtsm_series)       # hourly model Series -> corrected Series

op.predict rebuilds the exact lagged/bilinear features the operator was trained on and evaluates X · coef + intercept (numpy + pandas only).

The whole GESLA + GTSM compare, in a few lines

from hydromend.datasets import load_pair

# reads the gauge, finds its nearest GTSM point, streams that station, aligns hourly
df, info = load_pair("gesla4/Sheerness.txt", "GTSM_ERA5_E/water_level_*/*.nc")

op = lib.get(info["site"])
df["corrected"] = op.predict(df[["model"]])
print((df["observations"] - df["corrected"]).abs().mean())

Benchmark many sites / regressors at once (optional)

To compare feature sets or estimators across a whole network:

groups = hm.load_model_groups({"gtsm": "pairs/*.nc"})           # {model: {site: df}}
results, preds = hm.run_benchmark(
    groups,
    lags_hours=range(1, 25),
    feature_sets=("linear", "bilinear"),
    regressors=("vb_ard", "ols"),        # default is ("vb_ard",); add others to compare
    split_config=hm.SplitConfig(test_size_hours=4 * 8760),
)
hm.summarise_results(results)          # mean improvement ratio by model/regressor/features

What's in the box

Module Purpose
hydromend.features lagged + bilinear (Volterra) feature construction
hydromend.models regressors — vb_ard (default), ols, gam, gp_lag, gp_volterra
hydromend.benchmark train/test splitting, single-case fit, multi-site benchmark
hydromend.weights extract & plot the learned linear / bilinear kernels
hydromend.spectral frequency-domain views: admittance H(f), quadratic transfer H₂(f₁,f₂), weighted QTF
hydromend.data load observation/model pairs from NetCDF into per-site frames
hydromend.plotting diagnostic time-series / residual / learning-curve plots
hydromend.priors GP priors that couple neighbouring lag weights
hydromend.pretrained Operator / OperatorLibrary — save, ship, and apply weights
hydromend.gesla GESLA-4.1 tide-gauge reader (numpy/pandas only)
hydromend.gtsm stream a single GTSM-ERA5 station out of the monthly NetCDFs
hydromend.datasets load_pair — one gauge + its GTSM point, aligned hourly

See docs/ for the concepts guide and API reference.

Estimators

make_regressor() returns the default VB-ARD operator; pass a name or a kwargs dict to switch:

name class what it is
vb_ard (default) VBARDRegressor variational-Bayes automatic relevance determination — sparsifies lags, gives coefficient uncertainties
ols LinearRegression least squares — fast baseline
gam GAMRegressor additive splines (needs hydromend[gam])
gp_lag GPLagWeightRegressor linear lag model with a GP prior over w(τ)
gp_volterra GPVolterraRegressor linear and bilinear weights, each with a GP prior

Caveats

  • An operator is only as good as the model↔observation pairing it was fit on; apply it at (or very near) the station it was trained for.
  • Released mae_* fields describe in-sample fit quality unless stated otherwise — see the accompanying dataset / paper for cross-validated skill.
  • predict returns values referenced to whatever datum the operator was trained against (released ERA5-GTSM operators use the most-recent gauge datum segment).

Citation

If you use hydromend or the ERA5-GTSM operator library, please cite the accompanying paper and dataset:

@article{monahan2026learning,
  title={Learning unresolved coastal dynamics in hydrodynamic models},
  author={Monahan, Thomas Carey and Polton, Jeff and Innocenti, Silvia and Matte, Pascal and Ayyad, Mahmoud and Saman, Krijn and Adcock, Thomas AA},
  year={2026},
  publisher={EarthArXiv}
}

Metadata

Release files for hydromend 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hydromend 0.1.0
File Size Uploaded
hydromend-0.1.0.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for hydromend 0.1.0
File Interpreter ABI Platform
hydromend-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / hydromend-0.1.0.tar.gz

Download URL hydromend-0.1.0.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
f86381b54e0d435a03a5988386d387a10c1c1d66dfcd04098a02922d24cf67c2
BLAKE2b-256 checksum
How to use checksums
d6b844c3e1a9e2611e8ee4ddb5d23d79d4b15f4a25686529783a527e76bfd756
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.

Transparency log

Release files / hydromend-0.1.0-py3-none-any.whl

Download URL hydromend-0.1.0-py3-none-any.whl
Size 40.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc44464e79cfebeb3bec10f64a2654e783c39d0a8c9521e3d7780f985103c924
BLAKE2b-256 checksum
How to use checksums
111da2425033dea26ab73a1792eb1c5ff342e9a5ea7393938aefe633777b4646
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page