[[cylinder]] reference¶
The exhaustive field reference for the cylinder block and its sub-tables:
[cylinder.init], [cylinder.species_init], [[cylinder.port]],
[[cylinder.injector]], [cylinder.combustion] and [cylinder.fmep].
This is the [[cylinder]] counterpart of run-settings.md,
which covers the case-level [case] flags — including the three that govern
how a cylinder's ports exchange mass and energy with their runners
(cylinder_orifice_valve_flow, binary_valve_apply_cd,
valve_stagnation_recovery). Read that section too; the valve coupling is a
case-level decision, not a per-cylinder one.
For the physics behind these fields see theory §6, and for what the models do not cover see scope-and-limitations.
tests/unit/test_cylinder_reference_doc_coverage.py asserts that every field
of every spec below appears here, so a new knob fails CI until it is
documented.
[[cylinder]] — geometry and kinematics¶
| Field | Type | Default | Meaning |
|---|---|---|---|
id |
str |
required | Identifier. Used by controllers (cylinder_id), by the HDF5 writer for the per-cylinder group name, and by the GUI. |
bore |
float |
required | Cylinder bore \(D\) [m]. |
stroke |
float |
required | Piston stroke [m]. The crank radius is stroke / 2. |
con_rod_length |
float |
required | Connecting-rod length [m]. Enters the slider-crank displacement directly — it is not a ratio. |
clearance_volume |
float |
required | Volume at TDC [m³]. For a target compression ratio, V_disp / (CR - 1). |
omega_crank |
float |
0.0 |
Crank speed [rad/s]. Optional when the case carries a [crankshaft] block with rpm — the build path then derives every cylinder's ω from 2π·rpm/60. A per-cylinder value > 0 still wins (legacy override), and the build path warns when the two disagree. |
theta_init_deg |
float |
0.0 |
Crank angle at t = 0, in the user's CAD convention. This is how firing order is set on a multi-cylinder engine: an inline-4 firing 1-3-4-2 uses offsets 0 / 180 / 540 / 360. |
cad_at_tdc |
float |
0.0 |
Which CAD value corresponds to TDC. 0 (the historic default) means θ = 0 is TDC. Set e.g. 360 to declare that the user's θ = 0 is intake-stroke TDC and firing TDC sits at CAD 360. Read by the kinematics and by the GUI's "show time as CAD" readout. |
[crankshaft] — global engine speed and phasing¶
An engine has one crankshaft, so engine speed belongs to the case rather than
to each cylinder. This optional top-level block centralises it. It is a
singular [crankshaft] table, not a list.
| Field | Type | Default | Meaning |
|---|---|---|---|
rpm |
float |
0.0 |
Engine speed [rev/min]. The build path converts to ω = 2π·rpm/60 and assigns it to every cylinder's omega_crank. 0.0 means "leave each cylinder's own omega_crank alone", which is why adding this block to an existing case cannot change it. |
theta0_deg |
float |
0.0 |
Global crank-angle offset added on top of each cylinder's theta_init_deg. Re-phases the whole engine without editing every cylinder. |
firing_order |
list[str] |
[] |
Cylinder IDs in the order their power strokes fire. When set, the parser derives each named cylinder's theta_init_deg from the cumulative firing intervals — unless any named cylinder writes theta_init_deg itself, in which case nothing is derived and the case loads exactly as before. See Firing order and phasing. |
firing_interval_deg |
list[float] |
[] |
Crank-angle gap before each firing-order position, so entry 0 is always 0.0. Left empty it is materialised as [0, 720/n, …] (even fire, four-stroke). Write it explicitly for an odd-fire engine — a 90° V6 is [0, 90, 150, 90, 150, 90]. |
[crankshaft]
rpm = 7000.0
theta0_deg = 0.0
# firing_order = ["cyl1", "cyl5", "cyl3", "cyl6", "cyl2", "cyl4"]
A per-cylinder omega_crank > 0 still wins over rpm (legacy override), and
the build path warns when the two disagree rather than silently picking one.
Firing order and phasing¶
Set firing_order and the phases are derived for you:
[crankshaft]
rpm = 1500.0
firing_order = ["cyl1", "cyl5", "cyl3", "cyl6", "cyl2", "cyl4"]
gives 0, −120, −240, −360, −480, −600 — the cumulative-negative form the GT
importer uses. That is the same set of phases a hand-written file expresses
as 0, 600, 480, 360, 240, 120, because theta_init_deg enters a 720°-periodic
cycle (−120 ≡ 600). Either spelling runs identically.
For an odd-fire engine, supply the intervals:
firing_interval_deg = [0.0, 90.0, 150.0, 90.0, 150.0, 90.0] # 90° V6
Explicit phases always win. If any cylinder named in firing_order
writes its own theta_init_deg, nothing is derived and the case is
bit-identical to one without firing_order — validate notes that the field
is redundant for phasing there. This is deliberate: phases are relative, so one
cylinder is always the reference at 0.0, and a rule that treated "zero" as
"unset" would silently overwrite a hand-phased engine. The test for
"did the user set this?" is whether the key is present, not what its value is.
Errors you will get rather than silence: an ID in firing_order that matches no
cylinder, an interval list whose length differs from the firing order, a first
interval that is not 0.0, a negative interval, or intervals summing past 720°.
[cylinder.init] — initial charge state¶
Same body as a pipe's [pipe.init] (a UniformInit): set p plus one of
T / rho, optionally u. Omitted, the cylinder starts at 1 atm / 300 K.
[cylinder.species_init] — initial charge composition¶
A plain name = Y table of mass fractions, e.g.
[cylinder.species_init]
"N2" = 0.767
"O2" = 0.233
| Field | Type | Default | Meaning |
|---|---|---|---|
species_init |
dict[str, float] |
{} (empty) |
Initial mass fraction of each non-fuel species column. Empty means seed from the runners: on the first step the inventory is set from the port-average of the connected pipes' boundary composition, so the cylinder starts in equilibrium with them instead of writing a spurious Y = 0 step-down into them. Naming a species no connected pipe carries raises — a mass fraction that goes nowhere is a config error, not a no-op. The fuel column is not settable here; it is owned by m_fuel_per_cycle / volume.m_fuel. |
[[cylinder]] — combustion products¶
| Field | Type | Default | Meaning |
|---|---|---|---|
combustion_products |
bool |
True |
Convert burnt fuel into complete-combustion products in the cylinder's species inventory: O₂ consumed, CO₂ and H₂O produced, proportional to the fuel mass the combustion model reports burning. Only columns the case actually carries take part, so this is inert unless a connected pipe declares O₂ / CO₂ / H₂O. Default True is deliberate: with it off, a cylinder that tracks O₂ and burns fuel reports its intake oxygen level at the exhaust port forever. Set False only when the columns are meant as passive tracers. |
fuel_H_C_ratio |
float |
1.85 |
Fuel H/C atom ratio driving those yields, via CH_y + (1 + y/4) O₂ → CO₂ + (y/2) H₂O. 1.85 = diesel surrogate (stoichiometric AFR 14.54, matching the predictive diesel closure's 14.5); 2.25 ≈ gasoline (15.09); 4.0 = methane (17.20). The AFR is an output of this field, so it cannot disagree with the product split. See aeolus1d.chemistry.fuel_stoichiometry. |
[[cylinder]] — legacy single-injector fields¶
Ignored entirely when [[cylinder.injector]] is present. When
injectors is empty and m_fuel_per_cycle > 0, the build path migrates these
into a one-element injector list, so existing single-injector cases keep
working unchanged.
| Field | Type | Default | Meaning |
|---|---|---|---|
inject_cad |
list[float] |
[] |
Injection-rate abscissa in crank degrees. Cyclic over 720°. |
inject_rate |
list[float] |
[] |
Normalised rate shape against inject_cad. Dimensionless — only the shape matters; the runtime scales it so the cycle integral equals m_fuel_per_cycle exactly. |
m_fuel_per_cycle |
float |
0.0 |
Absolute fuel mass per 720° cycle [kg]. 0 (default) keeps the cylinder purely air-breathing. |
T_fuel |
float |
350.0 |
Temperature of the arriving fuel [K]. Sets the sensible energy ṁ·c_v·T added to the charge alongside the mass. |
cv_fuel |
float |
1700.0 |
Specific heat of the injected fuel [J/(kg·K)] — diesel-grade hydrocarbon. |
h_vap_fuel |
float |
0.0 |
Latent heat of vaporisation [J/kg]. > 0 turns on evaporative charge cooling: net injected energy becomes ṁ·(c_v·T_fuel − h_vap). Default 0 is bit-identical to no cooling. |
[[cylinder.injector]] — multi-injector (split injection, dual fuel)¶
A list; each entry is one injector. Present entries make the legacy fields above dead.
| Field | Type | Default | Meaning |
|---|---|---|---|
cad |
list[float] |
[] |
This injector's CAD abscissa. |
rate |
list[float] |
[] |
This injector's normalised rate shape. |
m_fuel_per_cycle |
float |
0.0 |
This injector's fuel mass per cycle [kg]. |
T_fuel |
float |
350.0 |
As above, per injector. |
cv_fuel |
float |
1700.0 |
As above, per injector. |
h_vap |
float |
0.0 |
As h_vap_fuel, per injector — this is where dual-fuel cases differentiate a volatile from a heavy fuel. |
fuel_species_name |
str |
"fuel" |
Species column this injector's fuel is routed to on connected pipes. A pipe participates when its primary species ([pipe.species] name) or one of its extras columns matches. "" disables routing for this injector. |
[[cylinder.port]] — valve ports¶
Two mutually exclusive valve modes. Binary (the default) means fully open
inside [open_deg, close_deg] modulo 720° and fully closed outside.
Continuous lift activates when both lift_cad and lift_y are populated,
giving an effective area Cd · A_port · lift(θ) / lift_max. The schedule is
cyclic on 720° because the kinematics wrap θ into [0°, 720°) before the
lookup.
| Field | Type | Default | Meaning |
|---|---|---|---|
pipe |
str |
required | Id of the runner pipe this port connects to. |
end |
"left" | "right" |
required | Which end of that pipe the cylinder sits at. |
A_port |
float |
required | Full-open port (curtain) area [m²]. |
open_deg |
float |
required | Valve-open crank angle [deg]. |
close_deg |
float |
required | Valve-close crank angle [deg]. May be numerically less than open_deg — the window wraps through 720°/0°, which is how overlap at TDC is expressed. |
lift_cad |
list[float] |
[] |
Lift-schedule abscissa [deg CAD]. Populating this and lift_y switches the port to continuous-lift mode. |
lift_y |
list[float] |
[] |
Lift values against lift_cad, in the same units as lift_max. |
lift_max |
float |
1.0 |
Lift value that corresponds to the full A_port. The area fraction is lift(θ)/lift_max, so lift_y can be given in mm with lift_max in mm. |
Cd |
float |
0.7 |
Discharge coefficient. Always applied on the lift-schedule branch. On a binary port it is applied only when the case sets binary_valve_apply_cd = true; the legacy default uses Cd = 1.0 there and so over-breathes by ~1/Cd. |
phase_offset_deg |
float |
0.0 |
VVT cam-phaser shift applied to both the open/close window and the lift lookup. Positive = advance (valve opens earlier). Mutable at runtime by a controller. |
lift_scale |
float |
1.0 |
Variable-lift multiplier on the final area fraction. 1.0 nominal, < 1.0 reduced lift (VTEC low-cam, AFM half-lift), 0.0 effective cylinder deactivation. Mutable at runtime. |
[cylinder.combustion] — heat release¶
Present ⇒ the cylinder burns; absent ⇒ purely air-breathing (the historical
default, and bit-equivalent to omitting the block). The block selects between
three models with its kind field: "wiebe" (default), "si_turbulent"
(predictive SI turbulent-flame entrainment) and "diesel_predictive". Only
the selected kind's parameters are read.
The Wiebe burnt-fraction profile is
with gas-side heat release Q̇ = m_fuel · LHV · dx_b/dθ · ω.
Because this block carries three models' parameter sets, its full field list
lives with the model that owns it — see theory §6.2
for Wiebe, and WiebeSpec in aeolus1d/io/schema.py for the
si_* and diesel_* parameter groups. The fields most cases touch:
| Field | Type | Default | Meaning |
|---|---|---|---|
theta_start_deg |
float |
— | Start of combustion [deg CAD]. |
duration_deg |
float |
— | Burn duration Δθ [deg]. |
a |
float |
— | Wiebe efficiency parameter. 5.0 burns ~99 % of the fuel within the window. |
m |
float |
— | Wiebe shape exponent. 2 is standard SI; CI runs higher. |
m_fuel |
float |
— | Fuel mass per cycle for the burn [kg]. Overridden at runtime by the actually-injected mass whenever the cylinder has an injection schedule; set it explicitly with injection off for a fixed-mass burn study. |
LHV |
float |
— | Lower heating value [J/kg]. Inherited from the case-level [[species]] entry of the same name when that is present. |
two_zone |
bool |
false |
Maintain a separate burnt-zone temperature for diagnostics. |
gamma_burnt, gamma_unburnt |
float |
unset | Per-zone γ. When both are set the volume's thermodynamic mapping uses γ_eff(x_burnt), which pulls the single-γ Wiebe peak-temperature artefact (~6000 K) toward the physical adiabatic-flame band (~2500 K). Note this affects the volume's E↔T mapping only; the port/HLLC machinery still uses the constant advection γ — see "Two γ's, and the seam between them" below. |
Two γ's, and the seam between them¶
Setting these gives the cylinder two γ's that must not be confused:
- the advection γ (
[gas] gamma, or the cylinder's own) — what the connected runner's HLLC and the orifice mass-flux relation use, and what keeps the neighbouring Riemann problem well-posed; γ_eff(x_burnt)— what the volume's own(E ↔ T, E → p)mapping uses.
Wherever a quantity crosses that seam it must be expressed in a form that does not presume a single γ. Two places where getting it wrong was silent:
- Enthalpy through the valve. The mass leaving carries
h = e + p/ρ, whichVolume.hcomputes as(E + pV)/m— exact underγ_eff, under the calorice = ∫c_v dTinverse and under a real-gas EOS alike. Building it ascp(γ_advection)·Tinstead under-removes1 − cp(γ_adv)/cp(γ_eff)of the outgoing energy (26 % at 1.4 against 1.27) without breaking global conservation, because the same flux is credited to the runner. The cylinder simply keeps energy the exhaust should have taken. x_burntis composition, not progress. It is the burnt-mass fraction of what is in the cylinder now, maintained as an inventory that survives the cycle wrap and dilutes across the intake stroke. The burn model'slast_xb— 0 before ignition, 1 after, reset every cycle — is a progress variable and feeds the inventory as a source. Assigning one to the other makesγ_effjump at the wrap, and with itp = (γ_eff−1)E/VandT.
Both are measured on the EPW diesel in
examples/validation/avl_epw_diesel_steady/README.md.
Three further crossings of the same seam were closed afterwards, each with the same signature — conserved, therefore silent:
- The valve's mass flux, not just its energy. The gas accelerating
through the throat belongs to whichever side is upstream, so the
isentropic-nozzle relations take that side's γ and R (
gamma_a/gamma_b,R_a/R_bonvalve_flow). Built from the neighbour's advection γ, a choked blowdown atγ_eff = 1.27against 1.4 passes 3.5 % too much mass; 2.3 % in late blowdown, 0.7 % through the exhaust stroke.Rcancels identically for a legacy volume — which definesT := p/(ρR)with that same R — and stops cancelling undercaloric_thermo, whereR_eff = R_mix(Y): +0.8 % alone, +4.3 % compounded with the γ error. - Reading the runner back. The runner's conserved state was encoded by
the pipe's γ — per cell when
per_cell_gammais on, wheregamma_padat the boundary slot is what the flux kernels themselves decode with. Decoding it with the cylinder's[gas] gammainflated the pressure the valve sees by 20–35 % on a burnt-gas runner, which then set the flow-direction test, the orifice pressure ratio, and — on inflow, where the cylinder passes noh0_a— the enthalpy carried in.examples/91_cylinder_species_transport.tomlis the bundled case where this was live; its documented mid-runner composition moved when it was fixed. - The two-zone burnt mass.
_update_two_zone_diagnosticssplit the charge withm_total · last_xb— progress again. The residual is burnt gas; booking it into the unburnt zone atT_uand then dividing the remainder by a too-smallm_binflatedT_burntby +537 K atx_b = 0.1(×18.6 on the Zeldovich NO rate) with the ~3.5 % residual the EPW diesel carries. Note the subtlety: the inventory is advanced inapply_fuel_step, which runs after the diagnostics, so a diagnostic must read_burnt_mass_now()— inventory plus the increment the current progress implies — and not the raw inventory, which is one step stale.
The 5 % gate that guards the two-zone division still reads last_xb, and
so does Woschni's motored reference: both ask a progress question ("has
enough burnt for this to mean anything"), which is the distinction the two
variables exist to keep apart.
To check the whole class at runtime on your own case, see
AEOLUS_PHYSICS_ASSERTS in troubleshooting.md.
[cylinder.fmep] — Chen-Flynn friction¶
Absent ⇒ friction is unmodelled: the per-cycle accumulator reports
FMEP_Pa = 0 and BMEP_Pa = IMEP.
| Field | Type | Default | Meaning |
|---|---|---|---|
A |
float |
75000.0 |
Constant term [Pa]. |
B |
float |
0.005 |
Peak-cylinder-pressure coefficient [-]. |
C |
float |
90.0 |
Mean-piston-speed coefficient [Pa·s/m]. |
D |
float |
1.2 |
Mean-piston-speed-squared coefficient [Pa·s²/m²]. |
Wall heat transfer (Woschni)¶
Flat [[cylinder]] fields. All-zero (the default) = adiabatic walls, the
legacy behaviour.
| Field | Type | Default | Meaning |
|---|---|---|---|
T_wall |
float |
0.0 |
Single lumped wall temperature [K]. > 0 constructs a WoschniHeatTransfer for the cylinder. |
T_head |
float |
0.0 |
Head-zone wall temperature [K]. |
T_liner |
float |
0.0 |
Liner-zone wall temperature [K]. |
T_piston |
float |
0.0 |
Piston-crown wall temperature [K]. Any of these three being > 0 splits Q̇ across head / liner / piston zones instead of one lumped wall. |
woschni_c1 |
float |
0.0 |
Woschni C₁ override. 0 = use the model default (2.28, Heywood eq. 12.30 for compression/expansion). GT-Suite cases often tune it down (~0.75) or up (~6.18 for gas exchange). |
woschni_c2 |
float |
0.0 |
Woschni combustion-turbulence coefficient [m/(s·K)]. 0 = the C₁-only single-term form (bit-exact); 3.24e-3 is the standard value, which lifts in-cylinder wall heat during combustion. |
Knock (Livengood-Wu + closed-loop retard)¶
Flat [[cylinder]] fields. The defaults are the Douaud-Eyzat values already on
the Cylinder dataclass, so a TOML without them is bit-exact.
| Field | Type | Default | Meaning |
|---|---|---|---|
ignition |
"auto" | "spark" | "compression" |
"auto" |
How this cylinder ignites, which decides the knock-controller defaults below. "auto" resolves at parse time: compression ratio ≥ 14 with a combustion kind other than si_turbulent means compression ignition. See Ignition mode and the knock controller. |
knock_A |
float |
17.68e-3 |
Ignition-delay coefficient [s·atm^n]. |
knock_n |
float |
1.7 |
Douaud-Eyzat pressure exponent. |
knock_B |
float |
3800.0 |
Arrhenius factor Eₐ/R [K]. |
knock_retard_deg_per_event |
float |
2.0 |
Spark retard applied each time knock fires [deg]. |
knock_retard_max_deg |
float |
10.0 |
Cap on accumulated retard [deg]. |
knock_advance_deg_per_cycle |
float |
0.5 |
Recovery per knock-free cycle [deg]. The knock integral accumulates only over the pre-ignition window [IVC, θ_start], so it fires on genuine pre-spark auto-ignition rather than every cycle. |
caloric_thermo |
bool |
true |
Use c_v(T, Y) from the tracked species' NASA-7 polynomials instead of a constant c_v from [gas] gamma. Default since 2026-08-06. See Caloric charge thermodynamics. |
ignition_auto_as |
str |
(observed) | Not a TOML key — what ignition = "auto" (or no ignition key) resolved to when the file was read, "" when the file named a mode. The GUI writer leaves such a mode out of a saved file, so the engine is re-classified from its geometry on the next load instead of being stamped spark / compression. Writing this key in a file does nothing. |
caloric_thermo_explicit |
bool |
(observed) | Not a TOML key — the loader sets it to whether the case file wrote caloric_thermo. A cylinder that asked for caloric thermodynamics but tracks no species gets a RuntimeWarning (the flag cannot engage); a case merely inheriting the true default stays on the silent composition-gated path. Writing this key in a file does nothing: explicitness is observed, not declared. |
Ignition mode and the knock controller¶
The knock criterion is a Livengood–Wu autoignition integral. In a spark-ignition engine, end-gas autoignition is knock, so retarding spark when the integral crosses 1.0 is the right control action.
A diesel burns by autoignition. Its integral crosses the threshold every
cycle by design, is read as knock, and retards until it saturates at
knock_retard_max_deg — and that retard is applied to every Wiebe stage, so
the whole burn executes late. Measured on the EPW HD diesel: all six cylinders
pinned at 10°, knock integrals 9–11.6 against a threshold of 1.0, recovery
(0.5°/cycle) far slower than re-trigger, costing peak firing pressure −18…−25 %.
So ignition decides the defaults:
ignition resolves to |
knock_retard_deg_per_event |
knock_retard_max_deg |
|---|---|---|
"spark" |
2.0 |
10.0 |
"compression" |
0.0 |
0.0 (controller off) |
Two things to know:
- Writing either knob keeps your value, on any engine. If you are
deliberately studying knock on a diesel, set
knock_retard_max_deg = 4.0and it stays 4.0. - A high-compression SI engine should say so. The
"auto"rule reads CR ≥ 14 as compression ignition. An Atkinson-cycle or high-compression NA petrol engine near that line should writeignition = "spark"explicitly.
aeolus1d validate still warns when a compression-ignition cylinder has an
active retard, so an explicit re-enable is visible rather than silent.
Caloric charge thermodynamics¶
Real γ is not constant. Air is 1.40 at 300 K but 1.34 by 900 K, and burnt gas
is ≈1.26 at 2200 K. With caloric_thermo = true (the default since
2026-08-06) the cylinder evaluates c_v(T, Y) from the tracked species'
NASA-7 polynomials, so γ responds to temperature, EGR and equivalence ratio on
its own. It needs a charge composition — either [cylinder.species_init] or
one seeded from the connected pipes — and every tracked species present in the
thermo DB.
With caloric_thermo = false the charge is calorically perfect: c_v is
fixed by [gas] gamma, and any variation has to be supplied by hand through
the combustion spec's gamma_burnt / gamma_unburnt, which blend on
burnt-mass fraction. Those two numbers are per-case calibration, and they are
inert under the default — Volume.gamma_eff returns c_p/c_v and never
reads them.
This changes the model, not a coefficient. Once c_v depends on
temperature, e = ∫₀ᵀ c_v dT′ ≠ c_v(T)·T, so the calorically-perfect identity
p = (γ−1)ρe no longer holds — it under-predicts pressure by ~3 % at intake
temperature and ~14 % at combustion temperature. Pressure is therefore taken
from ρRT, with T recovered by inverting the caloric energy, and γ becomes
the sound-speed / ratio-of-specific-heats diagnostic rather than the thing that
sets pressure. R stays constant, matching the pipes' constant-R / variable-γ
convention; a mixture's own R differs from 287.05 J/(kg·K) by only 0.4–1.6 %
across air and diesel products.
Why it became the default¶
It shipped opt-in because it moves gated engine metrics. It is now on by
default because the direction it moves them was measured. On the AVL EPW
diesel, three anchor operating points, mean absolute error in IMEP_High:
caloric_thermo = false |
caloric_thermo = true |
|
|---|---|---|
IMEP_High mean |err| |
16.0 % | 2.8 % |
The mechanism is a level error rather than a trend. The legacy
gamma_unburnt = 1.35 implies a c_v about 13 % too high for fresh air, which
under-predicts compression pressure at every operating point — so no
recalibration of the burn could have fixed it.
Two consequences worth planning for:
- Cost. Roughly 1.9× wall clock on a cylinder case; the temperature comes from a Newton inverse on every evaluation.
- Fitted wall closures need re-identifying. Anything calibrated against the
old charge model is now fitted to the wrong thing. The EPW case ships the
refit — exhaust-plenum
h·A850 → 550 W/K, rmsT_EXHerror 35.9 K → 7.1 K — and that sweep also confirmedh·Ais a clean lever:IMEP_High,PFPandQ_WHTcame back identical at everyh·Atried, so the refit absorbs no error from other channels.
Setting caloric_thermo = false restores the constant-c_v model unchanged.
The default is composition-gated¶
Turning the default on does not turn caloric thermodynamics on for a
cylinder that carries no composition. The NASA-7 table attaches lazily, once
the charge composition is resolved from [cylinder.species_init] or seeded
from the connected pipes. A species-less cylinder never switches and runs the
legacy constant-c_v path regardless of the flag.
This is why flipping the default moved no golden regression snapshot:
31_cylinder and 38_engine_turbo_scr are both species-less, so both are
genuinely unaffected. It is the safe direction — nothing changes silently —
but it does mean the default is conditional. If you want the caloric charge
model, give the cylinder a composition. Pinned by
tests/unit/test_caloric_default_is_composition_gated.py, which asserts the
spec says True and the built volume says False, so that "the snapshots
did not move" can never be confused with "the flag silently stopped working".
Structural fields¶
| Field | Type | Default | Meaning |
|---|---|---|---|
init |
table | 1 atm / 300 K | The [cylinder.init] sub-table above. |
ports |
list | [] |
The [[cylinder.port]] entries above. |
injectors |
list | [] |
The [[cylinder.injector]] entries above. |
combustion |
table | absent | absent | The [cylinder.combustion] sub-table above. |
fmep |
table | absent | absent | The [cylinder.fmep] sub-table above. |