Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ Incompatible changes
get_lat_lon_contiguous_grids(). They have been replaced with
generalised versions: xy_range(), get_xy_grids(),
and get_xy_contiguous_bounded_grids().
* Cube.coord_dims() now returns a tuple instead of a list.

Deprecations
------------
* The methods `Coord.cos()` and `Coord.sin()` have been deprecated.
* The function `load_strict()` function has been deprecated. Code should
now use `iris.load_cube()` and `iris.load_cubes()` instead.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stuff is already handled in the load API PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry already merged. Would you mind re-basing the load api PR and handling this conflict (and I will then merge).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rolls eyes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pelson Done.



----------------------------
Expand Down Expand Up @@ -114,7 +117,7 @@ Incompatible changes
Previously the signature was ``loader(filename, callback)`` and now looks like ``loader(filenames, callback)`` where ``filenames``
is an iterable of filenames.
* Custom aggregators should now create :class:`iris.analysis.Aggregator` instances rather than defining
a the partial creation of an Aggregate (i.e. ``partial(iris.analysis.Aggregate, ...)`` becomes ``iris.analsysis.Aggregator(...)``.
a the partial creation of an Aggregate (i.e. ``partial(iris.analysis.Aggregate, ...)`` becomes ``iris.analysis.Aggregator(...)``.
* PP STASH code ingestion is now stricter. In particular handling of partial stash codes (i.e. those without a model or section)
is no longer done, meaning that some cubes which previously had a standard name may no longer have a standard name.
Further work is planned to support field code translation which should provide more robust and accurate PP interpretations.
Expand Down Expand Up @@ -188,7 +191,7 @@ Release 0.5 (31 Oct, 2011)

* Added ability to derive area weights from a shapely geometry.

* Cubes can now be operated on (added, muliplied, etc.) using numpy arrays.
* Cubes can now be operated on (added, multiplied, etc.) using numpy arrays.

* New example of how to load custom ascii files into Cubes.

Expand Down
3 changes: 2 additions & 1 deletion docs/iris/src/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Incompatible changes
--------------------
* The "source" and "history" metadata are now represented as Cube
attributes, where previously they used coordinates.
* Cube.coord_dims() now returns a tuple instead of a list.

Deprecations
------------
Expand Down Expand Up @@ -196,7 +197,7 @@ Where previously it would have appeared as::
source: Data from Met Office Unified Model
...

.. note:: This change breaks backwards compatibiltiy with Iris 0.9. But
.. note:: This change breaks backwards compatibility with Iris 0.9. But
if it is desirable to have the "source" metadata expressed as a
coordinate then it can be done with the following pattern::

Expand Down
2 changes: 1 addition & 1 deletion lib/iris/analysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def coord_comparison(*cubes):

# get all coordinate groups which don't describe a dimension
# (None -> doesn't describe a dimension)
no_data_dim_fn = lambda cube, coord: cube.coord_dims(coord=coord) == []
no_data_dim_fn = lambda cube, coord: cube.coord_dims(coord=coord) == ()
if coord_group.matches_all(no_data_dim_fn):
no_data_dimension.add(coord_group)

Expand Down
53 changes: 28 additions & 25 deletions lib/iris/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def __init__(self, data, standard_name=None, long_name=None, units=None,
* dim_coords_and_dims
A list of coordinates with scalar dimension mappings, e.g ``[(lat_coord, 0), (lon_coord, 1)]``.
* aux_coords_and_dims
A list of coordinates with dimension mappings, e.g ``[(lat_coord, [0]), (lon_coord, [0,1])]``.
A list of coordinates with dimension mappings, e.g ``[(lat_coord, 0), (lon_coord, (0, 1))]``.
See also :meth:`Cube.add_dim_coord()<iris.cube.Cube.add_dim_coord>` and :meth:`Cube.add_aux_coord()<iris.cube.Cube.add_aux_coord>`.
* aux_factories
A list of auxiliary coordinate factories. See :mod:`iris.aux_factory`.
Expand Down Expand Up @@ -381,7 +381,7 @@ def add_aux_coord(self, coord, data_dims=None):
Kwargs:

* data_dims
Integer/list of integers giving the data dimensions spanned by the coordinate.
Integer or iterable of integers giving the data dimensions spanned by the coordinate.

Raises a ValueError if a coordinate with identical metadata already exists
on the cube.
Expand All @@ -390,14 +390,13 @@ def add_aux_coord(self, coord, data_dims=None):

"""

# Convert to a tuple of integers
if data_dims is None:
data_dims = []

# Convert to list of integers
if isinstance(data_dims, collections.Container):
data_dims = [int(d) for d in data_dims]
data_dims = tuple()
elif isinstance(data_dims, collections.Container):
data_dims = tuple(int(d) for d in data_dims)
else:
data_dims = [int(data_dims)]
data_dims = (int(data_dims),)

if data_dims:
if len(data_dims) != coord.ndim:
Expand Down Expand Up @@ -458,21 +457,22 @@ def add_dim_coord(self, dim_coord, data_dim=None):
if data_dim is None:
raise ValueError('You must supply a data dimension for a dimensioned coord.')
if isinstance(data_dim, collections.Container) and len(data_dim) != 1:
raise ValueError('The supplied data dimension must be a single number')
if self.coords(dimensions=data_dim, dim_coords=True):
raise ValueError('A dim_coord is already associated with dimension %d.' % data_dim)
raise ValueError('The supplied data dimension must be a single number')

#TODO tidy this up
# Convert data_dim to a single integer
if isinstance(data_dim, collections.Container):
data_dim = int(list(data_dim)[0])
else:
data_dim = int(data_dim)

# Check data_dim value is valid
if data_dim < 0 or data_dim >= self.ndim:
raise ValueError('The cube does not have the specified dimension (%d)' % data_dim)

# Check dimension is available
if self.coords(dimensions=data_dim, dim_coords=True):
raise ValueError('A dim_coord is already associated with dimension %d.' % data_dim)

# Check compatibility with the shape of the data
if dim_coord.shape[0] != self.shape[data_dim]:
msg = 'Unequal lengths. Cube dimension {} => {}; coord {!r} => {}.'
Expand Down Expand Up @@ -528,10 +528,12 @@ def replace_coord(self, new_coord):

def coord_dims(self, coord):
"""
Returns the list of data dimensions relevant to the given coordinate.
Returns a tuple of the data dimensions relevant to the given
coordinate.

When searching for the given coordinate in the cube the comparison is made using coordinate metadata
equality. Hence the given coordinate instance need not exist on the cube, and may contain different
When searching for the given coordinate in the cube the comparison is
made using coordinate metadata equality. Hence the given coordinate
instance need not exist on the cube, and may contain different
coordinate values.

Args:
Expand All @@ -545,7 +547,7 @@ def coord_dims(self, coord):
### Search by coord definition first

# Search dim coords first
matches = [[dim] for coord_, dim in self._dim_coords_and_dims if coord_._as_defn() == target_defn]
matches = [(dim,) for coord_, dim in self._dim_coords_and_dims if coord_._as_defn() == target_defn]

# Search aux coords
if not matches:
Expand All @@ -561,7 +563,7 @@ def coord_dims(self, coord):
# XXX Where did this come from? And why isn't it reflected in the docstring?

if not matches:
matches = [[dim] for coord_, dim in self._dim_coords_and_dims if coord_.name() == coord.name()]
matches = [(dim,) for coord_, dim in self._dim_coords_and_dims if coord_.name() == coord.name()]

# Search aux coords
if not matches:
Expand Down Expand Up @@ -653,8 +655,8 @@ def coords(self, name=None, standard_name=None, long_name=None, attributes=None,
* contains_dimension
The desired coordinate contains the data dimension. If None, does not check for the dimension.
* dimensions
The exact data dimensions of the desired coordinate. Coordinates with no data dimension can be found with an empty list (i.e. ``[]``).
If None, does not check for dimensions.
The exact data dimensions of the desired coordinate. Coordinates with no data dimension can be found with an
empty tuple or list (i.e. ``()`` or ``[]``). If None, does not check for dimensions.
* coord
Whether the desired coordinates have metadata equal to the given coordinate instance. If None, no check is done.
Accepts either a :class:'iris.coords.DimCoord`, :class:`iris.coords.AuxCoord` or :class:`iris.coords.CoordDefn`.
Expand Down Expand Up @@ -711,7 +713,7 @@ def coords(self, name=None, standard_name=None, long_name=None, attributes=None,
if dimensions is not None:
if not isinstance(dimensions, collections.Container):
dimensions = [dimensions]
coords_and_factories = filter(lambda coord_: dimensions == self.coord_dims(coord_), coords_and_factories)
coords_and_factories = filter(lambda coord_: tuple(dimensions) == self.coord_dims(coord_), coords_and_factories)

# If any factories remain after the above filters we have to make the coords so they can be returned
def extract_coord(coord_or_factory):
Expand Down Expand Up @@ -1441,9 +1443,10 @@ def remap_dim_coord(coord_and_dim):
return coord, dim_mapping[dim]
self._dim_coords_and_dims = map(remap_dim_coord, self._dim_coords_and_dims)

for coord, dims in self._aux_coords_and_dims:
for i, dim in enumerate(dims):
dims[i] = dim_mapping[dim]
def remap_aux_coord(coord_and_dims):
coord, dims = coord_and_dims
return coord, tuple(dim_mapping[dim] for dim in dims)
self._aux_coords_and_dims = map(remap_aux_coord, self._aux_coords_and_dims)

def xml(self, checksum=False):
"""
Expand Down Expand Up @@ -1488,7 +1491,7 @@ def _xml_element(self, doc, checksum=False):
cube_coord_xml_element = doc.createElement("coord")
coords_xml_element.appendChild(cube_coord_xml_element)

dims = self.coord_dims(coord)
dims = list(self.coord_dims(coord))
if dims:
cube_coord_xml_element.setAttribute("datadims", repr(dims))

Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/COLPEX/uwind_and_orog.cml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<attribute name="source" value="Data from Met Office Unified Model 7.04"/>
</attributes>
<coords>
<coord datadims="(1, 2, 3)">
<coord datadims="[1, 2, 3]">
<AuxCoord bounds="[[[[110.742, 115.678],
[136.728, 141.649],
[174.916, 179.815],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/cube_io/pickling/cubelist.cml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<attribute name="source" value="Data from Met Office Unified Model 7.04"/>
</attributes>
<coords>
<coord datadims="(1, 2, 3)">
<coord datadims="[1, 2, 3]">
<AuxCoord bounds="[[[[413.937, 426.634],
[417.817, 430.508],
[412.334, 425.034],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/cube_io/pickling/single_cube.cml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<attribute name="source" value="Data from Met Office Unified Model 7.04"/>
</attributes>
<coords>
<coord datadims="(1, 2, 3)">
<coord datadims="[1, 2, 3]">
<AuxCoord bounds="[[[[413.937, 426.634],
[417.817, 430.508],
[412.334, 425.034],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/derived/column.cml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="source" value="Iris test case"/>
</attributes>
<coords>
<coord datadims="(1,)">
<coord datadims="[1]">
<AuxCoord bounds="[[413.937, 426.634],
[426.634, 445.681],
[445.681, 471.079],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/derived/no_orog.cml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="source" value="Iris test case"/>
</attributes>
<coords>
<coord datadims="(1,)">
<coord datadims="[1]">
<AuxCoord bounds="[[0.0, 13.3333],
[13.3333, 33.3333],
[33.3333, 60.0],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/derived/removed_orog.cml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="source" value="Iris test case"/>
</attributes>
<coords>
<coord datadims="(1,)">
<coord datadims="[1]">
<AuxCoord bounds="[[0.0, 13.3333],
[13.3333, 33.3333],
[33.3333, 60.0],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/derived/removed_sigma.cml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="source" value="Iris test case"/>
</attributes>
<coords>
<coord datadims="(1, 2, 3)">
<coord datadims="[1, 2, 3]">
<AuxCoord bounds="[[[[0.0, 13.3333],
[0.0, 13.3333],
[0.0, 13.3333],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/derived/transposed.cml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="source" value="Iris test case"/>
</attributes>
<coords>
<coord datadims="(0, 1, 2)">
<coord datadims="[0, 1, 2]">
<AuxCoord bounds="[[[[413.937, 426.634],
[426.634, 445.681],
[445.681, 471.079],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<attribute name="source" value="Data from Met Office Unified Model 7.04"/>
</attributes>
<coords>
<coord datadims="(1, 2, 3)">
<coord datadims="[1, 2, 3]">
<AuxCoord bounds="[[[[99.1904, 112.371],
[122.346, 135.491],
[151.11, 164.211],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<attribute name="source" value="Data from Met Office Unified Model 7.04"/>
</attributes>
<coords>
<coord datadims="(1, 2, 3)">
<coord datadims="[1, 2, 3]">
<AuxCoord bounds="[[[[99.1904, 112.371],
[122.346, 135.491],
[151.11, 164.211],
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/stock/realistic_4d.cml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="source" value="Iris test case"/>
</attributes>
<coords>
<coord datadims="(1, 2, 3)">
<coord datadims="[1, 2, 3]">
<AuxCoord bounds="[[[[413.937, 426.634],
[417.817, 430.508],
[412.334, 425.034],
Expand Down
33 changes: 33 additions & 0 deletions lib/iris/tests/test_cdm.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,39 @@ def test_remove_coord(self):
self.cube.remove_coord('y')
self.assertEqual(self.cube.coords(), [])

def test_immutable_dimcoord_dims(self):
# Add DimCoord to dimension 1
dims = [1]
self.cube.add_dim_coord(self.x, dims)
self.assertEqual(self.cube.coord_dims(self.x), (1,))

# Change dims object
dims[0] = 0
# Check the cube is unchanged
self.assertEqual(self.cube.coord_dims(self.x), (1,))

# Check coord_dims cannot be changed
dims = self.cube.coord_dims(self.x)
with self.assertRaises(TypeError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would have been fine to simply do some type checking here (i.e. assert it is a tuple).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably the current check is better - it's testing the desired behaviour, and could conceivably survive a switch to an alternative immutable type.

dims[0] = 0

def test_immutable_auxcoord_dims(self):
# Add AuxCoord to dimensions (0, 1)
dims = [0, 1]
self.cube.add_aux_coord(self.xy, dims)
self.assertEqual(self.cube.coord_dims(self.xy), (0, 1))

# Change dims object
dims[0] = 1
dims[1] = 0
# Check the cube is unchanged
self.assertEqual(self.cube.coord_dims(self.xy), (0, 1))

# Check coord_dims cannot be changed
dims = self.cube.coord_dims(self.xy)
with self.assertRaises(TypeError):
dims[0] = 1


class TestStockCubeStringRepresentations(tests.IrisTest):
def setUp(self):
Expand Down