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)
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)
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()
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()
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
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
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
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
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
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
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
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
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
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
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
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
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
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