Skip to content

Cell conversion (linearize / simplexify / elevate)

meshioplusplus.convert_cells(mesh, mode=...) converts a mesh's element representation — which cell types it is built from — while leaving the object it describes intact. It is a mesh operation (like crop and split), not a file format, and uses only standard C++/numpy, so it runs under every mesh backend.

Each hexahedron split into 6 tetrahedra, coloured by child index

python
import meshioplusplus

mesh = meshioplusplus.read("domain.msh")

# drop the higher-order nodes: tetra10 -> tetra, hexahedron27 -> hexahedron, ...
linear = meshioplusplus.convert_cells(mesh, mode="linearize")

# decompose into simplices: hexahedron -> 6 tetra, quad -> 2 triangles, ...
tets = meshioplusplus.convert_cells(mesh, mode="simplexify")

# promote to serendipity quadratic: triangle -> triangle6, tetra -> tetra10, ...
quadratic = meshioplusplus.convert_cells(mesh, mode="elevate")

meshioplusplus.write("tets.vtu", tets)

Modes

modedoescellspoints
"linearize"every higher-order cell becomes its linear base, keeping the corner connectivity verbatimunchangedorphaned mid nodes pruned
"simplexify"every cell is decomposed into simplices of the same topological dimensiongrowsunchanged (children reuse the parent's corner nodes)
"elevate"every linear cell is promoted to its serendipity (edge-only) quadratic counterpartunchangedgrows (one new node per unique edge)

Type mappings:

linearquadratic (elevatelinearize)simplex decomposition (simplexify)
lineline3— (already a simplex)
triangletriangle6— (already a simplex)
quadquad82 × triangle
tetratetra10— (already a simplex)
pyramidpyramid132 × tetra
wedgewedge153 × tetra
hexahedronhexahedron206 × tetra
polygon(n)(n − 2) × triangle (fan around node 0)

linearize additionally accepts the full-Lagrange types (quad9, hexahedron27, wedge18, pyramid14, and the higher *20/*64 families), mapping each to its linear base.

All three modes are idempotent on cells they do not apply to — a linear block passes through linearize unchanged, an already-simplex block through simplexify, an already-quadratic block through elevate — so they are safe on a mixed-order mesh. Under simplexify, higher-order input is linearized first.

Two constructs raise rather than guess:

  • a polyhedron block under simplexify (a 2-level ragged block has no simplex template);
  • the full-Lagrange targets quad9/hexahedron27 under elevate — they need face- and body-centre nodes, which this version deliberately does not produce.

What changes

  • Block structure is preserved 1:1 in every mode: the output has exactly as many cell blocks as the input, in the same order. That is what keeps the cell_data correspondence (one array per block) correct under every backend.
  • linearize: corner connectivity verbatim; unreferenced points pruned and connectivity/point_data remapped. Cell count unchanged, so cell_data and field_data pass through.
  • simplexify: points untouched; each parent's cell_data row is replicated to its children; point_data/field_data pass through.
  • elevate: new nodes are appended after the originals, with coordinates at the edge midpoint and point_data set to the mean of the edge's endpoints. Cell count unchanged, so cell_data/field_data pass through.
  • point_sets / cell_sets are remapped in the Python layer. Under simplexify a cell-set entry expands to the parent's children.
  • record_parent_ids=True attaches an Int64 convert:parent_cell cell_data array recording, per output cell, the index of the input cell it came from within its own block.

Output is deterministic: the decomposition templates are fixed, and the elevate mid-edge numbering is assigned by a serial pass over a parallel-filled buffer, never a concurrent hash insert. Results are byte-identical across the MESHIO/NATIVE/KRATOS backends and any thread count.

Every emitted simplex is positively oriented for a well-oriented input, and the decomposition conserves volume — the hexahedron template is a canonical Freudenthal fan around the main diagonal 0–6. (It is conforming across a shared face only when neighbouring hexahedra agree on that diagonal, which is why the diagonal is fixed rather than chosen per cell.)

The numpy fallback handles rectangular cell blocks; ragged/polyhedron blocks are handled by the C++ core only.

CLI

bash
meshioplusplus convert-cells in.msh out.vtu --mode linearize
meshioplusplus convert-cells in.msh out.vtu --mode simplexify
meshioplusplus convert-cells in.msh out.vtu --mode elevate
meshioplusplus convert-cells in.msh out.vtu --mode simplexify --record-parent-ids

Note this is distinct from the format-conversion convert verb. See the CLI reference.

Other languages

  • C APImio_convert_cells(mesh, mode, record_parent_ids) returns an opaque mio_convert_cells_result (_mesh borrow, _take_mesh, zero-copy _point_map / _cell_map, _free). See the C API reference.
  • Fortranmesh%convert_cells(mode, record_parent_ids=..., point_map=...). See the Fortran reference.
  • WebAssembly / JavaScriptconvertCells(mesh, mode, recordParentIds). See the WebAssembly reference.

Released under the MIT License.