diff --git a/setup.cfg b/setup.cfg index 66af644c3..2ac70d25b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = meshio -version = 5.1.0 +version = 5.1.1 author = Nico Schlömer et al. author_email = nico.schloemer@gmail.com description = I/O for many mesh formats diff --git a/src/meshio/__init__.py b/src/meshio/__init__.py index 716f56560..16675ffbb 100644 --- a/src/meshio/__init__.py +++ b/src/meshio/__init__.py @@ -32,7 +32,6 @@ xdmf, ) from .__about__ import __version__ -from ._common import topological_dimension from ._exceptions import ReadError, WriteError from ._helpers import ( extension_to_filetype, diff --git a/src/meshio/_common.py b/src/meshio/_common.py index a477bc914..385843996 100644 --- a/src/meshio/_common.py +++ b/src/meshio/_common.py @@ -83,72 +83,6 @@ "tetra286": 286, } -topological_dimension = { - "line": 1, - "triangle": 2, - "quad": 2, - "tetra": 3, - "hexahedron": 3, - "wedge": 3, - "pyramid": 3, - "line3": 1, - "triangle6": 2, - "quad9": 2, - "tetra10": 3, - "hexahedron27": 3, - "wedge18": 3, - "pyramid14": 3, - "vertex": 0, - "quad8": 2, - "hexahedron20": 3, - "triangle10": 2, - "triangle15": 2, - "triangle21": 2, - "line4": 1, - "line5": 1, - "line6": 1, - "tetra20": 3, - "tetra35": 3, - "tetra56": 3, - "quad16": 2, - "quad25": 2, - "quad36": 2, - "triangle28": 2, - "triangle36": 2, - "triangle45": 2, - "triangle55": 2, - "triangle66": 2, - "quad49": 2, - "quad64": 2, - "quad81": 2, - "quad100": 2, - "quad121": 2, - "line7": 1, - "line8": 1, - "line9": 1, - "line10": 1, - "line11": 1, - "tetra84": 3, - "tetra120": 3, - "tetra165": 3, - "tetra220": 3, - "tetra286": 3, - "wedge40": 3, - "wedge75": 3, - "hexahedron64": 3, - "hexahedron125": 3, - "hexahedron216": 3, - "hexahedron343": 3, - "hexahedron512": 3, - "hexahedron729": 3, - "hexahedron1000": 3, - "wedge126": 3, - "wedge196": 3, - "wedge288": 3, - "wedge405": 3, - "wedge550": 3, -} - def cell_data_from_raw(cells, cell_data_raw): cs = np.cumsum([len(c) for c in cells])[:-1] diff --git a/src/meshio/_mesh.py b/src/meshio/_mesh.py index 7f331d397..7efd21824 100644 --- a/src/meshio/_mesh.py +++ b/src/meshio/_mesh.py @@ -8,18 +8,97 @@ from ._common import num_nodes_per_cell +topological_dimension = { + "line": 1, + "polygon": 2, + "triangle": 2, + "quad": 2, + "tetra": 3, + "hexahedron": 3, + "wedge": 3, + "pyramid": 3, + "line3": 1, + "triangle6": 2, + "quad9": 2, + "tetra10": 3, + "hexahedron27": 3, + "wedge18": 3, + "pyramid14": 3, + "vertex": 0, + "quad8": 2, + "hexahedron20": 3, + "triangle10": 2, + "triangle15": 2, + "triangle21": 2, + "line4": 1, + "line5": 1, + "line6": 1, + "tetra20": 3, + "tetra35": 3, + "tetra56": 3, + "quad16": 2, + "quad25": 2, + "quad36": 2, + "triangle28": 2, + "triangle36": 2, + "triangle45": 2, + "triangle55": 2, + "triangle66": 2, + "quad49": 2, + "quad64": 2, + "quad81": 2, + "quad100": 2, + "quad121": 2, + "line7": 1, + "line8": 1, + "line9": 1, + "line10": 1, + "line11": 1, + "tetra84": 3, + "tetra120": 3, + "tetra165": 3, + "tetra220": 3, + "tetra286": 3, + "wedge40": 3, + "wedge75": 3, + "hexahedron64": 3, + "hexahedron125": 3, + "hexahedron216": 3, + "hexahedron343": 3, + "hexahedron512": 3, + "hexahedron729": 3, + "hexahedron1000": 3, + "wedge126": 3, + "wedge196": 3, + "wedge288": 3, + "wedge405": 3, + "wedge550": 3, + "VTK_LAGRANGE_CURVE": 1, + "VTK_LAGRANGE_TRIANGLE": 2, + "VTK_LAGRANGE_QUADRILATERAL": 2, + "VTK_LAGRANGE_TETRAHEDRON": 3, + "VTK_LAGRANGE_HEXAHEDRON": 3, + "VTK_LAGRANGE_WEDGE": 3, + "VTK_LAGRANGE_PYRAMID": 3, +} + class CellBlock: def __init__( self, - type: str, + cell_type: str, data: list | np.ndarray, tags: list[str] | None = None, ): - self.type = type + self.type = cell_type self.data = data - if not type.startswith("polyhedron"): + + if cell_type.startswith("polyhedron"): + self.dim = 3 + else: self.data = np.asarray(self.data) + self.dim = topological_dimension[cell_type] + self.tags = [] if tags is None else tags def __repr__(self): diff --git a/src/meshio/gmsh/_gmsh22.py b/src/meshio/gmsh/_gmsh22.py index 29feb031f..23d6f4a51 100644 --- a/src/meshio/gmsh/_gmsh22.py +++ b/src/meshio/gmsh/_gmsh22.py @@ -2,6 +2,8 @@ I/O for Gmsh's msh format, cf. . """ +from __future__ import annotations + import logging import numpy as np @@ -128,7 +130,9 @@ def _read_cells(f, cells, point_tags, is_ascii): _read_cells_ascii(f, cells, cell_tags, total_num_cells) else: _read_cells_binary(f, cells, cell_tags, total_num_cells) - cells[:] = _gmsh_to_meshio_order(cells) + + # override cells in-place + cells[:] = [(key, _gmsh_to_meshio_order(key, values)) for key, values in cells] point_tags = np.asarray(point_tags, dtype=np.int32) - 1 remap = -np.ones((np.max(point_tags) + 1,), dtype=np.int32) @@ -263,8 +267,6 @@ def write(filename, mesh, float_fmt=".16e", binary=True): """Writes msh files, cf. . """ - cells = _meshio_to_gmsh_order(mesh.cells) - # Filter the point data: gmsh:dim_tags are tags, the rest is actual point data. point_data = {} for key, d in mesh.point_data.items(): @@ -288,7 +290,9 @@ def write(filename, mesh, float_fmt=".16e", binary=True): logging.warning( f"Appending zeros to replace the missing {tag[5:]} tag data." ) - tag_data[tag] = [np.zeros(len(x.data), dtype=c_int) for x in mesh.cells] + tag_data[tag] = [ + np.zeros(len(cell_block), dtype=c_int) for cell_block in mesh.cells + ] with open(filename, "wb") as fh: mode_idx = 1 if binary else 0 @@ -303,7 +307,7 @@ def write(filename, mesh, float_fmt=".16e", binary=True): _write_physical_names(fh, mesh.field_data) _write_nodes(fh, mesh.points, float_fmt, binary) - _write_elements(fh, cells, tag_data, binary) + _write_elements(fh, mesh.cells, tag_data, binary) if mesh.gmsh_periodic is not None: _write_periodic(fh, mesh.gmsh_periodic, float_fmt) @@ -335,31 +339,18 @@ def _write_nodes(fh, points, float_fmt, binary): fh.write(b"$EndNodes\n") -def _write_elements(fh, cells, tag_data, binary): +def _write_elements(fh, cells: list[CellBlock], tag_data, binary: bool): # write elements fh.write(b"$Elements\n") # count all cells - total_num_cells = 0 - for k, cell_block in enumerate(cells): - if isinstance(cell_block, tuple): - node_idcs = cell_block[1] - else: - assert isinstance(cell_block, CellBlock) - node_idcs = cell_block.data - - total_num_cells += len(node_idcs) - + total_num_cells = sum(len(cell_block) for cell_block in cells) fh.write(f"{total_num_cells}\n".encode()) consecutive_index = 0 for k, cell_block in enumerate(cells): - if isinstance(cell_block, tuple): - cell_type, node_idcs = cell_block - else: - assert isinstance(cell_block, CellBlock) - cell_type = cell_block.type - node_idcs = cell_block.data + cell_type = cell_block.type + node_idcs = _meshio_to_gmsh_order(cell_type, cell_block.data) tags = [] for name in ["gmsh:physical", "gmsh:geometrical", "cell_tags"]: diff --git a/src/meshio/gmsh/_gmsh40.py b/src/meshio/gmsh/_gmsh40.py index c08235e40..46e62e96a 100644 --- a/src/meshio/gmsh/_gmsh40.py +++ b/src/meshio/gmsh/_gmsh40.py @@ -2,16 +2,13 @@ I/O for Gmsh's msh format (version 4.0, as used by Gmsh 4.1.5), cf. . """ +from __future__ import annotations + from functools import partial import numpy as np -from .._common import ( - cell_data_from_raw, - num_nodes_per_cell, - raw_from_cell_data, - topological_dimension, -) +from .._common import cell_data_from_raw, num_nodes_per_cell, raw_from_cell_data from .._exceptions import ReadError from .._mesh import CellBlock, Mesh from .common import ( @@ -32,7 +29,7 @@ c_double = np.dtype("d") -def read_buffer(f, is_ascii, data_size): +def read_buffer(f, is_ascii: bool, data_size) -> Mesh: # Initialize the optional data fields points = [] field_data = {} @@ -53,13 +50,11 @@ def read_buffer(f, is_ascii, data_size): if environ == "PhysicalNames": _read_physical_names(f, field_data) elif environ == "Entities": - physical_tags = _read_entities(f, is_ascii, data_size) + physical_tags = _read_entities(f, is_ascii) elif environ == "Nodes": - points, point_tags = _read_nodes(f, is_ascii, data_size) + points, point_tags = _read_nodes(f, is_ascii) elif environ == "Elements": - cells, cell_tags = _read_elements( - f, point_tags, physical_tags, is_ascii, data_size - ) + cells, cell_tags = _read_elements(f, point_tags, physical_tags, is_ascii) elif environ == "Periodic": periodic = _read_periodic(f, is_ascii) elif environ == "NodeData": @@ -90,7 +85,7 @@ def read_buffer(f, is_ascii, data_size): ) -def _read_entities(f, is_ascii, data_size): +def _read_entities(f, is_ascii: bool): physical_tags = tuple({} for _ in range(4)) # dims 0, 1, 2, 3 fromfile = partial(np.fromfile, sep=" " if is_ascii else "") number = fromfile(f, c_ulong, 4) # dims 0, 1, 2, 3 @@ -108,7 +103,7 @@ def _read_entities(f, is_ascii, data_size): return physical_tags -def _read_nodes(f, is_ascii, data_size): +def _read_nodes(f, is_ascii): if is_ascii: # first line: numEntityBlocks(unsigned long) numNodes(unsigned long) line = f.readline().decode() @@ -118,12 +113,12 @@ def _read_nodes(f, is_ascii, data_size): tags = np.empty(total_num_nodes, dtype=int) idx = 0 - for k in range(num_entity_blocks): + for _ in range(num_entity_blocks): # first line in the entity block: # tagEntity(int) dimEntity(int) typeNode(int) numNodes(unsigned long) line = f.readline().decode() - tag_entity, dim_entity, type_node, num_nodes = map(int, line.split()) - for i in range(num_nodes): + _, _, _, num_nodes = map(int, line.split()) + for _ in range(num_nodes): # tag(int) x(double) y(double) z(double) line = f.readline().decode() tag, x, y, z = line.split() @@ -156,15 +151,15 @@ def _read_nodes(f, is_ascii, data_size): return points, tags -def _read_elements(f, point_tags, physical_tags, is_ascii, data_size): +def _read_elements(f, point_tags, physical_tags, is_ascii): fromfile = partial(np.fromfile, sep=" " if is_ascii else "") # numEntityBlocks(unsigned long) numElements(unsigned long) - num_entity_blocks, total_num_elements = fromfile(f, c_ulong, 2) + num_entity_blocks, _ = fromfile(f, c_ulong, 2) data = [] - for k in range(num_entity_blocks): + for _ in range(num_entity_blocks): # tagEntity(int) dimEntity(int) typeEle(int) numElements(unsigned long) tag_entity, dim_entity, type_ele = fromfile(f, c_int, 3) (num_ele,) = fromfile(f, c_ulong, 1) @@ -196,7 +191,7 @@ def _read_elements(f, point_tags, physical_tags, is_ascii, data_size): cells = [] cell_data = {} for physical_tag, geom_tag, key, values in data: - cells.append((key, values)) + cells.append(CellBlock(key, _gmsh_to_meshio_order(key, values))) if physical_tag: if "gmsh:physical" not in cell_data: cell_data["gmsh:physical"] = [] @@ -206,7 +201,6 @@ def _read_elements(f, point_tags, physical_tags, is_ascii, data_size): if "gmsh:geometrical" not in cell_data: cell_data["gmsh:geometrical"] = [] cell_data["gmsh:geometrical"].append(np.full(len(values), geom_tag, int)) - cells[:] = _gmsh_to_meshio_order(cells) return cells, cell_data @@ -241,12 +235,10 @@ def _read_periodic(f, is_ascii): return periodic -def write(filename, mesh, float_fmt=".16e", binary=True): +def write(filename, mesh: Mesh, float_fmt: str = ".16e", binary: bool = True) -> None: """Writes msh files, cf. . """ - cells = _meshio_to_gmsh_order(mesh.cells) - with open(filename, "wb") as fh: mode_idx = 1 if binary else 0 size_of_double = 8 @@ -260,7 +252,7 @@ def write(filename, mesh, float_fmt=".16e", binary=True): _write_physical_names(fh, mesh.field_data) _write_nodes(fh, mesh.points, float_fmt, binary) - _write_elements(fh, cells, binary) + _write_elements(fh, mesh.cells, binary) if mesh.gmsh_periodic is not None: _write_periodic(fh, mesh.gmsh_periodic, float_fmt, binary) for name, dat in mesh.point_data.items(): @@ -270,7 +262,7 @@ def write(filename, mesh, float_fmt=".16e", binary=True): _write_data(fh, "ElementData", name, dat, binary) -def _write_nodes(fh, points, float_fmt, binary): +def _write_nodes(fh, points: np.ndarray, float_fmt: str, binary: bool) -> None: if points.shape[1] == 2: points = np.column_stack([points, np.zeros_like(points[:, 0])]) @@ -310,27 +302,22 @@ def _write_nodes(fh, points, float_fmt, binary): fh.write(b"$EndNodes\n") -def _write_elements(fh, cells, binary): +def _write_elements(fh, cell_blocks: list[CellBlock], binary: bool): # TODO respect binary # write elements fh.write(b"$Elements\n") if binary: - total_num_cells = sum(len(cell_block) for cell_block in cells) - np.array([len(cells), total_num_cells], dtype=c_ulong).tofile(fh) + total_num_cells = sum(len(cell_block) for cell_block in cell_blocks) + np.array([len(cell_blocks), total_num_cells], dtype=c_ulong).tofile(fh) consecutive_index = 0 - for cell_block in cells: - if isinstance(cell_block, tuple): - cell_type, node_idcs = cell_block - else: - assert isinstance(cell_block, CellBlock) - cell_type = cell_block.type - node_idcs = cell_block.data + for cell_block in cell_blocks: + node_idcs = _meshio_to_gmsh_order(cell_block.type, cell_block.data) # tagEntity(int) dimEntity(int) typeEle(int) numElements(unsigned long) np.array( - [1, topological_dimension[cell_type], _meshio_to_gmsh_type[cell_type]], + [1, cell_block.dim, _meshio_to_gmsh_type[cell_block.type]], dtype=c_int, ).tofile(fh) np.array([node_idcs.shape[0]], dtype=c_ulong).tofile(fh) @@ -356,22 +343,19 @@ def _write_elements(fh, cells, binary): fh.write(b"\n") else: # count all cells - total_num_cells = sum(len(cell_block) for cell_block in cells) - fh.write(f"{len(cells)} {total_num_cells}\n".encode()) + total_num_cells = sum(len(cell_block) for cell_block in cell_blocks) + fh.write(f"{len(cell_blocks)} {total_num_cells}\n".encode()) consecutive_index = 0 - for cell_block in cells: - if isinstance(cell_block, tuple): - cell_type, cell_data = cell_block - else: - assert isinstance(cell_block, CellBlock) - cell_type = cell_block.type - cell_data = cell_block.data + for cell_block in cell_blocks: + cell_type = cell_block.type + cell_data = _meshio_to_gmsh_order(cell_block.type, cell_block.data) + # tagEntity(int) dimEntity(int) typeEle(int) numElements(unsigned long) fh.write( "{} {} {} {}\n".format( 1, # tag - topological_dimension[cell_type], + cell_block.dim, _meshio_to_gmsh_type[cell_type], len(cell_data), ).encode() diff --git a/src/meshio/gmsh/_gmsh41.py b/src/meshio/gmsh/_gmsh41.py index 0c733e2ee..c379ede49 100644 --- a/src/meshio/gmsh/_gmsh41.py +++ b/src/meshio/gmsh/_gmsh41.py @@ -7,12 +7,7 @@ import numpy as np -from .._common import ( - cell_data_from_raw, - num_nodes_per_cell, - raw_from_cell_data, - topological_dimension, -) +from .._common import cell_data_from_raw, num_nodes_per_cell, raw_from_cell_data from .._exceptions import ReadError, WriteError from .._mesh import CellBlock, Mesh from .common import ( @@ -37,7 +32,7 @@ def _size_type(data_size): return np.dtype(f"u{data_size}") -def read_buffer(f, is_ascii, data_size): +def read_buffer(f, is_ascii: bool, data_size): # The format is specified at # . @@ -117,7 +112,7 @@ def read_buffer(f, is_ascii, data_size): ) -def _read_entities(f, is_ascii, data_size): +def _read_entities(f, is_ascii: bool, data_size): # Read the entity section. Return physical tags of the entities, and (for # entities of dimension > 0) the bounding entities (so points that form # the boundary of a line etc). @@ -146,7 +141,7 @@ def _read_entities(f, is_ascii, data_size): return physical_tags, bounding_entities -def _read_nodes(f, is_ascii, data_size): +def _read_nodes(f, is_ascii: bool, data_size): # Read node data: Node coordinates and tags. # Also find the entities of the nodes, and store this as point_data. # Note that entity tags are 1-offset within each dimension, thus it is @@ -156,9 +151,7 @@ def _read_nodes(f, is_ascii, data_size): c_size_t = _size_type(data_size) # numEntityBlocks numNodes minNodeTag maxNodeTag (all size_t) - num_entity_blocks, total_num_nodes, min_node_tag, max_node_tag = fromfile( - f, c_size_t, 4 - ) + num_entity_blocks, total_num_nodes, _, _ = fromfile(f, c_size_t, 4) points = np.empty((total_num_nodes, 3), dtype=float) tags = np.empty(total_num_nodes, dtype=int) @@ -167,7 +160,7 @@ def _read_nodes(f, is_ascii, data_size): # To save the entity block id for each node, initialize an array here, # populate it with num_nodes idx = 0 - for k in range(num_entity_blocks): + for _ in range(num_entity_blocks): # entityDim(int) entityTag(int) parametric(int) numNodes(size_t) dim, entity_tag, parametric = fromfile(f, c_int, 3) if parametric != 0: @@ -205,9 +198,7 @@ def _read_elements( c_size_t = _size_type(data_size) # numEntityBlocks numElements minElementTag maxElementTag (all size_t) - num_entity_blocks, total_num_elements, min_ele_tag, max_ele_tag = fromfile( - f, c_size_t, 4 - ) + num_entity_blocks, _, _, _ = fromfile(f, c_size_t, 4) data = [] cell_data = {} @@ -257,7 +248,7 @@ def _read_elements( cells = [] for physical_tag, bound_entity, geom_tag, key, values in data: - cells.append((key, values)) + cells.append(CellBlock(key, _gmsh_to_meshio_order(key, values))) if physical_tag: if "gmsh:physical" not in cell_data: cell_data["gmsh:physical"] = [] @@ -274,8 +265,6 @@ def _read_elements( cell_sets["gmsh:bounding_entities"] = [] cell_sets["gmsh:bounding_entities"].append(bound_entity) - cells[:] = _gmsh_to_meshio_order(cells) - return cells, cell_data, cell_sets @@ -306,8 +295,6 @@ def write(filename, mesh, float_fmt=".16e", binary=True): """Writes msh files, cf. . """ - cells = _meshio_to_gmsh_order(mesh.cells) - # Filter the point data: gmsh:dim_tags are tags, the rest is actual point data. point_data = {} for key, d in mesh.point_data.items(): @@ -337,9 +324,11 @@ def write(filename, mesh, float_fmt=".16e", binary=True): if mesh.field_data: _write_physical_names(fh, mesh.field_data) - _write_entities(fh, cells, tag_data, mesh.cell_sets, mesh.point_data, binary) + _write_entities( + fh, mesh.cells, tag_data, mesh.cell_sets, mesh.point_data, binary + ) _write_nodes(fh, mesh.points, mesh.cells, mesh.point_data, float_fmt, binary) - _write_elements(fh, cells, tag_data, binary) + _write_elements(fh, mesh.cells, tag_data, binary) if mesh.gmsh_periodic is not None: _write_periodic(fh, mesh.gmsh_periodic, float_fmt, binary) @@ -403,18 +392,6 @@ def _write_entities(fh, cells, tag_data, cell_sets, point_data, binary): if "gmsh:dim_tags" not in point_data: return - if binary: - for k, cell_block in enumerate(cells): - if isinstance(cell_block, tuple): - cell_type, cell_data = cell_block - else: - assert isinstance(cell_block, CellBlock) - cell_type = cell_block.type - cell_data = cell_block.data - if cell_data.dtype != c_size_t: - # Binary Gmsh needs c_size_t. Converting." - cells[k] = CellBlock(cell_type, cell_data.astype(c_size_t)) - fh.write(b"$Entities\n") # Array of entity tag (first row) and dimension (second row) per node. @@ -435,9 +412,9 @@ def _write_entities(fh, cells, tag_data, cell_sets, point_data, binary): # Array of dimension and entity tag per cell. Will be compared with the # similar not array. cell_dim_tags = np.empty((len(cells), 2), dtype=int) - for ci in range(len(cells)): + for ci, cell_block in enumerate(cells): cell_dim_tags[ci] = [ - topological_dimension[cells[ci].type], + cell_block.dim, tag_data["gmsh:geometrical"][ci][0], ] @@ -607,7 +584,7 @@ def _write_nodes(fh, points, cells, point_data, float_fmt, binary): + "to deal with more than one cell type. " ) - dim = topological_dimension[cells[0].type] + dim = cells[0].dim tag = 0 node_dim_tags = np.array([[dim, tag]]) # All nodes map to the (single) dimension-entity object @@ -647,10 +624,9 @@ def _write_nodes(fh, points, cells, point_data, float_fmt, binary): fh.write(b"\n") fh.write(b"$EndNodes\n") - return -def _write_elements(fh, cells, tag_data, binary): +def _write_elements(fh, cells, tag_data, binary: bool) -> None: """write the $Elements block $Elements @@ -676,25 +652,22 @@ def _write_elements(fh, cells, tag_data, binary): tag0 = 1 for ci, cell_block in enumerate(cells): - if isinstance(cell_block, tuple): - cell_type, node_idcs = cell_block - else: - assert isinstance(cell_block, CellBlock) - cell_type = cell_block.type - node_idcs = cell_block.data + node_idcs = _meshio_to_gmsh_order(cell_block.type, cell_block.data) + if node_idcs.dtype != c_size_t: + # Binary Gmsh needs c_size_t. Converting." + node_idcs = node_idcs.astype(c_size_t) # entityDim(int) entityTag(int) elementType(int) # numElementsBlock(size_t) - dim = topological_dimension[cell_type] # The entity tag should be equal within a CellBlock if "gmsh:geometrical" in tag_data: entity_tag = tag_data["gmsh:geometrical"][ci][0] else: entity_tag = 0 - cell_type = _meshio_to_gmsh_type[cell_type] - np.array([dim, entity_tag, cell_type], dtype=c_int).tofile(fh) + cell_type = _meshio_to_gmsh_type[cell_block.type] + np.array([cell_block.dim, entity_tag, cell_type], dtype=c_int).tofile(fh) n = node_idcs.shape[0] np.array([n], dtype=c_size_t).tofile(fh) @@ -724,38 +697,32 @@ def _write_elements(fh, cells, tag_data, binary): tag0 = 1 for ci, cell_block in enumerate(cells): - if isinstance(cell_block, tuple): - cell_type, cell_data = cell_block - else: - assert isinstance(cell_block, CellBlock) - cell_type = cell_block.type - cell_data = cell_block.data + node_idcs = _meshio_to_gmsh_order(cell_block.type, cell_block.data) + # entityDim(int) entityTag(int) elementType(int) numElementsBlock(size_t) - dim = topological_dimension[cell_type] # The entity tag should be equal within a CellBlock if "gmsh:geometrical" in tag_data: entity_tag = tag_data["gmsh:geometrical"][ci][0] else: entity_tag = 0 - cell_type = _meshio_to_gmsh_type[cell_type] - n = len(cell_data) - fh.write(f"{dim} {entity_tag} {cell_type} {n}\n".encode()) + cell_type = _meshio_to_gmsh_type[cell_block.type] + n = len(cell_block.data) + fh.write(f"{cell_block.dim} {entity_tag} {cell_type} {n}\n".encode()) np.savetxt( fh, # Gmsh indexes from 1 not 0 - np.column_stack([np.arange(tag0, tag0 + n), cell_data + 1]), + np.column_stack([np.arange(tag0, tag0 + n), node_idcs + 1]), "%d", " ", ) tag0 += n fh.write(b"$EndElements\n") - return -def _write_periodic(fh, periodic, float_fmt, binary): +def _write_periodic(fh, periodic, float_fmt: str, binary: bool) -> None: """write the $Periodic block specified as diff --git a/src/meshio/gmsh/common.py b/src/meshio/gmsh/common.py index 83cbbde3a..106d878f6 100644 --- a/src/meshio/gmsh/common.py +++ b/src/meshio/gmsh/common.py @@ -4,9 +4,9 @@ import shlex import numpy as np +from numpy.typing import ArrayLike from .._exceptions import ReadError, WriteError -from .._mesh import CellBlock c_int = np.dtype("int32") c_double = np.dtype("float64") @@ -161,25 +161,7 @@ def _read_data(f, tag, data_dict, data_size, is_ascii): _meshio_to_gmsh_type = {v: k for k, v in _gmsh_to_meshio_type.items()} -def _reorder_cells( - cells: list[CellBlock | tuple[str, np.ndarray]], ordering -) -> list[tuple[str, np.ndarray]]: - cells = cells[:] - for i, cell_block in enumerate(cells): - if isinstance(cell_block, tuple): - cell_type, cell_data = cell_block - else: - assert isinstance(cell_block, CellBlock) - cell_type = cell_block.type - cell_data = cell_block.data - - permutation = ordering.get(cell_type) - if permutation is not None: - cells[i] = (cell_type, cell_data[:, permutation]) - return cells - - -def _gmsh_to_meshio_order(cells): +def _gmsh_to_meshio_order(cell_type: str, idx: ArrayLike) -> np.ndarray: # Gmsh cells are mostly ordered like VTK, with a few exceptions: meshio_ordering = { # fmt: off @@ -199,10 +181,13 @@ def _gmsh_to_meshio_order(cells): "pyramid13": [0, 1, 2, 3, 4, 5, 8, 10, 6, 7, 9, 11, 12], # fmt: on } - return _reorder_cells(cells, meshio_ordering) + idx = np.asarray(idx) + if cell_type not in meshio_ordering: + return idx + return idx[:, meshio_ordering[cell_type]] -def _meshio_to_gmsh_order(cells: list[CellBlock]): +def _meshio_to_gmsh_order(cell_type: str, idx: ArrayLike) -> np.ndarray: # Gmsh cells are mostly ordered like VTK, with a few exceptions: gmsh_ordering = { # fmt: off @@ -222,7 +207,10 @@ def _meshio_to_gmsh_order(cells: list[CellBlock]): "pyramid13": [0, 1, 2, 3, 4, 5, 8, 9, 6, 10, 7, 11, 12], # fmt: on } - return _reorder_cells(cells, gmsh_ordering) + idx = np.asarray(idx) + if cell_type not in gmsh_ordering: + return idx + return idx[:, gmsh_ordering[cell_type]] def _write_physical_names(fh, field_data): diff --git a/src/meshio/netgen/_netgen.py b/src/meshio/netgen/_netgen.py index b93818c76..304f68af7 100644 --- a/src/meshio/netgen/_netgen.py +++ b/src/meshio/netgen/_netgen.py @@ -7,7 +7,6 @@ import numpy as np from ..__about__ import __version__ -from .._common import topological_dimension from .._files import open_file from .._helpers import register_format from .._mesh import Mesh @@ -165,36 +164,35 @@ def _read_cells(f, netgen_cell_type, cells, cells_index, skip_every_other_line=F line, _ = _fast_forward_over_blank_lines(f) -def _write_cells(f, block, index=None): - if len(block) == 0: +def _write_cells(f, cell_block, index=None): + if len(cell_block) == 0: return - pmap = np.array(meshio_to_netgen_pmap[block.type]) - dim = topological_dimension[block.type] + pmap = np.array(meshio_to_netgen_pmap[cell_block.type]) post_data = [] pre_data = [] i_index = 0 - if dim == 0: + if cell_block.dim == 0: post_data = [1] i_index = 1 - elif dim == 1: + elif cell_block.dim == 1: pre_data = [1, 0] post_data = [-1, -1, 0, 0, 1, 0, 1, 0] - elif dim == 2: + elif cell_block.dim == 2: pre_data = [1, 1, 0, 0, len(pmap)] i_index = 1 - elif dim == 3: + elif cell_block.dim == 3: pre_data = [1, len(pmap)] else: - raise ValueError(f"Invalid cell dimension: {dim}") + raise ValueError(f"Invalid cell dimension: {cell_block.dim}") col1 = len(pre_data) col2 = col1 + len(pmap) col3 = col2 + len(post_data) - pi = np.zeros((len(block), col3), dtype=np.int32) - pi[:, :col1] = np.repeat([pre_data], len(block), axis=0) - pi[:, col1:col2] = block.data[:, pmap] + 1 - pi[:, col2:] = np.repeat([post_data], len(block), axis=0) + pi = np.zeros((len(cell_block), col3), dtype=np.int32) + pi[:, :col1] = np.repeat([pre_data], len(cell_block), axis=0) + pi[:, col1:col2] = cell_block.data[:, pmap] + 1 + pi[:, col2:] = np.repeat([post_data], len(cell_block), axis=0) if index is not None: pi[:, i_index] = index np.savetxt(f, pi, "%i") @@ -216,10 +214,10 @@ def _write_codim_domain_data(f, mesh, cells_index, dim, codim): # set generic default names (is this appropriate/useful?) if len(data) == 0: indices = set() - for block, index in zip(mesh.cells, cells_index): + for cell_block, index in zip(mesh.cells, cells_index): if index is None: continue - if topological_dimension[block.type] == dim - codim: + if cell_block.dim == dim - codim: indices = indices.union(set(index)) for idx in indices: @@ -384,8 +382,8 @@ def write_buffer(f, mesh, float_fmt): if cells_index is None: cells_index = [None] * len(mesh.cells) - for block in mesh.cells: - cells_per_dim[topological_dimension[block.type]] += len(block) + for cell_block in mesh.cells: + cells_per_dim[cell_block.dim] += len(cell_block) f.write(f"# Generated by meshio {__version__}\n") f.write("mesh3d\n\n") @@ -399,25 +397,25 @@ def write_buffer(f, mesh, float_fmt): f.write("\n# surfnr bcnr domin domout np p1 p2 p3\n") f.write("surfaceelements\n") f.write(f"{cells_per_dim[2]}\n") - for block, index in zip(mesh.cells, cells_index): - if topological_dimension[block.type] == 2: - _write_cells(f, block, index) + for cell_block, index in zip(mesh.cells, cells_index): + if cell_block.dim == 2: + _write_cells(f, cell_block, index) f.write("\n# matnr np p1 p2 p3 p4\n") f.write("volumeelements\n") f.write(f"{cells_per_dim[3]}\n") - for block, index in zip(mesh.cells, cells_index): - if topological_dimension[block.type] == 3: - _write_cells(f, block, index) + for cell_block, index in zip(mesh.cells, cells_index): + if cell_block.dim == 3: + _write_cells(f, cell_block, index) f.write( "\n# surfid 0 p1 p2 trignum1 trignum2 domin/surfnr1 domout/surfnr2 ednr1 dist1 ednr2 dist2\n", ) f.write("edgesegmentsgi2\n") f.write(f"{cells_per_dim[1]}\n") - for block, index in zip(mesh.cells, cells_index): - if topological_dimension[block.type] == 1: - _write_cells(f, block, index) + for cell_block, index in zip(mesh.cells, cells_index): + if cell_block.dim == 1: + _write_cells(f, cell_block, index) f.write("\n# X Y Z\n") f.write("points\n") @@ -433,9 +431,9 @@ def write_buffer(f, mesh, float_fmt): f.write("\n# pnum index\n") f.write("pointelements\n") f.write(f"{cells_per_dim[0]}\n") - for block, index in zip(mesh.cells, cells_index): - if topological_dimension[block.type] == 0: - _write_cells(f, block, index) + for cell_block, index in zip(mesh.cells, cells_index): + if cell_block.dim == 0: + _write_cells(f, cell_block, index) # currently, there is no better place for identification data if isinstance(mesh.info, dict): diff --git a/tests/test_public.py b/tests/test_public.py index 12b1b23de..d60fafd8e 100644 --- a/tests/test_public.py +++ b/tests/test_public.py @@ -4,4 +4,3 @@ def test_public_attributes(): # Just make sure this is here meshio.extension_to_filetype - meshio.topological_dimension