#include "FluidSim/Scenario.hpp" #include "FluidSim/TwoStrokeScenario.hpp" #include "FluidSim/HelmholtzScenario.hpp" #include "FluidSim/PulseTestScenario.hpp" #include #include #include #include #include #include #include #include #include #include using namespace fluidsim::port; static void set_uniform_pipe_resolution(std::vector& pipes, int cells_per_meter) { if (cells_per_meter <= 0) throw std::invalid_argument("cells per meter must be positive"); for (auto& pipe : pipes) { const float length = std::accumulate(pipe.cell_lengths.begin(), pipe.cell_lengths.end(), 0.0f); const float damping = pipe.damping_multiplier; const float relaxation = pipe.energy_relaxation_rate; const float ambient = pipe.ambient_pressure; const int count = std::max(1, static_cast(std::ceil(length * cells_per_meter))); pipe = Pipe1D(pipe.area, length, count); pipe.damping_multiplier = damping; pipe.energy_relaxation_rate = relaxation; pipe.ambient_pressure = ambient; } } template void run(const char* name, S& s, int seconds, Control control) { double checksum = 0; std::uint64_t previous_iterations = 0; std::uint64_t previous_substeps = 0; for (int second = 0; second < seconds; ++second) { float peak = 0; const auto begin = std::chrono::steady_clock::now(); for (int i = 0; i < 44100; ++i) { control(second); auto value = s.process(); if constexpr (requires { value.left; }) { if (!std::isfinite(value.left) || !std::isfinite(value.right)) throw std::runtime_error("Nonfinite benchmark audio"); checksum += value.left; peak = std::max({peak, std::abs(value.left), std::abs(value.right)}); } else checksum += value; } const double elapsed = std::chrono::duration(std::chrono::steady_clock::now()-begin).count(); std::cout << name << ',' << second+1 << ',' << elapsed << ',' << checksum << std::endl; if constexpr (requires { s.pipes; }) { unsigned tiny = 0; std::uint64_t repairs=0, fallbacks=0; for(const auto& pipe:s.pipes) { repairs+=pipe.positivity_repairs; fallbacks+=pipe.hllc_fallbacks; } if(repairs) throw std::runtime_error("benchmark required pipe positivity repairs"); std::cerr< 3 ? argv[3] : "all"; const int uniform_v8_cells_per_meter = argc > 4 ? std::stoi(argv[4]) : 0; const auto selected_solver=argc>5 ? parse_pipe_solver(argv[5]) : PipeSolver::Rusanov; std::cerr << "solver=" << pipe_solver_name(selected_solver) << "\n"; auto pedal = [&](int second) { return mode == "idle" ? 0.0f : mode == "coast" ? (second >= 1 && second < 5 ? .7f : 0.f) : mode == "transient" && (second == 0 || second >= seconds-2) ? 0.0f : .7f; }; std::cout << "scenario,second,wall_seconds,checksum\n"; for (auto name : {"twin", "b205", "v8"}) { if (only != "all" && only != name) continue; auto s = std::make_unique(44100, std::string(name)=="v8" ? FourStrokePreset::AmericanV8 : std::string(name)=="b205" ? FourStrokePreset::SaabB205 : FourStrokePreset::Twin); if (std::string(name) == "v8" && uniform_v8_cells_per_meter > 0) { set_uniform_pipe_resolution(s->pipes, uniform_v8_cells_per_meter); std::cerr << "v8 uniform_cells_per_meter=" << uniform_v8_cells_per_meter << '\n'; } s->set_pipe_solver(selected_solver); s->throttle = .7f; run(name, *s, seconds, [&](int second) { s->throttle = pedal(second); if (mode == "loaded") s->crankshaft.omega = (std::string(name)=="v8" ? 4500 : 7000) * 2*pi/60; }); } for (bool racing : {true, false}) { if (only != "all" && only != (racing ? "two-stroke-racing" : "two-stroke-straight")) continue; TwoStrokeConfig config; config.exhaust = racing ? TwoStrokeExhaust::Racing : TwoStrokeExhaust::Straight; TwoStrokeScenario s(44100, config); s.set_pipe_solver(selected_solver); s.set_throttle(.7f); run(racing ? "two-stroke-racing" : "two-stroke-straight", s, seconds, [&](int second) { s.set_throttle(pedal(second)); if (mode == "loaded") s.crankshaft.omega = 10000 * 2*pi/60; }); } if (only == "all" || only == "helmholtz") { HelmholtzScenario helmholtz; run("helmholtz", helmholtz, seconds, [](int){}); } if (only == "all" || only == "pulse") { PulseTestScenario pulse; run("pulse", pulse, seconds, [](int){}); } }