Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/7125.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:user:`gaoflow` fixed :meth:`iris.cube.CubeList.concatenate` so that cubes
with string coordinates of differing width, such as dtypes ``<U1`` and
``<U5``, can be concatenated. The result coordinate is promoted to the wider
dtype. (:issue:`6676`)
5 changes: 5 additions & 0 deletions lib/iris/_concatenate.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ def __new__(mcs, coord, dims):
bounds_dtype = (
coord.core_bounds().dtype if coord.core_bounds() is not None else None
)
# Ignore string width; joined coordinates promote to a common width.
if points_dtype.kind in ("U", "S"):
points_dtype = np.dtype(points_dtype.kind)
if bounds_dtype is not None and bounds_dtype.kind in ("U", "S"):
bounds_dtype = np.dtype(bounds_dtype.kind)
kwargs = {}
# Add scalar flag metadata.
kwargs["scalar"] = coord.core_points().size == 1
Expand Down
33 changes: 33 additions & 0 deletions lib/iris/tests/unit/concatenate/test_concatenate.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,39 @@ def test_desc_bounds_all_singleton(self):
assert result1 == result2


class TestStringAuxCoordWidths:
# Cubes with string auxiliary coordinates of differing width (dtype) should
# still concatenate, with the result promoted to the wider dtype (#6676).
def _make_cube(self, dim_points, aux_points):
data = np.arange(len(dim_points))
cube = iris.cube.Cube(data, long_name="test")
cube.add_dim_coord(iris.coords.DimCoord(dim_points, long_name="dim"), 0)
cube.add_aux_coord(
iris.coords.AuxCoord(np.array(aux_points), long_name="example"), 0
)
return cube

def test_different_widths(self):
cube_a = self._make_cube([0, 1], ["1", "2"])
cube_b = self._make_cube([10, 11, 12], ["1", "123", "12345"])
assert cube_a.coord("example").dtype != cube_b.coord("example").dtype

(result,) = concatenate([cube_a, cube_b], True)

coord = result.coord("example")
assert coord.dtype == np.dtype("<U5")
np.testing.assert_array_equal(coord.points, ["1", "2", "1", "123", "12345"])

def test_different_dtype_kind_still_rejected(self):
# A genuine dtype-kind difference (string vs integer) must still block
# concatenation.
cube_a = self._make_cube([0, 1], ["1", "2"])
cube_b = self._make_cube([2, 3], [1, 2])

with pytest.raises(ConcatenateError):
_ = concatenate([cube_a, cube_b], True)


class TestConcatenate__dask:
@pytest.fixture
def sample_lazy_cubes(self):
Expand Down
Loading