diff --git a/changelog/7253.bugfix.rst b/changelog/7253.bugfix.rst new file mode 100644 index 0000000000..935fcec87c --- /dev/null +++ b/changelog/7253.bugfix.rst @@ -0,0 +1,2 @@ +:user:`stephenworsley` fixed a bug which caused indexing to fail when +``start_index`` was 1. (:issue:`7246`) \ No newline at end of file diff --git a/lib/iris/mesh/components.py b/lib/iris/mesh/components.py index 2f888da3a2..960d991a2a 100644 --- a/lib/iris/mesh/components.py +++ b/lib/iris/mesh/components.py @@ -3132,6 +3132,7 @@ def indexed( # used by MeshCoord, which also maintains laziness. indices = connectivity.lazy_indices() indices_indexed = connectivity.indices_by_location(indices)[indexing] + indices_indexed -= connectivity.start_index # Map node indices in "values" to their new zero-based positions # via the inverse-lookup table. This creates a contiguous @@ -3524,6 +3525,7 @@ def _calculate_node_bool_index(self): conn_indices = connectivity.indices_by_location( connectivity.core_indices() )[self.indices] + conn_indices -= connectivity.start_index al = da if _lazy.is_lazy_data(conn_indices) else np # Flatten and drop masked padding (ragged connectivities) by scattering # membership into a fixed-shape boolean mask. diff --git a/lib/iris/tests/integration/mesh/test_indexing_meshes.py b/lib/iris/tests/integration/mesh/test_indexing_meshes.py index 38d1effbbe..fdc06e4e3a 100644 --- a/lib/iris/tests/integration/mesh/test_indexing_meshes.py +++ b/lib/iris/tests/integration/mesh/test_indexing_meshes.py @@ -3,6 +3,7 @@ # This file is part of Iris and is released under the BSD license. # See LICENSE in the root of the repository for full licensing details. +import numpy as np import pytest from iris.coords import AuxCoord, Coord @@ -47,6 +48,17 @@ def cube_mesh_face(): return (sample_mesh_cube(location=location, mesh=mesh), location) +@pytest.fixture +def cube_mesh_1_indexed(cube_mesh_from_file): + cube = cube_mesh_from_file[0] + mesh = cube.mesh + for con in mesh.all_connectivities: + if con is not None: + con.indices[:] += 1 + con._metadata_manager.start_index = 1 + return (cube, "face") + + def change_and_assert_no_change_in_right(left: Coord, right: Coord, value: int): left.points[0] = value assert right.points[0] != value @@ -187,3 +199,34 @@ def change_original_mesh_reflected_in_index_set( change_original_mesh_reflected_in_index_set( cube, indexed_cube, "longitude", location, value ) + + +@pytest.mark.parametrize( + "fixture", + ["cube_mesh_from_file", "cube_mesh_edge", "cube_mesh_face", "cube_mesh_1_indexed"], +) +def test_indexing_mode_equivalency(fixture, request): + cube: Cube + location: str + (cube, location) = request.getfixturevalue(fixture) + + with mesh_coord_indexing.SETTING.context(mesh_coord_indexing.Options.AUX_COORD): + indexed_cube_aux = cube[0, 1:-1] + with mesh_coord_indexing.SETTING.context(mesh_coord_indexing.Options.NEW_MESH): + indexed_cube_mesh = cube[0, 1:-1] + with mesh_coord_indexing.SETTING.context( + mesh_coord_indexing.Options.MESH_INDEX_SET + ): + indexed_cube_mis = cube[0, 1:-1] + + aux_lon = indexed_cube_aux.coord("longitude") + aux_lat = indexed_cube_aux.coord("latitude") + mesh_lon = indexed_cube_mesh.coord("longitude") + mesh_lat = indexed_cube_mesh.coord("latitude") + mis_lon = indexed_cube_mis.coord("longitude") + mis_lat = indexed_cube_mis.coord("latitude") + + assert np.array_equal(aux_lon.bounds, mesh_lon.bounds) + assert np.array_equal(aux_lat.bounds, mesh_lat.bounds) + assert np.array_equal(aux_lon.bounds, mis_lon.bounds) + assert np.array_equal(aux_lat.bounds, mis_lat.bounds) diff --git a/lib/iris/tests/unit/mesh/components/test_MeshIndexSet.py b/lib/iris/tests/unit/mesh/components/test_MeshIndexSet.py index 02184f31fd..be33cb5f28 100644 --- a/lib/iris/tests/unit/mesh/components/test_MeshIndexSet.py +++ b/lib/iris/tests/unit/mesh/components/test_MeshIndexSet.py @@ -513,12 +513,27 @@ def varied_mesh(self, lazy_values): ) return mesh - def test_varied_faces(self, varied_mesh): + @pytest.fixture + def varied_mesh_1_indexed(self, varied_mesh): mesh = varied_mesh + for con in mesh.all_connectivities: + if con is not None: + con.indices[:] += 1 + con._metadata_manager.start_index = 1 + return mesh + + @pytest.mark.parametrize( + "fixture", + ["varied_mesh", "varied_mesh_1_indexed"], + ) + def test_varied_faces(self, fixture, request): + mesh: MeshXY = request.getfixturevalue(fixture) index_set = _MeshIndexSet(indices=[0, 1, 3], mesh=mesh, location="face") _shared_utils.assert_array_equal(index_set.indices, np.array([0, 1, 3])) + connectivity = index_set.connectivity(cf_role="face_node_connectivity") + normalised_indices = connectivity.indices - connectivity.start_index _shared_utils.assert_array_equal( - index_set.connectivity(cf_role="face_node_connectivity").indices, + normalised_indices, np.ma.masked_array( data=[[7, 8, 10, 9, -1], [3, 7, 6, -1, -1], [0, 1, 2, 5, 4]], mask=[[0, 0, 0, 0, 1], [0, 0, 0, 1, 1], [0, 0, 0, 0, 0]], @@ -532,12 +547,18 @@ def test_varied_faces(self, varied_mesh): np.array([-2, -3, -4, -1, -3, -4, -1, -2, -3, -2, -3]), ) - def test_varied_edges(self, varied_mesh): - mesh = varied_mesh + @pytest.mark.parametrize( + "fixture", + ["varied_mesh", "varied_mesh_1_indexed"], + ) + def test_varied_edges(self, fixture, request): + mesh: MeshXY = request.getfixturevalue(fixture) index_set = _MeshIndexSet(indices=[0, 1, 4], mesh=mesh, location="edge") _shared_utils.assert_array_equal(index_set.indices, np.array([0, 1, 4])) + connectivity = index_set.connectivity(cf_role="edge_node_connectivity") + normalised_indices = connectivity.indices - connectivity.start_index _shared_utils.assert_array_equal( - index_set.connectivity(cf_role="edge_node_connectivity").indices, + normalised_indices, # Note the shared node. np.array([[0, 1], [1, 2], [3, 4]]), )