diff --git a/src/Plugins/SimplnxCore/wrapping/python/simplnxpy.cpp b/src/Plugins/SimplnxCore/wrapping/python/simplnxpy.cpp index be106a1981..c4219fb6d0 100644 --- a/src/Plugins/SimplnxCore/wrapping/python/simplnxpy.cpp +++ b/src/Plugins/SimplnxCore/wrapping/python/simplnxpy.cpp @@ -453,6 +453,57 @@ PYBIND11_MODULE(simplnx, mod) dataType.value("float64", DataType::float64); dataType.value("boolean", DataType::boolean); + mod.def( + "convert_np_dtype_to_datatype", + [](const py::dtype& dtype) { + if(dtype.is(py::dtype::of())) + { + return DataType::int8; + } + if(dtype.is(py::dtype::of())) + { + return DataType::uint8; + } + if(dtype.is(py::dtype::of())) + { + return DataType::int16; + } + if(dtype.is(py::dtype::of())) + { + return DataType::uint16; + } + if(dtype.is(py::dtype::of())) + { + return DataType::int32; + } + if(dtype.is(py::dtype::of())) + { + return DataType::uint32; + } + if(dtype.is(py::dtype::of())) + { + return DataType::int64; + } + if(dtype.is(py::dtype::of())) + { + return DataType::uint64; + } + if(dtype.is(py::dtype::of())) + { + return DataType::float32; + } + if(dtype.is(py::dtype::of())) + { + return DataType::float64; + } + if(dtype.is(py::dtype::of())) + { + return DataType::boolean; + } + throw std::invalid_argument("Unable to convert dtype to DataType: Unsupported dtype."); + }, + "Convert numpy dtype to simplnx DataType", "dtype"_a); + py::enum_ arrayHandlingType(mod, "ArrayHandlingType"); arrayHandlingType.value("Copy", ArrayHandlingType::Copy); arrayHandlingType.value("Move", ArrayHandlingType::Move); diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/ReadMeshFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/ReadMeshFileFilter.md new file mode 100644 index 0000000000..de19e9382a --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/ReadMeshFileFilter.md @@ -0,0 +1,39 @@ +# MeshIO: Read Mesh File + +## Group (Subgroup) +Core (IO/Read) + +## Description +The `ReadMeshFile` filter uses the Python MeshIO library to read a mesh file into a simplnx geometry. + +The following mesh formats are supported by this filter: ++ Abaqus (.inp) ++ Ansys (.msh) ++ Gmsh (.msh) ++ Med (.med) ++ TetGen (.node/.ele) ++ VTK (.vtu) + +*NOTE*: This filter makes the assumption (for cell and point data stored inside the mesh file) that the first element in the numpy shape list is the only tuple dimension, and every other shape list element is part of the component dimensions! + +## Parameters + +### Input Parameters +- **`Input File`** (str): The path to the input mesh file. + +### Created Parameters +- **`Created Geometry`** (nx.DataPath): The path to the geometry that will be created. +- **`Vertex Attribute Matrix Name`** (str): The name of the vertex attribute matrix that will be created inside the geometry. +- **`Cell Attribute Matrix Name`** (str): The name of the cell attribute matrix that will be created inside the geometry. +- **`Vertex Array Name`** (str): The name of the vertex array that will be created inside the geometry. +- **`Cell Array Name`** (str): The name of the cell array that will be created inside the geometry. + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteAbaqusFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteAbaqusFileFilter.md new file mode 100644 index 0000000000..ebb5747073 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteAbaqusFileFilter.md @@ -0,0 +1,27 @@ +# MeshIO: Write Abaqus File + +## Group (Subgroup) +Core (IO/Read) + +## Description +The `WriteAbaqusFile` filter uses the Python MeshIO library to write a simplnx geometry to an Abaqus .inp file. + +*Note*: This filter does not support writing cell data or point data to the mesh file. + +## Parameters + +### Input Parameters +- **`Input Geometry`** (nx.DataPath): Specifies the complete path to the input geometry. + +### Output Parameters +- **`Output File`** (str): The path to the output .inp file. + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteAnsysFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteAnsysFileFilter.md new file mode 100644 index 0000000000..ccb89637de --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteAnsysFileFilter.md @@ -0,0 +1,27 @@ +# MeshIO: Write Ansys File + +## Group (Subgroup) +Core (IO/Read) + +## Description +The `WriteAnsysFile` filter uses the Python MeshIO library to write a simplnx geometry to an Ansys .msh file. + +*Note*: This filter does not support writing cell data or point data to the mesh file. + +## Parameters + +### Input Parameters +- **`Input Geometry`** (nx.DataPath): Specifies the complete path to the input geometry. + +### Output Parameters +- **`Output File`** (str): The path to the output .msh file. + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteGmshFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteGmshFileFilter.md new file mode 100644 index 0000000000..b664551303 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteGmshFileFilter.md @@ -0,0 +1,27 @@ +# MeshIO: Write Gmsh File + +## Group (Subgroup) +Core (IO/Read) + +## Description +The `WriteGmshFile` filter uses the Python MeshIO library to write a simplnx geometry to a Gmsh .msh file. + +## Parameters + +### Input Parameters +- **`Input Geometry`** (nx.DataPath): Specifies the complete path to the input geometry. +- **`Cell Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the cell data arrays to include in the output mesh file. +- **`Point Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the point data arrays to include in the output mesh file. + +### Output Parameters +- **`Output File`** (str): The path to the output .msh file. + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteMedFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteMedFileFilter.md new file mode 100644 index 0000000000..ecc30db778 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteMedFileFilter.md @@ -0,0 +1,27 @@ +# MeshIO: Write Med File + +## Group (Subgroup) +Core (IO/Read) + +## Description +The `WriteMedFile` filter uses the Python MeshIO library to write a simplnx geometry to a Med .med file. + +## Parameters + +### Input Parameters +- **`Input Geometry`** (nx.DataPath): Specifies the complete path to the input geometry. +- **`Cell Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the cell data arrays to include in the output mesh file. +- **`Point Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the point data arrays to include in the output mesh file. + +### Output Parameters +- **`Output File`** (str): The path to the output .med file. + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteTetGenFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteTetGenFileFilter.md new file mode 100644 index 0000000000..96a630f593 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteTetGenFileFilter.md @@ -0,0 +1,29 @@ +# MeshIO: Write TetGen File + +## Group (Subgroup) +Core (IO/Read) + +## Description +The `WriteTetGenFile` filter uses the Python MeshIO library to write a simplnx tetrahedral geometry to TetGen .node and .ele files. + +*Note*: Either .node or .ele can be chosen as the output file; the one that is not chosen will also be automatically created. + +## Parameters + +### Input Parameters +- **`Input Geometry`** (nx.DataPath): Specifies the complete path to the input geometry. +- **`Cell Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the cell data arrays to include in the output mesh file. +- **`Point Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the point data arrays to include in the output mesh file. + +### Output Parameters +- **`Output File`** (str): The path to the output .node/.ele file. Regardless of which extension is chosen as the output file, both .node and .ele files will be written to the chosen location with the chosen file name. + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteVtuFileFilter.md b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteVtuFileFilter.md new file mode 100644 index 0000000000..429465f8b5 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/docs/WriteVtuFileFilter.md @@ -0,0 +1,30 @@ +# MeshIO: Write Vtu File + +## Group (Subgroup) +Core (IO/Read) + +## Description +The `WriteVtuFile` filter uses the Python MeshIO library to write a simplnx geometry to a VTK .vtu file. + +The output file can also be compressed if the user chooses LZMA or ZLIB as the Output Compression Type. + +## Parameters + +### Input Parameters +- **`Input Geometry`** (nx.DataPath): Specifies the complete path to the input geometry. +- **`Cell Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the cell data arrays to include in the output mesh file. +- **`Point Data Arrays To Write`** (List[nx.DataPath]): List of DataPaths to the point data arrays to include in the output mesh file. + +### Output Parameters +- **`Output File`** (str): The path to the output .vtu file. +- **`Output Compression Type`** (int): The compression type to use when writing the output file. The choices are Uncompressed, LZMA, and ZLIB. + +## Example Pipelines + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/Plugin.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/Plugin.py index 30a963c437..40e2851335 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/Plugin.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/Plugin.py @@ -60,6 +60,62 @@ pass # FILTER_END: ReadPeregrineHDF5File +# FILTER_START: WriteAbaqusFile +try: + from DataAnalysisToolkit.WriteAbaqusFile import WriteAbaqusFile + _filters.append(WriteAbaqusFile) +except ImportError: + pass +# FILTER_END: WriteAbaqusFile + +# FILTER_START: WriteAnsysFile +try: + from DataAnalysisToolkit.WriteAnsysFile import WriteAnsysFile + _filters.append(WriteAnsysFile) +except ImportError: + pass +# FILTER_END: WriteAnsysFile + +# FILTER_START: WriteMedFile +try: + from DataAnalysisToolkit.WriteMedFile import WriteMedFile + _filters.append(WriteMedFile) +except ImportError: + pass +# FILTER_END: WriteMedFile + +# FILTER_START: WriteGmshFile +try: + from DataAnalysisToolkit.WriteGmshFile import WriteGmshFile + _filters.append(WriteGmshFile) +except ImportError: + pass +# FILTER_END: WriteGmshFile + +# FILTER_START: WriteTetGenFile +try: + from DataAnalysisToolkit.WriteTetGenFile import WriteTetGenFile + _filters.append(WriteTetGenFile) +except ImportError: + pass +# FILTER_END: WriteTetGenFile + +# FILTER_START: WriteVtuFile +try: + from DataAnalysisToolkit.WriteVtuFile import WriteVtuFile + _filters.append(WriteVtuFile) +except ImportError: + pass +# FILTER_END: WriteVtuFile + +# FILTER_START: ReadMeshFile +try: + from DataAnalysisToolkit.ReadMeshFile import ReadMeshFile + _filters.append(ReadMeshFile) +except ImportError: + pass +# FILTER_END: ReadMeshFile + import simplnx as nx class NXDataAnalysisToolkit: diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/ReadMeshFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/ReadMeshFile.py new file mode 100644 index 0000000000..e87ff914df --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/ReadMeshFile.py @@ -0,0 +1,221 @@ +from typing import List +from pathlib import Path +import simplnx as nx +import meshio +import numpy as np +from .common.Result import Result, make_error_result, make_warning_result +from .utilities import meshio_utilities as mu + +# Cache variables +_input_file_path_cache: Path = None +_mesh_cache: meshio.Mesh = None + +class ReadMeshFile: + +# ----------------------------------------------------------------------------- +# These methods should not be edited +# ----------------------------------------------------------------------------- + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('f2b29688-91c7-4c48-9e7d-edd1b582f0b7') + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'ReadMeshFile' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'ReadMeshFile' + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: ReadMeshFile + """ + return ReadMeshFile() + +# ----------------------------------------------------------------------------- +# These methods CAN (and probably should) be updated. For instance, the +# human_name() is what users of the filter will see in the DREAM3D-NX GUI. You +# might want to consider putting spaces between workd, using proper capitalization +# and putting "(Python)" at the end of the name (or beginning if you want the +# filter list to group your filters togther) +# ----------------------------------------------------------------------------- + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'MeshIO: Read Mesh File (Python)' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python', 'ReadMeshFile', 'inp', 'msh', 'med', 'node', 'ele', 'vtu', 'vtk', 'abaqus', 'ansys', 'gmsh', 'tetgen', 'format', 'input', 'import', 'ingest', 'compute', 'generate', 'create'] + + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_FILE_PATH_KEY = 'input_file_path' + CREATED_GEOMETRY_PATH = 'created_geometry_path' + VERTEX_ATTR_MATRIX_NAME = 'vertex_attr_matrix_name' + CELL_ATTR_MATRIX_NAME = 'cell_attr_matrix_name' + VERTEX_ARRAY_NAME = 'vertex_array_name' + CELL_ARRAY_NAME = 'cell_array_name' + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. + """ + 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(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.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')) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. + :returns: + :rtype: nx.IFilter.PreflightResult + """ + # Extract the values from the user interface from the 'args' + input_file_path: Path = args[ReadMeshFile.INPUT_FILE_PATH_KEY] + created_geometry_path: nx.DataPath = args[ReadMeshFile.CREATED_GEOMETRY_PATH] + vertex_attr_matrix_name: str = args[ReadMeshFile.VERTEX_ATTR_MATRIX_NAME] + cell_attr_matrix_name: str = args[ReadMeshFile.CELL_ATTR_MATRIX_NAME] + vertex_array_name: str = args[ReadMeshFile.VERTEX_ARRAY_NAME] + cell_array_name: str = args[ReadMeshFile.CELL_ARRAY_NAME] + + # Read the mesh file and then cache the contents... we need to know which geometry to create + global _input_file_path_cache, _mesh_cache + if _input_file_path_cache is None or _mesh_cache is None or _input_file_path_cache != input_file_path: + _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 + points_array: np.ndarray = _mesh_cache.points + + if cell_type == mu.EDGE_TYPE_STR: + output_actions.append_action(nx.CreateEdgeGeometryAction(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.TRIANGLE_TYPE_STR: + output_actions.append_action(nx.CreateTriangleGeometryAction(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.QUAD_TYPE_STR: + output_actions.append_action(nx.CreateQuadGeometryAction(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.TETRAHEDRAL_TYPE_STR: + 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)) + 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.")]) + + # NOTE: This filter makes the assumption for cell and point data that the first element in the numpy shape + # list is the only tuple dimension, and every other shape list element is part of the component dimensions! + + # Create the cell data arrays + cell_attr_mat_path = created_geometry_path.create_child_path(cell_attr_matrix_name) + for cell_data_array_name, cell_data_array in _mesh_cache.cell_data.items(): + cell_data_array = cell_data_array[0] + cell_array_path = cell_attr_mat_path.create_child_path(cell_data_array_name) + cell_data_tuple_dims = [cell_data_array.shape[0]] + cell_data_comp_dims = cell_data_array.shape[1:] + if not cell_data_comp_dims: + cell_data_comp_dims = [1] + output_actions.append_action(nx.CreateArrayAction(nx.convert_np_dtype_to_datatype(cell_data_array.dtype), cell_data_tuple_dims, cell_data_comp_dims, cell_array_path)) + + # 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:] + if not point_data_comp_dims: + point_data_comp_dims = [1] + output_actions.append_action(nx.CreateArrayAction(nx.convert_np_dtype_to_datatype(point_data_array.dtype), point_data_tuple_dims, point_data_comp_dims, point_array_path)) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=output_actions, warnings=warnings) + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + # Extract the values from the user interface from the 'args' + # This is basically repeated from the preflight because the variables are scoped to the method() + created_geometry_path: nx.DataPath = args[ReadMeshFile.CREATED_GEOMETRY_PATH] + vertex_attr_matrix_name: str = args[ReadMeshFile.VERTEX_ATTR_MATRIX_NAME] + cell_attr_matrix_name: str = args[ReadMeshFile.CELL_ATTR_MATRIX_NAME] + + # Grab the geometry from the data structure + geometry: nx.INodeGeometry1D = data_structure[created_geometry_path] + + # Grab the proper cells array from the geometry + 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[:] + + # Copy over the cell data + cell_attr_mat_path = created_geometry_path.create_child_path(cell_attr_matrix_name) + for cell_data_array_name, cell_data_array in _mesh_cache.cell_data.items(): + 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][:] + + # 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][:] + + return nx.Result() \ No newline at end of file diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAbaqusFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAbaqusFile.py new file mode 100644 index 0000000000..7bd7d4959a --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAbaqusFile.py @@ -0,0 +1,108 @@ +from typing import List +from pathlib import Path +import simplnx as nx +import meshio +from .utilities import meshio_utilities as mu + +class WriteAbaqusFile: + +# ----------------------------------------------------------------------------- +# These methods should not be edited +# ----------------------------------------------------------------------------- + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('836ba7c0-7f73-4a20-933a-185ff265a104') + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'WriteAbaqusFile' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'WriteAbaqusFile' + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: WriteAbaqusFile + """ + return WriteAbaqusFile() + +# ----------------------------------------------------------------------------- +# These methods CAN (and probably should) be updated. For instance, the +# human_name() is what users of the filter will see in the DREAM3D-NX GUI. You +# might want to consider putting spaces between workd, using proper capitalization +# and putting "(Python)" at the end of the name (or beginning if you want the +# filter list to group your filters togther) +# ----------------------------------------------------------------------------- + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'MeshIO: Write Abaqus File (Python)' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python', 'WriteAbaqusFile', 'inp', 'format', 'output', 'export', 'abaqus', 'compute', 'generate', 'create'] + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_GEOMETRY_KEY = 'input_geometry_path' + OUTPUT_FILE_PATH_KEY = 'output_file_path' + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. + """ + 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(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)) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. + :returns: + :rtype: nx.IFilter.PreflightResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteAbaqusFile.INPUT_GEOMETRY_KEY] + + errors, warnings = mu.preflight_meshio_writer_filter(data_structure=data_structure, input_geometry_path=input_geometry_path) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=nx.OutputActions(), errors=errors, warnings=warnings, preflight_values=None) + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + + # Extract the values from the user interface from the 'args' + 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) \ No newline at end of file diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAnsysFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAnsysFile.py new file mode 100644 index 0000000000..99566baaeb --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteAnsysFile.py @@ -0,0 +1,109 @@ +from typing import List +from pathlib import Path +import simplnx as nx +import meshio +from .utilities import meshio_utilities as mu + +class WriteAnsysFile: + +# ----------------------------------------------------------------------------- +# These methods should not be edited +# ----------------------------------------------------------------------------- + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('81db3801-4e1f-436e-9a2b-94cc08e9dfbc') + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'WriteAnsysFile' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'WriteAnsysFile' + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: WriteAnsysFile + """ + return WriteAnsysFile() + +# ----------------------------------------------------------------------------- +# These methods CAN (and probably should) be updated. For instance, the +# human_name() is what users of the filter will see in the DREAM3D-NX GUI. You +# might want to consider putting spaces between workd, using proper capitalization +# and putting "(Python)" at the end of the name (or beginning if you want the +# filter list to group your filters togther) +# ----------------------------------------------------------------------------- + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'MeshIO: Write Ansys File (Python)' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python', 'WriteAnsysFile', '.msh', 'msh', 'format', 'export', 'output', 'compute', 'generate', 'create'] + + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_GEOMETRY_KEY = 'input_geometry_path' + OUTPUT_FILE_PATH_KEY = 'output_file_path' + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. + """ + 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(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)) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. + :returns: + :rtype: nx.IFilter.PreflightResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteAnsysFile.INPUT_GEOMETRY_KEY] + + errors, warnings = mu.preflight_meshio_writer_filter(data_structure=data_structure, input_geometry_path=input_geometry_path) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=nx.OutputActions(), errors=errors, warnings=warnings, preflight_values=None) + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + + # Extract the values from the user interface from the 'args' + 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) \ No newline at end of file diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteGmshFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteGmshFile.py new file mode 100644 index 0000000000..d5f0a99a10 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteGmshFile.py @@ -0,0 +1,117 @@ +from typing import List +from pathlib import Path +import simplnx as nx +import meshio +from .utilities import meshio_utilities as mu + +class WriteGmshFile: + +# ----------------------------------------------------------------------------- +# These methods should not be edited +# ----------------------------------------------------------------------------- + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('2ab68db2-2923-471f-a8b7-cd506a852923') + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'WriteGmshFile' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'WriteGmshFile' + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: WriteGmshFile + """ + return WriteGmshFile() + +# ----------------------------------------------------------------------------- +# These methods CAN (and probably should) be updated. For instance, the +# human_name() is what users of the filter will see in the DREAM3D-NX GUI. You +# might want to consider putting spaces between workd, using proper capitalization +# and putting "(Python)" at the end of the name (or beginning if you want the +# filter list to group your filters togther) +# ----------------------------------------------------------------------------- + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'MeshIO: Write Gmsh File (Python)' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python', 'WriteGmshFile', '.msh', 'msh', 'format', 'export', 'output', 'compute', 'generate', 'create'] + + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_GEOMETRY_KEY = 'input_geometry_path' + CELL_DATA_ARRAY_PATHS_KEY = 'cell_data_array_paths' + POINT_DATA_ARRAY_PATHS_KEY = 'point_data_array_paths' + OUTPUT_FILE_PATH_KEY = 'output_file_path' + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. + """ + 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.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())) + + params.insert(params.Separator("Output Parameters")) + params.insert(nx.FileSystemPathParameter(WriteGmshFile.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)) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. + :returns: + :rtype: nx.IFilter.PreflightResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteGmshFile.INPUT_GEOMETRY_KEY] + cell_data_array_paths = args[WriteGmshFile.CELL_DATA_ARRAY_PATHS_KEY] + point_data_array_paths = args[WriteGmshFile.POINT_DATA_ARRAY_PATHS_KEY] + + errors, warnings = mu.preflight_meshio_writer_filter(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) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=nx.OutputActions(), errors=errors, warnings=warnings, preflight_values=None) + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteGmshFile.INPUT_GEOMETRY_KEY] + output_file_path: Path = args[WriteGmshFile.OUTPUT_FILE_PATH_KEY] + 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) \ 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 new file mode 100644 index 0000000000..02b18a6fe1 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteMedFile.py @@ -0,0 +1,117 @@ +from typing import List +from pathlib import Path +import simplnx as nx +import meshio +from .utilities import meshio_utilities as mu + +class WriteMedFile: + +# ----------------------------------------------------------------------------- +# These methods should not be edited +# ----------------------------------------------------------------------------- + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('d3e11fbf-ff08-45c2-a55f-920441db940d') + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'WriteMedFile' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'WriteMedFile' + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: WriteMedFile + """ + return WriteMedFile() + +# ----------------------------------------------------------------------------- +# These methods CAN (and probably should) be updated. For instance, the +# human_name() is what users of the filter will see in the DREAM3D-NX GUI. You +# might want to consider putting spaces between workd, using proper capitalization +# and putting "(Python)" at the end of the name (or beginning if you want the +# filter list to group your filters togther) +# ----------------------------------------------------------------------------- + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'MeshIO: Write Med/Salome File (Python)' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python', 'WriteMedFile', '.med', 'med', 'format', 'export', 'output', 'compute', 'generate', 'create'] + + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_GEOMETRY_KEY = 'input_geometry_path' + CELL_DATA_ARRAY_PATHS_KEY = 'cell_data_array_paths' + POINT_DATA_ARRAY_PATHS_KEY = 'point_data_array_paths' + OUTPUT_FILE_PATH_KEY = 'output_file_path' + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. + """ + 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.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())) + + params.insert(params.Separator("Output Parameters")) + params.insert(nx.FileSystemPathParameter(WriteMedFile.OUTPUT_FILE_PATH_KEY, 'Output File (.med)', 'The output file that contains the specified geometry as a mesh.', '', extensions_type={'.med'}, path_type=nx.FileSystemPathParameter.PathType.OutputFile)) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. + :returns: + :rtype: nx.IFilter.PreflightResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteMedFile.INPUT_GEOMETRY_KEY] + cell_data_array_paths = args[WriteMedFile.CELL_DATA_ARRAY_PATHS_KEY] + point_data_array_paths = args[WriteMedFile.POINT_DATA_ARRAY_PATHS_KEY] + + errors, warnings = mu.preflight_meshio_writer_filter(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) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=nx.OutputActions(), errors=errors, warnings=warnings, preflight_values=None) + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteMedFile.INPUT_GEOMETRY_KEY] + output_file_path: Path = args[WriteMedFile.OUTPUT_FILE_PATH_KEY] + 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) \ No newline at end of file diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteTetGenFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteTetGenFile.py new file mode 100644 index 0000000000..237beb7e6b --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteTetGenFile.py @@ -0,0 +1,117 @@ +from typing import List +from pathlib import Path +import simplnx as nx +import meshio +from .utilities import meshio_utilities as mu + +class WriteTetGenFile: + +# ----------------------------------------------------------------------------- +# These methods should not be edited +# ----------------------------------------------------------------------------- + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('0b879f55-f8bb-4d3a-a36d-6abc4b1151ba') + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'WriteTetGenFile' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'WriteTetGenFile' + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: WriteTetGenFile + """ + return WriteTetGenFile() + +# ----------------------------------------------------------------------------- +# These methods CAN (and probably should) be updated. For instance, the +# human_name() is what users of the filter will see in the DREAM3D-NX GUI. You +# might want to consider putting spaces between workd, using proper capitalization +# and putting "(Python)" at the end of the name (or beginning if you want the +# filter list to group your filters togther) +# ----------------------------------------------------------------------------- + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'MeshIO: Write TetGen File (Python)' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python', 'WriteTetGenFile', '.node', 'node', '.ele', 'ele', 'format', 'export', 'output', 'compute', 'generate', 'create'] + + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_GEOMETRY_KEY = 'input_geometry_path' + CELL_DATA_ARRAY_PATHS_KEY = 'cell_data_array_paths' + POINT_DATA_ARRAY_PATHS_KEY = 'point_data_array_paths' + OUTPUT_FILE_PATH_KEY = 'output_file_path' + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. + """ + params = nx.Parameters() + + params.insert(params.Separator("Input Parameters")) + params.insert(nx.GeometrySelectionParameter(WriteTetGenFile.INPUT_GEOMETRY_KEY, 'Input Geometry', 'The input geometry that will be written to the TetGen file.', nx.DataPath(), allowed_types={nx.IGeometry.Type.Tetrahedral})) + params.insert(nx.MultiArraySelectionParameter(WriteTetGenFile.CELL_DATA_ARRAY_PATHS_KEY, 'Cell Data Arrays To Write', 'The cell data arrays to write to the TetGen file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) + params.insert(nx.MultiArraySelectionParameter(WriteTetGenFile.POINT_DATA_ARRAY_PATHS_KEY, 'Point Data Arrays To Write', 'The point data arrays to write to the TetGen file.', [], allowed_types={nx.IArray.ArrayType.DataArray}, allowed_data_types=nx.get_all_data_types())) + + params.insert(params.Separator("Output Parameters")) + params.insert(nx.FileSystemPathParameter(WriteTetGenFile.OUTPUT_FILE_PATH_KEY, 'Output File (.node/.ele)', 'The output node/element file that contains the vertices/cells of the mesh. If you choose the .node extension, the .ele file will be created alongside it with the same file name, and vice versa.', '', extensions_type={'.node', '.ele'}, path_type=nx.FileSystemPathParameter.PathType.OutputFile)) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. + :returns: + :rtype: nx.IFilter.PreflightResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteTetGenFile.INPUT_GEOMETRY_KEY] + cell_data_array_paths = args[WriteTetGenFile.CELL_DATA_ARRAY_PATHS_KEY] + point_data_array_paths = args[WriteTetGenFile.POINT_DATA_ARRAY_PATHS_KEY] + + errors, warnings = mu.preflight_meshio_writer_filter(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) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=nx.OutputActions(), errors=errors, warnings=warnings, preflight_values=None) + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteTetGenFile.INPUT_GEOMETRY_KEY] + output_file_path: Path = args[WriteTetGenFile.OUTPUT_FILE_PATH_KEY] + 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 newline at end of file diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteVtuFile.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteVtuFile.py new file mode 100644 index 0000000000..59e221c775 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/WriteVtuFile.py @@ -0,0 +1,135 @@ +from typing import List +from enum import Enum +from pathlib import Path +import simplnx as nx +from .utilities import meshio_utilities as mu + +class WriteVtuFile: + +# ----------------------------------------------------------------------------- +# These methods should not be edited +# ----------------------------------------------------------------------------- + def uuid(self) -> nx.Uuid: + """This returns the UUID of the filter. Each filter has a unique UUID value + :return: The Filter's Uuid value + :rtype: string + """ + return nx.Uuid('3198f046-a243-411e-ba9c-5d91265cf58e') + + def class_name(self) -> str: + """The returns the name of the class that implements the filter + :return: The name of the implementation class + :rtype: string + """ + return 'WriteVtuFile' + + def name(self) -> str: + """The returns the name of filter + :return: The name of the filter + :rtype: string + """ + return 'WriteVtuFile' + + def clone(self): + """Clones the filter + :return: A new instance of the filter + :rtype: WriteVtuFile + """ + return WriteVtuFile() + +# ----------------------------------------------------------------------------- +# These methods CAN (and probably should) be updated. For instance, the +# human_name() is what users of the filter will see in the DREAM3D-NX GUI. You +# might want to consider putting spaces between workd, using proper capitalization +# and putting "(Python)" at the end of the name (or beginning if you want the +# filter list to group your filters togther) +# ----------------------------------------------------------------------------- + def human_name(self) -> str: + """This returns the name of the filter as a user of DREAM3DNX would see it + :return: The filter's human name + :rtype: string + """ + return 'MeshIO: Write VTU File (Python)' + + def default_tags(self) -> List[str]: + """This returns the default tags for this filter + :return: The default tags for the filter + :rtype: list + """ + return ['python', 'WriteVtuFile', 'vtk', '.vtu', 'vtu', 'format', 'export', 'output', 'compute', 'generate', 'create'] + + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + INPUT_GEOMETRY_KEY = 'input_geometry_path' + CELL_DATA_ARRAY_PATHS_KEY = 'cell_data_array_paths' + POINT_DATA_ARRAY_PATHS_KEY = 'point_data_array_paths' + OUTPUT_FILE_PATH_KEY = 'output_file_path' + COMPRESSION_TYPE_KEY = 'compression_type' + + class CompressionType(Enum): + Uncompressed = 0 + LZMA = 1 + ZLIB = 2 + + compression_type_strs = [ + 'Uncompressed', + 'LZMA', + 'ZLIB' + ] + + compression_type_values = {CompressionType.Uncompressed: None, + CompressionType.LZMA: 'lzma', + CompressionType.ZLIB: 'zlib'} + + def parameters(self) -> nx.Parameters: + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. + """ + 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.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())) + + params.insert(params.Separator("Output Parameters")) + params.insert(nx.FileSystemPathParameter(WriteVtuFile.OUTPUT_FILE_PATH_KEY, 'Output File (.vtu)', 'The output file that contains the specified geometry as a mesh.', '', extensions_type={'.vtu'}, path_type=nx.FileSystemPathParameter.PathType.OutputFile)) + params.insert(nx.ChoicesParameter(WriteVtuFile.COMPRESSION_TYPE_KEY, 'Output Compression Type', 'The compression type to use when writing the output file.', self.CompressionType.Uncompressed.value, self.compression_type_strs)) + + return params + + def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: + """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. + :returns: + :rtype: nx.IFilter.PreflightResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteVtuFile.INPUT_GEOMETRY_KEY] + cell_data_array_paths = args[WriteVtuFile.CELL_DATA_ARRAY_PATHS_KEY] + point_data_array_paths = args[WriteVtuFile.POINT_DATA_ARRAY_PATHS_KEY] + + errors, warnings = mu.preflight_meshio_writer_filter(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) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=nx.OutputActions(), errors=errors, warnings=warnings, preflight_values=None) + + def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: + """ This method actually executes the filter algorithm and reports results. + :returns: + :rtype: nx.IFilter.ExecuteResult + """ + + # Extract the values from the user interface from the 'args' + input_geometry_path: nx.DataPath = args[WriteVtuFile.INPUT_GEOMETRY_KEY] + output_file_path: Path = args[WriteVtuFile.OUTPUT_FILE_PATH_KEY] + compression_type: self.CompressionType = self.CompressionType(args[WriteVtuFile.COMPRESSION_TYPE_KEY]) + 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]) \ No newline at end of file diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/__init__.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/__init__.py index 95efd194b3..e02bde4431 100644 --- a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/__init__.py +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/__init__.py @@ -1,4 +1,4 @@ -from NXDataAnalysisToolkit.Plugin import NXDataAnalysisToolkit +from .Plugin import NXDataAnalysisToolkit __all__ = ['NXDataAnalysisToolkit', 'get_plugin'] @@ -8,7 +8,7 @@ # FILTER_START: CalculateHistogramFilter try: - from NXDataAnalysisToolkit.CalculateHistogramFilter import CalculateHistogramFilter + from .CalculateHistogramFilter import CalculateHistogramFilter __all__.append('CalculateHistogramFilter') except ImportError: pass @@ -16,7 +16,7 @@ # FILTER_START: InterpolateGridDataFilter try: - from NXDataAnalysisToolkit.InterpolateGridDataFilter import InterpolateGridDataFilter + from .InterpolateGridDataFilter import InterpolateGridDataFilter __all__.append('InterpolateGridDataFilter') except ImportError: pass @@ -24,7 +24,7 @@ # FILTER_START: CliReaderFilter try: - from NXDataAnalysisToolkit.CliReaderFilter import CliReaderFilter + from .CliReaderFilter import CliReaderFilter __all__.append('CliReaderFilter') except ImportError: pass @@ -32,7 +32,7 @@ # FILTER_START: ContourDetectionFilter try: - from NXDataAnalysisToolkit.ContourDetectionFilter import ContourDetectionFilter + from .ContourDetectionFilter import ContourDetectionFilter __all__.append('ContourDetectionFilter') except ImportError: pass @@ -40,7 +40,7 @@ # FILTER_START: NPSortArray try: - from NXDataAnalysisToolkit.NPSortArray import NPSortArray + from .NPSortArray import NPSortArray __all__.append('NPSortArray') except ImportError: pass @@ -48,11 +48,67 @@ # FILTER_START: ReadPeregrineHDF5File try: - from NXDataAnalysisToolkit.ReadPeregrineHDF5File import ReadPeregrineHDF5File + from .ReadPeregrineHDF5File import ReadPeregrineHDF5File __all__.append('ReadPeregrineHDF5File') except ImportError: pass # FILTER_END: ReadPeregrineHDF5File +# FILTER_START: WriteAbaqusFile +try: + from .WriteAbaqusFile import WriteAbaqusFile + __all__.append('WriteAbaqusFile') +except ImportError: + pass +# FILTER_END: WriteAbaqusFile + +# FILTER_START: WriteAnsysFile +try: + from .WriteAnsysFile import WriteAnsysFile + __all__.append('WriteAnsysFile') +except ImportError: + pass +# FILTER_END: WriteAnsysFile + +# FILTER_START: WriteMedFile +try: + from .WriteMedFile import WriteMedFile + __all__.append('WriteMedFile') +except ImportError: + pass +# FILTER_END: WriteMedFile + +# FILTER_START: WriteGmshFile +try: + from .WriteGmshFile import WriteGmshFile + __all__.append('WriteGmshFile') +except ImportError: + pass +# FILTER_END: WriteGmshFile + +# FILTER_START: WriteTetGenFile +try: + from .WriteTetGenFile import WriteTetGenFile + __all__.append('WriteTetGenFile') +except ImportError: + pass +# FILTER_END: WriteTetGenFile + +# FILTER_START: WriteVtuFile +try: + from .WriteVtuFile import WriteVtuFile + __all__.append('WriteVtuFile') +except ImportError: + pass +# FILTER_END: WriteVtuFile + +# FILTER_START: ReadMeshFile +try: + from .ReadMeshFile import ReadMeshFile + __all__.append('ReadMeshFile') +except ImportError: + pass +# FILTER_END: ReadMeshFile + def get_plugin(): return NXDataAnalysisToolkit() diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/__init__.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/meshio_utilities.py b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/meshio_utilities.py new file mode 100644 index 0000000000..7e64a03aa7 --- /dev/null +++ b/wrapping/python/plugins/NXDataAnalysisToolkit/src/NXDataAnalysisToolkit/utilities/meshio_utilities.py @@ -0,0 +1,92 @@ +import simplnx as nx +import meshio +import numpy as np +from typing import List +from pathlib import Path + +EDGE_TYPE_STR = 'line' +TRIANGLE_TYPE_STR = 'triangle' +QUAD_TYPE_STR = 'quad' +TETRAHEDRAL_TYPE_STR = 'tetra' +HEXAHEDRAL_TYPE_STR = 'hexahedron' + +def get_geometry_cell_tuple_dimensions(geom: nx.INodeGeometry0D) -> list: + if geom.type == nx.DataObject.DataObjectType.EdgeGeom: + return geom.edges.tdims + if geom.type == nx.DataObject.DataObjectType.TriangleGeom or geom.type == nx.DataObject.DataObjectType.QuadGeom: + return geom.faces.tdims + if geom.type == nx.DataObject.DataObjectType.TetrahedralGeom or geom.type == nx.DataObject.DataObjectType.HexahedralGeom: + return geom.polyhedra.tdims + +def create_meshio_cells_list(geom: nx.INodeGeometry0D) -> list: + # Meshio requires the cells to be either int32 or int64, so use int64 + if geom.type == nx.DataObject.DataObjectType.EdgeGeom: + cells_list = geom.edges.npview().astype(np.int64) + cells = [(EDGE_TYPE_STR, cells_list)] + elif geom.type == nx.DataObject.DataObjectType.TriangleGeom: + cells_list = geom.faces.npview().astype(np.int64) + cells = [(TRIANGLE_TYPE_STR, cells_list)] + elif geom.type == nx.DataObject.DataObjectType.QuadGeom: + cells_list = geom.faces.npview().astype(np.int64) + cells = [(QUAD_TYPE_STR, cells_list)] + elif geom.type == nx.DataObject.DataObjectType.TetrahedralGeom: + cells_list = geom.polyhedra.npview().astype(np.int64) + cells = [(TETRAHEDRAL_TYPE_STR, cells_list)] + else: # Hexahedral geometry + cells_list = geom.polyhedra.npview().astype(np.int64) + cells = [(HEXAHEDRAL_TYPE_STR, cells_list)] + + return cells + +def preflight_meshio_writer_filter(data_structure: nx.DataStructure, input_geometry_path: nx.DataPath, cell_data_array_paths: List[nx.DataPath] = None, point_data_array_paths: List[nx.DataPath] = None) -> nx.IFilter.PreflightResult: + geom = data_structure[input_geometry_path] + geom_cell_tdims = get_geometry_cell_tuple_dimensions(geom) + + warnings: List[nx.Warning] = [] + + if cell_data_array_paths is not None: + for data_array_path in cell_data_array_paths: + data_array: nx.IDataArray = data_structure[data_array_path] + if data_array.tdims != geom_cell_tdims: + warnings.append(nx.Warning(-3010, f"Cell data array '{data_array_path}' has tuple dimensions {data_array.tdims} but the input geometry requires tuple dimensions {geom_cell_tdims}. This MAY still work if the pipeline does not know the actual number of cells yet. This can happen if the geometry was created earlier in the pipeline.")) + + if point_data_array_paths is not None: + for data_array_path in point_data_array_paths: + data_array: nx.IDataArray = data_structure[data_array_path] + if data_array.tdims != geom.vertices.tdims: + warnings.append(nx.Warning(-3011, 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}. This MAY still work if the pipeline does not know the actual number of vertices yet. This can happen if the geometry was created earlier in the pipeline.")) + + 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): + 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) + + # Create meshio point_data + point_data = {} + if point_data_array_paths is not None: + 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) + + # Create meshio Mesh + mesh = meshio.Mesh(points=list(geom.vertices.npview().astype(np.float64)), cells=cells, cell_data=cell_data, point_data=point_data) + + # Output the mesh + meshio.write(output_file_path, mesh, file_format=file_format, **write_kwargs) + + return nx.Result() \ No newline at end of file