Пример #1
0
 def nfunc(*args, **kwargs):
     if builtin.any(hasattr(iarg, 'chunks') for iarg in args):
         if dtypes is not None:
             kwargs['dtype'] = np.dtype(dtypes[str(args[0].dtype)])
         else:
             kwargs['dtype'] = args[0].dtype
         return map_blocks(func, *args, **kwargs)
     else:
         return func(*args, **kwargs)
Пример #2
0
Файл: ma.py Проект: m-rossi/dask
def masked_values(x, value, rtol=1e-05, atol=1e-08, shrink=True):
    x = asanyarray(x)
    if getattr(value, "shape", ()):
        raise ValueError("da.ma.masked_values doesn't support array `value`s")
    return map_blocks(np.ma.masked_values,
                      x,
                      value,
                      rtol=rtol,
                      atol=atol,
                      shrink=shrink)
Пример #3
0
    def test_dask_unchunked_input(self):
        p = da.from_array(self.p_def)
        t = da.from_array(self.t_def)
        q = da.from_array(self.q_def)

        # Start dask cluster
        cluster = dd.LocalCluster(n_workers=3, threads_per_worker=2)
        print(cluster.dashboard_link)
        client = dd.Client(cluster)

        out = map_blocks(relhum, t, q, p).compute()

        assert np.allclose(out, self.rh_gt_2, atol=0.1)

        client.shutdown()
Пример #4
0
    def test_dask_chunked_input(self):
        tk = da.from_array(np.asarray(t_def) + 273.15, chunks="auto")
        rh = da.from_array(rh_def, chunks="auto")

        # Start dask cluster
        cluster = dd.LocalCluster(n_workers=3, threads_per_worker=2)
        print(cluster.dashboard_link)
        client = dd.Client(cluster)

        out = map_blocks(dewtemp, tk, rh).compute()

        assert np.allclose(out - 273.15, dt_2, atol=0.1)

        cluster.close()
        client.close()
Пример #5
0
def linint2pts(fi, xo, yo, icycx=0, msg_py=None):
    # ''' signature : fo = dlinint2pts(xi,yi,fi,xo,yo,[icycx,xmsg,iopt])

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):
        raise Exception("fi is required to be an xarray.DataArray")

    xi = fi.coords[fi.dims[-1]]
    yi = fi.coords[fi.dims[-2]]

    if xo.shape != yo.shape:
        raise Exception("xo and yo, be be of equal length")

    # ensure rightmost dimensions of input are not chunked
    if list(fi.chunks)[-2:] != [yi.shape, xi.shape]:
        raise Exception("fi must be unchunked along the last two dimensions")

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = (xo.shape, )
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {k: v for (k, v) in fi.coords.items()}
    # fo_coords.remove(fi.dims[-1]) # this dimension dissapears
    fo_coords[fi.dims[-1]] = xo  # remove this line omce dims are figured out
    fo_coords[fi.dims[-2]] = yo  # maybe replace with 'pts'
    # ''' end of boilerplate

    fo = map_blocks(
        _linint2pts,
        yi,
        xi,
        fi.data,
        yo,
        xo,
        icycx,
        msg_py,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2],
    )
    fo = xr.DataArray(fo.compute(), attrs=fi.attrs)
    return fo
Пример #6
0
def trim_internal(x, axes, boundary=None):
    """Trim sides from each block

    This couples well with the overlap operation, which may leave excess data on
    each block

    See also
    --------
    dask.array.chunk.trim
    dask.array.map_blocks
    """
    boundary = coerce_boundary(x.ndim, boundary)

    olist = []
    for i, bd in enumerate(x.chunks):
        bdy = boundary.get(i, "none")
        overlap = axes.get(i, 0)
        ilist = []
        for j, d in enumerate(bd):
            if bdy != "none":
                if isinstance(overlap, tuple):
                    d = d - sum(overlap)
                else:
                    d = d - overlap * 2

            else:
                if isinstance(overlap, tuple):
                    d = d - overlap[0] if j != 0 else d
                    d = d - overlap[1] if j != len(bd) - 1 else d
                else:
                    d = d - overlap if j != 0 else d
                    d = d - overlap if j != len(bd) - 1 else d

            ilist.append(d)
        olist.append(tuple(ilist))
    chunks = tuple(olist)

    return map_blocks(
        partial(_trim, axes=axes, boundary=boundary),
        x,
        chunks=chunks,
        dtype=x.dtype,
        meta=x._meta,
    )
def triple_to_grid(data,
                   x_in,
                   y_in,
                   x_out,
                   y_out,
                   method=1,
                   domain=float(1.0),
                   distmx=None,
                   missing_value=np.nan,
                   meta=False):
    """
    Places unstructured (randomly-spaced) data onto the nearest locations of a rectilinear grid.

    Parameters
    ----------

    data : :class:`xarray.DataArray`: or :class:`numpy.ndarray`:
        A multi-dimensional array, whose rightmost dimension is the same
        length as `x_in` and `y_in`, containing the values associated with
        the "x" and "y" coordinates. Missing values may be present but
        will be ignored.

    x_in : :class:`xarray.DataArray`: or :class:`numpy.ndarray`:
        One-dimensional arrays of the same length containing the coordinates
        associated with the data values. For geophysical variables, "x"
        correspond to longitude.

    y_in : :class:`xarray.DataArray`: or :class:`numpy.ndarray`:
        One-dimensional arrays of the same length containing the coordinates
        associated with the data values. For geophysical variables, "y"
        correspond to latitude.

    x_out : :class:`xarray.DataArray`: or :class:`numpy.ndarray`:
        A one-dimensional array of length M containing the "x" coordinates
        associated with the returned two-grid. For geophysical variables,
        these are longitudes. The coordinates' values must be
        monotonically increasing.

    y_out : :class:`xarray.DataArray`: or :class:`numpy.ndarray`:
        A one-dimensional array of length N containing the "y" coordinates
        associated with the returned grid. For geophysical ~variables,
        these are latitudes. The coordinates' values must be
        monotonically increasing.

    Optional Parameters
    -------------------

    method :
        An integer value that can be 0 or 1. The default value is 1.
        A value of 1 means to use the great circle distance formula
        for distance calculations.
        Warning: `method` = 0, together with `domain` = 1.0, could
        result in many of the target grid points to be set to the
        missing value if the number of grid points is large (ie: a
        high resolution grid) and the number of observations
        relatively small.

    domain :
        A float value that should be set to a value >= 0. The
        default value is 1.0. If present, the larger this factor,
        the wider the spatial domain allowed to influence grid boundary
        points. Typically, `domain` is 1.0 or 2.0. If `domain` <= 0.0,
        then values located outside the grid domain specified by
        `x_out` and `y_out` arguments will not be used.

    distmx :
        Setting `distmx` allows the user to specify a search
        radius (km) beyond which observations are not considered
        for nearest neighbor. Only applicable when `method` = 1.
        The default `distmx`=1e20 (km) means that every grid point
        will have a nearest neighbor. It is suggested that users
        specify a reasonable value for `distmx`.

    missing_value : :obj:`numpy.number`:
        A numpy scalar value that represent
        a missing value in `data`. The default value is `np.nan`.
        If specified explicitly, this argument allows the user to
        use a missing value scheme other than NaN or masked arrays.

    meta : :obj:`bool`:
        If set to True and the input array is an Xarray,
        the metadata from the input array will be copied to the
        output array; default is False.
        Warning: This option is not yet supported for this function.

    Returns
    -------

    grid : :class:`xarray.DataArray`:
        The returned array will be K x N x M, where K
        represents the leftmost dimensions of `data`, N represent the size of `y_out`,
        and M represent the size of `x_out` coordinate vectors. It will be of type
        double if any of the input is double, and float otherwise.

    Description
    -----------

        This function puts unstructured data (randomly-spaced) onto the nearest
        locations of a rectilinear grid. A default value of `domain` option is
        now set to 1.0 instead of 0.0.

        This function does not perform interpolation; rather, each individual
        data point is assigned to the nearest grid point. It is possible that
        upon return, grid will contain grid points set to missing value if
        no `x_in(n)`, `y_in(n)` are nearby.

    Examples
    --------

    Example 1: Using triple_to_grid with :class:`xarray.DataArray` input

    .. code-block:: python

        import numpy as np
        import xarray as xr
        import geocat.comp

        # Open a netCDF data file using xarray default engine and load the data stream
        ds = xr.open_dataset("./ruc.nc")

        # [INPUT] Grid & data info on the source curvilinear
        data = ds.DIST_236_CBL[:]
        x_in = ds.gridlat_236[:]
        y_in = ds.gridlon_236[:]
        x_out = ds.gridlat_236[:]
        y_out = ds.gridlon_236[:]


        # [OUTPUT] Grid on destination points grid (or read the 1D lat and lon from
        #	       an other .nc file.
        newlat1D_points=np.linspace(lat2D_curv.min(), lat2D_curv.max(), 100)
        newlon1D_points=np.linspace(lon2D_curv.min(), lon2D_curv.max(), 100)

        output = geocat.comp.triple_to_grid(data, x_out, y_out, x_in, y_in)
        """

    # TODO: May need to be revisited after sanity_check work is finished

    if (x_in is None) | (y_in is None):
        raise CoordinateError(
            "ERROR triple_to_grid: Arguments x_in and y_in must always be explicitly provided"
        )

    # ''' Start of boilerplate
    # If a Numpy input is given, convert it to Xarray and chunk it just with its dims
    if not isinstance(data, xr.DataArray):
        data = xr.DataArray(data)
        data_chunk = dict([
            (k, v) for (k, v) in zip(list(data.dims), list(data.shape))
        ])

        data = xr.DataArray(
            data.data,
            # coords={
            #     data.dims[-1]: x_in,
            #     data.dims[-2]: y_in,
            # },
            dims=data.dims,
        ).chunk(data_chunk)
    else:
        # If an unchunked Xarray input is given, chunk it just with its dims
        if (data.chunks is None):
            data_chunk = dict([
                (k, v) for (k, v) in zip(list(data.dims), list(data.shape))
            ])
            data = data.chunk(data_chunk)

        # Ensure the rightmost dimension of input is not chunked
        elif list(data.chunks)[-1:] != [x_in.shape]:
            raise ChunkError(
                "ERROR triple_to_grid: Data must be unchunked along the rightmost two dimensions"
            )

    # x_in = data.coords[data.dims[-1]]
    # y_in = data.coords[data.dims[-2]]

    # Basic sanity checks
    if x_in.shape[0] != y_in.shape[0] or x_in.shape[0] != data.shape[data.ndim
                                                                     - 1]:
        raise DimensionError(
            "ERROR triple_to_grid: The length of `x_in` and `y_in` must be the same "
            "as the rightmost dimension of `data` !")
    if x_in.ndim > 1 or y_in.ndim > 1:
        raise DimensionError(
            "ERROR triple_to_grid: `x_in` and `y_in` arguments must be one-dimensional arrays !\n"
        )
    if x_out.ndim > 1 or y_out.ndim > 1:
        raise DimensionError(
            "ERROR triple_to_grid: `x_out` and `y_out` arguments must be one-dimensional array !\n"
        )

    if not isinstance(method, int):
        raise TypeError(
            'ERROR triple_to_grid: `method` arg must be an integer. Set it to either 1 or 0.'
        )

    if (method != 0) and (method != 1):
        raise TypeError(
            'ERROR triple_to_grid: `method` arg accepts either 0 or 1.')

    # `distmx` is only applicable when `method`==1
    if method:
        if np.asarray(distmx).size != 1:
            raise ValueError(
                "ERROR triple_to_grid: Provide a scalar value for `distmx` !")
    else:
        if distmx is not None:
            raise ValueError(
                "ERROR triple_to_grid: `distmx` is only applicable when `method`==1 !"
            )

    if np.asarray(domain).size != 1:
        raise ValueError(
            "ERROR triple_to_grid: Provide a scalar value for `domain` !")

    # `data` data structure elements and autochunking
    data_chunks = list(data.dims)
    data_chunks[:-1] = [
        (k, 1) for (k, v) in zip(list(data.dims)[:-1],
                                 list(data.chunks)[:-1])
    ]
    data_chunks[-1:] = [
        (k, v[0])
        for (k, v) in zip(list(data.dims)[-1:],
                          list(data.chunks)[-1:])
    ]
    data_chunks = dict(data_chunks)
    data = data.chunk(data_chunks)

    # grid datastructure elements
    grid_chunks = list(data.chunks)
    grid_chunks[-1] = (y_out.shape[0] * x_out.shape[0], )
    grid_chunks = tuple(grid_chunks)
    dask_grid_shape = tuple(a[0] for a in list(grid_chunks))
    grid_coords = {k: v for (k, v) in data.coords.items()}
    grid_coords[data.dims[-1]] = x_out
    grid_coords[data.dims[-2]] = y_out
    # ''' end of boilerplate

    grid = map_blocks(
        _triple_to_grid,
        data.data,
        x_in,
        y_in,
        x_out,
        y_out,
        dask_grid_shape,
        method=method,
        distmx=distmx,
        domain=domain,
        msg_py=missing_value,
        chunks=grid_chunks,
        dtype=data.dtype,
        drop_axis=[data.ndim - 1],
        new_axis=[data.ndim - 1],
    )

    # Reshape grid to its final shape
    grid_shape = (data.shape[:-1] + (y_out.shape[0], ) + (x_out.shape[0], ))
    grid = grid.reshape(grid_shape)

    if meta:
        # grid = xr.DataArray(grid.compute(), attrs=data.attrs, dims=data.dims, coords=grid_coords)
        import warnings
        warnings.warn(
            "WARNING triple_to_grid: Retention of metadata is not yet supported; "
            "it will thus be ignored in the output!")
    # else:
    #     grid = xr.DataArray(grid.compute(), coords=grid_coords)

    grid = xr.DataArray(grid.compute())

    return grid
Пример #8
0
def map_overlap(func,
                *args,
                depth=None,
                boundary=None,
                trim=True,
                align_arrays=True,
                **kwargs):
    """Map a function over blocks of arrays with some overlap

    We share neighboring zones between blocks of the array, map a
    function, and then trim away the neighboring strips. If depth is
    larger than any chunk along a particular axis, then the array is
    rechunked.

    Note that this function will attempt to automatically determine the output
    array type before computing it, please refer to the ``meta`` keyword argument
    in ``map_blocks`` if you expect that the function will not succeed when
    operating on 0-d arrays.

    Parameters
    ----------
    func: function
        The function to apply to each extended block.
        If multiple arrays are provided, then the function should expect to
        receive chunks of each array in the same order.
    args : dask arrays
    depth: int, tuple, dict or list
        The number of elements that each block should share with its neighbors
        If a tuple or dict then this can be different per axis.
        If a list then each element of that list must be an int, tuple or dict
        defining depth for the corresponding array in `args`.
        Asymmetric depths may be specified using a dict value of (-/+) tuples.
        Note that asymmetric depths are currently only supported when
        ``boundary`` is 'none'.
        The default value is 0.
    boundary: str, tuple, dict or list
        How to handle the boundaries.
        Values include 'reflect', 'periodic', 'nearest', 'none',
        or any constant value like 0 or np.nan.
        If a list then each element must be a str, tuple or dict defining the
        boundary for the corresponding array in `args`.
        The default value is 'reflect'.
    trim: bool
        Whether or not to trim ``depth`` elements from each block after
        calling the map function.
        Set this to False if your mapping function already does this for you
    align_arrays: bool
        Whether or not to align chunks along equally sized dimensions when
        multiple arrays are provided.  This allows for larger chunks in some
        arrays to be broken into smaller ones that match chunk sizes in other
        arrays such that they are compatible for block function mapping. If
        this is false, then an error will be thrown if arrays do not already
        have the same number of blocks in each dimension.
    **kwargs:
        Other keyword arguments valid in ``map_blocks``

    Examples
    --------
    >>> import numpy as np
    >>> import dask.array as da

    >>> x = np.array([1, 1, 2, 3, 3, 3, 2, 1, 1])
    >>> x = da.from_array(x, chunks=5)
    >>> def derivative(x):
    ...     return x - np.roll(x, 1)

    >>> y = x.map_overlap(derivative, depth=1, boundary=0)
    >>> y.compute()
    array([ 1,  0,  1,  1,  0,  0, -1, -1,  0])

    >>> x = np.arange(16).reshape((4, 4))
    >>> d = da.from_array(x, chunks=(2, 2))
    >>> d.map_overlap(lambda x: x + x.size, depth=1, boundary='reflect').compute()
    array([[16, 17, 18, 19],
           [20, 21, 22, 23],
           [24, 25, 26, 27],
           [28, 29, 30, 31]])

    >>> func = lambda x: x + x.size
    >>> depth = {0: 1, 1: 1}
    >>> boundary = {0: 'reflect', 1: 'none'}
    >>> d.map_overlap(func, depth, boundary).compute()  # doctest: +NORMALIZE_WHITESPACE
    array([[12,  13,  14,  15],
           [16,  17,  18,  19],
           [20,  21,  22,  23],
           [24,  25,  26,  27]])

    The ``da.map_overlap`` function can also accept multiple arrays.

    >>> func = lambda x, y: x + y
    >>> x = da.arange(8).reshape(2, 4).rechunk((1, 2))
    >>> y = da.arange(4).rechunk(2)
    >>> da.map_overlap(func, x, y, depth=1, boundary='reflect').compute() # doctest: +NORMALIZE_WHITESPACE
    array([[ 0,  2,  4,  6],
           [ 4,  6,  8,  10]])

    When multiple arrays are given, they do not need to have the
    same number of dimensions but they must broadcast together.
    Arrays are aligned block by block (just as in ``da.map_blocks``)
    so the blocks must have a common chunk size.  This common chunking
    is determined automatically as long as ``align_arrays`` is True.

    >>> x = da.arange(8, chunks=4)
    >>> y = da.arange(8, chunks=2)
    >>> r = da.map_overlap(func, x, y, depth=1, boundary='reflect', align_arrays=True)
    >>> len(r.to_delayed())
    4

    >>> da.map_overlap(func, x, y, depth=1, boundary='reflect', align_arrays=False).compute()
    Traceback (most recent call last):
        ...
    ValueError: Shapes do not align {'.0': {2, 4}}

    Note also that this function is equivalent to ``map_blocks``
    by default.  A non-zero ``depth`` must be defined for any
    overlap to appear in the arrays provided to ``func``.

    >>> func = lambda x: x.sum()
    >>> x = da.ones(10, dtype='int')
    >>> block_args = dict(chunks=(), drop_axis=0)
    >>> da.map_blocks(func, x, **block_args).compute()
    10
    >>> da.map_overlap(func, x, **block_args, boundary='reflect').compute()
    10
    >>> da.map_overlap(func, x, **block_args, depth=1, boundary='reflect').compute()
    12

    For functions that may not handle 0-d arrays, it's also possible to specify
    ``meta`` with an empty array matching the type of the expected result. In
    the example below, ``func`` will result in an ``IndexError`` when computing
    ``meta``:

    >>> x = np.arange(16).reshape((4, 4))
    >>> d = da.from_array(x, chunks=(2, 2))
    >>> y = d.map_overlap(lambda x: x + x[2], depth=1, boundary='reflect', meta=np.array(()))
    >>> y
    dask.array<_trim, shape=(4, 4), dtype=float64, chunksize=(2, 2), chunktype=numpy.ndarray>
    >>> y.compute()
    array([[ 4,  6,  8, 10],
           [ 8, 10, 12, 14],
           [20, 22, 24, 26],
           [24, 26, 28, 30]])

    Similarly, it's possible to specify a non-NumPy array to ``meta``:

    >>> import cupy  # doctest: +SKIP
    >>> x = cupy.arange(16).reshape((4, 4))  # doctest: +SKIP
    >>> d = da.from_array(x, chunks=(2, 2))  # doctest: +SKIP
    >>> y = d.map_overlap(lambda x: x + x[2], depth=1, boundary='reflect', meta=cupy.array(()))  # doctest: +SKIP
    >>> y  # doctest: +SKIP
    dask.array<_trim, shape=(4, 4), dtype=float64, chunksize=(2, 2), chunktype=cupy.ndarray>
    >>> y.compute()  # doctest: +SKIP
    array([[ 4,  6,  8, 10],
           [ 8, 10, 12, 14],
           [20, 22, 24, 26],
           [24, 26, 28, 30]])
    """
    # Look for invocation using deprecated single-array signature
    # map_overlap(x, func, depth, boundary=None, trim=True, **kwargs)
    if isinstance(func, Array) and callable(args[0]):
        warnings.warn(
            "The use of map_overlap(array, func, **kwargs) is deprecated since dask 2.17.0 "
            "and will be an error in a future release. To silence this warning, use the syntax "
            "map_overlap(func, array0,[ array1, ...,] **kwargs) instead.",
            FutureWarning,
        )
        sig = ["func", "depth", "boundary", "trim"]
        depth = get(sig.index("depth"), args, depth)
        boundary = get(sig.index("boundary"), args, boundary)
        trim = get(sig.index("trim"), args, trim)
        func, args = args[0], [func]

    if not callable(func):
        raise TypeError("First argument must be callable function, not {}\n"
                        "Usage:   da.map_overlap(function, x)\n"
                        "   or:   da.map_overlap(function, x, y, z)".format(
                            type(func).__name__))
    if not all(isinstance(x, Array) for x in args):
        raise TypeError("All variadic arguments must be arrays, not {}\n"
                        "Usage:   da.map_overlap(function, x)\n"
                        "   or:   da.map_overlap(function, x, y, z)".format(
                            [type(x).__name__ for x in args]))

    # Coerce depth and boundary arguments to lists of individual
    # specifications for each array argument
    def coerce(xs, arg, fn):
        if not isinstance(arg, list):
            arg = [arg] * len(xs)
        return [fn(x.ndim, a) for x, a in zip(xs, arg)]

    depth = coerce(args, depth, coerce_depth)
    boundary = coerce(args, boundary, coerce_boundary)

    # Align chunks in each array to a common size
    if align_arrays:
        # Reverse unification order to allow block broadcasting
        inds = [list(reversed(range(x.ndim))) for x in args]
        _, args = unify_chunks(*list(concat(zip(args, inds))), warn=False)

    # Escape to map_blocks if depth is zero (a more efficient computation)
    if all([all(depth_val == 0 for depth_val in d.values()) for d in depth]):
        return map_blocks(func, *args, **kwargs)

    for i, x in enumerate(args):
        for j in range(x.ndim):
            if isinstance(depth[i][j], tuple) and boundary[i][j] != "none":
                raise NotImplementedError(
                    "Asymmetric overlap is currently only implemented "
                    "for boundary='none', however boundary for dimension "
                    "{} in array argument {} is {}".format(
                        j, i, boundary[i][j]))

    def assert_int_chunksize(xs):
        assert all(type(c) is int for x in xs for cc in x.chunks for c in cc)

    assert_int_chunksize(args)
    if not trim and "chunks" not in kwargs:
        kwargs["chunks"] = args[0].chunks
    args = [
        overlap(x, depth=d, boundary=b)
        for x, d, b in zip(args, depth, boundary)
    ]
    assert_int_chunksize(args)
    x = map_blocks(func, *args, **kwargs)
    assert_int_chunksize([x])
    if trim:
        # Find index of array argument with maximum rank and break ties by choosing first provided
        i = sorted(enumerate(args), key=lambda v: (v[1].ndim, -v[0]))[-1][0]
        # Trim using depth/boundary setting for array of highest rank
        depth = depth[i]
        boundary = boundary[i]
        # remove any dropped axes from depth and boundary variables
        drop_axis = kwargs.pop("drop_axis", None)
        if drop_axis is not None:
            if isinstance(drop_axis, Number):
                drop_axis = [drop_axis]

            # convert negative drop_axis to equivalent positive value
            ndim_out = max(a.ndim for a in args if isinstance(a, Array))
            drop_axis = [d % ndim_out for d in drop_axis]

            kept_axes = tuple(ax for ax in range(args[i].ndim)
                              if ax not in drop_axis)
            # note that keys are relabeled to match values in range(x.ndim)
            depth = {n: depth[ax] for n, ax in enumerate(kept_axes)}
            boundary = {n: boundary[ax] for n, ax in enumerate(kept_axes)}
        return trim_internal(x, depth, boundary)
    else:
        return x
Пример #9
0
def rcm2points(lat2d, lon2d, fi, lat1d, lon1d, opt=0, msg=None, meta=False):
    """Interpolates data on a curvilinear grid (i.e. RCM, WRF, NARR) to an
    unstructured grid.

    Paraemeters
    -----------
    lat2d : :class:`numpy.ndarray`:
        A two-dimensional array that specifies the latitudes locations
        of fi. The latitude order must be south-to-north.

    lon2d : :class:`numpy.ndarray`:
        A two-dimensional array that specifies the longitude locations
        of fi. The latitude order must be west-to-east.

    fi : :class:`numpy.ndarray`:
        A multi-dimensional array to be interpolated. The rightmost two
        dimensions (latitude, longitude) are the dimensions to be interpolated.

    lat1d : :class:`numpy.ndarray`:
        A one-dimensional array that specifies the latitude coordinates of
        the output locations.

    lon1d : :class:`numpy.ndarray`:
        A one-dimensional array that specifies the longitude coordinates of
        the output locations.

    opt : :obj:`numpy.number`:
        opt=0 or 1 means use an inverse distance weight interpolation.
        opt=2 means use a bilinear interpolation.

    msg : :obj:`numpy.number`:
        A numpy scalar value that represent a missing value in fi.
        This argument allows a user to use a missing value scheme
        other than NaN or masked arrays, similar to what NCL allows.

    meta : :obj:`bool`:
        If set to True and the input array is an Xarray, the metadata
        from the input array will be copied to the output array;
        default is False.
        Warning: This option is not currently supported.

    Returns
    -------

        :class:`numpy.ndarray`: The interpolated grid. A multi-dimensional array
        of the same size as fi except that the rightmost dimension sizes have been
        replaced by the number of coordinate pairs (lat1dPoints, lon1dPoints).
        Double if fi is double, otherwise float.

    Description
    -----------

        Interpolates data on a curvilinear grid, such as those used by the RCM
        (Regional Climate Model), WRF (Weather Research and Forecasting) and NARR
        (North American Regional Reanalysis) models/datasets to an unstructured grid.
        All of these have latitudes that are oriented south-to-north. An inverse
        distance squared algorithm is used to perform the interpolation. Missing
        values are allowed and no extrapolation is performed.
    """

    if (lon2d is None) | (lat2d is None):
        raise CoordinateError(
            "rcm2points: lon2d and lat2d should always be provided")

    # Basic sanity checks
    if lat2d.shape[0] != lon2d.shape[0] or lat2d.shape[1] != lon2d.shape[1]:
        raise DimensionError(
            "ERROR rcm2points: The input lat/lon grids must be the same size !"
        )

    if lat1d.shape[0] != lon1d.shape[0]:
        raise DimensionError(
            "ERROR rcm2points: The output lat/lon grids must be same size !")

    if lat2d.shape[0] < 2 or lon2d.shape[0] < 2 or lat2d.shape[
            1] < 2 or lon2d.shape[1] < 2:
        raise DimensionError(
            "ERROR rcm2points: The input/output lat/lon grids must have at least 2 elements !"
        )

    if fi.ndim < 2:
        raise DimensionError(
            "ERROR rcm2points: fi must be at least two dimensions !\n")

    if fi.shape[fi.ndim -
                2] != lat2d.shape[0] or fi.shape[fi.ndim -
                                                 1] != lon2d.shape[1]:
        raise DimensionError(
            "ERROR rcm2points: The rightmost dimensions of fi must be (nlat2d x nlon2d),"
            "where nlat2d and nlon2d are the size of the lat2d/lon2d arrays !")

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):
        fi = xr.DataArray(fi, )
        fi_chunk = dict([(k, v)
                         for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            dims=fi.dims,
        ).chunk(fi_chunk)

    # ensure rightmost dimensions of input are not chunked
    if fi.chunks is None:
        fi = fi.chunk()

    if list(fi.chunks)[-2:] != [(lat2d.shape[0], ), (lat2d.shape[1], )]:
        # [(lon2d.shape[0]), (lon2d.shape[1])] could also be used
        raise ChunkError(
            "rcm2points: fi must be unchunked along the rightmost two dimensions"
        )

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-2] = [
        (k, 1) for (k, v) in zip(list(fi.dims)[:-2],
                                 list(fi.chunks)[:-2])
    ]
    fi_chunks[-2:] = [
        (k, v[0]) for (k, v) in zip(list(fi.dims)[-2:],
                                    list(fi.chunks)[-2:])
    ]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = (lon1d.shape, )
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    # ''' end of boilerplate

    fo = map_blocks(
        _rcm2points,
        lat2d,
        lon2d,
        fi.data,
        lat1d,
        lon1d,
        msg,
        opt,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2],
    )

    fo = xr.DataArray(fo.compute(), attrs=fi.attrs)
    return fo
Пример #10
0
def linint1(fi, xo, xi=None, icycx=0, msg_py=None):
    # ''' signature : fo = dlinint1(xi,fi,xo,[icycx,xmsg,iopt])
    """Interpolates from one series to another using piecewise linear
    interpolation across the rightmost dimension.

    linint1 uses piecewise linear interpolation to interpolate from
    one series to another.  The series may be cyclic in the X
    direction.

    If missing values are present, then linint1 will perform the
    piecewise linear interpolation at all points possible, but
    will return missing values at coordinates which could not
    be used.

    If any of the output coordinates xo are outside those of the
    input coordinates xi, the fo values at those coordinates will
    be set to missing (i.e. no extrapolation is performed).


    Parameters
    ----------

    fi : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        An array of one or more dimensions. If xi is passed in as an
        argument, then the size of the rightmost dimension of fi
        must match the rightmost dimension of xi.

        If missing values are present, then linint1 will perform the
        piecewise linear interpolation at all points possible, but
        will return missing values at coordinates which could not be
        used.

        Note:
            This variable must be
            supplied as a :class:`xarray.DataArray` in order to copy
            the dimension names to the output. Otherwise, default
            names will be used.

    xo : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        A one-dimensional array that specifies the X coordinates of
        the return array. It must be strictly monotonically
        increasing or decreasing, but may be unequally spaced.

        If the output coordinates (xo) are outside those of the
        input coordinates (xi), then the fo values at those
        coordinates will be set to missing (i.e. no extrapolation is
        performed).

    xi (:class:`numpy.ndarray`):
        An array that specifies the X coordinates of the fi array.
        Most frequently, this array is one-dimensional.  It must be
        strictly monotonically increasing or decreasing, but can be
        unequally spaced.
        If xi is multi-dimensional, then its
        dimensions must be the same as fi's dimensions. If it is
        one-dimensional, its length must be the same as the rightmost
        (fastest varying) dimension of fi.

        Note:
            If fi is of type :class:`xarray.DataArray` and xi is
            left unspecified, then the rightmost coordinate
            dimension of fi will be used. If fi is not of type
            :class:`xarray.DataArray`, then xi becomes a mandatory
            parameter. This parameter must be specified as a keyword
            argument.

    icycx : :obj:`bool`:
        An option to indicate whether the rightmost dimension of fi
        is cyclic. This should be set to True only if you have
        global data, but your longitude values don't quite wrap all
        the way around the globe. For example, if your longitude
        values go from, say, -179.75 to 179.75, or 0.5 to 359.5,
        then you would set this to True.

    msg_py : :obj:`numpy.number`:
        A numpy scalar value that represent a missing value in fi.
        This argument allows a user to use a missing value scheme
        other than NaN or masked arrays, similar to what NCL allows.

    Returns
    -------
    fo : :class:`xarray.DataArray`:
        The interpolated series. The returned value will have the same
        dimensions as fi, except for the rightmost dimension which
        will have the same dimension size as the length of xo.
        The return type will be double if fi is double, and float
        otherwise.

    Examples
    --------

    Example 1: Using linint1 with :class:`xarray.DataArray` input
    .. code-block:: python
        import numpy as np
        import xarray as xr
        import geocat.comp
        fi_np = np.random.rand(80)  # random 80-element array
        # xi does not have to be equally spaced, but it is
        # in this example
        xi = np.arange(80)
        # create target coordinate array, in this case use the same
        # min/max values as xi, but with different spacing
        xo = np.linspace(xi.min(), xi.max(), 100)
        # create :class:`xarray.DataArray` and chunk it using the
        # full shape of the original array.
        # note that xi is attached as a coordinate array
        fi = xr.DataArray(fi_np,
                          dims=['x'],
                          coords={'x': xi}
                         ).chunk(fi_np.shape)
        fo = geocat.comp.linint1(fi, xo, icycx=0)
    """

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):
        if (xi is None):
            raise CoordinateError(
                "linint2: Argument xi must be provided explicitly unless fi is an xarray.DataArray."
            )

        fi = xr.DataArray(fi, )
        fi_chunk = dict([(k, v)
                         for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            coords={
                fi.dims[-1]: xi,
            },
            dims=fi.dims,
        ).chunk(fi_chunk)

    xi = fi.coords[fi.dims[-1]]

    # ensure rightmost dimensions of input are not chunked
    if fi.chunks is None:
        fi = fi.chunk()

    if list(fi.chunks)[-1:] != [xi.shape]:
        raise Exception("fi must be unchunked along the last dimension")

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-1] = [
        (k, 1) for (k, v) in zip(list(fi.dims)[:-1],
                                 list(fi.chunks)[:-1])
    ]
    fi_chunks[-1:] = [
        (k, v[0]) for (k, v) in zip(list(fi.dims)[-1:],
                                    list(fi.chunks)[-1:])
    ]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-1:] = (xo.shape, )
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {k: v for (k, v) in fi.coords.items()}
    fo_coords[fi.dims[-1]] = xo
    # ''' end of boilerplate

    fo = map_blocks(
        _linint1,
        xi,
        fi.data,
        xo,
        icycx,
        msg_py,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 1],
        new_axis=[fi.ndim - 1],
    )

    fo = xr.DataArray(fo.compute(),
                      attrs=fi.attrs,
                      dims=fi.dims,
                      coords=fo_coords)
    return fo
Пример #11
0
def rcm2rgrid(lat2d, lon2d, fi, lat1d, lon1d, msg=None, meta=False):
    """
    Interpolates data on a curvilinear grid (i.e. RCM, WRF, NARR) to a rectilinear grid.

    Parameters
    ----------

    lat2d : :class:`numpy.ndarray`:
        A two-dimensional array that specifies the latitudes locations
        of fi. Because this array is two-dimensional it is not an associated
        coordinate variable of `fi`. The latitude order must be south-to-north.

    lon2d : :class:`numpy.ndarray`:
        A two-dimensional array that specifies the longitude locations
        of fi. Because this array is two-dimensional it is not an associated
        coordinate variable of `fi`. The latitude order must be west-to-east.

    fi : :class:`numpy.ndarray`:
        A multi-dimensional array to be interpolated. The rightmost two
        dimensions (latitude, longitude) are the dimensions to be interpolated.

    lat1d : :class:`numpy.ndarray`:
        A one-dimensional array that specifies the latitude coordinates of
        the regular grid. Must be monotonically increasing.

    lon1d : :class:`numpy.ndarray`:
        A one-dimensional array that specifies the longitude coordinates of
        the regular grid. Must be monotonically increasing.

    msg : :obj:`numpy.number`:
        A numpy scalar value that represent a missing value in fi.
        This argument allows a user to use a missing value scheme
        other than NaN or masked arrays, similar to what NCL allows.

    meta : :obj:`bool`:
        If set to True and the input array is an Xarray, the metadata
        from the input array will be copied to the output array;
        default is False.
        Warning: This option is not currently supported.

    Returns
    -------

    fo : :class:`numpy.ndarray`:
        The interpolated grid. A multi-dimensional array
        of the same size as fi except that the rightmost dimension sizes have been
        replaced by the sizes of lat1d and lon1d respectively.
        Double if fi is double, otherwise float.

    Description
    -----------

        Interpolates RCM (Regional Climate Model), WRF (Weather Research and Forecasting) and
        NARR (North American Regional Reanalysis) grids to a rectilinear grid. Actually, this
        function will interpolate most grids that use curvilinear latitude/longitude grids.
        No extrapolation is performed beyond the range of the input coordinates. Missing values
        are allowed but ignored.

        The weighting method used is simple inverse distance squared. Missing values are allowed
        but ignored.

        The code searches the input curvilinear grid latitudes and longitudes for the four
        grid points that surround a specified output grid coordinate. Because one or more of
        these input points could contain missing values, fewer than four points
        could be used in the interpolation.

        Curvilinear grids which have two-dimensional latitude and longitude coordinate axes present
        some issues because the coordinates are not necessarily monotonically increasing. The simple
        search algorithm used by rcm2rgrid is not capable of handling all cases. The result is that,
        sometimes, there are small gaps in the interpolated grids. Any interior points not
        interpolated in the initial interpolation pass will be filled using linear interpolation.

        In some cases, edge points may not be filled.

    Examples
    --------

    Example 1: Using rcm2rgrid with :class:`xarray.DataArray` input

    .. code-block:: python

        import numpy as np
        import xarray as xr
        import geocat.comp

        # Open a netCDF data file using xarray default engine and load the data stream
        ds = xr.open_dataset("./ruc.nc")

        # [INPUT] Grid & data info on the source curvilinear
        ht_curv=ds.DIST_236_CBL[:]
        lat2D_curv=ds.gridlat_236[:]
        lon2D_curv=ds.gridlon_236[:]

        # [OUTPUT] Grid on destination rectilinear grid (or read the 1D lat and lon from
        #          an other .nc file.
        newlat1D_rect=np.linspace(lat2D_curv.min(), lat2D_curv.max(), 100)
        newlon1D_rect=np.linspace(lon2D_curv.min(), lon2D_curv.max(), 100)

        ht_rect = geocat.comp.rcm2rgrid(lat2D_curv, lon2D_curv, ht_curv, newlat1D_rect, newlon1D_rect)

    """
    if (lon2d is None) | (lat2d is None):
        raise CoordinateError(
            "rcm2rgrid: lon2d and lat2d should always be provided")

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):

        fi = xr.DataArray(fi, )
        fi_chunk = dict([(k, v)
                         for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            # coords={
            #     fi.dims[-1]: lon2d,
            #     fi.dims[-2]: lat2d,
            # },
            dims=fi.dims,
        ).chunk(fi_chunk)

    # lon2d = fi.coords[fi.dims[-1]]
    # lat2d = fi.coords[fi.dims[-2]]

    # ensure rightmost dimensions of input are not chunked
    if list(fi.chunks)[-2:] != [(lat2d.shape[0], ), (lat2d.shape[1], )]:
        # [(lon2d.shape[0]), (lon2d.shape[1])] would also be used
        raise ChunkError(
            "rcm2rgrid: fi must be unchunked along the rightmost two dimensions"
        )

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-2] = [
        (k, 1) for (k, v) in zip(list(fi.dims)[:-2],
                                 list(fi.chunks)[:-2])
    ]
    fi_chunks[-2:] = [
        (k, v[0]) for (k, v) in zip(list(fi.dims)[-2:],
                                    list(fi.chunks)[-2:])
    ]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = (lat1d.shape, lon1d.shape)
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {k: v for (k, v) in fi.coords.items()}
    fo_coords[fi.dims[-1]] = lon1d
    fo_coords[fi.dims[-2]] = lat1d
    # ''' end of boilerplate

    fo = map_blocks(
        _rcm2rgrid,
        lat2d,
        lon2d,
        fi.data,
        lat1d,
        lon1d,
        msg,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2, fi.ndim - 1],
    )
    fo = xr.DataArray(fo.compute(),
                      attrs=fi.attrs,
                      dims=fi.dims,
                      coords=fo_coords)
    return fo
Пример #12
0
def rgrid2rcm(lat1d, lon1d, fi, lat2d, lon2d, msg=None, meta=False):
    """
    Interpolates data on a rectilinear lat/lon grid to a curvilinear grid like
    those used by the RCM, WRF and NARR models/datasets.

    Parameters
    ----------

    lat1d : :class:`numpy.ndarray`:
        A one-dimensional array that specifies the latitude coordinates of
        the regular grid. Must be monotonically increasing.

    lon1d : :class:`numpy.ndarray`:
        A one-dimensional array that specifies the longitude coordinates of
        the regular grid. Must be monotonically increasing.

    fi : :class:`numpy.ndarray`:
        A multi-dimensional array to be interpolated. The rightmost two
        dimensions (latitude, longitude) are the dimensions to be interpolated.

    lat2d : :class:`numpy.ndarray`:
        A two-dimensional array that specifies the latitude locations
        of fi. Because this array is two-dimensional it is not an associated
        coordinate variable of `fi`.

    lon2d : :class:`numpy.ndarray`:
        A two-dimensional array that specifies the longitude locations
        of fi. Because this array is two-dimensional it is not an associated
        coordinate variable of `fi`.

    msg :obj:`numpy.number`:
        A numpy scalar value that represent a missing value in fi.
        This argument allows a user to use a missing value scheme
        other than NaN or masked arrays, similar to what NCL allows.

    meta :obj:`bool`:
        If set to True and the input array is an Xarray, the metadata
        from the input array will be copied to the output array;
        default is False.
        Warning: this option is not currently supported.

    Returns
    -------

    fo : :class:`numpy.ndarray`: The interpolated grid. A multi-dimensional array of the
    same size as `fi` except that the rightmost dimension sizes have been replaced
    by the sizes of `lat2d` and `lon2d` respectively. Double if `fi` is double,
    otherwise float.

    Description
    -----------

        Interpolates data on a rectilinear lat/lon grid to a curvilinear grid, such as those
    used by the RCM (Regional Climate Model), WRF (Weather Research and Forecasting) and
    NARR (North American Regional Reanalysis) models/datasets. No extrapolation is
    performed beyond the range of the input coordinates. The method used is simple inverse
    distance weighting. Missing values are allowed but ignored.

    Examples
    --------

    Example 1: Using rgrid2rcm with :class:`xarray.DataArray` input

    .. code-block:: python

        import numpy as np
        import xarray as xr
        import geocat.comp

        # Open a netCDF data file using xarray default engine and load the data stream
        # input grid and data
        ds_rect = xr.open_dataset("./DATAFILE_RECT.nc")

        # [INPUT] Grid & data info on the source rectilinear
        ht_rect   =ds_rect.SOME_FIELD[:]
        lat1D_rect=ds_rect.gridlat_[:]
        lon1D_rect=ds_rect.gridlon_[:]

        # Open a netCDF data file using xarray default engine and load the data stream
        # for output grid
        ds_curv = xr.open_dataset("./DATAFILE_CURV.nc")

        # [OUTPUT] Grid on destination curvilinear grid (or read the 2D lat and lon from
        #          an other .nc file
        newlat2D_rect=ds_curv.gridlat2D_[:]
        newlon2D_rect=ds_curv.gridlat2D_[:]

        ht_curv = geocat.comp.rgrid2rcm(lat1D_rect, lon1D_rect, ht_rect, newlat2D_curv, newlon2D_curv)

    """

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):
        if (lon1d is None) | (lat1d is None):
            raise CoordinateError(
                "rgrid2rcm: Arguments lon1d and lat1d must be provided explicitly unless fi is an xarray.DataArray."
            )

        fi = xr.DataArray(fi, )
        fi_chunk = dict([(k, v)
                         for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            coords={
                fi.dims[-1]: lon1d,
                fi.dims[-2]: lat1d,
            },
            dims=fi.dims,
        ).chunk(fi_chunk)

    lon1d = fi.coords[fi.dims[-1]]
    lat1d = fi.coords[fi.dims[-2]]

    # ensure rightmost dimensions of input are not chunked
    if list(fi.chunks)[-2:] != [lat1d.shape, lon1d.shape]:
        raise Exception("fi must be unchunked along the last two dimensions")

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-2] = [
        (k, 1) for (k, v) in zip(list(fi.dims)[:-2],
                                 list(fi.chunks)[:-2])
    ]
    fi_chunks[-2:] = [
        (k, v[0]) for (k, v) in zip(list(fi.dims)[-2:],
                                    list(fi.chunks)[-2:])
    ]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = [(lat2d.shape[0], ), (lat2d.shape[1], )]
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {k: v for (k, v) in fi.coords.items()}
    # fo_coords[fi.dims[-1]] = lon2d
    # fo_coords[fi.dims[-2]] = lat2d
    # ''' end of boilerplate

    fo = map_blocks(
        _rgrid2rcm,
        lat1d,
        lon1d,
        fi.data,
        lat2d,
        lon2d,
        msg,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2, fi.ndim - 1],
    )
    fo = xr.DataArray(fo.compute(),
                      attrs=fi.attrs,
                      dims=fi.dims,
                      coords=fo_coords)
    return fo
Пример #13
0
def rcm2rgrid(lat2d, lon2d, fi, lat1d, lon1d, msg_py=None):

    if (lon2d is None) | (lat2d is None):
        raise CoordinateError(
            "rcm2rgrid: lon2d and lat2d should always be provided")

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):

        fi = xr.DataArray(
            fi,
        )
        fi_chunk = dict([(k, v) for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            # coords={
            #     fi.dims[-1]: lon2d,
            #     fi.dims[-2]: lat2d,
            # },
            dims=fi.dims,
        ).chunk(fi_chunk)

    # lon2d = fi.coords[fi.dims[-1]]
    # lat2d = fi.coords[fi.dims[-2]]

    # ensure rightmost dimensions of input are not chunked
    if list(fi.chunks)[-2:] != [(lat2d.shape[0],), (lat2d.shape[1],)]:
                             # [(lon2d.shape[0]), (lon2d.shape[1])] would also be used
        raise ChunkError("linint2: fi must be unchunked along the rightmost two dimensions")

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-2] = [(k, 1) for (k, v) in zip(list(fi.dims)[:-2], list(fi.chunks)[:-2])]
    fi_chunks[-2:] = [(k, v[0]) for (k, v) in zip(list(fi.dims)[-2:], list(fi.chunks)[-2:])]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = (lat1d.shape, lon1d.shape)
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {
        k: v for (k, v) in fi.coords.items()
    }
    fo_coords[fi.dims[-1]] = lon1d
    fo_coords[fi.dims[-2]] = lat1d
    # ''' end of boilerplate


    fo = map_blocks(
        _rcm2rgrid,
        lat2d,
        lon2d,
        fi.data,
        lat1d,
        lon1d,
        msg_py,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2, fi.ndim - 1],
    )
    fo = xr.DataArray(fo.compute(), attrs=fi.attrs, dims=fi.dims, coords=fo_coords)
    return fo
Пример #14
0
def rgrid2rcm(lat1d, lon1d, fi, lat2d=None, lon2d=None, msg_py=None):

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):
        if (lon1d is None) | (lat1d is None):
            raise CoordinateError(
                "rgrid2rcm: Arguments lon1d and lat1d must be provided explicitly unless fi is an xarray.DataArray.")

        fi = xr.DataArray(
            fi,
        )
        fi_chunk = dict([(k, v) for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            coords={
                fi.dims[-1]: lon1d,
                fi.dims[-2]: lat1d,
            },
            dims=fi.dims,
        ).chunk(fi_chunk)

    lon1d = fi.coords[fi.dims[-1]]
    lat1d = fi.coords[fi.dims[-2]]

    # ensure rightmost dimensions of input are not chunked
    if list(fi.chunks)[-2:] != [lat1d.shape, lon1d.shape]:
        raise Exception("fi must be unchunked along the last two dimensions")

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-2] = [(k, 1) for (k, v) in zip(list(fi.dims)[:-2], list(fi.chunks)[:-2])]
    fi_chunks[-2:] = [(k, v[0]) for (k, v) in zip(list(fi.dims)[-2:], list(fi.chunks)[-2:])]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = [(lat2d.shape[0],), (lat2d.shape[1],)]
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {
        k: v for (k, v) in fi.coords.items()
    }
    # fo_coords[fi.dims[-1]] = lon2d
    # fo_coords[fi.dims[-2]] = lat2d
    # ''' end of boilerplate

    fo = map_blocks(
        _rgrid2rcm,
        lat1d,
        lon1d,
        fi.data,
        lat2d,
        lon2d,
        msg_py,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2, fi.ndim - 1],
    )
    fo = xr.DataArray(fo.compute(), attrs=fi.attrs, dims=fi.dims, coords=fo_coords)
    return fo
Пример #15
0
def linint1(fi, xo, xi=None, icycx=0, msg_py=None):
    # ''' signature : fo = dlinint1(xi,fi,xo,[icycx,xmsg,iopt])

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):
        if (xi is None):
            raise CoordinateError(
                "linint2: Argument xi must be provided explicitly unless fi is an xarray.DataArray."
            )

        fi = xr.DataArray(fi, )
        fi_chunk = dict([(k, v)
                         for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            coords={
                fi.dims[-1]: xi,
            },
            dims=fi.dims,
        ).chunk(fi_chunk)

    xi = fi.coords[fi.dims[-1]]

    # ensure rightmost dimensions of input are not chunked
    if list(fi.chunks)[-1:] != [xi.shape]:
        raise Exception("fi must be unchunked along the last dimension")

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-1] = [
        (k, 1) for (k, v) in zip(list(fi.dims)[:-1],
                                 list(fi.chunks)[:-1])
    ]
    fi_chunks[-1:] = [
        (k, v[0]) for (k, v) in zip(list(fi.dims)[-1:],
                                    list(fi.chunks)[-1:])
    ]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-1:] = (xo.shape, )
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {k: v for (k, v) in fi.coords.items()}
    fo_coords[fi.dims[-1]] = xo
    # ''' end of boilerplate

    fo = map_blocks(
        _linint1,
        xi,
        fi.data,
        xo,
        icycx,
        msg_py,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 1],
        new_axis=[fi.ndim - 1],
    )

    fo = xr.DataArray(fo.compute(),
                      attrs=fi.attrs,
                      dims=fi.dims,
                      coords=fo_coords)
    return fo
Пример #16
0
def linint2(fi, xo, yo, xi=None, yi=None, icycx=0, msg_py=None):
    """Interpolates a regular grid to a rectilinear one using bi-linear
    interpolation.
    linint2 uses bilinear interpolation to interpolate from one
    rectilinear grid to another. The input grid may be cyclic in the x
    direction. The interpolation is first performed in the x direction,
    and then in the y direction.
    Parameters
    ----------
    fi : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        An array of two or more dimensions. If xi is passed in as an
        argument, then the size of the rightmost dimension of fi
        must match the rightmost dimension of xi. Similarly, if yi
        is passed in as an argument, then the size of the second-
        rightmost dimension of fi must match the rightmost dimension
        of yi.
        If missing values are present, then linint2 will perform the
        bilinear interpolation at all points possible, but will
        return missing values at coordinates which could not be
        used.
        Note:
            This variable must be
            supplied as a :class:`xarray.DataArray` in order to copy
            the dimension names to the output. Otherwise, default
            names will be used.
    xo : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        A one-dimensional array that specifies the X coordinates of
        the return array. It must be strictly monotonically
        increasing, but may be unequally spaced.
        For geo-referenced data, xo is generally the longitude
        array.
        If the output coordinates (xo) are outside those of the
        input coordinates (xi), then the fo values at those
        coordinates will be set to missing (i.e. no extrapolation is
        performed).
    yo : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        A one-dimensional array that specifies the Y coordinates of
        the return array. It must be strictly monotonically
        increasing, but may be unequally spaced.
        For geo-referenced data, yo is generally the latitude array.
        If the output coordinates (yo) are outside those of the
        input coordinates (yi), then the fo values at those
        coordinates will be set to missing (i.e. no extrapolation is
        performed).
    xi (:class:`numpy.ndarray`):
        An array that specifies the X coordinates of the fi array.
        Most frequently, this is a 1D strictly monotonically
        increasing array that may be unequally spaced. In some
        cases, xi can be a multi-dimensional array (see next
        paragraph). The rightmost dimension (call it nxi) must have
        at least two elements, and is the last (fastest varying)
        dimension of fi.
        If xi is a multi-dimensional array, then each nxi subsection
        of xi must be strictly monotonically increasing, but may be
        unequally spaced. All but its rightmost dimension must be
        the same size as all but fi's rightmost two dimensions.
        For geo-referenced data, xi is generally the longitude
        array.
        Note:
            If fi is of type :class:`xarray.DataArray` and xi is
            left unspecified, then the rightmost coordinate
            dimension of fi will be used. If fi is not of type
            :class:`xarray.DataArray`, then xi becomes a mandatory
            parameter. This parameter must be specified as a keyword
            argument.
    yi (:class:`numpy.ndarray`):
        An array that specifies the Y coordinates of the fi array.
        Most frequently, this is a 1D strictly monotonically
        increasing array that may be unequally spaced. In some
        cases, yi can be a multi-dimensional array (see next
        paragraph). The rightmost dimension (call it nyi) must have
        at least two elements, and is the second-to-last dimension
        of fi.
        If yi is a multi-dimensional array, then each nyi subsection
        of yi must be strictly monotonically increasing, but may be
        unequally spaced. All but its rightmost dimension must be
        the same size as all but fi's rightmost two dimensions.
        For geo-referenced data, yi is generally the latitude array.
        Note:
            If fi is of type :class:`xarray.DataArray` and xi is
            left unspecified, then the second-to-rightmost
            coordinate dimension of fi will be used. If fi is not of
            type :class:`xarray.DataArray`, then xi becomes a
            mandatory parameter. This parameter must be specified as
            a keyword argument.
    icycx : :obj:`bool`:
        An option to indicate whether the rightmost dimension of fi
        is cyclic. This should be set to True only if you have
        global data, but your longitude values don't quite wrap all
        the way around the globe. For example, if your longitude
        values go from, say, -179.75 to 179.75, or 0.5 to 359.5,
        then you would set this to True.
    msg_py : :obj:`numpy.number`:
        A numpy scalar value that represent a missing value in fi.
        This argument allows a user to use a missing value scheme
        other than NaN or masked arrays, similar to what NCL allows.
    Returns
    -------
    fo : :class:`xarray.DataArray`:
        The interpolated grid. If the *meta*
        parameter is True, then the result will include named dimensions
        matching the input array. The returned value will have the same
        dimensions as fi, except for the rightmost two dimensions which
        will have the same dimension sizes as the lengths of yo and xo.
        The return type will be double if fi is double, and float
        otherwise.
    Examples
    --------
    Example 1: Using linint2 with :class:`xarray.DataArray` input
    .. code-block:: python
        import numpy as np
        import xarray as xr
        import geocat.comp
        fi_np = np.random.rand(30, 80)  # random 30x80 array
        # xi and yi do not have to be equally spaced, but they are
        # in this example
        xi = np.arange(80)
        yi = np.arange(30)
        # create target coordinate arrays, in this case use the same
        # min/max values as xi and yi, but with different spacing
        xo = np.linspace(xi.min(), xi.max(), 100)
        yo = np.linspace(yi.min(), yi.max(), 50)
        # create :class:`xarray.DataArray` and chunk it using the
        # full shape of the original array.
        # note that xi and yi are attached as coordinate arrays
        fi = xr.DataArray(fi_np,
                          dims=['lat', 'lon'],
                          coords={'lat': yi, 'lon': xi}
                         ).chunk(fi_np.shape)
        fo = geocat.comp.linint2(fi, xo, yo, icycx=0)
    """

    # ''' Start of boilerplate
    if not isinstance(fi, xr.DataArray):
        if (xi is None) | (yi is None):
            raise CoordinateError(
                "linint2: Arguments xi and yi must be provided explicitly unless fi is an xarray.DataArray."
            )

        fi = xr.DataArray(fi, )
        fi_chunk = dict([(k, v)
                         for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            coords={
                fi.dims[-1]: xi,
                fi.dims[-2]: yi,
            },
            dims=fi.dims,
        ).chunk(fi_chunk)

    xi = fi.coords[fi.dims[-1]]
    yi = fi.coords[fi.dims[-2]]

    # ensure rightmost dimensions of input are not chunked
    if fi.chunks is None:
        fi = fi.chunk()

    if list(fi.chunks)[-2:] != [yi.shape, xi.shape]:
        raise ChunkError(
            "linint2: fi must be unchunked along the rightmost two dimensions")

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-2] = [
        (k, 1) for (k, v) in zip(list(fi.dims)[:-2],
                                 list(fi.chunks)[:-2])
    ]
    fi_chunks[-2:] = [
        (k, v[0]) for (k, v) in zip(list(fi.dims)[-2:],
                                    list(fi.chunks)[-2:])
    ]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = (yo.shape, xo.shape)
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {k: v for (k, v) in fi.coords.items()}
    fo_coords[fi.dims[-1]] = xo
    fo_coords[fi.dims[-2]] = yo
    # ''' end of boilerplate

    fo = map_blocks(
        _linint2,
        yi,
        xi,
        fi.data,
        yo,
        xo,
        icycx,
        msg_py,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2, fi.ndim - 1],
    )
    fo = xr.DataArray(fo.compute(),
                      attrs=fi.attrs,
                      dims=fi.dims,
                      coords=fo_coords)
    return fo
Пример #17
0
def linint2pts(fi, xo, yo, icycx=False, msg_py=None, xi=None, yi=None):
    """Interpolates from a rectilinear grid to an unstructured grid or
    locations using bilinear interpolation.

    Parameters
    ----------
    fi : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        An array of two or more dimensions. The two rightmost
        dimensions (nyi x nxi) are the dimensions to be used in
        the interpolation. If user-defined missing values are
        present (other than NaNs), the value of `msg_py` must be
        set appropriately.
    xo : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        A one-dimensional array that specifies the X (longitude)
        coordinates of the unstructured grid.
    yo : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        A one-dimensional array that specifies the Y (latitude)
        coordinates of the unstructured grid. It must be the same
        length as `xo`.
    icycx : :obj:`bool`:
        An option to indicate whether the rightmost dimension of fi
        is cyclic. Default valus is 0. This should be set to True
        only if you have global data, but your longitude values
        don't quite wrap all the way around the globe. For example,
        if your longitude values go from, say, -179.75 to 179.75,
        or 0.5 to 359.5, then you would set this to True.
    msg_py : :obj:`numpy.number`:
        A numpy scalar value that represent a missing value in fi.
        This argument allows a user to use a missing value scheme
        other than NaN or masked arrays, similar to what NCL allows.
    xi : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        A strictly monotonically increasing array that specifies
        the X [longitude] coordinates of the `fi` array. `xi` might
        be defined as the coordinates of `fi` when `fi` is of type
        `xarray.DataArray`; in this case `xi` may not be explicitly
        given as a function argument.
    yi : :class:`xarray.DataArray` or :class:`numpy.ndarray`:
        A strictly monotonically increasing array that specifies
        the Y [latitude] coordinates of the `fi` array. ``yi` might
        be defined as the coordinates of `fi` when `fi` is of type
        `xarray.DataArray`; in this case `yi` may not be explicitly
        given as a function argument.
    Returns
    -------
    fo: :class:`numpy.ndarray`:
        The returned value will have the same dimensions as `fi`,
        except for the rightmost dimension which will have the same
        dimension size as the length of `yo` and `xo`. The return
        type will be double if `fi` is double, and float otherwise.
    Description
    -----------
        The `linint2pts` function uses bilinear interpolation to interpolate
        from a rectilinear grid to an unstructured grid.
        If missing values are present, then `linint2pts` will perform the
        piecewise linear interpolation at all points possible, but will return
        missing values at coordinates which could not be used. If one or more
        of the four closest grid points to a particular (xo, yo) coordinate
        pair are missing, then the return value for this coordinate pair will
        be missing.
        If the user inadvertently specifies output coordinates (xo, yo) that
        are outside those of the input coordinates (xi, yi), the output value
        at this coordinate pair will be set to missing as no extrapolation
        is performed.
        `linint2pts` is different from `linint2` in that `xo` and `yo` are
        coordinate pairs, and need not be monotonically increasing. It is
        also different in the dimensioning of the return array.
        This function could be used if the user wanted to interpolate gridded
        data to, say, the location of rawinsonde sites or buoy/xbt locations.

        Warning: if `xi` contains longitudes, then the `xo` values must be in the
        same range. In addition, if the `xi` values span 0 to 360, then the `xo`
        values must also be specified in this range (i.e. -180 to 180 will not work).

    Examples
    --------
    Example 1: Using linint2pts with :class:`xarray.DataArray` input
        .. code-block:: python
        import numpy as np
        import xarray as xr
        import geocat.comp
        fi_np = np.random.rand(30, 80)  # random 30x80 array
        # xi and yi do not have to be equally spaced, but they are
        # in this example
        xi = np.arange(80)
        yi = np.arange(30)
        # create target coordinate arrays, in this case use the same
        # min/max values as xi and yi, but with different spacing
        xo = np.linspace(xi.min(), xi.max(), 100)
        yo = np.linspace(yi.min(), yi.max(), 50)
        # create :class:`xarray.DataArray` and chunk it using the
        # full shape of the original array.
        # note that xi and yi are attached as coordinate arrays
        fi = xr.DataArray(fi_np,
                          dims=['lat', 'lon'],
                          coords={'lat': yi, 'lon': xi}
                         ).chunk(fi_np.shape)
        fo = geocat.comp.linint2pts(fi, xo, yo, 0)
    """

    # ''' Start of boilerplate
    # If a Numpy input is given, convert it to Xarray and chunk it just
    # with its dims
    if not isinstance(fi, xr.DataArray):
        if (xi is None) | (yi is None):
            raise CoordinateError(
                "linint2pts: Arguments xi and yi must be provided explicitly unless fi is an xarray.DataArray."
            )

        fi = xr.DataArray(fi)
        fi_chunk = dict([(k, v)
                         for (k, v) in zip(list(fi.dims), list(fi.shape))])

        fi = xr.DataArray(
            fi.data,
            coords={
                fi.dims[-1]: xi,
                fi.dims[-2]: yi,
            },
            dims=fi.dims,
        ).chunk(fi_chunk)

    # Xarray input
    else:
        # If an unchunked Xarray input is given, chunk it just with its dims
        if (fi.chunks is None):
            fi_chunk = dict([(k, v)
                             for (k, v) in zip(list(fi.dims), list(fi.shape))])
            data = fi.chunk(fi_chunk)

    xi = fi.coords[fi.dims[-1]]
    yi = fi.coords[fi.dims[-2]]

    # Ensure the rightmost dimension of input is not chunked
    if fi.chunks is None:
        fi = fi.chunk()

    if list(fi.chunks)[-2:] != [yi.shape, xi.shape]:
        raise ChunkError(
            "ERROR linint2pts: fi must be unchunked along the rightmost two dimensions"
        )

    if xo.shape != yo.shape:
        raise Exception("ERROR linint2pts xo and yo must be of equal length")

    # fi data structure elements and autochunking
    fi_chunks = list(fi.dims)
    fi_chunks[:-2] = [
        (k, 1) for (k, v) in zip(list(fi.dims)[:-2],
                                 list(fi.chunks)[:-2])
    ]
    fi_chunks[-2:] = [
        (k, v[0]) for (k, v) in zip(list(fi.dims)[-2:],
                                    list(fi.chunks)[-2:])
    ]
    fi_chunks = dict(fi_chunks)
    fi = fi.chunk(fi_chunks)

    # fo datastructure elements
    fo_chunks = list(fi.chunks)
    fo_chunks[-2:] = (xo.shape, )
    fo_chunks = tuple(fo_chunks)
    fo_shape = tuple(a[0] for a in list(fo_chunks))
    fo_coords = {k: v for (k, v) in fi.coords.items()}
    # fo_coords.remove(fi.dims[-1]) # this dimension dissapears
    fo_coords[fi.dims[-1]] = xo  # remove this line omce dims are figured out
    fo_coords[fi.dims[-2]] = yo  # maybe replace with 'pts'
    # ''' end of boilerplate

    fo = map_blocks(
        _linint2pts,
        yi,
        xi,
        fi.data,
        yo,
        xo,
        icycx,
        msg_py,
        fo_shape,
        chunks=fo_chunks,
        dtype=fi.dtype,
        drop_axis=[fi.ndim - 2, fi.ndim - 1],
        new_axis=[fi.ndim - 2],
    )
    fo = xr.DataArray(fo.compute(), attrs=fi.attrs)
    return fo
Пример #18
0
def rgrid2rcm(lat1d, lon1d, fi, lat2d, lon2d, msg=None, meta=False):
    """Interpolates data on a rectilinear lat/lon grid to a curvilinear grid like
       those used by the RCM, WRF and NARR models/datasets.

    Args:

        lat1d (:class:`numpy.ndarray`):
	    A one-dimensional array that specifies the latitude coordinates of
	    the regular grid. Must be monotonically increasing.

        lon1d (:class:`numpy.ndarray`):
	    A one-dimensional array that specifies the longitude coordinates of
	    the regular grid. Must be monotonically increasing.

        fi (:class:`numpy.ndarray`):
	    A multi-dimensional array to be interpolated. The rightmost two
	    dimensions (latitude, longitude) are the dimensions to be interpolated.

        lat2d (:class:`numpy.ndarray`):
	    A two-dimensional array that specifies the latitude locations
	    of fi. Because this array is two-dimensional it is not an associated
	    coordinate variable of `fi`.

        lon2d (:class:`numpy.ndarray`):
	    A two-dimensional array that specifies the longitude locations
	    of fi. Because this array is two-dimensional it is not an associated
	    coordinate variable of `fi`.

        msg (:obj:`numpy.number`):
            A numpy scalar value that represent a missing value in fi.
            This argument allows a user to use a missing value scheme
            other than NaN or masked arrays, similar to what NCL allows.

        meta (:obj:`bool`):
            If set to True and the input array is an Xarray, the metadata
            from the input array will be copied to the output array;
            default is False.
            Warning: this option is not currently supported.

    Returns:
        :class:`numpy.ndarray`: The interpolated grid. A multi-dimensional array of the
	same size as `fi` except that the rightmost dimension sizes have been replaced
	by the sizes of `lat2d` and `lon2d` respectively. Double if `fi` is double,
	otherwise float.

    Description:
        Interpolates data on a rectilinear lat/lon grid to a curvilinear grid, such as those
	used by the RCM (Regional Climate Model), WRF (Weather Research and Forecasting) and
	NARR (North American Regional Reanalysis) models/datasets. No extrapolation is
	performed beyond the range of the input coordinates. The method used is simple inverse
	distance weighting. Missing values are allowed but ignored.

    Examples:

        Example 1: Using rgrid2rcm with :class:`xarray.DataArray` input

        .. code-block:: python

            import numpy as np
            import xarray as xr
            import geocat.comp

            # Open a netCDF data file using xarray default engine and load the data stream
            # input grid and data
            ds_rect = xr.open_dataset("./DATAFILE_RECT.nc")

            # [INPUT] Grid & data info on the source rectilinear
            ht_rect   =ds_rect.SOME_FIELD[:]
            lat1D_rect=ds_rect.gridlat_[:]
            lon1D_rect=ds_rect.gridlon_[:]

            # Open a netCDF data file using xarray default engine and load the data stream
            # for output grid
            ds_curv = xr.open_dataset("./DATAFILE_CURV.nc")

            # [OUTPUT] Grid on destination curvilinear grid (or read the 2D lat and lon from
            #          an other .nc file
            newlat2D_rect=ds_curv.gridlat2D_[:]
            newlon2D_rect=ds_curv.gridlat2D_[:]

            ht_curv = geocat.comp.rgrid2rcm(lat1D_rect, lon1D_rect, ht_rect, newlat2D_curv, newlon2D_curv)


    """

    # todo: Revisit for handling of "meta" argument
    # Basic sanity checks
    if lat2d.shape[0] != lon2d.shape[0] or lat2d.shape[1] != lon2d.shape[1]:
        raise DimensionError(
            "ERROR rgrid2rcm: The output lat2D/lon2D grids must be the same size !"
        )

    if lat2d.shape[0] < 2 or lon2d.shape[0] < 2 or lat2d.shape[
            1] < 2 or lon2d.shape[1] < 2:
        raise DimensionError(
            "ERROR rgrid2rcm: The input/output lat/lon grids must have at least 2 elements !"
        )

    if fi.ndim < 2:
        raise DimensionError(
            "ERROR rgrid2rcm: fi must be at least two dimensions !\n")

    if fi.shape[fi.ndim -
                2] != lat1d.shape[0] or fi.shape[fi.ndim -
                                                 1] != lon1d.shape[0]:
        raise DimensionError(
            "ERROR rgrid2rcm: The rightmost dimensions of fi must be (nlat1d x nlon1d),"
            "where nlat1d and nlon1d are the size of the lat1d/lon1d arrays !")

    if isinstance(lat1d, xr.DataArray):
        lat1d = lat1d.values

    if isinstance(lon1d, xr.DataArray):
        lon1d = lon1d.values

    if not isinstance(fi, xr.DataArray):
        fi = xr.DataArray(fi)

    # ensure lat2d and lon2d are numpy.ndarrays
    if isinstance(lat2d, xr.DataArray):
        lat2d = lat2d.values
    if isinstance(lon2d, xr.DataArray):
        lon2d = lon2d.values

    fi_data = fi.data

    if isinstance(fi_data, da.Array):
        chunks = list(fi.chunks)

        # ensure rightmost dimensions of input are not chunked
        if chunks[-2:] != [lon1d.shape, lat1d.shape]:
            raise ChunkError(
                "rgrid2rcm: the two rightmost dimensions of fi must"
                " not be chunked.")

        # ensure rightmost dimensions of output are not chunked
        chunks[-2:] = (lon2d.shape, lat2d.shape)

        fo = map_blocks(_ncomp._rgrid2rcm,
                        lat1d,
                        lon1d,
                        fi_data,
                        lat2d,
                        lon2d,
                        msg,
                        chunks=chunks,
                        dtype=fi.dtype,
                        drop_axis=[fi.ndim - 2, fi.ndim - 1],
                        new_axis=[fi.ndim - 2, fi.ndim - 1])
    elif isinstance(fi_data, np.ndarray):
        fo = _ncomp._rgrid2rcm(lat1d, lon1d, fi_data, lat2d, lon2d, msg)
    else:
        raise TypeError("rgrid2rcm: the fi input argument must be a "
                        "numpy.ndarray, a dask.array.Array, or an "
                        "xarray.DataArray containing either a numpy.ndarray or"
                        " a dask.array.Array.")

    if meta and isinstance(input, xr.DataArray):
        raise MetaError(
            "ERROR rgrid2rcm: retention of metadata is not yet supported !")
    else:
        fo = xr.DataArray(fo)

    return fo
Пример #19
0
def rcm2rgrid(lat2d, lon2d, fi, lat1d, lon1d, msg=None, meta=False):
    """Interpolates data on a curvilinear grid (i.e. RCM, WRF, NARR) to a rectilinear grid.

    Args:

        lat2d (:class:`numpy.ndarray`):
	    A two-dimensional array that specifies the latitudes locations
	    of fi. Because this array is two-dimensional it is not an associated
	    coordinate variable of `fi`. The latitude order must be south-to-north.

        lon2d (:class:`numpy.ndarray`):
	    A two-dimensional array that specifies the longitude locations
	    of fi. Because this array is two-dimensional it is not an associated
	    coordinate variable of `fi`. The latitude order must be west-to-east.

        fi (:class:`numpy.ndarray`):
	    A multi-dimensional array to be interpolated. The rightmost two
	    dimensions (latitude, longitude) are the dimensions to be interpolated.

        lat1d (:class:`numpy.ndarray`):
	    A one-dimensional array that specifies the latitude coordinates of
	    the regular grid. Must be monotonically increasing.

        lon1d (:class:`numpy.ndarray`):
	    A one-dimensional array that specifies the longitude coordinates of
	    the regular grid. Must be monotonically increasing.

        msg (:obj:`numpy.number`):
            A numpy scalar value that represent a missing value in fi.
            This argument allows a user to use a missing value scheme
            other than NaN or masked arrays, similar to what NCL allows.

        meta (:obj:`bool`):
            If set to True and the input array is an Xarray, the metadata
            from the input array will be copied to the output array;
            default is False.
            Warning: this option is not currently supported.

    Returns:
        :class:`numpy.ndarray`: The interpolated grid. A multi-dimensional array
	of the same size as fi except that the rightmost dimension sizes have been
	replaced by the sizes of lat1d and lon1d respectively.
	Double if fi is double, otherwise float.

    Description:
        Interpolates RCM (Regional Climate Model), WRF (Weather Research and Forecasting) and
        NARR (North American Regional Reanalysis) grids to a rectilinear grid. Actually, this
	function will interpolate most grids that use curvilinear latitude/longitude grids.
	No extrapolation is performed beyond the range of the input coordinates. Missing values
	are allowed but ignored.

	The weighting method used is simple inverse distance squared. Missing values are allowed
	but ignored.

	The code searches the input curvilinear grid latitudes and longitudes for the four
	grid points that surround a specified output grid coordinate. Because one or more of
	these input points could contain missing values, fewer than four points
	could be used in the interpolation.

	Curvilinear grids which have two-dimensional latitude and longitude coordinate axes present
	some issues because the coordinates are not necessarily monotonically increasing. The simple
	search algorithm used by rcm2rgrid is not capable of handling all cases. The result is that,
	sometimes, there are small gaps in the interpolated grids. Any interior points not
	interpolated in the initial interpolation pass will be filled using linear interpolation.
        In some cases, edge points may not be filled.

    Examples:

        Example 1: Using rcm2rgrid with :class:`xarray.DataArray` input

        .. code-block:: python

            import numpy as np
            import xarray as xr
            import geocat.comp

            # Open a netCDF data file using xarray default engine and load the data stream
            ds = xr.open_dataset("./ruc.nc")

            # [INPUT] Grid & data info on the source curvilinear
            ht_curv=ds.DIST_236_CBL[:]
            lat2D_curv=ds.gridlat_236[:]
            lon2D_curv=ds.gridlon_236[:]

            # [OUTPUT] Grid on destination rectilinear grid (or read the 1D lat and lon from
            #          an other .nc file.
            newlat1D_rect=np.linspace(lat2D_curv.min(), lat2D_curv.max(), 100)
            newlon1D_rect=np.linspace(lon2D_curv.min(), lon2D_curv.max(), 100)

            ht_rect = geocat.comp.rcm2rgrid(lat2D_curv, lon2D_curv, ht_curv, newlat1D_rect, newlon1D_rect)


    """

    # todo: Revisit for handling of "meta" argument
    # Basic sanity checks
    if lat2d.shape[0] != lon2d.shape[0] or lat2d.shape[1] != lon2d.shape[1]:
        raise DimensionError(
            "ERROR rcm2rgrid: The input lat/lon grids must be the same size !")

    if lat2d.shape[0] < 2 or lon2d.shape[0] < 2 or lat2d.shape[
            1] < 2 or lon2d.shape[1] < 2:
        raise DimensionError(
            "ERROR rcm2rgrid: The input/output lat/lon grids must have at least 2 elements !"
        )

    if fi.ndim < 2:
        raise DimensionError(
            "ERROR rcm2rgrid: fi must be at least two dimensions !\n")

    if fi.shape[fi.ndim -
                2] != lat2d.shape[0] or fi.shape[fi.ndim -
                                                 1] != lon2d.shape[1]:
        raise DimensionError(
            "ERROR rcm2rgrid: The rightmost dimensions of fi must be (nlat2d x nlon2d),"
            "where nlat2d and nlon2d are the size of the lat2d/lon2d arrays !")

    if isinstance(lat2d, xr.DataArray):
        lat2d = lat2d.values

    if isinstance(lon2d, xr.DataArray):
        lon2d = lon2d.values

    if not isinstance(fi, xr.DataArray):
        fi = xr.DataArray(fi)

    # ensure lat1d and lon1d are numpy.ndarrays
    if isinstance(lat1d, xr.DataArray):
        lat1d = lat1d.values
    if isinstance(lon1d, xr.DataArray):
        lon1d = lon1d.values

    fi_data = fi.data

    if isinstance(fi_data, da.Array):
        chunks = list(fi.chunks)

        # ensure rightmost dimensions of input are not chunked
        if chunks[-2:] != [lon2d.shape, lat2d.shape]:
            raise ChunkError(
                "rcm2rgrid: the two rightmost dimensions of fi must"
                " not be chunked.")

        # ensure rightmost dimensions of output are not chunked
        chunks[-2:] = (lon1d.shape, lat1d.shape)

        fo = map_blocks(_ncomp._rcm2rgrid,
                        lat2d,
                        lon2d,
                        fi_data,
                        lat1d,
                        lon1d,
                        msg,
                        chunks=chunks,
                        dtype=fi.dtype,
                        drop_axis=[fi.ndim - 2, fi.ndim - 1],
                        new_axis=[fi.ndim - 2, fi.ndim - 1])
    elif isinstance(fi_data, np.ndarray):
        fo = _ncomp._rcm2rgrid(lat2d, lon2d, fi_data, lat1d, lon1d, msg)
    else:
        raise TypeError("rcm2rgrid: the fi input argument must be a "
                        "numpy.ndarray, a dask.array.Array, or an "
                        "xarray.DataArray containing either a numpy.ndarray or"
                        " a dask.array.Array.")

    if meta and isinstance(input, xr.DataArray):
        raise MetaError(
            "ERROR rcm2rgrid: retention of metadata is not yet supported !")
    else:
        fo = xr.DataArray(fo)

    return fo