diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 8b986e0ac3..baf9f4d30d 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -62,6 +62,10 @@ This document explains the changes made to Iris for this release #. `@gcaria`_ fixed :meth:`~iris.cube.Cube.ancillary_variable_dims` to also accept the string name of a :class:`~iris.coords.AncillaryVariable`. (:pull:`3931`) +#. `@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:`4046`) + 💣 Incompatible Changes ======================= diff --git a/lib/iris/cube.py b/lib/iris/cube.py index a15951900b..a8c202a21f 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -3186,18 +3186,19 @@ 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. + 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 ded401cab3..3efc6a93f8 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))