Skip to content
2 changes: 2 additions & 0 deletions src/parcels/_core/_windowed_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,6 @@ def maybe_windowed(data: xr.DataArray, max_levels: int | None = None):
return data
if data.dims and data.dims[0] == "time" and is_dask_collection(data.data):
return WindowedArray(data, max_levels=max_levels)
elif data.dims and data.dims[0] == "mockT" and is_dask_collection(data.data):
return data.compute()
return data
7 changes: 7 additions & 0 deletions src/parcels/_core/xgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import numpy.typing as npt
import xarray as xr
import xgcm
from dask import is_dask_collection

import parcels._sgrid as sgrid
import parcels._typing as ptyping
Expand Down Expand Up @@ -207,6 +208,9 @@ def lon(self):
_ = self.xgcm_grid.axes["X"]
except KeyError:
return np.zeros(1)
# ensure lon is loaded into memory for dask-backed datasets, as it is used in the search method
if is_dask_collection(self._ds["lon"].data):
self._ds["lon"].load()
return self._ds["lon"].values

@property
Expand All @@ -221,6 +225,9 @@ def lat(self):
_ = self.xgcm_grid.axes["Y"]
except KeyError:
return np.zeros(1)
# ensure lat is loaded into memory for dask-backed datasets, as it is used in the search method
if is_dask_collection(self._ds["lat"].data):
self._ds["lat"].load()
return self._ds["lat"].values

@property
Expand Down
20 changes: 20 additions & 0 deletions src/parcels/_reprs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

import numpy as np
import xarray as xr
from dask.base import is_dask_collection

from parcels._core._windowed_array import WindowedArray
from parcels._python import isinstance_noimport

if TYPE_CHECKING:
Expand Down Expand Up @@ -190,13 +192,15 @@ class _FieldSetDescriptionRow:
model_id: int | None
name: str
interp_method_or_value: str
backend: str | None = None

def to_dict(self) -> dict[str, str]:
return {
"Name": self.name,
"Type": self.type_,
"Grid number": str(self.model_id) if self.model_id is not None else "-",
"Interp method / value": self.interp_method_or_value,
"Backend": self.backend if self.backend is not None else "-",
}


Expand All @@ -213,6 +217,20 @@ def _print_time_interval(time_interval: TimeInterval | None) -> str:
return repr((time_interval.left, time_interval.right))


def _field_backend(field: Field | VectorField) -> str | None:
if hasattr(field, "data"):
if isinstance(field.data, WindowedArray):
return "WindowedArray"
elif is_dask_collection(field.data.data):
return "Dask"
elif isinstance(field.data.data, np.ndarray):
return "NumPy"
else:
return type(field.data).__name__
else:
return None


def fieldset_describe(fieldset: FieldSet) -> str:
rows: list[_FieldSetDescriptionRow] = []
models: dict[int, int] = {} # mapping of memory ID to a human readable ID
Expand All @@ -235,6 +253,7 @@ def fieldset_describe(fieldset: FieldSet) -> str:
model_id=model_id,
name=field.name,
interp_method_or_value=repr(field.interp_method),
backend=_field_backend(field),
)
)
for k, v in fieldset.context.items():
Expand All @@ -244,6 +263,7 @@ def fieldset_describe(fieldset: FieldSet) -> str:
model_id=None,
name=k,
interp_method_or_value=repr(v),
backend=None,
)
)
return (
Expand Down
73 changes: 62 additions & 11 deletions tests/test_fieldset.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pandas as pd
import pytest

import parcels.tutorial
from parcels import Field, ParticleFile, ParticleSet, XGrid, convert
from parcels._core.fieldset import FieldSet, _datetime_to_msg
from parcels._core.model import _default_vector_field_components
Expand Down Expand Up @@ -398,21 +399,71 @@ def test_fieldset_describe(fieldset_two_models: FieldSet):
fieldset = fieldset_two_models
io = StringIO()
expected = """\
| Name | Type | Grid number | Interp method / value |
|:---------------|:------------|:--------------|:------------------------|
| my_list | Context | - | [1, 2, 'hello'] |
| my_value | Context | - | 2.0 |
| U | Field | 0 | XLinear(...) |
| V | Field | 0 | XLinear(...) |
| UV | VectorField | 0 | XLinear_Velocity(...) |
| U_wind | Field | 1 | XLinear(...) |
| V_wind | Field | 1 | XLinear(...) |
| UV_wind | VectorField | 1 | XLinear_Velocity(...) |
| constant_field | Field | 2 | XConstantField(...) |
| Name | Type | Grid number | Interp method / value | Backend |
Comment thread
erikvansebille marked this conversation as resolved.
|:---------------|:------------|:--------------|:------------------------|:----------|
| my_list | Context | - | [1, 2, 'hello'] | - |
| my_value | Context | - | 2.0 | - |
| U | Field | 0 | XLinear(...) | NumPy |
| V | Field | 0 | XLinear(...) | NumPy |
| UV | VectorField | 0 | XLinear_Velocity(...) | - |
| U_wind | Field | 1 | XLinear(...) | NumPy |
| V_wind | Field | 1 | XLinear(...) | NumPy |
| UV_wind | VectorField | 1 | XLinear_Velocity(...) | - |
| constant_field | Field | 2 | XConstantField(...) | NumPy |

mesh: flat
time interval: (np.datetime64('2000-01-01T00:00:00.000000000'), np.datetime64('2001-01-01T00:00:00.000000000'))
"""
fieldset.describe(io)
actual = io.getvalue()
assert actual == expected


def test_fieldset_describe_backends():
ds_u = parcels.tutorial.open_dataset("NemoNorthSeaORCA025-N006_data/U")
ds_v = parcels.tutorial.open_dataset("NemoNorthSeaORCA025-N006_data/V")
ds_w = parcels.tutorial.open_dataset("NemoNorthSeaORCA025-N006_data/W")
ds_coords = parcels.tutorial.open_dataset("NemoNorthSeaORCA025-N006_data/mesh_mask")[["glamf", "gphif"]]

ds_fset = convert.nemo_to_sgrid(
fields={"U": ds_u["uo"], "V": ds_v["vo"], "W": ds_w["wo"]},
coords=ds_coords,
)
fieldset = FieldSet.from_sgrid_conventions(ds_fset)

io = StringIO()
expected = """\
| Name | Type | Grid number | Interp method / value | Backend |
|:-------|:------------|--------------:|:------------------------|:----------|
| U | Field | 0 | XLinear(...) | Dask |
| V | Field | 0 | XLinear(...) | Dask |
| W | Field | 0 | XLinear(...) | Dask |
| UV | VectorField | 0 | CGrid_Velocity(...) | - |
| UVW | VectorField | 0 | CGrid_Velocity(...) | - |

mesh: spherical
time interval: (np.datetime64('2000-01-02T12:00:00.000000000'), np.datetime64('2000-01-27T12:00:00.000000000'))
"""
fieldset.describe(io)
actual = io.getvalue()
assert actual == expected

# Also run with WindowedArray backend
fieldset = fieldset.to_windowed_arrays()

io = StringIO()
expected = """\
| Name | Type | Grid number | Interp method / value | Backend |
|:-------|:------------|--------------:|:------------------------|:--------------|
| U | Field | 0 | XLinear(...) | WindowedArray |
| V | Field | 0 | XLinear(...) | WindowedArray |
| W | Field | 0 | XLinear(...) | WindowedArray |
| UV | VectorField | 0 | CGrid_Velocity(...) | - |
| UVW | VectorField | 0 | CGrid_Velocity(...) | - |

mesh: spherical
time interval: (np.datetime64('2000-01-02T12:00:00.000000000'), np.datetime64('2000-01-27T12:00:00.000000000'))
"""
fieldset.describe(io)
actual = io.getvalue()
assert actual == expected
Loading