Ensim4
Engine Simulator 4, or ensim4, is a rewrite of ensim3 with emphasis on cache locality, and now includes SIMD based branchless 1D-CFD (Computational Fluid Dynamics) LF (Lax–Friedrichs) for audio generation.
Seen here, an inline 8 engine with modeled fuel injection, combustion, isentropic mass flow,
and two exhaust plenums, each output displacement vs. amplitude in their wave_0_pa and wave_1_pa
widgets to the right:
Having explored both HLLC and HLLE Riemann solvers (which promised better audio fidelity due to their better contact surface restoration characteristics) I found that the audio fidelity of a branchless Riemann-like Lax–Friedrichs solver is almost imperceptible from the Riemann powerhouses, especially for straight pipes where flow is almost guaranteed to be subsonic.
static struct wave_flux_s
calc_solver_flux(struct wave_prim_s ql, struct wave_prim_s qr)
{
struct wave_cons_s ul = prim_to_cons(ql);
struct wave_cons_s ur = prim_to_cons(qr);
double cl = sqrt(g_wave_gamma * ql.p / ql.r);
double cr = sqrt(g_wave_gamma * qr.p / qr.r);
struct wave_flux_s fl = { .r = ul.m, .m = ul.m * ql.u + ql.p, .e = (ul.e + ql.p) * ql.u };
struct wave_flux_s fr = { .r = ur.m, .m = ur.m * qr.u + qr.p, .e = (ur.e + qr.p) * qr.u };
double a = fmax(fabs(ql.u) + cl, fabs(qr.u) + cr);
/*
* 1 1
* FC = --- (FL + FR) - --- a * (UR - UL)
* 2 2
*/
return (struct wave_flux_s) {
.r = 0.5 * (fl.r + fr.r) - 0.5 * a * (ur.r - ul.r),
.m = 0.5 * (fl.m + fr.m) - 0.5 * a * (ur.m - ul.m),
.e = 0.5 * (fl.e + fr.e) - 0.5 * a * (ur.e - ul.e),
};
}
The branchless nature is SIMD friendly and readily auto-vectorizes, at least with clang.
For reference, consider the inline 8 from ensim2 which did not model exhaust pipe CFD, where simply the exhaust plenum’s chamber pressure was sampled: