From 37e1884c81d20a4b6e4a7dd2fcdf2ccfee3cbbff Mon Sep 17 00:00:00 2001 From: Joey Kleingers Date: Thu, 19 Dec 2024 08:34:08 -0500 Subject: [PATCH] MeshIO Python Filters: Add support for .ply files and vertex geometries. Signed-off-by: Joey Kleingers --- .../docs/ReadMeshFileFilter.md | 1 + .../src/NXDataAnalysisToolkit/ReadMeshFile.py | 30 +++++------ .../NXDataAnalysisToolkit/WriteAbaqusFile.py | 5 +- .../NXDataAnalysisToolkit/WriteAnsysFile.py | 5 +- .../NXDataAnalysisToolkit/WriteGmshFile.py | 17 +++++- .../src/NXDataAnalysisToolkit/WriteMedFile.py | 5 +- .../NXDataAnalysisToolkit/WriteTetGenFile.py | 4 +- .../src/NXDataAnalysisToolkit/WriteVtuFile.py | 5 +- .../utilities/meshio_utilities.py | 54 +++++++++++++------ 9 files changed, 81 insertions(+), 45 deletions(-) diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/ReadMeshFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/ReadMeshFileFilter.md index de19e9382a..470a1560e7 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/ReadMeshFileFilter.md +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/ReadMeshFileFilter.md @@ -11,6 +11,7 @@ The following mesh formats are supported by this filter: + Ansys (.msh) + Gmsh (.msh) + Med (.med) ++ PLY (.ply) + TetGen (.node/.ele) + VTK (.vtu) diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/ReadMeshFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/ReadMeshFile.py index cc38e8f0df..c0b64eddf9 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/ReadMeshFile.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/ReadMeshFile.py @@ -83,14 +83,14 @@ def parameters(self) -> nx.Parameters: params = nx.Parameters() params.insert(params.Separator("Input Parameters")) - params.insert(nx.FileSystemPathParameter(ReadMeshFile.INPUT_FILE_PATH_KEY, 'Input File (.inp, .msh, .med, .node, .ele, .vtu)', 'The input file that contains the mesh which will be read in as a geometry.', '', extensions_type={'.inp', '.msh', '.med', '.node', '.ele', '.vtu'}, path_type=nx.FileSystemPathParameter.PathType.InputFile)) + params.insert(nx.FileSystemPathParameter(ReadMeshFile.INPUT_FILE_PATH_KEY, 'Input File (.inp, .msh, .med, .node, .ele, .ply, .vtu)', 'The input file that contains the mesh which will be read in as a geometry.', '', extensions_type={'.inp', '.msh', '.med', '.node', '.ele', '.vtu', '.ply'}, path_type=nx.FileSystemPathParameter.PathType.InputFile)) params.insert(params.Separator("Created Parameters")) params.insert(nx.DataGroupCreationParameter(ReadMeshFile.CREATED_GEOMETRY_PATH, 'Created Geometry', 'The path to where the geometry will be created in the data structure.', nx.DataPath())) params.insert(nx.DataObjectNameParameter(ReadMeshFile.VERTEX_ATTR_MATRIX_NAME, 'Vertex Attribute Matrix Name', 'The name of the vertex attribute matrix that will be created inside the geometry.', 'Vertex Data')) - params.insert(nx.DataObjectNameParameter(ReadMeshFile.CELL_ATTR_MATRIX_NAME, 'Cell Attribute Matrix Name', 'The name of the cell attribute matrix that will be created inside the geometry.', 'Cell Data')) + params.insert(nx.DataObjectNameParameter(ReadMeshFile.CELL_ATTR_MATRIX_NAME, 'Cell Attribute Matrix Name', 'The name of the cell attribute matrix that will be created inside the geometry. This attribute matrix will only be created IF the input mesh file has cells!', 'Cell Data')) params.insert(nx.DataObjectNameParameter(ReadMeshFile.VERTEX_ARRAY_NAME, 'Vertex Array Name', 'The name of the vertex array that will be created inside the geometry.', 'Vertices')) - params.insert(nx.DataObjectNameParameter(ReadMeshFile.CELL_ARRAY_NAME, 'Cell Array Name', 'The name of the cell array that will be created inside the geometry.', 'Cells')) + params.insert(nx.DataObjectNameParameter(ReadMeshFile.CELL_ARRAY_NAME, 'Cell Array Name', 'The name of the cell array that will be created inside the geometry. This array will only be created IF the input mesh file has cells!', 'Cells')) return params @@ -118,17 +118,14 @@ def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_h _mesh_cache = meshio.read(str(input_file_path)) _input_file_path_cache = input_file_path - if len(_mesh_cache.cells) == 0: - return nx.IFilter.PreflightResult(errors=[make_error_result(code=-3040, message=f"Mesh file '{str(input_file_path)}' does not contain a cell type. A cell type is required to be able to create a geometry from the mesh file!")]) - if len(_mesh_cache.cells) > 1: return nx.IFilter.PreflightResult(errors=[make_error_result(code=-3041, message=f"Mesh file '{str(input_file_path)}' has more than one declared cell type. Multiple cell types in one file are not supported.")]) # Create the proper geometry output_actions: nx.OutputActions = nx.OutputActions() warnings: List[nx.Warning] = [] - cell_type: str = _mesh_cache.cells[0].type - cells_array: np.ndarray = _mesh_cache.cells[0].data + cell_type: str = _mesh_cache.cells[0].type if len(_mesh_cache.cells) != 0 else mu.VERTEX_TYPE_STR + cells_array: np.ndarray = _mesh_cache.cells[0].data if len(_mesh_cache.cells) != 0 else None points_array: np.ndarray = _mesh_cache.points if cell_type == mu.EDGE_TYPE_STR: @@ -141,6 +138,8 @@ def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_h output_actions.append_action(nx.CreateTetrahedralGeometryAction(created_geometry_path, cells_array.shape[0], points_array.shape[0], vertex_attr_matrix_name, cell_attr_matrix_name, vertex_array_name, cell_array_name)) elif cell_type == mu.HEXAHEDRAL_TYPE_STR: output_actions.append_action(nx.CreateHexahedralGeometryAction(created_geometry_path, cells_array.shape[0], points_array.shape[0], vertex_attr_matrix_name, cell_attr_matrix_name, vertex_array_name, cell_array_name)) + elif cell_type == mu.VERTEX_TYPE_STR: + output_actions.append_action(nx.CreateVertexGeometryAction(created_geometry_path, points_array.shape[0], vertex_attr_matrix_name, vertex_array_name)) else: return nx.IFilter.PreflightResult(errors=[nx.Error(code=-3042, message=f"Unsupported mesh type '{cell_type}'. Only '{mu.EDGE_TYPE_STR}', '{mu.TRIANGLE_TYPE_STR}', '{mu.QUAD_TYPE_STR}', '{mu.TETRAHEDRAL_TYPE_STR}', and '{mu.HEXAHEDRAL_TYPE_STR}' types are supported.")]) @@ -161,7 +160,6 @@ def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_h # Create the point data arrays vertex_attr_mat_path = created_geometry_path.create_child_path(vertex_attr_matrix_name) for point_data_array_name, point_data_array in _mesh_cache.point_data.items(): - point_data_array = point_data_array[0] point_array_path = vertex_attr_mat_path.create_child_path(point_data_array_name) point_data_tuple_dims = [point_data_array.shape[0]] point_data_comp_dims = point_data_array.shape[1:] @@ -187,23 +185,22 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han geometry: nx.INodeGeometry1D = data_structure[created_geometry_path] # Grab the proper cells array from the geometry + cells_array = None if isinstance(geometry, nx.INodeGeometry3D): cells_array = geometry.polyhedra elif isinstance(geometry, nx.INodeGeometry2D): cells_array = geometry.faces elif isinstance(geometry, nx.INodeGeometry1D): cells_array = geometry.edges - else: - # This SHOULD NOT happen, but we'll check for it anyways... - return Result(errors=[make_error_result(code=-3042, message=f"Created geometry at path '{str(created_geometry_path)}' with type '{type(geometry).__name__}' is not a 1D, 2D, or 3D node-based geometry.")]) # Copy over the vertices vertices_np_array = geometry.vertices.npview() vertices_np_array[:] = _mesh_cache.points[:] # Copy over the cells - cells_np_array = cells_array.npview() - cells_np_array[:] = _mesh_cache.cells[0].data[:] + if cells_array is not None: + cells_np_array = cells_array.npview() + cells_np_array[:] = _mesh_cache.cells[0].data[:] # Copy over the cell data cell_attr_mat_path = created_geometry_path.create_child_path(cell_attr_matrix_name) @@ -211,14 +208,13 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han cell_data_array = cell_data_array[0] cell_data_array_path = cell_attr_mat_path.create_child_path(cell_data_array_name) cell_data_np_array = np.squeeze(data_structure[cell_data_array_path].npview()) - cell_data_np_array[:] = _mesh_cache.cell_data[cell_data_array_name][0][:] + cell_data_np_array[:] = np.squeeze(cell_data_array[:]) # Copy over the point data vertex_attr_mat_path = created_geometry_path.create_child_path(vertex_attr_matrix_name) for point_data_array_name, point_data_array in _mesh_cache.point_data.items(): - point_data_array = point_data_array[0] point_data_array_path = vertex_attr_mat_path.create_child_path(point_data_array_name) point_data_np_array = np.squeeze(data_structure[point_data_array_path].npview()) - point_data_np_array[:] = _mesh_cache.point_data[point_data_array_name][0][:] + point_data_np_array[:] = np.squeeze(point_data_array[:]) return nx.Result() diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAbaqusFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAbaqusFile.py index ecefc18160..e1c697bf70 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAbaqusFile.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAbaqusFile.py @@ -72,7 +72,7 @@ def parameters(self) -> nx.Parameters: params = nx.Parameters() params.insert(params.Separator("Input Parameters")) - params.insert(nx.GeometrySelectionParameter(WriteAbaqusFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the ABAQUS file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) + params.insert(nx.GeometrySelectionParameter(WriteAbaqusFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the ABAQUS file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Vertex, nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) params.insert(params.Separator("Output Parameters")) params.insert(nx.FileSystemPathParameter(WriteAbaqusFile.OUTPUT_FILE_PATH_KEY, 'Output File (.inp)', 'The output file that contains the specified geometry as a mesh.', '', extensions_type={'.inp'}, path_type=nx.FileSystemPathParameter.PathType.OutputFile)) @@ -108,4 +108,5 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han input_geometry_path: nx.DataPath = args[WriteAbaqusFile.INPUT_GEOMETRY_KEY] output_file_path: Path = args[WriteAbaqusFile.OUTPUT_FILE_PATH_KEY] - return mu.execute_meshio_writer_filter(file_format='abaqus', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=None, point_data_array_paths=None, output_file_path=output_file_path) + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Writing geometry to Abaqus file "{str(output_file_path)}"')) + return mu.execute_meshio_writer_filter(file_format='abaqus', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=None, point_data_array_paths=None, output_file_path=output_file_path, message_handler=message_handler, should_cancel=should_cancel) diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAnsysFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAnsysFile.py index 3494b56971..9889ab00ad 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAnsysFile.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAnsysFile.py @@ -73,7 +73,7 @@ def parameters(self) -> nx.Parameters: params = nx.Parameters() params.insert(params.Separator("Input Parameters")) - params.insert(nx.GeometrySelectionParameter(WriteAnsysFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the ANSYS file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) + params.insert(nx.GeometrySelectionParameter(WriteAnsysFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the ANSYS file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Vertex, nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) params.insert(params.Separator("Output Parameters")) params.insert(nx.FileSystemPathParameter(WriteAnsysFile.OUTPUT_FILE_PATH_KEY, 'Output File (.msh)', 'The output file that contains the specified geometry as a mesh.', '', extensions_type={'.msh'}, path_type=nx.FileSystemPathParameter.PathType.OutputFile)) @@ -109,4 +109,5 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han input_geometry_path: nx.DataPath = args[WriteAnsysFile.INPUT_GEOMETRY_KEY] output_file_path: Path = args[WriteAnsysFile.OUTPUT_FILE_PATH_KEY] - return mu.execute_meshio_writer_filter(file_format='ansys', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=None, point_data_array_paths=None, output_file_path=output_file_path, binary=True) + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Writing geometry to Ansys file "{str(output_file_path)}"')) + return mu.execute_meshio_writer_filter(file_format='ansys', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=None, point_data_array_paths=None, output_file_path=output_file_path, message_handler=message_handler, should_cancel=should_cancel, binary=True) diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteGmshFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteGmshFile.py index 33fb59d5d1..e504e154c8 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteGmshFile.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteGmshFile.py @@ -1,6 +1,7 @@ from typing import List from pathlib import Path import simplnx as nx +import numpy as np import meshio from .utilities import meshio_utilities as mu @@ -75,7 +76,7 @@ def parameters(self) -> nx.Parameters: params = nx.Parameters() params.insert(params.Separator("Input Parameters")) - params.insert(nx.GeometrySelectionParameter(WriteGmshFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the ANSYS file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) + params.insert(nx.GeometrySelectionParameter(WriteGmshFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the ANSYS file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Vertex, nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) params.insert(nx.MultiArraySelectionParameter(WriteGmshFile.CELL_DATA_ARRAY_PATHS_KEY, 'Cell Data Arrays To Write', 'The cell data arrays to write to the ANSYS file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) params.insert(nx.MultiArraySelectionParameter(WriteGmshFile.POINT_DATA_ARRAY_PATHS_KEY, 'Point Data Arrays To Write', 'The point data arrays to write to the ANSYS file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) @@ -117,4 +118,16 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han cell_data_array_paths = args[WriteGmshFile.CELL_DATA_ARRAY_PATHS_KEY] point_data_array_paths = args[WriteGmshFile.POINT_DATA_ARRAY_PATHS_KEY] - return mu.execute_meshio_writer_filter(file_format='gmsh', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path) + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Writing geometry to Gmsh file "{str(output_file_path)}"')) + mesh, result = mu.create_mesh(file_format='gmsh', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path, message_handler=message_handler, should_cancel=should_cancel) + if result.invalid(): + return result + + # Write the dim_tags for the point data + geom = data_structure[input_geometry_path] + mesh.point_data["gmsh:dim_tags"] = np.full((geom.vertices.tdims[0], 2), [3, 0], dtype=np.int64) + + # Output the mesh + meshio.write(output_file_path, mesh, file_format='gmsh') + + return nx.Result() \ No newline at end of file diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteMedFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteMedFile.py index e912bdbf01..e8773d9c3b 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteMedFile.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteMedFile.py @@ -75,7 +75,7 @@ def parameters(self) -> nx.Parameters: params = nx.Parameters() params.insert(params.Separator("Input Parameters")) - params.insert(nx.GeometrySelectionParameter(WriteMedFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the MED file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) + params.insert(nx.GeometrySelectionParameter(WriteMedFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the MED file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Vertex, nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) params.insert(nx.MultiArraySelectionParameter(WriteMedFile.CELL_DATA_ARRAY_PATHS_KEY, 'Cell Data Arrays To Write', 'The cell data arrays to write to the MED file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) params.insert(nx.MultiArraySelectionParameter(WriteMedFile.POINT_DATA_ARRAY_PATHS_KEY, 'Point Data Arrays To Write', 'The point data arrays to write to the MED file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) @@ -117,4 +117,5 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han cell_data_array_paths = args[WriteMedFile.CELL_DATA_ARRAY_PATHS_KEY] point_data_array_paths = args[WriteMedFile.POINT_DATA_ARRAY_PATHS_KEY] - return mu.execute_meshio_writer_filter(file_format='med', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path) + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Writing geometry to Med file "{str(output_file_path)}"')) + return mu.execute_meshio_writer_filter(file_format='med', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path, message_handler=message_handler, should_cancel=should_cancel) diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteTetGenFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteTetGenFile.py index 125cac9604..62ad5272b5 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteTetGenFile.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteTetGenFile.py @@ -117,4 +117,6 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han cell_data_array_paths = args[WriteTetGenFile.CELL_DATA_ARRAY_PATHS_KEY] point_data_array_paths = args[WriteTetGenFile.POINT_DATA_ARRAY_PATHS_KEY] - return mu.execute_meshio_writer_filter(file_format='tetgen', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path) + no_suffix_path = output_file_path.with_suffix("") + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Writing geometry to TetGen files "{str(no_suffix_path.with_suffix(".node"))}" and "{str(no_suffix_path.with_suffix(".ele"))}"')) + return mu.execute_meshio_writer_filter(file_format='tetgen', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path, message_handler=message_handler, should_cancel=should_cancel) diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteVtuFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteVtuFile.py index feaf5730a2..f8a44aa318 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteVtuFile.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteVtuFile.py @@ -91,7 +91,7 @@ def parameters(self) -> nx.Parameters: params = nx.Parameters() params.insert(params.Separator("Input Parameters")) - params.insert(nx.GeometrySelectionParameter(WriteVtuFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the VTK file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) + params.insert(nx.GeometrySelectionParameter(WriteVtuFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the VTK file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Vertex, nx.IGeometry.Type.Edge, nx.IGeometry.Type.Triangle, nx.IGeometry.Type.Quad, nx.IGeometry.Type.Tetrahedral, nx.IGeometry.Type.Hexahedral})) params.insert(nx.MultiArraySelectionParameter(WriteVtuFile.CELL_DATA_ARRAY_PATHS_KEY, 'Cell Data Arrays To Write', 'The cell data arrays to write to the VTK file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) params.insert(nx.MultiArraySelectionParameter(WriteVtuFile.POINT_DATA_ARRAY_PATHS_KEY, 'Point Data Arrays To Write', 'The point data arrays to write to the VTK file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) @@ -135,4 +135,5 @@ def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_han cell_data_array_paths = args[WriteVtuFile.CELL_DATA_ARRAY_PATHS_KEY] point_data_array_paths = args[WriteVtuFile.POINT_DATA_ARRAY_PATHS_KEY] - return mu.execute_meshio_writer_filter(file_format='vtu', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path, remove_array_name_spaces=True, binary=True, compression=self.compression_type_values[compression_type]) + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Writing geometry to Vtu file "{str(output_file_path)}"')) + return mu.execute_meshio_writer_filter(file_format='vtu', data_structure=data_structure, input_geometry_path=input_geometry_path, cell_data_array_paths=cell_data_array_paths, point_data_array_paths=point_data_array_paths, output_file_path=output_file_path, message_handler=message_handler, should_cancel=should_cancel, remove_array_name_spaces=True, binary=True, compression=self.compression_type_values[compression_type]) diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/meshio_utilities.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/meshio_utilities.py index 7e64a03aa7..dcfe8d6a32 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/meshio_utilities.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/meshio_utilities.py @@ -1,9 +1,10 @@ import simplnx as nx import meshio import numpy as np -from typing import List +from typing import List, Tuple from pathlib import Path +VERTEX_TYPE_STR = 'vertices' EDGE_TYPE_STR = 'line' TRIANGLE_TYPE_STR = 'triangle' QUAD_TYPE_STR = 'quad' @@ -58,21 +59,23 @@ def preflight_meshio_writer_filter(data_structure: nx.DataStructure, input_geome return None, warnings -def execute_meshio_writer_filter(file_format: str, data_structure: nx.DataStructure, input_geometry_path: nx.DataPath, cell_data_array_paths: List[nx.DataPath], point_data_array_paths: List[nx.DataPath], output_file_path: Path, remove_array_name_spaces: bool = False, **write_kwargs): +def create_mesh(file_format: str, data_structure: nx.DataStructure, input_geometry_path: nx.DataPath, cell_data_array_paths: List[nx.DataPath], point_data_array_paths: List[nx.DataPath], output_file_path: Path, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy, remove_array_name_spaces: bool = False, **write_kwargs) -> Tuple[meshio.Mesh,nx.Result]: geom = data_structure[input_geometry_path] - geom_cell_tdims = get_geometry_cell_tuple_dimensions(geom) - - # Create meshio cells list - cells = create_meshio_cells_list(geom) - - # Create meshio cell_data - cell_data = {} - if cell_data_array_paths is not None: - for data_array_path in cell_data_array_paths: - data_array = data_structure[data_array_path] - if data_array.tdims != geom_cell_tdims: - return nx.Result(errors=[nx.Error(-4010, f"Cell data array '{data_array_path}' has tuple dimensions {data_array.tdims} but the input geometry requires tuple dimensions {geom_cell_tdims}.")]) - cell_data[data_array.name.replace(' ', '') if remove_array_name_spaces else data_array.name] = [data_array.npview()] # Remove spaces in cell data array names (this is required for some formats like VTK) + cells = [] + cell_data = None + if geom.type != nx.DataObject.DataObjectType.VertexGeom: + geom_cell_tdims = get_geometry_cell_tuple_dimensions(geom) + + # Create meshio cells list + cells = create_meshio_cells_list(geom) + + # Create meshio cell_data + if cell_data_array_paths is not None: + for data_array_path in cell_data_array_paths: + data_array = data_structure[data_array_path] + if data_array.tdims != geom_cell_tdims: + return None, nx.Result(errors=[nx.Error(-4010, f"Cell data array '{data_array_path}' has tuple dimensions {data_array.tdims} but the input geometry requires tuple dimensions {geom_cell_tdims}.")]) + cell_data[data_array.name.replace(' ', '') if remove_array_name_spaces else data_array.name] = np.squeeze(data_array.npview()) # Remove spaces in cell data array names (this is required for some formats like VTK) # Create meshio point_data point_data = {} @@ -80,12 +83,29 @@ def execute_meshio_writer_filter(file_format: str, data_structure: nx.DataStruct for data_array_path in point_data_array_paths: data_array = data_structure[data_array_path] if data_array.tdims != geom.vertices.tdims: - return nx.Result(errors=[nx.Error(-4011, f"Point data array '{data_array_path}' has tuple dimensions {data_array.tdims} but the input geometry point data requires tuple dimensions {geom.vertices.tdims}.")]) - point_data[data_array.name.replace(' ', '') if remove_array_name_spaces else data_array.name] = [data_array.npview()] # Remove spaces in point data array names (this is required for some formats like VTK) + return None, nx.Result(errors=[nx.Error(-4011, f"Point data array '{data_array_path}' has tuple dimensions {data_array.tdims} but the input geometry point data requires tuple dimensions {geom.vertices.tdims}.")]) + point_data[data_array.name.replace(' ', '') if remove_array_name_spaces else data_array.name] = np.squeeze(data_array.npview()) # Remove spaces in point data array names (this is required for some formats like VTK) # Create meshio Mesh mesh = meshio.Mesh(points=list(geom.vertices.npview().astype(np.float64)), cells=cells, cell_data=cell_data, point_data=point_data) + return mesh, nx.Result() + +def execute_meshio_writer_filter(file_format: str, data_structure: nx.DataStructure, input_geometry_path: nx.DataPath, cell_data_array_paths: List[nx.DataPath], point_data_array_paths: List[nx.DataPath], output_file_path: Path, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy, remove_array_name_spaces: bool = False, **write_kwargs): + # Create mesh + mesh, result = create_mesh(file_format=file_format, + data_structure=data_structure, + input_geometry_path=input_geometry_path, + cell_data_array_paths=cell_data_array_paths, + point_data_array_paths=point_data_array_paths, + output_file_path=output_file_path, + message_handler=message_handler, + should_cancel=should_cancel, + remove_array_name_spaces=remove_array_name_spaces, + write_kwargs=write_kwargs) + if result.invalid(): + return result + # Output the mesh meshio.write(output_file_path, mesh, file_format=file_format, **write_kwargs)