104 lines
4.9 KiB
C++
104 lines
4.9 KiB
C++
#include "FluidSim/TwoStrokeScenario.hpp"
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
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<PipeSection, 3> 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<PipeSection, 1> 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';
|
|
}
|