CLI reference¶
Two console scripts ship with the wheel:
aeolus1d— solver CLI (run cases, optimize, modes, interop, …).aeolus1d-gui— entry point for the visual editor (alias forpython -m aeolus1d_gui).
All 25 registered subcommands, grouped; each is described below.
- Run / diagnose:
run,warmup,analyze-cfl,doctor,bench,examples,parallel - Engine / vehicle:
cycle-settle,drive-cycle - Calibration:
optimize,sensitivity - Acoustics / post:
modes,acoustics,audio,turbo-map - Case files:
expand,validate - Chemistry data:
thermo-db - Interop:
import,export,export-fmu,extract,inspect-gtm,fields,auci-compare - GUI:
gui
aeolus1d run¶
aeolus1d run <case.toml> [--out RUN.H5] [--trace-dt SECONDS] [...]
| Flag | Purpose |
|---|---|
<case.toml> |
Path to a TOML case file. |
--out |
Optional HDF5 file to write the final state (and, with --trace-dt, the time series) to. |
--cgns |
Optional CGNS file to write (ParaView / Tecplot / EnSight; needs the [interop] extra). |
--trace-dt |
Sample every pipe / volume's primitives at this Δt (simulated seconds) into the time_series HDF5 group. |
--trace-max-snapshots |
Soft cap on in-memory trace snapshots (default 100,000); on hit, the tracer decimates 2× and doubles the effective --trace-dt. 0 disables. |
--progress / --no-progress |
t = <now> / <t_end> heartbeat lines. Default: on when stderr is an interactive terminal, off otherwise (pipes / CI logs stay silent). --progress forces on, --no-progress forces off. |
--progress-dt |
Cadence (simulated seconds) for the progress lines. Default t_end / 100. |
--info-every-n |
One step <i> t dt line per N solver iterations (plus picard = N/M under the implicit-network driver). Off by default. |
--live-flush-interval |
With --out + --trace-dt: append the partial trace to the HDF5 every N wall-clock seconds so a GUI can watch the run live. |
--live-control |
Path to a JSON file polled mid-run for live tuning ({"cfl": 0.4, "paused": false, "_revision": N}). |
--resume |
Resume from a prior run's HDF5 checkpoint; continues t_final → t_end and merges the prior time series. Since 2026-09-29 each [[controller]] resumes where it stopped (previous error and time, and its actuator: EGR valve opening, DPF heater power, injected fuel, spark timing, DOC sulfur state); a file written before that resumes with the loop re-primed and the actuator back at the case's initial value, and says so. |
--max-steps |
Watchdog cap on solver iterations (default 10,000,000). |
--chem-every-n |
Override [case] chem_every_n (chemistry super-cycle) from the shell. |
--chem-rtol / --chem-atol |
Override the chemistry ODE integrator tolerances. |
--log-json / --log-level |
Structured JSON log lines / log-level filter. |
There is no --cfl run flag — the CFL number is the [case] cfl
field in the TOML (default 0.5); --live-control can adjust it
mid-run.
At the start of every run aeolus1d prints a one-shot solver-settings
banner summarising the active driver and every solver knob it actually
reads from the case header — values that differ from the schema default
are flagged with *. See run-settings.md for the field-by-field
reference of every CaseHeader key the banner can surface (66 total;
10 are in CaseHeader.BASIC_FIELDS and appear up-front in the GUI
inspector, the rest live under a collapsed "Advanced settings…"
expander shipped in MR !132). The banner is suppressed by setting
AEOLUS_QUIET_SETTINGS=1 (useful for stdout-strict scripts and test
harnesses); leaving it unset is the recommended default.
The TOML schema is documented in aeolus1d.io.schema. A minimal case:
[case]
name = "two-pipes"
t_end = 0.005
cfl = 0.4
scheme = "muscl"
[gas]
gamma = 1.4
R_gas = 287.05
[[pipe]]
id = "up"
length = 0.5
n_cells = 200
area = 1.0e-3
[pipe.init]
p = 1.0e5
T = 300.0
[[pipe]]
id = "down"
length = 0.5
n_cells = 200
area = 1.0e-3
[pipe.init]
p = 1.0e5
T = 300.0
[[junction]]
id = "j1"
kind = "plenum"
volume = 1.0e-3
[junction.init]
p = 1.0e5
T = 300.0
[[junction.ports]]
pipe = "up"
end = "right"
area = 1.0e-3
[[junction.ports]]
pipe = "down"
end = "left"
area = 1.0e-3
[[bc]]
pipe = "up"
end = "left"
kind = "stagnation_inlet"
p0 = 1.1e5
T0 = 300.0
p_back = 1.0e5
[[bc]]
pipe = "down"
end = "right"
kind = "pressure_outlet"
p_back = 1.0e5
aeolus1d validate¶
aeolus1d validate case.toml
Load and validate a case TOML without running it — the same parse +
validate_case pass aeolus1d run performs before building the solver,
without spending solver time or, on a cluster, a compute allocation to
find out a case is broken. Exits 0 and prints a one-line summary
(pipe / junction / BC counts) when the case is sound; a fatal issue
exits 2 with the same curated message run would print (path, source
file:line when available, a fix). Warning-only issues (a plausibility
check, an unknown key with a "did you mean?" hint) print to stderr
either way and don't affect the exit code — they wouldn't stop a real
run either.
Takes one path (use shell globbing + xargs — or a job-array
pre-flight step — to batch a directory of submissions):
for f in cases/*.toml; do aeolus1d validate "$f" || exit 1; done
aeolus1d warmup¶
aeolus1d warmup [case.toml ...] [--steps N]
Pre-compile the Numba solver kernels so the first real run on a fresh
environment skips the one-time cold-cache compile (~19 s measured
2026-08-08 on x86-64 / numba 0.65.1; older or slower machines differ). Runs a
few solver steps (--steps, default 3) per case — just enough to
trigger compilation — over a small bundled spread covering the
explicit, catalyst, and quasi-steady-chemistry kernel families, or
over the case TOMLs you pass (compiles exactly the kernels your case
uses). The compiled kernels are cached on disk and reused by every
subsequent run; aeolus1d doctor reports whether the cache is warm.
aeolus1d analyze-cfl¶
aeolus1d analyze-cfl <case.toml> [--cfl CFL] [--cells]
Pre-run CFL analysis: identifies the pipe + cell that will pin the
timeloop's Δt and estimates the resulting step count — answering
"why is my run slow / why is dt tiny" before a solver run.
--cells prints the constraining cells per pipe; --cfl evaluates
at a CFL other than the case's.
aeolus1d bench¶
Convenience runner for the bundled benchmark cases. The single positional argument is one of:
aeolus1d bench sod # Sod shock-tube
aeolus1d bench lax # Lax shock-tube (stronger than Sod)
aeolus1d bench nozzle # Quasi-1D Laval nozzle
aeolus1d bench shock_reflection # Normal-shock reflection off a closed wall
aeolus1d bench smooth # Smooth density-wave advection (grid-refinement)
aeolus1d bench moody # Darcy-Weisbach friction pressure-drop sanity
aeolus1d bench tjunction # 2-in / 1-out plenum T-junction steady state
aeolus1d bench tjunction_cp # Same geometry, Corberán constant-pressure junction
aeolus1d bench helmholtz # Helmholtz resonator (1-D neck + 0-D plenum)
aeolus1d bench helmholtz_quasi2d # Resolved-chamber Helmholtz (no 0-D plenum)
Each prints summary metrics (resolved features, error vs analytical where applicable, walltime). Passing an unknown name prints the full list and exits non-zero.
aeolus1d doctor¶
aeolus1d doctor [--json]
Print environment diagnostics for bug-report paste. Probes the
installed aeolus1d version, the Python interpreter + platform,
the three required dependencies (numpy / scipy / numba),
the optional extras (cantera, CoolProp, h5py, the [gui]
stack, pycgns, …), the env vars aeolus1d and its hot deps read
at import time (AEOLUS_*, NUMBA_*, OMP_NUM_THREADS), and
the warm/cold state of the Numba JIT kernel cache — probed where
numba actually writes it: NUMBA_CACHE_DIR when set, otherwise
the __pycache__/ directories inside the installed aeolus1d
package tree (aeolus1d warmup populates them).
Default output is a fixed-width plain-text block suitable for pasting straight into a GitLab / GitHub issue:
aeolus1d 0.2.0
python 3.10.12 (CPython) — /…/.venv/bin/python
platform Linux 6.6.x (x86_64)
core dependencies
numpy 2.2.6
scipy 1.15.3
numba 0.64.0
optional dependencies
cantera not installed
CoolProp 7.2.0
…
environment
AEOLUS_CONFIG_DIR <unset>
NUMBA_CACHE_DIR <unset>
…
numba cache warm — 194 cached kernels under /…/aeolus1d/**/__pycache__
--json emits the same data as a JSON object, for issue-tracker
bots, CI diagnostic capture, and pipe-into-jq style flows.
aeolus1d examples¶
aeolus1d examples
Locate and list all 212 bundled example TOMLs recursively — the
root-level cases first, then the subdirectory trees
(avl_cruise_m/, validation/, benchmarks/, optimize/, …)
grouped by directory, with a total count line (the count printed is
computed live, so it tracks the bundled set exactly). Files under
plots/ are post-processing scripts, not cases, and are skipped.
The GUI's quick-load dropdown reads from the same source, so this is
the CLI counterpart for shell users.
aeolus1d modes¶
aeolus1d modes <case.toml> [--pipe ID] [--n-modes 10] [--n-elements 8] \
[--p 4] [--bc-left {wall,open}] [--bc-right {wall,open}] [--out OUT]
Run the FEM eigensolver on a single pipe from a TOML case (--pipe
selects which; default the first pipe). Prints the lowest
--n-modes eigenfrequencies (Hz, default 10); --out writes
the mode shapes as CSV (one row per FEM DOF). --p sets the
element polynomial order (default 4), --n-elements the mesh
(default 8); --bc-left / --bc-right override the eigensolve
BC (wall = ∂φ/∂x = 0, open = φ = 0; defaults wall / open)
without rewriting the case file. Network modes (multi-pipe +
junction DOFs, Phase-42) are surfaced through the GUI's Modes dock;
CLI is single-pipe today.
aeolus1d gui¶
aeolus1d gui [case.toml]
Launch the GUI (same entry point as the aeolus1d-gui console
script and python -m aeolus1d_gui). With an optional case.toml
positional the file is pre-loaded once the main window paints — the
same code path as File → Open (a vehicle-system TOML pops the
vehicle-case dialog automatically). Requires the [gui] extras;
a missing Qt / PyVista stack surfaces a clear install hint.
aeolus1d optimize¶
aeolus1d optimize <case.toml> [--out tuned.toml]
Phase-44 case-driven optimization. Reads the [optimize] block
from the TOML, runs scipy.optimize.minimize against the
declared probe references, and prints a per-evaluation
(theta, J) trace. --out writes the optimal case TOML
with the substituted parameters; exits non-zero on solver
failure or a missing [optimize] block.
The block schema:
[optimize]
method = "nelder-mead" # any scipy.optimize.minimize method
maxiter = 60
tol = 1.0e-2
dt_sample = 1.0e-4 # trace sampling rate for probe matching
[[optimize.params]]
path = "pipes[0].init.p" # path-based binding into the Case tree
theta0 = 1.10e5
bounds = [5.0e4, 3.0e5] # optional; required for bounded methods
[[optimize.probes]]
pipe = "tube"
x_frac = 0.5 # fractional position along the pipe
var = "p" # rho / u / p / T
reference_csv = "measured.csv" # path resolved relative to this TOML
metric = "rmse" # l1 / l2 / rmse / max
See examples/optimize/tune_initial_pressure.toml for a worked
example that recovers a 1.50e5 Pa truth from a 1.10e5 Pa guess.
aeolus1d sensitivity¶
aeolus1d sensitivity <case.toml> [--h H] [--kind {central,forward}]
Finite-difference gradient of the [optimize] objective at the
baseline parameter vector — answers "which knob matters" before you
pay for a full optimization. Reads the same [optimize] block as
aeolus1d optimize (params + probes), perturbs each parameter
(--h step; --kind central is 2 runs/param, forward is 1)
and prints the per-parameter gradient table.
aeolus1d cycle-settle¶
aeolus1d cycle-settle <engine.toml> [--rpm RPM] [--cycles N] [--tol TOL]
Cycle-convergence diagnostic for engine cases: runs the case for up
to --cycles engine cycles and reports when the per-cycle IMEP /
W_cycle has settled to within --tol cycle-to-cycle — i.e. how
many settle cycles a quoted number actually needs. --rpm
overrides the case's engine speed.
aeolus1d drive-cycle¶
aeolus1d drive-cycle <vehicle_case.toml> [--out TRACE.csv]
Run a closed-loop driver over a drive cycle from a vehicle-system
TOML case (the W5 workflow) and report cycle KPIs (energy, SOC,
fuel, distance); --out writes the per-step trace CSV (an output equal to the case
file is refused, exit 2; so is a bad vehicle case, with error: <file>: <reason> on stderr). See
vehicle-system.md for the case format.
aeolus1d parallel¶
aeolus1d parallel case1.toml case2.toml ... [--out-dir DIR] [-j JOBS]
Run multiple cases concurrently across N worker processes
(parameter sweeps, Monte-Carlo, batch comparisons). Each case
writes <out-dir>/<stem>.h5; -j defaults to the CPU count.
aeolus1d acoustics¶
aeolus1d acoustics <run.h5> --probe pipe:<id>:<x_frac> [--probe-out P] \
[--probe-2 P --probe-out-2 P --probe-spacing M [--c-sound M/S]] \
[--third-octave] [--octave-rolloff ORDER]
Post-process a stored pressure trace (the HDF5 must come from
aeolus1d run --trace-dt) into SPL / dB(A) / dB(C) and 1/1- or
1/3-octave band levels. With --probe-out it also reports the
transmission loss between the two probes — note this is the naive
amplitude-ratio TL, valid for anechoic terminations only. For a
reflective termination add --probe-2 (the second probe of the upstream
pair), --probe-out-2 (the second probe of the downstream pair),
--probe-spacing (the spacing of each pair, m) and optionally --c-sound
(default 343 m/s): the command then prints one extra line with the
two-microphone wave-decomposition TL (the transmission_loss_decomposed
Python API); an incomplete set exits 2 (added 2026-10-07).
aeolus1d audio¶
aeolus1d audio <run.h5> --probe pipe:<id>:<x_frac> --out OUT.wav \
[--rate HZ] [--duration S]
Convert a pressure-probe trace from a --trace-dt HDF5 into a
WAV file — listen to the exhaust note. --rate resamples to the
given audio rate; --duration loops/trims to a target length.
aeolus1d turbo-map¶
aeolus1d turbo-map <map.cmp|.trb|.npz> [--out PNG] [--trace RUN.H5] \
[--turbo-id ID] [--no-envelope] [--no-eta] [--title T] [--dpi N]
Render a turbo map file as a compressor / turbine plot (speed lines,
efficiency islands, surge/choke envelope). --trace overlays the
operating-point trajectory recorded in a run HDF5 (--turbo-id
picks the machine when the case has several). See
turbo-map-format.md for the file formats.
aeolus1d thermo-db¶
aeolus1d thermo-db [info|fetch] [--force]
Fetch and inspect Burcat & Ruscic's Third Millennium thermochemical
database — NASA-7 polynomials for ~3700 species, against the ten in the
package's built-in seed DB. info (the default) reports the cache
location, species count and citation; fetch downloads it (~2.3 MB).
The data is not bundled. Burcat's terms are "free of charge on
condition that proper quotation is given" and "the sale of this
information is strictly forbidden and it cannot be listed, printed or
copied in any commercial publication form"; aeolus1d is MIT licensed, which
grants commercial use, so redistributing it inside the wheel would ship
parts that disagree about what you may do. It caches to
$XDG_DATA_HOME/aeolus1d/burcat (override with AEOLUS_BURCAT_DIR),
deliberately outside the installed package.
If you publish results that use it, cite ANL-05/20 / TAE 960 (2005) —
aeolus1d thermo-db info prints the full reference. See
chemistry-file-formats.md for the loader API and the format notes.
aeolus1d expand¶
aeolus1d expand <case.toml> [--out OUT] [--full] [--quiet]
Rewrite a case TOML's [case] block in canonical form. Any
CaseHeader field omitted from [case] fills from its schema
default — a minimal block listing only the knobs that differ from
default (plus the name / t_end anchors) is the idiomatic form,
and that compact block is what expand writes by default. Pass
--full to emit every CaseHeader field explicitly (the older
fully-specified form). Either way, all other sections ([gas],
[[pipe]], [[junction]], [[bc]], ...) plus comments and
whitespace are preserved verbatim, and load_case reconstructs an
identical Case from both forms.
Unknown / misspelled keys are always rejected with a fuzzy-match
suggestion (e.g. cf1 → did you mean 'cfl'?); that typo-safety is
independent of the default-fill policy. The optional full-form lint
(load_case(..., strict_case_header=True), off by default) additionally
requires every field be written explicitly; it is honoured unless
AEOLUS_LENIENT_CASE_HEADER=1 is set.
For the field-by-field reference of every CaseHeader key (name, type,
default, one-line purpose), including default flips like
qs_chem_fused_rhs = True (MR !134) and
substrate_wall_in_newton_residual = True (MR !137), see
run-settings.md.
Interop subcommands¶
aeolus1d import {gt_power|avl_boost|wave|avl_cruise_m|gt_suite_native} <input> <output.toml>
aeolus1d export {gt_power|avl_boost|wave} <case.toml> <output.xml>
aeolus1d export-fmu <case.toml> <output.fmu>
Best-effort structural import / export with GT-Power / AVL Boost /
WAVE (XML), plus native AVL Cruise M (.proj archive) and GT-Suite
(.gtm) import, and FMI 2.0 FMU export for co-simulation with
GT-SUITE / Simulink / Modelica / Dymola. See interop.md for the
translation tables and lossy-field warnings. Three companion
diagnostics:
aeolus1d extract¶
aeolus1d extract <file.proj|file.gtm|file.gtsub> [--out DIR]
Split a Cruise M .proj or GT-Suite .gtm/.gtsub into per-object
files under a sub-directory — inspect exactly what the vendor file
contains before/after an import.
aeolus1d inspect-gtm¶
aeolus1d inspect-gtm <file.gtm> [--top N]
Diagnose a GT-Suite native .gtm: template-id usage, object counts,
sample object names — use it to assess import coverage for a given
model before running aeolus1d import gt_suite_native.
aeolus1d fields¶
aeolus1d fields <file.proj|file.gtm> [--kind KIND] [--status STATUS ...]
List what a Cruise M .proj or GT-Suite .gtm holds that aeolus1d does
not import, grouped by status (not-supported, unknown; --status all
adds read and not-needed) with the reason for each. The same counts
appear as one line in every aeolus1d import report. For a .gtm it
also prints the object names each template id carries in that file.
aeolus1d handshake¶
aeolus1d handshake <case.toml> [--all] [--json]
Build the case and print its connection-handshake ledger: for every gas
connection (pipe end to plenum, cylinder, turbo, BC or another pipe), the
species each side carries and the gas model each side computes with
(gamma, R, EOS), and whether the two agree. A connection is loss when a
species one side carries is dropped or builds up on the other, and
mismatch when the two sides read the handed-over state with different
gas models. By default only those are listed; --all lists every
connection. The exit code is 1 when any exists. [case] handshake decides
what a run does about them (see run settings); the GUI
draws the same ledger under Run → Connections….
aeolus1d auci-compare¶
aeolus1d auci-compare <project.proj> [--template TEMPLATE]
Compare aeolus1d's aftertreatment closure defaults against the Cruise M
AUCI rate constants in a .proj, side-by-side — the calibration
diff when importing an AVL aftertreatment model.
Headers in TOML¶
Some example files leave header comments like # scheme=muscl
N_left=160. The loader honours these as overrides — useful for
sweeping a parameter without editing the body.
Programmatic alternative¶
If you'd rather drive the solver from a script, the aeolus1d run
path is a thin wrapper over two aeolus1d.io.case calls — load the
TOML into a Case, then advance it:
from aeolus1d.io.case import load_case, run_case
case = load_case("two-pipes.toml")
pipes, junctions, t_final = run_case(case) # dict[str, Pipe], list, float
run_case(case, on_step=None, max_steps=None, t_start=0.0,
live_control=None) builds the network and advances it to
case.case.t_end, returning the same Pipe + Junction objects the
CLI works with (the CLI writes the HDF5 / CGNS / manifest itself via
aeolus1d.io.writer.write_hdf5 — run_case does not touch disk).
Useful for batch sweeps and ML closure training. The optimization
API is similarly available as
aeolus1d.optimize.optimize_case(case_factory, params, objective, …)
for users who'd rather build the objective callable in Python than
declare it in TOML.