735 lines
41 KiB
Markdown
735 lines
41 KiB
Markdown
# FluidSimCpp
|
|
|
|
Solver performance measurements and reproducible SIMD/threading experiments are
|
|
documented in [the optimization audit](verification/OPTIMIZATION.md). Current
|
|
scenarios use serial pipe execution because per-substep threading costs more than
|
|
it saves at their graph sizes. Portable compiler SIMD remains enabled by default.
|
|
|
|
FluidSimCpp aims to reproduce sound from a physically accurate simulation of
|
|
compressible flow in pipes and connected volumes, sampled at 44,100 Hz. Each
|
|
audio sample advances physical time by 1/44100 second; adaptive internal
|
|
substeps resolve the fluid dynamics. Every subsystem must ultimately follow
|
|
physical laws with explicit units, assumptions and validation. Matching the
|
|
C# reference is a regression aid, not proof of physical accuracy.
|
|
|
|
This is a work in progress. Physical correctness is the acceptance goal, not
|
|
a claim about all current models. Validation must include conservation of
|
|
mass, momentum and energy, analytic acoustic modes, boundary reflection and
|
|
radiation, thermodynamics and convergence under time/space refinement.
|
|
|
|
## Build and run (Windows and Linux)
|
|
|
|
Both platforms use CMake 3.28 or newer, Git, and a C++20 compiler. The presets
|
|
build with optimizations and debugging symbols (`RelWithDebInfo`). The first
|
|
desktop configure fetches SFML 3.0.2 if it is not installed and needs network
|
|
access. Visual Studio is not required.
|
|
|
|
### Windows
|
|
|
|
Install CMake and Git and ensure both are on PATH. Then run from this folder
|
|
in a regular PowerShell terminal:
|
|
|
|
```powershell
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\Setup-Windows.ps1
|
|
cmake --preset windows
|
|
cmake --build --preset windows
|
|
ctest --preset windows
|
|
.\out\build\windows-gcc\fluidsim_desktop.exe
|
|
```
|
|
|
|
The setup script installs the portable [WinLibs](https://winlibs.com/) x64 UCRT
|
|
GCC bundle, including GDB and Ninja, in
|
|
`%LOCALAPPDATA%/FluidSimTools/mingw64`. It verifies the download's SHA-256
|
|
checksum, removes the downloaded archive, and reuses an existing installation.
|
|
It needs no administrator access and does not change your system PATH.
|
|
Each built executable gets its GCC runtime DLLs copied beside it so it also
|
|
runs directly from a terminal or Explorer.
|
|
|
|
### Linux
|
|
|
|
Install CMake 3.28 or newer, GCC with C++20 support, Make, GDB, and the SFML
|
|
development dependencies. For Ubuntu 24.04 / Debian with a suitable CMake:
|
|
|
|
```sh
|
|
sudo apt-get update
|
|
sudo apt-get install build-essential cmake git gdb pkg-config \
|
|
libx11-dev libxrandr-dev libxcursor-dev libxi-dev libudev-dev \
|
|
libgl1-mesa-dev libfreetype6-dev libflac-dev libogg-dev libvorbis-dev \
|
|
fonts-dejavu-core
|
|
cmake --preset linux
|
|
cmake --build --preset linux
|
|
ctest --preset linux
|
|
./build-desktop/fluidsim_desktop
|
|
```
|
|
|
|
Other distributions need equivalent packages; see
|
|
[SFML's source-build dependencies](https://www.sfml-dev.org/tutorials/3.0/getting-started/build-from-source/).
|
|
Running the desktop app requires a graphical session and an audio device.
|
|
|
|
### VS Code
|
|
|
|
Install the recommended **C/C++** and **CMake Tools** extensions and open this
|
|
folder. Select the `windows` or `linux` CMake preset for your platform. Choose
|
|
**FluidSim desktop**, **FluidSim two-stroke**, or **FluidSim Saab B205** in
|
|
**Run and Debug**, then press F5 or the green Run button. The launch
|
|
configurations select the platform's executable and GDB automatically; the
|
|
build task configures and builds the desktop target before launch.
|
|
Ctrl+F5 runs without debugging; Ctrl+Shift+B builds the desktop target only.
|
|
The command-line builds above also build the CLI, benchmarks, and tests.
|
|
|
|
The UI uses `../fonts/FiraCodeNerdFont-Medium.ttf` when available, then tries
|
|
installed Windows or Linux system fonts. To bundle another font, add
|
|
`-DFLUIDSIM_FONT_FILE=/path/to/font.ttf` to the configure command. For a
|
|
headless build, add `-DFLUIDSIM_BUILD_DESKTOP=OFF`; this skips SFML and the
|
|
desktop's graphics/audio dependencies.
|
|
|
|
Controls: W/S raise/lower the pedal over 0.5 seconds, C disengages the clutch,
|
|
V applies starter torque while held,
|
|
A/D shift, Z selects neutral, and left/right adjust engine load. The on-screen
|
|
pedal is direct and springs back on release; zero pedal retains ECU idle control.
|
|
Space toggles real-time and manual playback. In manual mode, Shift+scroll or
|
|
Page Up/Down changes speed; ordinary scrolling zooms the view. Rendering uses
|
|
display VSync independently of simulation speed, including 0.01x slow motion.
|
|
|
|
The command-line runner is useful for repeatable checks:
|
|
|
|
```powershell
|
|
.\out\build\windows-gcc\fluidsim_cpp.exe --seconds 1 --throttle 0.35
|
|
.\out\build\windows-gcc\fluidsim_cpp.exe --scenario two-stroke --seconds 1 --throttle 0.3
|
|
.\out\build\windows-gcc\fluidsim_cpp.exe --scenario helmholtz --seconds 1
|
|
.\out\build\windows-gcc\fluidsim_cpp.exe --scenario pulse --seconds 1
|
|
```
|
|
|
|
On Linux, use `./build-desktop/fluidsim_cpp` with the same arguments.
|
|
|
|
To compare deterministic checkpoints from the active C# twin scenario against
|
|
the C++ port (energy variation is disabled for repeatability), build the C++
|
|
trace runner and execute the PowerShell comparison script:
|
|
|
|
```powershell
|
|
cmake --build build-msvc --target fluidsim_parity_trace
|
|
.\verification\Compare-ScenarioTrace.ps1
|
|
```
|
|
|
|
## Audio fidelity and playback
|
|
|
|
Desktop playback starts at real time: one solver output frame per 44.1 kHz
|
|
stereo device frame. A simulation worker fills a bounded 8192-frame queue in
|
|
up to 128-frame batches with a 4096-frame startup reserve (about 93 ms). Batches
|
|
shrink with playback speed so 0.01x does not advance in large bursts. The device
|
|
callback only reads samples, removes DC, converts to PCM and handles underruns;
|
|
it performs no simulation, allocation or waiting. An underrun fades out and
|
|
recovery crossfades over 64 samples rather than stepping abruptly to silence.
|
|
The callback never advances physical time to conceal missing samples.
|
|
|
|
The primary source is a linear compact-monopole approximation,
|
|
`p(r,t) = rho_ambient * (d volume_flow/dt)/(4*pi*r)`. Mouth volume flow
|
|
comes from the boundary face velocity and area. Fluid conservation retains the
|
|
actual mass flux; hot/cold density changes are excluded from the acoustic source.
|
|
Intake and exhaust sources sit at the open ends of the same world routes used
|
|
to draw the pipes, including both outlets of dual exhausts. The desktop camera
|
|
is the listener: orbit, pan and zoom change its position and ear orientation.
|
|
Left and right ears are 18 cm apart, with separate fractional-sample travel
|
|
delays at 343 m/s and inverse-distance spreading. Camera updates smooth over
|
|
20 ms while retaining propagation history and reflection filter state.
|
|
Playback calibration is 200 Pa per device full scale;
|
|
this is not calibrated speaker SPL. A second-order 6 kHz monitoring filter
|
|
reduces sharp sample-to-sample transients without saturation or synthetic bangs.
|
|
A roughly 3.5 Hz device DC blocker and final PCM clipping protection remain.
|
|
Use 1x playback for acoustic evaluation. Toggle **G** off to hear the full
|
|
distance-related loudness change; automatic gain otherwise compensates for it.
|
|
|
|
The default environment is a nearby wall. Press **R** to cycle dry, field,
|
|
wall, woodland, and tunnel. Ground/wall image paths provide early reflections;
|
|
the live eight-line feedback-delay network supplies a scene-dependent late
|
|
tail. Early reflections follow the listener geometry, with plane reflections
|
|
disabled when the listener crosses to the other side. The diffuse tail receives
|
|
a delayed centre signal rather than summing the ears, avoiding ear-delay comb
|
|
filtering in the reverb input. Woodland scatterers and the hard-surface tunnel
|
|
images are approximate.
|
|
These are listening profiles, not validated acoustics. Mechanical sounds are
|
|
not implemented.
|
|
Open pipe mouths now use a causal unflanged radiation boundary with frequency-
|
|
dependent reflection and end correction. Intake mouths draw fresh ambient gas
|
|
directly, replacing the fixed atmosphere component and its inlet orifice.
|
|
The model assumes plane waves and low mean flow; strong jet and vortex effects
|
|
remain unmodeled. The compact source does not implement validated directivity
|
|
or exhaust afterfire chemistry. Filtering cannot recover aliased
|
|
source information. See [audio verification](verification/AUDIO_RENDERING.md)
|
|
for capture commands, current renderer details and limitations.
|
|
|
|
Use the **Cells/m - / +** control in the sidebar to change the active scenario's
|
|
pipe resolution. Changes rebuild the mesh in the background and restart the
|
|
engine, retaining solver, listener, scene and driving-control inputs. Values are
|
|
remembered separately per scenario until the app closes. Set persistent defaults
|
|
in each engine TOML's `[simulation] cells_per_meter`. The desktop engine editor
|
|
has been removed; edit engine calibration in TOML.
|
|
|
|
|
|
Launching `fluidsim_desktop` without `--scenario` opens a small scenario menu.
|
|
Choose the twin, YZ125, Saab B205, or American V8 card, or select with Up/Down
|
|
and press Enter.
|
|
Escape returns from the scenario to the menu, stopping its simulation and
|
|
audio so another scenario can be selected. Escape closes the menu at the category
|
|
screen. Simulation and audio start after selection. Passing
|
|
`--scenario twin`, `--scenario two-stroke`, `--scenario b205`, or `--scenario v8`
|
|
bypasses the menu; `--scene` and `--exhaust` options also apply
|
|
when choosing through the menu.
|
|
|
|
To test the two desktop engines from the repository root:
|
|
|
|
```sh
|
|
cmake --build build-desktop --target fluidsim_desktop -j 4
|
|
./build-desktop/fluidsim_desktop --scenario two-stroke --scene wall
|
|
# Or:
|
|
./build-desktop/fluidsim_desktop --scenario twin --scene field
|
|
```
|
|
|
|
Hold **W** to raise the pedal and **S** to lower it. Releasing the keyboard
|
|
returns the pedal over 0.5 seconds. The UI lever is direct and springs back. Use
|
|
**Space** to toggle real-time/manual playback and **R** to change environment.
|
|
In manual mode use **Shift+scroll** or **Page Up/Down** to adjust speed;
|
|
ordinary scrolling continues to zoom the view. The two-stroke desktop uses
|
|
the existing 125 cc configuration; its dimensions and calibrations must be
|
|
adapted before it represents a particular engine. Both scenarios use the same
|
|
motorcycle test model, dashboard and rolling peak dyno graph (kW and Nm versus
|
|
RPM). Start in neutral, hold **C** to disengage the clutch, press **D** for first
|
|
gear, apply throttle and release **C** to pull away. **D/A** shift up/down;
|
|
**Z** selects neutral. Left/right adjust an external engine load, not a wheel
|
|
brake. The graph records simulated crank power/torque, not measured wheel power.
|
|
|
|
The two-stroke now uses a YZ125 baseline: published 2022 primary, gearbox and
|
|
final reductions, 54 x 54.5 mm geometry, and an estimated racing expansion
|
|
chamber. Ports use piston-uncovered window areas. The pipe display follows
|
|
physical cell diameter and length. Compare exhausts with `--exhaust racing`
|
|
(default) or `--exhaust straight`; the desktop, CLI and WAV capture tool accept
|
|
this option. See [YZ125 verification](verification/YZ125_BASELINE.md) for the
|
|
geometry, assumptions, speed sweep and remaining unloaded-RPM limitation.
|
|
|
|
## 1980s American V8
|
|
|
|
Choose **1980s American 5.0 V8** in the desktop menu, or run:
|
|
|
|
```sh
|
|
./build-desktop/fluidsim_desktop --scenario v8 --scene field
|
|
```
|
|
|
|
This is an estimated naturally aspirated 302-class cross-plane V8: eight
|
|
101.6 x 76.2 mm cylinders, 7:1 compression, mild pushrod-style valve timing,
|
|
1-5-4-2-6-3-7-8 firing order, 750 RPM idle target and 7000 RPM limiter
|
|
(6750 RPM recovery). Crank/flywheel inertia is 1.1 kg m², versus the original
|
|
.28. Eight unequal .60--.78 m headers feed two independent 1.5-litre bank
|
|
collectors: cylinders 1--4 and 5--8. Each collector has its own 50 mm outlet
|
|
pipe (1.8 / 1.95 m), and each outlet has a separate spatial audio emitter. A
|
|
nominal 3000 kg five-speed car uses the existing clutch/gear controls.
|
|
The V8 clutch has 1100 Nm capacity to shorten launch slip with the heavy
|
|
flywheel and car; engagement remains progressive rather than forcibly locking.
|
|
Final drive is approximately 3.624:1 (6.16 / 1.7), making gearing 1.7x taller
|
|
than the previous setup: 1.7x road speed at the same RPM and selected gear.
|
|
Throttle opening uses a 65 ms response and 2.3 travels/second limit; closing
|
|
uses 160 ms and 1.4 travels/second for a slower release.
|
|
The V8 uses the shared ideal EFI fuel dose with ECU throttle/idle control,
|
|
ignition, overrun fuel cut and rev limiting. Its earlier carb setup has been
|
|
removed. Independent exhaust banks retain their uneven cross-plane pulse
|
|
spacing rather than blending both banks in a shared collector. Numerical
|
|
diffusion in the pipe solver and audio filtering still limit high-frequency rasp.
|
|
Dimensions and ignition/cam calibration are design estimates;
|
|
the output is not a validated period engine dyno curve.
|
|
|
|
The intake uses one 70 mm inlet duct, one 65 mm throttle body and a shared
|
|
4-litre plenum feeding eight 38 mm diameter, 12 cm runners (previously 30 cm).
|
|
The revised mesh has 19 pipes and 190 cells, down from 310. Runner cells are
|
|
6 cm long and exhaust cells approximately 5--6.5 cm long. This coarsening trades
|
|
some high-frequency resolution for more real-time headroom.
|
|
|
|
The earlier lift-off slowdown came from the CFL timestep falling below the
|
|
22.7 microsecond audio interval: the whole fluid network advanced almost twice
|
|
per sample, doubling boundary solves and cell updates. The revised 16-second
|
|
rev/coast benchmark stayed at one substep per sample and took .51--.57 wall
|
|
seconds per simulated second here, including coast-down (previously up to
|
|
1.28). It returned to 759 RPM with finite audio and zero pipe positivity
|
|
repairs. The CFL limit remains enforced; other operating points may still
|
|
require subdivisions. The desktop retains the 4x playback gain.
|
|
|
|
The V8 uses the same per-pipe resolution policy as other scenarios. Uniform
|
|
mesh benchmarks found that 35 cells/m was near real time under one pedal
|
|
workload and 33 cells/m was near real time at steady 4500 RPM on the tested
|
|
machine; denser uniform meshes ran slower than real time. The existing
|
|
per-pipe mesh is faster and remains in use. See [V8 performance notes](verification/V8_PIPE_PERFORMANCE.md)
|
|
for measurements and reproduction commands.
|
|
|
|
Benchmark or capture with:
|
|
|
|
```sh
|
|
./build-desktop/fluidsim_scenario_benchmark 8 transient v8
|
|
./build-desktop/fluidsim_scenario_benchmark 20 coast v8
|
|
./build-desktop/fluidsim_audio_capture --scenario v8 --seconds 6 --throttle .3 --out /tmp/v8
|
|
```
|
|
|
|
## Saab B205 / 9-5 wagon baseline
|
|
|
|
The Saab menu entry models the naturally aspirated B205 baseline.
|
|
|
|
Select **Saab B205 / 9-5 wagon** from the desktop menu, or launch directly:
|
|
|
|
```sh
|
|
./build-desktop/fluidsim_desktop --scenario b205 --scene field
|
|
./build-audit/fluidsim_cpp --scenario b205 --seconds 4 --throttle 0.5
|
|
```
|
|
|
|
The shared four-stroke scenario now supports two or four cylinders. The B205
|
|
uses 90 x 78 mm geometry, 8.8:1 compression, two intake/two exhaust valves per
|
|
cylinder and 1-3-4-2 firing order. Four intake runners and four exhaust headers
|
|
connect shared plenums and a tailpipe. The vehicle approximates a 9-5 wagon:
|
|
1675 kg with driver, five-speed manual, FM55 B05 overall ratios
|
|
13.70 / 7.12 / 4.77 / 3.62 / 2.67, and 4.05 final drive. No sixth gear is offered.
|
|
Controls and dyno match the other scenarios. Pipe/cam dimensions,
|
|
friction, idle/ignition maps, clutch, drag and loaded tire radius are estimates.
|
|
See [B205 verification](verification/B205_BASELINE.md) for sources and checks.
|
|
|
|
## Current port status
|
|
|
|
The C++ core now includes the shared `EngineCylinder` model with four-stroke and
|
|
two-stroke cycle implementations, crankshaft, crankcase, atmosphere and
|
|
configurable 0-D volume components (including thermal relaxation and moving
|
|
volume work), profiled compressible 1-D pipes and boundaries, adaptive solver
|
|
stepping, the impulse-clutch motorcycle/road-load model, and the active 600 cc
|
|
twin topology. Audio includes the flow-derivative source, spatialized
|
|
intake/exhaust radiation, outdoor feedback-delay reverb, a lock-free simulation
|
|
audio buffer, a `SoundEngine` lifecycle wrapper,
|
|
and an SFML desktop frontend. The shared 50-RPM-bin dyno history and graph are
|
|
also ported. A crankcase-scavenged 125 cc two-stroke scenario is available in
|
|
the core and desktop, with an ECU, finite-response throttle and dynamic reed.
|
|
|
|
This is still an in-progress port, not a claimed complete replacement. All four
|
|
current C# scenarios (600 cc twin, 125 cc two-stroke, Helmholtz resonator, and
|
|
pressure pulse) have headless C++ counterparts and can be selected by the CLI.
|
|
Remaining parity work includes completing the C# drawing/diagnostic controls in
|
|
the desktop UI and resolving numerical differences reported by the comparison
|
|
harness. Earlier Windows builds passed CTest, but the current audio and two-stroke
|
|
changes still need Windows runtime verification. The Linux desktop builds and
|
|
runs; installed-font fallbacks provide labels when no bundled font is present.
|
|
|
|
The one-second twin trace remains substantially outside its 1% tolerance. The
|
|
latest comparison's largest relative difference is 40.6745 for cylinder 1
|
|
pressure at checkpoint 33075 (69.8 kPa in C# versus 2.91 MPa in C++). A dense
|
|
intake trace matches closely at first, then the C# plenum pressure begins
|
|
rising at checkpoint 63 while C++ remains near ambient. This divergence needs
|
|
to be resolved before claiming numerical parity. CTest covers cylinder
|
|
geometry and valve timing, dyno-bin interpolation, configurable 0-D volume
|
|
thermodynamics, crankcase and atmosphere state, profiled-pipe geometry,
|
|
inertive and closed boundaries, gearing and clutch momentum transfer, finite
|
|
runs for every scenario, and finite audio/reverb output.
|
|
|
|
## Fuel and throttle control
|
|
|
|
Desktop playback has automatic gain disabled by default for every scenario.
|
|
Press **G** to toggle it; the dashboard shows its state. It tracks stereo RMS
|
|
and peaks after DC removal, lifting quiet idle audio gradually (1.5-second
|
|
gain-rise time constant) and reducing gain faster for loud revs (30 ms).
|
|
Extra gain is capped at 12x above the existing fixed 4x playback gain, with
|
|
linked stereo peak protection. Silence does not change the engine controls and
|
|
underruns freeze the detector. Disabling smoothly returns to fixed playback
|
|
gain. Simulation pressures and WAV capture levels are unaffected.
|
|
|
|
Both desktop engines use `Ecu` (`include/FluidSim/Ecu.hpp`). The desktop and
|
|
CLI throttle values are pedal requests; the actual throttle is advanced every
|
|
simulation sample, independently of rendering. Opening and closing use an
|
|
1 ms first-order throttle-body response capped at 1000 full travels per second.
|
|
The two-stroke carb slide uses 0.2 ms and 5000 full travels per second.
|
|
Only the keyboard pedal request ramps over 0.5 seconds; the UI slider is direct.
|
|
Physical fuel-film and mixture transport dynamics remain in the carburetor. The four-stroke uses an ideal butterfly projection; the
|
|
two-stroke uses the circular-segment aperture uncovered by a translating slide.
|
|
Shaft obstruction and edge leakage are not modeled.
|
|
|
|
The four-strokes use ideal sensors and direct access to trapped fresh-air mass
|
|
at intake-valve closure to calculate each cylinder's fuel dose. This explicitly
|
|
permitted idealization replaces injector flow/duty-cycle and fuel-film models.
|
|
Their nominal AFR is 14.7, enriched progressively toward 12.8 above 70% throttle.
|
|
A PI idle controller targets 1800 RPM with filtered RPM feedback, bounded idle
|
|
authority and anti-windup. Overrun fuel cut engages above 3500 RPM on a closed
|
|
pedal and restores fuel below 2400 RPM to catch idle. The twin's rev limiter cuts fuel
|
|
and spark at 10000 RPM, recovering below 9750 RPM.
|
|
|
|
The two-stroke uses a simulated 38.1 mm slide carburetor instead of the EFI
|
|
dose. Effective pilot, needle and main restrictions draw liquid fuel from a
|
|
finite float bowl with the orifice relation `m_dot = Cd*A*sqrt(2*rho*delta_p)`.
|
|
The jet pressure signal comes from simulated mass flow, local venturi dynamic
|
|
pressure and the measured static pressure drop. A wall film deposits and
|
|
evaporates fuel during transients. Delivered fuel is added downstream of the
|
|
carb as a conserved species and travels through the plenum, reed, crankcase,
|
|
transfer duct and cylinder. Combustion is therefore limited by the fuel and
|
|
oxygen actually trapped, rather than receiving a cylinder-side AFR dose.
|
|
|
|
The two-stroke has no rev limiter and deliberately does not use electronic
|
|
overrun fuel cut. On a high-RPM throttle lift, wet-wall and pilot fuel can pass
|
|
through unlit cycles if the trapped mixture cannot ignite. Spark remains at
|
|
the CDI timing; there is no scheduled overrun firing pattern. Unburned fuel
|
|
is transported out through the exhaust without pipe combustion. No sound
|
|
samples are mixed in.
|
|
|
|
All ECU parameters are accessible calibrations, not measured engine maps.
|
|
Holding V applies configurable constant starter torque at the crankshaft in every
|
|
scenario; release or loss of window focus disengages it. This is a torque source,
|
|
without battery, motor electrical, or starter gearing models. There is no
|
|
automatic restart, knock sensor, lambda sensor,
|
|
catalyst, cold-start temperature model or injector electrical model. Idle
|
|
control can still stall under excessive load and does not impose crankshaft
|
|
speed or add artificial sustaining torque. Existing combustion/thermal models
|
|
remain approximations; ECU changes do not establish their physical accuracy.
|
|
The two-stroke uses a fixed mechanical carb slide stop. With the pedal released,
|
|
the ECU trims ignition timing around the configured idle RPM instead of moving
|
|
the carb stop. Each carb scenario config sets
|
|
`TwoStrokeConfig::idle_slide_opening` (normalized 0..1); the catalog 125 cc
|
|
scenarios now use a calibrated 3% stop. Pedal demand above that stop opens the
|
|
slide further. The resulting idle RPM depends on the simulation and load. Its
|
|
reed is a damped lumped cantilever model with finite opening time and seating
|
|
contacts. Cylinder and crankcase moving-volume work use adiabatic energy changes.
|
|
Jet dimensions are effective flow calibrations rather than measured Mikuni
|
|
parts. Fuel uses the gas model after wall-film release; droplet size,
|
|
atomization, evaporation heat and oil are not modeled. Reed material,
|
|
combustion and controller parameters still require calibration.
|
|
C# trace parity is intentionally changed by these control and physics changes.
|
|
|
|
VS Code's default build task builds the desktop through CMake. Use
|
|
Ctrl+Shift+B to build, or choose **FluidSim desktop** in Run and Debug and
|
|
press F5 to build and run. The single-file C/C++ editor Run shortcut is disabled
|
|
because this project requires multiple sources and SFML; IntelliSense gets
|
|
include paths from CMake Tools. Open the repository root as the VS Code folder.
|
|
|
|
## Fluid physics verification and performance
|
|
|
|
See [the fluid physics audit](verification/FLUID_PHYSICS_AUDIT.md) for corrected
|
|
conservation/boundary equations, analytic tests, measured performance and
|
|
remaining model limits. The validation executable exercises 29 cases covering
|
|
walls, pressure outlets, nozzles, inertance, pipe joints, volumes, species,
|
|
acoustic modes, shocks and invalid states. The default coupled solver updates
|
|
components on every substep, uses area-weighted shared fluxes and matches
|
|
orifice discharge to duct characteristics. These changes intentionally alter
|
|
older C# trace comparisons.
|
|
|
|
Build headless with CMake, then run `ctest --test-dir build-audit
|
|
--output-on-failure`. `fluidsim_physics_benchmark` and
|
|
`fluidsim_thread_benchmark` reproduce the performance experiments.
|
|
`FLUIDSIM_ENABLE_SIMD=OFF` provides a scalar comparison; fast-math is not used.
|
|
Production fluid solving remains serial because threading was slower for the
|
|
current small pipe graph. SIMD and scalar Linux checks are covered; Windows
|
|
execution is still unverified.
|
|
|
|
The twin intake is supplied by an explicit 300 K atmospheric reservoir rather
|
|
than a pressure-only boundary with extrapolated entropy. The repeated-blip
|
|
`fluidsim_intake_regression` checks fresh-air supply and pressure/idle recovery.
|
|
Manifold vacuum at closed throttle remains part of the simulated pumping cycle.
|
|
|
|
## Pipe solver comparisons
|
|
|
|
The desktop accepts `--solver rusanov|hlle|hllc|muscl`. Press **F3** during playback to
|
|
cycle Rusanov, HLLE, HLLC and MUSCL; the dashboard shows the current solver. All pipes
|
|
switch together between simulation samples. Rusanov remains the startup default.
|
|
Changing solver affects the ongoing engine state; allow several cycles and the
|
|
audio queue to settle before judging it. Use the same scene and auto-gain setting.
|
|
|
|
Each method currently uses the same first-order spatial discretization and CFL
|
|
limit. HLLC resolves contact transport with less diffusion and uses HLLE when
|
|
its intermediate states are invalid. These are solver experiments; sharper
|
|
signals do not by themselves prove more accurate combustion or sound.
|
|
|
|
For repeatable comparisons, WAV capture accepts `--solver`, `--rpm` (an ideal
|
|
speed-holding dyno) and `--warmup` in seconds. Capture gains are identical across
|
|
solvers and do not include desktop automatic gain. For example:
|
|
|
|
```sh
|
|
./build-desktop/fluidsim_desktop --scenario v8 --scene dry --solver hllc
|
|
./build-desktop/fluidsim_desktop --scenario two-stroke --scene dry --solver hllc
|
|
./build-desktop/fluidsim_scenario_benchmark 8 loaded v8 0 hllc
|
|
./build-desktop/fluidsim_scenario_benchmark 8 loaded two-stroke-racing 0 hllc
|
|
./build-desktop/fluidsim_audio_capture --scenario v8 --solver hllc --scene dry --rpm 4500 --warmup 2 --seconds 6 --throttle .7 --out /tmp/v8-hllc
|
|
```
|
|
|
|
The benchmark's fourth argument is the existing optional uniform V8 cells/m
|
|
setting (0 keeps the scenario mesh); its fifth argument selects the pipe solver.
|
|
See [solver measurements](verification/SOLVER_COMPARISON.md) for results.
|
|
|
|
## Desktop workspace and 3D view
|
|
|
|
The desktop has a neutral black/gray workspace: a left telemetry and control
|
|
panel, an engine viewport, a dyno panel, and a cylinder inspector. Resize or
|
|
maximize the window to give the engine view more space. All desktop engine
|
|
presets use the OpenGL 3D renderer by default. Tab switches to the existing
|
|
schematic view. Left drag orbits, right/middle drag pans, the wheel zooms, and
|
|
Home fits the engine again. Camera gestures are confined to the engine viewport.
|
|
|
|
The spring-return pedal slider sends a pedal request to the ECU. W/S pedal
|
|
travel takes 0.5 seconds in either direction; releasing W returns toward zero
|
|
at the same speed. End releases the pedal immediately. Zero pedal lets the ECU
|
|
operate its idle throttle opening. Solver cycling is now F3; S lowers the pedal.
|
|
|
|
Pipes are continuous indexed tube meshes with smooth bends, physical diameters
|
|
from the simulated area profile, and per-vertex pressure colors. Route points
|
|
are keyed by distance along the simulated pipe, in metres. Moving those drawing
|
|
points changes the schematic route, not the fluid solver lengths. The fixed
|
|
pressure scale is blue at vacuum, green at 101325 Pa and red at/above 1 MPa.
|
|
Volumes and cylinder gas use the same pressure scale. Pistons and rods animate
|
|
from snapshot piston positions and crank phases. Geometry is schematic rather
|
|
than a dimensionally complete engine CAD model.
|
|
|
|
Code responsibilities are split into `DesktopUi`/`WorkspaceLayout`, `OrbitCamera`,
|
|
`PedalControl`, `PipePath`, `EngineRenderer3D`, `EngineSchematic` and `DynoRenderer`.
|
|
The renderer consumes snapshots and reuses meshes; it does not step the solver.
|
|
OpenGL is linked through CMake's `OpenGL::GL`. This first renderer uses indexed
|
|
client arrays, lighting and a depth buffer in an OpenGL compatibility context,
|
|
with SFML's GL state preservation around the viewport pass. It is not a core
|
|
profile shader renderer. Windows runtime still needs verification on Windows.
|
|
|
|
### Saab exhaust silencers
|
|
|
|
The B205 exhaust now includes a 6 L straight-through resonator and an 18 L
|
|
straight-through rear muffler, separated by 0.8 m of pipe and followed by a
|
|
radiating tailpipe. Saab uses two minimum fluid substeps per audio sample; the
|
|
acoustic CFL condition can request more. These are estimated large
|
|
passenger-car silencers, not measured Saab internals. `ExhaustConfig` and
|
|
`SilencerConfig` in `include/FluidSim/Silencer.hpp` configure length, nominal body
|
|
volume, core diameter, chamber count, baffle passage diameter, linear resistance
|
|
(1/s), and quadratic loss coefficient. Pass an `ExhaustConfig` as the third
|
|
`FourStrokeScenario` constructor argument; `enabled=false` bypasses the silencers.
|
|
|
|
Expansion and chambered silencers use resolved variable-area passages. Body
|
|
radius is derived from volume and length; tapered transitions reduce actual gas
|
|
volume below the nominal cylindrical body volume. Straight-through mode retains
|
|
a constant core through the housing and uses a linear resistance surrogate for
|
|
packing. It does not yet resolve perforation coupling or porous-material acoustics.
|
|
Loss coefficients are tuning inputs, not calibrated predictions from body geometry.
|
|
Momentum losses retain total gas energy, converting kinetic energy into heat.
|
|
The renderer draws round housings with their configured lengths and diameters.
|
|
|
|
### N-port junctions and V8 4-2-1 exhaust
|
|
|
|
`BoundarySystem::add_junction({{pipe_index, left_end}, ...})` connects any number
|
|
of pipe faces without a storage volume. A shared stationary Rusanov state is computed algebraically from all connected
|
|
faces. Integrated mass, total energy and species fluxes sum to zero at each node. Setup claims every endpoint;
|
|
updates allocate no memory. This is a dissipative one-dimensional numerical coupling, not a
|
|
geometry-resolving junction model. Momentum changes
|
|
are reacted by the fitting. Branch angle losses are not modeled. The separate
|
|
`Junction` class remains available for explicit finite-volume chambers.
|
|
|
|
Each V8 bank has a 4-2-1 header: four primaries feed two secondaries, then a
|
|
50 mm exhaust. Pairings are 1+2 / 3+4
|
|
and 5+7 / 6+8 with this simulation's bank numbering and firing order. Two T
|
|
junctions and a 780 mm x 38 mm tube form the H-pipe. Small configurable port-coupled junction volumes are used by the V8. The intake
|
|
plenum also remains a storage volume.
|
|
The TOML `primaries` pipe group configures diameter; measured
|
|
`geometry.pipes` centerlines configure length and position. Headers now use
|
|
direct swept bends with revised lengths, and the short/long variants have
|
|
distinct physical layouts. See [geometry definitions](docs/ENGINE_CONFIG.md).
|
|
The
|
|
`[junctions] h_pipe` enables the H crossover;
|
|
pass it as the fourth `FourStrokeScenario` constructor argument. The renderer
|
|
uses the same centerlines, cross-sections and active storage-volume dimensions
|
|
as the fluid system, with local pressure colors and mitered hard corners.
|
|
|
|
The previous header optimization measurements applied to the earlier finite-volume
|
|
4-1 layout and do not establish an optimum for this new 4-2-1 network.
|
|
|
|
### Example browser and small port-coupled junctions
|
|
|
|
The startup browser now scrolls with the mouse wheel, Up/Down, Page Up/Down,
|
|
Home and End. It adapts to the window size and shows a scroll indicator. Click
|
|
an example or press Enter to start it. The examples are defined in
|
|
`engines/*.toml`, loaded at startup and grouped into categories with selectable variants. These are configuration comparisons of
|
|
the existing engine models, not measurements of additional production engines.
|
|
|
|
The V8 currently uses port-coupled finite-volume fittings, configured by
|
|
`JunctionConfig::junction_volume` (default 50 cc per fitting). They use the
|
|
existing conservative component/port exchange and have no volume boxes in the
|
|
renderer. The earlier zero-storage Rusanov junction remains available for
|
|
comparison through `BoundarySystem::add_junction`, but is not used by the V8.
|
|
The 4-2-1 piping and the H crossover geometry remain the same.
|
|
|
|
### TOML engine definitions
|
|
|
|
Every engine is defined in its own file under `engines/`. Copy an existing
|
|
`.toml` file, change its `id`, name and calibration, then restart to load it.
|
|
No rebuild is required when editing definitions in the active directory.
|
|
Unknown keys, invalid dimensions, malformed timing and duplicate IDs produce
|
|
a file-specific startup error. See [the engine configuration guide](docs/ENGINE_CONFIG.md)
|
|
for units, directory discovery, sections and pipe topology ordering.
|
|
|
|
### Engine families and inspired builds
|
|
|
|
The browser opens at engine categories; click a category to see variants, then
|
|
click a variant or press Enter to start. Backspace, Left, Escape or the
|
|
`< Categories` header returns to categories. Both levels support wheel and
|
|
keyboard scrolling.
|
|
|
|
`EngineConfig` supplies cylinder count (up to 12), inline/V/boxer layout,
|
|
bank angle, bore/stroke/rod, compression, firing phases, rev limit and fueling.
|
|
The catalog includes four-stroke singles, parallel and V twins, inline 3/4/5/6,
|
|
V6/V8/V10/V12 and flat 4/6 examples. Inspired builds include F20C, Audi five,
|
|
RB26, Busso V6, EJ20, 911 flat-six, 916 V-twin and 250 V12 concepts. These use
|
|
estimated calibrations and simplified common-plenum/exhaust networks: they are
|
|
not factory replicas, measured torque curves, or turbocharged configurations.
|
|
|
|
The 8.0 L big-block variant transports carburetor fuel through its intake into
|
|
the cylinders. Its fixed idle stop is configured with `[ecu] idle_base_opening` in its TOML file.
|
|
It reuses the lumped carb model with scaled effective jets; multiple carb barrels,
|
|
floats and cylinder-specific fuel distribution are not resolved. Larger engine
|
|
geometry requires its own mixture and idle calibration.
|
|
|
|
Two-stroke families include 50, 125 and 500 cc singles. The 125 variants compare
|
|
standard, straight, long, short and wide chambers. The 50/500 concepts scale
|
|
bore/stroke, rod, crankcase clearance, port widths, flywheel inertia and chamber
|
|
geometry; they retain estimated carb and combustion calibrations. A different
|
|
chamber is a geometry experiment, not a guaranteed RPM or power improvement.
|
|
|
|
Launch a specific variant directly with `--example big-carb`, `--example flat6`,
|
|
`--example fifty` or another catalog ID. Carb jet scaling is configurable per
|
|
engine: four-strokes use effective jet diameters in `[fuel]`; two-strokes
|
|
use jet multipliers in `[two_stroke]`.
|
|
|
|
### Pulse-preserving custom exhausts
|
|
|
|
The custom inline three, F20 four, inline five and inline six examples use
|
|
unequal-length primaries, 50 cc junction collectors and secondary pipes rather
|
|
than a single large mixing volume. Their pipe damping matches the undamped Saab
|
|
path. Inline fours/fives/sixes use physical firing orders so collector grouping
|
|
preserves pulse timing; combustion remains even-fire.
|
|
|
|
The custom big-block V8 uses the American V8's cross-plane 1-5-4-2-6-3-7-8 firing
|
|
order and 4-2-1 headers with an H crossover, retaining its own displacement and
|
|
carburetor. V10/V12 examples retain separate banks through junction collectors,
|
|
secondary pipes and two radiating outlets, including both outlets in the spatial
|
|
audio renderer. The existing Saab, American V8, single and twin presets retain
|
|
their exhaust paths.
|
|
|
|
Capture any catalog entry at controlled RPM for comparison:
|
|
|
|
```powershell
|
|
./out/build/windows-gcc/fluidsim_audio_capture.exe --example v12 --rpm 3000 --warmup 0.6 --seconds 2 --throttle 0.55 --scene dry --out out/audio/v12
|
|
```
|
|
|
|
See [custom exhaust verification](verification/CUSTOM_EXHAUST_CHARACTER.md) for
|
|
fixed-RPM comparison results.
|
|
|
|
### Carburetor calibration
|
|
|
|
All seven two-stroke catalog variants and both carbureted four-strokes have
|
|
separate pilot, main-jet and needle-taper calibrations. Progressive pilot metering
|
|
fills the 5% throttle transition after lowering the 125 idle stops. The smaller four-stroke
|
|
single (`single-450`) now uses a 40 mm carburetor, transported fuel and milder
|
|
355/565-degree intake and 145/370-degree exhaust timing. The 650 cc single keeps
|
|
EFI. The big-block idle opening is 7%; the straight-pipe 125 uses 3%. The racing
|
|
125 and its chamber variants use the calibrated 3% mechanical slide stop.
|
|
|
|
Needle annulus area is interpolated continuously across slide positions; liquid
|
|
fuel still comes from the simulated jet pressure drop and float bowl. There is
|
|
no AFR feedback or cylinder-side fuel correction. The displayed carb AFR is the
|
|
uncapped ratio of filtered air flow to delivered fuel flow.
|
|
|
|
`fluidsim_carb_calibration --check` tests every carbureted catalog scenario at
|
|
idle, 5%, 10%, 15%, 25%, 50%, 75% and full pedal on an imposed-speed dyno, then
|
|
releases the dyno for an idle run. It fails on AFR outside the calibration
|
|
bounds, stalling, nonfinite RPM/AFR, pipe positivity repairs, a changed racing
|
|
125 stop, or loss of transported carb fuel in the 450 single. Run it through
|
|
`ctest --preset windows -R carb_calibration --output-on-failure`.
|
|
|
|
The tool also prints CSV diagnostics: run without arguments for the four main
|
|
operating points, or use `--sweep single-450` for a denser sweep of one scenario.
|
|
See [carb calibration results](verification/CARB_CALIBRATION.md) for operating
|
|
conditions, AFR values and the distinction between carb and recirculated charge.
|
|
|
|
### Source-separated audio diagnostics
|
|
|
|
The capture tool writes `-intake.wav`, `-exhaust.wav`, `-exhaust-2.wav`, and
|
|
`-valve-contact.wav` alongside the existing rendered, dry, and unfiltered mixes.
|
|
Source stems contain direct sound and early reflections, before the shared diffuse
|
|
reverb; `-tail.wav` contains that shared tail. With all sources enabled, the four
|
|
stems plus the tail reconstruct the rendered mix before WAV quantization/clipping.
|
|
Absent sources produce silent files. Peak, RMS, maximum sample delta, and clipping
|
|
counts are reported for every stem, in device full-scale units, without normalization.
|
|
|
|
Use `--solo intake`, `--solo exhaust`, `--solo exhaust-2`, or
|
|
`--solo valve-contact` to select a source in the output mix (default: `--solo all`).
|
|
All diagnostic stems remain active during soloing. These controls affect audio only;
|
|
they do not change the fluid simulation. Source controls are simulation-thread APIs.
|
|
|
|
```powershell
|
|
.\out\build\windows-gcc\fluidsim_audio_capture.exe --example single-450 --rpm 3000 --throttle .3 --warmup 1 --seconds 2 --scene dry --out out/audio/single-450-baseline
|
|
```
|
|
|
|
For intake-only listening, repeat with `--solo intake` and a different output prefix.
|
|
The shared tail can retain decay when controls change during playback.
|
|
|
|
Captures use 32-bit IEEE float WAV to preserve very quiet sources without clipping.
|
|
For audible, individually peak-normalized PCM16 listening copies, run:
|
|
|
|
```powershell
|
|
py tools/audio_preview.py out/audio/single-450-baseline
|
|
```
|
|
|
|
Previews appear in `out/audio/previews/`, with gain reported for each file.
|
|
Silent sources are skipped. Preview loudness does not represent relative source
|
|
strength; use the original captures and metrics for calibration.
|
|
|
|
### Acoustic pulse diagnostics
|
|
|
|
The `fluidsim_acoustic_benchmark` target compares straight connections, idealized
|
|
three-way junctions, and finite-volume fittings across meshes, timesteps, and
|
|
all three flux solvers. It exports pressure/velocity traces and acoustic energy
|
|
ratios, with conservation and numerical-repair diagnostics. See
|
|
[the benchmark guide and initial findings](docs/ACOUSTIC_BENCHMARK.md).
|
|
|
|
### Listening comparison: limited second-order pipes
|
|
|
|
Desktop auto gain now starts **off**. Press **G** to enable it if desired.
|
|
For a direct comparison on the current engine, keep throttle and master volume
|
|
fixed and press **F3** to cycle `rusanov ? hlle ? hllc ? muscl`.
|
|
The dashboard shows the selected solver. Rusanov remains the startup default.
|
|
To start directly in the new mode:
|
|
|
|
```powershell
|
|
.\out\build\windows-gcc\fluidsim_desktop.exe --example single-450 --solver muscl --scene dry
|
|
```
|
|
|
|
`muscl` uses minmod-limited primitive MUSCL-Hancock reconstruction with HLLC
|
|
fluxes on uniform pipe sections. Matching direct connections share reconstruction
|
|
stencils. Component boundaries and area/profile transitions retain first-order
|
|
coupling; nonphysical predicted edges fall back to cell averages, and HLLC keeps
|
|
its existing HLLE fallback. Reconstruction buffers are allocated at construction.
|
|
This is a listening candidate, not a calibrated real-engine sound model.
|
|
|
|
At 3000 RPM / 0.3 pedal, four-second dry captures are in
|
|
`out/audio/pipe-ab-rusanov-rendered.wav` and `out/audio/pipe-ab-muscl-rendered.wav`.
|
|
They preserve the actual level difference. New wave resolution can also change
|
|
cylinder filling, combustion, and torque. Restart in each solver for clean
|
|
comparisons; live switching retains the engine's current state.
|
|
|
|
For a comparison at equal RMS loudness, use
|
|
`out/audio/pipe-ab-rusanov-level-matched.wav` and
|
|
`out/audio/pipe-ab-muscl-level-matched.wav`. Both are PCM16 listening copies;
|
|
original float captures keep their physical output scale. Equal RMS does not
|
|
imply identical perceived loudness or identical engine state.
|
|
|
|
In one local loaded V8 check, MUSCL took 1.23 wall seconds per simulated second,
|
|
versus 0.78 for Rusanov. Large engines may underrun in real-time playback;
|
|
use the single-450 for the first listening comparison.
|
|
|
|
### Direction-aware characteristic junctions
|
|
|
|
A nonlinear zero-storage exhaust junction solver is now available separately
|
|
from the pipe solver. Start with Rusanov pipes to isolate the junction change:
|
|
|
|
```powershell
|
|
.\out\build\windows-gcc\fluidsim_desktop.exe --example v8-h --solver rusanov --junctions characteristic --junction-loss 0.5 --scene dry
|
|
```
|
|
|
|
Use `--junctions volume` to compare against the original volume fittings.
|
|
`--junctions legacy` selects the old dissipative zero-storage Rusanov node.
|
|
Default engine behavior still uses volume fittings. Direction losses use local
|
|
route tangents, with explicit TOML branch direction overrides supported. The
|
|
loss coefficient is an uncalibrated engineering parameter; zero disables angle
|
|
losses. Auto gain still defaults off.
|
|
|
|
The single-450 also supports these switches, but its two-port collector cannot
|
|
exercise multi-branch turning. Selecting a zero-storage node there removes its
|
|
collector-volume resonance. See [junction details](docs/JUNCTION_SOLVER.md).
|