Add turbocharged twin scenario and cross-platform test harness

This commit is contained in:
2026-10-23 02:27:03 +02:00
parent a859740a95
commit 589d22aa5e
63 changed files with 4903 additions and 771 deletions

213
README.md
View File

@@ -1,7 +1,16 @@
# FluidSimCpp
Native C++20 port of FluidSim. The C# application remains the reference while
the component models and behavior are being ported and checked against it.
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 (Windows / Visual Studio)
@@ -14,6 +23,28 @@ cmake --build build
ctest --test-dir build --output-on-failure
```
## Build (Linux / Windows presets)
The repository includes CMake presets and VS Code tasks for the desktop app.
Install CMake 3.28 or newer, a C++20 compiler, and Make on Linux. On Windows,
install Visual Studio 2022 with the C++ desktop workload and CMake. Configure
and build from the project root with:
```sh
cmake --preset linux
cmake --build --preset linux --target fluidsim_desktop
```
On Windows, use the `windows` preset in place of `linux`. CMake fetches SFML
3.0.2 if it is not installed, so the first desktop configure needs network
access. In VS Code, choose **FluidSim desktop** in Run and Debug; the
same configuration selects the Windows executable on Windows. The `.vscode`
build task configures and builds the desktop target before launching it.
The UI uses the optional Fira Code Nerd Font file from `../fonts` when present.
Without it, the simulation still runs but text labels are hidden. To bundle a
font from another location, configure with `-DFLUIDSIM_FONT_FILE=/path/to/font.ttf`.
The first configure fetches SFML 3.0.2 with CMake FetchContent for the desktop
window and audio stream. `-DFLUIDSIM_BUILD_DESKTOP=OFF` builds only the headless
runner and physics tests.
@@ -47,6 +78,111 @@ 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
512-frame batches with a 4096-frame startup reserve (about 93 ms). 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 source is a linear compact-monopole approximation,
`p(r,t) = (d mass_flow/dt)/(4*pi*r)`. Intake and exhaust have separate static
positions, fractional-sample propagation delays, inverse-distance spreading,
and left/right ear paths. 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.
The default environment is a nearby wall. Press **R** to cycle dry, field,
wall and woodland. Ground/wall image paths provide early reflections; the
woodland scatterers and decaying feedback-delay tail are approximate effects,
not validated outdoor acoustics. Synthetic mechanical noise remains optional.
The compact source does not implement pipe-end radiation impedance, validated
directivity, or exhaust afterfire chemistry. Filtering cannot recover aliased
source information. See [audio verification](verification/AUDIO_RENDERING.md)
for tests, capture commands, limitations and measured transient reductions.
Launching `fluidsim_desktop` without `--scenario` opens a small scenario menu.
Click the twin, YZ125 or Saab B205 card to start, or select with Up/Down and press Enter.
Escape closes the menu. Simulation and audio start after selection. Passing
`--scenario twin`, `--scenario two-stroke` or `--scenario b205` 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** for throttle, use **Up/Down** to adjust the persistent pedal request,
**Space** to restore real-time playback and **R** to change environment. 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.
## Twin with a VZ21-size turbo
Choose **600 cc twin + VZ21-size turbo** in the startup menu, or bypass it:
```sh
./build-desktop/fluidsim_desktop --scenario twin --turbo vz21 --scene field
./build-audit/fluidsim_cpp --scenario twin --turbo vz21 --seconds 5 --throttle 0.7
```
`Turbocharger` connects to four existing pipe ends and couples turbine and
compressor work through a rigid rotor. Small housing volumes preserve unsteady
exhaust pressure. Backpressure, charge temperature and spool speed emerge from
flow restrictions and energy exchange. A pressure-can/spring wastegate opens a
bypass; no boost pressure is imposed. HUD pressures are absolute. The default
unboosted twin is unchanged unless `--turbo vz21` is selected.
This is an estimated VZ21-size model, not a measured turbo map. Engine exhaust
pulses are implemented; blade-passing whistle/wakes are still pending. The
seven-second twin transient and all nine CTest suites passed. Sustained high-load
performance is close to, or slower than, real time in the current benchmark;
watch the underrun counter during testing. See
[turbo verification](verification/TURBO_VZ21.md) for assumptions and results.
## Saab B205 / 9-5 wagon 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. There is no turbo system yet;
this is an atmospheric-breathing B205 geometry baseline. Pipe/cam dimensions,
friction, idle/ignition maps, clutch, drag and loaded tire radius are estimates.
It does not reproduce factory turbo power or turbo/exhaust acoustics.
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
@@ -59,17 +195,16 @@ intake/exhaust radiation, outdoor feedback-delay reverb, optional mechanical
noise, 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.
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. The latest MSVC build completed, CTest passed, and the one-second
headless twin run completed with finite output. The desktop executable opened,
but its client area rendered blank in the captured run, so desktop rendering is
not yet verified.
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
@@ -81,3 +216,67 @@ 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.
## ECU and throttle control
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
80 ms first-order actuator response and a maximum travel rate of two full
travels per second. An ideal butterfly's projected opening determines orifice
area continuously; shaft obstruction and edge leakage are not modeled.
The ECU uses 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.
The 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 has no rev
limiter; its idle, overrun and sensor-fault controls remain active. Invalid sensor readings
inhibit fuel. A simple RPM-based ignition advance map is provided. An already
burning charge completes combustion when a cut begins.
All ECU parameters are accessible calibrations, not measured engine maps.
There is no starter motor, 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 doses fuel after the last scavenging port closes, targets
3000 RPM idle, and uses idle ignition trim in addition to throttle control. 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.
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.