Fast nearshore spectral wave transformation: backward ray-traced transfer operators with refraction, shoaling, bottom friction and depth-limited breaking
Project description
waveray
Fast last-stage nearshore spectral wave transformation: precomputed backward ray-traced linear transfer operators over local bathymetry, with parametric depth-limited wave breaking at the target.
📖 Documentation: https://oceanum.github.io/waveray/
Built to downscale directional wave spectra from SWAN (or WW3) hindcasts and nowcasts through the final nearshore transformation to a target site — decades of hourly spectra in seconds, no wave model in the runtime loop.
Install
pip install waveray # core
pip install "waveray[datamesh]" # + Oceanum Datamesh bathymetry/spectra access
Documentation
Full docs: https://oceanum.github.io/waveray/
| Guide | Read it for |
|---|---|
| Installation | Install, extras, environment |
| Quickstart | A working example in 20 lines |
| Concepts | What the operator is, and the physics inside it |
| User guide | Bathymetry, boundary points, tide, breaking, persistence, ray export |
| wavespectra interop | Spectral conventions and the .spec accessor |
| Validation | Measured skill against parent SWAN models |
| Limitations | What waveray does not model |
| API reference | Every public function and its arguments |
The pages are plain Markdown in docs/ and render on GitHub too.
Method
Setup, once per site:
- Pull local bathymetry (e.g. from an Oceanum Datamesh datasource) into a
LocalGridon a local tangent plane. - For every (frequency, direction) bin, trace ray bundles backward from the target over the 2D bathymetry (O'Reilly & Guza style spectral refraction). Each sub-ray records where and in what direction it exits the domain, or whether it runs aground (island/headland sheltering).
- Assemble a linear operator
T[f, dir_t, bp, dir_b]mapping K boundary spectra (the SWAN output points around the domain perimeter — alongshore inhomogeneity is handled by exit-point interpolation) to the target spectrum. The per-ray coefficient is the exact linear invariantE(f, theta) * c * cg = const, which reproduces Snell refraction and energy-flux shoaling identically (validated against analytic plane-beach solutions in the test suite).
Runtime, per hindcast:
E_t = einsum(T, E_b)over all timesteps at once.- Depth-limited breaking as a nonlinear post-step: transformed Hm0 is capped
at a Miche-type (Battjes-Janssen) or
gamma * dlimit with optional tide-modulated depth; the cap scales the spectrum proportionally, matching how SWAN distributes surf-breaking dissipation over the spectrum.
The stationary, linear-propagation assumption (no temporal nonlinear evolution, no quadruplets/triads) is deliberate: over a last-mile nearshore domain the propagation time is minutes and the transformation is dominated by refraction, shoaling, sheltering and breaking.
Quickstart
import numpy as np
from waveray import SiteModel, fetch_datamesh_bathymetry
bathy = fetch_datamesh_bathymetry(
"gebco_2025", bbox=(114.35, -28.95, 114.65, -28.60), positive="up"
)
model = SiteModel.build(
bathy=bathy,
target=(114.58, -28.77), # lon, lat
boundary_points=[(114.35, -28.90), (114.35, -28.65)], # SWAN output sites
freqs=efth_boundary.freq.values,
dirs=efth_boundary.dir.values,
)
efth_near = model.transform(efth_boundary, tide=tide_series) # (time, freq, dir)
model.to_netcdf("geraldton_berth.nc") # reuse without rebuilding
efth follows the wavespectra convention: dims (time, [site,] freq, dir),
dir = coming-from nautical degrees, density per Hz per degree. The site
dimension (K boundary points, same order as boundary_points) is required
when K > 1.
Ray paths can be exported for inspection (QGIS, EIDOS, any web map) as a GeoJSON FeatureCollection — one MultiLineString per (freq, dir) bin, with period, direction, per-ray status and friction decay in the properties:
from waveray import ray_paths_geojson
x, y = bathy_grid.to_local(lon, lat)
ray_paths_geojson(bathy_grid, (x[0], y[0]), freqs=[0.06, 0.1],
dirs=np.arange(0, 360, 15), path="rays.geojson")
Notebooks
Two executed, illustrated notebooks (plots included in the committed output):
| Notebook | What it shows |
|---|---|
notebooks/01_holland_downscaling.ipynb |
End-to-end downscaling of a SWAN 1 km hindcast to a point off Noordwijk aan Zee through storms Pia and Henk: bathymetry, ray geometry and the arrival cone, offshore vs nearshore Hs, the directional spectrum before/after, validation against SWAN (r ≈ 0.99), and what depth-limited breaking contributes. |
notebooks/02_abrolhos_validation.ipynb |
Held-out validation on a reef-fronted WA coast, and an ablation isolating JONSWAP bottom friction (Hm0 bias +1.11 m → +0.18 m when friction is on). |
uv sync --extra datamesh --extra notebooks
DATAMESH_TOKEN=... uv run jupyter lab notebooks/
Limitations (v0)
- No diffraction: accuracy degrades inside harbours / behind breakwaters, and island shadows are sharper than reality (rays block, they do not leak).
- Island / headland sheltering is included — a ray grounding on any land
carries no energy — but blocking is binary and only as good as the
bathymetry: a feature smaller than ~2 grid cells is smoothed away by the
bilinear depth sampling and will not shelter (GEBCO at ~450 m cannot
shelter behind a small reef or islet). See
tests/test_island.py. - Bottom friction IS included (JONSWAP, integrated along ray paths) and is ON
by default with the SWAN swell coefficient
cf_jonswap=0.038; passcf_jonswap=Nonefor pure refraction + shoaling. No triad interactions. - Breaking is an endpoint cap, not accumulated dissipation along the approach
— appropriate at berths and outside the inner surf zone; tune
gammaper site against observations. - The operator is built at a fixed water level; tide only modulates the breaking cap. For strongly tide-dependent refraction, build operators per tide stage.
- Boundary spectra quality bounds achievable skill — nested-model literature consistently finds offshore directional accuracy, not nearshore resolution, is the limiting factor.
Development
uv sync # or: uv sync --extra datamesh
uv run pytest # physics-validation suite
uv run ruff check . && uv run ruff format --check .
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file waveray-0.1.0.tar.gz.
File metadata
- Download URL: waveray-0.1.0.tar.gz
- Upload date:
- Size: 24.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63db4d0f2abb958f44b00d5856967bc6352d7f9edae2da76b24da533a8e53bdf
|
|
| MD5 |
0e47c98620a102a9c015ab71cf28e56c
|
|
| BLAKE2b-256 |
d730100aaf0c53024951f3dd812f010cba2574a325a975f4dcf4a5d5dd91853a
|
Provenance
The following attestation bundles were made for waveray-0.1.0.tar.gz:
Publisher:
publish.yml on oceanum/waveray
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
waveray-0.1.0.tar.gz -
Subject digest:
63db4d0f2abb958f44b00d5856967bc6352d7f9edae2da76b24da533a8e53bdf - Sigstore transparency entry: 2133621570
- Sigstore integration time:
-
Permalink:
oceanum/waveray@98e31eecad10aef2c356ef753c36e60b7d83d8a4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/oceanum
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@98e31eecad10aef2c356ef753c36e60b7d83d8a4 -
Trigger Event:
release
-
Statement type:
File details
Details for the file waveray-0.1.0-py3-none-any.whl.
File metadata
- Download URL: waveray-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d24e4bff31a9c03c4f68fa0d888c39cfe371b8df54bb08a838d17d9d838e3a4
|
|
| MD5 |
90df4d7b188363e27c0e769ac5127ff1
|
|
| BLAKE2b-256 |
faaa575050dde968e64a183fad4556fae7a9d13a26f4eccba89beb1a6e905589
|
Provenance
The following attestation bundles were made for waveray-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on oceanum/waveray
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
waveray-0.1.0-py3-none-any.whl -
Subject digest:
0d24e4bff31a9c03c4f68fa0d888c39cfe371b8df54bb08a838d17d9d838e3a4 - Sigstore transparency entry: 2133621595
- Sigstore integration time:
-
Permalink:
oceanum/waveray@98e31eecad10aef2c356ef753c36e60b7d83d8a4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/oceanum
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@98e31eecad10aef2c356ef753c36e60b7d83d8a4 -
Trigger Event:
release
-
Statement type: