Salome HOME
[bos #40653][CEA] New mesh import export formats with meshio.
[modules/smesh.git] / test / SMESH_MeshioShapes.py
diff --git a/test/SMESH_MeshioShapes.py b/test/SMESH_MeshioShapes.py
new file mode 100644 (file)
index 0000000..f77d7a9
--- /dev/null
@@ -0,0 +1,260 @@
+#  -*- coding: iso-8859-1 -*-
+# Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+# =======================================
+# Testing export/import of some simple shapes in different formats with meshio library
+#  File   : SMESH_MeshioShapes.py
+#  Module : SMESH
+
+import tempfile
+from pathlib import Path
+
+import salome
+salome.salome_init()
+
+import GEOM
+from salome.geom import geomBuilder
+
+import SMESH
+from salome.smesh import smeshBuilder
+
+import SALOME
+
+# Constants
+EXPORT_TITLE = 'Export'
+IMPORT_TITLE = 'Import'
+UNKNOWN_EXCEPTION = 'Unknown exception'
+OUTPUT_DIVIDER = '\n==============================================='
+
+
+geompy = geomBuilder.New()
+smesh = smeshBuilder.New()
+
+
+def make_plane():
+    """
+    Makes default plane for testing.
+    """
+
+    plane = geompy.MakeFaceHW(100, 100, 1)
+    geompy.addToStudy( plane, 'plane' )
+
+    return plane
+
+
+def make_box():
+    """
+    Makes default box for testing.
+    """
+
+    box = geompy.MakeBoxDXDYDZ(200, 200, 200)
+    geompy.addToStudy(box, 'box')
+
+    return box
+
+
+def make_mesh(shape, name):
+    """
+    Makes a mesh from a given shape.
+    """
+    res_mesh = smesh.Mesh(shape,name)
+    NETGEN_1D_2D = res_mesh.Triangle(algo=smeshBuilder.NETGEN_1D2D)
+    isDone = res_mesh.Compute()
+    assert isDone
+
+    smesh.SetName(res_mesh, name)
+    smesh.SetName(NETGEN_1D_2D.GetAlgorithm(), 'NETGEN 1D-2D')
+    smesh.SetName(res_mesh.GetMesh(), name)
+
+    return res_mesh
+
+
+def file_extensions():
+    """
+    Returns all files extensions supported by meshio.
+    Commented formats should be checked on next meshio release to see if the problem was fixed.
+    """
+
+    return [
+        '.avs',
+        '.bdf',
+        # '.cgns', # meshio IndexError: index 2 is out of bounds for axis 1 with size 2
+        # '.dat', # meshio ValueError: need at least one array to concatenate
+        '.dato',
+        '.dato.gz',
+        '.e',
+        # '.ele', # never returns from meshio convert command
+        '.exo',
+        # '.f3grid', # meshio IndexError: Replacement index 3 out of range for positional args tuple
+        '.fem',
+        # '.h5m', # meshio AttributeError: 'list' object has no attribute 'items'
+        '.inp',
+        '.mdpa',
+        '.med',
+        '.mesh',
+        '.meshb',
+        # '.msh', # meshio KeyError: "Illegal ANSYS cell type 'line'
+        '.nas',
+        # '.node', # never returns from meshio convert command
+        # '.obj', # meshio._exceptions.WriteError: Wavefront .obj files can only contain triangle or quad cells
+        # '.off', # failed on Windows only
+        # '.ply', # Export Warning: PLY doesn't support 64-bit integers. Casting down to 32-bit. Then import failed
+        '.post',
+        '.post.gz',
+        '.stl',
+        # '.su2', # meshio TypeError: cannot unpack non-iterable CellBlock object
+        # '.svg', # meshio._exceptions.WriteError: SVG can only handle flat 2D meshes
+        # '.ugrid', # meshio AttributeError: 'list' object has no attribute 'reshape'
+        '.vol',
+        '.vol.gz',
+        '.vtk',
+        '.vtu',
+        # '.wkt', # Export Warning: WTK only supports triangle cells. Skipping {", ".join(skip)} then import failed
+        '.xdmf',
+        '.xmf',
+        '.xml'
+    ]
+
+
+def exception_handle(file_name, errors, operation_type, ex_text):
+    """
+    Pepares and saves an exception message.
+    """
+
+    ext = Path(file_name).suffix.upper()
+    msg = '{} {} failed! File name: {}'.format(operation_type, ext, file_name)
+    if ex_text:
+        msg += ' Exception: ' + ex_text + OUTPUT_DIVIDER + '\n'
+
+    errors.append(msg)
+
+
+def export_mesh(mesh, file_name, errors):
+    """
+    Performs an export operatin.
+    """
+
+    try:
+        print('Export to file: ', file_name)
+
+        # Here we use an empty selected filter to make things simpler
+        mesh.ExportMESHIO(file_name, '', mesh)
+        return True
+
+    except SALOME.SALOME_Exception as ex:
+        exception_handle(file_name, errors, EXPORT_TITLE, ex.details.text)
+
+    except:
+        exception_handle(file_name, errors, EXPORT_TITLE, UNKNOWN_EXCEPTION)
+
+    return False
+
+
+def import_file(file_name, errors):
+    """
+    Performs an import operatin.
+    """
+
+    try:
+        print('Import from file: ', file_name)
+
+        ([mesh], status) = smesh.CreateMeshesFromMESHIO(file_name)
+        return mesh
+
+    except SALOME.SALOME_Exception as ex:
+        exception_handle(file_name, errors, IMPORT_TITLE, ex.details.text)
+
+    except:
+        exception_handle(file_name, errors, IMPORT_TITLE, UNKNOWN_EXCEPTION)
+
+    return None
+
+
+def print_errors(errors):
+    """
+    Checks if we have got any saved error messages and print them.
+    The test failed in this case.
+    """
+
+    if not len(errors):
+        return
+
+    print('\nErrors:\n')
+    for idx, err in enumerate(errors):
+        print('{:02}: {}'.format(idx, err))
+
+    print(OUTPUT_DIVIDER)
+
+
+def perform(mesh, name, errors):
+    """
+    Exports a given mesh and imports it back for each file format.
+    """
+
+    for ext in file_extensions():
+        with tempfile.NamedTemporaryFile(suffix=ext, prefix=name) as file:
+            file.close() # prevents PermissionError on Windows
+            if (export_mesh(mesh, file.name, errors)):
+                file.close() # prevents PermissionError on Windows
+                import_file(file.name, errors)
+
+
+def test_box(errors):
+    """
+    Tests export / import of a mesh for a default box geometry.
+    """
+
+    box = make_box()
+
+    mesh_name = 'Test_box_'
+    mesh = make_mesh(box, mesh_name)
+    perform(mesh, mesh_name, errors)
+
+
+def test_plane(errors):
+    """
+    Tests export / import of a mesh for a default plane geometry.
+    """
+
+    plane = make_plane()
+
+    mesh_name = 'Test_plane_'
+    mesh = make_mesh(plane, mesh_name)
+    perform(mesh, mesh_name, errors)
+
+
+def test():
+    """
+    Creates meshes from plane and box and tries to export them to a temp files
+    and import them back with different formats supported by meshio library.
+    Prints errors if we have any.
+    """
+
+    errors = []
+
+    test_plane(errors)
+    test_box(errors)
+
+    print_errors(errors)
+    assert not len(errors)
+
+
+if __name__ == "__main__":
+    test()