From 4676c3192b4411ee6795e26ed2b4ba9c9737440e Mon Sep 17 00:00:00 2001 From: Bill Senior Date: Tue, 11 Nov 2025 17:34:36 +0100 Subject: [PATCH 01/14] GRIDEDIT-2042 Updated comment --- meshkernel/meshkernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index 511c6796..6c6c0dfa 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -286,7 +286,7 @@ def mesh2d_delete_faces_in_polygons( self, geometry_list: GeometryList, ) -> None: - """Deletes a faces of the mesh that lie inside a polygon or polygons. + """Deletes the faces of the mesh that lie inside a polygon or polygons. Args: geometry_list (GeometryList): The GeometryList describing the polygon where to perform the deletion. From 596ac92c72ba1bc73c0dbd700b5dab050053f33b Mon Sep 17 00:00:00 2001 From: BillSenior Date: Thu, 13 Nov 2025 15:42:27 +0100 Subject: [PATCH 02/14] GRIDEDIT-2042 Use mesh.nodes_per_face instead of mesh.face_x when setting mesh.num_faces. This is then consistent with mesh.face_nodes array (both will be set or both will be null) --- meshkernel/c_structures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshkernel/c_structures.py b/meshkernel/c_structures.py index 9b74f128..742e542b 100644 --- a/meshkernel/c_structures.py +++ b/meshkernel/c_structures.py @@ -111,7 +111,7 @@ def from_mesh2d(mesh2d: Mesh2d) -> CMesh2d: # Set the sizes c_mesh2d.num_nodes = mesh2d.node_x.size c_mesh2d.num_edges = mesh2d.edge_nodes.size // 2 - c_mesh2d.num_faces = mesh2d.face_x.size + c_mesh2d.num_faces = mesh2d.nodes_per_face.size c_mesh2d.num_face_nodes = mesh2d.face_nodes.size return c_mesh2d From ddb57027a3fc03bd35730f0ffcfb0d7fb7d1086d Mon Sep 17 00:00:00 2001 From: veenstrajelmer Date: Thu, 13 Nov 2025 16:29:40 +0100 Subject: [PATCH 03/14] add testcase for mesh2d_set including holes/face_node_connectivity --- tests/test_mesh2d_basics.py | 65 +++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/test_mesh2d_basics.py b/tests/test_mesh2d_basics.py index 9d91feba..8f42780e 100644 --- a/tests/test_mesh2d_basics.py +++ b/tests/test_mesh2d_basics.py @@ -73,6 +73,71 @@ def test_mesh2d_set_and_mesh2d_get(): assert_array_equal(output_mesh2d.edge_y, np.array([0.0, 0.5, 1.0, 0.5])) +def test_mesh2d_set_and_mesh2d_get_with_holes(): + """ + mesh2d_set has to preserve any existing holes/illegalcells in the grid + Was not the case before resolving issue + https://github.com/Deltares/MeshKernelPy/issues/247 + """ + + # create base grid + projection = ProjectionType.SPHERICAL + make_grid_parameters = MakeGridParameters( + angle=0, + origin_x=147.75, + origin_y=-40.4, + upper_right_x=147.9, + upper_right_y=-40.25, + block_size_x=0.01, + block_size_y=0.01, + ) + + mk = MeshKernel(projection=projection) + mk.curvilinear_compute_rectangular_grid_on_extension(make_grid_parameters) + mk.curvilinear_convert_to_mesh2d() + + # this grid has 320 faces before deletion + assert len(mk.mesh2d_get().face_x) == 320 + + #cut a hole in the grid + xx, yy = np.array([[147.815, -40.282], + [147.835, -40.282], + [147.835 , -40.305], + [147.815, -40.305], + [147.815, -40.282]]).T + + delete_pol_geom = GeometryList( + x_coordinates=xx, + y_coordinates=yy, + geometry_separator=-999, + ) + mk.mesh2d_delete(geometry_list=delete_pol_geom, + delete_option=DeleteMeshOption.INSIDE_NOT_INTERSECTED, + invert_deletion=False, + ) + + mk_grid = mk.mesh2d_get() + + # recreate the grid with minimal information, specifically by leaving out + # face_x/face_y but including face_nodes and nodes_per_face since this is + # the face_node_connectivity + mk2_grid = Mesh2d( + node_x=mk_grid.node_x, + node_y=mk_grid.node_y, + edge_nodes=mk_grid.edge_nodes, + face_nodes=mk_grid.face_nodes, + nodes_per_face=mk_grid.nodes_per_face, + ) + + mk2 = MeshKernel(projection=projection) + mk2.mesh2d_set(mk2_grid) + + # assert the number of faces, if the face_node_connectivity includes a face + # at the location of the deleted cells, there will be 319 faces + assert len(mk.mesh2d_get().face_x) == 318 + assert len(mk2.mesh2d_get().face_x) == 318 + + def test_mesh2d_add(): """Test adding a 2d mesh""" mk = MeshKernel() From a10e84a199a9f3cc6afd4367e2dde971a1fe8b59 Mon Sep 17 00:00:00 2001 From: veenstrajelmer Date: Thu, 13 Nov 2025 16:31:51 +0100 Subject: [PATCH 04/14] black --- tests/test_mesh2d_basics.py | 49 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/tests/test_mesh2d_basics.py b/tests/test_mesh2d_basics.py index 8f42780e..1c6576bf 100644 --- a/tests/test_mesh2d_basics.py +++ b/tests/test_mesh2d_basics.py @@ -79,7 +79,7 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): Was not the case before resolving issue https://github.com/Deltares/MeshKernelPy/issues/247 """ - + # create base grid projection = ProjectionType.SPHERICAL make_grid_parameters = MakeGridParameters( @@ -90,34 +90,39 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): upper_right_y=-40.25, block_size_x=0.01, block_size_y=0.01, - ) - + ) + mk = MeshKernel(projection=projection) mk.curvilinear_compute_rectangular_grid_on_extension(make_grid_parameters) mk.curvilinear_convert_to_mesh2d() - + # this grid has 320 faces before deletion assert len(mk.mesh2d_get().face_x) == 320 - - #cut a hole in the grid - xx, yy = np.array([[147.815, -40.282], - [147.835, -40.282], - [147.835 , -40.305], - [147.815, -40.305], - [147.815, -40.282]]).T - + + # cut a hole in the grid + xx, yy = np.array( + [ + [147.815, -40.282], + [147.835, -40.282], + [147.835, -40.305], + [147.815, -40.305], + [147.815, -40.282], + ] + ).T + delete_pol_geom = GeometryList( x_coordinates=xx, y_coordinates=yy, geometry_separator=-999, - ) - mk.mesh2d_delete(geometry_list=delete_pol_geom, - delete_option=DeleteMeshOption.INSIDE_NOT_INTERSECTED, - invert_deletion=False, - ) - + ) + mk.mesh2d_delete( + geometry_list=delete_pol_geom, + delete_option=DeleteMeshOption.INSIDE_NOT_INTERSECTED, + invert_deletion=False, + ) + mk_grid = mk.mesh2d_get() - + # recreate the grid with minimal information, specifically by leaving out # face_x/face_y but including face_nodes and nodes_per_face since this is # the face_node_connectivity @@ -127,11 +132,11 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): edge_nodes=mk_grid.edge_nodes, face_nodes=mk_grid.face_nodes, nodes_per_face=mk_grid.nodes_per_face, - ) - + ) + mk2 = MeshKernel(projection=projection) mk2.mesh2d_set(mk2_grid) - + # assert the number of faces, if the face_node_connectivity includes a face # at the location of the deleted cells, there will be 319 faces assert len(mk.mesh2d_get().face_x) == 318 From 861c55332923cfa80ac48446ab5a02eaf8126f6e Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 17 Nov 2025 12:13:59 +0100 Subject: [PATCH 05/14] GRIDEDIT-2044 Expose getting of the inner boundary polygons --- meshkernel/meshkernel.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index 6c6c0dfa..b1273e4b 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -282,6 +282,40 @@ def mesh2d_delete( c_int(invert_deletion), ) + def mesh2d_get_inner_boundary_polygons(self) -> Mesh2d: + """Gets the two-dimensional mesh state from the MeshKernel. + + Please note that this involves a copy of the data. + + Returns: + Mesh2d: A copy of the inner boundary polygons. + """ + + c_mesh2d = self._mesh2d_get_inner_boundary_polygons_dimension() + mesh2d = c_mesh2d.allocate_memory() + self._execute_function( + self.lib.mkernel_mesh2d_get_inner_boundary_polygon_data, self._meshkernelid, byref(c_mesh2d) + ) + + mesh2d.remove_invalid_values(float_invalid_value=self._float_invalid_value) + + return mesh2d + + def _mesh2d_get_inner_boundary_polygons_dimension(self) -> CMesh2d: + """For internal use only. + + Gets the inner boundary polygons dimensions. + + Returns: + Mesh2d: The inner boudnary polygons array dimensions. + """ + c_mesh2d = CMesh2d() + self._execute_function( + self.lib.mesh2d_get_inner_boundary_polygons_dimension, self._meshkernelid, byref(c_mesh2d) + ) + return c_mesh2d + + def mesh2d_delete_faces_in_polygons( self, geometry_list: GeometryList, From 071586cb0dd374e59a4a12b606bc6cb6f4680ff2 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 17 Nov 2025 17:58:29 +0100 Subject: [PATCH 06/14] GRIDEDIT-2044 Fixed black formatting warning --- meshkernel/meshkernel.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index b1273e4b..84abe089 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -294,7 +294,9 @@ def mesh2d_get_inner_boundary_polygons(self) -> Mesh2d: c_mesh2d = self._mesh2d_get_inner_boundary_polygons_dimension() mesh2d = c_mesh2d.allocate_memory() self._execute_function( - self.lib.mkernel_mesh2d_get_inner_boundary_polygon_data, self._meshkernelid, byref(c_mesh2d) + self.lib.mkernel_mesh2d_get_inner_boundary_polygon_data, + self._meshkernelid, + byref(c_mesh2d), ) mesh2d.remove_invalid_values(float_invalid_value=self._float_invalid_value) @@ -311,11 +313,12 @@ def _mesh2d_get_inner_boundary_polygons_dimension(self) -> CMesh2d: """ c_mesh2d = CMesh2d() self._execute_function( - self.lib.mesh2d_get_inner_boundary_polygons_dimension, self._meshkernelid, byref(c_mesh2d) + self.lib.mesh2d_get_inner_boundary_polygons_dimension, + self._meshkernelid, + byref(c_mesh2d), ) return c_mesh2d - def mesh2d_delete_faces_in_polygons( self, geometry_list: GeometryList, From 0c30b966a3b0d4428b80ef6983145d232210ca3f Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 17 Nov 2025 17:59:44 +0100 Subject: [PATCH 07/14] GRIDEDIT-2044 Fixed spelling in comment --- meshkernel/meshkernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index 84abe089..32af6c9a 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -309,7 +309,7 @@ def _mesh2d_get_inner_boundary_polygons_dimension(self) -> CMesh2d: Gets the inner boundary polygons dimensions. Returns: - Mesh2d: The inner boudnary polygons array dimensions. + Mesh2d: The inner boundary polygons array dimensions. """ c_mesh2d = CMesh2d() self._execute_function( From 3308d9cfbf5c9f3095c330b7a4b333c4f18c6995 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 17 Nov 2025 18:26:37 +0100 Subject: [PATCH 08/14] GRIDEDIT-2044 Started adding unit test for inner polygons and change return type of inner polygons --- meshkernel/meshkernel.py | 27 +++++++++++++++++++++------ tests/test_mesh2d_basics.py | 7 +++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index 32af6c9a..9ec2205e 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -282,8 +282,8 @@ def mesh2d_delete( c_int(invert_deletion), ) - def mesh2d_get_inner_boundary_polygons(self) -> Mesh2d: - """Gets the two-dimensional mesh state from the MeshKernel. + def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: + """Gets the geometry list from the MeshKernel. Please note that this involves a copy of the data. @@ -291,15 +291,30 @@ def mesh2d_get_inner_boundary_polygons(self) -> Mesh2d: Mesh2d: A copy of the inner boundary polygons. """ - c_mesh2d = self._mesh2d_get_inner_boundary_polygons_dimension() - mesh2d = c_mesh2d.allocate_memory() + c_geometry_list_dimension = c_int() + + self._execute_function( + self.lib.meshkernel_mesh2d_get_inner_boundary_polygons_dimension, + self._meshkernelid, + byref(c_geometry_list_dimension), + ) + + n_coordinates = c_geometry_list_dimension.value + x_coordinates = np.empty(n_coordinates, dtype=np.double) + y_coordinates = np.empty(n_coordinates, dtype=np.double) + + face_polygons = GeometryList( + x_coordinates=x_coordinates, y_coordinates=y_coordinates + ) + + c_face_polygons = CGeometryList.from_geometrylist(face_polygons) + self._execute_function( self.lib.mkernel_mesh2d_get_inner_boundary_polygon_data, self._meshkernelid, - byref(c_mesh2d), + byref(c_face_polygons), ) - mesh2d.remove_invalid_values(float_invalid_value=self._float_invalid_value) return mesh2d diff --git a/tests/test_mesh2d_basics.py b/tests/test_mesh2d_basics.py index 1c6576bf..cc059c28 100644 --- a/tests/test_mesh2d_basics.py +++ b/tests/test_mesh2d_basics.py @@ -142,6 +142,13 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): assert len(mk.mesh2d_get().face_x) == 318 assert len(mk2.mesh2d_get().face_x) == 318 + innerPolygon = GeometryList( + geometry_separator=-999, + ) + + innerPolygon = mk.mesh2d_get_inner_boundary_polygons (); + assert innerPolygon.n_coordinates == 32 + def test_mesh2d_add(): """Test adding a 2d mesh""" From 73f5eb3d6d19d49d416eb19a0955c651b7c7d27d Mon Sep 17 00:00:00 2001 From: Bill Senior Date: Tue, 18 Nov 2025 09:50:50 +0100 Subject: [PATCH 09/14] GRIDEDIT-2044 Corrected code getting inner polygons --- meshkernel/meshkernel.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index 9ec2205e..26becf74 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -283,9 +283,7 @@ def mesh2d_delete( ) def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: - """Gets the geometry list from the MeshKernel. - - Please note that this involves a copy of the data. + """Gets the inner boundary polygons from the MeshKernel. Returns: Mesh2d: A copy of the inner boundary polygons. @@ -315,8 +313,7 @@ def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: byref(c_face_polygons), ) - - return mesh2d + return face_polygons def _mesh2d_get_inner_boundary_polygons_dimension(self) -> CMesh2d: """For internal use only. From aee870d745e3d66eed13e5bcbdca42d1da08f2cc Mon Sep 17 00:00:00 2001 From: Bill Senior Date: Tue, 18 Nov 2025 09:54:44 +0100 Subject: [PATCH 10/14] GRIDEDIT-2044 Fixed formatting warning --- tests/test_mesh2d_basics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_mesh2d_basics.py b/tests/test_mesh2d_basics.py index cc059c28..4a944c73 100644 --- a/tests/test_mesh2d_basics.py +++ b/tests/test_mesh2d_basics.py @@ -146,8 +146,8 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): geometry_separator=-999, ) - innerPolygon = mk.mesh2d_get_inner_boundary_polygons (); - assert innerPolygon.n_coordinates == 32 + innerPolygon = mk.mesh2d_get_inner_boundary_polygons(); + assert innerPolygon.n_coordinates == 5 def test_mesh2d_add(): From 9d79aa9ec2bee4dc5623df4fbe241c1d0c754edf Mon Sep 17 00:00:00 2001 From: Bill Senior Date: Tue, 18 Nov 2025 10:00:38 +0100 Subject: [PATCH 11/14] GRIDEDIT-2044 Removed unnecessary semicolon --- tests/test_mesh2d_basics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_mesh2d_basics.py b/tests/test_mesh2d_basics.py index 4a944c73..d6044aea 100644 --- a/tests/test_mesh2d_basics.py +++ b/tests/test_mesh2d_basics.py @@ -146,7 +146,7 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): geometry_separator=-999, ) - innerPolygon = mk.mesh2d_get_inner_boundary_polygons(); + innerPolygon = mk.mesh2d_get_inner_boundary_polygons() assert innerPolygon.n_coordinates == 5 From 979abae3742b48fada510bdea2bf1026e9b1a3e9 Mon Sep 17 00:00:00 2001 From: Bill Senior Date: Tue, 18 Nov 2025 10:29:34 +0100 Subject: [PATCH 12/14] GRIDEDIT-2044 Corrected code getting inner boundary polygons and the unit test --- meshkernel/meshkernel.py | 2 +- tests/test_mesh2d_basics.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index 26becf74..fe48d685 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -292,7 +292,7 @@ def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: c_geometry_list_dimension = c_int() self._execute_function( - self.lib.meshkernel_mesh2d_get_inner_boundary_polygons_dimension, + self.lib.mkernel_mesh2d_get_inner_boundary_polygon_dimension, self._meshkernelid, byref(c_geometry_list_dimension), ) diff --git a/tests/test_mesh2d_basics.py b/tests/test_mesh2d_basics.py index d6044aea..cdab029b 100644 --- a/tests/test_mesh2d_basics.py +++ b/tests/test_mesh2d_basics.py @@ -146,8 +146,8 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): geometry_separator=-999, ) - innerPolygon = mk.mesh2d_get_inner_boundary_polygons() - assert innerPolygon.n_coordinates == 5 + innerPolygon = mk2.mesh2d_get_inner_boundary_polygons() + assert innerPolygon.x_coordinates.size == 7 def test_mesh2d_add(): From 2c3aee4686d38835fa59f1e9988c6d18c815e693 Mon Sep 17 00:00:00 2001 From: Bill Senior Date: Tue, 18 Nov 2025 10:32:01 +0100 Subject: [PATCH 13/14] GRIDEDIT-2044 Removed unused function --- meshkernel/meshkernel.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index fe48d685..1d3d65bf 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -315,22 +315,6 @@ def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: return face_polygons - def _mesh2d_get_inner_boundary_polygons_dimension(self) -> CMesh2d: - """For internal use only. - - Gets the inner boundary polygons dimensions. - - Returns: - Mesh2d: The inner boundary polygons array dimensions. - """ - c_mesh2d = CMesh2d() - self._execute_function( - self.lib.mesh2d_get_inner_boundary_polygons_dimension, - self._meshkernelid, - byref(c_mesh2d), - ) - return c_mesh2d - def mesh2d_delete_faces_in_polygons( self, geometry_list: GeometryList, From bde5a8b5bc1cab6b5d31244e7b53bff325fae53e Mon Sep 17 00:00:00 2001 From: Bill Senior Date: Tue, 18 Nov 2025 13:46:33 +0100 Subject: [PATCH 14/14] GRIDEDIT-2044 Minor change to the mk api function names --- meshkernel/meshkernel.py | 6 +++--- tests/test_mesh2d_basics.py | 6 +----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/meshkernel/meshkernel.py b/meshkernel/meshkernel.py index 1d3d65bf..acfc8e62 100644 --- a/meshkernel/meshkernel.py +++ b/meshkernel/meshkernel.py @@ -282,7 +282,7 @@ def mesh2d_delete( c_int(invert_deletion), ) - def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: + def mesh2d_get_mesh_inner_boundaries_as_polygons(self) -> GeometryList: """Gets the inner boundary polygons from the MeshKernel. Returns: @@ -292,7 +292,7 @@ def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: c_geometry_list_dimension = c_int() self._execute_function( - self.lib.mkernel_mesh2d_get_inner_boundary_polygon_dimension, + self.lib.mkernel_mesh2d_get_mesh_inner_boundaries_as_polygons_dimension, self._meshkernelid, byref(c_geometry_list_dimension), ) @@ -308,7 +308,7 @@ def mesh2d_get_inner_boundary_polygons(self) -> GeometryList: c_face_polygons = CGeometryList.from_geometrylist(face_polygons) self._execute_function( - self.lib.mkernel_mesh2d_get_inner_boundary_polygon_data, + self.lib.mkernel_mesh2d_get_mesh_inner_boundaries_as_polygons_data, self._meshkernelid, byref(c_face_polygons), ) diff --git a/tests/test_mesh2d_basics.py b/tests/test_mesh2d_basics.py index cdab029b..ea48b41d 100644 --- a/tests/test_mesh2d_basics.py +++ b/tests/test_mesh2d_basics.py @@ -142,11 +142,7 @@ def test_mesh2d_set_and_mesh2d_get_with_holes(): assert len(mk.mesh2d_get().face_x) == 318 assert len(mk2.mesh2d_get().face_x) == 318 - innerPolygon = GeometryList( - geometry_separator=-999, - ) - - innerPolygon = mk2.mesh2d_get_inner_boundary_polygons() + innerPolygon = mk2.mesh2d_get_mesh_inner_boundaries_as_polygons() assert innerPolygon.x_coordinates.size == 7