Skip to content

Replace iris.co_realise_cubes with CubeList.realise_data. #3013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 22, 2018
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
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
* Added new function :func:`iris.co_realise_cubes` to compute multiple lazy
values in a single operation, avoiding repeated re-loading of data or
re-calculation of expressions.
* Added new function :func:`iris.cube.CubeList.realise_data` to compute
multiple lazy values in a single operation, avoiding repeated re-loading of
data or re-calculation of expressions.
3 changes: 1 addition & 2 deletions lib/iris/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ def callback(cube, field, filename):
from iris._deprecation import IrisDeprecation, warn_deprecated
import iris.fileformats
import iris.io
from iris._lazy_data import co_realise_cubes


try:
Expand All @@ -128,7 +127,7 @@ def callback(cube, field, filename):
__all__ = ['load', 'load_cube', 'load_cubes', 'load_raw',
'save', 'Constraint', 'AttributeConstraint', 'sample_data_path',
'site_configuration', 'Future', 'FUTURE',
'IrisDeprecation', 'co_realise_cubes']
'IrisDeprecation']


Constraint = iris._constraints.Constraint
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/_lazy_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def co_realise_cubes(*cubes):
std_err = (a_std * a_std + b_std * b_std) ** 0.5

# Compute stats together (to avoid multiple data passes).
iris.co_realise_cubes(a_std, b_std, ab_mean_diff, std_err)
co_realise_cubes(a_std, b_std, ab_mean_diff, std_err)


.. Note::
Expand Down
27 changes: 13 additions & 14 deletions lib/iris/coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@

from iris._data_manager import DataManager
from iris._deprecation import warn_deprecated
from iris._lazy_data import (as_concrete_data, is_lazy_data,
multidim_lazy_stack, lazy_elementwise)
import iris._lazy_data as _lazy
import iris.aux_factory
import iris.exceptions
import iris.time
Expand Down Expand Up @@ -554,7 +553,7 @@ def from_coord(cls, coord):
return cls(**kwargs)

def _sanitise_array(self, src, ndmin):
if is_lazy_data(src):
if _lazy.is_lazy_data(src):
# Lazy data : just ensure ndmin requirement.
ndims_missing = ndmin - src.ndim
if ndims_missing <= 0:
Expand Down Expand Up @@ -672,7 +671,7 @@ def core_points(self):

"""
result = self._points_dm.core_data()
if not is_lazy_data(result):
if not _lazy.is_lazy_data(result):
result = result.view()
return result

Expand All @@ -685,7 +684,7 @@ def core_bounds(self):
result = None
if self.has_bounds():
result = self._bounds_dm.core_data()
if not is_lazy_data(result):
if not _lazy.is_lazy_data(result):
result = result.view()
return result

Expand Down Expand Up @@ -925,15 +924,15 @@ def pointwise_convert(values):
return old_unit.convert(values, new_unit)

if self.has_lazy_points():
new_points = lazy_elementwise(self.lazy_points(),
pointwise_convert)
new_points = _lazy.lazy_elementwise(self.lazy_points(),
pointwise_convert)
else:
new_points = self.units.convert(self.points, unit)
self.points = new_points
if self.has_bounds():
if self.has_lazy_bounds():
new_bounds = lazy_elementwise(self.lazy_bounds(),
pointwise_convert)
new_bounds = _lazy.lazy_elementwise(self.lazy_bounds(),
pointwise_convert)
else:
new_bounds = self.units.convert(self.bounds, unit)
self.bounds = new_bounds
Expand Down Expand Up @@ -1230,8 +1229,8 @@ def serialize(x):
points = (float(lower) + float(upper)) * 0.5

# Create the new collapsed coordinate.
if is_lazy_data(item):
bounds = multidim_lazy_stack(bounds)
if _lazy.is_lazy_data(item):
bounds = _lazy.multidim_lazy_stack(bounds)
coord = self.copy(points=points, bounds=bounds)
else:
bounds = np.concatenate(bounds)
Expand Down Expand Up @@ -1753,7 +1752,7 @@ def _points_setter(self, points):
# DimCoord always realises the points, to allow monotonicity checks.
# Ensure it is an actual array, and also make our own copy so that we
# can make it read-only.
points = as_concrete_data(points)
points = _lazy.as_concrete_data(points)
points = np.array(points)

# Check validity requirements for dimension-coordinate points.
Expand Down Expand Up @@ -1812,7 +1811,7 @@ def _new_bounds_requirements(self, bounds):
def _bounds_setter(self, bounds):
if bounds is not None:
# Ensure we have a realised array of new bounds values.
bounds = as_concrete_data(bounds)
bounds = _lazy.as_concrete_data(bounds)
bounds = np.array(bounds)

# Check validity requirements for dimension-coordinate bounds.
Expand Down Expand Up @@ -1938,7 +1937,7 @@ def data(self, data):
if data is None:
raise ValueError('The data payload of a CellMeasure may not be '
'None; it must be a numpy array or equivalent.')
if is_lazy_data(data) and data.dtype.kind in 'biu':
if _lazy.is_lazy_data(data) and data.dtype.kind in 'biu':
# Non-floating cell measures are not valid up to CF v1.7
msg = ('Cannot create cell measure with lazy data of type {}, as '
'integer types are not currently supported.')
Expand Down
33 changes: 31 additions & 2 deletions lib/iris/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import six

import collections
import copy
from copy import deepcopy
import datetime
from functools import reduce
Expand All @@ -41,7 +42,7 @@
import iris._concatenate
import iris._constraints
from iris._data_manager import DataManager
from iris._lazy_data import lazy_elementwise
import iris._lazy_data as _lazy

import iris._merge
import iris.analysis
Expand Down Expand Up @@ -592,6 +593,33 @@ def concatenate(self, check_aux_coords=True):
return iris._concatenate.concatenate(self,
check_aux_coords=check_aux_coords)

def realise_data(self):
"""
Fetch 'real' data for all cubes, in a shared calculation.

This computes any lazy data, equivalent to accessing each `cube.data`.
However, lazy calculations and data fetches can be shared between the
computations, improving performance.

For example::

# Form stats.
a_std = cube_a.collapsed(['x', 'y'], iris.analysis.STD_DEV)
b_std = cube_b.collapsed(['x', 'y'], iris.analysis.STD_DEV)
ab_mean_diff = (cube_b - cube_a).collapsed(['x', 'y'],
iris.analysis.MEAN)
std_err = (a_std * a_std + b_std * b_std) ** 0.5

# Compute these stats together (avoiding multiple data passes).
CubeList([a_std, b_std, ab_mean_diff, std_err]).realise_data()

.. Note::

Cubes with non-lazy data are not affected.

"""
_lazy.co_realise_cubes(*self)


def _is_single_item(testee):
"""
Expand Down Expand Up @@ -883,7 +911,8 @@ def convert_units(self, unit):
def pointwise_convert(values):
return old_unit.convert(values, new_unit)

new_data = lazy_elementwise(self.lazy_data(), pointwise_convert)
new_data = _lazy.lazy_elementwise(self.lazy_data(),
pointwise_convert)
else:
new_data = self.units.convert(self.data, unit)
self.data = new_data
Expand Down
14 changes: 14 additions & 0 deletions lib/iris/tests/unit/cube/test_CubeList.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import iris.coord_systems
import iris.exceptions
from iris.fileformats.pp import STASH
from iris.tests import mock


class Test_concatenate_cube(tests.IrisTest):
Expand Down Expand Up @@ -301,5 +302,18 @@ def test_summary_stash(self):
self.assertEqual(str(self.cubes), expected)


class TestRealiseData(tests.IrisTest):
def test_realise_data(self):
# Simply check that calling CubeList.realise_data is calling
# _lazy_data.co_realise_cubes.
mock_cubes_list = [mock.Mock(ident=count) for count in range(3)]
test_cubelist = CubeList(mock_cubes_list)
call_patch = self.patch('iris._lazy_data.co_realise_cubes')
test_cubelist.realise_data()
# Check it was called once, passing cubes as *args.
self.assertEqual(call_patch.call_args_list,
[mock.call(*mock_cubes_list)])


if __name__ == "__main__":
tests.main()