Skip to content

Commit 0722f06

Browse files
Move select_uxinterpolator into fieldset
1 parent ef1ba3c commit 0722f06

1 file changed

Lines changed: 43 additions & 16 deletions

File tree

src/parcels/_core/fieldset.py

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from parcels._core.xgrid import _DEFAULT_XGCM_KWARGS, XGrid
2020
from parcels._logger import logger
2121
from parcels._typing import Mesh
22-
from parcels.interpolators import XConstantField
22+
from parcels.interpolators import UXPiecewiseConstantFace, UXPiecewiseLinearNode, XConstantField, XLinear
2323

2424
if TYPE_CHECKING:
2525
from parcels._core.basegrid import BaseGrid
@@ -240,22 +240,22 @@ def from_copernicusmarine(ds: xr.Dataset):
240240

241241
fields = {}
242242
if "U" in ds.data_vars and "V" in ds.data_vars:
243-
fields["U"] = Field("U", ds["U"], grid)
244-
fields["V"] = Field("V", ds["V"], grid)
243+
fields["U"] = Field("U", ds["U"], grid, XLinear)
244+
fields["V"] = Field("V", ds["V"], grid, XLinear)
245245
fields["U"].units = GeographicPolar()
246246
fields["V"].units = Geographic()
247247

248248
if "W" in ds.data_vars:
249249
ds["W"] -= ds[
250250
"W"
251251
] # Negate W to convert from up positive to down positive (as that's the direction of positive z)
252-
fields["W"] = Field("W", ds["W"], grid)
252+
fields["W"] = Field("W", ds["W"], grid, XLinear)
253253
fields["UVW"] = VectorField("UVW", fields["U"], fields["V"], fields["W"])
254254
else:
255255
fields["UV"] = VectorField("UV", fields["U"], fields["V"])
256256

257257
for varname in set(ds.data_vars) - set(fields.keys()):
258-
fields[varname] = Field(varname, ds[varname], grid)
258+
fields[varname] = Field(varname, ds[varname], grid, XLinear)
259259

260260
return FieldSet(list(fields.values()))
261261

@@ -283,19 +283,19 @@ def from_fesom2(ds: ux.UxDataset):
283283

284284
fields = {}
285285
if "U" in ds.data_vars and "V" in ds.data_vars:
286-
fields["U"] = Field("U", ds["U"], grid)
287-
fields["V"] = Field("V", ds["V"], grid)
286+
fields["U"] = Field("U", ds["U"], grid, _select_uxinterpolator(ds["U"]))
287+
fields["V"] = Field("V", ds["V"], grid, _select_uxinterpolator(ds["U"]))
288288
fields["U"].units = GeographicPolar()
289289
fields["V"].units = Geographic()
290290

291291
if "W" in ds.data_vars:
292-
fields["W"] = Field("W", ds["W"], grid)
292+
fields["W"] = Field("W", ds["W"], grid, _select_uxinterpolator(ds["U"]))
293293
fields["UVW"] = VectorField("UVW", fields["U"], fields["V"], fields["W"])
294294
else:
295295
fields["UV"] = VectorField("UV", fields["U"], fields["V"])
296296

297297
for varname in set(ds.data_vars) - set(fields.keys()):
298-
fields[varname] = Field(varname, ds[varname], grid)
298+
fields[varname] = Field(varname, ds[varname], grid, _select_uxinterpolator(ds[varname]))
299299

300300
return FieldSet(list(fields.values()))
301301

@@ -425,7 +425,6 @@ def _discover_fesom2_U_and_V(ds: ux.UxDataset) -> ux.UxDataset:
425425
"Dataset has only one of the two variables 'U' and 'V'. Please rename the appropriate variable in your dataset to have both 'U' and 'V' for Parcels simulation."
426426
)
427427

428-
uv_found = False
429428
for common_U, common_V in common_fesom_UV:
430429
if common_U in ds:
431430
if common_V not in ds:
@@ -436,7 +435,6 @@ def _discover_fesom2_U_and_V(ds: ux.UxDataset) -> ux.UxDataset:
436435
)
437436
else:
438437
ds = _ds_rename_using_standard_names(ds, {common_U: "U", common_V: "V"})
439-
uv_found = True
440438
break
441439

442440
else:
@@ -447,11 +445,7 @@ def _discover_fesom2_U_and_V(ds: ux.UxDataset) -> ux.UxDataset:
447445
"Please rename the appropriate variables in your dataset to have both 'U' and 'V' for Parcels simulation."
448446
)
449447
continue
450-
if not uv_found:
451-
raise ValueError(
452-
f"Dataset has neither 'U' nor 'V' in potential options {common_fesom_UV!r}, "
453-
f"Please provide a dataset that has both 'U' and 'V' for Parcels simulation."
454-
)
448+
455449
return ds
456450

457451

@@ -463,3 +457,36 @@ def _ds_rename_using_standard_names(ds: xr.Dataset | ux.UxDataset, name_dict: di
463457
f"cf_xarray found variable {name!r} with CF standard name {standard_name!r} in dataset, renamed it to {rename_to!r} for Parcels simulation."
464458
)
465459
return ds
460+
461+
462+
def _select_uxinterpolator(da: ux.UxDataArray):
463+
"""Selects the appropriate uxarray interpolator for a given uxarray UxDataArray"""
464+
supported_uxinterp_mapping = {
465+
# (nz1,n_face): face-center laterally, layer centers vertically — piecewise constant
466+
"nz1,n_face": UXPiecewiseConstantFace,
467+
# (nz,n_node): node/corner laterally, layer interfaces vertically — barycentric lateral & linear vertical
468+
"nz,n_node": UXPiecewiseLinearNode,
469+
}
470+
# Extract only spatial dimensions, neglecting time
471+
da_spatial_dims = tuple(d for d in da.dims if d not in ("time",))
472+
if len(da_spatial_dims) != 2:
473+
raise ValueError(
474+
"Fields on unstructured grids must have two spatial dimensions, one vertical (nz or nz1) and one lateral (n_face, n_edge, or n_node)"
475+
)
476+
477+
# Construct key (string) for mapping to interpolator
478+
# Find vertical and lateral tokens
479+
vdim = None
480+
ldim = None
481+
for d in da_spatial_dims:
482+
if d in ("nz", "nz1"):
483+
vdim = d
484+
if d in ("n_face", "n_node"):
485+
ldim = d
486+
# Map to supported interpolators
487+
if vdim and ldim:
488+
key = f"{vdim},{ldim}"
489+
if key in supported_uxinterp_mapping.keys():
490+
return supported_uxinterp_mapping[key]
491+
492+
return None

0 commit comments

Comments
 (0)