Skip to content

Commit 004cd98

Browse files
Cleaning up SWASH convert
1 parent ccde7e2 commit 004cd98

1 file changed

Lines changed: 38 additions & 56 deletions

File tree

src/parcels/convert.py

Lines changed: 38 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
from __future__ import annotations
1414

1515
import enum
16+
import re
1617
import typing
1718
import warnings
1819
from typing import cast
1920

2021
import numpy as np
22+
import scipy.io as sio
2123
import xarray as xr
2224

2325
import parcels._sgrid as sgrid
@@ -567,16 +569,23 @@ def copernicusmarine_to_sgrid(
567569
return ds
568570

569571

570-
def swash_to_sgrid(coord_file, data_file, total_depth) -> xr.Dataset:
571-
"""Create an sgrid-compliant xarray.Dataset from a dataset of SWASH netcdf files."""
572-
import re
572+
def swash_to_sgrid(data_file: str, coord_file: str, total_depth: float) -> xr.Dataset:
573+
"""Create an sgrid-compliant xarray.Dataset from a dataset of SWASH netcdf files.
573574
574-
import pandas as pd
575-
import scipy.io as sio
576-
577-
import parcels._sgrid as sgrid
575+
Parameters
576+
----------
577+
data_file : str
578+
Path to the SWASH data file (MATLAB binary format).
579+
coord_file : str
580+
Path to the SWASH coordinate file (MATLAB binary format).
581+
total_depth : float
582+
Total depth of the water column.
578583
579-
## First load the coordinates and data files (SWASH output - matlab binary format)
584+
Returns
585+
-------
586+
xarray.Dataset
587+
Dataset object following SGRID conventions to be (optionally) modified and passed to a FieldSet constructor.
588+
"""
580589
coord = sio.loadmat(coord_file)
581590
x = coord["Xp"][0, :]
582591
y = coord["Yp"][:, 0]
@@ -588,17 +597,18 @@ def swash_to_sgrid(coord_file, data_file, total_depth) -> xr.Dataset:
588597
time_keys = sorted(
589598
set((int(m.group(1)), int(m.group(2))) for k in keys for m in [re.search(r"_(\d{6})_(\d{3})$", k)] if m)
590599
)
591-
times = [t[0] + t[1] / 1000 for t in time_keys]
600+
times = np.array([t[0] * 1000 + t[1] for t in time_keys]).astype("timedelta64[ms]")
592601

593602
n_layers = max(int(re.search(r"Vksi_k(\d+)_", k).group(1)) for k in keys if re.search(r"Vksi_k(\d+)_", k))
594-
n_w_layers = n_layers
595-
596-
nx, ny, nt = len(x), len(y), len(times)
603+
n_layers_f = n_layers + 1
604+
depth_centers = np.array([total_depth * (i - 0.5) / n_layers for i in range(1, n_layers + 1)], dtype=np.float32)
605+
depth_interfaces = np.array([total_depth * i / n_layers for i in range(n_layers_f)], dtype=np.float32)
597606

607+
nt, ny, nx = len(times), len(y), len(x)
598608
watlev = np.full((nt, ny, nx), np.nan, dtype=np.float32)
599609
vksi = np.full((nt, n_layers, ny, nx), np.nan, dtype=np.float32)
600610
veta = np.full((nt, n_layers, ny, nx), np.nan, dtype=np.float32)
601-
w = np.full((nt, n_w_layers, ny, nx), np.nan, dtype=np.float32)
611+
w = np.full((nt, n_layers_f, ny, nx), np.nan, dtype=np.float32)
602612

603613
for ti, (ts_int, ts_dec) in enumerate(time_keys):
604614
ts_str = f"{ts_int:06d}_{ts_dec:03d}"
@@ -612,57 +622,29 @@ def swash_to_sgrid(coord_file, data_file, total_depth) -> xr.Dataset:
612622
if m:
613623
veta[ti, int(m.group(1)) - 1, :, :] = mat[k]
614624
m = re.match(rf"w(\d+)_{ts_str}$", k)
615-
if m and int(m.group(1)) < n_w_layers:
625+
if m and int(m.group(1)) < n_layers:
616626
w[ti, int(m.group(1)), :, :] = mat[k]
617627

618-
t0 = pd.Timestamp("2026-06-01 00:00:00")
619-
time_dt = np.array([t0 + pd.to_timedelta(t, unit="s") for t in times], dtype="datetime64[ns]")
620-
621628
ds = xr.Dataset(
622629
{
623-
"watlev": (["time", "y", "x"], watlev),
624-
"U": (["time", "depth", "y", "x"], vksi),
625-
"V": (["time", "depth", "y", "x"], veta),
626-
"W": (["time", "depth_f", "y", "x"], w),
627-
"botlev": (["y", "x"], bot),
630+
"watlev": (["time", "lat", "lon"], watlev),
631+
"U": (["time", "depth", "lat", "lon"], vksi),
632+
"V": (["time", "depth", "lat", "lon"], veta),
633+
"W": (["time", "depth_f", "lat", "lon"], w),
634+
"botlev": (["lat", "lon"], bot),
628635
},
629636
coords={
630-
"time": time_dt,
631-
"depth": np.arange(1, n_layers + 1),
632-
"depth_f": np.arange(0, n_w_layers),
633-
"y": y,
634-
"x": x,
637+
"time": (["time"], times, {"axis": "T", "units": "ms"}),
638+
"depth": (["depth"], depth_centers, {"axis": "Z", "units": "m", "positive": "down"}),
639+
"depth_f": (["depth_f"], depth_interfaces, {"axis": "Z", "units": "m", "positive": "down"}),
640+
"lat": (["lat"], y, {"axis": "Y", "units": "m"}),
641+
"lon": (["lon"], x, {"axis": "X", "units": "m"}),
635642
},
636643
)
637-
ds.attrs.update(source="SWASH version 11.01ABC", project="progWave", run="A14", Conventions="CF-1.8")
638-
639-
n_layers = ds.sizes["depth"]
640-
n_layers_f = ds.sizes["depth_f"]
641-
if n_layers_f != n_layers:
642-
raise ValueError(f"Expected depth_f to match depth in length (got {n_layers_f} vs {n_layers})")
643-
644-
depth_centers = np.array([total_depth * (i - 0.5) / n_layers for i in range(1, n_layers + 1)], dtype=np.float32)
645-
depth_interfaces = np.array([total_depth * i / n_layers for i in range(n_layers_f)], dtype=np.float32)
646-
647-
# Rename x/y -> lon/lat: Parcels' from_sgrid_conventions expects these names
648-
# literally, even on a flat/Cartesian mesh (units stay in meters).
649-
ds = ds.rename({"x": "lon", "y": "lat"})
650-
651-
ds = ds.assign_coords(
652-
{
653-
"depth": ("depth", depth_centers),
654-
"depth_f": ("depth_f", depth_interfaces),
655-
}
656-
)
657-
658-
ds["time"].attrs.update(axis="T")
659-
ds["lon"].attrs.update(axis="X", units="m")
660-
ds["lat"].attrs.update(axis="Y", units="m")
661-
ds["depth"].attrs.update(axis="Z", units="m", positive="down")
662-
ds["depth_f"].attrs.update(axis="Z", units="m", positive="down")
663-
664-
if "grid" in ds.cf.cf_roles:
665-
raise ValueError("Dataset already has a 'grid' variable (cf_role grid_topology).")
644+
header = mat["__header__"]
645+
if isinstance(header, bytes):
646+
header = header.decode("utf-8")
647+
ds.attrs.update(header=header, version=mat["__version__"], globals=mat["__globals__"])
666648

667649
ds["grid"] = xr.DataArray(
668650
0,

0 commit comments

Comments
 (0)