File: regrid.py

package info (click to toggle)
cf-python 1.3.2%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 7,996 kB
  • sloc: python: 51,733; ansic: 2,736; makefile: 78; sh: 2
file content (764 lines) | stat: -rw-r--r-- 24,697 bytes parent folder | download
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
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
# -*- coding: utf-8 -*-
from numpy import array as numpy_array
from numpy import empty as numpy_empty
from numpy import where as numpy_where
from numpy import sum as numpy_sum
from .data.data import Data
from .coordinate import DimensionCoordinate
from .functions import REGRID_LOGGING
from . import _found_ESMF
if _found_ESMF:
    try:
        import ESMF
    except Exception as error:
        print("WARNING: Can not import ESMF for regridding: {0}".format(error))

class Regrid:
    """

Class containing all the methods required for accessing ESMF
regridding through ESMPY and the associated utility methods.

    """
    
    def __init__(self, srcfield, dstfield, srcfracfield, dstfracfield,
                 method='conservative'):
        """

Creates a handle for regridding fields from a source grid to a
destination grid that can then be used by the run_regridding method.

:Parameters:

    srcfield: ESMF.Field
        The source field with an associated grid to be used for
        regridding.

    dstfield: ESMF.Field
        The destination field with an associated grid to be used
        for regridding.

    srcfracfield: ESMF.Field
        A field to hold the fraction of the source field that
        contributes to conservative regridding.

    dstfracfield: ESMF.Field
        A field to hold the fraction of the source field that
        contributes to conservative regridding.

    method: string, optional
        By default the regridding method is set to 'conservative'. In
        this case first-order conservative regridding is used. If it
        is set to 'bilinear' then multilinear interpolation is used.
        If it is set to 'nearest_stod' then nearest neighbor
        interpolation is used where each destination point is mapped
        to the closest source point. A given source point may map to
        multiple destination points, but no destination point will
        receive input from more than one source point. If it is set
        to 'nearest_dtos' then nearest neighbor interpolation is used
        where each source point is mapped to the closest destination
        point. A given destination point may receive input from
        multiple source points, but no source point will map to more
        than one destination point.

    srchasbounds: bool, optional
        Whether or not the source field has bounds for use by the
        auto method.

    dsthasbounds: bool, optional
        Whether or not the destination fields has bounds for use by
        the auto method

        """
        # create a handle to the regridding method
        if method == 'conservative':
            regrid_method = ESMF.RegridMethod.CONSERVE
        elif method == 'bilinear':
            regrid_method = ESMF.RegridMethod.BILINEAR
        elif method == 'nearest_stod':
            regrid_method = ESMF.RegridMethod.NEAREST_STOD
        elif method == 'nearest_dtos':
            regrid_method = ESMF.RegridMethod.NEAREST_DTOS
        else:
            raise ValueError('Regrid method not recognised.')
        
        self.regridSrc2Dst = ESMF.Regrid(srcfield, dstfield, regrid_method=regrid_method,
                                         src_mask_values=numpy_array([0], dtype='int32'),
                                         dst_mask_values=numpy_array([0], dtype='int32'),
                                         src_frac_field=srcfracfield,
                                         dst_frac_field=dstfracfield,
                                         unmapped_action=ESMF.UnmappedAction.IGNORE)
    #--- End: def
    
    def destroy(self):
        """

Free the memory associated with the ESMF.Regrid instance.

        """
        self.regridSrc2Dst.destroy()
    #--- End: def
    
    @staticmethod
    def initialize():
        """

Check whether ESMF has been found. If not raise an import
error. Initialise the ESMPy manager. Whether logging is enabled or not
is determined by cf.REGRID_LOGGING. If it is then logging takes place
after every call to ESMPy.

:Returns:

    manager: ESMF.Manager
        A singleton instance of the ESMPy manager.

        """
        if not _found_ESMF:
            raise ImportError('The ESMF package is needed to support regridding.')
        
        manager = ESMF.Manager(debug=REGRID_LOGGING())
        return manager
    #--- End: def

    @staticmethod
    def create_grid(lon, lat, cyclic, mask=None, use_bounds=True):
        """

Create an ESMPy grid given 1D latitude and 1D longitude coordinates
for use as a source or destination grid in regridding. Optionally the
grid may have an associated mask.

:Parameters:

    lon : DimensionCoordinate
        The DimensionCoordinate containing the 1D longitude
        coordinates.

    lat : DimensionCoordinate
        The DimensionCoordinate containing the 1D latitude
        coordinates.

    cyclic : bool
        Whether or not the longitude is cyclic.

    mask : numpy.ndarray, optional
        An optional numpy array of booleans containing the grid points
        to mask.  Where the elements of mask are True the output grid
        is masked.

    use_bounds : bool, optional
        Whether to populate the grid corners with information from
        the bounds or not. True by default.

:Returns:

    out: ESMF.Grid
        The resulting ESMPy grid for use as a source or destination
        grid in regridding.

        """
        if use_bounds:
            # Get the bounds, creating them if they do not exist
            x_bounds = lon.get_bounds(create=True)
            y_bounds = lat.get_bounds(create=True).clip(-90, 90, 'degrees').array
            
            # If not set as cyclic already, check for cyclicity
            if not cyclic:
                cyclic = abs(x_bounds.datum(-1) - x_bounds.datum(0)) == Data(360,
                             'degrees')
            #--- End: if
            x_bounds = x_bounds.array
        #--- End: if
        
        # Create empty grid
        max_index = numpy_array([lon.size, lat.size], dtype='int32')
        if use_bounds:
            staggerLocs = [ESMF.StaggerLoc.CORNER, ESMF.StaggerLoc.CENTER]
        else:
            staggerLocs = [ESMF.StaggerLoc.CENTER]
        #--- End: if
        if cyclic:
            grid = ESMF.Grid(max_index, num_peri_dims=1, staggerloc=staggerLocs)
        else:
            grid = ESMF.Grid(max_index, staggerloc=staggerLocs)
        #--- End: if
        
        # Populate grid centres
        x, y = 0, 1
        gridXCentre = grid.get_coords(x, staggerloc=ESMF.StaggerLoc.CENTER)
        gridXCentre[...] = lon.array.reshape((lon.size, 1))
        gridYCentre = grid.get_coords(y, staggerloc=ESMF.StaggerLoc.CENTER)
        gridYCentre[...] = lat.array.reshape((1, lat.size))
        
        if use_bounds:
            # Populate grid corners
            gridCorner = grid.coords[ESMF.StaggerLoc.CORNER]
            if cyclic:
                gridCorner[x][...] = x_bounds[:, 0].reshape(lon.size, 1)
            else:
                n = x_bounds.shape[0]
                tmp_x = numpy_empty(n + 1)
                tmp_x[:n] = x_bounds[:,0]
                tmp_x[n] = x_bounds[-1,1]
                gridCorner[x][...] = tmp_x.reshape(lon.size + 1, 1)
            #--- End: if
            n = y_bounds.shape[0]
            tmp_y = numpy_empty(n + 1)
            tmp_y[:n] = y_bounds[:,0]
            tmp_y[n] = y_bounds[-1,1]
            gridCorner[y][...] = tmp_y.reshape(1, lat.size + 1)
        #--- End: if
        
        # Add the mask if appropriate
        if mask is not None:
            gmask = grid.add_item(ESMF.GridItem.MASK)
            gmask[...] = 1
            gmask[mask] = 0
        #--- End: if
        
        return grid
    #--- End: def
    
    @staticmethod
    def create_2Dgrid(lon, lat, x_order, y_order, cyclic, mask=None,
                      use_bounds=True):
        """

Create an ESMPy grid given 2D latitude and 2D longitude coordinates
for use as a source or destination grid in regridding. Optionally the
grid may have an associated mask.

:Parameters:

    lon: AuxiliaryCoordinate
        The AuxiliaryCoordinate containing the 2D longitude
        coordinates.

    lat : AuxiliaryCoordinate
        The AuxiliaryCoordinate containing the 2D latitude
        coordinates.

    x_order : tuple
        A tuple indicating the order of the x and y axes for
        longitude.

    y_order : tuple
        A tuple indicating the order of the x and y axes for
        latitude.

    cyclic : bool
        Whether or not the longitude is cyclic.

    mask : numpy.ndarray, optional
        An optional numpy array of booleans containing the grid
        points to mask.  Where the elements of mask are True the
        output grid is masked.

    use_bounds : bool, optional
        Whether to populate the grid corners with information from
        the bounds or not. True by default.

:Returns:

    out: ESMF.Grid
        The resulting ESMPy grid for use as a source or destination
        grid in regridding.

        """
        # Get the shape of the grid
        shape = lon.transpose(x_order).shape
        if lat.shape != lon.shape:
            raise ValueError('The longitude and latitude coordinates must' +
                             ' have the same shape.')
        
        # Check whether bounds exist or not, and get them if they do
        if use_bounds:
            x_bounds = lon.bounds
            y_bounds = lat.bounds.clip(-90, 90, 'degrees')
            n = x_bounds.shape[0]
            m = x_bounds.shape[1]
            x_bounds = x_bounds.array
            y_bounds = y_bounds.array
            
            tmp_x = numpy_empty((n + 1, m + 1))
            tmp_x[:n,:m] = x_bounds[:,:,0]
            tmp_x[:n,m] = x_bounds[:,-1,1]
            tmp_x[n,:m] = x_bounds[-1,:,3]
            tmp_x[n,m] = x_bounds[-1,-1,2]

            tmp_y = numpy_empty((n + 1, m + 1))
            tmp_y[:n,:m] = y_bounds[:,:,0]
            tmp_y[:n,m] = y_bounds[:,-1,1]
            tmp_y[n,:m] = y_bounds[-1,:,3]
            tmp_y[n,m] = y_bounds[-1,-1,2]

            x_bounds = tmp_x
            y_bounds = tmp_y
        #--- End: if
        
        # Create empty grid
        max_index = numpy_array(shape, dtype='int32')
        if use_bounds:
            staggerLocs = [ESMF.StaggerLoc.CORNER, ESMF.StaggerLoc.CENTER]
        else:
            staggerLocs = ESMF.StaggerLoc.CENTER
        #--- End: if
        if cyclic:
            grid = ESMF.Grid(max_index, num_peri_dims=1, staggerloc=staggerLocs)
        else:
            grid = ESMF.Grid(max_index, staggerloc=staggerLocs)
        #--- End: if
        
        # Populate grid centres
        x, y = 0, 1
        gridXCentre = grid.get_coords(x, staggerloc=ESMF.StaggerLoc.CENTER)
        gridXCentre[...] = lon.transpose(x_order).array
        gridYCentre = grid.get_coords(y, staggerloc=ESMF.StaggerLoc.CENTER)
        gridYCentre[...] = lat.transpose(y_order).array
        
        # Populate grid corners if there are bounds
        if use_bounds:
            gridCorner = grid.coords[ESMF.StaggerLoc.CORNER]
            x_bounds = x_bounds.transpose(x_order)
            y_bounds = y_bounds.transpose(y_order)
            if cyclic:
                x_bounds = x_bounds[:-1,:]
                y_bounds = y_bounds[:-1,:]
            gridCorner[x][...] = x_bounds
            gridCorner[y][...] = y_bounds
        #--- End: if
        
        # Add the mask if appropriate
        if mask is not None:
            gmask = grid.add_item(ESMF.GridItem.MASK)
            gmask[...] = 1
            gmask[mask] = 0
        #--- End: if
        
        return grid
    #--- End: def

    @staticmethod
    def create_cartesian_grid(coords, mask=None, use_bounds=True):
        """

Create a cartesian grid with between 1 and 3 dimensions given a
tuple or list of dimension coordinates and optionally a mask. The
number of coordinates passed will determine the dimensionality
of the grid.

:Parameters:

    coords : tuple or list of cf.DimensionCoordinate objects
        The coordinates specifying the grid. There must be between
        1 and 3 elements.

    mask : numpy.ndarray, optional
        An optional numpy array of booleans containing the grid
        points to mask. Where the elements of mask are True the
        output grid is masked.

    use_bounds : bool, optional
        Whether to populate the grid corners with information from
        the bounds or not. True by default.

:Returns:

    out: ESMF.Grid
        The resulting ESMPy grid for use as a source or destination
        grid in regridding.

        """
        
        # Test the dimensionality of the list of coordinates
        ndim = len(coords)
        if ndim < 1 or ndim > 3:
            raise ValueError('Cartesian grid must have between 1 and 3 ' +
                             'dimensions.')
        #--- End: if
        if ndim == 1:
            coords = [DimensionCoordinate(data=Data(0),
                                          bounds=Data([-1e-6,1e-6]))] + coords
            if mask is not None:
                mask = mask[None,:]
            ndim = 2
        #--- End: if
        
        shape = list()
        for coord in coords:
            shape.append(coord.size)
        
        # Initialize the grid
        max_index = numpy_array(shape, dtype='int32')
        if use_bounds:
            if ndim < 3:
                staggerLocs = [ESMF.StaggerLoc.CORNER, ESMF.StaggerLoc.CENTER]
            else:
                staggerLocs = [ESMF.StaggerLoc.CENTER_VCENTER,
                               ESMF.StaggerLoc.CORNER_VFACE]
            #--- End: if
        else:
            if ndim < 3:
                staggerLocs = [ESMF.StaggerLoc.CENTER]
            else:
                staggerLocs = [ESMF.StaggerLoc.CENTER_VCENTER]
            #--- End: if
        #--- End: if
        grid = ESMF.Grid(max_index, coord_sys=ESMF.CoordSys.CART,
                         staggerloc=staggerLocs)
        
        # Populate the grid centres
        for d in xrange(0, ndim):
            if ndim < 3:
                gridCentre = grid.get_coords(d, staggerloc=ESMF.StaggerLoc.CENTER)
            else:
                gridCentre = grid.get_coords(d, staggerloc=ESMF.StaggerLoc.CENTER_VCENTER)
            gridCentre[...] = coords[d].array.reshape([shape[d] if x == d else 1
                                                       for x in xrange(0, ndim)])
            #--- End: if
        #--- End: for
        
        # Populate grid corners
        if use_bounds:
            if ndim < 3:
                gridCorner = grid.coords[ESMF.StaggerLoc.CORNER]
            else:
                gridCorner = grid.coords[ESMF.StaggerLoc.CORNER_VFACE]
            #--- End: if
            for d in xrange(0, ndim):
                boundsD = coords[d].get_bounds(create=True).array
                if shape[d] > 1:
                    tmp = numpy_empty(shape[d] + 1)
                    tmp[0:-1] = boundsD[:, 0]
                    tmp[-1] = boundsD[-1, 1]
                    boundsD = tmp
                #--- End: if
                gridCorner[d][...] = boundsD.reshape([shape[d] + 1 if x == d else 1
                                                      for x in xrange(0, ndim)])
            #--- End: for
        #--- End: if
        
        # Add the mask if appropriate
        if mask is not None:
            gmask = grid.add_item(ESMF.GridItem.MASK)
            gmask[...] = 1
            gmask[mask] = 0
        #--- End: if
        
        return grid
    #--- End: def

    @staticmethod
    def create_field(grid, name):
        """

Create an ESMPy field for use as a source or destination field in
regridding given an ESMPy grid and a name.

:Parameters:

    grid : ESMF.Grid
        The ESMPy grid to use in creating the field.

    name : str
        The name to give the field.

:Returns:

    out : ESMF.Field
        The resulting ESMPy field for use as a source or destination
        field in regridding.

        """
        field = ESMF.Field(grid, name)
        return field
    #--- End: def
    
    def run_regridding(self, srcfield, dstfield):
        dstfield = self.regridSrc2Dst(srcfield, dstfield,
                                      zero_region=ESMF.Region.SELECT)
        return dstfield
    #--- End: def
    
    @staticmethod
    def concatenate_data(data_list, axis):
        """

Concatenates a list of Data objects into a single Data object along
the specified access (see cf.Data.concatenate for details). In the
case that the list contains only one element, that element is simply
returned.

:Parameters:

    data_list : list
        The list of data objects to concatenate.

    axis : int
        The axis along which to perform the concatenation.

:Returns:

    out : Data
        The resulting single Data object.

        """
        if len(data_list) > 1:
            return Data.concatenate(data_list, axis=axis)
        else:
            assert len(data_list) == 1
            return data_list[0]
        #--- End: if
    #--- End: def
    
    @staticmethod
    def reconstruct_sectioned_data(sections):
        """

Expects a dictionary of Data objects with ordering information as
keys, as output by the section method when called with a Data
object. Returns a reconstructed cf.Data object with the sections in
the original order.

:Parameters:

    sections : dict
        The dictionary or Data objects with ordering information as
        keys.

:Returns:

    out : Data
        The resulting reconstructed Data object.

        """
        ndims = len(sections.keys()[0])
        for i in range(ndims - 1, -1, -1):
            keys = sorted(sections.keys())
            if i==0:
                if keys[0][i] is None:
                    assert len(keys) == 1
                    return sections.values()[0]
                else:
                    data_list = []
                    for k in keys:
                        data_list.append(sections[k])
                    return Regrid.concatenate_data(data_list, i)
                #--- End: if
            else:
                if keys[0][i] is None:
                    pass
                else:
                    new_sections = {}
                    new_key = keys[0][:i]
                    data_list = []
                    for k in keys:
                        if k[:i] == new_key:
                            data_list.append(sections[k])
                        else:
                            new_sections[new_key] = Regrid.concatenate_data(data_list, i)
                            new_key = k[:i]
                            data_list = [sections[k]]
                        #--- End: if
                    new_sections[new_key] = Regrid.concatenate_data(data_list, i)
                    sections = new_sections
                #--- End: if
            #--- End: if
    #--- End: def
    
    @staticmethod
    def get_latlong(f, name):
        """

Retrieve the latitude and longitude coordinates of a field for
regridding and the associated informarion required. If 1D lat/long
coordinates are found then these are returned, otherwise 2D lat/long
coordinates are searched for and if found returned.

:Parameters:

    f : Field
        The field to retrieve coordinates from.

    name : string
        A name to identify the field in error messages.

:Returns:

    x : Coordinate
        The x coordinate (1D dimension coordinate or 2D
        auxilliary coordinate).

    y : Coordinate
        The y coordinate (1D dimension coordinate or 2D
        auxilliary coordinate).

    x_axis : string
        The key of the x dimension coordinate.

    y_axis : string
        The key of the y dimension coordinate.

    x_key : string
        The key of the x coordinate (1D dimension
        coordinate or 2D auxilliary coordinate).

    y_key : string
        The key of the y coordinate (1D dimension
        coordinate or 2D auxilliary coordinate).

    x_size : int
        The size of the x dimension coordinate.

    y_size : int
        The size of the y dimension coordinate.

    f_2D : bool
        True if 2D auxiliary coordinates are returned.

        """
        # Retrieve the field's X and Y dimension coordinates
        x = f.dims('X')
        y = f.dims('Y')
        if len(x) != 1 or len(y) != 1:
            raise ValueError('Unique dimension coordinates specifying the X' +
                             ' and Y axes of the ' + name + ' field not found.')
        #--- End: if
        x_axis, x = x.popitem()
        y_axis, y = y.popitem()
        x_key = x_axis
        y_key = y_axis        
        x_size = x.size
        y_size = y.size

        # If 1D latitude and longitude coordinates for the field are not found
        # search for 2D auxiliary coordinates.
        if not x.Units.islongitude or not y.Units.islatitude:
            lon_found = False
            lat_found = False
            for key, aux in f.auxs(ndim=2).iteritems():
                if aux.Units.islongitude:
                    if lon_found:
                        raise ValueError('The 2D auxiliary longitude coordinate' +
                                         ' of the ' + name + ' field is not unique.')
                    else:
                        lon_found = True
                        x = aux
                        x_key = key
                    #--- End: if
                #--- End: if
                if aux.Units.islatitude:
                    if lat_found:
                        raise ValueError('The 2D auxiliary latitude coordinate' +
                                         ' of the ' + name + ' field is not unique.')
                    else:
                        lat_found = True
                        y = aux
                        y_key = key
                    #--- End: if
                #--- End: if
            if not lon_found or not lat_found:
                raise ValueError('Both longitude and latitude coordinates' +
                                 ' were not found for the ' + name + ' field.')
            f_2D = True
        else:
            f_2D = False
        #--- End: if
        
        return x, y, x_axis, y_axis, x_key, y_key, x_size, y_size, f_2D
    #--- End: def
    
    @staticmethod
    def get_cartesian_coords(f, name, axes):
        '''

Retrieve the specified cartesian dimension coordinates of a field and their
corresponding keys.

:Parameters:

    f : Field
        The field to retrieve dimension coordinates from.

    name : string
        A name to identify the field in error messages.

    axes : iterable
        Specifiers for the dimension coordinates to be retrieved. See
        cf.Field.axes for details.

:Returns:

    axis_keys : list
        A list of the keys of the dimension coordinates retrieved.

    coords : list
        A list of the dimension coordinates retrieved.

        '''
        axis_keys = []
        for axis in axes:
            tmp = f.axes(axis)
            if len(tmp) != 1:
                raise ValueError('Each item in axes keywrod must specify ' +
                                 'exactly one axis.')
            axis_keys.append(tmp.pop())
        
        coords = []
        for key in axis_keys:
            d = f.dim(key)
            if d is None:
                raise ValueError('No ' + name + ' dimension coordinate ' +
                                 'matches key ' + key + '.')
            coords.append(d)
        
        return axis_keys, coords

    @staticmethod
    def compute_mass_grid(valuefield, areafield, dofrac=False, fracfield=None, 
                      uninitval=422397696.):
        '''

Compute the mass of a data field.

:Parameters:

    
    valuefield : ESMF.Field
        This contains data values of a field built on the cells
        of a grid.

    areafield : ESMF.Field
        This contains the areas associated with the grid cells.

    fracfield : ESMF.Field
        This contains the fractions of each cell which contributed
        to a regridding operation involving 'valuefield.

    dofrac : bool
        This gives the option to not use the 'fracfield'.

    uninitval : float
        The value uninitialised cells take.

:Returns:

    mass : float
        The mass of the data field is computed.

        '''
        mass = 0.0
        areafield.get_area()
        
        ind = numpy_where(valuefield.data != uninitval)
        
        if dofrac:
            mass = numpy_sum(areafield.data[ind] * valuefield.data[ind] * fracfield.data[ind])
        else:
            mass = numpy_sum(areafield.data[ind] * valuefield.data[ind])
        
        return mass
    #--- End: def

#--- End: class