Skip to content

Exodus II (.e, .exo, .ex2)

The Exodus II format, stored in netCDF using its classic variable/dimension conventions.

Format nameexodus
Extensions.e, .exo, .ex2
Read / Write✓ / ✓
Extra dependenciesnetCDF4 (or a C++ build with netCDF)

Reading & writing

python
import meshioplusplus

mesh = meshioplusplus.read("mesh.exo")
meshioplusplus.exodus.write("out.exo", mesh)

write takes no keyword arguments.

File structure

Global attrs: title, version=5.1f, api_version=5.1f, floating_point_word_size=8. Dimensions: num_nodes, num_dim, num_elem, num_el_blk, num_node_sets, len_string=33, len_line=81, four=4, time_step (unlimited). Key variables:

  • time_whole(time_step) — a dummy single 0.0 timestep is always written.
  • coor_names(num_dim, len_string) — single-character "X", "Y", "Z".
  • coord(num_dim, num_nodes) — transposed relative to meshio++'s (n, dim) layout — or, alternatively, separate coordx/coordy/coordz(num_nodes) variables (both styles are accepted on read).
  • eb_prop1(num_el_blk) — arbitrary distinct per-block ids (their exact values don't matter, only that they differ, per a ParaView requirement noted in the source).
  • connect{k}(num_el_in_blk{k}, num_nod_per_el{k}) for k = 1..num_el_blk, with a text elem_type attribute; 1-based node indices.
  • name_nod_var/vals_nod_var{k} — point-data names and values (only the first timestep is read/written; extra timesteps trigger a warning).
  • name_elem_var/vals_elem_var{idx}[eb{block}] — cell data indexed by (variable index, element block), later concatenated across blocks in block order and re-split by target cell-block size.
  • ns_names/node_ns{k} — node (point) sets, 1-based.
  • info_records/qa_records — free-text info strings.

Cell types

A large type table; representative entries:

Exodusmeshio++Exodusmeshio++
SPHEREvertexTETRA, TET4tetra4 (note: not "tetra")
BEAM, BEAM2, BAR2lineTETRA4tetra4
BEAM3line3TETRA8tetra8
SHELL4, QUAD4quadTETRA10tetra10
SHELL8, QUAD8quad8TETRA14tetra14
SHELL9, QUAD9quad9PYRAMIDpyramid
HEX8, HEXAHEDRONhexahedronWEDGEwedge
HEX20hexahedron20TRI3, TRIANGLEtriangle
HEX27hexahedron27TRI6triangle6

The write-side reverse map picks one canonical Exodus name per meshio++ type (e.g. hexahedron → HEX8, tetra → TETRA, tetra4 → TET4 — a distinct entry from plain tetra).

Data mapping

  • point_data — arbitrary names, with automatic recombination: names ending in X/Y/Z are checked for sibling Y/Z names and, if found, stacked into a 3-component vector; names ending _R/_Z are checked for a sibling and stacked into a 2-component vector.
  • cell_data — arbitrary names, split per cell block by node count.
  • point_sets — node sets (Exodus's own "node set" concept), 1-based in file.
  • mesh.info — free-text strings from info_records/qa_records.

Quirks & limitations

  • The point-data name recombination (categorize()) has a deliberately preserved quirk: the check for a paired variable uses Python truthiness on the found array index, so an index of exactly 0 is treated the same as "not found". This is a latent edge case in the reference implementation that the C++ port reproduces on purpose, rather than silently fixing — changing it would make the two implementations disagree on some inputs.
  • Only the first timestep is ever read (a warning is emitted if more exist) — matches a known ParaView writer limitation referenced in the source.
  • info_records/qa_records/ns_names/node_ns* (info strings and node sets) are explicitly unsupported by the C++ reader, which throws and routes to Python whenever any of them are present in the file.
  • The C++ writer does not support mesh.point_sets at all; the shim only attempts the C++ write path when point_sets is empty.

Notes

  • Read/written through the C++ core when built with MESHIO_WITH_NETCDF, otherwise through netCDF4.
  • No reference fixture exists under tests/meshes/exodus/; tests round-trip synthetic meshes, including one exercising point_sets (which always forces the Python path).

Released under the MIT License.