288 lines
14 KiB
Markdown
288 lines
14 KiB
Markdown
# Engine definitions
|
|
|
|
Each `engines/*.toml` file defines one engine and its default vehicle/load
|
|
calibration. Files are loaded once when the program starts. Add a file, give it
|
|
a unique `id`, then restart to see it in the browser. Editing a definition does
|
|
not require recompiling the engine simulator.
|
|
|
|
Start by copying a similar engine, such as `single-450.toml`, `five.toml`,
|
|
`v8-h.toml` or `yz-racing.toml`. Existing definitions contain their effective
|
|
calibration values, including the previous carb and idle tunes.
|
|
|
|
```toml
|
|
schema_version = 2
|
|
id = "my-single"
|
|
name = "My four-stroke single"
|
|
category = "Singles / four-stroke"
|
|
description = "My calibration"
|
|
order = 100
|
|
cycle = "four_stroke"
|
|
|
|
[simulation]
|
|
cells_per_meter = 20.0
|
|
```
|
|
|
|
`id`, `name`, `category`, `cycle` and `schema_version` are required. IDs use
|
|
lowercase letters, digits, `-` and `_`. `order` controls menu order; ties sort by
|
|
ID. Duplicate IDs, unknown keys, unsupported versions, incorrect types and
|
|
invalid physical values stop loading with a diagnostic identifying the file
|
|
and field. TOML syntax errors also report their location.
|
|
|
|
Version 2 requires explicit physical geometry. Version 1 remains readable and
|
|
receives a generated layout for compatibility. All bundled scenarios use version 2.
|
|
|
|
## Physical geometry
|
|
|
|
Pipe centerlines and storage boxes are shared by simulation, rendering, mouth
|
|
positions, and junction directions. Coordinates use millimetres, with Y up.
|
|
Pipe indices follow the expanded pipe-group ordering below.
|
|
|
|
```toml
|
|
[[geometry.pipes]]
|
|
index = 0
|
|
points_mm = [[-200.0, 300.0, 0.0], [-100.0, 300.0, 0.0], [-100.0, 200.0, 0.0]]
|
|
|
|
[[geometry.volumes]]
|
|
id = "intake_plenum"
|
|
size_mm = [200.0, 100.0, 50.0]
|
|
center_mm = [0.0, 200.0, 0.0]
|
|
|
|
[[geometry.cylinders]]
|
|
origin_mm = [0.0, 0.0, 0.0] # crank axis origin
|
|
tilt_deg = 0.0 # rotation about X
|
|
```
|
|
|
|
That pipe is 200 mm long: its length is the sum of the two straight segments.
|
|
There is no independent pipe-length field in version 2 definitions. Mesh cells
|
|
are distributed along the measured centerline; hard corners remain hard corners
|
|
in both views. The 3D tube uses bounded miter joins rather than a spline.
|
|
Paths require 2..64 finite points without consecutive duplicates.
|
|
|
|
The box above is 1 litre. Storage volumes support either `size_mm` with optional
|
|
`center_mm` (the center defaults to the origin), or two opposite corners:
|
|
|
|
```toml
|
|
[[geometry.volumes]]
|
|
id = "intake_plenum"
|
|
corners_mm = [[-100.0, 150.0, -25.0], [100.0, 250.0, 25.0]]
|
|
```
|
|
|
|
Use one form per box. Corner order can be reversed. Zero or negative sizes,
|
|
duplicate IDs, missing required volumes, or mismatched pipe/cylinder counts
|
|
are rejected. Four-stroke storage IDs are `intake_plenum`, `exhaust_collector`
|
|
for a volume collector, and `junction_0`, `junction_1`, etc. in topology order.
|
|
Two-strokes use `intake_plenum` and `crankcase`; the latter defines crankcase
|
|
clearance volume, and the renderer adds the live piston displacement to its box.
|
|
Only active fluid storage components are drawn as pressure-colored boxes.
|
|
|
|
Cylinder gas volume comes from bore, stroke, rod length, compression, and piston
|
|
position; cylinder placement supplies the crank origin and bank tilt. The 3D
|
|
cutaway uses these dimensions for sleeves, pistons, rods, heads, and valves.
|
|
All 32 bundled layouts have explicit cylinder placements and routed plumbing.
|
|
The four-stroke headers use direct swept bends and realistic collector placement,
|
|
with new lengths measured from their geometry rather than folded detours to
|
|
retain the old numerical lengths. This intentionally changes exhaust tuning.
|
|
See [header length changes](../verification/geometry-header-lengths.csv).
|
|
Intake, tailpipe, crossover and two-stroke profile lengths retain their existing
|
|
dimensions. Smooth manufactured bends are explicitly sampled centerlines;
|
|
authored hard corners still use miter joins.
|
|
|
|
Profiled two-stroke exhaust and silencers retain their sectional dimensions.
|
|
Their axial section proportions are mapped onto the measured centerline, so
|
|
changing the overall route scales section lengths while preserving diameters.
|
|
Silencer `diameter_mm` and `length_mm` define its cylindrical casing size;
|
|
straight-through gas uses the separate core diameter. Rendering shows the gas
|
|
profile and a cutaway casing. Bends do not add a new distributed bend-loss model.
|
|
The existing named engine topologies still define connections; arbitrary graph
|
|
editing is a future layer over this shared geometry.
|
|
|
|
## File discovery and distribution
|
|
|
|
The first available directory is used:
|
|
|
|
1. `FLUIDSIM_ENGINE_DIR`, when set. A missing override directory is an error.
|
|
2. `engines/` under the working directory.
|
|
3. `engines/` beside the executable on Windows and Linux.
|
|
4. The source checkout's `engines/` directory as a development fallback.
|
|
|
|
Definitions are cached for the process lifetime. The selected directory is
|
|
loaded as one catalog; it is not merged with the bundled files. In a repository
|
|
working directory, source definitions take priority over build copies.
|
|
|
|
CMake synchronizes the definitions into the build output when building an
|
|
executable. Distribute that `engines/` directory with the executable. The TOML
|
|
parser is vendored, so loading definitions needs no network connection.
|
|
|
|
```powershell
|
|
$env:FLUIDSIM_ENGINE_DIR = "C:\MyEngines"
|
|
.\fluidsim_desktop.exe --example my-single
|
|
```
|
|
|
|
## Units and sections
|
|
|
|
File keys state dimensional units. `_mm` is millimetres, `_litres` is litres,
|
|
`_cc` is cubic centimetres, `_deg` is crank degrees, `_s` is seconds, `_nm` is
|
|
newton metres and `_kg_m2` is rotational inertia. The loader converts lengths
|
|
and volumes to SI units. Openings and mixture/taper multipliers are normalized
|
|
fractions, not percentages: `0.03` means 3%.
|
|
|
|
Four-stroke definitions use these sections:
|
|
|
|
| Section | Contents |
|
|
| --- | --- |
|
|
| `[engine]` | Cylinder count, layout, bank angle, phases, throttle, startup, load, and exhaust topology |
|
|
| `[cylinder]` | Bore/stroke/rod, compression, valve diameters/counts, combustion and heat transfer |
|
|
| `[cam.intake]`, `[cam.exhaust]` | Cam lift curves as `[angle_deg, lift_mm]` points over a 720 degree cycle |
|
|
| `[valves.intake]`, `[valves.exhaust]` | Valve mass, spring, damping, seat preload, travel and contact restitution |
|
|
| `[valve_sound]` | Contact resonance and radiating area for four-stroke valve impacts |
|
|
| `[crankshaft]` | Inertia and mechanical friction |
|
|
| `[ecu]` | Idle, actuator response, ignition curve, limiter and overrun thresholds |
|
|
| `[vehicle]` | Vehicle mass, gears, clutch, wheel geometry and road resistance |
|
|
| `[fuel]` | `system = "injection"` or `"carburetor"`, jet diameters and taper/film calibration |
|
|
| `[junctions]` | Fitting volume and H crossover selection |
|
|
| `[silencers]` | Connecting pipes, resonator and muffler profiles, when used |
|
|
| `[[pipes]]` | Ordered pipe groups for the selected exhaust topology |
|
|
| `[[geometry.pipes]]`, `[[geometry.volumes]]`, `[[geometry.cylinders]]` | Physical routes, storage sizes and component placements |
|
|
|
|
`engine.phase_degrees` contains one phase per cylinder in cylinder index order,
|
|
within `[0,720)`. It records the actual crank offsets rather than a firing-order
|
|
label. `ecu.ignition_map` is an array of `[RPM, degrees BTDC]` pairs with strictly
|
|
increasing RPM. Negative advance means ignition after TDC.
|
|
|
|
Four-stroke valve lift is driven by the cam profile and a one dimensional
|
|
mass-spring model. Cam points must cover 0 through 720 degrees, have strictly
|
|
increasing angles, and start and end at zero lift. A valve can separate from
|
|
the cam at high RPM, producing valve float; seat, cam and travel-stop impacts
|
|
excite a damped contact mode spatialized from the engine through the existing
|
|
stereo, ear-distance and travel-delay paths. These profiles and dynamics apply
|
|
only to four-stroke cylinders. Valve masses are per valve; the model scales
|
|
effective mass, spring force and contact impulse by the configured valve count.
|
|
|
|
For a carbureted four-stroke, use `ecu.idle_control = "fixed"` and set
|
|
`ecu.idle_base_opening`. Its limiter and overrun control cut ignition while the
|
|
carburetor continues supplying fuel. Jet diameters in `[fuel]` are effective
|
|
diameters after the previous displacement and jet multipliers have been applied.
|
|
|
|
Two-stroke definitions use `[two_stroke]`, `[ecu]` and `[vehicle]`.
|
|
`two_stroke.idle_slide_opening` sets the mechanical stop;
|
|
`idle_spark_gain` and `idle_minimum_advance_deg` set its idle spark trim.
|
|
Two-stroke jet multipliers retain their existing meaning.
|
|
`chamber_sections_mm` contains six `[length, inlet diameter, outlet diameter]`
|
|
sections: header, two diffusers, belly, baffle cone and stinger.
|
|
|
|
Required cylinder geometry must be explicit. Optional calibration fields use
|
|
the typed config defaults; a misspelled field is always an error. Existing files
|
|
include explicit values so their behavior does not depend on future defaults.
|
|
|
|
## Scenario mesh resolution
|
|
|
|
Every bundled scenario defines a shared pipe density, for example:
|
|
|
|
```toml
|
|
[simulation]
|
|
cells_per_meter = 20.0
|
|
```
|
|
|
|
This applies to all intake, runner, header, tailpipe, silencer and two-stroke
|
|
profile sections. Uniform pipes use `max(1, ceil(length_m * cells_per_meter))`;
|
|
profiled pipes subdivide each geometry section at that maximum cell length.
|
|
Geometry and integrated pipe volumes are preserved. Tiny floating-point rounding
|
|
at exact integer counts is ignored. Legacy per-pipe `cells` and exhaust cell
|
|
length fields remain readable for older definitions without a global density;
|
|
when density is present it controls the mesh.
|
|
|
|
Valid density is 1..500 cells/m, with existing per-pipe and network cell caps
|
|
also enforced. Higher resolution reduces numerical diffusion and costs more CPU.
|
|
In the desktop sidebar, **Cells/m - / +** changes density in steps of five.
|
|
The mesh builds in the background and the engine restarts at its configured RPM;
|
|
solver, scene, listener and driving-control inputs are retained. Each scenario's
|
|
selection is remembered for the current app session. These controls do not write
|
|
TOML; edit `cells_per_meter` in the scenario file for a permanent default.
|
|
|
|
The desktop cam/valve, fuel and geometry editor has been removed. Engine
|
|
calibration remains available through TOML. Escape returns directly to the menu.
|
|
|
|
## Pipe groups and exhaust topologies
|
|
|
|
These definitions configure the existing topology builders. They do not yet
|
|
describe arbitrary graphs of cylinders and junctions. A group's `id` must be
|
|
unique; `count` repeats its dimensions across adjacent pipes.
|
|
|
|
```toml
|
|
[[pipes]]
|
|
id = "primaries"
|
|
count = 8
|
|
diameter_mm = 38.0
|
|
```
|
|
|
|
Each expanded pipe gets its own `geometry.pipes` centerline. A group's diameter
|
|
remains the cross-section geometry; its length is derived from that centerline.
|
|
|
|
The order is significant because the topology builder connects the expanded
|
|
groups to fixed cylinder and junction ports:
|
|
|
|
| `engine.topology` | Pipe order after expansion |
|
|
| --- | --- |
|
|
| `collector` | Intake, N runners, N primaries, one tailpipe |
|
|
| `staged` | Intake, N runners, N primaries, outlet pipe(s), secondary pipe(s) |
|
|
| `american_v8` | Intake, 8 runners, 8 primaries, 4 secondaries, 2 bank collectors, 2 tailpipes, H crossover |
|
|
|
|
The staged inline triple has one secondary; other staged layouts have two.
|
|
Staged V engines have two independent outlets. The American topology requires
|
|
eight cylinders in a V layout; setting `junctions.h_pipe = false` closes both
|
|
ends of the unused crossover and leaves the banks independent.
|
|
|
|
A collector outlet can instead use `profile = "silenced"`. Its dimensions and
|
|
mesh then come from `[silencers]`, `[silencers.resonator]` and
|
|
`[silencers.muffler]`; omit the group's uniform pipe dimensions. The loader
|
|
checks expanded pipe counts and mesh limits before construction. Pipe positions
|
|
and sound-source locations come from the explicit centerlines.
|
|
|
|
## Characteristic exhaust junctions
|
|
|
|
Existing files default to the finite-volume model. To opt into nonlinear,
|
|
zero-storage exhaust junctions, add:
|
|
|
|
```toml
|
|
[junctions]
|
|
model = "characteristic" # "volume" (default), "characteristic", or "legacy"
|
|
turning_loss = 0.5 # dimensionless, 0..10; 0 gives ideal angle-independent coupling
|
|
```
|
|
|
|
This applies to exhaust header fittings, staged collectors, and H-pipe nodes.
|
|
For `collector` topology it replaces the exhaust collector storage volume with
|
|
a zero-storage node, including the two-port single-cylinder transition. That
|
|
changes the physical model and removes collector compliance. Intake plenums,
|
|
cylinders, silencers, and intentional pipe volumes remain unchanged. The
|
|
storage dimensions come from the corresponding geometry boxes in version 2.
|
|
`junction_volume_cc` is retained only for legacy version 1 layout generation.
|
|
|
|
Branch directions point **outward from the node into the pipe**. By default they
|
|
come from the first/last segment of the physical pipe centerline. Optional
|
|
direction overrides remain available for measured fitting axes. For example:
|
|
|
|
```toml
|
|
[[junctions.branches]]
|
|
pipe = 2
|
|
left = false
|
|
direction = [-1.0, 0.0, 0.0]
|
|
|
|
[[junctions.branches]]
|
|
pipe = 3
|
|
left = true
|
|
direction = [1.0, 0.0, 0.0]
|
|
```
|
|
|
|
Directions are normalized, must be finite and nonzero, and must identify active
|
|
node endpoints in characteristic/legacy mode. Pipe indices follow expanded
|
|
scenario ordering, not TOML pipe-group indices. `left` is the endpoint at pipe
|
|
coordinate zero; it does not prescribe flow direction. Reversal is automatic.
|
|
Opposing outward axes describe straight-through flow; perpendicular axes describe
|
|
a 90-degree turn. A common world rotation leaves the solution unchanged.
|
|
|
|
For listening without editing TOML, desktop and capture accept
|
|
`--junctions characteristic --junction-loss 0.5`; use `--junctions volume` for
|
|
the original fittings. Restart the scenario between models. These switches
|
|
apply to four-stroke scenarios; two-stroke exhaust is currently a profiled pipe.
|
|
See [junction solver details and validation](JUNCTION_SOLVER.md).
|