Skip to content

Commit

Permalink
Added more descriptive errors within concatenate (#6005)
Browse files Browse the repository at this point in the history
* added error messages appropriately

* added tests and fixed typos

* whatsnew

* review comments

---------

Co-authored-by: Martin Yeo <[email protected]>
  • Loading branch information
ESadek-MO and trexfeathers committed Jun 14, 2024
1 parent e92e36a commit 998a568
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 6 deletions.
3 changes: 2 additions & 1 deletion docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 40 additions & 4 deletions lib/iris/_concatenate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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:
Expand All @@ -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):
Expand Down
58 changes: 57 additions & 1 deletion lib/iris/tests/unit/concatenate/test_concatenate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 998a568

Please sign in to comment.