374 lines
20 KiB
Markdown
374 lines
20 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 (Windows / Visual Studio)
|
|
|
|
Open a Visual Studio Developer PowerShell (or initialize the MSVC environment
|
|
with `VsDevCmd.bat`), then run from this directory:
|
|
|
|
```powershell
|
|
cmake -S . -B build -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release
|
|
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.
|
|
|
|
Run the interactive simulator with:
|
|
|
|
```powershell
|
|
.\build\fluidsim_desktop.exe
|
|
```
|
|
|
|
Controls: hold `W` for throttle, `C` for clutch, `A` / `D` to shift, `Z` for
|
|
neutral, up/down to adjust throttle target, left/right to adjust engine load,
|
|
`M` toggles mechanical engine noise, mouse wheel changes simulation playback
|
|
speed, and Space toggles real time.
|
|
|
|
The command-line runner is useful for repeatable checks:
|
|
|
|
```powershell
|
|
.\build\fluidsim_cpp.exe --seconds 1 --throttle 0.35
|
|
.\build\fluidsim_cpp.exe --scenario two-stroke --seconds 1 --throttle 0.3
|
|
.\build\fluidsim_cpp.exe --scenario helmholtz --seconds 1
|
|
.\build\fluidsim_cpp.exe --scenario pulse --seconds 1
|
|
```
|
|
|
|
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
|
|
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.
|
|
|
|
## 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.
|
|
|
|
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
|
|
```
|
|
|
|
## 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. The optimized
|
|
7,000-RPM workload takes about 0.65 wall seconds per simulated second on the audit
|
|
machine; actual playback headroom depends on the computer. See
|
|
[turbo verification](verification/TURBO_VZ21.md) for assumptions and results.
|
|
|
|
## Saab B205 / 9-5 wagon baseline
|
|
|
|
The Saab menu entry now includes an estimated GT17 turbo and ECU wastegate boost
|
|
control (up to 0.45 bar gauge). Launch with `--scenario b205 --turbo gt17`, or use
|
|
`--turbo none` for the naturally aspirated baseline. See [GT17 verification](verification/SAAB_GT17.md)
|
|
for controller behavior, assumptions and tests. Charge heating and backpressure
|
|
are simulated; an intercooler is not yet included.
|
|
|
|
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. The original atmospheric-breathing
|
|
geometry baseline remains available with `--turbo none`. Pipe/cam dimensions,
|
|
friction, idle/ignition maps, clutch, drag and loaded tire radius are estimates.
|
|
Turbo calibration and acoustics are approximate; factory power is not validated.
|
|
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, 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 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 enabled 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 request additional boost 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
|
|
80 ms first-order actuator response and a maximum travel rate of two full
|
|
travels per second. 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.
|
|
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 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.
|
|
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.
|