Skip to content

Instantly share code, notes, and snippets.

@LuizZak
Last active September 9, 2022 01:50
Show Gist options
  • Save LuizZak/e0f4f2df9c3c68fda3abc321a6247ff8 to your computer and use it in GitHub Desktop.
Save LuizZak/e0f4f2df9c3c68fda3abc321a6247ff8 to your computer and use it in GitHub Desktop.
RC221V
import "engine_sim.mr"
// Honda RC211V 75.5° V5 - used in MotoGP from ~2001-2004
// Reference for some of the numbers in the design: https://patents.google.com/patent/US6745730B2/en
// Potential firing order referenced from: https://youtu.be/U5_wWC2cURU?t=260
/*
- Running engine:
- main.mr:
set_engine(
RC211V(
timings: v5_75_2004_timings() // or v5_75_2001_timings()
)
)
set_vehicle(
RC211V_vehicle()
)
set_transmission(
RC211V_transmission()
)
- Or:
RC211V_main()
- Changing timings/firing order on RC211V_main (defaults to v5_75_2004_timings):
- main.mr:
RC211V_main(
timings: v5_75_2001_timings()
)
*/
units units()
constants constants()
impulse_response_library ir_lib()
label cycle(2 * 360 * units.deg)
label bank_separation(77.5 * units.deg)
label rod_journal_separation(104.5 * units.deg)
label cylinder_volume(197.89 * units.cc)
label compression_ratio(1.0 / 13.0)
// Timings
// Approximate potential ignition timings (in 720° cycle percentage / °degrees):
// 3-cylinder bank (bank #1) (odd cylinders):
// 0% (0°)
// 50% (360°)
// 3-cylinder bank (center cylinder):
// 14.51% (104.5°)
// 64.51% (464.5°)
// 2-cylinder bank (bank #2):
// 39.51% (284.5°)
// 89.51% (644.5°)
// Defines timings of piston firing on a V5 engine based on the TDC of the front-most piston (first piston on 3-cylinder bank)
// Timings are in degrees, adding up to 720° (4-strokes of 180° each).
// Timings are also assumed to be the TDC point of the respective cylinder, as timing of the camshaft is also computed from the
// pistons' ignition timings.
public node v5_75_timings {
// A textual label appended to the engine's name for context.
input label;
// Top dead center of the first piston (forward cylinder in 3-cylinder bank)
input tdc_crankshaft: bank_separation / 2.0;
input piston1_ignition; // Bank 1 piston 1
input piston2_ignition; // Bank 2 piston 1
input piston3_ignition; // Bank 2 piston 2 (off by 104°)
input piston4_ignition; // Bank 1 piston 2
input piston5_ignition; // Bank 2 piston 3
}
// Approximates the timings of the initial Honda RC211V v5 from 2001
// Source: https://en.wikipedia.org/wiki/Big-bang_firing_order#Five-cylinder_engines
public node v5_75_2001_timings {
alias output __out:
v5_75_timings(
label: "2001 ignition",
piston1_ignition: 0.0 * units.deg,
piston2_ignition: 284.5 * units.deg,
piston3_ignition: 464.5 * units.deg,
piston4_ignition: 644.5 * units.deg,
piston5_ignition: 360.0 * units.deg
);
}
// Approximates the timings of the initial Honda RC211V v5 from 2004
// Source: https://en.wikipedia.org/wiki/Big-bang_firing_order#Five-cylinder_engines
public node v5_75_2004_timings {
alias output __out:
v5_75_timings(
label: "2004 ignition",
piston1_ignition: 0.0 * units.deg,
piston2_ignition: 284.5 * units.deg,
piston3_ignition: 464.5 * units.deg,
piston4_ignition: 284.5 * units.deg,
piston5_ignition: 0.0 * units.deg
);
}
// Head
private node add_flow_sample {
input lift;
input flow;
input this;
alias output __out: this;
this.add_sample(lift * units.thou, k_28inH2O(flow))
}
// This cylinder head is a work of plugging numbers and seeing what works
public node v5_75_head {
input intake_camshaft;
input exhaust_camshaft;
input chamber_volume: cylinder_volume * compression_ratio;
input flip_display: false;
alias output __out: head;
input flow_attenuation: 1.7;
input lift_scale: 1.0;
function intake_flow(50 * units.thou)
intake_flow
.add_flow_sample(0 * lift_scale, 0 * flow_attenuation)
.add_flow_sample(160 * lift_scale, 135 * flow_attenuation)
.add_flow_sample(210 * lift_scale, 186 * flow_attenuation)
.add_flow_sample(260 * lift_scale, 210 * flow_attenuation)
.add_flow_sample(310 * lift_scale, 256 * flow_attenuation)
.add_flow_sample(360 * lift_scale, 285 * flow_attenuation)
.add_flow_sample(410 * lift_scale, 322 * flow_attenuation)
.add_flow_sample(460 * lift_scale, 330 * flow_attenuation)
.add_flow_sample(510 * lift_scale, 365 * flow_attenuation)
.add_flow_sample(560 * lift_scale, 385 * flow_attenuation)
.add_flow_sample(610 * lift_scale, 404 * flow_attenuation)
.add_flow_sample(660 * lift_scale, 410 * flow_attenuation)
.add_flow_sample(710 * lift_scale, 424 * flow_attenuation)
.add_flow_sample(760 * lift_scale, 424 * flow_attenuation)
.add_flow_sample(810 * lift_scale, 424 * flow_attenuation)
function exhaust_flow(50 * units.thou)
exhaust_flow
.add_flow_sample(0 * lift_scale, 0 * flow_attenuation)
.add_flow_sample(160 * lift_scale, 135 * flow_attenuation)
.add_flow_sample(210 * lift_scale, 180 * flow_attenuation)
.add_flow_sample(260 * lift_scale, 210 * flow_attenuation)
.add_flow_sample(310 * lift_scale, 242 * flow_attenuation)
.add_flow_sample(360 * lift_scale, 250 * flow_attenuation)
.add_flow_sample(410 * lift_scale, 266 * flow_attenuation)
.add_flow_sample(460 * lift_scale, 280 * flow_attenuation)
.add_flow_sample(510 * lift_scale, 291 * flow_attenuation)
.add_flow_sample(560 * lift_scale, 301 * flow_attenuation)
.add_flow_sample(610 * lift_scale, 317 * flow_attenuation)
.add_flow_sample(660 * lift_scale, 324 * flow_attenuation)
.add_flow_sample(710 * lift_scale, 338 * flow_attenuation)
.add_flow_sample(760 * lift_scale, 338 * flow_attenuation)
.add_flow_sample(810 * lift_scale, 338 * flow_attenuation)
cylinder_head head(
chamber_volume: chamber_volume,
intake_runner_volume: 900.00 * units.cc,
intake_runner_cross_section_area: 7.00 * units.cm2,
intake_port_flow: intake_flow,
exhaust_port_flow: exhaust_flow,
intake_camshaft: intake_camshaft,
exhaust_camshaft: exhaust_camshaft,
flip_display: flip_display
)
}
// Distributor / wiring
private node wires {
output wire1: ignition_wire();
output wire2: ignition_wire();
output wire3: ignition_wire();
output wire4: ignition_wire();
output wire5: ignition_wire();
}
label ignition_offset(-5.0 * units.deg)
private node v5_75_distributor {
input wires;
input timing_curve;
input rev_limit;
input timings;
alias output __out:
ignition_module(timing_curve: timing_curve, rev_limit: rev_limit)
.connect_wire(wires.wire1, timings.piston1_ignition + ignition_offset)
.connect_wire(wires.wire2, timings.piston2_ignition + ignition_offset)
.connect_wire(wires.wire3, timings.piston3_ignition + ignition_offset)
.connect_wire(wires.wire4, timings.piston4_ignition + ignition_offset)
.connect_wire(wires.wire5, timings.piston5_ignition + ignition_offset);
}
// Camshaft
private node v5_75_camshaft_builder {
input intake_lobe_profile;
input exhaust_lobe_profile;
input base_radius;
input lobe_separation: 114.0 * units.deg;
input intake_lobe_center: lobe_separation;
input exhaust_lobe_center: lobe_separation;
input advance: 0.0 * units.deg;
input timings;
output intake_cam_0: _intake_cam_0;
output intake_cam_1: _intake_cam_1;
output exhaust_cam_0: _exhaust_cam_0;
output exhaust_cam_1: _exhaust_cam_1;
camshaft_parameters params(
advance: advance,
base_radius: base_radius
)
camshaft _intake_cam_0(params, lobe_profile: intake_lobe_profile)
camshaft _intake_cam_1(params, lobe_profile: intake_lobe_profile)
camshaft _exhaust_cam_0(params, lobe_profile: exhaust_lobe_profile)
camshaft _exhaust_cam_1(params, lobe_profile: exhaust_lobe_profile)
label rot360(360 * units.deg)
label intake_after_ignition(rot360 - 20 * units.deg)
label exhaust_before_ignition(-rot360 - 10 * units.deg)
label camshaft_offset(0.0 * units.deg)
_intake_cam_0
.add_lobe(timings.piston1_ignition + intake_after_ignition + intake_lobe_center + camshaft_offset)
.add_lobe(timings.piston3_ignition + intake_after_ignition + intake_lobe_center + camshaft_offset)
.add_lobe(timings.piston5_ignition + intake_after_ignition + intake_lobe_center + camshaft_offset)
_exhaust_cam_0
.add_lobe(timings.piston1_ignition + exhaust_before_ignition - exhaust_lobe_center + camshaft_offset)
.add_lobe(timings.piston3_ignition + exhaust_before_ignition - exhaust_lobe_center + camshaft_offset)
.add_lobe(timings.piston5_ignition + exhaust_before_ignition - exhaust_lobe_center + camshaft_offset)
_intake_cam_1
.add_lobe(timings.piston2_ignition + intake_after_ignition + intake_lobe_center + camshaft_offset)
.add_lobe(timings.piston4_ignition + intake_after_ignition + intake_lobe_center + camshaft_offset)
_exhaust_cam_1
.add_lobe(timings.piston2_ignition + exhaust_before_ignition - exhaust_lobe_center + camshaft_offset)
.add_lobe(timings.piston4_ignition + exhaust_before_ignition - exhaust_lobe_center + camshaft_offset)
}
private node v5_75_camshaft {
input timings;
alias output __out:
v5_75_camshaft_builder(
intake_lobe_profile: intake_lobe,
exhaust_lobe_profile: exhaust_lobe,
lobe_separation: 112 * units.deg,
base_radius: 12 * units.mm,
timings: timings
);
harmonic_cam_lobe intake_lobe(
duration_at_50_thou: 185 * units.deg,
gamma: 0.9,
lift: 11 * units.mm,
steps: 100
)
harmonic_cam_lobe exhaust_lobe(
duration_at_50_thou: 200 * units.deg,
gamma: 0.9,
lift: 11 * units.mm,
steps: 100
)
}
// Engine
public node RC211V {
alias output __out: engine;
input timings;
// Engine specs constants
label stroke_length(48.2 * units.mm)
label bore_diameter(72.3 * units.mm)
label piston_compression_height(20.0 * units.mm)
label connecting_rod_length(95.0 * units.mm)
//
wires wires()
engine engine(
name: string_add(
"Honda RC211V",
string_add(
" (",
string_add(
timings.label,
")"
)
)
),
starter_torque: 300 * units.lb_ft,
starter_speed: 1000 * units.rpm,
redline: 14000 * units.rpm,
fuel: fuel(
max_turbulence_effect: 1.0,
burning_efficiency_randomness: 0.2,
max_burning_efficiency: 1.0
)
)
// idk
crankshaft c0(
throw: stroke_length / 2,
flywheel_mass: 2.3 * units.kg,
mass: 3 * units.kg,
friction_torque: 10.0 * units.Nm,
moment_of_inertia: 0.02,
position_x: 0.0,
position_y: 0.0,
tdc: timings.tdc_crankshaft
)
rod_journal rj0(angle: 0.0)
rod_journal rj1(angle: rod_journal_separation)
rod_journal rj2(angle: 0.0)
c0
.add_rod_journal(rj0)
.add_rod_journal(rj1)
.add_rod_journal(rj2)
piston_parameters piston_params(
mass: 450.0 * units.g,
blowby: k_28inH2O(0.07),
compression_height: piston_compression_height,
wrist_pin_position: 0.0,
displacement: 0 * units.cc
)
connecting_rod_parameters cr_params(
mass: 380.0 * units.g,
moment_of_inertia: 0.0014,
center_of_mass: 0.0,
length: connecting_rod_length
)
cylinder_bank_parameters bank_params(
bore: bore_diameter,
deck_height: connecting_rod_length + piston_compression_height + stroke_length / 2
)
// ??
intake intake1(
plenum_volume: 2.5 * units.L,
plenum_cross_section_area: 20.0 * units.cm2,
intake_flow_rate: k_carb(800.0),
idle_flow_rate: k_carb(0.0),
idle_throttle_plate_position: 0.991,
throttle_gamma: 1.0
)
// ?? copied from a Kawasaki Ninja 650R
exhaust_system_parameters es_params(
outlet_flow_rate: k_carb(700.0),
primary_tube_length: 10.0 * units.cm,
primary_flow_rate: k_carb(120.0),
velocity_decay: 0.4,
volume: 40.0 * units.L
)
exhaust_system exhaust0(
es_params,
audio_volume: 0.4,
impulse_response: ir_lib.default_0
)
exhaust_system exhaust1(
es_params,
audio_volume: 0.2,
impulse_response: ir_lib.default_0
)
exhaust_system exhaust2(
es_params,
audio_volume: 0.1,
impulse_response: ir_lib.default_0
)
exhaust_system exhaust3(
es_params,
audio_volume: 0.1,
impulse_response: ir_lib.default_0
)
cylinder_bank b0(bank_params, angle: -bank_separation / 2.0)
b0
.add_cylinder(
piston: piston(piston_params),
connecting_rod: connecting_rod(cr_params),
rod_journal: rj0,
intake: intake1,
exhaust_system: exhaust0,
ignition_wire: wires.wire1
)
.add_cylinder(
piston: piston(piston_params),
connecting_rod: connecting_rod(cr_params),
rod_journal: rj1,
intake: intake1,
exhaust_system: exhaust2,
ignition_wire: wires.wire3
)
.add_cylinder(
piston: piston(piston_params),
connecting_rod: connecting_rod(cr_params),
rod_journal: rj2,
intake: intake1,
exhaust_system: exhaust1,
ignition_wire: wires.wire5
)
cylinder_bank b1(bank_params, angle: bank_separation / 2.0)
b1
.add_cylinder(
piston: piston(piston_params),
connecting_rod: connecting_rod(cr_params),
rod_journal: rj0,
intake: intake1,
exhaust_system: exhaust3,
ignition_wire: wires.wire2
)
.add_cylinder(
piston: piston(piston_params),
connecting_rod: connecting_rod(cr_params),
rod_journal: rj2,
intake: intake1,
exhaust_system: exhaust3,
ignition_wire: wires.wire4
)
engine
.add_cylinder_bank(b0)
.add_cylinder_bank(b1)
engine.add_crankshaft(c0)
v5_75_camshaft camshaft(
timings: timings
)
b0.set_cylinder_head (
v5_75_head(
intake_camshaft: camshaft.intake_cam_0,
exhaust_camshaft: camshaft.exhaust_cam_0
)
)
b1.set_cylinder_head (
v5_75_head(
intake_camshaft: camshaft.intake_cam_1,
exhaust_camshaft: camshaft.exhaust_cam_1,
flip_display: true
)
)
// Timing curves are very exceptionally off, especially at higher RPMs
label timing_curve_offset(10 * units.deg)
label timing_curve_attenuation(1.20)
function timing_curve(1000 * units.rpm)
timing_curve
.add_sample(0 * units.rpm, (0 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(1000 * units.rpm, (10 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(2000 * units.rpm, (20 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(3000 * units.rpm, (30 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(4000 * units.rpm, (40 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(5000 * units.rpm, (50 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(6000 * units.rpm, (60 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(7000 * units.rpm, (70 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(8000 * units.rpm, (80 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(9000 * units.rpm, (85 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(10000 * units.rpm, (90 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(12000 * units.rpm, (93 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(13000 * units.rpm, (95 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(14000 * units.rpm, (99 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(15000 * units.rpm, (103 * units.deg) * timing_curve_attenuation + timing_curve_offset)
.add_sample(16000 * units.rpm, (107 * units.deg) * timing_curve_attenuation + timing_curve_offset)
engine.add_ignition_module(
v5_75_distributor(
wires: wires,
timing_curve: timing_curve,
rev_limit: 14000 * units.rpm,
timings: timings
)
)
}
public node RC211V_vehicle {
input mass: 200 * units.kg;
// Tire radius and cross section area approximated from: https://en.wikipedia.org/wiki/Honda_RC211V#Specifications
input cross_section_area: (600 * units.mm) * (1130 * units.mm);
input tire_radius: 21.0 * units.cm;
input drag_coefficient: 0.92; // Guessed drag coefficient between 0.5-1.0, typical of MotoGP bikes
input diff_ratio: 3.5; // ??
input rolling_resistance: 25;
alias output __out: vehicle;
vehicle vehicle(
mass: mass,
diff_ratio: diff_ratio,
tire_radius: tire_radius,
drag_coefficient: drag_coefficient,
rolling_resistance: rolling_resistance
)
}
// Quick and dirty gear ratio derived from a Honda CB500K2 from https://ultimatemotorcycling.com/2019/12/18/motorcycle-gear-calculator-demystifies-gearing-ratios-and-calculated-top-speeds/
// Works very badly, but it works.
public node RC211V_transmission {
alias output __out: trans;
transmission trans(
max_clutch_torque: 250.0 * units.Nm // random number idk
)
trans
.add_gear(3.5)
.add_gear(2.5)
.add_gear(2)
.add_gear(1.536)
.add_gear(1.2)
.add_gear(1.0)
}
public node RC211V_main {
input timings: v5_75_2004_timings();
set_engine(
RC211V(
timings: timings
)
)
set_vehicle(
RC211V_vehicle()
)
set_transmission(
RC211V_transmission()
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment