Exodus II (.e, .exo, .ex2)
The Exodus II format, stored in netCDF using its classic variable/dimension conventions.
| Format name | exodus |
| Extensions | .e, .exo, .ex2 |
| Read / Write | ✓ / ✓ |
| Extra dependencies | netCDF4 (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 single0.0timestep 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, separatecoordx/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})fork = 1..num_el_blk, with a textelem_typeattribute; 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:
| Exodus | meshio++ | Exodus | meshio++ |
|---|---|---|---|
SPHERE | vertex | TETRA, TET4 | tetra4 (note: not "tetra") |
BEAM, BEAM2, BAR2 | line | TETRA4 | tetra4 |
BEAM3 | line3 | TETRA8 | tetra8 |
SHELL4, QUAD4 | quad | TETRA10 | tetra10 |
SHELL8, QUAD8 | quad8 | TETRA14 | tetra14 |
SHELL9, QUAD9 | quad9 | PYRAMID | pyramid |
HEX8, HEXAHEDRON | hexahedron | WEDGE | wedge |
HEX20 | hexahedron20 | TRI3, TRIANGLE | triangle |
HEX27 | hexahedron27 | TRI6 | triangle6 |
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 inX/Y/Zare checked for siblingY/Znames and, if found, stacked into a 3-component vector; names ending_R/_Zare 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 frominfo_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 exactly0is 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_setsat all; the shim only attempts the C++ write path whenpoint_setsis empty.
Notes
- Read/written through the C++ core when built with
MESHIO_WITH_NETCDF, otherwise throughnetCDF4. - No reference fixture exists under
tests/meshes/exodus/; tests round-trip synthetic meshes, including one exercisingpoint_sets(which always forces the Python path).