Skip to content

Commit c2f31e4

Browse files
Add open_raw_zarr helper (#2668)
* Add open_raw_zarr helper * Make open_raw_zarr read metadata And add test * Add check that data_vars are Zarr arrays
1 parent 1197923 commit c2f31e4

3 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/parcels/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import warnings as _stdlib_warnings
1111

1212
from parcels._core.fieldset import FieldSet
13+
from parcels._xarray import open_raw_zarr
1314
from parcels._core.particleset import ParticleSet
1415
from parcels._core.particlefile import ParticleFile, read_particlefile
1516
from parcels._core.particle import (
@@ -42,6 +43,7 @@
4243
__all__ = [ # noqa: RUF022
4344
# Core classes
4445
"FieldSet",
46+
"open_raw_zarr",
4547
"ParticleSet",
4648
"ParticleFile",
4749
"Variable",

src/parcels/_xarray.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from __future__ import annotations
2+
3+
import xarray as xr
4+
import zarr
5+
import zarr.storage
6+
from zarr.abc.store import Store
7+
8+
9+
def _not_implemented(*args, **kwargs):
10+
raise NotImplementedError("This function it not implemented")
11+
12+
13+
def open_raw_zarr(store: Store):
14+
"""Open a Zarr dataset in an Xarray dataset, bypassing Dask."""
15+
with xr.open_zarr(store) as ds:
16+
var_to_dims = {name: var.dims for name, var in ds.variables.items()}
17+
var_to_attrs = {name: var.attrs for name, var in ds.variables.items()}
18+
coords = {name: ds[name].variable.load() for name in ds.coords}
19+
ds_attrs = ds.attrs
20+
21+
group = zarr.open(store, mode="r")
22+
assert isinstance(group, zarr.Group)
23+
24+
data_vars = {}
25+
for name, array in group.members():
26+
if not isinstance(array, zarr.Array):
27+
raise ValueError("Discovered a zarr.Group in the root group. open_raw_zarr doesn't work with nested groups")
28+
if name in coords:
29+
continue
30+
31+
# trick xarray to prevent coersion to a numpy array
32+
array.__array_function__ = _not_implemented # type: ignore[attr-defined]
33+
data_vars[name] = xr.Variable(var_to_dims[name], array, attrs=var_to_attrs[name])
34+
35+
return xr.Dataset(data_vars, coords, attrs=ds_attrs)

tests/test_xarray.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import pytest
2+
import xarray as xr
3+
import zarr
4+
5+
from parcels import open_raw_zarr
6+
from parcels._datasets.structured.generic import datasets
7+
8+
9+
@pytest.mark.parametrize("ds", [pytest.param(v, id=k) for k, v in datasets.items()])
10+
def test_open_raw_zarr(ds, tmp_path):
11+
path = tmp_path / "ds.zarr"
12+
ds.to_zarr(path)
13+
14+
result = open_raw_zarr(path)
15+
16+
for k in result.data_vars:
17+
# tests that the internal representation within Xarray isn't coerced into a numpy array
18+
assert isinstance(result[k]._variable._data, zarr.Array)
19+
20+
xr.testing.assert_identical(result.load(), ds)

0 commit comments

Comments
 (0)