Repository: DHI/mikeio
Pull request #665 (2024-03-12 14:40:46)

Slim mesh api


Patch:
diff --git a/Makefile b/Makefile
index 34a80e11..e67e879f 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ build: typecheck test
 	python -m build
 
 lint:
-	ruff .
+	ruff check .
 
 pylint:
 	pylint --disable=all --enable=attribute-defined-outside-init mikeio/
diff --git a/mikeio/dfsu/_dfsu.py b/mikeio/dfsu/_dfsu.py
index 9b147c8d..008c3006 100644
--- a/mikeio/dfsu/_dfsu.py
+++ b/mikeio/dfsu/_dfsu.py
@@ -139,9 +139,7 @@ class _Dfsu:
         self._items = self._read_items(filename)
 
     def __repr__(self):
-        out = []
-        type_name = "Flexible Mesh" if self._type is None else self.type_name
-        out.append(type_name)
+        out = [f"<mikeio.{self.__class__.__name__}>"]
 
         if self._type is not DfsuFileType.DfsuSpectral0D:
             if self._type is not DfsuFileType.DfsuSpectral1D:
@@ -161,21 +159,18 @@ class _Dfsu:
             or self._type == DfsuFileType.Dfsu3DSigmaZ
         ):
             out.append(f"max number of z layers: {self.n_layers - self.n_sigma_layers}")
-        if hasattr(self, "items") and self.items is not None:
-            if self.n_items < 10:
-                out.append("items:")
-                for i, item in enumerate(self.items):
-                    out.append(f"  {i}:  {item}")
-            else:
-                out.append(f"number of items: {self.n_items}")
-        if self.n_timesteps is not None:
-            if self.n_timesteps == 1:
-                out.append(f"time: time-invariant file (1 step) at {self._start_time}")
-            else:
-                out.append(
-                    f"time: {str(self.time[0])} - {str(self.time[-1])} ({self.n_timesteps} records)"
-                )
-                out.append(f"      {self.start_time} -- {self.end_time}")
+        if self.n_items < 10:
+            out.append("items:")
+            for i, item in enumerate(self.items):
+                out.append(f"  {i}:  {item}")
+        else:
+            out.append(f"number of items: {self.n_items}")
+        if self.n_timesteps == 1:
+            out.append(f"time: time-invariant file (1 step) at {self.time[0]}")
+        else:
+            out.append(
+                f"time: {str(self.time[0])} - {str(self.time[-1])} ({self.n_timesteps} records)"
+            )
         return str.join("\n", out)
 
     def _read_items(self, filename: str) -> list[ItemInfo]:
diff --git a/mikeio/dfsu/_mesh.py b/mikeio/dfsu/_mesh.py
index f7300a46..16a25173 100644
--- a/mikeio/dfsu/_mesh.py
+++ b/mikeio/dfsu/_mesh.py
@@ -1,21 +1,18 @@
 from __future__ import annotations
 from pathlib import Path
 from typing import TYPE_CHECKING
-from collections.abc import Collection
-import warnings
+
 
 import numpy as np
 
-from mikecore.eum import eumQuantity
-from mikecore.MeshBuilder import MeshBuilder
+
 from mikecore.MeshFile import MeshFile
 
-from ..eum import EUMType, EUMUnit
+
 from ..spatial import GeometryFM2D
 from ._common import (
     get_elements_from_source,
     get_nodes_from_source,
-    element_table_to_mikecore,
 )
 
 if TYPE_CHECKING:
@@ -48,20 +45,8 @@ class Mesh:
     """
 
     def __init__(self, filename: str | Path) -> None:
-        ext = Path(filename).suffix.lower()
-
-        if ext == ".mesh":
-            self.geometry: GeometryFM2D = self._read_header(filename)
-        elif ext == ".dfsu":
-            # TODO remove in v 1.8
-            import mikeio
-
-            warnings.warn(
-                f'Reading dfsu with `Mesh` is deprecated. Read a .dfsu geometry with `geom = mikeio.open("{filename}").geometry`',
-                FutureWarning,
-            )
-            self.geometry = mikeio.open(str(filename)).geometry
 
+        self.geometry: GeometryFM2D = self._read_header(filename)
         self.plot = self.geometry.plot
 
     def _read_header(self, filename: str | Path) -> GeometryFM2D:
@@ -128,7 +113,7 @@ class Mesh:
         return self.geometry.node_coordinates[:, 2]
 
     @zn.setter
-    def zn(self, v: np.ndarray):
+    def zn(self, v: np.ndarray) -> None:
         if len(v) != self.n_nodes:
             raise ValueError(f"zn must have length of nodes ({self.n_nodes})")
         self.geometry._nc[:, 2] = v
@@ -136,8 +121,6 @@ class Mesh:
     def write(
         self,
         outfilename: str | Path,
-        elements: Collection[int] | None = None,
-        unit: EUMUnit = EUMUnit.meter,
     ) -> None:
         """write mesh to file
 
@@ -145,34 +128,13 @@ class Mesh:
         ----------
         outfilename : str
             path to file
-        elements : list(int)
-            list of element ids (subset) to be saved to new mesh
         """
-        # TODO reconsider if selection of elements should be done here, it is possible to do with geometry.isel and then write the mesh
-        builder = MeshBuilder()
 
-        if elements is not None:
-            geometry = self.geometry.isel(elements)
-        else:
-            geometry = self.geometry
+        geometry = self.geometry
 
         assert isinstance(geometry, GeometryFM2D)  # i.e. not a GeometryPoint2d
 
-        quantity = eumQuantity.Create(EUMType.Bathymetry, unit)
-        elem_table = element_table_to_mikecore(geometry.element_table)
-
-        nc = geometry.node_coordinates
-        builder.SetNodes(nc[:, 0], nc[:, 1], nc[:, 2], geometry.codes)
-
-        builder.SetElements(elem_table)
-        builder.SetProjection(geometry.projection_string)
-        builder.SetEumQuantity(quantity)
-
-        newMesh = builder.CreateMesh()
-        newMesh.Write(outfilename)
-
-    def plot_boundary_nodes(self, boundary_names=None, figsize=None, ax=None) -> None:
-        return self.geometry.plot.boundary_nodes(boundary_names, figsize, ax)
+        self.geometry.to_mesh(outfilename=outfilename)
 
     def to_shapely(self) -> MultiPolygon:
         """Convert Mesh geometry to shapely MultiPolygon


Test patch:
diff --git a/tests/test_consistency.py b/tests/test_consistency.py
index 314da71a..ce1c9e7d 100644
--- a/tests/test_consistency.py
+++ b/tests/test_consistency.py
@@ -263,6 +263,15 @@ def test_read_dfs_time_selection_str():
         assert dsr.shape == dsgetitem.shape
 
 
+def test_open_dfs_repr():
+    extensions = ["dfsu", "dfs2", "dfs1", "dfs0"]
+    for ext in extensions:
+        filename = f"tests/testdata/consistency/oresundHD.{ext}"
+        dfs = mikeio.open(filename=filename)
+        text = repr(dfs)
+        assert "<mikeio" in text
+
+
 def test_read_dfs_time_selection_str_specific():
     extensions = ["dfsu", "dfs2", "dfs1", "dfs0"]
     for ext in extensions:
diff --git a/tests/test_dfsu_plot.py b/tests/test_dfsu_plot.py
index f383b4e4..9687229e 100644
--- a/tests/test_dfsu_plot.py
+++ b/tests/test_dfsu_plot.py
@@ -73,8 +73,8 @@ def test_plot_mesh_ax():
 
 def test_plot_mesh_boundary_nodes():
     msh = Mesh("tests/testdata/odense_rough.mesh")
-    msh.plot_boundary_nodes()
-    msh.plot_boundary_nodes(["Land", "Sea"])
+    msh.plot.boundary_nodes()
+    msh.plot.boundary_nodes(["Land", "Sea"])
     assert True
 
 
diff --git a/tests/test_mesh.py b/tests/test_mesh.py
index 35b036fd..8944b68e 100644
--- a/tests/test_mesh.py
+++ b/tests/test_mesh.py
@@ -122,16 +122,6 @@ def test_write(tri_mesh, tmp_path):
     assert outfilename.exists()
 
 
-def test_write_part(tri_mesh, tmp_path):
-    outfilename = tmp_path / "simple_sub.mesh"
-
-    msh = tri_mesh
-
-    msh.write(outfilename, elements=list(range(0, 100)))
-
-    assert outfilename.exists()
-
-
 def test_write_part_isel(tri_mesh, tmp_path):
     outfilename = tmp_path / "simple_sub.mesh"
 
@@ -160,14 +150,6 @@ def test_write_mesh_from_dfsu(tmp_path):
     assert np.all(np.hstack(msh2.element_table) == np.hstack(geometry.element_table))
 
 
-def test_open_dfsu_as_mesh_is_removed_give_hint():
-    dfsufilename = "tests/testdata/FakeLake.dfsu"
-    with pytest.warns(FutureWarning):
-        msh = Mesh(dfsufilename)
-
-    msh.plot()
-
-
 def test_to_shapely(tri_mesh) -> None:
     msh = tri_mesh
     shp = msh.to_shapely()