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
33 changes: 30 additions & 3 deletions lib/iris/_merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,13 @@ class _CoordSignature(
class _CubeSignature(
namedtuple(
"CubeSignature",
["defn", "data_shape", "data_type", "cell_measures_and_dims"],
[
"defn",
"data_shape",
"data_type",
"cell_measures_and_dims",
"ancillary_variables_and_dims",
],
)
):
"""
Expand All @@ -349,6 +355,9 @@ class _CubeSignature(
* cell_measures_and_dims:
A list of cell_measures and dims for the cube.

* ancillary_variables_and_dims:
A list of ancillary variables and dims for the cube.

"""

__slots__ = ()
Expand Down Expand Up @@ -439,8 +448,16 @@ def match(self, other, error_on_mismatch):
if self.data_type != other.data_type:
msg = "cube data dtype differs: {} != {}"
msgs.append(msg.format(self.data_type, other.data_type))
# Both cell_measures_and_dims and ancillary_variables_and_dims are
# ordered by the same method, it is therefore not possible for a
# mismatch to be caused by a difference in order.
if self.cell_measures_and_dims != other.cell_measures_and_dims:
msgs.append("cube.cell_measures differ")
if (
self.ancillary_variables_and_dims
!= other.ancillary_variables_and_dims
):
msgs.append("cube.ancillary_variables differ")

match = not bool(msgs)
if error_on_mismatch and not match:
Expand Down Expand Up @@ -1187,9 +1204,10 @@ def __init__(self, cube):
self._vector_dim_coords_dims = []
self._vector_aux_coords_dims = []

# cell measures are not merge candidates
# cell measures and ancillary variables are not merge candidates
# they are checked and preserved through merge
self._cell_measures_and_dims = cube._cell_measures_and_dims
self._ancillary_variables_and_dims = cube._ancillary_variables_and_dims

def _report_duplicate(self, nd_indexes, group_by_nd_index):
# Find the first offending source-cube with duplicate metadata.
Expand Down Expand Up @@ -1581,11 +1599,16 @@ def _get_cube(self, data):
cms_and_dims = [
(deepcopy(cm), dims) for cm, dims in self._cell_measures_and_dims
]
avs_and_dims = [
(deepcopy(av), dims)
for av, dims in self._ancillary_variables_and_dims
]
cube = iris.cube.Cube(
data,
dim_coords_and_dims=dim_coords_and_dims,
aux_coords_and_dims=aux_coords_and_dims,
cell_measures_and_dims=cms_and_dims,
ancillary_variables_and_dims=avs_and_dims,
**kwargs,
)

Expand Down Expand Up @@ -1717,7 +1740,11 @@ def _build_signature(self, cube):
"""

return _CubeSignature(
cube.metadata, cube.shape, cube.dtype, cube._cell_measures_and_dims
cube.metadata,
cube.shape,
cube.dtype,
cube._cell_measures_and_dims,
cube._ancillary_variables_and_dims,
)

def _add_cube(self, cube, coord_payload):
Expand Down
76 changes: 76 additions & 0 deletions lib/iris/tests/test_merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,5 +1033,81 @@ def test_simple_points_merge(self):
self.assertCML(r, ("cube_merge", "test_simple_attributes3.cml"))


class TestCubeMergeWithAncils(tests.IrisTest):
def _makecube(self, y, cm=False, av=False):
cube = iris.cube.Cube([0, 0])
cube.add_dim_coord(iris.coords.DimCoord([0, 1], long_name="x"), 0)
cube.add_aux_coord(iris.coords.DimCoord(y, long_name="y"))
if cm:
cube.add_cell_measure(
iris.coords.CellMeasure([1, 1], long_name="foo"), 0,
)
if av:
cube.add_ancillary_variable(
iris.coords.AncillaryVariable([1, 1], long_name="bar"), 0,
)
return cube

def test_fail_missing_cell_measure(self):
cube1 = self._makecube(0, cm=True)
cube2 = self._makecube(1)
cubes = iris.cube.CubeList([cube1, cube2]).merge()
self.assertEqual(len(cubes), 2)

def test_fail_missing_ancillary_variable(self):
cube1 = self._makecube(0, av=True)
cube2 = self._makecube(1)
cubes = iris.cube.CubeList([cube1, cube2]).merge()
self.assertEqual(len(cubes), 2)

def test_fail_different_cell_measure(self):
cube1 = self._makecube(0, cm=True)
cube2 = self._makecube(1)
cube2.add_cell_measure(
iris.coords.CellMeasure([2, 2], long_name="foo"), 0
)
cubes = iris.cube.CubeList([cube1, cube2]).merge()
self.assertEqual(len(cubes), 2)

def test_fail_different_ancillary_variable(self):
cube1 = self._makecube(0, av=True)
cube2 = self._makecube(1)
cube2.add_ancillary_variable(
iris.coords.AncillaryVariable([2, 2], long_name="bar"), 0
)
cubes = iris.cube.CubeList([cube1, cube2]).merge()
self.assertEqual(len(cubes), 2)

def test_merge_with_cell_measure(self):
cube1 = self._makecube(0, cm=True)
cube2 = self._makecube(1, cm=True)
cubes = iris.cube.CubeList([cube1, cube2]).merge()
self.assertEqual(len(cubes), 1)
self.assertEqual(cube1.cell_measures(), cubes[0].cell_measures())

def test_merge_with_ancillary_variable(self):
cube1 = self._makecube(0, av=True)
cube2 = self._makecube(1, av=True)
cubes = iris.cube.CubeList([cube1, cube2]).merge()
self.assertEqual(len(cubes), 1)
self.assertEqual(
cube1.ancillary_variables(), cubes[0].ancillary_variables()
)

def test_cell_measure_error_msg(self):
msg = "cube.cell_measures differ"
cube1 = self._makecube(0, cm=True)
cube2 = self._makecube(1)
with self.assertRaisesRegex(iris.exceptions.MergeError, msg):
_ = iris.cube.CubeList([cube1, cube2]).merge_cube()

def test_ancillary_variable_error_msg(self):
msg = "cube.ancillary_variables differ"
cube1 = self._makecube(0, av=True)
cube2 = self._makecube(1)
with self.assertRaisesRegex(iris.exceptions.MergeError, msg):
_ = iris.cube.CubeList([cube1, cube2]).merge_cube()


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