Skip to content

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 for python -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.