-
Notifications
You must be signed in to change notification settings - Fork 19
/
regridoperator.py
548 lines (407 loc) · 15.2 KB
/
regridoperator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
from copy import deepcopy
from cfdm import Container
from ..decorators import _display_or_return
from ..functions import _DEPRECATION_ERROR_ATTRIBUTE, _DEPRECATION_ERROR_METHOD
from ..mixin_container import Container as mixin_Container
class RegridOperator(mixin_Container, Container):
"""A regridding operator between two grids.
Regridding is the process of interpolating from one grid
resolution to a different grid resolution.
The regridding operator stores the regridding weights; auxiliary
information, such as the grid shapes; the CF metadata for the
destination grid; and the source grid coordinates.
"""
def __init__(
self,
weights=None,
row=None,
col=None,
coord_sys=None,
method=None,
src_shape=None,
dst_shape=None,
src_cyclic=None,
dst_cyclic=None,
src_mask=None,
dst_mask=None,
src_coords=None,
src_bounds=None,
start_index=0,
src_axes=None,
dst_axes=None,
dst=None,
):
"""**Initialization**
:Parameters:
weights: array_like
The 1-d array of regridding weights for locations in
the 2-d dense weights matrix. The locations are defined
by the *row* and *col* parameters.
row, col: array_like, array_like
The 1-d arrays of the row and column indices of the
regridding weights in the dense weights matrix, which
has J rows and I columns, where J and I are the total
number of cells in the destination and source grids
respectively. See the *start_index* parameter.
coord_sys: `str`
The name of the coordinate system of the source and
destination grids. Either ``'spherical'`` or
``'Cartesian'``.
method: `str`
The name of the regridding method.
src_shape: sequence of `int`
The shape of the source grid.
dst_shape: sequence of `int`
The shape of the destination grid.
src_cyclic: `bool`
For spherical regridding, specifies whether or not the
source grid longitude axis is cyclic.
dst_cyclic: `bool`
For spherical regridding, specifies whether or not the
destination grid longitude axis is cyclic.
src_mask: `numpy.ndarray` or `None`, optional
If a `numpy.ndarray` with shape *src_shape* then this
is the source grid mask that was used during the
creation of the *weights*. If *src_mask* is a scalar
array with value `False`, then this is equivalent to a
source grid mask with shape *src_shape* entirely
populated with `False`.
If `None` (the default), then the weights are assumed
to have been created assuming no source grid masked
cells.
dst_mask: `numpy.ndarray` or `None`, optional
A destination grid mask to be applied to the weights
matrix, in addition to those destination grid cells
that have no non-zero weights. If `None` (the default)
then no additional destination grid cells are
masked. If a Boolean `numpy` array then it must have
shape *dst_shape*, and a value of `True` signifies a
masked destination grid cell.
start_index: `int`, optional
Specify whether the *row* and *col* parameters use 0-
or 1-based indexing. By default 0-based indexing is
used.
parameters: Deprecated at version 3.14.0
Use keyword parameters instead.
dst: `Field` or `Domain`
The definition of the destination grid.
dst_axes: `dict` or sequence or `None`, optional
The destination grid axes to be regridded.
src_axes: `dict` or sequence or `None`, optional
The source grid axes to be regridded.
"""
super().__init__()
if weights is None or row is None or col is None:
# This to allow a no-arg init!
return
self._set_component("weights", weights, copy=False)
self._set_component("row", row, copy=False)
self._set_component("col", col, copy=False)
self._set_component("coord_sys", coord_sys, copy=False)
self._set_component("method", method, copy=False)
self._set_component("src_mask", src_mask, copy=False)
self._set_component("dst_mask", dst_mask, copy=False)
self._set_component("src_cyclic", bool(src_cyclic), copy=False)
self._set_component("dst_cyclic", bool(dst_cyclic), copy=False)
self._set_component("src_shape", tuple(src_shape), copy=False)
self._set_component("dst_shape", tuple(dst_shape), copy=False)
self._set_component("src_coords", tuple(src_coords), copy=False)
self._set_component("src_bounds", tuple(src_bounds), copy=False)
self._set_component("start_index", int(start_index), copy=False)
self._set_component("src_axes", src_axes, copy=False)
self._set_component("dst_axes", dst_axes, copy=False)
self._set_component("dst", dst, copy=False)
def __repr__(self):
"""x.__repr__() <==> repr(x)"""
return (
f"<CF {self.__class__.__name__}: {self.coord_sys} {self.method}>"
)
@property
def col(self):
"""The 1-d array of the column indices of the regridding
weights.
.. versionadded:: 3.14.0
"""
return self._get_component("col")
@property
def coord_sys(self):
"""The name of the regridding coordinate system.
.. versionadded:: 3.14.0
"""
return self._get_component("coord_sys")
@property
def dst(self):
"""The definition of the destination grid.
Either a `Field` or` Domain`.
.. versionadded:: 3.14.0
"""
return self._get_component("dst")
@property
def dst_axes(self):
"""The destination grid axes to be regridded.
If not required then this will be `None`.
.. versionadded:: 3.14.0
"""
return self._get_component("dst_axes")
@property
def dst_cyclic(self):
"""Whether or not the destination grid longitude axis is
cyclic."""
return self._get_component("dst_cyclic")
@property
def dst_mask(self):
"""A destination grid mask to be applied to the weights matrix.
If `None` then no additional destination grid cells are
masked.
If a Boolean `numpy` array then it is required that this mask
is applied to the weights matrix prior to use in a regridding
operation. The mask must have shape `!dst_shape`, and a value
of `True` signifies a masked destination grid cell.
.. versionadded:: 3.14.0
"""
return self._get_component("dst_mask")
@property
def dst_shape(self):
"""The shape of the destination grid.
.. versionadded:: 3.14.0
"""
return self._get_component("dst_shape")
@property
def method(self):
"""The name of the regridding method.
.. versionadded:: 3.14.0
"""
return self._get_component("method")
@property
def name(self):
"""The name of the regridding method."""
_DEPRECATION_ERROR_ATTRIBUTE(
self,
"name",
version="3.14.0",
removed_at="5.0.0",
)
@property
def row(self):
"""The 1-d array of the row indices of the regridding weights.
.. versionadded:: 3.14.0
"""
return self._get_component("row")
@property
def src_axes(self):
"""The source grid axes to be regridded.
If not required then this will be `None`.
.. versionadded:: 3.14.0
"""
return self._get_component("src_axes")
@property
def src_bounds(self):
"""The bounds of the source grid cells.
.. versionadded:: 3.14.0
"""
return self._get_component("src_bounds")
@property
def src_coords(self):
"""The coordinates of the source grid cells.
.. versionadded:: 3.14.0
"""
return self._get_component("src_coords")
@property
def src_cyclic(self):
"""Whether or not the source grid longitude axis is cyclic.
.. versionadded:: 3.14.0
"""
return self._get_component("src_cyclic")
@property
def src_mask(self):
"""The source grid mask that was applied during the weights
creation.
If `None` then the weights are assumed to have been created
assuming no source grid masked cells.
If a Boolean `numpy.ndarray` with shape `!src_shape` then this
is the source grid mask that was used during the creation of
the *weights*.
.. versionadded:: 3.14.0
"""
return self._get_component("src_mask")
@property
def src_shape(self):
"""The shape of the source grid.
.. versionadded:: 3.14.0
"""
return self._get_component("src_shape")
@property
def start_index(self):
"""The start index of the row and column indices.
.. versionadded:: 3.14.0
"""
return self._get_component("start_index")
@property
def weights(self):
"""The 1-d array of the regridding weights.
.. versionadded:: 3.14.0
"""
return self._get_component("weights")
def copy(self):
"""Return a deep copy.
:Returns:
`RegridOperator`
The deep copy.
"""
return type(self)(
self.weights.copy(),
self.row.copy(),
self.col.copy(),
method=self.method,
src_shape=self.src_shape,
dst_shape=self.dst_shape,
src_cyclic=self.src_cyclic,
dst_cyclic=self.dst_cyclic,
src_mask=deepcopy(self.src_mask),
dst_mask=deepcopy(self.dst_mask),
src_coords=deepcopy(self.src_coords),
src_bounds=deepcopy(self.src_bounds),
coord_sys=self.coord_sys,
start_index=self.start_index,
src_axes=self.src_axes,
dst_axes=self.dst_axes,
dst=self.dst.copy(),
)
@_display_or_return
def dump(self, display=True):
"""A full description of the regrid operator.
Returns a description of all properties, including
the weights and their row and column indices.
.. versionadded:: 3.14.0
:Parameters:
display: `bool`, optional
If False then return the description as a string. By
default the description is printed.
:Returns:
{{returns dump}}
"""
_title = repr(self)[4:-1]
line = f"{''.ljust(len(_title), '-')}"
string = [line, _title, line]
for attr in (
"coord_sys",
"method",
"src_shape",
"dst_shape",
"src_cyclic",
"dst_cyclic",
"src_mask",
"dst_mask",
"src_coords",
"src_bounds",
"start_index",
"src_axes",
"dst_axes",
"dst",
"weights",
"row",
"col",
):
string.append(f"{attr}: {getattr(self, attr)!r}")
return "\n".join(string)
def get_parameter(self, parameter, *default):
"""Return a regrid operation parameter.
:Parameters:
parameter: `str`
The name of the parameter.
default: optional
Return the value of the *default* parameter if the
parameter has not been set.
If set to an `Exception` instance then it will be
raised instead.
.. versionadded:: 3.14.0
:Returns:
The value of the named parameter or the default value, if
set.
**Examples**
>>> r.get_parameter('dst_axes')
['domainaxis1', 'domainaxis0']
>>> r.get_parameter('x')
Traceback
...
ValueError: RegridOperator has no 'x' parameter
>>> r.get_parameter('x', 'missing')
'missing'
"""
_DEPRECATION_ERROR_METHOD(
self,
"get_parameter",
message="Using attributes instead.",
version="3.14.0",
removed_at="5.0.0",
)
try:
return self._get_component("parameters")[parameter]
except KeyError:
if default:
return default[0]
raise ValueError(
f"{self.__class__.__name__} has no {parameter!r} parameter"
)
def parameters(self):
"""Get the CF metadata parameters for the destination grid.
Deprecated at version 3.14.0.
Any parameter names and values are allowed, and it is assumed
that these are well defined during the creation and
subsequent use of a `RegridOperator` instance.
:Returns:
`dict`
The parameters.
**Examples**
>>> r.parameters()
{'dst': <CF Domain: {latitude(5), longitude(8), time(1)}>,
'dst_axes': ['domainaxis1', 'domainaxis2'],
'src_axes': None}
"""
_DEPRECATION_ERROR_METHOD(
self,
"parameters",
version="3.14.0",
removed_at="5.0.0",
)
def todense(self, order="C"):
"""Return the weights in dense format.
.. versionadded:: 3.14.0
.. seealso:: `tosparse`
:Parameters:
order: `str`, optional
Specify the memory layout of the returned weights
matrix. ``'C'`` (the default) means C order
(row-major), and``'F'`` means Fortran order
(column-major).
:Returns:
`numpy.ndarray`
The 2-d dense weights matrix, an array with with shape
``(J, I)``, where ``J`` is the number of destination
grid cells and ``I`` is the number of source grid
cells.
"""
return self.tosparse().todense(order=order)
def tosparse(self):
"""Return the weights in sparse COOrdinate format.
See `scipy.sparse._arrays.coo_array` for sparse format
details.
.. versionadded:: 3.14.0
.. seealso:: `todense`
:Returns:
`scipy.sparse._arrays.coo_array`
The sparse array of weights.
"""
from math import prod
from scipy.sparse import coo_array
row = self.row
col = self.col
start_index = self.start_index
if start_index:
row = row - start_index
col = col - start_index
src_size = prod(self.src_shape)
dst_size = prod(self.dst_shape)
return coo_array(
(self.weights, (row, col)), shape=[dst_size, src_size]
)