11
11
import dask .array as da
12
12
import numpy as np
13
13
14
- from iris ._lazy_data import concatenate
14
+ from iris ._lazy_data import _optimum_chunksize , concatenate , is_lazy_data
15
15
from iris .common import CFVariableMixin , CoordMetadata , metadata_manager_factory
16
16
import iris .coords
17
17
from iris .warnings import IrisIgnoringBoundsWarning
@@ -83,18 +83,18 @@ def _calculate_array(self, *dep_arrays, **other_args):
83
83
Parameters
84
84
----------
85
85
* dep_arrays : tuple of array-like
86
- arrays of data for each dependency.
86
+ Arrays of data for each dependency.
87
87
Must match the number of declared dependencies. All pre-aligned to the
88
88
same number of dimensions, and matching in dimensions,
89
89
i.e. all dimensions are == N(i) or 1, for each dim(i).
90
90
91
91
* other_args
92
- dict of keys providing class-specific additional arguments.
92
+ Dict of keys providing class-specific additional arguments.
93
93
94
94
Returns
95
95
-------
96
96
array-like
97
- result (normally lazy) with same dimensions as the dependencies,
97
+ A result (normally lazy) with same dimensions as the dependencies,
98
98
i.e. == N(i) for all i.
99
99
100
100
This is the basic derived calculation, defined by each hybrid class, which
@@ -117,51 +117,66 @@ def _derive_array(self, *dep_arrays, **other_args):
117
117
This routine is itself typically called by :meth:`make_coord`, to make both
118
118
points and bounds.
119
119
"""
120
- result = self ._calculate_array (* dep_arrays , ** other_args )
121
120
122
- # The dims of all the given components should be the same and, **presumably**,
123
- # the same as the result ??
124
- for i_dep , (dep , name ) in enumerate (zip (dep_arrays , self .dependencies .keys ())):
125
- if dep .ndim != result .ndim :
126
- msg = (
127
- f"Dependency #{ i_dep } , '{ name } ' has ndims={ dep .ndim } , "
128
- "not matching result {result.ndim!r}"
129
- " (shapes {dep.shape}/{result.shape})."
130
- )
131
- raise ValueError (msg )
121
+ def chunkslike (array ):
122
+ return array .chunksize if is_lazy_data (array ) else array .shape
123
+
124
+ result = self ._calculate_array (* dep_arrays , ** other_args )
132
125
133
126
# See if we need to improve on the chunking of the result
134
- from iris . _lazy_data import _optimum_chunksize
127
+ result_chunks = chunkslike ( result )
135
128
adjusted_chunks = _optimum_chunksize (
136
- chunks = result . chunksize ,
129
+ chunks = result_chunks ,
137
130
shape = result .shape ,
138
131
dtype = result .dtype ,
139
132
)
140
133
141
- if adjusted_chunks != result .chunksize :
134
+ # Does optimum_chunksize say we should have smaller chunks in some dimensions?
135
+ if np .any (adjusted_chunks < result_chunks ):
136
+ # co-broadcast all the deps to get same dimensions for each
137
+ dep_arrays = np .broadcast_arrays (* dep_arrays )
138
+
139
+ # The dims of all the given components should now be the same and, *presumably*,
140
+ # the same as the result ??
141
+ for i_dep , (dep , name ) in enumerate (
142
+ zip (dep_arrays , self .dependencies .keys ())
143
+ ):
144
+ if dep .ndim != result .ndim :
145
+ msg = (
146
+ f"Dependency #{ i_dep } , '{ name } ' has ndims={ dep .ndim } , "
147
+ f"not matching result { result .ndim !r} "
148
+ f" : respective shapes { dep .shape } , { result .shape } ."
149
+ )
150
+ raise ValueError (msg )
151
+
142
152
# Re-do the result calculation, re-chunking the inputs along dimensions
143
153
# which it is suggested to reduce.
144
154
# First make a (writable) copy of the inputs.....
145
155
new_deps = []
146
156
for i_dep , dep in enumerate (dep_arrays ):
147
157
# Reduce each dependency chunksize to the result chunksize if smaller.
148
- dep_chunks = dep .chunksize
149
- new_chunks = tuple ([
150
- min (dep_chunk , adj_chunk )
151
- for dep_chunk , adj_chunk in zip (dep_chunks , adjusted_chunks )
152
- ])
158
+ dep_chunks = chunkslike (dep )
159
+ new_chunks = tuple (
160
+ [
161
+ min (dep_chunk , adj_chunk )
162
+ for dep_chunk , adj_chunk in zip (dep_chunks , adjusted_chunks )
163
+ ]
164
+ )
153
165
# If the dep chunksize was reduced, replace with a rechunked version.
154
166
if new_chunks != dep_chunks :
167
+ if not is_lazy_data (dep ):
168
+ # I guess this is possible ?
169
+ # TODO: needs a test
170
+ dep = da .from_array (dep )
155
171
dep = dep .rechunk (new_chunks )
156
172
new_deps .append (dep )
157
173
158
174
# Finally, re-do the calculation, which hopefully results in a better
159
175
# overall chunking for the result
160
- result = self ._calculate_array (* new_deps )
176
+ result = self ._calculate_array (* new_deps , ** other_args )
161
177
162
178
return result
163
179
164
-
165
180
@abstractmethod
166
181
def make_coord (self , coord_dims_func ):
167
182
"""Return a new :class:`iris.coords.AuxCoord` as defined by this factory.
@@ -1108,7 +1123,9 @@ def dependencies(self):
1108
1123
zlev = self .zlev ,
1109
1124
)
1110
1125
1111
- def _calculate_array (self , sigma , eta , depth , depth_c , zlev , nsigma , coord_dims_func ):
1126
+ def _calculate_array (
1127
+ self , sigma , eta , depth , depth_c , zlev , nsigma , coord_dims_func
1128
+ ):
1112
1129
# Calculate the index of the 'z' dimension in the input arrays.
1113
1130
# First find the cube 'z' dimension ...
1114
1131
[cube_z_dim ] = coord_dims_func (self .dependencies ["zlev" ])
0 commit comments