예제 #1
0
def sample_size_f(a, axis=None, masked=False):
    """Return the sample size.

    :Parameters:

        axis: `int`, optional
            Axis along which to operate. By default, flattened input
            is used.

    :Returns:

        `numpy.ndarray`

    """
    if masked:
        N = numpy_sum(~a.mask, axis=axis, dtype=float)
        if not numpy_ndim(N):
            N = numpy_asanyarray(N)
    else:
        if axis is None:
            N = numpy_array(a.size, dtype=float)
        else:
            shape = a.shape
            N = numpy_empty(shape[:axis] + shape[axis + 1 :], dtype=float)
            N[...] = shape[axis]
    # --- End: if

    return asanyarray(N)
예제 #2
0
def make_surface(array):
    """pygame.surfarray.make_surface (array): return Surface

    Copy an array to a new surface.

    Create a new Surface that best resembles the data and format on the
    array. The array can be 2D or 3D with any sized integer values.
    """ 
    if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats:
        array = numpy_rint(array, numpy_empty(array.shape, dtype=numpy_uint32))
    return pix_make_surface (array)
예제 #3
0
def blit_array (surface, array):
    """pygame.surfarray.blit_array(Surface, array): return None

    Blit directly from a array values.

    Directly copy values from an array into a Surface. This is faster than
    converting the array into a Surface and blitting. The array must be the
    same dimensions as the Surface and will completely replace all pixel
    values. Only integer, ascii character and record arrays are accepted.

    This function will temporarily lock the Surface as the new values are
    copied.
    """
    if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats:
        array = numpy_rint(array, numpy_empty(array.shape, dtype=numpy_uint32))
    return array_to_surface(surface, array)
예제 #4
0
    def __getitem__(self, indices):
        """x.__getitem__(indices) <==> x[indices]

        Returns a numpy array.

        """
        if indices is Ellipsis:
            array_shape = self.shape
        else:
            array_shape = []
            for index in parse_indices(self.shape, indices):
                if isinstance(index, slice):
                    step = index.step
                    if step == 1:
                        array_shape.append(index.stop - index.start)
                    elif step == -1:
                        stop = index.stop
                        if stop is None:
                            array_shape.append(index.start + 1)
                        else:
                            array_shape.append(index.start - index.stop)
                    else:
                        stop = index.stop
                        if stop is None:
                            stop = -1

                        a, b = divmod(stop - index.start, step)
                        if b:
                            a += 1
                        array_shape.append(a)
                else:
                    array_shape.append(len(index))
        # --- End: if

        if self.fill_value() is cf_masked:
            return numpy_ma_masked_all(array_shape, dtype=self.dtype)
        elif self.fill_value() is not None:
            return numpy_full(array_shape,
                              fill_value=self.fill_value(),
                              dtype=self.dtype)
        else:
            return numpy_empty(array_shape, dtype=self.dtype)
예제 #5
0
def map_array(surface, array):
    """pygame.numpyarray.map_array(Surface, array3d): return array2d

    map a 3d array into a 2d array

    Convert a 3D array into a 2D array. This will use the given Surface
    format to control the conversion.

    Note: arrays do not need to be 3D, as long as the minor axis has
    three elements giving the component colours, any array shape can be
    used (for example, a single colour can be mapped, or an array of
    colours). The array shape is limited to eleven dimensions maximum,
    including the three element minor axis.
    """
    if array.ndim == 0:
        raise ValueError("array must have at least 1 dimension")
    shape = array.shape
    if shape[-1] != 3:
        raise ValueError("array must be a 3d array of 3-value color data")
    target = numpy_empty(shape[:-1], numpy.int32)
    pix_map_array(target, array, surface)
    return target
예제 #6
0
def map_array(surface, array):
    """pygame.numpyarray.map_array(Surface, array3d): return array2d

    map a 3d array into a 2d array

    Convert a 3D array into a 2D array. This will use the given Surface
    format to control the conversion.

    Note: arrays do not need to be 3D, as long as the minor axis has
    three elements giving the component colours, any array shape can be
    used (for example, a single colour can be mapped, or an array of
    colours). The array shape is limited to eleven dimensions maximum,
    including the three element minor axis.
    """
    if array.ndim == 0:
        raise ValueError("array must have at least 1 dimension")
    shape = array.shape
    if shape[-1] != 3:
        raise ValueError("array must be a 3d array of 3-value color data")
    target = numpy_empty(shape[:-1], numpy.int32)
    pix_map_array(target, array, surface)
    return target
예제 #7
0
def sample_size_f(a, axis=None, masked=False):
    '''TODO

    :Parameters:

        axis: `int`, optional
            non-negative


    '''
    if masked:
        N = numpy_sum(~a.mask, axis=axis, dtype=float)
        if not numpy_ndim(N):
            N = numpy_asanyarray(N)
    else:
        if axis is None:
            N = numpy_array(a.size, dtype=float)
        else:
            shape = a.shape
            N = numpy_empty(shape[:axis] + shape[axis + 1:], dtype=float)
            N[...] = shape[axis]
    # --- End: if

    return asanyarray(N)
    def create_bounds(self,
                      bound=None,
                      cellsize=None,
                      flt=0.5,
                      max=None,
                      min=None):
        """Create cell bounds.

        :Parameters:

            bound: optional
                If set to a value larger (smaller) than the largest
                (smallest) coordinate value then bounds are created which
                include this value and for which each coordinate is in the
                centre of its bounds.

            cellsize: optional
                Define the exact size of each cell that is
                created. Created cells are allowed to overlap do not have
                to be contigious. The *cellsize* parameter may be one of:

                  * A data-like scalar (see below) that defines the cell size,
                    either in the same units as the coordinates or in the
                    units provided. Note that in this case, the position of
                    each coordinate within the its cell is controlled by the
                    *flt* parameter.

                    *Parameter example:*
                        To specify cellsizes of 10, in the same units as the
                        coordinates: ``cellsize=10``.

                    *Parameter example:*
                        To specify cellsizes of 1 day: ``cellsize=cf.Data(1,
                        'day')`` (see `cf.Data` for details).

                    *Parameter example:*
                        For coordinates ``1, 2, 10``, setting ``cellsize=1``
                        will result in bounds of ``(0.5, 1.5), (1.5, 2.5),
                        (9.5, 10.5)``.

                    *Parameter example:*
                        For coordinates ``1, 2, 10`` kilometres, setting
                        ``cellsize=cf.Data(5000, 'm')`` will result in bounds
                        of ``(-1.5, 3.5), (-0.5, 4.5), (7.5, 12.5)`` (see
                        `cf.Data` for details).

                    *Parameter example:*
                        For decreasing coordinates ``2, 0, -12`` setting,
                        ``cellsize=2`` will result in bounds of ``(3, 1),
                        (1, -1), (-11, -13)``.

                  * A `cf.TimeDuration` defining the cell size. Only
                    applicable to reference time coordinates. It is possible
                    to "anchor" the cell bounds via the `cf.TimeDuration`
                    parameters. For example, to specify cell size of one
                    calendar month, starting and ending on the 15th day:
                    ``cellsize=cf.M(day=15)`` (see `cf.M` for details). Note
                    that the *flt* parameter is ignored in this case.

                    *Parameter example:*
                        For coordinates ``1984-12-01 12:00, 1984-12-02
                        12:00, 2000-04-15 12:00`` setting,
                        ``cellsize=cf.D()`` will result in bounds of
                        ``(1984-12-01, 1984-12-02), (1984-12-02,
                        1984-12-03), (2000-05-15, 2000-04-16)`` (see `cf.D`
                        for details).

                    *Parameter example:*
                        For coordinates ``1984-12-01, 1984-12-02,
                        2000-04-15`` setting, ``cellsize=cf.D()`` will
                        result in bounds of ``(1984-12-01, 1984-12-02),
                        (1984-12-02, 1984-12-03), (2000-05-15, 2000-04-16)``
                        (see `cf.D` for details).

                    *Parameter example:*
                        For coordinates ``1984-12-01, 1984-12-02,
                        2000-04-15`` setting, ``cellsize=cf.D(hour=12)``
                        will result in bounds of ``(1984-11:30 12:00,
                        1984-12-01 12:00), (1984-12-01 12:00, 1984-12-02
                        12:00), (2000-05-14 12:00, 2000-04-15 12:00)`` (see
                        `cf.D` for details).

                    *Parameter example:*
                        For coordinates ``1984-12-16 12:00, 1985-01-16
                        12:00`` setting, ``cellsize=cf.M()`` will result in
                        bounds of ``(1984-12-01, 1985-01-01), (1985-01-01,
                        1985-02-01)`` (see `cf.M` for details).

                    *Parameter example:*
                        For coordinates ``1984-12-01 12:00, 1985-01-01
                        12:00`` setting, ``cellsize=cf.M()`` will result in
                        bounds of ``(1984-12-01, 1985-01-01), (1985-01-01,
                        1985-02-01)`` (see `cf.M` for details).

                    *Parameter example:*
                        For coordinates ``1984-12-01 12:00, 1985-01-01
                        12:00`` setting, ``cellsize=cf.M(day=20)`` will
                        result in bounds of ``(1984-11-20, 1984-12-20),
                        (1984-12-20, 1985-01-20)`` (see `cf.M` for details).

                    *Parameter example:*
                        For coordinates ``1984-03-01, 1984-06-01`` setting,
                        ``cellsize=cf.Y()`` will result in bounds of
                        ``(1984-01-01, 1985-01-01), (1984-01-01,
                        1985-01-01)`` (see `cf.Y` for details). Note that in
                        this case each cell has the same bounds. This
                        because ``cf.Y()`` is equivalent to ``cf.Y(month=1,
                        day=1)`` and the closest 1st January to both
                        coordinates is 1st January 1984.

                {+data-like-scalar} TODO

            flt: `float`, optional
                When creating cells with sizes specified by the *cellsize*
                parameter, define the fraction of the each cell which is
                less its coordinate value. By default *flt* is 0.5, so that
                each cell has its coordinate at it's centre. Ignored if
                *cellsize* is not set.

                *Parameter example:*
                    For coordinates ``1, 2, 10``, setting ``cellsize=1,
                    flt=0.5`` will result in bounds of ``(0.5, 1.5), (1.5,
                    2.5), (9.5, 10.5)``.

                *Parameter example:*
                    For coordinates ``1, 2, 10``, setting ``cellsize=1,
                    flt=0.25`` will result in bounds of ``(0.75, 1.75),
                    (1.75, 2.75), (9.75, 10.75)``.

                *Parameter example:*
                    For decreasing coordinates ``2, 0, -12``, setting
                    ``cellsize=6, flt=0.9`` will result in bounds of
                    ``(2.6, -3.4), (0.6, -5.4), (-11.4, -17.4)``.

            max: optional
                Limit the created bounds to be no more than this number.

                *Parameter example:*
                    To ensure that all latitude bounds are at most 90:
                    ``max=90``.

            min: optional
                Limit the created bounds to be no less than this number.

                *Parameter example:*
                    To ensure that all latitude bounds are at least -90:
                    ``min=-90``.

        :Returns:

            `Bounds`
                The newly-created coordinate cell bounds object.

        **Examples:**

        >>> c.create_bounds()
        >>> c.create_bounds(bound=-9000.0)

        """
        array = self.array
        size = array.size

        if cellsize is not None:
            if bound:
                raise ValueError(
                    "bound parameter can't be True when setting the "
                    "cellsize parameter")

            if not isinstance(cellsize, TimeDuration):
                # ----------------------------------------------------
                # Create bounds based on cell sizes defined by a
                # data-like object
                #
                # E.g. cellsize=10
                #      cellsize=cf.Data(1, 'day')
                # ----------------------------------------------------
                cellsize = Data.asdata(abs(cellsize))
                if cellsize.Units:
                    if self.Units.isreftime:
                        if not cellsize.Units.istime:
                            raise ValueError("q123423423jhgsjhbd jh ")
                        cellsize.Units = Units(self.Units._utime.units)
                    else:
                        if not cellsize.Units.equivalent(self.Units):
                            raise ValueError("jhgsjhbd jh ")
                        cellsize.Units = self.Units
                cellsize = cellsize.datum()

                cellsize0 = cellsize * flt
                cellsize1 = cellsize * (1 - flt)
                if not self.direction():
                    cellsize0, cellsize1 = -cellsize1, -cellsize0

                bounds = numpy_empty((size, 2), dtype=array.dtype)
                bounds[:, 0] = array - cellsize0
                bounds[:, 1] = array + cellsize1
            else:
                # ----------------------------------------------------
                # Create bounds based on cell sizes defined by a
                # TimeDuration object
                #
                # E.g. cellsize=cf.s()
                #      cellsize=cf.m()
                #      cellsize=cf.h()
                #      cellsize=cf.D()
                #      cellsize=cf.M()
                #      cellsize=cf.Y()
                #      cellsize=cf.D(hour=12)
                #      cellsize=cf.M(day=16)
                #      cellsize=cf.M(2)
                #      cellsize=cf.M(2, day=15, hour=12)
                # ----------------------------------------------------
                if not self.Units.isreftime:
                    raise ValueError(
                        "Can't create reference time bounds for "
                        "non-reference time coordinates: {0!r}".format(
                            self.Units))

                bounds = numpy_empty((size, 2), dtype=object)

                cellsize_bounds = cellsize.bounds
                direction = bool(self.direction())

                for c, b in zip(self.datetime_array, bounds):
                    b[...] = cellsize_bounds(c, direction=direction)
        else:
            if bound is None:
                # ----------------------------------------------------
                # Creat Voronoi bounds
                # ----------------------------------------------------
                if size < 2:
                    raise ValueError(
                        "Can't create bounds for Voronoi cells from one value")

                bounds_1d = [array.item(0, ) * 1.5 - array.item(1, ) * 0.5]
                bounds_1d.extend((array[0:-1] + array[1:]) * 0.5)
                bounds_1d.append(
                    array.item(-1, ) * 1.5 - array.item(-2, ) * 0.5)

                dtype = type(bounds_1d[0])

                if max is not None:
                    if self.direction():
                        bounds_1d[-1] = max
                    else:
                        bounds_1d[0] = max
                if min is not None:
                    if self.direction():
                        bounds_1d[0] = min
                    else:
                        bounds_1d[-1] = min

            else:
                # ----------------------------------------------------
                # Create
                # ----------------------------------------------------
                direction = self.direction()
                if not direction and size > 1:
                    array = array[::-1]

                bounds_1d = [bound]
                if bound <= array.item(0, ):
                    for i in range(size):
                        bound = (2.0 * array.item(i, ) - bound)
                        bounds_1d.append(bound)
                elif bound >= array.item(-1, ):
                    for i in range(size - 1, -1, -1):
                        bound = (2.0 * array.item(i, ) - bound)
                        bounds_1d.append(bound)

                    bounds_1d = bounds_1d[::-1]
                else:
                    raise ValueError("bad bound value")

                dtype = type(bounds_1d[-1])

                if not direction:
                    bounds_1d = bounds_1d[::-1]

            bounds = numpy_empty((size, 2), dtype=dtype)
            bounds[:, 0] = bounds_1d[:-1]
            bounds[:, 1] = bounds_1d[1:]

        # Create coordinate bounds object
        bounds = Bounds(data=Data(bounds, units=self.Units), copy=False)

        return bounds
예제 #9
0
    def add_partitions(self, adimensions, master_flip, extra_boundaries, axis):
        '''Add partition boundaries.

    :Parameters:

        adimensions: `list`
            The ordered axis names of the master array.

        master_flip: `list`

        extra_boundaries: `list` of `int`
            The boundaries of the new partitions.

        axis: `str`
            The name of the axis to have the new partitions.

        '''
        def _update_p(matrix, location, master_index, part,
                      master_axis_to_position, master_flip):
            '''TODO

        :Parameters:

            matrix: numpy array of `Partition` objects

            location: `list`

            master_index: `int`

            part: `list`

            master_axis_to_position: `dict`

            master_flip: `list`

        :Returns:

            numpy array of `Partition` objects

            '''
            for partition in matrix.flat:
                partition.location = partition.location[:]
                partition.shape = partition.shape[:]

                partition.location[master_index] = location
                partition.shape[master_index] = shape

                partition.new_part(part, master_axis_to_position, master_flip)
            # --- End: for

            return matrix

        # If no extra boundaries have been provided, just return
        # without doing anything
        if not extra_boundaries:
            return

        master_index = adimensions.index(axis)
        index = self.axes.index(axis)

        # Find the position of the extra-boundaries dimension in the
        # list of master array dimensions
        extra_boundaries = extra_boundaries[:]

        # Create the master_axis_to_position dictionary required by
        # Partition.new_part
        master_axis_to_position = {}
        for i, data_axis in enumerate(adimensions):
            master_axis_to_position[data_axis] = i

        matrix = self.matrix
        shape = matrix.shape

        # Initialize the new partition matrix
        new_shape = list(shape)
        new_shape[index] += len(extra_boundaries)
        new_matrix = numpy_empty(new_shape, dtype=object)

        part = [slice(None)] * len(adimensions)
        indices = [slice(None)] * matrix.ndim
        new_indices = indices[:]
        new_indices[index] = slice(0, 1)  # 0

        # Find the first extra boundary
        x = extra_boundaries.pop(0)

        for i in range(shape[index]):
            indices[index] = slice(i, i + 1)
            sub_matrix = matrix[tuple(indices)]
            #            (r0, r1) = next(sub_matrix.flat).location[master_index]
            (r0, r1) = sub_matrix.item(0, ).location[master_index]

            # Could do better, perhaps, by assigning in blocks
            if not r0 < x < r1:
                # This new boundary is *not* within the span of this
                # sub-matrix. Therefore, just copy the sub-matrix
                # straight into the new matrix
                new_matrix[tuple(new_indices)] = sub_matrix
                #                new_indices[index] += 1
                new_indices[index] = slice(new_indices[index].start + 1,
                                           new_indices[index].stop + 1)
                continue

            # --------------------------------------------------------
            # Still here? Then this new boundary *is* within the span
            # of this sub-matrix.
            # --------------------------------------------------------

            # Find the new extent of the original partition(s)
            location = (r0, x)
            shape = x - r0
            part[master_index] = slice(0, shape)

            # Create new partition(s) in place of the original ones(s)
            # and set the location, shape and part attributes
            new_matrix[tuple(new_indices)] = _update_p(
                deepcopy(sub_matrix), location, master_index, part,
                master_axis_to_position, master_flip)
            #            new_indices[index] += 1
            new_indices[index] = slice(new_indices[index].start + 1,
                                       new_indices[index].stop + 1)

            while x < r1:
                # Find the extent of the new partition(s)
                if not extra_boundaries:
                    # There are no more new boundaries, so the new
                    # partition(s) run to the end of the original
                    # partition(s) in which they lie.
                    location1 = r1
                else:
                    # There are more new boundaries, so this
                    # new partition runs either to the next
                    # new boundary or to the end of the
                    # original partition, which comes first.
                    location1 = min(extra_boundaries[0], r1)

                location = (x, location1)
                shape = location1 - x
                offset = x - r0
                part[master_index] = slice(offset, offset + shape)

                # Create the new partition(s) and set the
                # location, shape and part attributes
                new_matrix[tuple(new_indices)] = _update_p(
                    deepcopy(sub_matrix), location, master_index, part,
                    master_axis_to_position, master_flip)

                new_indices[index] = slice(new_indices[index].start + 1,
                                           new_indices[index].stop + 1)
                #                new_indices[index] += 1

                if not extra_boundaries:
                    # There are no more extra boundaries, so we can
                    # return now.
                    #  new_indices[index] = slice(new_indices[index],
                    #  None)
                    new_indices[index] = slice(new_indices[index].start, None)
                    indices[index] = slice(i + 1, None)

                    new_matrix[tuple(new_indices)] = matrix[tuple(indices)]
                    self.matrix = new_matrix

                    return

                # Still here? Then move on to the next new boundary
                x = extra_boundaries.pop(0)
            # --- End: while
        # --- End: for

        self.matrix = new_matrix
예제 #10
0
from numpy import ndenumerate as numpy_ndenumerate
from numpy import empty as numpy_empty
from numpy import expand_dims as numpy_expand_dims
from numpy import squeeze as numpy_squeeze

from copy import deepcopy

from .partition import Partition

from ..functions import (_DEPRECATION_WARNING_METHOD,
                         _DEPRECATION_ERROR_METHOD)

from ..decorators import (_inplace_enabled,
                          _inplace_enabled_define_and_cleanup)

_empty_matrix = numpy_empty((), dtype=object)


class PartitionMatrix:
    '''

A hyperrectangular partition matrix of a master data array.

Each of elements (called partitions) span all or part of exactly one
sub-array of the master data array.

Normal numpy basic and advanced indexing is supported, but size 1
dimensions are always removed from the output array, i.e. a partition
rather than a partition matrix is returned if the output array has
size 1.
예제 #11
0
    def create_grid(
        coords,
        use_bounds,
        mask=None,
        cartesian=False,
        cyclic=False,
        coords_2D=False,
        coord_order=None,
    ):
        """Create an ESMPy grid given a sequence of coordinates for use
        as a source or destination grid in regridding. Optionally the
        grid may have an associated mask.

        :Parameters:

            coords: sequence
                The coordinates if not Cartesian it is assume that the
                first is longitude and the second is latitude.

            use_bounds: `bool`
                Whether to populate the grid corners with information from
                the bounds or not.

            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.

            cartesian: `bool`, optional
                Whether to create a Cartesian grid or a spherical one,
                False by default.

            cyclic: `bool`, optional
                Whether or not the longitude (if present) is cyclic. If
                None the a check for cyclicity is made from the
                bounds. None by default.

            coords_2D: `bool`, optional
                Whether the coordinates are 2D or not. Presently only
                works for spherical coordinates. False by default.

            coord_order: sequence, optional
                Two tuples one indicating the order of the x and y axes
                for 2D longitude, one for 2D latitude.

        :Returns:

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

        """

        if not cartesian:
            lon = coords[0]
            lat = coords[1]
            if not coords_2D:
                # Get the shape of the grid
                shape = [lon.size, lat.size]
            else:
                x_order = coord_order[0]
                y_order = coord_order[1]
                # Get the shape of the grid
                shape = lon.transpose(x_order).shape
                if lat.transpose(y_order).shape != shape:
                    raise ValueError(
                        "The longitude and latitude coordinates"
                        " must have the same shape."
                    )
            # --- End: if

            if use_bounds:
                if not coords_2D:
                    # Get the bounds
                    x_bounds = lon.get_bounds()
                    y_bounds = lat.get_bounds().clip(-90, 90, "degrees").array

                    # If cyclic not set already, check for cyclicity
                    if cyclic is None:
                        cyclic = abs(
                            x_bounds.datum(-1) - x_bounds.datum(0)
                        ) == Data(360, "degrees")

                    x_bounds = x_bounds.array
                else:
                    # Get the bounds
                    x_bounds = lon.get_bounds()
                    y_bounds = lat.get_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

            else:
                if not coords_2D:
                    # If cyclicity not set already, check for cyclicity
                    if cyclic is None:
                        try:
                            x_bounds = lon.get_bounds()
                            cyclic = abs(
                                x_bounds.datum(-1) - x_bounds.datum(0)
                            ) == Data(360, "degrees")
                        except ValueError:
                            pass
            # --- 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]

            if cyclic:
                grid = ESMF.Grid(
                    max_index, num_peri_dims=1, staggerloc=staggerLocs
                )
            else:
                grid = ESMF.Grid(max_index, staggerloc=staggerLocs)

            # Populate grid centres
            x, y = 0, 1
            gridXCentre = grid.get_coords(x, staggerloc=ESMF.StaggerLoc.CENTER)
            gridYCentre = grid.get_coords(y, staggerloc=ESMF.StaggerLoc.CENTER)
            if not coords_2D:
                gridXCentre[...] = lon.array.reshape((lon.size, 1))
                gridYCentre[...] = lat.array.reshape((1, lat.size))
            else:
                gridXCentre[...] = lon.transpose(x_order).array
                gridYCentre[...] = lat.transpose(y_order).array

            # Populate grid corners if there are bounds
            if use_bounds:
                gridCorner = grid.coords[ESMF.StaggerLoc.CORNER]
                if not coords_2D:
                    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)

                    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)
                else:
                    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
        else:
            # 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."
                )

            # For 1D conservative regridding add an extra dimension of size 1
            if ndim == 1:
                if not use_bounds:
                    # For 1D nonconservative regridding the extra dimension
                    # should already have been added in cf.Field.regridc.
                    raise ValueError(
                        "Cannot create a Cartesian grid from "
                        "one dimension coordinate with no bounds."
                    )
                coords = [
                    DimensionCoordinate(
                        data=Data(0),
                        bounds=Data(
                            [
                                numpy_finfo("float32").epsneg,
                                numpy_finfo("float32").eps,
                            ]
                        ),
                    )
                ] + coords
                if mask is not None:
                    mask = mask[None, :]
                ndim = 2

            shape = list()
            for coord in coords:
                shape.append(coord.size)

            # Initialise 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,
                    ]
            else:
                if ndim < 3:
                    staggerLocs = [ESMF.StaggerLoc.CENTER]
                else:
                    staggerLocs = [ESMF.StaggerLoc.CENTER_VCENTER]
            # --- End: if
            grid = ESMF.Grid(
                max_index, coord_sys=ESMF.CoordSys.CART, staggerloc=staggerLocs
            )

            # Populate the grid centres
            for d in range(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 range(0, ndim)]
                )
            # --- 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]

                for d in range(0, ndim):
                    # boundsD = coords[d].get_bounds(create=True).array
                    boundsD = coords[d].get_bounds(None)
                    if boundsD is None:
                        boundsD = coords[d].create_bounds()

                    boundsD = boundsD.array

                    if shape[d] > 1:
                        tmp = numpy_empty(shape[d] + 1)
                        tmp[0:-1] = boundsD[:, 0]
                        tmp[-1] = boundsD[-1, 1]
                        boundsD = tmp

                    gridCorner[d][...] = boundsD.reshape(
                        [shape[d] + 1 if x == d else 1 for x in range(0, ndim)]
                    )
            # --- End: if
        # --- End: if

        # Add the mask if appropriate
        if mask is not None:
            gmask = grid.add_item(ESMF.GridItem.MASK)
            gmask[...] = 1
            gmask[mask] = 0

        return grid