Files
FluidSimCpp/tools/scenario_benchmark.cpp

98 lines
5.0 KiB
C++

#include "FluidSim/Scenario.hpp"
#include "FluidSim/TwoStrokeScenario.hpp"
#include "FluidSim/HelmholtzScenario.hpp"
#include "FluidSim/PulseTestScenario.hpp"
#include <chrono>
#include <iostream>
#include <limits>
#include <memory>
#include <string>
using namespace fluidsim::port;
template<class S, class Control> 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<double>(std::chrono::steady_clock::now()-begin).count();
std::cout << name << ',' << second+1 << ',' << elapsed << ',' << checksum << std::endl;
if constexpr (requires { s.pipes; }) {
unsigned tiny = 0;
for (const auto& pipe : s.pipes)
for (const auto& cell : pipe.cells)
for (float value : {cell.rho, cell.momentum, cell.energy, cell.air})
tiny += std::fpclassify(value) == FP_SUBNORMAL;
std::cerr << name << " second=" << second+1 << " subnormal_cell_fields=" << tiny << '\n';
}
if constexpr (requires { s.snapshot().boundary_iterations; }) {
const auto state = s.snapshot();
std::cerr << name << " iterations_per_frame="
<< (state.boundary_iterations-previous_iterations)/44100.0 << '\n';
std::size_t limiting_pipe = 0;
float stable_dt = std::numeric_limits<float>::max();
for (std::size_t p = 0; p < s.pipes.size(); ++p) {
const float candidate = s.pipes[p].stable_timestep();
if (candidate < stable_dt) { stable_dt = candidate; limiting_pipe = p; }
}
std::cerr << name << " substeps_per_frame=" << (state.solver_substeps-previous_substeps)/44100.0
<< " limiting_pipe=" << limiting_pipe << " cfl_us=" << stable_dt*1e6f << '\n';
previous_substeps = state.solver_substeps;
unsigned long long repairs = 0;
for (const auto& pipe : s.pipes) repairs += pipe.positivity_repairs;
std::cerr << name << " rpm=" << state.rpm << " power_kW=" << state.average_power/1000
<< " peak=" << peak << " pipe_repairs=" << repairs << '\n';
previous_iterations = state.boundary_iterations;
}
}
}
int main(int argc, char** argv) {
const int seconds = argc > 1 ? std::stoi(argv[1]) : 6;
const std::string mode = argc > 2 ? argv[2] : "pedal";
const std::string only = argc > 3 ? argv[3] : "all";
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", "turbo", "b205", "b205-gt17", "v8"}) {
if (only != "all" && only != name) continue;
auto s = std::make_unique<FourStrokeScenario>(44100,
std::string(name)=="v8" ? FourStrokePreset::AmericanV8 : std::string(name).starts_with("b205") ? FourStrokePreset::SaabB205 : FourStrokePreset::Twin,
std::string(name)=="turbo" || std::string(name)=="b205-gt17");
s->throttle = .7f;
run(name, *s, seconds, [&](int second) {
s->throttle = pedal(second);
if (mode == "loaded") s->crankshaft.omega = (std::string(name)=="v8" ? 4500 : std::string(name)=="b205-gt17" ? 5500 : 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_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){});
}
}