Skip to content

Commit 170ca84

Browse files
authored
Merge pull request #312 from davidhassell/dask2
Introduction of Dask for all data manipulations
2 parents e0436b0 + 4b9995d commit 170ca84

62 files changed

Lines changed: 10939 additions & 2366 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Changelog.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
Version NEXTVERSION
22
-------------------
33

4-
**2024-??-??**
4+
**2024-12-??**
55

6+
* Introduction of `dask` for all data manipulations
7+
https://github.com/NCAS-CMS/cfdm/issues/317)
68
* Fix bug that returned incorrect results when an invalid identifer is
79
provided to `cf.Field.cell_methods`
810
(https://github.com/NCAS-CMS/cfdm/issues/299)
@@ -24,10 +26,11 @@ Version NEXTVERSION
2426
attribute (https://github.com/NCAS-CMS/cfdm/issues/303)
2527
* New class `cfdm.H5netcdfArray`
2628
* New class `cfdm.NetCDF4Array`
29+
* Changed dependency: ``numpy>=1.15,<2.0``
2730
* New dependency: ``h5netcdf>=1.3.0``
2831
* New dependency: ``h5py>=3.10.0``
2932
* New dependency: ``s3fs>=2024.6.0``
30-
* New dependency: ``dask>=2024.6.0``
33+
* New dependency: ``dask>=2024.6.0,<=2024.7.1``
3134
* Removed dependency: ``netcdf_flattener``
3235

3336
----

cfdm/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,12 @@
143143
raise ImportError(_error0 + str(error1))
144144

145145
_minimum_vn = "2024.6.0"
146-
if Version(dask.__version__) < Version(_minimum_vn):
146+
_maximum_vn = "2024.7.1"
147+
_dask_version = Version(dask.__version__)
148+
if not Version(_minimum_vn) <= _dask_version <= Version(_maximum_vn):
147149
raise ValueError(
148-
f"Bad scipy version: cfdm requires dask>={_minimum_vn}. "
149-
f"Got {dask.__version__} at {dask.__file__}"
150+
f"Bad dask version: cfdm requires {_minimum_vn}<=dask<={_maximum_vn}. "
151+
f"Got {_dask_version} at {dask.__file__}"
150152
)
151153

152154
from .constants import masked
@@ -160,10 +162,12 @@
160162
RTOL,
161163
abspath,
162164
atol,
165+
chunksize,
163166
configuration,
164167
environment,
165168
integer_dtype,
166169
log_level,
170+
parse_indices,
167171
rtol,
168172
unique_constructs,
169173
_disable_logging,
@@ -238,6 +242,8 @@
238242
from .datum import Datum
239243
from .interiorring import InteriorRing
240244

245+
from .units import Units
246+
241247
from .auxiliarycoordinate import AuxiliaryCoordinate
242248
from .cellconnectivity import CellConnectivity
243249
from .cellmeasure import CellMeasure

cfdm/cfdmimplementation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1378,7 +1378,7 @@ def get_data_maximum(self, parent):
13781378
Scalar `Data` instance
13791379
13801380
"""
1381-
return parent.data.maximum(squeeze=True)
1381+
return parent.data.max(squeeze=True)
13821382

13831383
def get_data_sum(self, parent):
13841384
"""Return the sum of the data.

cfdm/constants.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
from enum import Enum
44

55
import numpy as np
6+
from dask import config
7+
from dask.utils import parse_bytes
8+
9+
_CHUNKSIZE = "128 MiB"
10+
config.set({"array.chunk-size": _CHUNKSIZE})
611

712
"""A dictionary of useful constants.
813
@@ -23,11 +28,15 @@
2328
The minimal level of seriousness for which log messages are
2429
shown. See `cfdm.log_level`.
2530
31+
CHUNKSIZE: `int`
32+
The Dask chunk size (in bytes). See `cfdm.chunksize`.
33+
2634
"""
2735
CONSTANTS = {
2836
"ATOL": sys.float_info.epsilon,
2937
"RTOL": sys.float_info.epsilon,
3038
"LOG_LEVEL": logging.getLevelName(logging.getLogger().level),
39+
"CHUNKSIZE": parse_bytes(_CHUNKSIZE),
3140
}
3241

3342

cfdm/core/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@
5353
raise ImportError(_error0 + str(error1))
5454

5555
_minimum_vn = "1.15"
56+
_maximum_vn = "2.0"
57+
_np_version = Version(np.__version__)
58+
if not Version(_minimum_vn) <= _np_version <= Version(_maximum_vn):
59+
raise ValueError(
60+
"Bad numpy version: cfdm requires "
61+
f"{_minimum_vn}<=numpy<={_maximum_vn}. "
62+
f"Got {_np_version} at {np.__file__}"
63+
)
64+
5665
if Version(np.__version__) < Version(_minimum_vn):
5766
raise ValueError(
5867
f"Bad numpy version: cfdm.core requires numpy>={_minimum_vn}. "

cfdm/core/abstract/propertiesdata.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ def __data__(self):
108108

109109
@property
110110
def data(self):
111-
"""Return the data.
111+
"""The data.
112112
113-
``f.data`` is equivalent to ``f.get_data()``
113+
``f.data`` is equivalent to ``f.get_data()``.
114114
115-
Note that a `Data` instance is returned. Use its `array`
116-
attribute to return the data as a `numpy` array.
115+
Note that a `Data` instance is returned. Use the `array`
116+
attribute to get the data as a `numpy` array.
117117
118118
The units, calendar and fill value properties are, if set,
119119
inserted into the data.
@@ -143,6 +143,17 @@ def data(self):
143143
"""
144144
return self.get_data()
145145

146+
@data.setter
147+
def data(self, value):
148+
raise AttributeError(
149+
"Can't set attribute 'data'. Use the 'set_data' method, "
150+
"or assignment by indexing."
151+
)
152+
153+
@data.deleter
154+
def data(self):
155+
self.del_data()
156+
146157
@property
147158
def dtype(self):
148159
"""Data-type of the data elements.

cfdm/core/data/data.py

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ def __init__(
112112
fill_value = None
113113

114114
if units is not None:
115-
self.set_units(units)
115+
self._set_component("units", units, copy=False)
116116

117117
if calendar is not None:
118-
self.set_calendar(calendar)
118+
self._set_component("calendar", calendar, copy=False)
119119

120120
if fill_value is not None:
121-
self.set_fill_value(fill_value)
121+
self._set_component("fill_value", fill_value, copy=False)
122122

123123
if _use_array and array is not None:
124124
self._set_Array(array, copy=copy)
@@ -135,9 +135,6 @@ def __data__(self):
135135
"""
136136
return self
137137

138-
# ----------------------------------------------------------------
139-
# Attributes
140-
# ----------------------------------------------------------------
141138
@property
142139
def array(self):
143140
"""Return an independent `numpy` array containing the data.
@@ -282,9 +279,6 @@ def size(self):
282279
"""
283280
return self._get_Array().size
284281

285-
# ----------------------------------------------------------------
286-
# Methods
287-
# ----------------------------------------------------------------
288282
def copy(self, array=True):
289283
"""Return a deep copy of the data.
290284
@@ -365,14 +359,6 @@ def del_calendar(self, default=ValueError()):
365359
"""
366360
return self._del_component("calendar", default)
367361

368-
# try:
369-
# return self._del_component("calendar")
370-
# except ValueError:
371-
# print(88888, repr(default))
372-
# return self._default(
373-
# default, f"{self.__class__.__name__!r} has no calendar"
374-
# )
375-
376362
def del_fill_value(self, default=ValueError()):
377363
"""Delete the fill value.
378364
@@ -414,14 +400,6 @@ def del_fill_value(self, default=ValueError()):
414400
"""
415401
return self._del_component("fill_value", default)
416402

417-
# try:
418-
# return self._del_component("fill_value")
419-
# except ValueError:
420-
# return self._default(
421-
# default,
422-
# "{!r} has no fill value".format(self.__class__.__name__),
423-
# )
424-
425403
def del_units(self, default=ValueError()):
426404
"""Delete the units.
427405
@@ -460,13 +438,6 @@ def del_units(self, default=ValueError()):
460438
"""
461439
return self._del_component("units", default)
462440

463-
# try:
464-
# return self._del_component("units")
465-
# except ValueError:
466-
# return self._default(
467-
# default, "{!r} has no units".format(self.__class__.__name__)
468-
# )
469-
470441
def get_calendar(self, default=ValueError()):
471442
"""Return the calendar.
472443
@@ -506,13 +477,6 @@ def get_calendar(self, default=ValueError()):
506477
"""
507478
return self._get_component("calendar", default)
508479

509-
# try:
510-
# return self._get_component("calendar")
511-
# except ValueError:
512-
# return self._default(
513-
# default, "{!r} has no calendar".format(self.__class__.__name__)
514-
# )
515-
516480
def _get_Array(self, default=ValueError()):
517481
"""Return the array object.
518482
@@ -537,13 +501,6 @@ def _get_Array(self, default=ValueError()):
537501
"""
538502
return self._get_component("array", default)
539503

540-
# try:
541-
# return self._get_component("array")
542-
# except ValueError:
543-
# return self._default(
544-
# default, "{!r} has no array".format(self.__class__.__name__)
545-
# )
546-
547504
def get_fill_value(self, default=ValueError()):
548505
"""Return the missing data value.
549506
@@ -587,14 +544,6 @@ def get_fill_value(self, default=ValueError()):
587544
"""
588545
return self._get_component("fill_value", default)
589546

590-
# try:
591-
# return self._get_component("fill_value")
592-
# except ValueError:
593-
# return self._default(
594-
# default,
595-
# "{!r} has no fill value".format(self.__class__.__name__),
596-
# )
597-
598547
def get_units(self, default=ValueError()):
599548
"""Return the units.
600549
@@ -633,13 +582,6 @@ def get_units(self, default=ValueError()):
633582
"""
634583
return self._get_component("units", default)
635584

636-
# try:
637-
# return self._get_component("units")
638-
# except ValueError:
639-
# return self._default(
640-
# default, "{!r} has no units".format(self.__class__.__name__)
641-
# )
642-
643585
def has_units(self):
644586
"""Whether units have been set.
645587
@@ -834,9 +776,6 @@ def set_fill_value(self, value):
834776
False
835777
836778
"""
837-
# if value is None:
838-
# self.del_fill_value(None)
839-
840779
self._set_component("fill_value", value, copy=False)
841780

842781
def set_units(self, value):

cfdm/data/boundsfromnodesarray.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from .abstract import MeshArray
2+
from .mixin import CompressedArrayMixin
23
from .subarray import BoundsFromNodesSubarray
34

45

5-
class BoundsFromNodesArray(MeshArray):
6+
class BoundsFromNodesArray(CompressedArrayMixin, MeshArray):
67
"""An array of cell bounds defined by UGRID node coordinates.
78
89
The UGRID node coordinates contain the locations of the nodes of

cfdm/data/cellconnectivityarray.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from .abstract import MeshArray
2+
from .mixin import CompressedArrayMixin
23
from .subarray import CellConnectivitySubarray
34

45

5-
class CellConnectivityArray(MeshArray):
6+
class CellConnectivityArray(CompressedArrayMixin, MeshArray):
67
"""A connectivity array derived from a UGRID connectivity variable.
78
89
A UGRID connectivity variable contains indices which map each cell

0 commit comments

Comments
 (0)