4. Mesh file formats and interoperability
Mesh quality inspection rarely starts from a mesh you generated yourself. In practice, the mesh arrives from a preprocessing group, a legacy solver deck, or an open-source mesher, and your inspection script must first decode it. The five formats covered here — Abaqus .inp, Nastran .bdf/.nas, Gmsh .msh, VTK .vtk/.vtu, and Exodus .exo/.e — dominate finite element practice, and each encodes topology, coordinates, and element type information differently. Understanding those differences is what makes an inspection pipeline portable rather than tied to one toolchain.
What Each Format Actually Stores
All five formats carry the same three essentials — node coordinates, element connectivity, and element type — but they disagree on almost everything else: indexing base, section delimiters, whether element type is implicit in the section keyword or explicit per element, and whether groups/sets are first-class objects.
| Format | Typical extension | Node indexing | Element type encoding | Primary Python reader |
|---|---|---|---|---|
| Abaqus | .inp |
1-based | Keyword line (*ELEMENT, TYPE=C3D8) |
meshio, abaqusParser |
| Nastran | .bdf, .nas |
1-based | Card name (CTETRA, CQUAD4) |
meshio, pyNastran |
| Gmsh | .msh |
1-based (v2), 1-based (v4) | Numeric type ID (e.g. 4 = tet4, 5 = hex8) | meshio, gmsh API |
| VTK | .vtk, .vtu |
0-based | Cell type enum (e.g. 10 = tetra, 12 = hexahedron) | meshio, pyvista, vtk |
| Exodus | .exo, .e |
1-based | Element block with type string | netCDF4, meshio, exodus |
The indexing base is the single most common source of silent corruption. VTK is 0-based; the other four are 1-based. If you read a VTK file and write it back as Abaqus without shifting connectivity by +1, every element will reference the wrong nodes, and quality metrics will be computed on a mesh that does not exist. meshio normalizes everything to 0-based internally, which removes the hazard — but only if you stay inside meshio and do not hand-roll a parser.
A Portable Read–Inspect–Write Pipeline
The following example reads a mesh in any of the five formats, computes a simple quality metric (minimum scaled Jacobian proxy via edge-length ratio), and writes the result back out. It uses meshio as the interoperability layer and numpy for the metric.
import meshio
import numpy as np
def load_mesh(path):
"""Read any supported format; return points (N,3) and cells list."""
m = meshio.read(path)
pts = np.asarray(m.points, dtype=float)
return m, pts
def edge_ratio_quality(m, pts):
"""Per-element max/min edge length ratio. 1.0 = perfect, >>1 = poor."""
results = {}
for block in m.cells:
conn = np.asarray(block.data)
if conn.ndim != 2 or conn.shape[1] < 3:
continue
# edges of the element: consecutive node pairs (closed loop)
n = conn.shape[1]
idx = np.arange(n)
a = conn[:, idx]
b = conn[:, (idx + 1) % n]
d = np.linalg.norm(pts[a] - pts[b], axis=2) # (E, n)
d = np.where(d == 0.0, np.nan, d)
ratio = np.nanmax(d, axis=1) / np.nanmin(d, axis=1)
results[block.type] = ratio
return results
m, pts = load_mesh("bracket.inp")
q = edge_ratio_quality(m, pts)
for etype, ratio in q.items():
print(f"{etype:12s} n={len(ratio):6d} "
f"max_ratio={ratio.max():8.3f} "
f"p99={np.percentile(ratio, 99):8.3f}")
# Write back as VTK for ParaView inspection
meshio.write("bracket_checked.vtu", m)
Run against a typical hex-dominant bracket mesh, the output might look like this (values are illustrative, not measured):
hexahedron n= 18432 max_ratio= 14.207 p99= 3.881
wedge n= 1024 max_ratio= 6.540 p99= 2.115
The max_ratio of 14.2 flags a small number of badly distorted hexes; the p99 of 3.88 tells you the bulk of the mesh is acceptable. That gap between max and p99 is the signal you act on — a single outlier usually means a local geometry feature (a fillet, a sliver, a collapsed corner) rather than a global meshing failure.
Format-Specific Pitfalls
- Abaqus
.inp: Element type is declared once per*ELEMENTblock, so a mesh with mixed element types has multiple blocks. Node sets and element sets use*NSET/*ELSETand are frequently referenced by boundary conditions — preserve them when round-tripping, or downstream scripts will fail to find named regions. - Nastran
.bdf: Fixed-field format is column-sensitive; a single misplaced character shifts every field. Free-field (comma-separated) is safer for programmatic writing.pyNastranhandles both and exposesmodel.nodesandmodel.elementsas dictionaries keyed by ID. - Gmsh
.msh: Version 2 and version 4 have different section layouts. Version 4 stores entities in blocks with explicit tags, which is more robust but breaks naive line-based parsers. Always check the$MeshFormatversion line before parsing. - VTK: Legacy
.vtkis ASCII or binary;.vtuis XML-based and compresses well. Cell type enums are not the same as Gmsh type IDs — a Gmsh tet4 (type 4) is VTK cell type 10. Do not copy numeric IDs across formats. - Exodus: Built on NetCDF, so it is self-describing and handles large meshes efficiently. Element blocks group elements by type; a mesh with hexes and tets has two blocks. Reading requires
netCDF4ormeshio's Exodus backend.
meshio does this automatically; manual parsers almost always get it wrong at least once. Verify by checking that conn.min() == 0 after a meshio read and conn.min() == 1 in the raw file.
Choosing a Reader for Your Pipeline
For a quality-inspection tool that must accept whatever the analyst provides, meshio is the pragmatic default: one API, five formats, consistent 0-based indexing, and cell-type strings that map cleanly to quality-metric dispatch tables. Reach for a format-specific library when you need features meshio does not expose — pyNastran for full card-level access, the gmsh Python API for parametric remeshing, pyvista for interactive visualization, and netCDF4 for direct Exodus variable access.
The diagram above summarizes the flow: five source formats feed a single normalization layer, quality metrics are computed on the normalized representation, and results are written back to any target format. The indexing rule and round-trip test are the two safeguards that keep the pipeline honest across all five formats.