From 8e79cebb34b070add0e607d2b23996cb202e9286 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Mon, 1 Jun 2026 14:24:24 +0200 Subject: [PATCH] Copy target coordinates onto regridded result cube _create_cube placed the target grid's horizontal coordinate objects directly onto the result cube, so the result and the target shared the same coordinate objects: mutating one mutated the other. The docstring already states these coordinates are 'copied from the target cube', and source coordinates are likewise copied, so copy the target coordinates too. This makes all regridders that use _create_cube consistent. Fixes #6844. --- docs/src/whatsnew/latest.rst | 5 ++++- lib/iris/analysis/_regrid.py | 4 ++++ .../area_weighted/test_AreaWeightedRegridder.py | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 825508dca3..eb5bdab05b 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -36,7 +36,10 @@ This document explains the changes made to Iris for this release 🐛 Bugs Fixed ============= -#. N/A +#. :user:`gaoflow` fixed regridding so that the horizontal coordinates of the + result are copies of, rather than references to, the target grid's + coordinates. Previously, modifying a result coordinate would also modify the + target cube's coordinate (and vice versa). (:issue:`6844`) 💣 Incompatible Changes diff --git a/lib/iris/analysis/_regrid.py b/lib/iris/analysis/_regrid.py index 23d25ecc42..497ecaca39 100644 --- a/lib/iris/analysis/_regrid.py +++ b/lib/iris/analysis/_regrid.py @@ -1040,6 +1040,10 @@ def _create_cube(data, src, src_dims, tgt_coords, num_tgt_dims, regrid_callback) if num_tgt_dims == 1: grid_dim_x = grid_dim_y = min(src_dims) for tgt_coord, dim in zip(tgt_coords, (grid_dim_x, grid_dim_y)): + # Copy so that the result does not share coordinate objects with the + # target grid (mutating one would otherwise mutate the other). See + # #6844. + tgt_coord = tgt_coord.copy() if len(tgt_coord.shape) == 1: if isinstance(tgt_coord, DimCoord) and dim is not None: result.add_dim_coord(tgt_coord, dim) diff --git a/lib/iris/tests/unit/analysis/area_weighted/test_AreaWeightedRegridder.py b/lib/iris/tests/unit/analysis/area_weighted/test_AreaWeightedRegridder.py index c7208a841c..d06543f1a6 100644 --- a/lib/iris/tests/unit/analysis/area_weighted/test_AreaWeightedRegridder.py +++ b/lib/iris/tests/unit/analysis/area_weighted/test_AreaWeightedRegridder.py @@ -122,6 +122,22 @@ def test_src_and_target_are_the_same(self): result = regridder(src) _shared_utils.assert_array_all_close(result.data, target.data) + def test_result_coords_independent_of_target(self): + # The horizontal coordinates of the result must be copies, not the + # same objects as the target's: mutating one must not mutate the + # other (#6844). + src, target = self.grids() + regridder = AreaWeightedRegridder(src, target) + result = regridder(src) + for name in ("latitude", "longitude"): + result_coord = result.coord(name) + target_coord = target.coord(name) + assert result_coord == target_coord + assert result_coord is not target_coord + original = target_coord.points.copy() + result_coord.points = result_coord.points + 100 + _shared_utils.assert_array_equal(target.coord(name).points, original) + def test_multiple_src_on_same_grid(self): coord_names = ["latitude", "longitude"] src1 = self.cube(np.linspace(20, 32, 4), np.linspace(10, 22, 4))