Exodus II#
.e .exo .ex2 read + write eager needs netCDF4
Summary of the specification#
Exodus II is Sandia’s finite element database: a netCDF file whose dimensions, variables and attributes carry the names the Exodus library fixes. num_nodes and coord (num_dim rows of coordinates, or coordx / coordy / coordz) hold the geometry; the elements come in blocks, one connect<i> table per block with an elem_type attribute naming the shape - HEX8, TETRA10, SHELL4, BAR2 - and a 1-based node list per element; node_ns<i> lists the node sets, elem_ss<i> with side_ss<i> the side sets - an element and which of its sides - and elem_els<i> the element sets, each with an id in *_prop1 and a name in *_names. Results live along a time_step record dimension: vals_nod_var<i> per nodal variable, vals_elem_var<i>eb<j> per element variable and block, vals_glo_var for the whole-mesh values, named in name_nod_var, name_elem_var and name_glo_var, with the times in time_whole. Cubit writes it, Sierra and MOOSE run on it, ParaView reads it natively.
Specification at a glance#
container |
netCDF: classic ( |
geometry |
|
elements |
|
sets |
|
results |
|
numbering |
|
Reading#
import polyxios as px
mesh = px.read("out.e")
mesh.element_tags["steel"] # a block, by its name
mesh.vertex_tags["clamped"] # a node set
mesh.element_tags["load_face"] # a side set, read as faces
mesh.vertex_attrs["disp"] # disp_x, disp_y, disp_z folded
mesh = px.read("out.e", step=4) # the fifth time step
Every element block is read in file order and becomes an element tag group named by its name, or block_<id> when the file names it nothing; a block’s attributes - attrib<i> - are element attributes, NaN on the other blocks. A node set is a vertex tag group and an element set an element tag group, named the same way (nodeset_<id>, elemset_<id>). A side set names a side of a solid rather than an element, so it is read the way the Abaqus reader reads a *Surface: as the triangle or quadrilateral that side describes, appended after the blocks, tagged with the set’s name and linked to its parent by the face_parent and face_index element attributes. A side named twice is one face in both groups; a side of a shell or a bar, or a side number the element has no side of, has no face to read and is skipped with a warning naming the set. An empty block - the NULL block Cubit leaves for a set with nothing in it - reads as nothing, its name with it.
The variables at one time step - the first, or step= - are the attributes: nodal variables vertex_attrs, element variables element_attrs (NaN on a block the elem_var_tab leaves out), global variables global_attrs. Exodus holds scalars only, so a <name>_x / <name>_y / <name>_z triple folds back into one (n, 3) array and a <name>_0 / <name>_1 / … run into an (n, k) one. The step’s time is global_attrs["time"] and the file’s title global_attrs["title"]; node_num_map and elem_num_map land in original_ids when they say something the index does not - the faces a side set adds carry no id in the file and are numbered on past the largest, so the ids the file holds survive a round trip. A num_dim of 2 pads the coordinates and flags the mesh two-dimensional.
The shape is the block’s family and its node count together - HEX with twenty nodes per element reads as HEX20 does - so a file that spells TETRA for a ten-node block reads. A family Exodus does not define raises UnknownElementTypeError; a count the family has no VTK cell for - HEX9, WEDGE16, PYRAMID14, TETRA14 - is skipped with a warning, its values and set members with it. lazy=True raises LazyReadError.
The codec needs netCDF4, which is optional - pip install "polyxios[netcdf]". Without it UnsupportedFormatError spells that command.
Writing#
px.write(mesh, "out.e")
Exodus partitions the elements into blocks of one shape each, so the writer has to choose them: a tag group whose members are all of one type and overlap no group already taken goes out as a block of its name, and every element left over goes into a block per type, named triangle, tetra and so on - tetra_2 when a group already took the plain name, so the two never read back as one tag. A group whose members are all faces of solids - what a side set reads back as - goes out as a side set, and its faces are not written as elements unless another group names them; every other element group is an element set and every vertex group a node set. The variables are written at one time step: an (n, 3) attribute as <name>_x / <name>_y / <name>_z, any other (n, k) as <name>_0 to <name>_{k-1} - an array of more axes flattened to that first, an (n, 1) one as <name>_0 alone, so neither reads back in its shape - integers and booleans as doubles. A name two attributes would land on (v split beside a plain v_x) is written once, from the first, with a warning; a value on a face that goes out as a side has no place in the file and is dropped with a warning too. Numeric global_attrs are global variables, title the file’s title, time the step’s time; any other text is dropped with a warning. The file is classic netCDF with 64-bit offsets, CDF5 when an index needs 64 bits, netCDF-4 for an empty mesh.
Quirks worth knowing#
Node order. VTK’s for every shape but the quadratic hexahedra and wedges: Exodus lists a
HEX20’s mid-edge nodes bottom ring, vertical edges, top ring where VTK lists bottom, top, vertical, and aWEDGE15the same way; aHEX27puts the centroid first and the face centres bottom, top, left, right, front, back - nodes 22 to 27 of the manual’s side-set table - where VTK orders them by axis with the centroid last. Both are permuted on the way in and back on the way out, so a reader that trusted the file’s order would hand back a hexahedron whose top edges sit on its vertical ones.Side numbering. Exodus counts a solid’s lateral faces first and the ends last; polyxios’s
ELEMENT_FACESputs the ends first. The table between them is checked in the tests against the manual’s side-set node lists, so a side set read here names the face the manual names.Blocks come back as tags. A mesh written with no tags reads back with one tag per block -
triangle,tetra- because the file holds the partition and the reader reports what the file holds. A mesh whose tags partition it by type goes out under those names and comes back under them.A pixel and a voxel go out as the quad and hexahedron they are, corners reordered.
Buffers. A file object reads through the netCDF library’s in-memory open and writes through a temporary file, so a buffer receives the same bytes a path would. A
.gzname is compressed on the way out and read on the way in like any other format.Every element type the file holds a block for is read; a
NSIDEDorNFACEDblock - polygons and polyhedra - is not, and is skipped with a warning.
See also
Supported formats - the full format table.