exodusii
A Python interface for reading, writing, comparing, querying, and spatially reducing Exodus II finite-element databases.
Requires Python ≥ 3.13 and netCDF4.
Installation
python -m pip install -e .
Run tests:
cd /path/to/exodusii
pytest tests/api/ tests/mesh/ tests/core/ tests/cli/ tests/io/ tests/compat/ tests/test_attributes.py
Reading a database
from exodusii import ExodusFile
with ExodusFile.open("results.exo") as exo:
print(exo.title)
print(exo.dimension) # 1, 2, or 3
print(exo.node_count)
print(exo.element_count)
print(exo.times())
coords = exo.coordinates() # (n_nodes, dim)
temp = exo.values("TEMP", on="node", time="last")
energy = exo.values("ENERGY", on="element", block_id=1, time="last")
ke = exo.values("KE", on="global") # full time history
print(exo.element_block_ids())
print(exo.variable_names("node"))
print(exo.variable_names("element"))
Time selectors: "first", "last", an int (0-based index), or a float
(nearest physical time).
Writing a database
import numpy as np
from exodusii import ExodusWriter
coords = np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=float)
with ExodusWriter.create("out.exo") as w:
# Declare all counts upfront — this cannot change after initialize()
w.initialize("my mesh", dimension=2, node_count=4, element_count=1, element_blocks=1)
w.write_coordinates(coords)
w.define_element_block(10, "quad", [[1, 2, 3, 4]])
w.define_global_variables(["TOTAL_ENERGY"])
w.define_node_variables(["TEMP"])
w.define_element_variables(["STRESS"])
w.write_time(0.0)
w.write_global_values([0.0])
w.write_node_values("TEMP", [100.0, 200.0, 300.0, 400.0])
w.write_element_values("STRESS", [42.0], block_id=10)
Parallel / decomposed Exodus files
from exodusii import ParallelExodusFile
with ParallelExodusFile.open("mesh.e.4.0", "mesh.e.4.1", "mesh.e.4.2", "mesh.e.4.3") as exo:
print(exo.node_count) # global node count
coords = exo.coordinates() # assembled from all pieces
temp = exo.values("TEMP", on="node", time="last")
# Join all pieces into a single serial file
with ParallelExodusFile.open(*files) as exo:
exo.write("joined.exo")
Aggregation uses the per-file node_num_map / elem_num_map. If a map is
absent a UserWarning is emitted and a sequential fallback is used (only
correct for non-overlapping partitions).
Fast global reads from decomposed files
Global scalar variables are fully present in every component file. Use the
--piece CLI flag to read one small piece directly and avoid opening the full
joined (potentially multi-GB) database:
python -m exodusii stats run.exo.96.00 --select g/MAT_MASS_1 --piece 0
python -m exodusii query run.exo.96.00 --select g/MASSDELETED --time last --piece 0
Region-masked statistics and mass
Compute statistics of a field variable restricted to a geometric region, with an optional field-threshold predicate:
from exodusii import ExodusFile
from exodusii.mesh import Cylinder
with ExodusFile.open("run.exo") as exo:
cyl = Cylinder([0, 0, 0], [0.05, 0, 0], radius=0.013)
# Field statistics inside a cylinder, filtered by a field predicate
r = exo.region_stats(
"YIELD_STRESS_2",
block_id=3,
region=cyl,
where="EQPS_2 > 1.0", # VARNAME OP VALUE only
reduce=["mean", "max", "count"],
time="last",
symmetry_factor=4.0, # applied to sum/count; NOT mean/max
)
print(r.stats["mean"], r.count_selected)
# Material mass inside a region
result = exo.region_mass(
block_id=3,
region=cyl,
density_name="DENSITY",
volfrac_name="VOLFRC_2", # optional volume fraction
time="last",
symmetry_factor=4.0,
)
print(result.mass) # = 4 * sum(|vol| * density * volfrac)
Available reducers: mean, max, min, sum, count, std.
Symmetry factor scales only the extensive reducers (sum, count,
mass).
CLI:
python -m exodusii region-stats run.exo \
--select e/YIELD_STRESS_2 \
--cylinder 0 0 0 0.05 0 0 0.013 \
--where "EQPS_2 > 1.0" \
--reduce mean,max,count \
--time last --block 3 --symmetry 4
Region flags: --cylinder, --sphere, --circle, --rectangle.
Exodiff-style comparison
from exodusii import diff, DiffOptions, Tolerance, ToleranceMode
result = diff("gold.exo", "test.exo")
if not result:
for vd in result.variable_diffs:
print(vd.entity, vd.name, vd.max_delta)
# Custom tolerances
opts = DiffOptions(
default_tolerance=Tolerance(ToleranceMode.ABSOLUTE, 1e-8),
variable_tolerances={"PRESSURE": Tolerance(ToleranceMode.COMBINED, 1e-5)},
exclude=frozenset({"TIME"}),
)
result = diff("gold.exo", "test.exo", opts)
# Coordinate-based mesh matching (different node/element ordering)
opts = DiffOptions(coordinate_matching=True, matching_tolerance=1e-8)
result = diff("gold.exo", "reordered.exo", opts)
Tolerance modes mirror SEACAS exodiff: relative, absolute, combined,
ignore, eigenrel, eigenabs, eigencom, ulps_float, ulps_double.
TimeSelection controls which steps are compared and whether file-2 values
are interpolated to file-1 time points.
python -m exodusii diff gold.exo test.exo
python -m exodusii diff --absolute -t 1e-8 gold.exo test.exo
python -m exodusii diff --start LAST gold.exo test.exo
python -m exodusii diff --match-coordinates gold.exo reordered.exo
python -m exodusii diff --format json --terse gold.exo test.exo
Exit codes: 0 same, 1 error, 2 different.
Command (control) files
A diff can be driven by a command file on disk. Two formats are supported;
exodusii.read_command_file() (and the CLI -f/--command-file) auto-detect
which one a file uses. Explicit CLI flags override values from the file.
YAML (preferred, modern)
A structured, self-documenting format with full feature parity with the SEACAS exodiff command file:
# opts.yaml
tolerances:
default: {mode: relative, value: 1.0e-6, floor: 1.0e-12}
coordinate: {mode: absolute, value: 1.0e-8}
time: {mode: relative, value: 1.0e-6, floor: 1.0e-15}
categories: # per-category default tolerances
nodal: {mode: absolute, value: 1.0e-7}
variables: # per-variable overrides (highest priority)
DISPLX: {mode: relative, value: 1.0e-9}
variables:
ignore_case: true
exclude: [VELZ]
include: # per-category include lists
nodal: [DISPLX, DISPLY]
global: all # 'all' => compare all variables
coordinates: {compare: true}
attributes: {compare: true}
mesh_matching: {enabled: false, tolerance: 1.0e-6, require_unique: true}
time:
start: 1
stop: -1
increment: 1
step_offset: 0
exclude_steps: []
interpolate: false
report: {show_all: false}
Any tolerance may be an explicit mapping, a shorthand string
("absolute 1e-8" / "1e-6"), or a bare number.
python -m exodusii diff -f opts.yaml gold.exo test.exo
import exodusii
res = exodusii.read_command_file("opts.yaml") # -> CommandFileResult
result = exodusii.diff("gold.exo", "test.exo", res.options)
Emit a YAML command file from a DiffOptions (full/explicit; round-trips):
opts = exodusii.DiffOptions(...)
opts.to_yaml("opts.yaml") # write to a file
print(opts.to_yaml()) # or get the string
# Emit the effective options (command file + CLI flags) and exit, no compare
python -m exodusii diff --absolute -t 1e-8 --emit-options opts.yaml gold.exo test.exo
python -m exodusii diff --emit-options - gold.exo test.exo # to stdout
SEACAS exodiff text (legacy, backward compatible)
The original whitespace-delimited exodiff command-file grammar is also
accepted (auto-detected):
# cmds.txt (SEACAS exodiff command-file grammar)
DEFAULT TOLERANCE relative 1e-6 floor 1e-12
COORDINATES absolute 1e-8
NODAL VARIABLES absolute 1e-7
DISPLX relative 1e-9 # per-variable override
!VELZ # exclude
GLOBAL VARIABLES (all) relative 1e-4
python -m exodusii diff -f cmds.txt gold.exo test.exo
Directives: DEFAULT TOLERANCE, COORDINATES, TIME STEPS, FINAL TIME TOLERANCE, per-category <GLOBAL|NODAL|ELEMENT|NODESET|SIDESET|EDGEBLOCK| FACEBLOCK> VARIABLES blocks (with (all), per-variable tolerances, and
!name exclusions), ELEMENT ATTRIBUTES, STEP OFFSET, EXCLUDE TIMES,
INTERPOLATE, APPLY MATCHING/NODESET MATCH/SIDESET MATCH, and IGNORE CASE/CASE SENSITIVE. Keywords are case-insensitive and abbreviable. A
category block that lists specific names (without (all)) restricts the
comparison to those names for that category. Accepted-but-inert directives
(e.g. PEDANTIC, CALCULATE NORMS) are reported via CommandFileResult.warnings.
The reader classes (YamlCommandFileReader, ExodiffCommandFileReader) share
the abstract CommandFileReader base; command_file_reader(path) is the
format-detecting factory (YAML preferred).
Query and stats CLI
# Inspect a database
python -m exodusii inspect mesh.exo
python -m exodusii variables mesh.exo
python -m exodusii blocks mesh.exo
python -m exodusii times mesh.exo
# Query result variables (JSON output)
python -m exodusii query mesh.exo --select g/TOTAL_ENERGY
python -m exodusii query mesh.exo --select n/TEMP --time last --limit 10
python -m exodusii query mesh.exo --select e/ENERGY --time step:5
# Compact numeric summaries
python -m exodusii stats mesh.exo --select n/TEMP --time last
python -m exodusii stats mesh.exo --select e/ENERGY --time last --by-block
Variable selector format: ENTITY/NAME — e.g. g/TOTAL_ENERGY, n/TEMP,
e/ENERGY, ns/FLUX, ss/PRESSURE.
Python query API
from exodusii import ExodusFile, query, print_query
with ExodusFile.open("mesh.exo") as exo:
result = query(exo, "n/TEMP", "e/ENERGY", time="last")
print(result.names)
print(result.data[:5])
print_query(exo, "g/TM_STEP")
Mesh geometry helpers
from exodusii.mesh import entity_centers, element_volumes, bounding_box
from exodusii.mesh import Cylinder, Sphere, Circle, Rectangle
with ExodusFile.open("run.exo") as exo:
coords = exo.coordinates()
conn = exo.element_connectivity(block_id=1, zero_based=True)
# Element centroids — feed to region.contains() to select elements
centers = entity_centers(conn, coords)
# Per-element volumes/areas
vols = element_volumes("hex8", conn, coords)
# Bounding box
lo, hi = bounding_box(coords)
# Region predicates
cyl = Cylinder([0, 0, 0], [0.05, 0, 0], radius=0.013)
mask = cyl.contains(centers) # boolean ndarray
Supported element types: quad4, hex8, tri3, tet4, wedge6.
Agent CLI (self-learning)
# Static capability reference (no file required)
python -m exodusii learn capabilities overview
python -m exodusii learn capabilities region_reduce
python -m exodusii learn capabilities python_api.writer
# Skills (self-contained workflow guides)
python -m exodusii learn skills list
python -m exodusii learn skills exodusii-region-reduce
python -m exodusii learn skills exodusii-geometry
Package layout
exodusii/
├── api/ modern API (ExodusFile, ExodusWriter, ParallelExodusFile,
│ diff, query, region_reduce, lineout, copy)
├── core/ domain model (Entity, Block, Tolerance, errors, schema)
├── io/ NetCDF backend (Protocol + netCDF4 implementation)
├── mesh/ geometry (elements, regions, entity_centers, element_volumes)
├── compat/ legacy ExodusIIFile / put_*/get_* API
├── cli/ JSON-oriented CLI (main.py dispatch + one file per subcommand, exodiff.py, exoread, learn)
└── data/ capabilities.json, skills.json
Legacy API (compatibility)
The historical put_* / get_* API from the original exodusii library is
preserved for downstream code that has not yet migrated:
import exodusii
# Read
with exodusii.File("mesh.exo") as exo:
times = exo.get_times()
temp = exo.get_node_variable_values("TEMP", time_step=-1)
# Write
with exodusii.File("out.exo", mode="w") as exo:
exo.put_init("title", 2, 4, 1, 1, 0, 0)
exo.put_coords(coords)
exo.put_element_block(10, "quad", 1, 4)
exo.put_element_conn(10, [[1, 2, 3, 4]])
# Globals only
exodusii.write_globals({"energy": values}, times, filename="globals.exo")
New code should use the modern API (ExodusFile, ExodusWriter,
ParallelExodusFile). The compatibility layer is fully tested and maintained
but will not receive new features.
Metadata
Release files for exodusii 26.11.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| exodusii-26.11.7.tar.gz | 744.0 kB | With provenance | Details for exodusii-26.11.7.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| exodusii-26.11.7-py3-none-any.whl | Python 3 | none | any | Details for exodusii-26.11.7-py3-none-any.whl |
Total release size: 1.0 MB
Release files / exodusii-26.11.7.tar.gz
| Download URL | exodusii-26.11.7.tar.gz |
|---|---|
| Size | 744.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8b409261d8d004fc660e87b262a1b8c4d75f2c8e94bfdee9ca6b97208152a916
|
|
BLAKE2b-256 checksum How to use checksums |
3e3380ee9676e43fe9988f5c2b40d372e615a9c7ebedf20d173eea9a20c9f2c5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logRelease files / exodusii-26.11.7-py3-none-any.whl
| Download URL | exodusii-26.11.7-py3-none-any.whl |
|---|---|
| Size | 291.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6a79ab5dc8738952e886e791cc0e98ce08297cc484b031f8d58dff21d0025ea1
|
|
BLAKE2b-256 checksum How to use checksums |
a08a1891a4e9b131ec419347207975c0db2aa508fbcb7bf4a45792a42aaeae4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logSecurity for exodusii 26.11.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for exodusii-26.11.7.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.