Metadata-Version: 2.4
Name: aeolus1d
Version: 0.3.0rc1
Summary: 1D compressible gas-dynamics pipe-network solver
Author: Claude
Author-email: Niklas Nordin <niklas.nordin@nequam.se>
License-Expression: MIT
License-File: AUTHORS.md
License-File: LICENSE
Keywords: 1d,cfd,compressible,engine-simulation,gas-dynamics,numba
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Requires-Dist: h5py<4,>=3.8
Requires-Dist: numba<0.66,>=0.58
Requires-Dist: numpy<3,>=1.24
Requires-Dist: scipy<2,>=1.10
Requires-Dist: tomli<3,>=2.0; python_version < '3.11'
Provides-Extra: build
Requires-Dist: pyinstaller>=6.0; extra == 'build'
Provides-Extra: chem-fast
Requires-Dist: numbalsoda>=0.3; extra == 'chem-fast'
Provides-Extra: chemistry-yaml
Requires-Dist: pyyaml>=6.0; extra == 'chemistry-yaml'
Provides-Extra: dev
Requires-Dist: hypothesis; extra == 'dev'
Requires-Dist: matplotlib; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pip-tools>=7; extra == 'dev'
Requires-Dist: pre-commit>=3; extra == 'dev'
Requires-Dist: pytest-timeout; extra == 'dev'
Requires-Dist: pytest-xdist; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: types-markdown; extra == 'dev'
Requires-Dist: types-pyyaml; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs; extra == 'docs'
Requires-Dist: mkdocs-material; extra == 'docs'
Requires-Dist: mkdocstrings[python]; extra == 'docs'
Provides-Extra: gui
Requires-Dist: markdown>=3.4; extra == 'gui'
Requires-Dist: matplotlib>=3.4; extra == 'gui'
Requires-Dist: pyarrow>=10.0; extra == 'gui'
Requires-Dist: pyside6>=6.5; extra == 'gui'
Requires-Dist: pyvista>=0.42; extra == 'gui'
Requires-Dist: pyvistaqt>=0.11; extra == 'gui'
Provides-Extra: interop
Requires-Dist: defusedxml>=0.7; extra == 'interop'
Requires-Dist: fmpy>=0.3; extra == 'interop'
Requires-Dist: lxml>=5.0; extra == 'interop'
Requires-Dist: pycgns>=6.2; extra == 'interop'
Requires-Dist: pythonfmu>=0.6; extra == 'interop'
Provides-Extra: ml
Requires-Dist: scikit-learn>=1.0; extra == 'ml'
Requires-Dist: torch>=2.0; extra == 'ml'
Provides-Extra: real-gas
Requires-Dist: coolprop>=6.4; extra == 'real-gas'
Description-Content-Type: text/markdown

# aeolus1d

<!-- Badges must survive being rendered on PyPI, where this file is the
     project's front page and relative links do not resolve. The pipeline
     badge and the version/python badges' link targets used to point into
     the GitLab project, which is private — they rendered as a broken image
     and three dead links for anyone who is not a member. Only badges whose
     image AND link are publicly reachable belong here. -->

<!-- This badge tracks the latest FINAL release, not the pyproject line, and
     is pinned to it by tests/unit/test_version_agreement.py alongside
     CITATION.cff and SECURITY.md. 0.3.0rc1 is a pre-release; it does not
     move this until 0.3.0 ships. -->
[![version](https://img.shields.io/badge/version-0.2.0-informational)](https://pypi.org/project/aeolus1d/)
[![python](https://img.shields.io/badge/python-3.9%2B-blue)](https://pypi.org/project/aeolus1d/)
[![license](https://img.shields.io/badge/license-MIT-yellow)](https://opensource.org/licenses/MIT)
[![code style: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![type-checked: mypy --strict](https://img.shields.io/badge/type--checked-mypy%20--strict-blueviolet)](https://mypy-lang.org/)

A 1D compressible gas-dynamics pipe-network solver — open-source, pure
Python (NumPy / SciPy / Numba). Foundation for an open engine and
powertrain simulator in the tradition of 1D wave-action codes.

Named after Αἴολος, keeper of the winds.

---

## What's in the box

aeolus1d is a working 1D Euler solver with engine- and
aftertreatment-grade modelling depth, validated within a **stated
regime** — see **[Scope & limitations](docs/scope-and-limitations.md)**
for what is validated, what is demonstrative, and what it is not
qualified for. The codebase ships ~3900 unit + integration tests,
229 bundled examples, a golden-snapshot
regression net, a Qt / PyVista visual editor, and a TOML case format
with full schema validation.

### Solver core

- **HLLC Riemann solver** (Toro Ch. 10) with closed-form analytic
  Jacobians end-to-end (frozen-MUSCL ideal-gas + variable-γ + real-
  gas EOS-PR / RK / tabulated). Optional **HLLC-LM** low-Mach correction
  (Thornber).
- **Reconstruction**: 1st-order Godunov, MUSCL (van-Leer / minmod),
  or **MP5** (Suresh-Huynh, 5th-order monotonicity-preserving).
- **Time integration**: explicit SSP-RK2 / SSP-RK3, **plus** implicit
  Euler / BDF2 / Radau IIA(3) with block-tridiagonal Newton, adaptive
  Δt, and **Picard** outer iteration over network ↔ component coupling
  (`picard_max_iter`, adaptive controller). (A matrix-free JFNK path
  was benchmarked, found slower than the direct block-Thomas solve at
  every tested N, and removed — see `docs/numerics.md`.)
- **Quasi-1D Euler** with variable area and a per-cell closure stack
  (friction, wall heat transfer, porous-media drag, two-phase catalyst,
  reactive chemistry, surface kinetics).
- **Real-gas EOS**: ideal / Redlich-Kwong / Peng-Robinson /
  CoolProp-backed tabulated lookup, plumbed through the HLLC kernel
  and the network MUSCL path. Closed-form first- and second-derivative
  Jacobians for the cubic-EOS Newton path.
- **Variable-γ stack**: cell-averaged scalar, per-cell γ in HLLC, and
  per-cell γ in MUSCL / MP5.
- **Multi-species transport** with a `(Y, Y_extras)` stack; per-cell
  species columns travel with the gas via a JIT'd 1st-order upwind on
  the HLLC mass flux.
- **Strang-split chemistry**: scipy LSODA per cell or a hand-tuned
  `@njit` Bader-Deuflhard semi-implicit Bulirsch-Stoer kernel.
  Mechanism-driven kinetics from CHEMKIN / Cantera YAML / JANAF
  imports plus an in-tree species thermo DB (NASA-7 polynomials).

### Components

- **Pipe** — finite-volume grid with ghost cells (Ng = 1 or 2),
  variable area, optional per-cell γ + species columns, optional
  friction / heat / porous / catalyst / filter sub-state. Multiple
  init modes (uniform, Riemann, regional, mode-shape).
- **Junctions** — plenum (multi-port + species transport),
  Corberán CP+h (lossless or with per-port `K_loss`), Extended
  Helmholtz Resonator (per-port `u_n` ODE state), passthrough
  (transparent area-preserving connector). Phase-23 LODI /
  Poinsot-Lele characteristic port BC available on plenum ports.
- **Cylinder** — slider-crank V(θ), Wiebe combustion, two-zone
  burnt / unburnt diagnostic with per-zone γ tracking, continuous
  lift schedules on every port, Livengood-Wu knock prediction +
  closed-loop θ_spark retard controller. Engine-cycle accumulator
  (`EngineMetrics` / `cycle_summary` / `fleet_summary`) for IMEP /
  BSFC / knock-fired-rate post-processing. **Phase E5 VVT**: per-port
  `phase_offset_deg` (cam-phaser advance / retard) and `lift_scale`
  (variable-lift multiplier, 0.0 = effective deactivation) — both
  TOML-configurable and mutable at runtime by any controller.
  Carries a well-mixed **charge composition** — an inventory of every
  species column its ports touch, exchanged with the runners through
  both valve couplings, with burnt fuel converted to stoichiometric
  CO₂ + H₂O and the matching O₂ debit from the fuel's H/C ratio.
- **Turbomachinery** — quasi-steady map-driven Compressor + Turbine
  paired through a 1-DOF Shaft inertia ODE. 3-D `TurboMap` with a
  `vane_open` axis for VGT / IGV. CSV / NPZ map loaders.
  `bypass_open` actuator on Turbine for wastegate use. Compressor
  surge / choke / idle / backflow regime diagnostics. **v0.5
  robustness pack**: opt-in γ/R off-test-gas correction
  (`gas_property_correction`, GT Flow §3.10) for EGR / rich-SI /
  diesel part-load maps, near-surge mass-flow damping
  (`surge_damping`), and shaft mechanical efficiency (`eta_mech`) +
  viscous bearing / windage drag (`bearing_friction`).
- **Volume** — 0-D plenum with mass / energy / optional species
  state. Used by junctions and as a generic 0-D building block.
- **Helmholtz resonator** — 1-D neck + cavity component with a
  closed-form natural-frequency match against the 0-D textbook
  formula on the quasi-2D bench.
- **Coolant pipe + thermal wall** — Phase Coolant: a 1-D coolant
  pipe carrying per-cell ``T_cool(x)`` paired with a per-cell
  solid-wall thermal mass (``T_wall(x)``) coupled to a gas pipe
  through ``h_gas`` / ``h_cool``. Closes the engine-cooling story
  alongside the 0-D ``aeolus1d.coolant.CoolantState`` lumped model
  (Phase E3) — pick the 0-D model for fast bulk-block warm-up
  studies, the 1-D pipe for cylinder-head / jacket-channel spatial
  resolution. Wired into ``advance_network`` via the
  ``coolant_couplings`` parameter (Coolant-2); the standalone
  forward-Euler ``advance_coolant_system`` step driver remains for
  coolant-only studies.

### Closures

- **Friction**: NONE / `DARCY_CONST` / `COLEBROOK` (Haaland turbulent +
  laminar / transition) / `MINOR_LOSS` (K-method lumped bend / fitting
  loss, additive to wall friction; `K` either user-supplied or derived
  from bend geometry via the Idelchik smooth-bend correlation).
- **Wall heat**: NONE / `CONSTANT_TWALL` / `FLOW_NU` (flow-correlation
  Nusselt → h: Dittus-Boelter / Colburn / Gnielinski / Sieder-Tate over
  a laminar Nu = 3.66 floor).
- **Dynamic wall thermal mass**: per-cell `T_wall(x)` ODE, coolant-jacket
  coupled or standalone with external convection + radiation loss
  (engine warm-up / underbody heat rejection), two-way energy-coupled to
  the gas; optional axial conduction and **radial multi-layer** walls
  (steel + insulation + shield, series half-thickness conductance).
- **Porous (Darcy-Forchheimer)**: constant μ or Sutherland μ(T);
  ideal-gas + real-gas analytic Jacobian path.
- **Catalyst**: two-phase heat (`h · a_v · (T_s − T_g)`) with constant
  or Re/Pr-dependent Nusselt (Hawthorn monolith correlation).
  Mechanism-driven surface kinetics (Phase-46d).
- **Aftertreatment**: full DOC (HC + CO Arrhenius branches; optional
  sulfate-poisoning + active-regen via `DOCSulfurController`),
  DPF (passive trap + cake Δp + active O₂ regen + cake thermal mass +
  opt-in soot-ox heat-release stability clamp (`hrr_clamp`) +
  **substrate-wall thermal inertia** (`Omega_wall`/`h_wall`: the
  SiC/cordierite monolith as a thermal-inertia element for WHTC /
  cold-start, v0.5) + closed-loop heater PI), SCR (Cu-zeolite NH₃ ads/des / standard SCR /
  oxidation), SCRF, two-stage SCR, urea-water injector + HNCO
  hydrolysis, TWC (Voltz-Morgan-Liederman 3-pathway and Brinkmeier
  11-reaction with Ce O₂ storage, plus two opt-in channels: passive-SCR
  NH₃ generation `A_nh3_form_brnk` and C₃H₆ total oxidation
  `A_c3h6_ox_brnk`)).
- **Equation of state**: ideal / RK / PR / tabulated, with closed-form
  `∂a/∂T`, `∂a/∂ρ` for the cubic EOSs.
- **ML seam**: `py_friction` / `py_heat` / `py_porous` / `py_reaction`
  / `py_reaction_multi` / `py_reaction_surface` / `py_junction_bc` /
  `py_turbo_map` callables drop in at the per-cell or per-port level
  for learned closures. Bundled NN port-state and turbo-map surrogates
  (`LearnedTurboMap` MLP, PINN closure adapter).

### Throttle body

- **Throttle body inlet BC** (`aeolus1d.bc.throttle`) — Phase E1:
  motor-driven intake throttle modelled as a controllable
  isentropic orifice. ``Throttle.open ∈ [0, 1]`` mutable state
  field that any controller (or hand-written closure) can drive on
  every step. Same Saint-Venant choked / subsonic dispatch as the
  EGR-valve BC; sized for SI gasoline intake workflows
  (``Cd ≈ 0.7`` default, fresh-air composition write-through).
- **Charge-air-cooler closure** (`aeolus1d.closures.heat.make_charge_air_cooler_py_heat`)
  — Phase E2: counterpart to the EGR cooler for the intake side.
  Same ε-NTU model, just named for intake-side workflows so
  callers don't have to alias an exhaust-side name. Pairs with
  the throttle BC for a complete boosted-SI intake plumbing
  stack.

### Boundary conditions

`stagnation_inlet` / `pressure_outlet` / `mass_flow_inlet` /
`velocity_inlet` / `valve` (Chalet-Chesse-Hetet effective-area) /
`valve_orifice` (Winterbone-Pearson; forward/reverse `Cd`, area-ratio
default `Cd`, Borda-Carnot recovery, perforated-plate `n_holes`) /
`wall` / `transmissive` /
`nonreflecting` (LODI / Poinsot-Lele). Every static BC has a
`transient_*` counterpart driven by a `Clock` schedule (mass flow,
velocity, stagnation, pressure-outlet, plus per-extras-species
schedules for inflow `Y` mass fractions).

### Controllers

`BoostController` (VGT vane PI on manifold pressure),
`WastegateController` (sign-inverted PI on `Turbine.bypass_open`),
`DPFHeaterController` (heater PI on mean cake temperature),
`DOCSulfurController` (lumped sulfate storage / reduction integrator),
`EGRValveController` (PI on intake-pipe O₂ mass fraction, actuating
a controllable orifice BC's open fraction — Phase 8m-ctrl-2 +
Phase 8m-ctrl-3 added the `[[controller]] kind = "egr_valve"` +
`[[bc]] kind = "egr_valve"` TOML schema integration),
`FuelInjectorController` (PI on exhaust-side O₂ mass fraction —
the canonical lambda feedback signal — actuating
`Cylinder.m_fuel_per_cycle`; multi-injector cylinders preserve
per-injector ratios — Phase 8m-ctrl-4 + Phase 8m-ctrl-5 added the
`[[controller]] kind = "fuel_injector"` TOML schema integration),
`SparkTimingController` (PI on Livengood-Wu knock integral, actuating
`WiebeCombustion.theta_start_deg`; complements the existing reactive
knock-event retard with preventive margin control — Phase 8m-ctrl-6 +
Phase 8m-ctrl-7 added the `[[controller]] kind = "spark_timing"` TOML
schema integration).
All implemented as velocity-form PI; pluggable as `on_step=` callbacks
or wired via `[[controller]]` blocks in TOML — six controllers, full
TOML symmetry.

A general **control / signal toolbox** (`aeolus1d.control.signal_toolbox`)
provides reusable blocks the domain controllers and a driver model build
on: first-order low/high-pass + moving-average + exponential filters,
rate limiter, saturation, Schmitt relay, discrete delay, discrete-IIR
transfer function, signal sources (step/ramp/sine/sawtooth/exponential),
and RMS/RMSE.

### Vehicle & powertrain

- **Longitudinal vehicle** (`vehicle.py`): 1-DOF road load (rolling +
  aero + grade), rotating-mass factor.
- **Driveline** (`driveline.py`): N-speed gearbox, torque converter,
  clutch, auto-shifter; **AMT shift transients**
  (`transmission_shift.py`) — the four-phase disengage→sync→engage→end
  state machine + a torsional elastic flange.
- **Drive cycles & driver**: `drive_cycles.DriveCycle` (speed-time
  profiles from CSV / segments / illustrative) + a closed-loop
  `driver` (velocity-tracking PI + road-load feed-forward →
  engine-torque + brake demand), with `analysis.vehicle_tasks`
  (coastdown, full-load acceleration, elasticity, gradeability, cycle
  KPIs) and an engine **BSFC fuel map** (`fuel_map.py`).
- **Hybrid / electric**: parallel hybrid (`hybrid.py`); a multi-RC
  **ECM battery** (`battery_ecm.py`, with pack scaling), a map-based
  **e-motor** + **inverter** + **DC-DC** loss models (`emotor.py`), a
  **planetary** power-split (`planetary.py`), **regen-braking** split
  (`regen.py`), and **energy management** (`energy_management.py`:
  series-hybrid with thermostat + ECMS charge-sustaining strategies).

### Tooling

- **TOML case format** with full schema validation (Phase B10
  two-pass validator with 21 sub-validators + TOML source-position
  lookup for traceable error messages + unknown-key detection with
  ``difflib`` did-you-mean hints so typos like ``[[pipe.initt]]``
  surface as a warning instead of silently defaulting).
- **HDF5 output** with Phase-A17 checkpoint / resume.
- **Visual editor** (`aeolus1d-gui`) — Qt / PyVista drag-and-drop
  palette, auto-generated inspector forms from the schema's
  `KIND_FIELDS` table, run-as-subprocess controller, 3-D network
  view with HDF5 trace overlay, 2-D plot dock with probe time-series,
  acoustic-mode FEM eigensolver dock, Solver Internals diagnostic
  view (live analytic-vs-FD routing per closure).
- **CLI** — `aeolus1d run`, `aeolus1d bench`, `aeolus1d optimize`,
  `aeolus1d examples`, `aeolus1d gui`, `aeolus1d audio`,
  `aeolus1d acoustics` (NVH metrics — SPL / dB(A) / dB(C) +
  octave bands + transmission loss from a stored pressure-probe
  HDF5 trace), `aeolus1d turbo-map`
  (renders a `.cmp` / `.trb` / `.npz` turbo map as a
  manufacturer-style compressor or turbine plot — iso-speed
  lines, η contours, surge / choke envelope, optional
  operating-point overlay). Bench harness ships canonical
  literature cases with closed-form pass criteria
  (`examples/benchmarks/`).
- **Interop** — Cantera YAML import, CGNS export
  (Tecplot / Paraview-readable), CHEMKIN mechanism import +
  native JSON-per-mechanism persistence, AVL Cruise M `.cm` import,
  GT-Suite native `.gtm` import (Phase B1 inspect + B3 read for
  pipes / junctions / BCs + **Phase 9a** recursive `.gtsub`
  sub-assembly resolution + **Phase 9b** engine breathing-path
  templates — cylinder + cylgeom, compressor, turbine,
  shaft, exhaust valveconn — wired through the orifice-
  passthrough chain + **Phase 9c** EndEnvironment auto-attach
  emitting real `stagnation_inlet` / `pressure_outlet` BCs at
  the breathing-path termini + **Phase 9e** cylinder port
  wiring through the valveconn-flowsplit-pipe chain so the
  imported cylinders actually breathe + **Phase 9d** GT
  thermal lookup-table dispatch (HTC + T_wall stamped onto
  pipes by longest-substring name match — F2 intake ports
  get h=3000 W/m²K, bellows get h=35 W/m²K) + **Phase 9f**
  GT-Wide ASCII `.cmp` / `.trb` map readers so compressors /
  turbines carry their real performance data instead of
  synthetic defaults + **Phase 9g** controller sub-assembly
  translation (wastegate wires to `WastegateController`; the
  other 6+ F2 controllers silent-skip with named lossy entries
  pointing at FMI co-sim) + **Phase 9h Tier-1** 3-zone cylinder
  wall heat transfer (head / liner / piston each get their own
  prescribed T; Q̇ splits across the matching surfaces while
  gas-side h stays the bulk Woschni correlation; legacy
  single-T behaviour bit-exact when zone Ts collapse) +
  **Phase 9i** an
  end-to-end integration test pinning that the F2-BWC9-100
  driver case loads, builds, and steps the timeloop +
  **Phase 9j neutral schematic**
  carrying cylinders / turbos / shafts / heat-exchangers /
  controllers end-to-end so the importer lands directly on a
  runnable `Case`), FMU export scaffolding.
- **Optimisation + sensitivity** — `aeolus1d optimize` minimises a
  trace-vs-CSV metric over user-declared parameter handles;
  `aeolus1d.sensitivity` does parameter sweeps.
- **Quality bar** — `mypy --strict` coverage across all 196
  non-`__init__` source modules (148 in `src/aeolus1d/`, 48 in
  `aeolus1d_gui/`); ruff pre-commit hook on every staged file
  (with CI re-run on push); ~3900 unit + integration tests under
  parallel `pytest-xdist`. GitLab CI is mirrored on GitHub Actions.

### Post-processing

GT-POST / Cruise-M-grade analysis on top of any tracer / HDF5
output. Full reference in
**[docs/post-processing.md](docs/post-processing.md)**.

- **Engine cycle diagnostics** (`aeolus1d.engine_post`) — IMEP /
  PMEP / IMEP_gross / p-max, **heat-release inversion** with
  CA10 / CA50 / CA90 + polytropic exponents, **volumetric
  efficiency**, **COV(IMEP)** combustion-stability indicator,
  BSFC + η_thermal.
- **Live cycle accumulator** (`aeolus1d.analysis.engine_metrics`)
  — `CycleAccumulator` + `MultiCycleAccumulator` for multi-
  cylinder fleet rollup during a run via the `on_step=` hook
  (knock-fired rate, mean retard, max knock-integral).
- **Acoustics / NVH** (`aeolus1d.analysis.acoustics`) — SPL /
  **dB(A)/dB(C)/dB(Z)** per IEC 61672, power spectrum, IEC 61260
  **1/1 and 1/3 octave bands**, **engine-order analysis**,
  **transmission-loss** — the `aeolus1d acoustics --probe-out` CLI
  reports the naive amplitude-ratio TL (anechoic terminations
  only); the reflection-correct **two-microphone wave
  decomposition** (`transmission_loss_decomposed`) and insertion
  loss are in the Python API — **free-field monopole
  radiation** (in-pipe → listener SPL), and **combustion-noise**
  prediction from a cylinder-pressure trace. A frequency-domain
  **four-pole transfer-matrix** muffler solver
  (`aeolus1d.analysis.acoustics_tm`) gives transmission loss directly
  from chained ABCD duct elements (validated against the analytic
  expansion-chamber TL).
- **System-level reports** (`aeolus1d.analysis.system_reports`) —
  **turbo operating-point overlay** on the compressor map (with
  surge / choke envelope), Sankey-ready **energy balance** (fuel
  → work + pump + exhaust + wall + residual), **cumulative
  emissions** integration past a probe, **drive-cycle**
  (NEDC / WLTP / FTP / user) integration harness, and
  **steady-state / periodic-limit-cycle convergence** detection.
- **WAV export** (`aeolus1d.audio`) for listening to the engine
  note at audio rate.
- **State-space linearization** (`aeolus1d.analysis.linearize`) —
  Phase L1: build a continuous-time ``(A, B, C, D)`` model around a
  steady operating point by central finite-difference on the
  pipe-residual kernel. Plugs straight into ``scipy.signal.StateSpace``
  / ``python-control`` for Bode plots, pole / stability analysis,
  LQR / H∞ controller synthesis, and Kalman observer design.
  Phase L2 extends this to multi-pipe + junction-coupled networks:
  ``linearize_network`` packs every pipe's interior cells into one
  state vector, calls ``junction.apply_port_bcs()`` between FD
  perturbations so junctions cross-couple their connected pipes.
  Phase L2b (default) also packs each junction's ``(m, E_internal)``
  into the state vector and reads ``Junction.accumulate_rates()``
  for the corresponding time derivatives, giving full bidirectional
  pipe ↔ plenum coupling. Phase L2c additionally packs port-level
  ``u_n`` state — turbo shaft ω, extended-Helmholtz-resonator slug
  velocity, anything matching the network timeloop's
  ``port_u_rates`` / ``ports[i].u_n`` duck-typing contract. Two
  opt-out flags (``frozen_volumes`` / ``frozen_shafts``) collapse
  back to L2 (pipes-only) or L2b (pipes + volumes) when partial
  linearization is more honest — e.g. when a network contains a
  cylinder whose slider-crank angle deliberately stays out of the
  pack (kinematic clock, not an integrable ODE — cycle-averaged
  analysis is the right tool there).
  Phase L3 adds a ``method="analytic"`` opt-in that pulls A
  directly from the implicit Newton solver's closed-form
  block-tridiagonal Jacobian assembly (the same one Phases
  B16 → B22 ship for every closure kind). For pure pipe-and-far-BC
  networks (``junctions=()``) one assembly per pipe replaces
  ``2n`` residual sweeps, machine-precision-exact rather than
  FD-truncated. Phase L3b extends this to junction-coupled
  networks via a hybrid: pipe-on-pipe diagonal blocks are
  overlaid analytically onto the FD-derived A; cross-blocks
  (pipe ↔ volume, pipe ↔ u_n, pipe-pipe-through-junction) stay
  FD. Pure-analytic cross-blocks are BACKLOG L3c.

---

## Install

Requires Python ≥ 3.9. Canonical instructions live in
[docs/installation.md](docs/installation.md); the short form:

```bash
python -m venv .venv
source .venv/bin/activate            # Windows: .venv\Scripts\activate

pip install aeolus1d                 # solver + CLI
pip install "aeolus1d[gui]"          # + desktop GUI (optional)
```

Working **from a clone** instead (contributors), the same three lines
become editable installs:

```bash
pip install -e .
pip install -e ".[gui]"
pip install -e ".[dev]"              # + tests / lint / type-check
```

After this, the `aeolus1d` (and, with `[gui]`, `aeolus1d-gui`) console
scripts are on PATH. Verify:

```bash
aeolus1d warmup                        # one-time kernel compile, ~19 s
aeolus1d examples                      # lists the bundled cases; line 1 is their directory
aeolus1d run "$(aeolus1d examples | head -1)/01_single_pipe_sod.toml" --out sod.h5
aeolus1d-gui                           # opens the desktop GUI ([gui] extra)
```

> The bundled cases ship **inside the package**, so `examples/…` is not a
> path that exists in your working directory after `pip install`. Use
> `aeolus1d examples` to find them (it prints the directory), or run from
> a clone. Earlier versions of this README showed
> `aeolus1d run examples/01_single_pipe_sod.toml`, which only worked from
> a source checkout and failed with `case file not found` for everyone
> who installed from PyPI.

The first solver run on a fresh environment pays a one-time Numba kernel
compile — **~19 s** measured on x86-64 with numba 0.65.1, and
hardware-dependent. `aeolus1d warmup` does it out loud up front and
caches the kernels on disk so every later run starts in seconds.

Two loosely-pinned requirements files are shipped as an alternative
to the extras: `requirements.txt` (simulator core + the desktop GUI
runtime) and `requirements-dev.txt` (adds tests — pytest + xdist +
timeout + hypothesis — lint (ruff), type-check (mypy), plotting
tools, and the docs builder).

More optional extras through `pyproject.toml`:

```bash
pip install -e ".[real-gas]"       # CoolProp — cubic / multi-parameter EOS
pip install -e ".[chemistry-yaml]" # pyyaml — Cantera mechanism import
pip install -e ".[interop]"        # pycgns / pythonfmu / fmpy / lxml
pip install -e ".[build]"          # PyInstaller — end-user binaries
```

If you're going to commit code, install the pre-commit hook so
ruff issues fail locally before they reach the CI gate:

```bash
pre-commit install                  # one-time per checkout
```

After that, every `git commit` runs ruff (with `--fix`) on the
staged files. The same rules run in CI on push, so a clean
local commit means a green CI run.

---

## Quick start

```bash
pytest -q
aeolus1d bench nozzle
aeolus1d run examples/03_two_pipes_junction.toml --out run.h5
aeolus1d run examples/05_catalyst_plug.toml --out plug.h5
```

See `examples/` for the 229 bundled cases (`aeolus1d examples` lists
them all, grouped by subdirectory) and
`examples/benchmarks/` for canonical literature benches with
reference-solution pass criteria. Most top-level cases have a matching
`examples/plots/plot_NN_*.py` that renders the expected physics
visualisation; `examples/plots/README.md` marks the ones that do not.

The **[Tutorials](docs/tutorials/)** are the intended on-ramp for new
users — three guided walkthroughs (Sod shock tube → networks +
junctions → engine end-to-end), each anchored on a bundled example so
you can run the same commands and modify-then-rerun.

---

## GUI

`aeolus1d-gui` (or `aeolus1d gui` / `python -m aeolus1d_gui`) opens the
Qt / PyVista visual editor + post-processor. Drop pipes / junctions /
cylinders / BCs from the left palette onto the canvas; drag yellow
port markers to wire components together. Press F5 to run; the 3-D
network view colours pipes by ρ / u / p / T and a time slider scrubs
through traced samples. The GUI keeps a `*.layout.json` sidecar next
to each case file with node positions + probes so reopening restores
exactly what you had on screen.

Full walkthrough in **[docs/gui.md](docs/gui.md)**.

---

## Documentation

Built with MkDocs + Material theme. To serve locally:

```bash
pip install -e ".[docs]"
mkdocs serve              # http://127.0.0.1:8000
```

Pages of note:

- **[index](docs/index.md)** — headline-feature summary
- **[quickstart](docs/quickstart.md)** — first run with a guided example
- **[scope & limitations](docs/scope-and-limitations.md)** — what's
  validated, what's demonstrative, and what it's *not* qualified for
  (read this before trusting a number)
- **[tutorials/](docs/tutorials/)** — three narrative walkthroughs
- **[concepts](docs/concepts.md)** — runtime data flow end-to-end
- **[theory](docs/theory.md)** — governing equations + flux scheme +
  closures
- **[architecture](docs/architecture.md)** — module tree + design
  conventions
- **[closures](docs/closures.md)** — friction / heat / porous /
  catalyst / EOS protocols and ML-seam attachment points
- **[numerics](docs/numerics.md)** — implicit Newton, chord /
  Jacobian reuse, block-tridiag structure (incl. the JFNK
  removal story)
- **[implicit-solver-reference](docs/implicit-solver-reference.md)** —
  every `NewtonConfig` knob, decision table for picking the right path
- **[phase-iy-picard](docs/phase-iy-picard.md)** — outer Picard
  iteration on network coupling
- **[modes](docs/modes.md)** — FEM eigensolver for acoustic-mode
  analysis
- **[interop](docs/interop.md)** — Cantera YAML, CGNS, FMI,
  AVL Cruise M, GT-Suite
- **[gui](docs/gui.md)** — desktop visual editor walkthrough
- **[post-processing](docs/post-processing.md)** — IMEP /
  CA10/50/90 / SPL / dB(A) / order analysis / muffler TL /
  turbo overlay / energy balance / drive cycle
- **[examples](docs/examples.md)** — bundled cases by topic
- **[cli](docs/cli.md)** — full subcommand reference
- **[changelog](docs/changelog.md)** — milestone-by-milestone summary
- **[phase-log](docs/phase-log.md)** — detailed per-phase technical
  history (the historical record this README used to carry)
- **[perf-history](docs/perf-history.md)** — Phase Perf-1 → Perf-17
  before/after numbers and the empirical-vs-speculative pattern
- **[roadmap](docs/roadmap.md)** — what's planned next

The same Markdown content is reachable from the GUI (Help →
Documentation, Ctrl+H) — built-in viewer with a tree-of-contents
on the left and the formatted page on the right. Useful when you
want to skim the theory page while editing a case.

---

## Design notes

Hot kernels (Riemann flux, reconstruction, pipe residual, RK stages,
junction solve) are plain `@njit` functions operating on
struct-of-arrays — `jitclass` is deliberately avoided. Physics
closures are exposed as integer-kind-coded dispatch protocols so the
JIT can inline cheaply, and so learned surrogates can be attached
per-pipe without changes to the solver. All schema-driven closure
families register themselves at import time via
`aeolus1d.closures.registry.register_closure(...)` — the schema's
`Literal[...]` arms still need hand-maintenance, but a unit test
catches drift between the registry and the schema.

The full data flow — TOML → `Case` → live network → timeloop → HDF5
→ resume → GUI — is documented end-to-end in
**[docs/concepts.md](docs/concepts.md)**.

---

## Authors

- Niklas Nordin (`niklas.nordin@nequam.se`)
- Claude (Anthropic) — pair-programming assistant

---

## Community

- **[CONTRIBUTING.md](CONTRIBUTING.md)** — dev setup, local gates,
  branch + MR conventions, phase-workflow rule.
- **[SUPPORT.md](SUPPORT.md)** — how to get help and file a good bug
  report (best-effort, single maintainer, no SLA pre-1.0).
- **[MAINTAINERS.md](MAINTAINERS.md)** — who maintains aeolus1d, the
  bus-factor-1 reality, and how to become a second maintainer.
- **[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)** — Contributor Covenant
  v2.1; report concerns to `niklas.nordin@nequam.se`.
- **[SECURITY.md](SECURITY.md)** — responsible-disclosure policy
  for case-file parsers and the install path.
- **[CITATION.cff](CITATION.cff)** — how to cite aeolus1d in academic
  work (Zenodo DOI added at the first archived release).

---

## License

MIT.
