Netgen (.vol, .vol.gz)
The Netgen neutral mesh format (.vol), plain text and keyword-block based, optionally gzip-compressed (.vol.gz).
| Format name | netgen |
| Extensions | .vol, .vol.gz |
| Read / Write | ✓ / ✓ |
| Extra dependencies | — |
Reading & writing
Native whitespace tokens and normalized data rows view the original or gzip-inflated buffer until parsing completes; field/region names and result arrays remain owned. Numeric-prefix handling and writer bytes are unchanged. The deck-token evidence measures ordinary ASCII meshes; gzip ownership is separately fixture-tested.
The native reader views mapped/buffered source lines or the owned inflated gzip buffer, preserving concatenated gzip members, metadata and count bounds. Names and returned arrays remain owned; writer bytes are unchanged. See the deck-line comparison, which measures uncompressed inputs.
import meshioplusplus
mesh = meshioplusplus.read("mesh.vol")
meshioplusplus.netgen.write("out.vol", mesh, float_fmt=".16e")float_fmt— coordinate format.
File structure
The file starts with a literal mesh3d line, then keyword blocks in arbitrary order, each <keyword>\n<count>\n<count lines of data>; blank lines and #-comments are freely interspersed and skipped anywhere. Relevant blocks:
dimension— a single int (2 or 3).geomtype— a single int; unexpected values just warn.points—<N>then N coordinate rows.pointelements— dim-0 (vertex) cells.edgesegments/edgesegmentsgi/edgesegmentsgi2— dim-1 (line) cells (see the two-line variant below).surfaceelements/surfaceelementsgi/surfaceelementsuv— dim-2 cells; the node count is read from a fixed column position per row (variable per row: triangle=3, quad=4, triangle6=6, quad8=8).volumeelements— dim-3 cells; node count similarly variable (tetra=4, pyramid=5, wedge=6, hexahedron=8, tetra10=10, pyramid13=13, wedge15=15, hexahedron20=20).materials/bcnames/cd2names/cd3names— codimension-indexed name tables (codim 0/1/2/3 relative to mesh dimension) →field_data[name] = [idx, edim].identifications— periodic node-pair table,Nrows of(node1, node2, identification_type_id).identificationtypes— a single row ofNtype ids.face_colours,singular_edge_left/right,singular_face_inside/outside,singular_points— read and discarded.
A single integer cell index (region/material marker) is stored per element across every block, exposed as cell_data["netgen:index"].
Cell types & node ordering
Node-count → type, per topological dimension:
| dim | counts → types |
|---|---|
| 0 | 1 → vertex |
| 1 | 2 → line |
| 2 | 3→triangle, 6→triangle6, 4→quad, 8→quad8 |
| 3 | 4→tetra, 5→pyramid, 6→wedge, 8→hexahedron, 10→tetra10, 13→pyramid13, 15→wedge15, 20→hexahedron20 |
Full Netgen→meshio++ node permutation table (meshio++[i] = netgen[table[i]]); the meshio++→Netgen direction uses the exact per-entry inverse:
| type | permutation |
|---|---|
triangle6 | [0,1,2,5,3,4] |
quad8 | [0,1,2,3,4,7,5,6] |
tetra | [0,2,1,3] |
tetra10 | [0,2,1,3,5,7,4,6,9,8] |
pyramid | [0,3,2,1,4] |
pyramid13 | [0,3,2,1,4,7,6,8,5,9,12,11,10] |
wedge | [0,2,1,3,5,4] |
wedge15 | [0,2,1,3,5,4,7,8,6,13,14,12,9,11,10] |
hexahedron | [0,3,2,1,4,7,6,5] |
hexahedron20 | [0,3,2,1,4,7,6,5,10,9,11,8,16,19,18,17,14,13,15,12] |
(line/triangle/quad/vertex use natural order.)
Data mapping
cell_data["netgen:index"]— the single per-cell region/material marker; Netgen cannot store the field's name, so on write meshio++ prefers anetgen:indexentry if present, else the first integer-dtype cell_data array found.field_data[name] = [idx, edim]— codimension-domain names (materials, boundary-condition names, co-dim-2/3 names).mesh.info["netgen:identifications"](an(N,3)int array: node1/node2/type id) andmesh.info["netgen:identificationtypes"](a(1,N)int array) — periodic identification data in Python. Native meshes and flat bindings store these numeric arrays infield_dataunder the same keys, so registry reads/writes retain them without a format-info handle. Periodic node ids retain the file's 1-based numbering; operations do not remap these arrays, so rebuild them after changing points.
Quirks & limitations
- 1-based in file;
-1/+1applied on read/write. - Row layout is column-position-dependent:
surfaceelementsreads its node count from a fixed column and node ids start at another fixed column;volumeelementsuses different fixed positions — these encode Netgen's distinct per-dimension record schema (surfnr bcnr domin domout np p1 p2 ...for surfaces;matnr np p1 p2 ...for volumes). edgesegmentsgi2has a two-physical-line variant, triggered by a header line exactlysurf1 surf2 p1 p2— connectivity is on the first line and the second carries auxiliary data. Both readers support it, the periodic and codimension-name tables, and discardface_colours/singular_*sections as the Python reference does.- Only one integer cell-data array can be stored per file (a Netgen format limitation, not a meshio++ choice); when reading back, it is always named
"netgen:index"regardless of its original name. - Native
.vol.gzreads/writes requireMESHIOPLUSPLUS_WITH_ZLIB=ON; a build without zlib raises an explicit capability error, and Python can fall back togzip.open..vol.gzis also recognized by the native registry.
Notes
tests/python/meshes/netgen/periodic_1d.vol,periodic_2d.vol,periodic_3d.vol— periodic identification round-trip fixtures. Synthetic strict-core tests cover names, periodic tables, gzip, two-line edges and truncated sections without relying on Python fallback.