#pragma once #include "FluidSim/Component.hpp" #include "FluidSim/Constants.hpp" #include #include #include namespace fluidsim::port { class Ecu; class Crankshaft { public: float omega{400.0f * 2.0f * pi / 60.0f}; float angle{}, previous_angle{}, inertia{0.2f}; float friction_constant{}, friction_viscous{}; float friction_quadratic{}; // Nm / (rad/s)^2, dissipative windage term float load_torque{}, drivetrain_torque{}; float average_power{}, average_torque{}; void add_torque(float torque); void step(float dt); private: float external_torque_{}; std::array power_buffer_{}, torque_buffer_{}; std::size_t power_index_{}, power_count_{}, torque_index_{}, torque_count_{}; float power_sum_{}, torque_sum_{}; }; class EngineCylinder : public Component { public: Ecu* ecu{}; // Optional ideal electronic fuel/ignition controller. float bore{0.075f}, stroke{0.068f}, rod{0.112f}, compression{11.5f}; float intake_valve_diameter{0.030f}, exhaust_valve_diameter{0.028f}; int intake_valve_count{1}, exhaust_valve_count{1}; float intake_valve_lift{0.005f}, exhaust_valve_lift{0.005f}; float phase{}; float spark_advance{20.0f}, wiebe_a{5.0f}, wiebe_m{2.0f}, burn_duration{60.0f}, burn_start{5.0f}; float afr{14.7f}, fuel_lhv{44.0e6f}, energy_variation{0.05f}, misfire_probability{}; float wall_area{0.02f}, heat_transfer{100.0f}, ambient_temperature{300.0f}; bool rev_limit_enabled{}; float rev_limit_rpm{7500.0f}, rev_limit_cut_time{0.05f}, rev_limit_hysteresis{200.0f}; float air_mass{}, exhaust_mass{}, energy{}, volume{}, pressure{101325.0f}; float fuel_mass{}, burn_fraction{}; bool combustion_active{}, fuel_injected{}, rev_limit_active{}; Port intake{}, exhaust{}; std::vector ports() override { return {&intake, &exhaust}; } float swept_volume() const; float clearance_volume() const; float total_mass() const; float temperature() const; float air_fraction() const; float compute_volume(float theta) const; void initialize(const Crankshaft& crank); virtual float valve_area(float cycle_degrees, bool inlet) const = 0; virtual void pre_step(Crankshaft& crank, float dt, float throttle) = 0; void update_state(float dt) override; protected: float energy_factor_{1.0f}; float rev_limit_timer_{}; std::mt19937 random_{0xF17D51u}; std::uniform_real_distribution unit_{0.0f, 1.0f}; float random_unit(); static float wrap_degrees(float value, float cycle_degrees = 720.0f); static bool crossed_angle(float previous, float current, float event) { return current >= previous ? previous < event && current >= event : event > previous || event <= current; } static bool crossed_window_close(float previous, float current, float open, float close); void update_rev_limiter(float rpm, float dt); void sync_ports(); }; class FourStrokeCylinder final : public EngineCylinder { public: float intake_open{340.0f}, intake_close{590.0f}, exhaust_open{130.0f}, exhaust_close{390.0f}; float valve_area(float cycle_degrees, bool inlet) const override; void pre_step(Crankshaft& crank, float dt, float throttle) override; }; } // namespace fluidsim::port