Github Source

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: