Surface / boundary extraction
meshioplusplus.extract_surface(mesh, record_parent_ids=False) derives the boundary of a mesh's highest-dimension cells as a new Mesh. It is the general form of skin extraction: the same face-hashing algorithm (a facet whose sorted corner-node key occurs exactly once is on the boundary), but it picks the dimension automatically and also handles 2D surface meshes.

import meshioplusplus
vol = meshioplusplus.read("part.msh") # tetra/hexa/wedge/pyramid mesh
surf = meshioplusplus.extract_surface(vol) # triangle/quad surface mesh
meshioplusplus.write("surface.stl", surf)
sheet = meshioplusplus.read("sheet.vtu") # triangle/quad surface mesh
edges = meshioplusplus.extract_surface(sheet) # line/line3 boundary loop- If the mesh has any 3D volume cells, the boundary faces are returned (
triangle/quadand the quadratictriangle6/quad8/quad9). - Otherwise, for a 2D surface mesh, the boundary edges are returned (
line/line3).
Supported cell types
Volume → faces — tetra, tetra10, hexahedron, hexahedron20, hexahedron27, wedge, wedge15, pyramid, pyramid13, pyramid14 (same face tables as skin extraction).
Surface → edges
| surface type | boundary edges |
|---|---|
triangle | 3 × line |
triangle6 | 3 × line3 |
quad | 4 × line |
quad8 / quad9 | 4 × line3 |
Facet matching uses the corner nodes only, so mixed linear/quadratic meshes still cancel shared facets correctly.
Semantics
- Output block order is fixed:
triangle,triangle6,quad,quad8,quad9for faces;line,line3for edges (only non-empty blocks appear), in cell-block enumeration order within each block. - Points are compacted to the referenced subset in ascending original-index order;
point_datarows are subset the same way.field_data, sets, andinfoare dropped. record_parent_ids=Trueattaches anint64cell_dataarray"surface:parent_cell"giving, for each output facet, the global index of the unique input cell that owns it (counted block-major over every input cell of every block). Point compaction does not affect it — it is per-output-cell.- Unsupported same-dimension blocks (
polyhedron*, ragged blocks, Lagrange and very-high-order types) are skipped with a warning. If no supported 2D/3D block exists,ValueErroris raised.
Relationship to extract_skin
extract_surface and extract_skin share one implementation (src/cpp/src/operations/surface.cpp). Use extract_skin when you specifically want the volume skin with the optional linearize flag (what the STL/PLY/SVG/TikZ writers use); use extract_surface for the auto-dimension behavior, 2D-edge extraction, and parent-cell provenance.
Cross-language
Available in every binding surface: Python (extract_surface), the C API (mio_extract_surface), Fortran (mesh%extract_surface()), and WASM (extractSurface), plus the CLI verb meshioplusplus extract-surface.
Implementation
The extractor runs over the uniform mesh API (so it works under all three mesh backends) with a parallel facet-key phase and a serial dedup/emit phase that keeps output byte-identical across backends and thread counts. Face topology comes from src/cpp/include/meshioplusplus/detail/cell_faces.hpp and edge topology from detail/cell_edges.hpp; a pure-numpy twin (meshioplusplus._surface._extract_surface_py) produces identical output, asserted by tests/python/test_extract_surface.py.