diff --git a/docs/iris/src/whatsnew/3.0.2.rst b/docs/iris/src/whatsnew/3.0.2.rst index 90ce970326..769b673a4c 100644 --- a/docs/iris/src/whatsnew/3.0.2.rst +++ b/docs/iris/src/whatsnew/3.0.2.rst @@ -21,6 +21,10 @@ This document explains the changes made to Iris for this release #. `@jonseddon`_ handled a malformed ``um_stash_source`` CF variable attribute in a netCDF file rather than raising a ``ValueError``. (:pull:`4035`) + #. `@rcomer`_ fixed :meth:`~iris.cube.Cube.intersection` for special cases + where one cell's bounds align with the requested maximum and minimum, as + reported in :issue:`3391`. (:pull:`4059`) + 📚 **Documentation** #. `@bjlittle`_ updated the ``intersphinx_mapping`` and fixed documentation diff --git a/lib/iris/cube.py b/lib/iris/cube.py index f280891fde..129542fb21 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -3185,18 +3185,22 @@ def _intersect_modulus( pre_wrap_delta != post_wrap_delta ) - # Recalculate the extended minimum. indices = inside_indices[split_cell_indices] cells = bounds[indices] - cells_delta = np.diff(coord.bounds[indices]) - - # Watch out for ascending/descending bounds - if cells_delta[0, 0] > 0: - cells[:, 0] = cells[:, 1] - cells_delta[:, 0] - minimum = np.min(cells[:, 0]) - else: - cells[:, 1] = cells[:, 0] + cells_delta[:, 0] - minimum = np.min(cells[:, 1]) + if maximum % modulus not in cells: + # Recalculate the extended minimum only if the output bounds + # do not span the requested (minumum, maximum) range. If + # they do span that range, this adjustment would give unexpected + # results (see #3391). + cells_delta = np.diff(coord.bounds[indices]) + + # Watch out for ascending/descending bounds. + if cells_delta[0, 0] > 0: + cells[:, 0] = cells[:, 1] - cells_delta[:, 0] + minimum = np.min(cells[:, 0]) + else: + cells[:, 1] = cells[:, 0] + cells_delta[:, 0] + minimum = np.min(cells[:, 1]) points = wrap_lons(coord.points, minimum, modulus) diff --git a/lib/iris/tests/unit/cube/test_Cube.py b/lib/iris/tests/unit/cube/test_Cube.py index 9fe90f5a4e..fbf76b4260 100644 --- a/lib/iris/tests/unit/cube/test_Cube.py +++ b/lib/iris/tests/unit/cube/test_Cube.py @@ -1731,6 +1731,14 @@ def test_aligned_exclusive(self): self.assertEqual(result.data[0, 0, 0], 171) self.assertEqual(result.data[0, 0, -1], 189) + def test_aligned_bounds_at_modulus(self): + cube = create_cube(-179.5, 180.5, bounds=True) + result = cube.intersection(longitude=(0, 360)) + self.assertArrayEqual(result.coord("longitude").bounds[0], [0, 1]) + self.assertArrayEqual(result.coord("longitude").bounds[-1], [359, 360]) + self.assertEqual(result.data[0, 0, 0], 180) + self.assertEqual(result.data[0, 0, -1], 179) + def test_negative_misaligned_points_inside(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(-10.25, 10.25))