#include "FluidSim/TwoStrokeScenario.hpp" #include #include using namespace fluidsim::port; namespace { void check(bool condition, const char* message) { if (!condition) throw std::runtime_error(message); } float reflected_pulse(bool expanding) { const float inlet = expanding ? .03f : .08f, outlet = expanding ? .08f : .03f; const std::array sections{{{.1f, inlet, inlet}, {.1f, inlet, outlet}, {.4f, outlet, outlet}}}; auto pipe = make_profiled_pipe(sections, .002f); const float rho = 1.2f, pressure = 101325, c = std::sqrt(fluidsim::port::gamma*pressure/rho); float x = 0; for (std::size_t i = 0; i < pipe.cells.size(); ++i) { const float center = x + .5f * pipe.cell_lengths[i]; const float dp = 500 * std::exp(-std::pow((center-.04f)/.006f, 2)); const float density = rho + dp/(c*c), velocity = dp/(rho*c); pipe.cells[i] = {density, density*velocity, (pressure+dp)/(fluidsim::port::gamma-1) + .5f*density*velocity*velocity, 1}; x += pipe.cell_lengths[i]; } pipe.left_ghost = pipe.right_ghost = {rho, 0, pressure, 1}; float extremum = 0; for (float time = 0; time < .0012f;) { const float dt = pipe.stable_timestep(.5f); pipe.advance(dt); time += dt; if (time > .00025f) { const float dp = pipe.state(29).pressure-pressure; extremum = expanding ? std::min(extremum, dp) : std::max(extremum, dp); } } check(pipe.positivity_repairs == 0, "taper pulse needed positivity repairs"); return extremum; } } int main() { const std::array cone{{{.4f, .03f, .10f}}}; const float expected = pi/12 * .4f * (.03f*.03f + .03f*.10f + .10f*.10f); for (float mesh : {.03f, .01f, .004f}) { const auto pipe = make_profiled_pipe(cone, mesh); double volume = 0; for (std::size_t i = 0; i < pipe.cells.size(); ++i) volume += pipe.cell_areas[i] * pipe.cell_lengths[i]; check(std::abs(volume-expected) < expected*2e-6, "frustum mesh changes physical volume"); } const float negative = reflected_pulse(true), positive = reflected_pulse(false); check(negative < -5, "diffuser did not return a rarefaction"); check(positive > 5, "convergent did not return a compression"); TwoStrokeScenario s; auto& cylinder = s.cylinder; const float closed = cylinder.compute_volume(cylinder.exhaust_close()*pi/180); check(std::abs(closed/cylinder.clearance_volume()-8.2f) < 1e-4f, "trapped compression was confused with geometric compression"); for (bool intake : {false, true}) { const float opens = intake ? cylinder.transfer_open() : cylinder.exhaust_open(); const float width = intake ? cylinder.transfer_width : cylinder.exhaust_width; const float area = pi*cylinder.bore*cylinder.bore*.25f; const float expected_area = width * (cylinder.compute_volume(pi)-cylinder.compute_volume(opens*pi/180))/area; check(std::abs(cylinder.valve_area(180, intake)-expected_area) < 1e-8f, "port is not a piston-uncovered window"); float previous = 0; for (float angle = opens; angle <= 180; angle += .5f) { const float current = cylinder.valve_area(angle, intake); check(current >= previous, "port does not open monotonically toward BDC"); check(std::abs(current-cylinder.valve_area(360-angle, intake)) < 1e-8f, "piston port area is not symmetric"); previous = current; } check(cylinder.valve_area(0, intake) == 0 && cylinder.valve_area(360, intake) == 0, "port is open at TDC"); } s.ecu.step(1.f/44100, 1, 6000); const float low_spark = s.ecu.spark_advance(); s.ecu.step(1.f/44100, 1, 17000); check(s.ecu.spark_advance() < low_spark && !s.ecu.fuel_cut(), "two-stroke high-speed ignition does not retard without cutting fuel"); s.vehicle.current_gear = 1; const float first = s.vehicle.total_gear_ratio(); s.vehicle.current_gear = 6; check(std::abs(first-30.27f)<.03f && std::abs(s.vehicle.total_gear_ratio()-13.4f)<.03f, "YZ125 primary/final/gear reduction incorrect"); Crankshaft crank; crank.omega = 100; crank.load_torque = 10; crank.friction_constant = 2; crank.add_torque(12); crank.step(1.f/44100); check(std::abs(crank.omega-100)<1e-6 && std::abs(crank.average_power-1000)<.01f && std::abs(crank.average_torque-10)<1e-6, "loaded steady-speed dyno lost shaft power"); crank.load_torque = 0; crank.friction_constant = 0; crank.friction_quadratic = .001f; crank.step(1.f/44100); check(crank.omega < 100 && crank.average_power < 1000, "windage is not dissipative"); std::cout << "Two-stroke physics passed: diffuser reflection=" << negative << " Pa, baffle reflection=" << positive << " Pa, first reduction=" << first << '\n'; }