Source code for windspharm.tools

"""
Tools for managing data for use with `~windspharm.standard.VectorWind`
(or indeed `spharm.Spharmt`).

"""
# Copyright (c) 2012-2017 Andrew Dawson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import absolute_import

import numpy as np


def __order_dims(d, inorder):
    if 'x' not in inorder or 'y' not in inorder:
        raise ValueError('a latitude-longitude grid is required')
    lonpos = inorder.lower().find('x')
    latpos = inorder.lower().find('y')
    d = np.rollaxis(d, lonpos)
    if latpos < lonpos:
        latpos += 1
    d = np.rollaxis(d, latpos)
    outorder = inorder.replace('x', '')
    outorder = outorder.replace('y', '')
    outorder = 'yx' + outorder
    return d, outorder


def __reshape(d):
    out = d.reshape(d.shape[:2] + (np.prod(d.shape[2:], dtype=np.int),))
    return out, d.shape


[docs]def prep_data(data, dimorder): """ Prepare data for input to `~windspharm.standard.VectorWind` (or to `spharm.Spharmt` method calls). Returns a dictionary of intermediate information that can be passed to `recover_data` or `get_recovery` to recover the original shape and order of the data. **Arguments:** *data* Data array. The array must be at least 2D. *dimorder* String specifying the order of dimensions in the data array. The characters 'x' and 'y' represent longitude and latitude respectively. Any other characters can be used to represent other dimensions. **Returns:** *pdata* *data* reshaped/reordered to (latitude, longitude, other). *info* A dictionary of information required to recover *data*. **See also:** `recover_data`, `get_recovery`. **Examples:** Prepare an array with dimensions (12, 17, 73, 144) where the dimensions are (time, level, latitude, longitude):: pdata, info = prep_data(data, 'tzyx') Prepare an array with dimensions (144, 16, 73, 21) where the first dimension is longitude and the third dimension is latitude. The characters used to represent the other dimensions are arbitrary:: pdata, info = prep_data(data, 'xayb') """ # Returns the prepared data and some data info to help data recovery. pdata, intorder = __order_dims(data, dimorder) pdata, intshape = __reshape(pdata) info = dict(intermediate_shape=intshape, intermediate_order=intorder, original_order=dimorder) return pdata, info
[docs]def recover_data(pdata, info): """ Recover the shape and dimension order of an array output from `~windspharm.standard.VectorWind` methods (or from `spharm.Spharmt` methods). This function performs the opposite of `prep_data`. For recovering the shape of multiple variables, see `get_recovery`. **Arguments:** *pdata* Data array with either 2 or 3 dimensions. The first two dimensions are latitude and longitude respectively. *info* Information dictionary output from `prep_data`. **Returns:** *data* The data reshaped/reordered. **See also:** `prep_data`, `get_recovery`. **Example:** Recover the original input shape and dimension order of an array processed with `prep_data` or an output of `~windspharm.standard.VectorWind` or `sparm.Spharmt` method calls on such data:: data = recover_data(pdata, info) """ # Convert to intermediate shape (full dimensionality, windspharm order). data = pdata.reshape(info['intermediate_shape']) # Re-order dimensions correctly. rolldims = np.array([info['intermediate_order'].index(dim) for dim in info['original_order'][::-1]]) for i in range(len(rolldims)): # Roll the axis to the front. data = np.rollaxis(data, rolldims[i]) rolldims = np.where(rolldims < rolldims[i], rolldims + 1, rolldims) return data
__recover_docstring_template = """Shape/dimension recovery. Recovers variable shape/dimension according to: {!s} Returns a `list` of variables. """
[docs]def get_recovery(info): """ Return a function that can be used to recover the shape and dimension order of multiple arrays output from `~windspharm.standard.VectorWind` methods (or from `spharm.Spharmt` methods) according to a single dictionary of recovery information. **Argument:** *info* Information dictionary output from `prep_data`. **Returns:** *recover* A function used to recover arrays. **See also:** `recover_data`, `prep_data`. **Example:** Generate a function to recover the original input shape and dimension order of arrays processed with `prep_data` and outputs of `~windspharm.standard.VectorWind` method calls on this data:: u, info = prep_data(u, 'tzyx') v, info = prep_data(v, 'tzyx') w = VectorWind(u, v) sf, vp = w.sfvp() recover = get_recovery(info) u, v, sf, vp = recover(u, v, sf, vp) """ def __recover(*args): return [recover_data(arg, info) for arg in args] info_nice = ["'{!s}': {!s}".format(key, value) for key, value in info.items()] __recover.__name__ = 'recover' __recover.__doc__ = __recover_docstring_template.format( '\n'.join(info_nice)) return __recover
[docs]def reverse_latdim(u, v, axis=0): """ Reverse the order of the latitude dimension of zonal and meridional wind components. **Arguments:** *u*, *v* Zonal and meridional wind components respectively. **Optional argument:** *axis* Index of the latitude dimension. This dimension will be reversed in the input arrays. Defaults to 0 (the first dimension). **Returns:** *ur*, *vr* Zonal and meridional wind components with the latitude dimensions reversed. These are always copies of the input. **See also:** `order_latdim`. **Examples:** Reverse the dimension corresponding to latitude when it is the first dimension of the inputs:: u, v = reverse_latdim(u, v) Reverse the dimension corresponding to latitude when it is the third dimension of the inputs:: u, v = reverse_latdim(u, v, axis=2) """ slicelist = [slice(0, None)] * u.ndim slicelist[axis] = slice(None, None, -1) u = u.copy()[tuple(slicelist)] v = v.copy()[tuple(slicelist)] return u, v
[docs]def order_latdim(latdim, u, v, axis=0): """Ensure the latitude dimension is north-to-south. Returns copies of the latitude dimension and wind components with the latitude dimension going from north to south. If the latitude dimension is already in this order then the output will just be copies of the input. **Arguments:** *latdim* Array of latitude values. *u*, *v* Zonal and meridional wind components respectively. **Keyword argument:** *axis* Index of the latitude dimension in the zonal and meridional wind components. Defaults to 0 (the first dimension). **Returns:** *latdimr* Possibly reversed *latdim*, always a copy of *latdim*. *ur*, *vr* Possibly reversed *u* and *v* respectively. Always copies of *u* and *v* respectively. **See also:** `reverse_latdim`. **Examples:** Order the latitude dimension when latitude is the first dimension of the wind components:: latdim, u, v = order_latdim(latdim, u, v) Order the latitude dimension when latitude is the third dimension of the wind components:: latdim, u, v = order_latdim(latdim, u, v, axis=2) """ latdim = latdim.copy() if latdim[0] < latdim[-1]: latdim = latdim[::-1] # reverse_latdim() will make copies of u and v u, v = reverse_latdim(u, v, axis=axis) else: # we return copies from this function u, v = u.copy(), v.copy() return latdim, u, v