137 lines
5.6 KiB
C++
137 lines
5.6 KiB
C++
#pragma once
|
|
|
|
#include "FluidSim/Component.hpp"
|
|
#include "FluidSim/Constants.hpp"
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <limits>
|
|
#include <optional>
|
|
#include <random>
|
|
#include <vector>
|
|
|
|
namespace fluidsim::port {
|
|
|
|
class Ecu;
|
|
|
|
struct CrankshaftConfig {
|
|
float inertia{0.2f};
|
|
float friction_constant{}, friction_viscous{};
|
|
float friction_quadratic{}; // Nm / (rad/s)^2, dissipative windage term
|
|
};
|
|
|
|
class Crankshaft : public CrankshaftConfig {
|
|
public:
|
|
float omega{400.0f * 2.0f * pi / 60.0f};
|
|
float angle{}, previous_angle{};
|
|
float load_torque{}, drivetrain_torque{};
|
|
float average_power{}, average_torque{};
|
|
|
|
void add_torque(float torque);
|
|
float take_torque(); // Consume gas torque when an external shaft integrator owns motion.
|
|
void record_output(float torque,float power);
|
|
void step(float dt);
|
|
private:
|
|
float external_torque_{};
|
|
std::array<float, 16384> power_buffer_{}, torque_buffer_{};
|
|
std::size_t power_index_{}, power_count_{}, torque_index_{}, torque_count_{};
|
|
float power_sum_{}, torque_sum_{};
|
|
};
|
|
|
|
struct CylinderConfig {
|
|
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 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};
|
|
};
|
|
|
|
class EngineCylinder : public Component, public CylinderConfig {
|
|
public:
|
|
Ecu* ecu{};
|
|
bool transported_fuel{};
|
|
float phase{};
|
|
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{};
|
|
float fuel_equivalent_mass{}; // passive fuel-origin tracer, unaffected by combustion
|
|
bool combustion_active{}, fuel_injected{}, rev_limit_active{};
|
|
Port intake{}, exhaust{};
|
|
|
|
std::vector<Port*> 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 active_fuel_mass_{};
|
|
float rev_limit_timer_{};
|
|
std::mt19937 random_{0xF17D51u};
|
|
std::uniform_real_distribution<float> 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;
|
|
}
|
|
void update_rev_limiter(float rpm, float dt);
|
|
void sync_ports();
|
|
};
|
|
|
|
class FourStrokeCylinder final : public EngineCylinder {
|
|
public:
|
|
struct IgnitionCommand {bool fire{},cut{true};};
|
|
// Present selects external timing exclusively. A fire pulse is consumed
|
|
// once; missing subsequent commands cannot recreate internal timed sparks.
|
|
// Requires physically transported fuel, including carburetor operation.
|
|
std::optional<IgnitionCommand> ignition_command;
|
|
// Once per cycle, before ignition consumes fuel. Independent of spark cut.
|
|
float charge_afr{std::numeric_limits<float>::quiet_NaN()};
|
|
bool charge_sampled{};
|
|
struct CamPoint { float angle_degrees{}, lift_metres{}; };
|
|
struct ValveDynamics {
|
|
float mass_kg{0.035f};
|
|
float spring_rate_n_per_m{30000.f};
|
|
float damping_n_s_per_m{24.f};
|
|
float seat_force_n{100.f};
|
|
float maximum_lift_m{0.015f};
|
|
float seat_restitution{0.005f}, cam_restitution{0.01f}, stop_restitution{0.01f};
|
|
};
|
|
struct CamProfile { std::vector<CamPoint> points; };
|
|
|
|
void set_valve_profiles(const CamProfile& intake_cam, const ValveDynamics& intake_valve,
|
|
const CamProfile& exhaust_cam, const ValveDynamics& exhaust_valve);
|
|
float valve_area(float cycle_degrees, bool inlet) const override;
|
|
void pre_step(Crankshaft& crank, float dt, float throttle) override;
|
|
float take_contact_impulse() { const float value=contact_impulse_;contact_impulse_=0;return value; }
|
|
bool valve_floating() const { return intake_floating_ || exhaust_floating_; }
|
|
float intake_lift() const { return intake_lift_; }
|
|
float exhaust_lift() const { return exhaust_lift_; }
|
|
static float cam_lift_at(const CamProfile& profile,float degrees) { return sample_cam(profile,degrees).lift_metres; }
|
|
|
|
private:
|
|
float external_burn_angle_{};
|
|
bool external_charge_ready_{};
|
|
struct ValveState { float lift{},velocity{};bool floating{}; };
|
|
const CamProfile* intake_cam_{};
|
|
const CamProfile* exhaust_cam_{};
|
|
ValveDynamics intake_dynamics_{},exhaust_dynamics_{};
|
|
ValveState intake_state_{},exhaust_state_{};
|
|
float intake_lift_{},exhaust_lift_{},contact_impulse_{};
|
|
bool intake_floating_{},exhaust_floating_{};
|
|
|
|
static CamPoint sample_cam(const CamProfile& profile,float degrees);
|
|
float advance_valve(ValveState& state,const CamProfile& cam,const ValveDynamics& dynamics,
|
|
bool inlet,const Crankshaft& crank,float dt);
|
|
};
|
|
} // namespace fluidsim::port
|