Skip to content

Commit f38e0d1

Browse files
use zero for land-values throughout Parcels (#2762)
* Add _assert_no_nan_in_field_data function * Use fillna in convert functions * Adding fill_nan_to_zero and assert_valid_fields options * Changing to one skip_field_data_validation option
1 parent f48ba29 commit f38e0d1

4 files changed

Lines changed: 40 additions & 7 deletions

File tree

src/parcels/_core/fieldset.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ def from_sgrid_conventions(
289289
ds: xr.Dataset,
290290
mesh: ptyping.TMesh | None = None,
291291
vector_fields: ptyping.VectorFields | NotSetType = NOTSET,
292+
skip_field_data_validation: bool = False,
292293
): # TODO: Update mesh to be discovered from the dataset metadata
293294
"""Create a FieldSet from a dataset using SGRID convention metadata.
294295
@@ -307,6 +308,8 @@ def from_sgrid_conventions(
307308
Mapping of vector field names to tuples of component variable names in the dataset.
308309
For example, ``{"UV": ("U", "V"), "UVW": ("U", "V", "W")}``.
309310
If omitted (default), vector fields are auto-discovered from standard variable names (``U``/``V``/``W``).
311+
skip_field_data_validation : bool, optional
312+
If True, skip validation of the field data. This can be useful for performance reasons, but may lead to unexpected behavior if the field data is invalid. Default is False.
310313
311314
Returns
312315
-------
@@ -321,7 +324,9 @@ def from_sgrid_conventions(
321324
322325
See https://sgrid.github.io/sgrid/ for more information on the SGRID conventions.
323326
"""
324-
model = StructuredModelData.from_sgrid_conventions(ds, mesh, vector_fields)
327+
model = StructuredModelData.from_sgrid_conventions(
328+
ds, mesh, vector_fields, skip_field_data_validation=skip_field_data_validation
329+
)
325330
return cls([model])
326331

327332
def describe(self, buf: IO | None = None) -> None:

src/parcels/_core/model.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,19 @@ def preprocess_sgrid_model_data(ds: xr.Dataset) -> xr.Dataset:
132132

133133

134134
class StructuredModelData(ModelData):
135-
def __init__(self, data: xr.Dataset, mesh: ptyping.TMesh, vector_field_components: ptyping.VectorFields):
135+
def __init__(
136+
self,
137+
data: xr.Dataset,
138+
mesh: ptyping.TMesh,
139+
vector_field_components: ptyping.VectorFields,
140+
skip_field_data_validation: bool = False,
141+
):
136142
if not isinstance(data, xr.Dataset):
137143
raise ValueError(f"Expected `data` to be an xarray.Dataset . Got {type(data)}")
138144

139145
data = preprocess_sgrid_model_data(data)
146+
if not skip_field_data_validation:
147+
data = data.fillna(0)
140148
grid = XGrid(data, mesh)
141149

142150
self.data = data
@@ -181,7 +189,11 @@ def construct_fields(self) -> list[Field | VectorField]:
181189

182190
@classmethod
183191
def from_sgrid_conventions(
184-
cls, ds: xr.Dataset, mesh: ptyping.TMesh | None, vector_fields: ptyping.VectorFields | NotSetType
192+
cls,
193+
ds: xr.Dataset,
194+
mesh: ptyping.TMesh | None,
195+
vector_fields: ptyping.VectorFields | NotSetType,
196+
skip_field_data_validation: bool = False,
185197
) -> Self:
186198
ds = ds.copy()
187199
if mesh is None:
@@ -212,7 +224,12 @@ def from_sgrid_conventions(
212224
vector_fields = resolve_vector_fields(ds, vector_fields)
213225
assert_valid_vector_fields(ds, vector_fields)
214226

215-
model = cls(ds, mesh=mesh, vector_field_components=vector_fields)
227+
model = cls(
228+
ds,
229+
mesh=mesh,
230+
vector_field_components=vector_fields,
231+
skip_field_data_validation=skip_field_data_validation,
232+
)
216233
model._fields = model.construct_fields()
217234
for f in model._fields:
218235
if isinstance(f, Field):

src/parcels/convert.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ def copernicusmarine_to_sgrid(
541541
ds.attrs.clear() # Clear global attributes from the merging
542542

543543
ds = _maybe_rename_coords(ds, _COPERNICUS_MARINE_AXIS_VARNAMES)
544+
544545
if "W" in ds.data_vars:
545546
# Negate W to convert from up positive to down positive (as that's the direction of positive z)
546547
ds["W"].data *= -1

tests/test_fieldset.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,16 @@ def test_fieldset_from_sgrid_conventions(ds_name):
330330
assert len(fieldset.fields) > 0
331331

332332

333+
def test_fieldset_skip_field_data_validation():
334+
ds = datasets_structured["ds_2d_left"]
335+
ds["U_A_grid"][:] = np.nan
336+
337+
fieldset = FieldSet.from_sgrid_conventions(ds, mesh="flat")
338+
assert np.isfinite(fieldset.U_A_grid.data).all()
339+
fieldset = FieldSet.from_sgrid_conventions(ds, mesh="flat", skip_field_data_validation=True)
340+
assert np.isnan(fieldset.U_A_grid.data).all()
341+
342+
333343
def test_fieldset_add_error_on_duplicate_fields():
334344
"""Test that adding FieldSets with overlapping field names raises a ValueError."""
335345
ds1 = datasets_structured["ds_2d_left"][["U_A_grid", "V_A_grid", "grid"]].rename({"U_A_grid": "U", "V_A_grid": "V"})
@@ -490,9 +500,9 @@ def test_fieldset_describe_backends(tmp_path):
490500
expected = """\
491501
| Name | Type | Grid number | Interp method / value | Parcels backend |
492502
|:-------|:------------|--------------:|:------------------------|:------------------|
493-
| U | Field | 0 | XLinear(...) | Zarr |
494-
| V | Field | 0 | XLinear(...) | Zarr |
495-
| W | Field | 0 | XLinear(...) | Zarr |
503+
| U | Field | 0 | XLinear(...) | NumPy |
504+
| V | Field | 0 | XLinear(...) | NumPy |
505+
| W | Field | 0 | XLinear(...) | NumPy |
496506
| UV | VectorField | 0 | CGrid_Velocity(...) | - |
497507
| UVW | VectorField | 0 | CGrid_Velocity(...) | - |
498508

0 commit comments

Comments
 (0)