Source code for cf.domain

from itertools import izip, izip_longest, chain
from operator  import itemgetter
from re        import search as re_search

from .coordinatereference import CoordinateReference
from .functions           import RTOL, ATOL, equals, allclose
from .functions           import inspect as cf_inspect
from .query               import Query, gt, le
from .units               import Units


# ====================================================================
#
# Domain object
#
# ====================================================================

[docs]class Domain(object): '''Completely describe a field's coordinate system (domain). It contains the domain axis constructs, dimension coordinate constructs, auxiliary coordinate constructs, cell measure constructs and coordinate reference constructs defined by the CF data model. ''' def __init__(self, axes=None, dim=None, aux=None, measure=None, ref=None, copy=True, assign_axes=None, **kwargs): '''**Initialization** :Parameters: axes : *optional* Initialize axes of the domain. The *axes* parameter may be one of: * `None`. This is the default and axes are inferred from the dimension coordinate specified with the *dim* parameter. .. * (A sequence of) `int`. For each integer, an axis with that size is inserted into the domain with domain identifiers ``'dim0'``, ``'dim1'``, etc. for the first to the last axes in the sequence. If *axes* is an integer (rather than a sequence) then it is treated as a single element sequence. *Example:* To insert an axis of size 12 with domain identifier ``'dim0'``: ``axes=12``. *Example:* To insert axes of sizes 73 and 96 with domain identifiers ``'dim0'`` and ``'dim1'`` respectively: ``axes=[73, 96]``. .. * A dictionary which maps axis domain identifiers to axis sizes. *Example:* To insert an axis of size 12 with domain identifier ``'dim4'``: ``axes={'dim4': 12}``. *Example:* To insert axes of sizes 73 and 96 with domain identifiers ``'dim3'`` and ``'dim4'`` respectively: ``axes={'dim3': 73, 'dim4': 96}``. Note that axis initialization occurs before the initialization of dimension coordiante, auxiliary coordinate and cell measure and coordinate reference objects. dim : *optional* Initialize dimension coordinate objects of the domain. Inserting a dimension coordinate object into the domain will automatically create a domain axis of the correct size, unless an axis with the same domain identifier has already been created with the *axes* parameter. So, in general, it is not necessary to initialize the axes spanned by dimension coordinates with the *axes* parameter. The *dim* parameter may be one of: * `None`. This is the default and no dimension coordinate objects are inserted into the domain. .. * (A sequence of) `cf.DimensionCoordinate`. Each dimension coordinate object is inserted into the domain with domain identifiers ``'dim0'``, ``'dim1'``, etc. for the first to the last dimension coordinate object in the sequence. If *dim* is a `cf.DimensionCoordinate` (rather than a sequence) then it is treated as a single element sequence. *Example:* To insert a dimension coordinate, ``x``, with domain identifier ``'dim0'``: ``dim=x``. *Example:* To insert dimension coordinates, ``x`` and ``y``, with domain identifiers ``'dim0'`` and ``'dim1'`` respectively: ``dim=[x, y]``. .. * A dictionary which maps dimension coordinate domain identifiers to `cf.DimensionCoordinate` objects. *Example:* To insert a dimension coordinate, ``t``, with domain identifier ``'dim4'``: ``dim={'dim4': t}``. *Example:* To insert dimension coordinates, ``x`` and ``y``, with domain identifiers ``'dim3'`` and ``'dim4'`` respectively: ``dim={'dim3': x, 'dim4': y}``. Note that dimension coordinate initialization occurs after axis initialization and before the initialization of auxiliary coordinate and cell measure and coordinate reference objects. aux : *optional* Initialize auxiliary coordinate objects of the domain. The axes spanned by an auxiliary coordinate **must** be defined by the *axes* and/or *dim* parameters. If there is no ambiguity (as will be the case if all of the axes have different sizes) then it is not necessary to describe which axes an auxiliary coordinate spans, and in which order. Otherwise, or in any case, the auxiliary coordinate axes may be specified with the *assign_axes* parameter. The *aux* parameter may be one of: * `None`. This is the default and no auxiliary coordinate objects are inserted into the domain. .. * (A sequence of) `cf.AuxiliaryCoordinate`. Each auxiliary coordinate object is inserted into the domain with domain identifiers ``'aux0'``, ``'aux1'``, etc. for the first to the last auxiliary coordinate object in the sequence. If *aux* is a `cf.AuxiliaryCoordinate` (rather than a sequence) then it is treated as a single element sequence. *Example:* To insert an auxiliary coordinate, ``p``, with domain identifier ``'aux0'``: ``aux=p``. *Example:* To insert auxiliary coordinates, ``p`` and ``q``, with domain identifiers ``'aux0'`` and ``'aux1'`` respectively: ``aux=[p, q]``. .. * A dictionary which maps auxiliary coordinate domain identifiers to `cf.AuxiliaryCoordinate` objects. *Example:* To insert an auxiliary coordinate, ``r``, with domain identifier ``'aux4'``: ``aux={'aux4': r}``. *Example:* To insert auxiliary coordinates, ``p`` and ``q``, with domain identifiers ``'aux3'`` and ``'aux4'`` respectively: ``aux={'aux3': p, 'aux4': q}``. Note that auxiliary coordinate initialization occurs after axis and dimension coordinate initialization. measure : *optional* Initialize cell measure objects of the domain. The axes spanned by a cell measure object **must** be defined by the *axes* and/or *dim* parameters. If there is no ambiguity (as will be the case if all of the axes have different sizes) then it is not necessary to describe which axes a cell measure spans, and in which order. Otherwise, or in any case, the cell measure axes may be specified with the *assign_axes* parameter. The *measure* parameter may be one of: * `None`. This is the default and no cell measure objects are inserted into the domain. .. * (A sequence of) `cf.CellMeasure`. Each cell measure object is inserted into the domain with domain identifiers ``'msr0'``, ``'msr1'``, etc. for the first to the last cell measure object in the sequence. If *msr* is a `cf.CellMeasure` (rather than a sequence) then it is treated as a single element sequence. *Example:* To insert a cell measure, ``m``, with domain identifier ``'msr0'``: ``msr=m``. *Example:* To insert cell measures, ``m`` and ``n``, with domain identifiers ``'msr0'`` and ``'msr1'`` respectively: ``msr=[m, n]``. .. * A dictionary which maps cell measure domain identifiers to `cf.CellMeasure` objects. *Example:* To insert a cell measure, ``m``, with domain identifier ``'msr4'``: ``msr={'msr4': m}``. *Example:* To insert cell measures, ``m`` and ``n``, with domain identifiers ``'msr3'`` and ``'msr4'`` respectively: ``msr={'msr3': m, 'msr4': n}``. Note that cell measure initialization occurs after axis, dimension coordinate and auxiliary coordinate initialization. assign_axes, kwargs : dict, optional Map coordinate and cell measure objects to the axes which they span. Each dictionary key is a domain identifier of a dimension coordinate, auxiliary coordinate or cell measure object which has been previously defined by the *dim*, *aux* or *measure* parameters. Its corresponding value specifies the axes that the item spans, in the correct order. The axes are those returned by this call of the domain's `axes` method: ``d.axes(value, order=True, **kwargs)`` (see `cf.Field.axes` for details). For each dimension coordinate, auxiliary coordinate or cell measure object, if there is no ambiguity as to which axes it spans (as will be the case if all of the axes have different sizes) then it is not necessary provide this item to the *assign_axes* dictionary, as the spanning axes may be deduced automatically. Otherwise it is required. *Example:* Auxiliary coordinate ``'aux0'`` spans axis ``'dim0'`` and auxiliary coordinate ``'aux1'`` spans axes ``'dim2'`` and ``dim1'``, in that order: ``assign_axes={'aux0': 'dim0', 'aux1': ['dim2', `dim1`]}``. *Example:* Auxiliary coordinate ``'aux0'`` spans axis the Z axis cell measure ``'msr1'`` spans the Y and X axes, in that order: ``assign_axes={'aux0': 'Z', 'msr1': ['Y', 'X']}``. In this case it is assumed that the axes have dimension coordinates with sufficient metadta to be able to define them as Z, Y and X axes. ref : *optional* Initialize coordinate reference objects of the domain. The *ref* parameter may be one of: * `None`. This is the default and no coordinate reference objects are inserted into the domain. .. * (A sequence of) `cf.CoordinateReference`. Each coordinate reference object is inserted into the domain with domain identifiers ``'ref0'``, ``'ref1'``, etc. for the first to the last coordinate reference object in the sequence. If *ref* is a `cf.CoordinateReference` (rather than a sequence) then it is treated as a single element sequence. *Example:* To insert a coordinate reference, ``b``, with domain identifier ``'ref0'``: ``ref=b``. *Example:* To insert coordinate references, ``b`` and ``c``, with domain identifiers ``'ref0'`` and ``'ref1'`` respectively: ``ref=[b, c]``. .. * A dictionary which maps coordinate reference domain identifiers to `cf.CoordinateReference` objects. *Example:* To insert a coordinate reference, ``m``, with domain identifier ``'ref4'``: ``ref={'ref4': m}``. *Example:* To insert coordinate references, ``b`` and ``c``, with domain identifiers ``'ref3'`` and ``'ref4'`` respectively: ``ref={'ref3': b, 'ref4': c}``. Note that coordinate reference initialization occurs after axis, dimension coordinate, auxiliary coordinate and cell measure initialization. copy : bool, optional If True (the default) then all dimension coordinate, auxiliary coordinate, cell measure and coordinate reference objects are copied prior to insertion. :Examples: In this example, four dots (``....``) refers to appropriate initialization parameters of the coordinate, cell measure and coordinate reference constructs, which are omitted here for clarity. >>> dim_coord_A = cf.DimensionCoordinate(....) >>> dim_coord_B = cf.DimensionCoordinate(....) >>> dim_coord_A.size, dim_coord_B.size (73, 96) >>> dim_coord_A.X, dim_coord_B.Y (True, True) >>> aux_coord_A = cf.AuxiliaryCoordinate(....) >>> aux_coord_A.shape (96, 73) >>> cell_measure_A = cf.CellMeasure(....) >>> cell_measure_A.shape (73, 96) >>> ref_A = cf.CoordinateReference(name='latitude_longitude', ....) >>> d = cf.Domain(dim=[dim_coord_A, dim_coord_B], ... aux=aux_coord_A, ... measure=cell_measure_A, ... ref=ref_A) ... >>> d.items_axes() {'aux0': ['dim1', 'dim0'], 'msr0': ['dim0', 'dim1'], 'dim1': ['dim1'], 'dim0': ['dim0']} >>> d.refs {'ref0' : <CF CoordinateReference: latitude_longitude>} It was not necessary to specify the axis mappings for ``aux_coord_A`` and ``cell_measure_A`` because the two axes have unambiguous sizes. The same domain could have been initialised using the dictionary form of the parameters and explicitly assigning axes described by their dimension coordinate metadata: >>> e = cf.Domain(axes={'dim0': 73, 'dim1': 96}, ... dim={'dim0': dim_coord_A, 'dim1': dim_coord_B}, ... aux={'aux0': aux_coord_A}, ... measure={'msr0': cell_measureA}, ... ref={'ref0': ref_A}, ... assign_axes={'aux0': ['Y', 'X'], ... 'msr0': ['X', 'Y']}) ... >>> e.equals(d) True ''' self.d = {} self.a = {} self.m = {} self.r = {} self._map = {} self._axes = {} self._axes_sizes = {} # ------------------------------------------------------------ # Initialize axes # ------------------------------------------------------------ if axes or axes == 0: if isinstance(axes, dict): for key, size in axes.iteritems(): self.insert_axis(size, key=key, replace=False) else: if isinstance(axes, (int, long)): axes = (axes,) for size in axes: self.insert_axis(size) #--- End: if # ------------------------------------------------------------ # Initialize dimension coordinates # ------------------------------------------------------------ if dim: if isinstance(dim, dict): for key, coord in dim.iteritems(): self.insert_dim(coord, key=key, copy=copy, replace=False) else: for coord in dim: key = self.new_axis_identifier() self.insert_dim(coord, key=key, copy=copy, replace=False) #--- End: if # ------------------------------------------------------------ # Assign axes to auxiliary coordinates and cell measures # ------------------------------------------------------------ if assign_axes: for key, value in assign_axes.iteritems(): self._axes[key] = self.axes(value, ordered=True) #--- End: if # ------------------------------------------------------------ # Initialize auxiliary coordinates # ------------------------------------------------------------ if aux: if isinstance(aux, dict): for key, coord in aux.iteritems(): aux_axes = self._axes.get(key, None) self.insert_aux(coord, key=key, axes=aux_axes, copy=copy) else: for coord in aux: key = self.new_aux_identifier() aux_axes = self._axes.get(key, None) self.insert_aux(coord, key=key, axes=aux_axes, copy=copy) #--- End: if # ------------------------------------------------------------ # Initialize cell measures # ------------------------------------------------------------ if measure: if isinstance(measure, dict): for key, msr in measure.iteritems(): msr_axes = self._axes.get(key, None) self.insert_measure(msr, key=key, axes=msr_axes, copy=copy) else: for msr in measure: key = self.new_measure_identifier() msr_axes = self._axes.get(key, None) self.insert_measure(msr, key=key, axes=msr_axes, copy=copy) #--- End: if # ------------------------------------------------------------ # Initialize coordinate references # ------------------------------------------------------------ if ref: if isinstance(ref, CoordinateReference): self.insert_ref(ref, copy=copy) elif isinstance(ref, dict): for key, coordref in ref.iteritems(): self.insert_ref(coordref, key=key, copy=copy) else: for N, coordref in enumerate(ref): self.insert_ref(coordref, copy=copy) #--- End: if #--- End: def def __repr__(self): ''' x.__repr__() <==> repr(x) ''' data_axes = self._axes.get('data', ()) w = sorted(["{0}({1})".format(self.axis_name(axis), size) for axis, size in self._axes_sizes.iteritems() if axis not in data_axes]) x = ["{0}({1})".format(self.axis_name(axis), self._axes_sizes[axis]) for axis in data_axes] axes = ', '.join(w+x) return '<CF {0}: {1}>'.format(self.__class__.__name__, axes) #--- End: def def __str__(self): ''' x.__str__() <==> str(x) ''' mmm = {} def _print_coord(domain, key, variable, dimension_coord): '''Private function called by __str__''' if dimension_coord: name = "%s(%d)" % (domain.axis_name(key), domain._axes_sizes[key]) mmm[key] = name if key not in domain.d: return name else: variable = domain.d[key] x = [name] #--- End: if # Still here? if not dimension_coord: # Auxiliary coordinate shape = [mmm[dim] for dim in domain._axes[key]] shape = str(tuple(shape)).replace("'", "") shape = shape.replace(',)', ')') x = [variable.name('domain%'+key)] x.append(shape) #--- End: if try: variable.compress except AttributeError: if variable._hasData: x.append(' = ') x.append(str(variable.Data)) else: x.append(' -> compressed ') compressed = [] for unc in domain[key].compress: shape = str(unc.size) compressed.append(unc.name('unc')+'('+shape+')') x.append(', '.join(compressed)) return ''.join(x) #--- End: def string = [] x = [_print_coord(self, dim[0], None, True) for dim in sorted(self._axes_sizes.iteritems(), key=itemgetter(1))] if x: string.append('Axes : ') string.append('\n : '.join(x)) x = [_print_coord(self, aux, v, False) for aux, v in sorted(self.a.items())] if x: string.append('\n') string.append('Aux coords : ') string.append('\n : '.join(x)) # Cell measures x = [_print_coord(self, msr, v, False) for msr, v in sorted(self.m.items())] if x: string.append('\n') string.append('Cell measures : ') string.append('\n : '.join(x)) # Coordinate references x = [repr(ref) for ref in self.r.values()] if x: string.append('\n') string.append('Coord refs : ') string.append('\n : '.join(x)) #--- End: if return ''.join(string) #--- End: def def _conform_ref(self, ref): '''Replace the content of ref.coords with domain coordinate identifiers, where possible. :Parameters: ref : cf.CoordinateReference :Returns: None :Examples: >>> d._conform_ref(r) ''' coord_map = {} role = ('d', 'a') for identifier in ref.coords: key = self.item(identifier, role=role, exact=True, key=True) if key is not None: coord_map[identifier] = key # else: # coord_map[identifier] = ref._standard_coords.get(identifier, identifier) #--- End: for ref.change_coord_identities(coord_map, i=True) #--- End: def def _replace_refs_coord_identifier(self, key): ''' Replace a coordinate domain key with a coordinate identity in all coordinate references. If the coordinate object has no identity then the coordinate object is effectively removed. :Parameters: key : str A domain coordinate object identifier. :Returns: None :Examples: >>> d._replace_refs_coord_identifier('dim1') >>> d._replace_refs_coord_identifier('aux0') ''' coord_map = {key: self.get(key).identity(None)} for ref in self.r.itervalues(): ref.change_coord_identities(coord_map, i=True) #--- End: def def _set(self, key, value): ''' Set the item of a pre-existing identifier of the domain. An item is either a dimension coordinate, an auxiliary coordinate, a cell measure or a coordinate reference object. .. note:: Consistency is NOT checked. .. seealso:: `get`, `has` :Parameters: key : str A domain identifier. value : The new item corresponding to the domain identifier given by *key*. :Returns: None :Examples: >>> d.items().keys() ['dim0', 'aux0', 'aux1', 'ref0'] >>> d._set('aux1', cf.AuxiliaryCoordinate(....)) >>> d._set('ref0', cf.CoordinateReference(....)) ''' getattr(self, self._map[key])[key] = value #--- End: def def _equal_refs(self, t, u, domain, rtol=None, atol=None, pointer_map={}, ignore_fill_value=False, traceback=False): ''' :Parameters: t : cf.CoordinateReference u : cf.CoordinateReference domain : cf.Domain The domain which contains *u*. pointer_map : dict ignore_fill_value : bool, optional If True then data arrays with different fill values are considered equal. By default they are considered unequal. traceback : bool, optional If True then print a traceback highlighting where the two instances differ. :Returns: out : bool :Examples: >>> ''' if rtol is None: rtol = RTOL() if atol is None: atol = ATOL() if not t.equals(u, rtol=rtol, atol=atol, ignore_fill_value=ignore_fill_value, traceback=traceback): if traceback: print( "%s: Unequal coordinate references (%r != %r)" % (self.__class__.__name__, t, u)) return False # If coordinate references have coordinate-valued terms - # check them for term in t.coord_terms: if u[term] in pointer_map: if t[term] == pointer_map[u[term]]: continue elif u[term] == t[term]: continue # Still here? if traceback: print( "%s: Unequal %s coordinate references %r term" % (self.__class__.__name__, t.name, term)) return False #--- End: for return True #--- End: def @property def rank(self): ''' The number of axes in the domain. :Examples: >>> len(d.axes()) 4 >>> d.rank 4 ''' return len(self._axes_sizes) #--- End: def
[docs] def equivalent(self, other, rtol=None, atol=None, traceback=False): ''' True if and only if two domains are logically equivalent. :Parameters: other : The object to compare for equivalence. atol : float, optional The absolute tolerance for all numerical comparisons, By default the value returned by the `ATOL` function is used. rtol : float, optional The relative tolerance for all numerical comparisons, By default the value returned by the `RTOL` function is used. traceback : bool, optional If True then print a traceback highlighting where the two objects differ. :Returns: out : bool Whether or not the two objects are equivalent. ''' if sorted(self._axes_sizes.values()) != sorted(self._axes_sizes.values()): if traceback: print("{0}: Different axis sizes: {1} != {2}".format( self.__class__.__name__, sorted(self._axes_sizes.values()), sorted(other._axes_sizes.values()))) return False s = self.analyse() t = other.analyse() if sorted(s['id_to_coord']) != sorted(t['id_to_coord']): if traceback: print( "{0}: Different axis identities: {1} != {2}".format( self.__class__.__name__, sorted(s['id_to_coord']), sorted(t['id_to_coord']))) return False for identity, coord0 in s['id_to_coord'].iteritems(): coord1 = t['id_to_coord'][identity] if not coord0._equivalent_data(coord1, rtol=rtol, atol=atol): if traceback: print( "{0}: Non-equivalent 1-d coordinate data array: {1}".format( self.__class__.__name__, identity)) return False #--- End: for keys1 = other.r.keys() for ref0 in self.r.itervalues(): found_match = False for key1 in keys1: ref1 = other_t[key1] if self.equivalent_refs(ref0, ref1, domain=other, traceback=False): found_match = True refs1.remove(key1) break #--- End: for if not found_match: if traceback: print( "{0}: Missing coordinate reference: {1}".format( self.__class__.__name__, ref0)) return False #--- End: for return True
#--- End: def
[docs] def equivalent_refs(self, t, u, domain, atol=None, rtol=None, traceback=False): ''' True if a coordinate refencence object is the same as one in another domain. :Parameters: t : cf.CoordinateReference u : cf.CoordinateReference domain : cf.Domain The domain which contains *u*. traceback : bool, optional If True then print a traceback highlighting where the two instances differ. :Returns: out : bool :Examples: >>> ''' if not t.equivalent(u, rtol=rtol, atol=atol, traceback=traceback): if traceback: print( "%s: Unequivalent coordinate references (%r != %r)" % (self.__class__.__name__, t, u)) return False t_coord_terms = t.coord_terms.copy() u_coord_terms = u.coord_terms.copy() for term in t_coord_terms.intersection(u_coord_terms): # Term is coordiante-valued in both t and u t_coord_terms.remove(term) u_coord_terms.remove(term) tcoord = self.item(t[term], role='da', exact=True) ucoord = domain.item(u[term], role='da', exact=True) if (tcoord is None or ucoord is None or not tcoord._equivalent_data(ucoord, rtol=rtol, atol=atol)): if traceback: print( "%s: Unequal coordinate reference %r term" % (self.__class__.__name__, term)) return False #--- End: for for term in t_coord_terms: # Term is coordiante-valued in t but missing from u coord = self.item(t[term], role='da', exact=True) default = t.default_value(term) if default is None or coord is None or not allclose(coord, default): if traceback: print( "%s: Unequivalent coordinate reference %r term" % (self.__class__.__name__, term)) return False #--- End: for for term in u_coord_terms: # Term is coordiante-valued in u but missing from t coord = self.item(u[term], role='da', exact=True) default = u.default_value(term) if default is None or coord is None or not allclose(coord, default): if traceback: print( "%s: Unequivalent coordinate reference %r term" % (self.__class__.__name__, term)) return False #--- End: for # Still here? return True
#--- End: def def canonical_ref(self, ref): ''' ''' ref = ref.copy() for term, value in ref.iteritems(): data = getattr(value, '__data__', None) if data is None: # Value has no units continue data = data() units = ref.canonical_units(term) if units is None: continue if isinstance(units, basestring): # units is a standard_name of a coordinate coord = self.item(units, role='da', exact=True) if coord is None: continue units = coord.Units #--- End: if if units.equivalent(data.Units): data.Units = units else: raise ValueError("asdddddddddddddd 87236768") #--- End: for return ref #--- End: def
[docs] def analyse(self): ''' Analyse a domain. :Returns: out : dict A desription of the domain. :Examples: >>> print d Axes : time(3) = [1979-05-01 12:00:00, ..., 1979-05-03 12:00:00] gregorian : air_pressure(5) = [850.000061035, ..., 50.0000038147] hPa : grid_longitude(106) = [-20.5400109887, ..., 25.6599887609] degrees : grid_latitude(110) = [23.3200002313, ..., -24.6399995089] degrees Aux coords : latitude(grid_latitude(110), grid_longitude(106)) = [[67.1246607722, ..., 22.8886948065]] degrees_N : longitude(grid_latitude(110), grid_longitude(106)) = [[-45.98136251, ..., 35.2925499052]] degrees_E Coord refs : <CF CoordinateReference: rotated_latitude_longitude> >>> d.analyse() {'aux_coords': {'N-d': {'aux0': <CF AuxiliaryCoordinate: latitude(110, 106) degrees_N>, 'aux1': <CF AuxiliaryCoordinate: longitude(110, 106) degrees_E>}, 'dim0': {'1-d': {}, 'N-d': {}}, 'dim1': {'1-d': {}, 'N-d': {}}, 'dim2': {'1-d': {}, 'N-d': {'aux0': <CF AuxiliaryCoordinate: latitude(110, 106) degrees_N>, 'aux1': <CF AuxiliaryCoordinate: longitude(110, 106) degrees_E>}}, 'dim3': {'1-d': {}, 'N-d': {'aux0': <CF AuxiliaryCoordinate: latitude(110, 106) degrees_N>, 'aux1': <CF AuxiliaryCoordinate: longitude(110, 106) degrees_E>}}}, 'axis_to_coord': {'dim0': <CF DimensionCoordinate: time(3) gregorian>, 'dim1': <CF DimensionCoordinate: air_pressure(5) hPa>, 'dim2': <CF DimensionCoordinate: grid_latitude(110) degrees>, 'dim3': <CF DimensionCoordinate: grid_longitude(106) degrees>}, 'axis_to_id': {'dim0': 'time', 'dim1': 'air_pressure', 'dim2': 'grid_latitude', 'dim3': 'grid_longitude'}, 'cell_measures': {'N-d': {}, 'dim0': {'1-d': {}, 'N-d': {}}, 'dim1': {'1-d': {}, 'N-d': {}}, 'dim2': {'1-d': {}, 'N-d': {}}, 'dim3': {'1-d': {}, 'N-d': {}}}, 'id_to_aux': {}, 'id_to_axis': {'air_pressure': 'dim1', 'grid_latitude': 'dim2', 'grid_longitude': 'dim3', 'time': 'dim0'}, 'id_to_coord': {'air_pressure': <CF DimensionCoordinate: air_pressure(5) hPa>, 'grid_latitude': <CF DimensionCoordinate: grid_latitude(110) degrees>, 'grid_longitude': <CF DimensionCoordinate: grid_longitude(106) degrees>, 'time': <CF DimensionCoordinate: time(3) gregorian>}, 'id_to_key': {'air_pressure': 'dim1', 'grid_latitude': 'dim2', 'grid_longitude': 'dim3', 'time': 'dim0'}, 'undefined_axes': [], 'warnings': [], } ''' a = {} # ------------------------------------------------------------ # Map each axis identity to its domain identifier, if such a # mapping exists. # # For example: # >>> id_to_axis # {'time': 'dim0', 'height': dim1'} # ------------------------------------------------------------ id_to_axis = {} # ------------------------------------------------------------ # For each dimension that is identified by a 1-d auxiliary # coordinate, map its dimension's its domain identifier. # # For example: # >>> id_to_aux # {'region': 'aux0'} # ------------------------------------------------------------ id_to_aux = {} # ------------------------------------------------------------ # # # For example: # >>> id_to_key # {'region': 'aux0'} # ------------------------------------------------------------ id_to_key = {} # ------------------------------------------------------------ # Map each dimension's identity to the coordinate which # provides that identity. # # For example: # >>> id_to_coord # {'time': <CF Coordinate: time(12)>} # ------------------------------------------------------------ id_to_coord = {} axis_to_coord = {} aux_to_coord = {} # ------------------------------------------------------------ # # ------------------------------------------------------------ aux_coords = {} aux_coords['N-d'] = {} cell_measures = {} cell_measures['N-d'] = {} # ------------------------------------------------------------ # List the dimensions which are undefined, in that no unique # identity can be assigned to them. # # For example: # >>> undefined_axes # ['dim2'] # ------------------------------------------------------------ undefined_axes = [] # ------------------------------------------------------------ # # ------------------------------------------------------------ warnings = [] for axis in self._axes_sizes: # Find this axis's 1-d and N-d auxiliary coordinates aux_coords[axis] = {} aux_coords[axis]['1-d'] = {} aux_coords[axis]['N-d'] = {} for aux, coord in self.items(role='a', axes=axis).iteritems(): if coord.ndim > 1: aux_coords['N-d'][aux] = coord aux_coords[axis]['N-d'][aux] = coord else: aux_coords[axis]['1-d'][aux] = coord #--- End: for # Find this axis's 1-d and N-d cell measures cell_measures[axis] = {} cell_measures[axis]['1-d'] = {} cell_measures[axis]['N-d'] = {} for msr, cell_measure in self.items(role='m', axes=axis).iteritems(): if cell_measure.ndim > 1: cell_measures['N-d'][msr] = cell_measure cell_measures[axis]['N-d'][msr] = cell_measure else: cell_measures[axis]['1-d'][msr] = cell_measure #--- End: for if axis in self.d: # This axis of the domain has a dimension coordinate dim_coord = self.d[axis] identity = dim_coord.identity() if identity is None: # Dimension coordinate has no identity, but it may # have a recognised axis. for ctype in ('T', 'X', 'Y', 'Z'): if getattr(dim_coord, ctype): identity = ctype break #--- End: if if identity is not None and dim_coord._hasData: if identity in id_to_axis: warnings.append( "Domain has more than one %r axis" % identity) id_to_axis[identity] = axis id_to_key[identity] = axis id_to_coord[identity] = dim_coord axis_to_coord[axis] = dim_coord continue elif len(aux_coords[axis]['1-d']) == 1: # This axis of the domain does not have a dimension # coordinate but it does have exactly one 1-d # auxiliary coordinate, so that will do. aux = list(aux_coords[axis]['1-d'])[0] aux_coord = self.a[aux] identity = aux_coord.identity() if identity is not None and aux_coord._hasData: if identity in id_to_axis: warnings.append( "Domain has more than one %r axis" % identity) id_to_aux[identity] = aux id_to_key[identity] = aux id_to_axis[identity] = axis id_to_coord[identity] = aux_coord axis_to_coord[axis] = aux_coord continue #--- End: if # Still here? Then this axis is undefined undefined_axes.append(axis) #--- End: for # ------------------------------------------------------------ # Invert the mapping between dimensions and identities # ------------------------------------------------------------ axis_to_id = {} for k, v in id_to_axis.iteritems(): axis_to_id[v] = k return {'aux_coords' : aux_coords, 'axis_to_id' : axis_to_id, 'axis_to_coord' : axis_to_coord, 'cell_measures' : cell_measures, 'id_to_aux' : id_to_aux, 'id_to_coord' : id_to_coord, 'id_to_axis' : id_to_axis, 'id_to_key' : id_to_key, 'undefined_axes': undefined_axes, 'warnings' : warnings, }
#--- End def # def attach_to_ref(self, ref, item, term=None, **kwargs): # ''' # #Attach a coordinate construct to a coordinate reference. # #:Parameters: # # ref : cf.CoordinateReference # # item, kwargs : *optional* # The coordinate to attach, identified as for the `item` # method. # # term : sequence of str, optional # # #:Returns: # # None # #:Examples: # #>>> t #<CF CoordinateReference: asdkjhfskjfhgslghldkjgf> #>>> d.attach_to_ref(t, 'dim2') #>>> d.attach_to_ref(t, 'longitude', term=['b1']) #>>> d.attach_to_ref(t, ['latitude', 'long_name:latitude'], exact=True) # #''' # kwargs['key'] = True # kwargs['role'] = ('d', 'a') # key = self.item(item, **kwargs) # if key is None: # raise ValueError("Can't find %r coordinate object to attach to %r" % # (item, ref)) # # if term: # for t in term: # ref[t] = key # else: # ref.coords.add(key) # # #--- End: def
[docs] def get(self, key, *default): '''Return the item corresponding to an internal identifier. An item is a dimension coordinate, an auxiliary coordinate, a cell measure or a coordinate reference object. .. seealso:: `has`, `item` :Parameters: key : str An internal identifier. default : *optional* Return *default* if and only if the domain does not have the given *key*. :Returns: out : The item of the domain with the given internal identifier. If none exists and *default* is set then *default* is returned. :Examples: >>> d.get('dim0') <CF DimensionCoordinate: atmosphere_hybrid_height_coordinate(1)> >>> d.get('aux1') <CF AuxiliaryCoordinate: latitude(10, 9) degree_N> >>> d.get('msr0') <CF CellMeasure: area(9, 10) km 2> >>> d.get('ref0') <CF CoordinateReference: rotated_latitude_longitude> >>> d.get('bad_id') ValueError: Domain doesn't have internal identifier 'bad_id' >>> print d.get('bad_id', None) None ''' if key in self._map: return getattr(self, self._map[key])[key] elif default: return default[0] else: raise ValueError( "Domain doesn't have internal identifier %r" % key)
#--- End: def
[docs] def has(self, key): ''' True if the domain has the given internal identifier. .. seealso:: `get`, 'item`, `items` :Parameters: key : str An internal identifier. :Returns: out : bool Whether or not the domain has the internal identifier. :Examples: >>> d.items().keys() ['dim0', 'aux0', 'aux1', 'ref0'] >>> d.has('dim0') True >>> d.has('aux2') False ''' return key in self._map
#--- End: def
[docs] def axis_name(self, axes=None, **kwargs): ''' Return the canonical name for an axis. :Parameters: axis, kwargs : optional Select the axis which would be selected by this call of the domain's `~cf.Domain.axis` method: ``d.axis(axis, **kwargs)``. See `cf.Domain.axis` for details. :Returns: out : str The canonical name for the axis. :Examples: >>> d.axis_name('dim0') 'time' >>> d.axis_name('dim1') 'domain%dim1' >>> d.axis_name('dim2') 'ncdim%lat' ''' axis = self.axis(axes, **kwargs) if axis is None: raise ValueError("No unique axis could be identified") dim = self.item(role='d', axes_all=axis) if dim is not None: # Get the name from the dimension coordinate return dim.name('domain%%%s' % axis) aux = self.item(role='a', axes_all=axis) if aux is not None: # Get the name from the unique 1-d auxiliary coordinate return aux.name('domain%%%s' % axis) else: try: # Get the name from netCDF dimension name return 'ncdim%%%s' % self.nc_dimensions[axis] except (KeyError, AttributeError): # Get the name from axis domain identifier return 'domain%%%s' % axis
#--- End: def def axis_identity(self, axis=None, **kwargs): ''' Return the canonical name for an axis. :Parameters: axis, kwargs : optional Select the axis which would be selected by this call of the domain's `~cf.Domain.axis` method: ``d.axis(axis, **kwargs)``. See `cf.Domain.axis` for details. :Returns: out : str The canonical name for the axis. :Examples: ''' axis = self.axis(axis, **kwargs) if axis is None: raise ValueError("No unique axis could be identified") dim = self.item(role='d', axes_all=axis) if dim is not None: # Get the identity from the dimension coordinate return dim.identity() aux = self.item(role='a', axes_all=axis) if aux is not None: # Get the identity from the unique 1-d auxiliary coordinate return aux.identity() return None #--- End: def
[docs] def direction(self, axis): ''' Return True if an axis is increasing, otherwise return False. An axis is considered to be increasing if its dimension coordinate values are increasing in index space or if it has no dimension coordinate. .. seealso:: `directions` :Parameters: axis : str A domain axis identifier, such as ``'dim0'``. :Returns: out : bool Whether or not the axis is increasing. :Examples: >>> d._axes_sizes {'dim0': 3, 'dim1': 1, 'dim2': 2, 'dim3': 2, 'dim4': 99} >>> d.items_axes() {'dim0': ['dim0'], 'dim1': ['dim1'], 'aux0': ['dim0'], 'aux1': ['dim2'], 'aux2': ['dim3'], } >>> d['dim0'].array array([ 0 30 60]) >>> d.direction('dim0') True >>> d['dim1'].array array([15]) >>> d['dim1'].bounds.array array([ 30 0]) >>> d.direction('dim1') False >>> d['aux1'].array array([0, -1]) >>> d.direction('dim2') True >>> d['aux2'].array array(['z' 'a']) >>> d.direction('dim3') True >>> d.direction('dim4') True ''' if axis in self.d: return self.d[axis].direction() return True
#--- End: def
[docs] def directions(self): ''' Return a dictionary mapping axes to their directions. .. seealso:: `direction` :Returns: out : dict A dictionary whose key/value pairs are axis identifiers and their directions. :Examples: >>> d.directions() {'dim1': True, 'dim0': False} ''' self_direction = self.direction directions = {} for axis in self._axes_sizes: directions[axis] = self_direction(axis) return directions
#--- End: def
[docs] def map_axes(self, other): ''' Map the axis identifiers of the domain to their equivalent axis identifiers of another. :Parameters: other : cf.Domain :Returns: out : dict A dictionary whose keys are the axis identifiers of the domain with corresponding values of axis identifiers of the of other domain. :Examples: >>> d.map_axes(e) {'dim0': 'dim1', 'dim1': 'dim0', 'dim2': 'dim2'} ''' s = self.analyse() t = other.analyse() out = {} for identity, dim in s['id_to_axis'].iteritems(): if identity in t['id_to_axis']: out[dim] = t['id_to_axis'][identity] #--- End: for return out
#--- End: def
[docs] def insert_axis(self, size, key=None, replace=True): ''' Insert an axis into the domain in place. This method has exactly the same interface, functionality and outputs as `cf.Field.insert_axis`. Therefore see `cf.Field.insert_axis` for the full documentation details. .. seealso:: `insert_aux`, insert_measure`, insert_dim`, insert_ref` :Examples 1: >>> d.insert_axis(24) :Parameters: size : int The size of the new axis. key : str, optional The domain identifier for the new axis. By default a new, unique identifier is generated. replace : bool, optional If False then do not replace an existing axis with the same identifier but a different size. By default an existing axis with the same identifier is changed to have the new size. :Returns: out : The domain identifier of the new axis. :Examples 2: See `cf.Field.insert_axis`. ''' if key is not None: if (key in self._axes_sizes and not replace and self._axes_sizes[key] != size): raise ValueError( "Can't insert axis: Existing axis %r has different size (got %d, expected %d)" % (key, size, self._axes_sizes[key])) self._axes_sizes[key] = size return key # Still here? Then no identifier was specified for the # dimension, so create a new one. key = self.new_axis_identifier() self._axes_sizes[key] = size return key
#--- End: def
[docs] def new_axis_identifier(self): ''' Return a new, unique axis identifier for the domain. .. seealso:: `new_aux_identifier`, `new_measure_identifier`, `new_dim_identifier`, `new_ref_identifier` The domain is not updated. :Returns: out : str The new identifier. :Examples: >>> d._axes_sizes.keys() ['dim2', 'dim0'] >>> d.new_axis_identifier() 'dim3' >>> d._axes_sizes.keys() [] >>> d.new_axis_identifier() 'dim0' ''' dimensions = self._axes_sizes n = len(dimensions) new_key = 'dim%d' % n while new_key in dimensions: n += 1 new_key = 'dim%d' % n #--- End: while return new_key
#--- End: def
[docs] def new_aux_identifier(self): ''' Return a new, unique auxiliary coordinate identifier for the domain. .. seealso:: `new_measure_identifier`, `new_dimemsion_identifier`, `new_ref_identifier` The domain is not updated. :Returns: out : str The new identifier. :Examples: >>> d.items(role='a').keys() ['aux2', 'aux0'] >>> d.new_aux_identifier() 'aux3' >>> d.items(role='a').keys() [] >>> d.new_aux_identifier() 'aux0' ''' keys = self.a n = len(keys) new_key = 'aux%d' % n while new_key in keys: n += 1 new_key = 'aux%d' % n #--- End: while return new_key
#--- End: def
[docs] def new_measure_identifier(self): ''' Return a new, unique cell measure identifier for the domain. The domain is not updated. .. seealso:: `new_aux_identifier`, `new_axis_identifier`, `new_ref_identifier` :Returns: out : str The new identifier. :Examples: >>> d.items(role='m').keys() ['msr2', 'msr0'] >>> d.new_measure_identifier() 'msr3' >>> d.items(role='m').keys() [] >>> d.new_measure_identifier() 'msr0' ''' keys = self.m n = len(keys) new_key = 'msr%d' % n while new_key in keys: n += 1 new_key = 'msr%d' % n #--- End: while return new_key
#--- End: def
[docs] def new_ref_identifier(self): ''' Return a new, unique coordinate reference identifier for the domain. The domain is not updated. .. seealso:: `new_aux_identifier`, `new_axis_identifier`, `new_measure_identifier` :Returns: out : str The new identifier. :Examples: >>> d.items(role='r').keys() ['ref1'] >>> d.new_ref_identifier() 'ref2' >>> d.items(role='r').keys() [] >>> d.new_ref_identifier() 'ref0' ''' if not self.r: return 'ref0' keys = self.r n = len(keys) new_key = 'ref%d' % n while new_key in keys: n += 1 new_key = 'ref%d' % n #--- End: while return new_key
#--- End: def
[docs] def insert_coord(self, variable, key, copy=True, axes=None): ''' Insert a dimension coordinate or auxiliary coordinate into the domain in place. :Parameters: variable : cf.AuxiliaryCoordinate or cf.DimensionCoordinate or cf.Coordinate The new dimension coordinate or auxiliary coordinate. If required, it will be converted to the appropiate object type (dimension coordinate or auxiliary). key : str The identifier for the new coordinate. The identifier must start with the string ``dim`` or ``aux``, corresponding to whether the *variable* is to be a dimension coordinate or an auxiliary coordinate respectively. axes : list, optional If the *variable* is an auxiliary coordinate then the identities of its dimensions must be provided. Ignored if the *variable* is a dimension coordinate. copy: bool, optional If False then the *variable* is not copied before insertion. By default it is copied. :Returns: None :Examples: >>> ''' if key.startswith('d'): self.insert_dim(variable, key=key, copy=copy) elif key.startswith('a'): self.insert_aux(variable, key=key, axes=axes, copy=copy) else: raise ValueError("bad key in insert_coord: %r" % key)
#--- End: def
[docs] def insert_dim(self, item, key=None, axis=None, copy=True, replace=True): ''' Insert a dimension coordinate to the domain in place. :Parameters: item: cf.DimensionCoordinate or cf.Coordinate or cf.AuxiliaryCoordinate The new coordinate. If not a dimension coordinate object then it will be converted to one. axis : str, optional key : str, optional The identifier for the new dimension coordinate. The identifier is of the form ``'dimN'`` where the ``N`` part should be replaced by an arbitrary integer greater then or equal to zero. By default a unique identifier will be generated. copy: bool, optional If False then the dimension coordinate is not copied before insertion. By default it is copied. replace : bool, optional If False then do not replace an existing dimension coordinate with the same identifier. By default an existing dimension coordinate with the same identifier is replaced with *coord*. :Returns: out : str The identifier for the new dimension coordinate (see the *key* parameter). :Examples: >>> ''' item = item.asdimension(copy=copy) if key is None: key = axis elif axis is not None and key != axis: raise ValueError("Incompatible key and axis parameters: %r, %r" % (key, axis)) if key is None: key = self.insert_axis(item.size) else: if key in self.d and not replace: raise ValueError( "Can't insert dimension coordinate object: replace=%s and %r identifier already exists" % (replace, key)) self.insert_axis(item.size, key, replace=False) #--- End: if dimensions = self._axes dimensions[key] = [key] # ------------------------------------------------------------ # Turn a scalar dimension coordinate into size 1, 1-d # ------------------------------------------------------------ if item.isscalar: item.expand_dims(0, i=True) self.d[key] = item self._map[key] = 'd' refs = self.r if refs: for ref in refs.itervalues(): self._conform_ref(ref) return key
#--- End: def def _insert_item(self, variable, key, role, axes=None, copy=True, replace=True): ''' Insert a new auxiliary coordinate into the domain in place, preserving internal consistency. :Parameters: coord :cf.Coordinate The new auxiliary coordinate. key : str The identifier for the auxiliary coordinate or cell measure. role : str axes : sequence, optional The ordered axes of the new coordinate. Ignored if the coordinate is a dimension coordinate. Required if the coordinate is an auxiliary coordinate. copy: bool, optional If False then the auxiliary coordinate is not copied before insertion. By default it is copied. replace : bool, optional If False then do not replace an existing dimension coordinate with the same identifier. By default an existing dimension coordinate with the same identifier is replaced with *coord*. :Returns: out : str The identifier for the new auxiliary coordinate (see the 'key' parameter). :Examples: >>> ''' if key in self._axes and not replace: raise ValueError( "Can't insert %s: %r identifier already exists" % (role, key)) ndim = variable.ndim if not ndim: ndim = 1 if axes is None: # -------------------------------------------------------- # The axes have not been set => infer the axes. # -------------------------------------------------------- variable_shape = variable.shape if (not variable_shape or len(variable_shape) != len(set(variable_shape))): raise ValueError( "Ambiguous %s shape: %s. Consider setting the axes parameter." % (variable.__class__.__name__, variable_shape)) axes = [] axes_sizes = self._axes_sizes.values() for n in variable_shape: if axes_sizes.count(n) == 1: axes.append(self.axis(size=n)) else: raise ValueError( "Ambiguous %s shape: %s. Consider setting the axes parameter." % (variable.__class__.__name__, variable_shape)) #--- End: for else: # Axes have been provided axes = self.axes(axes, ordered=True) if len(set(axes)) != ndim: raise ValueError( "Can't insert %s: Mismatched number of axes (%d != %d)" % (role, len(set(axes)), ndim)) aux_axes = [] for axis, size in izip_longest(axes, variable.shape, fillvalue=1): axis_size = self.axis_size(axis) if size != axis_size: raise ValueError( "Can't insert %s: Mismatched axis size (%d != %d)" % (role, size, axis_size)) aux_axes.append(axis) #--- End: for axes = aux_axes #--- End: if n_axes = len(set(axes)) if not (ndim == n_axes or (ndim == 0 and n_axes == 1)): raise ValueError( "Can't insert %s: Mismatched number of axes (%d != %d)" % (role, n_axes, ndim)) self._axes[key] = axes if not variable.ndim: # Turn a scalar item into size 1, 1-d, copying it # required. variable = variable.expand_dims(0, i=(not copy)) elif copy: # Copy the variable variable = variable.copy() return variable #--- End: def def inspect(self): ''' Inspect the object for debugging. .. seealso:: `cf.inspect` :Returns: None ''' print cf_inspect(self) #--- End: def
[docs] def items(self, items=None, role=None, axes=None, axes_all=None, axes_subset=None, axes_superset=None, ndim=None, match_and=True, exact=False, inverse=False, copy=False, strict_axes=False, _restrict_inverse=False): ''' Return items of the domain. .. seealso:: `axes`, `item`, `remove_items` :Parameters: {+items} {+role} {+axes} {+axes_all} {+axes_subset} {+axes_superset} {+ndim} {+exact} {+match_and} {+inverse} {+copy} :Returns: out : dict A dictionary whose keys are domain item identifiers with corresponding values of items of the domain. The dictionary may be empty. :Examples: See `cf.Field.items`. All of these examples are for the same domain, whose complete dictionary of items is shown in the first example. >>> d.items() {{'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>, 'dim1': <CF DimensionCoordinate: grid_longitude(106) degrees>, 'dim2': <CF DimensionCoordinate: time(12) days since 1997-1-1>, 'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>, 'aux1': <CF AuxiliaryCoordinate: latitude(111, 106) degrees_N>, 'aux2': <CF AuxiliaryCoordinate: forecast_reference_time(12) days since 1997-1-1> 'msr0': <CF CellMeasure: area(111, 106) m2>, 'ref0': <CF CoordinateReference: rotated_latitude_longitude>}} >>> d.items(axes='grid_latitude') {{'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>, 'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>, 'aux1': <CF AuxiliaryCoordinate: latitude(111, 106)> degrees_N, 'msr0': <CF CellMeasure: area(111, 106) m2>}} >>> d.items(axes='grid_latitude', ndim=1) {{'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>}} >>> d.items(axes='grid_latitude', strict_axes=True) {{'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>}} >>> d.items(axes='time') {{'dim2': <CF DimensionCoordinate: time(12) days since 1997-1-1>, 'aux2': <CF AuxiliaryCoordinate: forecast_reference_time(12) days since 1997-1-1>}} >>> d.items(axes='time', role='d') {{'dim2': <CF DimensionCoordinate: time(12) days since 1997-1-1>}} >>> d.items(axes='area') {{'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>, 'aux1': <CF AuxiliaryCoordinate: latitude(111, 106) degrees_N>, 'msr0': <CF CellMeasure: area(111, 106) m2>}} >>> d.items(axes=['grid_latitude', 'grid_longitude']) {{'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>, 'aux1': <CF AuxiliaryCoordinate: latitude(111, 106) degrees_N>, 'msr0': <CF CellMeasure: area(111, 106) m2>}} >>> d.items('grid') {{'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>, 'dim1': <CF DimensionCoordinate: grid_longitude(106) degrees>}} >>> d.items('grid', exact=True) {{}} >>> d.items({{'units': 'degrees_E'}}) {{'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>, 'dim1': <CF DimensionCoordinate: grid_longitude(106) degrees>, 'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>, 'aux1': <CF AuxiliaryCoordinate: latitude(111, 106)> degrees_N}} >>> d.items({{'units': 'degrees_E'}}, exact=True) {{'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>}} >>> d.items({{'units': 'radians', 'standard_name': 'time'}}) {{}} >>> d.items({{'units': 'radians', 'standard_name': 'time'}}, maximal_match=False) {{'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>, 'dim1': <CF DimensionCoordinate: grid_longitude(106) degrees>, 'dim2': <CF DimensionCoordinate: time(12) days since 1997-1-1>, 'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>, 'aux1': <CF AuxiliaryCoordinate: latitude(111, 106)> degrees_N}} >>> d.items({{'units': 'radians', 'standard_name': 'time'}}, maximal_match=False, exact=True) {{'dim2': <CF DimensionCoordinate: time(12) days since 1997-1-1>}} >>> set(d.items(role='da')) == set(d.items(role='ct', inverse=True)) True ''' if strict_axes: axes_all = axes print "WARNING: strict_axes has been deprecated. Replace the axes parameter with the axes_all parameter instead." pool = {} for r in ('d', 'a', 'm', 'r'): pool.update(getattr(self, r)) if inverse: if not _restrict_inverse or role is None: master = pool.copy() else: master = {} for r in role: master.update(getattr(self, r)) #--- End: if if items is None and axes is None and role is None and ndim is None: out = pool.copy() else: out = {} if pool and role is not None: # -------------------------------------------------------- # Select items which have a given role # -------------------------------------------------------- out = {} for r in role: out.update(getattr(self, r)) if match_and: pool = out else: for key in out: del pool[key] #--- End: if if pool and axes is not None: # -------------------------------------------------------- # Select items which span at least one of the given axes, # and possibly others. # -------------------------------------------------------- axes_out = {} if not isinstance(axes, dict): axes = {'axes': axes} axes_tmp = self.axes(**axes) if axes_tmp: domain_axes = self._axes for key, value in pool.iteritems(): if axes_tmp.intersection(domain_axes.get(key, ())): axes_out[key] = value #--- End: if if match_and: out = pool = axes_out else: for key in axes_out: out[key] = pool.pop(key) #--- End: if if pool and axes_subset is not None: # -------------------------------------------------------- # Select items whose data array spans all of the specified # axes, taken in any order, and possibly others. # -------------------------------------------------------- axes_out = {} if not isinstance(axes_subset, dict): axes_subset = {'axes': axes_subset} axes_tmp = self.axes(**axes_subset) if axes_tmp: domain_axes = self._axes for key, value in pool.iteritems(): if axes_tmp.issubset(domain_axes.get(key, ())): axes_out[key] = value #--- End: if if match_and: out = pool = axes_out else: for key in axes_out: out[key] = pool.pop(key) #--- End: if if pool and axes_superset is not None: # -------------------------------------------------------- # Select items whose data array spans a subset of the # specified axes, taken in any order, and no others. # -------------------------------------------------------- axes_out = {} if not isinstance(axes_superset, dict): axes_superset = {'axes': axes_superset} axes_tmp = self.axes(**axes_superset) if axes_tmp: domain_axes = self._axes for key, value in pool.iteritems(): if axes_tmp.issuperset(domain_axes.get(key, (None,))): axes_out[key] = value #--- End: if if match_and: out = pool = axes_out else: for key in axes_out: out[key] = pool.pop(key) #--- End: if if pool and axes_all is not None: # -------------------------------------------------------- # Select items which span all of the given axes and no # others # -------------------------------------------------------- axes_out = {} if not isinstance(axes_all, dict): axes_all = {'axes': axes_all} axes_tmp = self.axes(**axes_all) if axes_tmp: domain_axes = self._axes for key, value in pool.iteritems(): if axes_tmp == set(domain_axes.get(key, ())): axes_out[key] = value #--- End: if if match_and: out = pool = axes_out else: for key in axes_out: out[key] = pool.pop(key) #--- End: if if pool and ndim is not None: # -------------------------------------------------------- # Select items whose number of data array axes satisfies a # condition # -------------------------------------------------------- domain_axes = self._axes ndim_out = {} for key, item in pool.iteritems(): if ndim == len(domain_axes.get(key, ())): ndim_out[key] = item #--- End: for if match_and: out = pool = ndim_out else: for key in ndim_out: out[key] = pool.pop(key) #--- End: if if pool and items is not None: # -------------------------------------------------------- # Select items whose properties satisfy conditions # -------------------------------------------------------- items_out = {} if isinstance(items, (basestring, dict, Query)): items = (items,) if items: pool2 = pool.copy() match = [] for m in items: if m.__hash__ and m in pool: # m is a domain item identifier items_out[m] = pool2.pop(m) else: match.append(m) #--- End: for if match and pool: for key, item in pool2.iteritems(): if item.match(match, exact=exact): # This item matches the critieria items_out[key] = item #--- End: if if match_and: out = pool = items_out else: for key in items_out: out[key] = pool.pop(key) #--- End: if #--- End: if if inverse: # -------------------------------------------------------- # Select items other than those previously selected # -------------------------------------------------------- for key in out: del master[key] out = master #--- End: if if copy: # -------------------------------------------------------- # Copy the items # -------------------------------------------------------- out2 = {} for key, item in out.iteritems(): out2[key] = item.copy() out = out2 #--- End: if # ------------------------------------------------------------ # Return the selected items # ------------------------------------------------------------ return out
#--- End: def
[docs] def ref_axes(self, key): '''Return the axes spanned by the coordinate object inputs of a coordinate reference object. :Parameters: key : str A coordinate reference domain identifier. *Example:* To select the coordinate reference with domain identifier "ref1": ``key='ref1'``. :Returns: out : set A set of the domain identifiers of the axes spanned by the coordinate reference's coordinates. :Examples: >>> key = d.item('rotated_latitude_longitude', key=True) >>> d.ref_axes(key) set(['dim2', 'dim1']) ''' axes = self._axes raxes = [] for ckey in self.r[key].coords: raxes.extend(axes.get(ckey, ())) return set(raxes)
#--- End: def
[docs] def insert_aux(self, item, key=None, axes=None, copy=True, replace=True): ''' Insert a auxiliary coordinate into the domain in place. :Parameters: coord : cf.AuxiliaryCoordinate or cf.Coordinate or cf.DimensionCoordinate The new coordinate. If not an auxiliary coordinate object then it will be converted to one. key : str, optional The identifier for the new dimension coordinate. The identifier is of the form ``'auxN'`` where the ``N`` part should be replaced by an arbitrary integer greater then or equal to zero. By default a unique identifier will be generated. axes : list, optional The ordered axes of the new coordinate. Ignored if the coordinate is a dimension coordinate. Required if the coordinate is an auxiliary coordinate. copy: bool, optional If False then the auxiliary coordinate is not copied before insertion. By default it is copied. replace : bool, optional If False then do not replace an existing auxiliary coordinate with the same identifier. By default an existing auxiliary coordinate with the same identifier is replaced with *coord*. :Returns: out : str The identifier for the new auxiliary coordinate (see the *key* parameter). :Examples: >>> ''' item = item.asauxiliary(copy=copy) if not key: key = self.new_aux_identifier() item = self._insert_item(item, key, 'auxiliary coordinate', axes=axes, copy=False) self.a[key] = item self._map[key] = 'a' refs = self.r if refs: for ref in refs.itervalues(): self._conform_ref(ref) return key
#--- End: def
[docs] def insert_measure(self, item, key=None, axes=None, copy=True, replace=True): '''Insert a cell measure into the domain in place. :Parameters: item : cf.CellMeasure The new cell measure. key : str, optional The identifier for the new cell measure. The identifier is of the form ``'msrN'`` where the ``N`` part should be replaced by an arbitrary integer greater then or equal to zero. By default a unique identifier will be generated. axes : sequence, optional The ordered axes of the new cell measure. copy : bool, optional If False then the cell measure is not copied before insertion. By default it is copied. replace : bool, optional If False then do not replace an existing cell measure with the same identifier. By default an existing cell measure with the same identifier is replaced with *item*. :Returns: out : str The identifier for the new cell measure (see the *key* parameter). :Examples: >>> ''' if key is None: key = self.new_measure_identifier() item = self._insert_item(item, key, 'cell measure', axes=axes, copy=copy) self.m[key] = item self._map[key] = 'm' return key
#--- End: def
[docs] def insert_ref(self, item, key=None, copy=True, replace=False): ''' Insert a coordinate reference object into the domain in place. :Parameters: item : cf.CoordinateReference The new coordinate reference object. key : str, optional The identifier for the new coordinate reference object. By default a unique identifier will be generated. copy : bool, optional If False then the coordinate reference object is not copied before insertion. By default it is copied. replace : bool, optional If True then replace an existing coordinate reference object with the same identifier. By default an exception is raised if there is an existing coordinate reference object with the same identifier. :Returns: out : str The internal identifier of the new coordinate reference object. :Examples: >>> ''' if key is None: key = self.new_ref_identifier() elif not replace and key in self.r: raise ValueError( "Can't insert coordinate reference object: replace=%s and %r identifier already exists" % (replace, key)) if copy: item = item.copy() self._conform_ref(item) self.r[key] = item self._map[key] = 'r' return key
#--- End: def
[docs] def remove_axes(self, axes=None, **kwargs): ''' Remove and return axes from the domain. This method has exactly the same interface, functionality and outputs as `cf.Field.remove_axes`. Therefore see `cf.Field.remove_axes` for the full documentation details. .. seealso:: `axes`, `remove_axis`, `remove_item`, `remove_items` :Parameters: axes, kwargs : *optional* See `cf.Field.remove_axes`. :Returns: out : set The removed axes. The set may be empty. :Examples: See `cf.Field.remove_axes`. ''' d = self # ------------------------------------------------------------ # Find the domain axis identifiers # ------------------------------------------------------------ axes = d.axes(axes, **kwargs) if not axes: return set() if axes.intersection(d._axes.get('data', ())): raise ValueError( "Can't remove an axis which is spanned by the data array") axes_sizes = d._axes_sizes for axis in axes: if (axes_sizes[axis] > 1 and d.items(role=('d', 'a', 'm'), ndim=gt(1), axes=axis)): raise ValueError( "Can't remove an axis with size > 1 which is spanned by a multidimensional item") #--- End: for items = d.items(role=('d', 'a', 'm'), axes=axes) for key, item in items.iteritems(): item_axes = d._axes[key] # Remove the item if it only spans removed axes if axes.issuperset(item_axes): d.remove_item(key) continue # Still here? Then squeeze removed axes from the # multidimensional item. iaxes = [item_axes.index(axis) for axis in axes if axis in item_axes] item.squeeze(iaxes, i=True) # item.squeeze(axes.intersection(item_axes)) # if not item.ndim: # # Remove the multidimensional item if it doesn't span # # any axes after being squeezed # d.remove_item(key) # else: # Remove the removed axes from the multidimensional item's # list of axes for axis in axes.intersection(item_axes): item_axes.remove(axis) ##--- End: for #if not item_axes: # # Remove the item if it doesn't span any axes # # after being squeezed # d.remove_item(key) #--- End: for # ------------------------------------------------------------ # Remove the axes # ------------------------------------------------------------ for axis in axes: del axes_sizes[axis] return axes
#--- End: def
[docs] def remove_axis(self, axes=None, **kwargs): ''' Remove and return an axis from the domain. This method has exactly the same interface, functionality and outputs as `cf.Field.remove_axis`. Therefore see `cf.Field.remove_axis` for the full documentation details. .. seealso:: `axis`, `remove_axes`, `remove_item`, `remove_items` :Parameters: axes, kwargs : *optional* See `cf.Field.remove_axis`. :Returns: out : The domain identifier of the removed axis, or None if there isn't one. :Examples: See `cf.Field.remove_axis`. ''' axis = self.axis(axes, **kwargs) if axis is None: return return self.remove_axes(axis).pop()
#--- End: def
[docs] def remove_item(self, items=None, key=False, **kwargs): ''' Remove and return an item from the domain. This method has exactly the same interface, functionality and outputs as `cf.Field.remove_item`. Therefore see `cf.Field.remove_item` for the full documentation details. .. seealso:: `item`, `remove_axes`, `remove_axis`, `remove_items` :Parameters: items, kwargs : *optional* See `cf.Field.remove_item`. :Returns: out : The removed item, or None if no unique item could be found. :Examples: See `cf.Field.remove_item`. >>> d.items() {'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>, 'dim1': <CF DimensionCoordinate: grid_longitude(106) degrees>, 'dim2': <CF DimensionCoordinate: time(12) days since 1997-1-1>, 'aux0': <CF AuxiliaryCoordinate: longitude(111, 106) degrees_E>, 'aux1': <CF AuxiliaryCoordinate: latitude(111, 106) degrees_N>, 'aux2': <CF AuxiliaryCoordinate: forecast_reference_time(12) days since 1997-1-1> 'msr0': <CF CellMeasure: area(111, 106) m2>, 'ref0': <CF CoordinateReference: rotated_latitude_longitude>} >>> d.remove_item('grid_long') >>> d.remove_item('aux1') >>> d.remove_item('T') >>> d.remove_item('longitude', role='a', exact=True) >>> d.remove_item('rotated_latitude_longitude') >>> d.remove_item({None: 'area', 'units': 'km2'}) >>> d.items() {'dim0': <CF DimensionCoordinate: grid_latitude(111) degrees>} ''' items = self.items(items, **kwargs) if not items: return item_key = items.popitem()[0] if items: return items = self.remove_items(item_key).popitem() if key: return items[0] else: return items[1]
#--- End: def
[docs] def remove_items(self, items=None, **kwargs): '''Remove and return items from the domain. This method has exactly the same interface, functionality and outputs as `cf.Field.remove_items`. Therefore see `cf.Field.remove_items` for the full documentation details. .. seealso:: `items`, `remove_axes`, `remove_axis`, `remove_item` :Parameters: items, kwargs : *optional* See `cf.Field.remove_items`. :Returns: out : dict A dictionary whose keys are domain item identifiers with corresponding values of the removed items of the domain. The dictionary may be empty. :Examples: See `cf.Field.remove_items`. ''' out = {} for key, item in self.items(items, **kwargs).iteritems(): x = self._map[key] # If the removed item is a dimension of auxiliary # coordinate then ..... if x in 'da': self._replace_refs_coord_identifier(key) del self._map[key] self._axes.pop(key, None) del getattr(self, x)[key] out[key] = item #--- End: if return out
#--- End: def
[docs] def copy(self): ''' Return a deep copy. ``d.copy()`` is equivalent to ``copy.deepcopy(d)``. :Returns: out : The deep copy. :Examples: >>> e = d.copy() ''' X = type(self) new = X.__new__(X) new._axes = {} for key, value in self._axes.iteritems(): new._axes[key] = value[:] new._axes_sizes = self._axes_sizes.copy() new._map = self._map.copy() new.d = {} for key, value in self.d.iteritems(): new.d[key] = value.copy() new.a = {} for key, value in self.a.iteritems(): new.a[key] = value.copy() new.m = {} for key, value in self.m.iteritems(): new.m[key] = value.copy() new.r = {} for key, value in self.r.iteritems(): new.r[key] = value.copy() nc_dimensions = getattr(self, 'nc_dimensions', None) if nc_dimensions: new.nc_dimensions = nc_dimensions.copy() else: new.nc_dimensions = {} return new
#--- End: def
[docs] def close(self): ''' Close all referenced open data files. :Returns: None :Examples: >>> d.close() ''' for item in self.items().itervalues(): item.close()
#--- End: def
[docs] def item(self, items=None, key=False, **kwargs): ''' Return an item of the domain, or its domain identifier. This method has exactly the same interface, functionality and outputs as `cf.Field.item`. Therefore see `cf.Field.item` for the full documentation details. .. seealso:: `axis`, `items`, `remove_item` :Parameters: items, kwargs : *optional* See `cf.Field.item`. key : bool, option See `cf.Field.item`. :Returns: out : See `cf.Field.item`. :Examples: See `cf.Field.items`. The following examples are base on the following domain: >>> d.items() {'dim0': <CF DimensionCoordinate: grid_latitude(73) 'dim1': <CF DimensionCoordinate: grid_longitude(96)>, 'dim2': <CF DimensionCoordinate: time(12)>, 'aux0': <CF AuxiliaryCoordinate: latitude(73, 96)>, 'aux1': <CF AuxiliaryCoordinate: longitude(73, 96)>, 'msr0': <CF CellMeasure: area(96, 73)>, 'ref0': <CF CoordinateReference: rotated_latitude_longitude>} >>> d.item('longitude') <CF DimensionCoordinate: longitude(360)> >>> d.item('long') <CF DimensionCoordinate: longitude(360)> >>> d.item('long', key=True) 'dim2' >>> d.item('lon', exact=True) None >>> d.item('longitude', exact=True) <CF DimensionCoordinate: longitude(360)> >>> d.item('msr0') <CF CellMeasure: area(96, 73)> >>> d.item({'units': 'degrees'}) None >>> d.item({'units': 'degreeN'}) <CF AuxiliaryCoordinate: latitude(73, 96)> >>> d.item(axes='time') <CF DimensionCoordinate: time(12)> >>> d.item(axes='grid_latitude') None >>> d.item(axes='grid_latitude', strict_axes=True) <CF DimensionCoordinate: grid_latitude(73) >>> d.item(axes='grid_longitude', ndim=1, key=True) 'dim1' ''' d = self.items(items, **kwargs) if not d: return items = d.popitem() if d: return if key: return items[0] else: return items[1]
#--- End: def def items_axes(self, items=None, **kwargs): ''' Return the axes of a domain item. This method has exactly the same interface, functionality and outputs as `cf.Field.item_axes`. Therefore see `cf.Field.item_axes` for the full documentation details. .. seealso:: `axes`, `data_axes`, `item` :Parameters: item, kwargs : *optional* See `cf.Field.item_axes`. :Returns: out : list or None The ordered list of axes for the item or, if there is no unique item or the item is a coordinate reference, then None is returned. :Examples: See `cf.Field.item_axes`. ''' kwargs.setdefault('role', ('d', 'a', 'm')) keys = self.items(items, **kwargs).keys() _items_axes = self._axes out = {} for key in keys: out[key] = _items_axes[key][:] return out #--- End: def
[docs] def item_axes(self, items=None, **kwargs): ''' Return the axes of a domain item. This method has exactly the same interface, functionality and outputs as `cf.Field.item_axes`. Therefore see `cf.Field.item_axes` for the full documentation details. .. seealso:: `axes`, `data_axes`, `item` :Parameters: items, kwargs : *optional* See `cf.Field.item_axes`. :Returns: out : list or None The ordered list of axes for the item or, if there is no unique item or the item is a coordinate reference, then None is returned. :Examples: See `cf.Field.item_axes`. ''' kwargs['key'] = True key = self.item(items, **kwargs) if key is not None and self._map[key] != 'r': return self._axes[key][:]
#--- End: def
[docs] def data_axes(self): ''' Return the axes of the field's data array. This method has exactly the same interface, functionality and outputs as `cf.Field.data_axes`. Therefore see `cf.Field.data_axes` for the full documentation details. .. seealso:: `axes`, `item_axes` :Returns: out : list or None The ordered axes of the field's data array. If there is no data array then None is returned. :Examples: See `cf.Field.data_axes`. ''' axes = self._axes.get('data', None) if axes is not None: return axes[:]
#--- End: def
[docs] def axes(self, axes=None, size=None, ordered=False, **kwargs): ''' Return domain axis identifiers. This method has exactly the same interface, functionality and outputs as `cf.Field.axes`. See `cf.Field.axes` for details. .. seealso:: `axis`, `items`, `remove_axes` :Parameters: axes, kwargs: *optional* See `cf.Field.axes`. size : int or cf.Query, optional See `cf.Field.axes`. ordered : bool, optional See `cf.Field.axes`. :Returns: out : set or list A set of domain axis identifiers, or a list if *ordered* is True. The set or list may be empty. :Examples: See `cf.Field.axes`. ''' def _axes(self, axes, size, item_axes, axes_sizes, kwargs): a = None if axes is not None: if axes.__hash__: if isinstance(axes, slice): try: a = tuple(item_axes.get('data', ())[axes]) except IndexError: a = () elif axes in axes_sizes: # -------------------------------------------- # axes is a domain axis identifier # -------------------------------------------- a = (axes,) elif axes in item_axes and not kwargs: # -------------------------------------------- # axes is a domain item identifier # -------------------------------------------- a = item_axes[axes] elif isinstance(axes, slice): # -------------------------------------------- # axes is a slice object # -------------------------------------------- a = item_axes.get('data', ())[axes] else: # -------------------------------------------- # See if axes is an integer # -------------------------------------------- try: a = (item_axes.get('data', ())[axes],) except IndexError: a = () except TypeError: pass #--- End: if elif not kwargs: a = tuple(axes_sizes) #--- End: if if a is None: # ---------------------------------------------------- # Assume that axes is a value accepted by the items # method # ---------------------------------------------------- a = [] for key in self.items(axes, **kwargs): a += item_axes.get(key, ()) #--- End: if if size: a = [axis for axis in a if size == axes_sizes[axis]] return a #--- End: def if kwargs: kwargs['axes'] = None item_axes = self._axes axes_sizes = self._axes_sizes if axes is None or isinstance(axes, (basestring, dict, slice, int, long)): # -------------------------------------------------------- # axes is not a sequence or a set # -------------------------------------------------------- a = _axes(self, axes, size, item_axes, axes_sizes, kwargs) else: # -------------------------------------------------------- # axes is a sequence or a set # -------------------------------------------------------- a = [] for x in axes: a += _axes(self, x, size, item_axes, axes_sizes, kwargs) #--- End: if if not ordered: return set(a) else: return list(a)
#--- End: def
[docs] def axis(self, axes=None, size=None, **kwargs): ''' Return a domain axis identifier. The axis may be selected with the keyword arguments. When multiple criteria are given, the axis will be the intersection of the selections. If no unique axis can be found then None is returned. .. seealso:: `axes`, `item`, `items`, `remove_item` :Parameters: {+axes, kwargs} :Returns: out : str The unique domain axis identifier. If there isn't a unique axis then None is returned. :Examples: ''' axes = self.axes(axes, size=size, **kwargs) if not axes: return axis = axes.pop() if not axes: return axis else: return
#--- End: def def axes_sizes(self, axes=None, size=None, key=False, **kwargs): ''' ''' out = {} axes = self.axes(axes, size=size, **kwargs) for axis in axes: out[axis] = self._axes_sizes[axis] if not key: out2 = {} for axis, size in out.iteritems(): out2[self.axis_name(axis)] = size return out2 #--- End: if return out #--- End: def def axis_size(self, axes=None, **kwargs): ''' ''' axis = self.axis(axes, **kwargs) if axis is None: return None return self._axes_sizes[axis] #--- End: def
[docs] def expand_dims(self, coord=None, size=1, copy=True): ''' Expand the domain with a new dimension in place. The new dimension may by of any size greater then 0. :Parameters: coord : cf.Coordinate, optional A dimension coordinate for the new dimension. The new dimension's size is set to the size of the coordinate's array. size : int, optional The size of the new dimension. By default a dimension of size 1 is introduced. Ignored if *coord* is set. :Returns: None :Examples: >>> d.expand_dims() >>> d.expand_dims(size=12) >>> c <CF DimensionCoordinate: > >>> d.expand_dims(coord=c) ''' if coord: self.insert_dim(coord, copy=copy) else: self.insert_axis(size)
#--- End: def
[docs] def dump_axes(self, display=True, _level=0): ''' Return a string containing a description of the domain. :Parameters: display : bool, optional If False then return the description as a string. By default the description is printed. :Returns: out : str A string containing the description. :Examples: ''' indent1 = ' ' * _level indent2 = ' ' * (_level+1) string = ['%sAxes:' % indent1] data_axes = self._axes.get('data', ()) w = sorted(["{0}{1}({2})".format(indent2, self.axis_name(axis), size) for axis, size in self._axes_sizes.iteritems() if axis not in data_axes]) x = ["{0}{1}({2})".format(indent2, self.axis_name(axis), self._axes_sizes[axis]) for axis in data_axes] string = '\n'.join(string + w + x) if display: print string else: return string
#--- End: def
[docs] def dump_components(self, complete=False, display=True, _level=0): ''' Return a string containing a full description of the domain. :Parameters: complete : bool, optional display : bool, optional If False then return the description as a string. By default the description is printed. :Returns: out : str A string containing the description. :Examples: ''' indent1 = ' ' * _level string = [] # Dimension coordinates for key, value in sorted(self.d.iteritems()): string.append('') string.append('%sDimension coordinate: %s' % (indent1, value.name(''))) string.append(value.dump(display=False, domain=self, key=key, _level=_level+1)) # Auxiliary coordinates for key, value in sorted(self.a.iteritems()): string.append('') string.append('%sAuxiliary coordinate: %s' % (indent1, value.name(''))) string.append(value.dump(display=False, domain=self, key=key, _level=_level+1)) # Cell measures for key, value in sorted(self.m.iteritems()): string.append('') string.append(value.dump(display=False, domain=self, key=key, _level=_level)) # Coordinate references for key, value in sorted(self.r.iteritems()): string.append('') string.append(value.dump(display=False, complete=complete, domain=self, _level=_level)) return '\n'.join(string)
#--- End: def
[docs] def dump(self, complete=False, display=True, _level=0): ''' Return a string containing a full description of the domain. :Parameters: complete : bool, optional Output a complete dump. Fields contained in coordinate reference are themselves described with their dumps. display : bool, optional If False then return the description as a string. By default the description is printed, i.e. ``d.dump()`` is equivalent to ``print d.dump(display=False)``. :Returns: out : None or str A string containing the description. complete : bool, optional :Examples: ''' string = (self.dump_axes(display=False, _level=_level), self.dump_components(complete=complete, display=False, _level=_level), ) string = '\n'.join(string) if display: print string else: return string
#--- End: def
[docs] def equals(self, other, rtol=None, atol=None, ignore_fill_value=False, traceback=False, verbose=False): ''' True if two domains are equal, False otherwise. Equality is defined as follows: * There is one-to-one correspondence between dimensions and dimension sizes between the two domains. * For each domain component type (dimension coordinate, auxiliary coordinate and cell measures), the set of constructs in one domain equals that of the other domain. The component identifiers need not be the same. * The set of coordinate references in one domain equals that of the other domain. The coordinate reference identifiers need not be the same. Equality of numbers is to within a tolerance. :Parameters: other : The object to compare for equality. atol : float, optional The absolute tolerance for all numerical comparisons, By default the value returned by the `ATOL` function is used. rtol : float, optional The relative tolerance for all numerical comparisons, By default the value returned by the `RTOL` function is used. ignore_fill_value : bool, optional If True then data arrays with different fill values are considered equal. By default they are considered unequal. traceback : bool, optional If True then print a traceback highlighting where the two instances differ. :Returns: out : bool Whether or not the two instances are equal. :Examples: >>> d.equals(s) True >>> d.equals(t) False >>> d.equals(t, traceback=True) ''' if self is other: return True # Check that each instance is the same type if type(self) != type(other): print("%s: Different types: %s, %s" % (self.__class__.__name__, self.__class__.__name__, other.__class__.__name__)) return False #--- End: if if (sorted(self._axes_sizes.values()) != sorted(other._axes_sizes.values())): # There is not a 1-1 correspondence between dimensions and # dimension sizes between the two domains. if traceback: print("%s: Different domain ranks: %s != %s" % (self.__class__.__name__, sorted(self._axes_sizes.values()), sorted(other._axes_sizes.values()))) return False #--- End: if if rtol is None: rtol = RTOL() if atol is None: atol = ATOL() # ------------------------------------------------------------ # Test the coordinates and cell measures. Don't worry about # coordinate references yet - we'll do so later. # ------------------------------------------------------------ key_map = {} for self_keys, other_keys in izip((self.d, self.a, self.m), (other.d, other.a, other.m)): self_items = self_keys.items() other_items = other_keys.items() for key0, value0 in self_items: found_match = False for i, (key1, value1) in enumerate(other_items): if value0.equals(value1, rtol=rtol, atol=atol, ignore_fill_value=ignore_fill_value, traceback=verbose): found_match = True key_map[key1] = key0 other_items.pop(i) break #--- End: for if not found_match: if traceback: print("{0}: No {1} equal to: {2!r}".format( self.__class__.__name__, value1.__class__.__name__, value0)) return False #--- End: for #--- End: for # ------------------------------------------------------------ # Test the coordinate references # ------------------------------------------------------------ self_t = self.r other_t = other.r if not self_t: if other_t: # Self doesn't have any coordinate references but other does if traceback: print( "%s: Different numbers of coordinate references: 0 != %d" % (self.__class__.__name__, len(other_t))) return False else: if not other_t: # Other doesn't have any coordinate references but self does if traceback: print( "%s: Different numbers of coordinate references: %d != 0" % (self.__class__.__name__, len(self_t))) return False #--- End: if refs1 = other_t.keys() for key0, ref0 in self_t.iteritems(): found_match = False for key1 in refs1: ref1 = other_t[key1] if self._equal_refs(ref0, ref1, domain=other, pointer_map=key_map, ignore_fill_value=ignore_fill_value, traceback=verbose): # This coordinate reference is also in other found_match = True refs1.remove(key1) break #--- End: for if not found_match: # This coordinate reference was not found in other if traceback: print("%s: Missing coordinate reference: %r" % (self.__class__.__name__, ref0)) return False #--- End: for #--- End: if # ------------------------------------------------------------ # Still here? Then the two domains are equal # ------------------------------------------------------------ return True
#--- End: def #--- End: class