Skip to content

Commit 76e790c

Browse files
authored
Merge pull request #1 from TomNicholas/main
Initial commit
2 parents 5afddf1 + 50445b9 commit 76e790c

File tree

7 files changed

+292
-0
lines changed

7 files changed

+292
-0
lines changed

cubed_xarray/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from importlib.metadata import version
2+
3+
4+
try:
5+
__version__ = version("cubed-xarray")
6+
except Exception:
7+
# Local copy or not installed with setuptools.
8+
# Disable minimum version checks on downstream libraries.
9+
__version__ = "999"

cubed_xarray/cubedmanager.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import functools
2+
import sys
3+
from abc import ABC, abstractmethod
4+
from collections.abc import Sequence
5+
from importlib.metadata import entry_points
6+
from typing import TYPE_CHECKING, Any, Callable, Generic, Optional, TypeVar, Union, Tuple, List
7+
8+
import numpy as np
9+
10+
from xarray.core import utils
11+
from xarray.core.parallelcompat import ChunkManagerEntrypoint
12+
from xarray.core.pycompat import is_chunked_array, is_duck_dask_array
13+
14+
T_ChunkedArray = TypeVar("T_ChunkedArray")
15+
16+
# TODO importing TypeAlias is a pain on python 3.9 without typing_extensions in the CI
17+
# T_Chunks: TypeAlias = tuple[tuple[int, ...], ...]
18+
T_Chunks = Any
19+
20+
CHUNK_MANAGERS: dict[str, type["ChunkManagerEntrypoint"]] = {}
21+
22+
if TYPE_CHECKING:
23+
from xarray.core.types import CubedArray, ZarrArray
24+
25+
26+
class CubedManager(ChunkManagerEntrypoint["CubedArray"]):
27+
array_cls: type["CubedArray"]
28+
29+
def __init__(self):
30+
from cubed import Array
31+
32+
self.array_cls = Array
33+
34+
def chunks(self, data: "CubedArray") -> T_Chunks:
35+
return data.chunks
36+
37+
def from_array(self, data: np.ndarray, chunks, **kwargs) -> "CubedArray":
38+
from cubed import Array, from_array
39+
40+
from xarray.core import indexing
41+
42+
# cubed-specific kwargs
43+
spec = kwargs.pop("spec", None)
44+
45+
if isinstance(data, Array):
46+
data = data.rechunk(chunks)
47+
elif is_duck_dask_array(data):
48+
raise TypeError("Trying to rechunk a dask array using cubed")
49+
else:
50+
if isinstance(data, indexing.ExplicitlyIndexed):
51+
# Unambiguously handle array storage backends (like NetCDF4 and h5py)
52+
# that can't handle general array indexing. For example, in netCDF4 you
53+
# can do "outer" indexing along two dimensions independent, which works
54+
# differently from how NumPy handles it.
55+
# da.from_array works by using lazy indexing with a tuple of slices.
56+
# Using OuterIndexer is a pragmatic choice: dask does not yet handle
57+
# different indexing types in an explicit way:
58+
# https://github.com/dask/dask/issues/2883
59+
data = indexing.ImplicitToExplicitIndexingAdapter(
60+
data, indexing.OuterIndexer
61+
)
62+
63+
if utils.is_dict_like(chunks):
64+
chunks = tuple(chunks.get(n, s) for n, s in enumerate(data.shape))
65+
66+
data = from_array(
67+
data,
68+
chunks,
69+
spec=spec,
70+
)
71+
72+
return data
73+
74+
def rechunk(self, data: "CubedArray", chunks, **kwargs) -> "CubedArray":
75+
return data.rechunk(chunks, **kwargs)
76+
77+
def compute(self, *data: "CubedArray", **kwargs) -> np.ndarray:
78+
from cubed import compute
79+
80+
return compute(*data, **kwargs)
81+
82+
@property
83+
def array_api(self) -> Any:
84+
from cubed import array_api
85+
86+
return array_api
87+
88+
def reduction(
89+
self,
90+
arr: T_ChunkedArray,
91+
func: Callable,
92+
combine_func: Optional[Callable] = None,
93+
aggregate_func: Optional[Callable] = None,
94+
axis: Optional[Union[int, Sequence[int]]] = None,
95+
dtype: Optional[np.dtype] = None,
96+
keepdims: bool = False,
97+
) -> T_ChunkedArray:
98+
from cubed.core.ops import reduction
99+
100+
return reduction(
101+
arr,
102+
func=func,
103+
combine_func=combine_func,
104+
aggegrate_func=aggregate_func, # TODO fix the typo in argument name in cubed
105+
axis=axis,
106+
dtype=dtype,
107+
keepdims=keepdims,
108+
)
109+
110+
def map_blocks(
111+
self,
112+
func,
113+
*args,
114+
dtype=None,
115+
chunks=None,
116+
drop_axis=[],
117+
new_axis=None,
118+
**kwargs,
119+
):
120+
from cubed.core.ops import map_blocks
121+
122+
return map_blocks(
123+
func,
124+
*args,
125+
dtype=dtype,
126+
chunks=chunks,
127+
drop_axis=drop_axis,
128+
new_axis=new_axis,
129+
**kwargs,
130+
)
131+
132+
def blockwise(
133+
self,
134+
func,
135+
out_ind,
136+
*args: Any,
137+
# can't type this as mypy assumes args are all same type, but blockwise args alternate types
138+
dtype=None,
139+
adjust_chunks=None,
140+
new_axes=None,
141+
align_arrays=True,
142+
target_store=None,
143+
**kwargs,
144+
):
145+
from cubed.core.ops import blockwise
146+
147+
# TODO where to get the target_store kwarg from? Filter down from a blockwise call? Set as attribute on CubedManager?
148+
149+
return blockwise(
150+
func,
151+
out_ind,
152+
*args,
153+
dtype=dtype,
154+
adjust_chunks=adjust_chunks,
155+
new_axes=new_axes,
156+
align_arrays=align_arrays,
157+
target_store=target_store,
158+
**kwargs,
159+
)
160+
161+
def apply_gufunc(
162+
self,
163+
func,
164+
signature,
165+
*args,
166+
axes=None,
167+
axis=None,
168+
keepdims=False,
169+
output_dtypes=None,
170+
output_sizes=None,
171+
vectorize=None,
172+
allow_rechunk=False,
173+
meta=None,
174+
**kwargs,
175+
):
176+
if allow_rechunk:
177+
raise NotImplementedError(
178+
"cubed.apply_gufunc doesn't support allow_rechunk"
179+
)
180+
if keepdims:
181+
raise NotImplementedError("cubed.apply_gufunc doesn't support keepdims")
182+
183+
from cubed import apply_gufunc
184+
185+
return apply_gufunc(
186+
func,
187+
signature,
188+
*args,
189+
axes=axes,
190+
axis=axis,
191+
output_dtypes=output_dtypes,
192+
output_sizes=output_sizes,
193+
vectorize=vectorize,
194+
**kwargs,
195+
)
196+
197+
def unify_chunks(
198+
self, *args, **kwargs
199+
) -> tuple[dict[str, T_Chunks], list["CubedArray"]]:
200+
from cubed.core import unify_chunks
201+
202+
return unify_chunks(*args, **kwargs)
203+
204+
def store(
205+
self,
206+
sources: Union["CubedArray", Sequence["CubedArray"]],
207+
targets: Union["ZarrArray", Sequence["ZarrArray"]],
208+
**kwargs: dict[str, Any],
209+
):
210+
"""Used when writing to any backend."""
211+
from cubed.core.ops import store
212+
213+
return store(
214+
sources,
215+
targets,
216+
**kwargs,
217+
)

cubed_xarray/tests/__init__.py

Whitespace-only changes.

pyproject.toml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[project]
2+
name = "cubed-xarray"
3+
authors = [
4+
{name = "Tom Nicholas", email = "[email protected]"}
5+
]
6+
description = "Interface for using cubed with xarray for parallel computation."
7+
license = {text = "Apache-2"}
8+
readme = "README.md"
9+
classifiers = [
10+
"Development Status :: 3 - Alpha",
11+
"Environment :: Console",
12+
"Intended Audience :: Science/Research",
13+
"License :: OSI Approved :: Apache Software License",
14+
"Operating System :: OS Independent",
15+
"Programming Language :: Python",
16+
"Programming Language :: Python :: 3.9",
17+
"Programming Language :: Python :: 3.10",
18+
"Programming Language :: Python :: 3.11",
19+
"Topic :: Scientific/Engineering",
20+
]
21+
requires-python = ">=3.9"
22+
dependencies = [
23+
"numpy >= 1.17",
24+
"xarray >= 0.16.1",
25+
"cubed >= 0.6.0",
26+
]
27+
dynamic = ["version"]
28+
29+
[project.urls]
30+
Home = "https://github.com/xarray-contrib/cubed-xarray"
31+
Documentation = "https://github.com/xarray-contrib/cubed-xarray#readme"
32+
33+
[tool.setuptools.packages.find]
34+
include = [
35+
"cubed_xarray",
36+
"cubed_xarray.tests",
37+
]
38+
39+
[build-system]
40+
requires = ["setuptools >= 64", "setuptools_scm >= 7.0"]
41+
build-backend = "setuptools.build_meta"
42+
43+
[tool.setuptools_scm]
44+
fallback_version = "999"
45+
46+
[tool.pytest.ini_options]
47+
junit_family = "xunit2"
48+
49+
[tool.isort]
50+
profile = "black"
51+
skip_gitignore = "true"
52+
force_to_top = "true"
53+
default_section = "THIRDPARTY"
54+
known_first_party = "cubed_xarray"

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cubed>=0.6.0
2+
numpy>=1.17.1
3+
xarray>=2023.03.0

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[options.entry_points]
2+
xarray.chunkmanagers =
3+
cubed = cubed_xarray.cubedmanager:CubedManager

setup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/user/bin/env python
2+
3+
from setuptools import setup
4+
5+
if __name__ == "__main__":
6+
setup()

0 commit comments

Comments
 (0)