diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index fb8098f766..3b7b0e92a9 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -30,7 +30,8 @@ This document explains the changes made to Iris for this release ✨ Features =========== -#. N/A +#. `@ESadek-MO`_ updated the error messages in :meth:`iris.cube.CubeList.concatenate` + to better explain the error. (:pull:`6005`) 🐛 Bugs Fixed diff --git a/lib/iris/_concatenate.py b/lib/iris/_concatenate.py index b25698da89..927ee9989c 100644 --- a/lib/iris/_concatenate.py +++ b/lib/iris/_concatenate.py @@ -893,6 +893,7 @@ def register( # Check for compatible cube signatures. cube_signature = _CubeSignature(cube) match = self._cube_signature.match(cube_signature, error_on_mismatch) + mismatch_error_msg = None # Check for compatible coordinate signatures. if match: @@ -901,17 +902,20 @@ def register( match = candidate_axis is not None and ( candidate_axis == axis or axis is None ) + if not match: + mismatch_error_msg = ( + f"Cannot find an axis to concatenate over for phenomenon " + f"`{self._cube.name()}`" + ) # Check for compatible coordinate extents. if match: dim_ind = self._coord_signature.dim_mapping.index(candidate_axis) match = self._sequence(coord_signature.dim_extents[dim_ind], candidate_axis) if error_on_mismatch and not match: - msg = f"Found cubes with overlap on concatenate axis {candidate_axis}, cannot concatenate overlapping cubes" - raise iris.exceptions.ConcatenateError([msg]) + mismatch_error_msg = f"Found cubes with overlap on concatenate axis {candidate_axis}, cannot concatenate overlapping cubes" elif not match: - msg = f"Found cubes with overlap on concatenate axis {candidate_axis}, skipping concatenation for these cubes" - warnings.warn(msg, category=iris.warnings.IrisUserWarning) + mismatch_error_msg = f"Found cubes with overlap on concatenate axis {candidate_axis}, skipping concatenation for these cubes" # Check for compatible AuxCoords. if match: @@ -926,6 +930,12 @@ def register( or candidate_axis not in coord_b.dims ): if not coord_a == coord_b: + mismatch_error_msg = ( + "Auxiliary coordinates are unequal for phenomenon" + f" `{self._cube.name()}`:\n" + f"a: {coord_a}\n" + f"b: {coord_b}" + ) match = False # Check for compatible CellMeasures. @@ -941,6 +951,12 @@ def register( or candidate_axis not in coord_b.dims ): if not coord_a == coord_b: + mismatch_error_msg = ( + "Cell measures are unequal for phenomenon" + f" `{self._cube.name()}`:\n" + f"a: {coord_a}\n" + f"b: {coord_b}" + ) match = False # Check for compatible AncillaryVariables. @@ -956,6 +972,12 @@ def register( or candidate_axis not in coord_b.dims ): if not coord_a == coord_b: + mismatch_error_msg = ( + "Ancillary variables are unequal for phenomenon" + f" `{self._cube.name()}`:\n" + f"a: {coord_a}\n" + f"b: {coord_b}" + ) match = False # Check for compatible derived coordinates. @@ -971,6 +993,12 @@ def register( or candidate_axis not in coord_b.dims ): if not coord_a == coord_b: + mismatch_error_msg = ( + "Derived coordinates are unequal for phenomenon" + f" `{self._cube.name()}`:\n" + f"a: {coord_a}\n" + f"b: {coord_b}" + ) match = False if match: @@ -991,6 +1019,14 @@ def register( if existing_order == _CONSTANT and this_order != _CONSTANT: self._coord_signature.dim_order[dim_ind] = this_order + if mismatch_error_msg and not match: + if error_on_mismatch: + raise iris.exceptions.ConcatenateError([mismatch_error_msg]) + else: + warnings.warn( + mismatch_error_msg, category=iris.warnings.IrisUserWarning + ) + return match def _add_skeleton(self, coord_signature, data): diff --git a/lib/iris/tests/unit/concatenate/test_concatenate.py b/lib/iris/tests/unit/concatenate/test_concatenate.py index 34db2b02f1..59312a542d 100644 --- a/lib/iris/tests/unit/concatenate/test_concatenate.py +++ b/lib/iris/tests/unit/concatenate/test_concatenate.py @@ -52,7 +52,7 @@ def test_concat_1d_with_same_time_units(self): self.assertEqual(result[0].shape, (10,)) -class TestMessages(tests.IrisTest): +class _MessagesMixin(tests.IrisTest): def setUp(self): data = np.arange(24, dtype=np.float32).reshape(2, 3, 4) cube = iris.cube.Cube(data, standard_name="air_temperature", units="K") @@ -108,6 +108,18 @@ def setUp(self): cube.add_aux_factory(HybridHeightFactory(delta, sigma, orog)) self.cube = cube + +class TestMessages(_MessagesMixin): + def setUp(self): + super().setUp() + + def test_dim_coords_same_message(self): + cube_1 = self.cube + cube_2 = cube_1.copy() + exc_regexp = "Cannot find an axis to concatenate over for phenomenon *" + with self.assertRaisesRegex(ConcatenateError, exc_regexp): + _ = concatenate([cube_1, cube_2], True) + def test_definition_difference_message(self): cube_1 = self.cube cube_2 = cube_1.copy() @@ -246,6 +258,50 @@ def test_dim_coords_overlap_message(self): _ = concatenate([cube_1, cube_2], True) +class TestNonMetadataMessages(_MessagesMixin): + def setUp(self): + super().setUp() + cube_2 = self.cube.copy() + cube_2.coord("time").points = cube_2.coord("time").points + 2 + self.cube_2 = cube_2 + + def test_aux_coords_diff_message(self): + self.cube_2.coord("foo").points = [3, 4, 5] + + exc_regexp = "Auxiliary coordinates are unequal for phenomenon * " + with self.assertRaisesRegex(ConcatenateError, exc_regexp): + _ = concatenate([self.cube, self.cube_2], True) + with self.assertWarnsRegex(iris.warnings.IrisUserWarning, exc_regexp): + _ = concatenate([self.cube, self.cube_2], False) + + def test_cell_measures_diff_message(self): + self.cube_2.cell_measure("bar").data = [3, 4, 5] + + exc_regexp = "Cell measures are unequal for phenomenon * " + with self.assertRaisesRegex(ConcatenateError, exc_regexp): + _ = concatenate([self.cube, self.cube_2], True) + with self.assertWarnsRegex(iris.warnings.IrisUserWarning, exc_regexp): + _ = concatenate([self.cube, self.cube_2], False) + + def test_ancillary_variable_diff_message(self): + self.cube_2.ancillary_variable("baz").data = [3, 4, 5] + + exc_regexp = "Ancillary variables are unequal for phenomenon * " + with self.assertRaisesRegex(ConcatenateError, exc_regexp): + _ = concatenate([self.cube, self.cube_2], True) + with self.assertWarnsRegex(iris.warnings.IrisUserWarning, exc_regexp): + _ = concatenate([self.cube, self.cube_2], False) + + def test_derived_coords_diff_message(self): + self.cube_2.aux_factories[0].update(self.cube_2.coord("sigma"), None) + + exc_regexp = "Derived coordinates are unequal for phenomenon * " + with self.assertRaisesRegex(ConcatenateError, exc_regexp): + _ = concatenate([self.cube, self.cube_2], True) + with self.assertWarnsRegex(iris.warnings.IrisUserWarning, exc_regexp): + _ = concatenate([self.cube, self.cube_2], False) + + class TestOrder(tests.IrisTest): def _make_cube(self, points, bounds=None): nx = 4