def _check_axisoptions(x, axisoptions): """checks the axis=options to make sure that it is valid""" axlist = cdms2.orderparse(axisoptions) if Ellipsis in axlist: raise AveragerError, 'Error: Ellipsis (...) not allowed in axis options' try: cdms2.order2index(x.getAxisList(), axisoptions) return axlist except: raise AveragerError, 'Error: You have specified an invalid axis= option.'
def check_order(var, allowed, vertical=None, copy=False, reorder=False, extended=None, getorder=False): """Check that the axis order of a variable is matches at least one the specifed valid orders :Params: - **var**: MV2 array. - **allowed**: A single order string or a list. It should contain one or several of these letters: - ``x``: longitudes, - ``y``: latitudes, - ``z``: vertical levels, - ``t``: time axis, - ``d``: data values (ignored), - ``-``: any kind of axis. :Return: ``var``, or ``var, order, reordered`` if **reorder** is True. """ # Check allowed orders # - consistency if not isinstance(allowed, (list, tuple)): allowed = [allowed] else: allowed = list(allowed) withd = 'd' in allowed[0] get_rank = lambda o: len(o.replace('d', '')) rank = get_rank(allowed[0]) for order in allowed: try: cdms2.orderparse(order.lower().replace('d', '')) except: raise VACUMMError("Wrong allowed order: "+order) if ('d' in order and not withd) or ('d' not in order and withd): raise VACUMMError("'d' only partially present in allowed order: %s"%allowed) if get_rank(order)!=rank: raise VACUMMError("Inconsistent ranks between allowed orders: %s"%[get_rank(o) for o in allowed]) # - check extended mode if extended is None: # extended? re_upper = re.compile('[XYZT]').search for order in allowed: if re_upper(order) is not None: extended = True # force extended mode break else: extended = False if extended is False: # lower allowed = [order.lower() for order in allowed] else: #add tolerance for lower case orders re_sub = re.compile('[xyzt]').sub allowed = allowed+[re_sub('-', order) for order in allowed] # - unique and lower case _, idx = N.unique(allowed, return_index=True) idx = N.sort(idx) allowed = N.array(allowed)[idx].tolist() # - restrict to vertical or horizontal (1D data only) if vertical is not None and len(allowed[0])==2: allowed = [oo for oo in allowed if oo[int(vertical)]=='d'] # Data order data_cdms_order = get_order(var) # Loop on allowed orders from vacumm.misc.grid.misc import get_axis, var2d reordered = False for allowed_order in allowed: # Handle data case d = allowed_order.find('d') if d!=-1: allowed_order = allowed_order.replace('d', '') # pure axis # Check cdms order allowed_cdms_order = allowed_order.lower() # lower case if order_match(data_cdms_order, allowed_cdms_order, strict='right'): break # It is already good # Try to reorder if reorder: try: reordered = cdms2.order2index(var.getAxisList(), allowed_cdms_order) new_var = var.reorder(allowed_cdms_order) if allowed_cdms_order[-1]=='x' and len(get_axis(new_var, -1).shape)==2: # 2D axes del var var = var2d(new_var, MV2.transpose(get_axis(new_var, 0)), MV2.transpose(get_axis(new_var, -1)), copy=0) set_order(new_var, allowed_cdms_order) else: del var var = new_var data_cdms_order = get_order(var) break # No error so it worked and we leave except: continue else: raise VACUMMError('Wrong type of axes. Possible forms are: %s'%', '.join(allowed)) if not getorder: return var if d!=-1: data_cdms_order = cdms2.orderparse(data_cdms_order) data_cdms_order.insert(d, 'd') data_cdms_order = ''.join(data_cdms_order) return var, data_cdms_order, reordered
def generic1d(data, weights, axis=0, mask='same', copy=True, cyclic=False): """Generic 1D filter applied to :mod:`MV2` variables using convolution. :Params: - **data**: Atleast 1D :mod:`MV2` variable. - **weights**: integer, 1D weights. They are expected to be symmetric and of odd sizes. - **axis**, optional: axis on which to operate - **mask**, optional: mode of masking. The mask is also filtered, and its value helps defining the new mask, depending on this parameter: - If a float is provided, data are masked if the mask value is greater than this parameter. - ``"minimal"``: Equivalent to ``1.``. Data is not masked if a valid value was used to compute data. - ``"maximal"``: Equivalent to ``0.``. Data is masked if a invalid value was used to compute data. - ``"same"``: Mask is the same as input mask. - **copy**, optional: Copy variable before filtering it. :Return: - The filtered :mod:`MV2` variable. :Example: >>> generic1d(data, 3.) # running mean using a 3-points block >>> generic1d(data, [1.,2.,1], axis=2) # shapiro filter on third axis :See also: :func:`scipy.signal.convolve2d` """ # Setup # - data data = MV2.asarray(data) assert data.ndim, 'Input data array must be at least 1D' if axis<0: axis += data.ndim if axis!=data.ndim-1: init_order = data.getOrder(ids=1) data = data.reorder('...%i'%axis) datan = data.filled(0.).copy() nx = datan.shape[-1] datan.shape = -1, nx # - weights if isinstance(weights, int): weights = N.ones(weights) else: weights = N.asarray(weights) assert weights.ndim, 'Input weights array must be at least 1D' assert weights.shape[-1] % 2 == 1, 'Shape of weights must be of odd size' ww = (~N.ma.getmaskarray(data)).astype('i') # = good 2D nw = weights.shape[-1] nw2 = weights.shape[-1] / 2 weights.shape = 1, -1 ww.shape = datan.shape if data.mask is not MV2.nomask: one2d = N.ones(datan.shape, 'i') # Cyclic case if cyclic: mode = 'valid' fdatan = N.concatenate((datan[:, -nw2:], datan, datan[:, :nw2]), axis=1) fww = N.concatenate((ww[:, -nw2:], ww, ww[:, :nw2]), axis=1) one1d = N.ones(datan.shape[-1]+nw2*2, 'i') else: mode = 'same' fdatan = datan fww = ww one1d = N.ones(datan.shape[-1], 'i') # Filter kwf = dict(mode=mode) datan = convolve2d(fdatan, weights, **kwf) one2d = N.resize(N.convolve(one1d, weights[0], **kwf), datan.shape) del one1d if data.mask is MV2.nomask: ww = one2d else: ww = convolve2d(fww, weights, **kwf) del fdatan, fww bad = ww==0 ww[bad] = 1 datan[:] = N.where(bad, datan, datan/ww.astype('d')) ww[bad] = 0 # Set if copy: datao = data.clone() else: datao = data datan.shape = data.shape bad.shape = data.shape if data.mask is not MV2.nomask: ww.shape = one2d.shape = data.shape if mask is 'same': bad = data.mask else: if mask == 'minimal': mask = 1. elif mask == 'maximal': mask = 0. else: mask = float(mask) bad |= (ww/one2d)<(1-mask) # threshold datao[:] = N.ma.masked_where(bad, datan, copy=False) del ww, one2d, bad else: datao[:] = datan if axis!=data.ndim-1: init_order = cdms2.order2index(datao.getAxisList(), init_order) return datao.reorder(init_order) return datao
def deriv(data, axis=0, fast=True, fill_value=None, physical=True, lat=None): """Derivative along a given axis - **data**: Data array (converted to MV array if needed) - *axis*: Axis on which the derivative is performed [default: 0] - *fast*: Filled masked array before derivating, so use Numeric which is faster than MA or MV [*WARNING* default: True] - *physical*: Try physical derivative, taking axis units into account [default: True] - *lat*: Latitude for geographical deriviative to convert positions in degrees to meters """ ## print 'deriv2d' data = MV.masked_array(data) # Reordering if needed if axis: init_order = data.getOrder(ids=1) data = data.reorder(str(axis)+'...') data_deriv = data.clone() data_deriv.id += '_deriv%i'%axis # cdms or Numeric variable to work on? if data.mask is MV.nomask: fast = True if fast: if fill_value is None: data_to_use = data.filled() else: data_to_use = data.filled(fill_value) else: data_to_use = data # Derivative data_deriv[1:-1] = data_to_use[2:]-data_to_use[:-2] data_deriv[0] = data_to_use[1]-data_to_use[0] data_deriv[-1] = data_to_use[-2]-data_to_use[-1] # if not fast: # for i in 0,-1: data_deriv[i] = MV.masked # Physical derivative if physical: pos = N.resize(data.getAxis(0)[:],data.shape[::-1]).transpose() if islon(data.getAxis(0)): if lat is None: for i,lataxis in enumerate(data.getAxisList()): if islat(lataxis): sh = list(data.shape) if i != data.ndim-1: olen = sh[-1] sh[-1] = len(lataxis) sh[i] = olen lat = N.swapaxes(N.resize(lataxis[:],sh),i,-1) else: lat = N.resize(lataxis[:],sh) break if islon(data.getAxis(0)) or islat(data.getAxis(0)): pos = deg2m(pos,lat) units = 'm-1' else: units = getattr(data.getAxis(0),'units',None) data_deriv[1:-1] /= (pos[2:]-pos[:-2]) data_deriv[0] /= (pos[1]-pos[0]) data_deriv[-1] /= (pos[-2]-pos[-1]) else: data_deriv[1:-1] = data_deriv[1:-1] * .5 units = None # Mask if fast: mask = MV.getmaskarray(data) dmask = mask.copy() dmask[1:-1] = N.logical_or(mask[2:],mask[:-2]) # for i in 0,-1: dmask[i] = 1 data_deriv[:] = MV.masked_array(data_deriv,mask=dmask) else: for i in 0,-1: data_deriv[i] = MV.masked # Reordering back if axis: init_order = cdms2.order2index(data_deriv.getAxisList(), init_order) data_deriv = data_deriv.reorder(init_order) # Units if units is not None: data_units = getattr(data_deriv,'units',None) if data_units is None: data_deriv.units = units else: data_deriv.units += ' '+units if data_deriv.units == 'm m-1': del data_deriv.units return data_deriv
def __check_weightoptions(x, axisoptions, weightoptions): """ Checks the weights=options to make sure that it is valid. Default: 'weighted' weight options are one of 'weighted', 'unweighted', an array of weights for each dimension or a MaskedVariable of the same shape as the data x. - 'weighted' means use the grid information to generate weights for that dimension or if multiple axes are passed, generate goes ahead and generates the full masked area weights. - 'unweighted' means use equal weights for all the grid points in that axis. - Also an array of weights (of the same shape as the dimension being averaged over or same shape as V) can be passed. """ # # Since the _check_axisoptions was passed, the variable x has already been reordered # to the required order..... check it anyway. # # __DEBUG__ = 0 # if __DEBUG__: print 'Axis options entering __check_weightoptions = ', axisoptions # axisindex = cdms2.order2index(x.getAxisList(), axisoptions) if __DEBUG__: print 'This should be 0,1,2... etc. is it? ', axisindex # axislist = cdms2.orderparse(axisoptions) if __DEBUG__: print 'axislist = ', axislist # if not isinstance(weightoptions, types.ListType): # # We have either 1 axis only or multiple axes and one MV2 of weights # if MV2.isMaskedVariable(weightoptions): # # Weight passed is an MV2. Probably OK # try: # We had required that the weights be of the same rank as the variable to be averaged. weightoptions = weightoptions(order=axisoptions) # # KRISHNA : check this for combinewts!! # ## if x.shape != weightoptions.shape: ## raise AveragerError, \ ## 'Error: Shape of weight array does not match shape of data array' ## # end of if x.shape != weightoptions.shape: if __DEBUG__: print '... definitely OK' except: raise AveragerError, \ 'Error: Did not find the axes requested in the Masked variable.' elif len(axislist) == 1: # # Only one axis to reduce over....' # figure out if it is an array (numpy or numpy.ma) or 'equal' or 'generate' or something else # if __DEBUG__: print 'I have only 1 axis and 1 option' weightoptions = __check_each_weight_option(x, axislist[0], axisindex[0], weightoptions) else: # # More than 1 axis to reduce over, and not an MV2 of the same dimensionality as x! # if weightoptions in ['generate','weighted']: weightoptions = area_weights(x,axisoptions) else: # Cannot do this because 'generate' was not passed with multiple axes. raise AveragerError, 'Error: Multiple axes passed without weights to match' # end of if weightoptions == 'generate': else: # # I have a list of weightoptions i.e more than 1 axis in axisoption # # # We have multiple axes to deal with each with a weight.... # for i in range(len(axislist)): weightoptions[i] = __check_each_weight_option(x, axislist[i], axisindex[i], weightoptions[i]) # end of for i in range(len(axislist)): # if len(axislist) < len(weightoptions): for j in range(len(axislist), len(weightoptions), 1): weightoptions[j] = __check_each_weight_option(x, None, j, weightoptions[j]) # end of if len(axislist) < len(weightoptions): # end of if not isinstance(weightoptions, types.ListType): # if __DEBUG__: print 'Successful with __check_weightoptions' # return weightoptions
def __check_weightoptions(x, axisoptions, weightoptions): """ Checks the weights=options to make sure that it is valid. Default: 'weighted' weight options are one of 'weighted', 'unweighted', an array of weights for each dimension or a MaskedVariable of the same shape as the data x. - 'weighted' means use the grid information to generate weights for that dimension or if multiple axes are passed, generate goes ahead and generates the full masked area weights. - 'unweighted' means use equal weights for all the grid points in that axis. - Also an array of weights (of the same shape as the dimension being averaged over or same shape as V) can be passed. """ # # Since the _check_axisoptions was passed, the variable x has already been reordered # to the required order..... check it anyway. # # __DEBUG__ = 0 # if __DEBUG__: print 'Axis options entering __check_weightoptions = ', axisoptions # axisindex = cdms2.order2index(x.getAxisList(), axisoptions) if __DEBUG__: print 'This should be 0,1,2... etc. is it? ', axisindex # axislist = cdms2.orderparse(axisoptions) if __DEBUG__: print 'axislist = ', axislist # if not isinstance(weightoptions, types.ListType): # # We have either 1 axis only or multiple axes and one MV2 of weights # if MV2.isMaskedVariable(weightoptions): # # Weight passed is an MV2. Probably OK # try: # We had required that the weights be of the same rank as the variable to be averaged. weightoptions = weightoptions(order=axisoptions) # # KRISHNA : check this for combinewts!! # ## if x.shape != weightoptions.shape: ## raise AveragerError, \ ## 'Error: Shape of weight array does not match shape of data array' ## # end of if x.shape != weightoptions.shape: if __DEBUG__: print '... definitely OK' except: raise AveragerError, \ 'Error: Did not find the axes requested in the Masked variable.' elif len(axislist) == 1: # # Only one axis to reduce over....' # figure out if it is an array (numpy or numpy.ma) or 'equal' or 'generate' or something else # if __DEBUG__: print 'I have only 1 axis and 1 option' weightoptions = __check_each_weight_option(x, axislist[0], axisindex[0], weightoptions) else: # # More than 1 axis to reduce over, and not an MV2 of the same dimensionality as x! # if weightoptions in ['generate', 'weighted']: weightoptions = area_weights(x, axisoptions) else: # Cannot do this because 'generate' was not passed with multiple axes. raise AveragerError, 'Error: Multiple axes passed without weights to match' # end of if weightoptions == 'generate': else: # # I have a list of weightoptions i.e more than 1 axis in axisoption # # # We have multiple axes to deal with each with a weight.... # for i in range(len(axislist)): weightoptions[i] = __check_each_weight_option( x, axislist[i], axisindex[i], weightoptions[i]) # end of for i in range(len(axislist)): # if len(axislist) < len(weightoptions): for j in range(len(axislist), len(weightoptions), 1): weightoptions[j] = __check_each_weight_option( x, None, j, weightoptions[j]) # end of if len(axislist) < len(weightoptions): # end of if not isinstance(weightoptions, types.ListType): # if __DEBUG__: print 'Successful with __check_weightoptions' # return weightoptions
def generic1d(data, weights, axis=0, mask='same', copy=True, cyclic=False): """Generic 1D filter applied to :mod:`MV2` variables using convolution. :Params: - **data**: Atleast 1D :mod:`MV2` variable. - **weights**: integer, 1D weights. They are expected to be symmetric and of odd sizes. - **axis**, optional: axis on which to operate - **mask**, optional: mode of masking. The mask is also filtered, and its value helps defining the new mask, depending on this parameter: - If a float is provided, data are masked if the mask value is greater than this parameter. - ``"minimal"``: Equivalent to ``1.``. Data is not masked if a valid value was used to compute data. - ``"maximal"``: Equivalent to ``0.``. Data is masked if a invalid value was used to compute data. - ``"same"``: Mask is the same as input mask. - **copy**, optional: Copy variable before filtering it. :Return: - The filtered :mod:`MV2` variable. :Example: >>> generic1d(data, 3.) # running mean using a 3-points block >>> generic1d(data, [1.,2.,1], axis=2) # shapiro filter on third axis :See also: :func:`scipy.signal.convolve2d` """ # Setup # - data data = MV2.asarray(data) assert data.ndim, 'Input data array must be at least 1D' if axis < 0: axis += data.ndim if axis != data.ndim - 1: init_order = data.getOrder(ids=1) data = data.reorder('...%i' % axis) datan = data.filled(0.).copy() nx = datan.shape[-1] datan.shape = -1, nx # - weights if isinstance(weights, int): weights = N.ones(weights) else: weights = N.asarray(weights) assert weights.ndim, 'Input weights array must be at least 1D' assert weights.shape[-1] % 2 == 1, 'Shape of weights must be of odd size' ww = (~N.ma.getmaskarray(data)).astype('i') # = good 2D nw = weights.shape[-1] nw2 = weights.shape[-1] / 2 weights.shape = 1, -1 ww.shape = datan.shape if data.mask is not MV2.nomask: one2d = N.ones(datan.shape, 'i') # Cyclic case if cyclic: mode = 'valid' fdatan = N.concatenate((datan[:, -nw2:], datan, datan[:, :nw2]), axis=1) fww = N.concatenate((ww[:, -nw2:], ww, ww[:, :nw2]), axis=1) one1d = N.ones(datan.shape[-1] + nw2 * 2, 'i') else: mode = 'same' fdatan = datan fww = ww one1d = N.ones(datan.shape[-1], 'i') # Filter kwf = dict(mode=mode) datan = convolve2d(fdatan, weights, **kwf) one2d = N.resize(N.convolve(one1d, weights[0], **kwf), datan.shape) del one1d if data.mask is MV2.nomask: ww = one2d else: ww = convolve2d(fww, weights, **kwf) del fdatan, fww bad = ww == 0 ww[bad] = 1 datan[:] = N.where(bad, datan, datan / ww.astype('d')) ww[bad] = 0 # Set if copy: datao = data.clone() else: datao = data datan.shape = data.shape bad.shape = data.shape if data.mask is not MV2.nomask: ww.shape = one2d.shape = data.shape if mask is 'same': bad = data.mask else: if mask == 'minimal': mask = 1. elif mask == 'maximal': mask = 0. else: mask = float(mask) bad |= (ww / one2d) < (1 - mask) # threshold datao[:] = N.ma.masked_where(bad, datan, copy=False) del ww, one2d, bad else: datao[:] = datan if axis != data.ndim - 1: init_order = cdms2.order2index(datao.getAxisList(), init_order) return datao.reorder(init_order) return datao
def deriv(data, axis=0, fast=True, fill_value=None, physical=True, lat=None): """Derivative along a given axis - **data**: Data array (converted to MV array if needed) - *axis*: Axis on which the derivative is performed [default: 0] - *fast*: Filled masked array before derivating, so use Numeric which is faster than MA or MV [*WARNING* default: True] - *physical*: Try physical derivative, taking axis units into account [default: True] - *lat*: Latitude for geographical deriviative to convert positions in degrees to meters """ ## print 'deriv2d' data = MV.masked_array(data) # Reordering if needed if axis: init_order = data.getOrder(ids=1) data = data.reorder(str(axis) + '...') data_deriv = data.clone() data_deriv.id += '_deriv%i' % axis # cdms or Numeric variable to work on? if data.mask is MV.nomask: fast = True if fast: if fill_value is None: data_to_use = data.filled() else: data_to_use = data.filled(fill_value) else: data_to_use = data # Derivative data_deriv[1:-1] = data_to_use[2:] - data_to_use[:-2] data_deriv[0] = data_to_use[1] - data_to_use[0] data_deriv[-1] = data_to_use[-2] - data_to_use[-1] # if not fast: # for i in 0,-1: data_deriv[i] = MV.masked # Physical derivative if physical: pos = N.resize(data.getAxis(0)[:], data.shape[::-1]).transpose() if islon(data.getAxis(0)): if lat is None: for i, lataxis in enumerate(data.getAxisList()): if islat(lataxis): sh = list(data.shape) if i != data.ndim - 1: olen = sh[-1] sh[-1] = len(lataxis) sh[i] = olen lat = N.swapaxes(N.resize(lataxis[:], sh), i, -1) else: lat = N.resize(lataxis[:], sh) break if islon(data.getAxis(0)) or islat(data.getAxis(0)): pos = deg2m(pos, lat) units = 'm-1' else: units = getattr(data.getAxis(0), 'units', None) data_deriv[1:-1] /= (pos[2:] - pos[:-2]) data_deriv[0] /= (pos[1] - pos[0]) data_deriv[-1] /= (pos[-2] - pos[-1]) else: data_deriv[1:-1] = data_deriv[1:-1] * .5 units = None # Mask if fast: mask = MV.getmaskarray(data) dmask = mask.copy() dmask[1:-1] = N.logical_or(mask[2:], mask[:-2]) # for i in 0,-1: dmask[i] = 1 data_deriv[:] = MV.masked_array(data_deriv, mask=dmask) else: for i in 0, -1: data_deriv[i] = MV.masked # Reordering back if axis: init_order = cdms2.order2index(data_deriv.getAxisList(), init_order) data_deriv = data_deriv.reorder(init_order) # Units if units is not None: data_units = getattr(data_deriv, 'units', None) if data_units is None: data_deriv.units = units else: data_deriv.units += ' ' + units if data_deriv.units == 'm m-1': del data_deriv.units return data_deriv
def check_order(var, allowed, vertical=None, copy=False, reorder=False, extended=None, getorder=False): """Check that the axis order of a variable is matches at least one the specifed valid orders :Params: - **var**: MV2 array. - **allowed**: A single order string or a list. It should contain one or several of these letters: - ``x``: longitudes, - ``y``: latitudes, - ``z``: vertical levels, - ``t``: time axis, - ``d``: data values (ignored), - ``-``: any kind of axis. :Return: ``var``, or ``var, order, reordered`` if **reorder** is True. """ # Check allowed orders # - consistency if not isinstance(allowed, (list, tuple)): allowed = [allowed] else: allowed = list(allowed) withd = 'd' in allowed[0] get_rank = lambda o: len(o.replace('d', '')) rank = get_rank(allowed[0]) for order in allowed: try: cdms2.orderparse(order.lower().replace('d', '')) except: raise VACUMMError("Wrong allowed order: " + order) if ('d' in order and not withd) or ('d' not in order and withd): raise VACUMMError( "'d' only partially present in allowed order: %s" % allowed) if get_rank(order) != rank: raise VACUMMError("Inconsistent ranks between allowed orders: %s" % [get_rank(o) for o in allowed]) # - check extended mode if extended is None: # extended? re_upper = re.compile('[XYZT]').search for order in allowed: if re_upper(order) is not None: extended = True # force extended mode break else: extended = False if extended is False: # lower allowed = [order.lower() for order in allowed] else: #add tolerance for lower case orders re_sub = re.compile('[xyzt]').sub allowed = allowed + [re_sub('-', order) for order in allowed] # - unique and lower case _, idx = N.unique(allowed, return_index=True) idx = N.sort(idx) allowed = N.array(allowed)[idx].tolist() # - restrict to vertical or horizontal (1D data only) if vertical is not None and len(allowed[0]) == 2: allowed = [oo for oo in allowed if oo[int(vertical)] == 'd'] # Data order data_cdms_order = get_order(var) # Loop on allowed orders from vacumm.misc.grid.misc import get_axis, var2d reordered = False for allowed_order in allowed: # Handle data case d = allowed_order.find('d') if d != -1: allowed_order = allowed_order.replace('d', '') # pure axis # Check cdms order allowed_cdms_order = allowed_order.lower() # lower case if order_match(data_cdms_order, allowed_cdms_order, strict='right'): break # It is already good # Try to reorder if reorder: try: reordered = cdms2.order2index(var.getAxisList(), allowed_cdms_order) new_var = var.reorder(allowed_cdms_order) if allowed_cdms_order[-1] == 'x' and len( get_axis(new_var, -1).shape) == 2: # 2D axes del var var = var2d(new_var, MV2.transpose(get_axis(new_var, 0)), MV2.transpose(get_axis(new_var, -1)), copy=0) set_order(new_var, allowed_cdms_order) else: del var var = new_var data_cdms_order = get_order(var) break # No error so it worked and we leave except: continue else: raise VACUMMError('Wrong type of axes. Possible forms are: %s' % ', '.join(allowed)) if not getorder: return var if d != -1: data_cdms_order = cdms2.orderparse(data_cdms_order) data_cdms_order.insert(d, 'd') data_cdms_order = ''.join(data_cdms_order) return var, data_cdms_order, reordered