Source code for cf

'''

cf-python
=========
:ref:`Table of contents <toc>`

See the `cf-python home page <http://code.google.com/p/cf-python/>`_
for more documentation on the CF data model, downloads and source code.

Field structure
===============
:ref:`Table of contents <toc>`

A field is composed as follows (refer to the data model description
and accompanying UML diagram for more details):

:ref:`Field <Field>`:
    * :ref:`Attributes <Attributes>`
    * :ref:`Data <Data>`
    * :ref:`Units <Units0>`
    * :ref:`Cell methods <CellMethods>`
    * :ref:`Space <Space0>`:
        * :ref:`Coordinates <Coordinate>`:
            * :ref:`Attributes <Attributes>`
            * :ref:`Data <Data>`
            * :ref:`Units <Units0>`
            * :ref:`Bounds <CoordinateBounds>`
        * :ref:`Cell measures <CellMeasures>`
            * :ref:`Attributes <Attributes>`
            * :ref:`Data <Data>`
            * :ref:`Units <Units0>`
        * :ref:`Transforms <Transform>`
            * `Attributes`
            * `Attributes`

All components of a field are optional.


.. _variable_data_storage_access_and_manipulation:

Data storage, access and manipulation
=====================================
:ref:`Table of contents <toc>`

The :class:`.Variable` class and all objects which inherit it (namely
:class:`.Field`, :class:`.Coordinate`, :class:`.CoordinateBounds` and
:class:`.CellMeasures`) contain data arrays. The data storage, access
and manipulation model is very similar for all of them and it will be
noted when certain objects exhibit different behaviour. In this
section, the word "variable" refers a :class:`.Variable` object or any
object which inherits from the :class:`.Variable` class.

Resizing
--------
:ref:`Table of contents <toc>`

A variable's data may be sliced by indexing its *slice*
attribute. Indexing is similar to that of a numpy array. The
differences to numpy array indexing are:

    1. An integer index `i` takes the `i`-th element but **does not**
       reduce the rank of the output array by one.

    2. More than one dimension's slice may be a 1-d boolean array or
       1-d sequence of integers, and these indices work independently
       along each dimension (similar to the way vector subscripts work
       in Fortran).

The result of a slice is a new variable instance and if the variable's
the new variable's data will be a deep copy of the slice of the
original array.

>>> isinstance(v, Variable)
True
>>> v.shape
(10, 20, 30)
>>> v.slice[0].shape
(1, 20, 30)
>>> v.slice[0, -1, 3].shape
(1, 1, 1)
>>> v.slice[0:5,...,slice(11,0,-2)].shape
(5, 20, 6)
>>> v.shape
(10, 20, 30)
>>> v.slice[:,:,numpy.arange(30) < 4].shape
(10, 20, 4)
>>> v.slice([1,2], [3,4], [5,6]).shape
(2, 2, 2)

For variables which contain other variable types, the contained
variables are also sliced. So slicing a coordinate also slices its
bounds, if there are any; and slicing a field also slices its space by
applying each dimension's slice to the appropriate space elements.

Size 1 dimensions
^^^^^^^^^^^^^^^^^
:ref:`Table of contents <toc>`

Size 1 dimensions resulting from a slice are always retained. However,
a size 1 dimension may optionally be dropped from the field's data and
its dimension coordinate reduced to a scalar if no auxiliary
coordinates or cell measures span that dimension. Therefore, fields
and dimension coordinates may contain scalar data, but auxiliary
coordinates and cell measures may not. A field's :meth:`squash
<.Field.squash>` method will carry out this reduction.

Resizing by coordinate values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:ref:`Table of contents <toc>`

A field may be resized by specifying conditions on its coordinate's
values. These conditions are specified as arguments to a call to the
field's :attr:`slice <.Field.slice>` attribute (as opposed to indexing
it). Note that only a field's `slice` attribute may be called in this
manner.
 
>>> s
<CF Field: air_temperature(7070, 64, 128)>
>>> print s
Data            : air_temperature(time, latitude, longitude)
Cell methods    : time: mean (interval: 1.0 month)
Dimensions      : time(7070) -> 450-11-16 00:00:00 to 1049-12-16 12:00:00
                : latitude(64) -> -87.8638 to 87.8638 degrees_north
                : longitude(128) -> 0 to 357.1875 degrees_east
                : height(1) -> 2 m
Auxiliary coords:
>>> s.slice[:,32, 64]
<CF Field: air_temperature(7070, 1, 1)
>>> s.slice(longitude=0.0, latitude=0.0)
<CF Field: air_temperature(7070, 1, 1)

Assignment to the data array
----------------------------
:ref:`Table of contents <toc>`

Assignment to the data array is done with the same indexing of the
*slice* attribute as for data access. Assignment can not change the
shape of the data:

>>> v.shape
(10, 20, 30)
>>> v.slice[0, 0, 0] = 273.15
>>> v.varray[0,0,0]
273.15
>>> v.slice[0] = 273.15
>>> v.shape
(10, 20, 30)
>>> v.slice[1,2,:] = range(30)
>>> v.slice[...] = numpy.arange(6000).reshape(10, 20, 30)
   
Data storage
------------
:ref:`Table of contents <toc>`

The data may be stored as numpy arrays or as file pointers. A file
pointer may be any kind of object which has attributes *shape*,
*size*, *ndim* and *dtype*; and is indexable all in the same manner as
a numpy array. A *netCDF4.Variable* instance is an example of a valid
file pointer object. Accessing the data makes no distinction between
the two storage methods, but there are I/O, memory and speed
performance issues to consider. If data are stored as a file pointer
then accessing any part of the array will (at present) cause the
entire array to be read from disk into memory and stored as a numpy
array, replacing the original file pointer. Refer to :func:`.read` and
:func:`.write`.

A variable stores a list of numpy arrays and file pointers which is
treated by the variable as if it were a single numpy array created by
realizing any file pointers in memory and concatenating all arrays in
the list along their common dimension. Such a list is usually created
when fields are aggregated, thereby not requiring data to be read from
disk during the aggregation process. Units, missing data values,
packing scale factors, packing offsets and array dimension orders may
all differ between list elements, with any required conversions being
made at the time of data access.

The list of numpy arrays and file pointers is held inside a
:class:`.Data` instance and a variable's data may be completely
replaced by assigning a :class:`.Data` object to its *_data*
attribute:

>>> v._data = cf.Data(numpy.arange(10))
>>> type(ncvariable)
<type 'netCDF4.Variable'>
>>> v._data = cf.Data(ncvariable)

Note that this assignment makes does not check on consistency with the
variable's metadata.

Copying
-------
:ref:`Table of contents <toc>`

A deep copy of a variable may be created using its *copy* method or,
equivalently, using the :mod:`copy` module.

>>> w = v.copy()
>>> w = copy.deepcopy(v)

This type of copy does not read any data from disk, i.e. it will not
convert a file pointer to a numpy array. A third type of copy,
however, will do such a conversion, namely slicing the variable with
an ellipsis (or equivalent):

>>> v.type
<type 'netCDF4.Variable'>
>>> w = v.copy()
>>> w.type, v.type
(<type 'netCDF4.Variable'>, <type 'netCDF4.Variable'>)
>>> w = v.slice[...]
>>> w.type, v.type
(<type 'numpy.ndarray'>, <type 'numpy.ndarray'>)


.. _Units0:

Units
=====
:ref:`Table of contents <toc>`

A variable always contains a :class:`Units` object which gives the
physical units of the values contained in its data array, if it has
one. The :class:`Units` object is stored in the `Units` attribute but
is typically accessed through the variable's `units` and `calendar`
attributes, which may take any value allowed by the `CF conventions
<http://cf-pcmdi.llnl.gov/documents/cf-conventions/latest-cf-conventions-document-1>`_. In
particular, the value of the `units` attribute is a string that can be
recognized by `UNIDATA's Udunits-2 package
<http://www.unidata.ucar.edu/software/udunits/>`_, with a few
exceptions for greater consistency with CF. These are detailed by
:class:`Units` object.


>>> v.units = 'm s-1'
>>> v.Units
<Cf Units: 'm s-1'>
>>> v.units = 'days since 2004-3-1'
>>> v.calendar = 'noleap'
>>> v.Units
<CF Units: days since 2004-3-1 calendar=noleap>
>>> v.units = 'level'
>>> v.units = 'Sv'
>>> v.units = 'psu'
>>> v.units = 'decibel'
>>> v.Units = cf.Units('dB')


Equality and equivalence of units
---------------------------------
:ref:`Table of contents <toc>`

The :class:`Units` object has methods for assessing whether two units
are equivalent or equal. Two units are equivalent if and only if
numeric values in one unit are convertible to numeric values in the
other unit (such as ``kilometres`` and ``metres``). Two units are
equal if and only if they are equivalent and their conversion is a
scale factor of 1 (such as ``kilometres`` and ``1000 metres``). Note
that equivalence and equality are based on internally stored binary
representations of the units, rather than their string
representations.

>>> v.units = 'm/s'

>>> w.units = 'm s-1'
>>> v.Units == w.Units
True

>>> w.units = 'km s-1'
>>> w.Units.equals(w.Units)
False
>>> u.Units.equivalent(w.Units)
True

>>> w.units = '0.001 kilometer.second-1'
>>> v.Units.equals(w.Units)
True
>>> v.Units.equivalent(w.Units)
True

>>> w.units = 'days since 1987-12-3'
>>> v.Units.equals(w.Units)
False
>>> v.units = 'hours since 2000-12-1'
>>> v.Units.equivalent(w.Units)
True

Time units
----------
:ref:`Table of contents <toc>`

Time units may be given as durations of time or as an amount of time
since a reference time:

>>> v.units = 's'
>>> v.units = 'day'
>>> v.units = 'days since 1970-01-01'
>>> v.units = 'seconds since 1992-10-8 15:15:42.5 -6:00'

.. note::

   It is recommended that the units ``year`` and ``month`` be used
   with caution, as explained in the following excerpt from the CF
   conventions: "The Udunits package defines a year to be exactly
   365.242198781 days (the interval between 2 successive passages of
   the sun through vernal equinox). It is not a calendar year. Udunits
   includes the following definitions for years: a common_year is 365
   days, a leap_year is 366 days, a Julian_year is 365.25 days, and a
   Gregorian_year is 365.2425 days. For similar reasons the unit
   ``month``, which is defined to be exactly year/12, should also be
   used with caution."

Calendar
^^^^^^^^
:ref:`Table of contents <toc>`

The date given in reference time units is always associated with one
of the calendars recognized by the CF conventions and may be set with
the variable's `calendar` attribute. However, as in the CF
conventions, if the calendar is not set then, for the purposes of
calculation and comparison, it defaults to the mixed Gregorian/Julian
calendar as defined by Udunits:

>>> v.units = 'days since 2000-1-1'
>>> v.calendar
AttributeError: Can't get 'Variable' attribute 'calendar'
>>> w.units = 'days since 2000-1-1'
>>> w.calendar = 'gregorian'
>>> w.Units.equals(v.Units)
True

Changing units
--------------
:ref:`Table of contents <toc>`

Changing units to equivalent units causes the variable's data array
values to be modified in place (if required) when they are next
accessed, and not before:

>>> v.units
'metre'
>>> v.varray
array([    0.,  1000.,  2000.,  3000.,  4000.])
>>> v.units = 'kilometre'
>>> v.units
'kilometre'
>>> v.varray
array([ 0.,  1.,  2.,  3.,  4.])

>>> v.units
'hours since 2000-1-1'
>>> v.varray
array([-1227192., -1227168., -1227144.])
>>> v.units = 'days since 1860-1-1'
>>> v.varray
array([ 1.,  2.,  3.])

Equivalently to assigning new units as a strings to the *units* and
*calendar* attributes, the :class:`Units` object stored in the *Units*
attribute may be operated on with overloaded augmented arithmetic
assignments and binary arithmetic operations:

>>> v.units
'kelvin'
>>> v.varray
array([ 273.15,  274.15,  275.15,  276.15,  277.15])

>>> v.Units -= 273.15
>>> v.units
'K @ 273.15'
>>> v.varray
array([ 0.,  1.,  2.,  3.,  4.])

>>> v.Units = v.Units + 273.15
>>> v.units
'K'
>>> v.varray
array([ 273.15,  274.15,  275.15,  276.15,  277.15])

>>> v.units = 'K @ 237.15'
'K @ 273.15'
>>> v.varray
array([ 0.,  1.,  2.,  3.,  4.])

If the variable has a data array and its units are changed to
non-equivalent units then a :mod:`TypeError` will be raised when the
data are next accessed:

>>> v.units
'm s-1'
>>> v.units = 'K'
>>> v.varray
TypeError: Units are not convertible: <CF Units: m s-1>, <CF Units: K>

Any numpy array may also be modified (in place or otherwise) for
equivalent units using the :class:`Units` object's :meth:`conform
<Units.conform>` static method.

Changing units without modifying the data
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:ref:`Table of contents <toc>`

It is possible to change a variable's units without modifying the data
by using the variable's :meth:`override_Units` method. This could be
useful for correcting erroneously set units and setting units where
there were none:

>>> v.Units
<CF Units: metre>
>>> v.varray
array([ 0.,  1.,  2.,  3.,  4.])
>>> v.override_Units(cf.Units('km'))
>>> v.Units
<CF Units: km>
>>> v.varray
array([ 0.,  1.,  2.,  3.,  4.])


>>> v.units
AttributeError: 'Variable' object has no attribute 'units'
>>> v.varray
array([ 0.,  1.,  2.,  3.,  4.])
>>> w.Units
<CF Units: days since 2000-1-1 calendar='noleap'>
>>> v.override_Units(w.Units)
>>> v.Units
<CF Units: days since 2000-1-1 calendar='noleap'>
>>> v.varray
array([ 0.,  1.,  2.,  3.,  4.])


Changing the calendar
^^^^^^^^^^^^^^^^^^^^^
:ref:`Table of contents <toc>`

.. warning:: 

   Do not change the calendar of reference time units in the current
   version. Whilst this is possible, it will almost certainly result
   in an incorrect interpretation of the data array or an
   error. Allowing the calendar to be changed is under development and
   will be available soon.

Coordinate units
----------------
:ref:`Table of contents <toc>`

The units of a coordinate's bounds are always the same as the
coordinate itself, and the units of the bounds automatically change
when a coordinate's units are changed:

>>> c.units
'degrees'
>>> c.bounds.units
'degrees'
>>> c.units = 'radians'
>>> c.bounds.units
'radians'


Associating units with non-variable objects
-------------------------------------------
:ref:`Table of contents <toc>`

The :class:`Value` object allows units to be associated with a number
or a numpy array, so that units may be taken into account when it is
combined with a variable, another :class:`Value` object, a python
numeric type or a numpy array:

>>> s  = cf.Value(2.0, units='s')
>>> m  = cf.Value(2000.0, units='metres')
>>> km = cf.Value(1.0, units='km')

>>> b = m / s
>>> b
<CF Value: array(1000.0, units='m s-1'>

>>> b -= 1
>>> b
<CF Value: array(999.0, units='km'>
>>> b = m ** 2
>>> b
<CF Value: array(4000000.0, units='m2'>

>>> b = m - km
>>> b
<CF Value: array(1000.0, units='m'>

>>> b = cf.Value(numpy.array([1, 2, 3], units='m')
>>> b *= b
>>> b
<CF Value: array([1, 4, 9]), units='m2')
>>> b == b
array([ True, True, True], dtype=bool)
>>> b.value
array([1, 4, 9])
>>> b += b.value
>>> b
<CF Value: array([2, 8, 18]), units='m2')

There are some restrictions when combining and comparing
:class:`Value` objects with variables. See the section on
:ref:`arithmetic and comparison with variables <Arithmetic>` for more
details and examples.


.. _Arithmetic:

Arithmetic and comparison with variables
========================================
:ref:`Table of contents <toc>`

When using a variable in arithmetic operations, a new variable is
created with modified data, or the variable's data may be changed in
place.

When a variable is used in comparison opeations, a boolean numpy array
is returned which gives the result of the comparison for the
variable's data.

Operator overloading
--------------------
:ref:`Table of contents <toc>`

The following operators, operations and assignments are overloaded in
a variable to apply element-wise to the variable's data as a numpy
array.

Comparison operators:

    ``==, !=, >, <, >=, <=``

Binary arithmetic operations:
    
    ``+, -, *, /, //, __truediv__, %, pow(), **, &, ^, |``

Unary arithmetic operations:

    ``-, +, abs(), ~``

Augmented arithmetic assignments:

    ``+=, -=, *=, /=, //=, %=, **=, &=, ^=, |=``

Valid operations
----------------
:ref:`Table of contents <toc>`

A variable maybe combined or compared with one of the following
objects in the manners described:

================  ====================================================
Object            How combined or compared
================  ====================================================
python numeric    The variable's data array is combined/compared with
                  the python scalar

:class:`Value`    The variable's data array is combined/compared with
                  the :class:`Value` object's scalar value, taking
                  into account the possibility of different, but
                  equivalent, units.

variable          The two variables' data arrays are
                  combined/compared, taking into account the
                  possibility of different, but equivalent, units.
      
		  .. note:: 

		     It is intended that when combining/comparing two
		     :class:`Field` objects, different dimension
		     orders and directions will be taken into account,
		     if possible. However, this has not been
		     implemented in the current version.

================  ====================================================

A variable may appear on the left or right of the above operations. A
variable is always returned from unary and binary arithmetic
operations and a numpy array is always returned from the comparison
operations. Augmented arthmetic operations change a variable in place.

*(Examples will arrive shortly)*

.. note::

   Combining/comparing a numpy array with a variable does not raise an
   exception if the numpy array is on the right, but the result (a
   numpy array of variable objects) will be generally unintended.


Tolerance of numeric equality
=============================
:ref:`Table of contents <toc>`

Most objects defined in the :mod:`cf` package have an *equals* method
which determines the congruence of two instances. The aspects of this
equality vary between objects, but for all objects numeric equalities
are tested to within a tolerance defined by parameters 'rtol'
(relative tolerance) and 'atol' (absolute tolerance), where two
numbers a and b (from the left and right sides respectively of the
comparison) are considered equal if

    :math:`|a - b| <= atol + rtol*|b|`
    
Default tolerances may be found and set with the functions
:func:`.RTOL` and :func:`.ATOL`. Numerically tolerant equality
of two objects is also tested by the :func:`.equals` function.


.. _Space0:

Space structure
===============
:ref:`Table of contents <toc>`

A space contains any number of dimensions and space components, the
latter comprising dimension coordinates, auxiliary coordinates, cell
measures and transforms. A field's space is stored in its
`attr:`~.cf.Field.space` attribute, the value of which is a
:class:`.Space` object.


Dimensionality
--------------
:ref:`Table of contents <toc>`

The dimensions of the space, and of its associated field, are given by
the space's *dimension_sizes* and *dimensions* attributes.

The *dimension_sizes* attribute is a dictionary whose keys are
dimension identifiers and values are positive integer dimension
sizes. A dimension identifier is the string ``dim`` suffixed by an
arbitrary, but unique, integer. For example:

>>> s
<CF Space: (30, 24, 1, 17)>
>>> s.dimension_sizes
{'dim0': 1, 'dim1': 17, 'dim2': 30, 'dim3': 24}

The *dimensions* attribute specifies which dimensions relate to each
space component (coordinates and cell measures) and to the field which
holds the space. For example:

>>> s.dimensions
{'data': ['dim1', 'dim2', 'dim3'],
 'aux0': ['dim2', 'dim3'],
 'aux1': ['dim2', 'dim3'],
 'cm0' : ['dim2', 'dim3'],
 'dim0': ['dim0'],
 'dim1': ['dim1'],
 'dim2': ['dim2'],
 'dim3': ['dim3']}

A key of this dictionary identifies a space component or the field's
data as follows:

==========  ==============================
Key prefix  Description
==========  ==============================
aux         Auxiliary coordinate
cm          Cell measures
data        The field containing the space
dim         Dimension coordinate
==========  ==============================

Each key's value is an ordered list which corresponds to the shape of
that component's data, with two exceptions:

    1. If the field's data is a scalar then its value is an empty list

    2. If a dimension coordinate is a scalar it retains its dimension
       as a single element list.

An arbitrary non-negative integer after the prefix discerns between
space components of the same type.

It is possible for a space dimension to have no space components.


Storage of coordinates and cell measures variables
--------------------------------------------------
:ref:`Table of contents <toc>`

The space object is a kind of dictionary which support the built-in
dictionary's methods. Its keys are the identifiers of the space
components dimension coordinates, auxiliary coordinates and cell
measures. Each key's value stores the variable it identifies. For
example, a space's key and value pairs may be:

>>> s.keys()
['dim0', 'dim1', 'dim2', 'dim3', 'aux0', 'aux1']
>>> for key, value in s.iteritems():
...     print key, ':', repr(value)
...
dim0: <CF Coordinate: sigma(20)>
dim1: <CF Coordinate: time(12)>
dim2: <CF Coordinate: latitude(111)>
dim3: <CF Coordinate: longitude(106)>
aux0: <CF Coordinate: grid_latitude(111, 106)>
aux1: <CF Coordinate: grid_longitude(111, 106)>


Transforms
----------
:ref:`Table of contents <toc>`

The space may have any number of transforms describing projection
parameters (CF grid mappings) or potential auxiliary coordinates
(coordinates defined by CF formula_terms equations). These are stored
in the space's `transform` attribute, which is a dictionary of
:class:`.Transform` objects. For example:

>>> s.transform
{'trans0': <CF Transform: atmosphere_sigma_coordinate>,
 'trans1': <CF Transform: rotated_latitude_longitude>}

>>> cf.dump(s.transform['trans0'])
atmosphere_sigma_coordinate transform
-------------------------------------
Transform['ps'] = <CF Field: surface_air_pressure(106, 111)>
Transform['ptop'] = <CF Field: ptop>
Transform['sigma'] = 'dim0'

>>> cf.dump(s.transform['trans1'])
rotated_latitude_longitude transform
------------------------------------
Transform['grid_mapping_name'] = 'rotated_latitude_longitude'
Transform['grid_north_pole_latitude'] = 70.0
Transform['grid_north_pole_longitude'] = 100.0
Transform['north_pole_grid_longitude'] = -80.0

A transform may also be linked with any number of the space's
coordinates via their `transform` attributes:

>>> s['dim0'].transform
'trans0'
>>> s['dim2'].transform
'trans1'
>>> s['dim3'].transform
'trans1'
>>> s['aux0'].transform
'trans1'
>>> s['aux1'].transform
'trans1'


.. _Attributes:

Attributes
==========
:ref:`Table of contents <toc>`


Instance attributes
-------------------
:ref:`Table of contents <toc>`

Attributes may be set on a variable in the usual way, but these will
not be recognized as CF attributes. Some attributes, such as existing
methods, are reserved and may not be set:

>>> v.property = 999
>>> v.property
999
>>> v.match = 'test'
AttributeError

Refer to :class:`~cf.Variable` for details on reserved attributes and
methods.


Public attributes
-----------------
:ref:`Table of contents <toc>`

Public variable attributes are those which you would expect to read
from or be written to a CF variable on disk. They are set, retrieved
and deleted with the methods *setpub*, *getpub* and *delpub*
respectively, thereby allowing public attributes to share names with
reserved attribute names, instance attributes and private attributes:

>>> v.getpub('standard_name')
AttributeError: 'Variable' object doesn't have public attribute 'standard_name'
>>> v.setpub('standard_name', 'air_temperature')
>>> v.getpub('standard_name')
'air_temperature'
>>> v.delpub('standard_name')
>>> v.getpub('standard_name')
AttributeError: 'Variable' object doesn't have public attribute 'standard_name'
>>> v.getpub('standard_name', 'default_value')
'default_value'

For convenience, the variable's *pub* method provides a shorter way
of setting and retrieving public attributes which is exactly equivalent
to either the *setpub* or *getpub* methods:

>>> v.pub(standard_name='air_temperature')
>>> v.pub('standard_name')
'air_temperature'
>>> v.delpub('standard_name')
>>> v.pub('standard_name', 'default_value')
'default_value'

For further convenience, attributes with a special CF meaning for a
particular type of CF variable may be set, retrieved and deleted
directly on the variable itself, equivalently to using the *setpub*,
*getpub* or *delpub* methods:

>>> v.standard_name = 'air_pressure'
>>> v.standard_name
'air_pressure'
>>> del v.standard_name
>>> v.pub(standard_name='air_pressure')
>>> v.standard_name
'air_pressure'

In general, these attributes match those given in appendix A of the
`CF conventions
<http://cf-pcmdi.llnl.gov/documents/cf-conventions/latest-cf-conventions-document-1>`_,
with any deviations given in the documentation of the relevant classes
(:class:`.Variable`, :class:`.Field` or :class:`.Coordinate`).

Private attributes
------------------
:ref:`Table of contents <toc>`

Private attributes are those which are solely for convienience within
the API and are not to be written to a CF variable on disk. Similarly
to public attributes, they may be set, retrieved and deleted with the
methods *setpriv*, *getpriv* and *delpriv* respectively, thereby
allowing private attributes to share names with reserved attribute
names, instance attributes and public attributes:

>>> v.setpriv('file', '../file.nc')
>>> v.getpriv('file')
'../file.nc'
>>> v.delpriv('file')
>>> v.getpriv('file')
AttributeError: 'Variable' object doesn't have private attribute 'file'
>>> v.getpriv('file', 'default_value')
'default_value'

For convenience, the variable's *priv* method provides a shorter way
of setting and retrieving private attributes which is exactly
equivalent to either the *setpriv* or *getpriv* methods:

>>> v.priv(ncvar='data1')
>>> v.priv('ncvar')
'data1'
>>> v.delpriv('ncvar')
>>> v.priv('ncvar', 'default_value')
'default_value'

Variable lists
==============
:ref:`Table of contents <toc>`

The :class:`.Variable`, :class:`.Coordinate` and
:class:`.Field` objects have associated list objects which allow an
ordered sequence of instances to be stored as a single, list-like
object:

==========================  ========================
Class                       Associated list class
==========================  ========================
:class:`.Variable`          :class:`.VariableList`
:class:`.Coordinate`        :class:`.CoordinateList`
:class:`.Field`             :class:`.FieldList`
==========================  ========================

These list-like objects behave in much the same way as built-in lists:

>>> type(sl)
<class 'cf.field.FieldList'>
>>> sl
[<CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>,
 <CF Field: precipitation_flux(2400, 96, 192)>,
 <CF Field: air_temperature(2400, 96, 192)>]
>>> len(sl)
3

They are indexable in same way as built-in lists returning either list
or non-list types depending on the nature of the index:

>>> type(sl[0])
<class 'cf.field.Field'>
>>> sl[0]
<CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>

>>> type(sl[2::-2])
<class 'cf.field.FieldList'>
>>> sl[2::-2]
[<CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>]
>>> type(sl[slice(1,2)])
<class 'cf.field.FieldList'>
>>> sl[slice(1,2)]
[<CF Field: precipitation_flux(2400, 96, 192)>]

The usual built-in list methods work as expected. For example:

>>> print repr(sl.reverse()
[<CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: precipitation_flux(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>]
>>> sl.append(sl[0])
>>> sl
[<CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: precipitation_flux(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>,
 <CF Field: air_temperature(2400, 96, 192)>]
>>> len(sl)
4
>>> sl.insert(1, sl[2])
>>> sl
[<CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>,
 <CF Field: precipitation_flux(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>,
 <CF Field: air_temperature(2400, 96, 192)>]
>>> len(sl)
5
>>> sl.index(sl[2])
2
>>> sl[3] in sl
True
>>> sl.extend(sl[0:2])
>>> sl
[<CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>,
 <CF Field: precipitation_flux(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>,
 <CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>]
>>> len(sl)
7
>>> sl.pop()
<CF Field: toa_outgoing_longwave_flux_assuming_clear_sky(2400, 96, 192)>
>>> len(sl)
6
>>> sl.remove(sl[0])
>>> len(sl)
5

Non-list instances are also indexable as if they were single element
lists. Indexing them is essentially a null operation, but allows them
to be used interchangeably with their list counterparts. This is
particularly useful in iterative situations when it is not known if an
instance is a variable or its list counterpart:

>>> type(x)
<class 'cf.field.Field'>
>>> type(x[0])
<class 'cf.field.Field'>
>>> type(y)
<class 'cf.field.FieldList'>
>>> len(x), len(y)
(1, 3)
>>> for i in x:
...     print type(i), repr(i)
...
<class 'cf.field.Field'> <CF Field: air_density(2400, 96, 192)>
>>> for i in y:
...     print type(i), repr(i)
...
<class 'cf.field.Field'> <CF Field: air_pressure(2400, 96, 192)>
<class 'cf.field.Field'> <CF Field: air_temperature(2400, 96, 192)>
<class 'cf.field.Field'> <CF Field: precipitation_flux(2400, 96, 192)>

Attributes and methods of a variable are all available on its list
counterpart by broadcasting to each element of the list:

>>> sl.units
['Pa', 'K', 'kg m-2 s-1']
>>> sl[1].units
'K'
>>> sl.units[1]
'K'
>>> sl[1:2].units
['K']
>>> sl.new_attribute = 'test'
>>> sl.new_attribute
['test', 'test', 'test']
>>> s = sl[2]
>>> s.new_attribute
'test'

>>> sl.name()
['air_pressure',
 'air_temperature',
 'precipitation_flux'] 
>>> sl.__repr__()
[<CF Field: air_pressure(2400, 96, 192)>,
 <CF Field: air_temperature(2400, 96, 192)>,
 <CF Field: precipitation_flux(2400, 96, 192)>]
>>> sl.match(units='K')
[False, True, False]

Use the *pub* and *priv* (or *getpub* and *getpriv*) methods to return
defaults values for missing attributes:

>>> sl._FillValue
AttributeError: Can't get 'Field' attribute '_FillValue'
>>> sl.pub('_FillValue')
AttributeError: Can't get 'Field' attribute '_FillValue'
>>> sl.pub('_FillValue', None)
[None, None, None, -1.0e+30]


.. _Aggregation:

Aggregation
===========
:ref:`Table of contents <toc>`

Fields read from disk with the :func:`.read` function are, by default,
aggregated in to as few fields as possible according to the `CF
aggregation rules
<http://www.met.reading.ac.uk/~david/cf_aggregation_rules.html>`_. Fields
in memory may also be aggregated with the :func:`.aggregate` function.


 .. _Classes:

Classes
=======
:ref:`Table of contents <toc>`

==========================  =================================  ========================
Class                       Description                        Parent class   
==========================  =================================  ========================
:class:`.Variable`          *Abstract base class*              :class:`object`
:class:`.CfList`            *Abstract base class*              :class:`MutableSequence`
:class:`.CfDict`            *Abstract base class*              :class:`MutableMapping` 
:class:`.Field`             Field                              :class:`.Variable`
:class:`.Coordinate`        Dimension or auxiliary coordinate  :class:`.Variable`
:class:`.CoordinateBounds`  Coordinate bounds                  :class:`.Variable`
:class:`.CellMeasures`      Cell measures                      :class:`.Variable`
:class:`.Space`             Space                              :class:`.CfDict`     
:class:`.Transform`         Coordinate transforms              :class:`.CfDict`     
:class:`.CellMethods`       Cell methods                       :class:`.CfList`
:class:`.Units`             Comparison expression              :class:`object`         
:class:`.Data`              Comparison expression              :class:`object`         
:class:`.VariableList`      List of variables                  :class:`.CfList`     
:class:`.CoordinateList`    List of coordinates                :class:`.VariableList`
:class:`.FieldList`         List of fields                     :class:`.VariableList`
:class:`.Comparison`        Comparison expression              :class:`object`         
:class:`.Value`             Values with units                  :class:`object`         
==========================  =================================  ========================


Functions
=========
:ref:`Table of contents <toc>`

==============================  =========================================================
Function                        Description
==============================  =========================================================
:func:`.aggregate`              Aggregate fields into as few fields as possible.
:func:`.dump`                   Print the string returned from an object's *dump* method.
:func:`.eq`                     Create a :class:`.Comparison` object.     
:func:`.equals`                 Determine whether two objects are congruent.    
:func:`.ATOL`                   Absolute tolerance for numerical equality.
:func:`.RTOL`                   Relative tolerance for numerical equality.
:func:`.ge`                     Create a :class:`.Comparison` object.       
:func:`.gt`                     Create a :class:`.Comparison` object.    
:func:`.inside`                 Create a :class:`.Comparison` object.     
:func:`.le`                     Create a :class:`.Comparison` object.     
:func:`.lt`                     Create a :class:`.Comparison` object.     
:func:`.ne`                     Create a :class:`.Comparison` object.     
:func:`.outside`                Create a :class:`.Comparison` object.     
:func:`.read`                   Read fields from netCDF files.                          
:func:`.write`                  Write fields to a netCDF file.                        
==============================  =========================================================

'''

__Conventions__ = 'CF-1.5'
__author__      = 'David Hassell'
__date__        = '15 March 2012'
__version__     = '0.9.4'

import imp
import sys

# Check the version of python
if not 0x020600f0 <= sys.hexversion <= 0x020702f0:
    raise Exception, "cf will not work with this version of python: %s" % hex(sys.hexversion)

# Check that the dependencies are met
for _module in ('netCDF4', 'numpy'):
    try:
        imp.find_module(_module)
    except ImportError:
        raise ImportError, "Missing dependency: cf requires package '%(_module)s'" % \
            locals()
#--- End: for
del _module

from .field     import (Field, CellMethods, Space, Coordinate, 
                        CoordinateBounds, CellMeasures, Transform, 
                        Variable, VariableList, CoordinateList, FieldList)
from .read      import read
from .write     import write
from .utils     import (Comparison, CfList, CfDict,
                        lt, le, gt, ge, eq, ne, inside, outside, 
                        equals, RTOL, ATOL, close,
                        dump)
from .units     import Units
from .value     import Value
from .data      import Data
from .aggregate import aggregate
from .collapse  import collapse