V-Engines — Flat-Plane vs Cross-Plane, in One Framework
Series: ← The Slider-Crank, Three Ways · ← Multi-Cylinder I4 · ← Boxer-4 · ← Rocking Couples · ← Non-boxer Flat-4 · ← Summary · V-Engines · Combustion → · Balance Shafts → · Engine Mounts → · Active Damping → · Chassis Response → · Engine Mounts →
Reference: Field Guide · Concepts Primer · Physics · Computational Machinery · Dimensional Reduction
The inline and flat engines we covered in the earlier chapters all had piston motion along a single world axis. One scalar sign per cylinder ( or ) was enough to say “right bank” or “left bank” — and the phasor sum was a single complex number per harmonic.
V-engines break that assumption. Each bank sits at its own angle, so the inertial force from every cylinder lives along its own direction. The fix is straightforward: replace the scalar with a 2D unit vector pointing along the cylinder’s own bore axis, and run the phasor sum independently on the x- and y-components.
The script: v_engine_analysis.py.
Presets: I4 / boxer-4 / flat-4 (sanity checks) plus V-twin 90°, V6 60°,
V8 flat-plane, V8 cross-plane.
Same solver run, same phasor framework, one extra degree of freedom per cylinder.
1. The 2D phasor extension
Earlier chapters summed:
with . That is the one-axis special case of a 2D vector sum. Every cylinder’s piston motion points along some bank angle measured from world +x. The inertial force on the main bearing from that cylinder is the per-cylinder force magnitude times that bank direction:
In phasor land this splits into two independent sums:
Moments sum the same way, weighted by each cylinder’s x-position along the crankshaft:
Four complex phasor sums per harmonic — one each for force-x, force-y, moment-pitch, moment-yaw. The earlier inline/flat results come back exactly when all banks are at 0° or 180° (so and ): the y-force and yaw-moment sums vanish and the x-force and pitch-moment sums collapse to the scalar-sign case.
The two helpers phasor_sum_2d() and phasor_moment_sum_2d() in
_common.py
are exactly this; 10 lines each.
2. An engine is three lists
Every V-engine in this framework is fully specified by three parallel per-cylinder lists:
phases_deg— crank angle at which this cylinder is at TDCbank_angles_deg— bank direction (0° = horizontal right, 90° = vertical up), measured from world +xpositions— x-position along the crankshaft axis
The tricky part isn’t the math — it’s the bookkeeping. Real V-engines use shared crankpins (two cylinders per pin, 90° apart by bank angle for a 90° V), which means the phase of cylinder is , not just the pin angle. Get that wrong and you get a nonsense firing order.
Get it right and the framework does exactly what you would do by hand.
The preset library in v_engine_analysis.py encodes all seven:
| Preset | Config | Phases (deg) | Banks (deg) |
|---|---|---|---|
I4_flat_plane | inline-4 (sanity) | [0, 180, 180, 0] | [0, 0, 0, 0] |
boxer_4 | flat-4, boxer crank (sanity) | [0, 0, 180, 180] | [0, 180, 0, 180] |
flat4_non_boxer | flat-4, I4 crank (sanity) | [0, 180, 180, 0] | [0, 180, 0, 180] |
v_twin_90 | V-twin, shared pin | [45, 135] | [45, 135] |
v6_60 | 60° V6, 3 shared pins | [60, 120, 300, 0, 180, 240] | [60, 120, 60, 120, 60, 120] |
v8_flat_plane | 90° V8, pins [0,180,180,0] | [45, 135, 225, 315, 225, 315, 45, 135] | [45, 135, 45, 135, 45, 135, 45, 135] |
v8_cross_plane | 90° V8, pins [0,90,180,270] | [45, 135, 315, 45, 225, 315, 135, 225] | [45, 135, 45, 135, 45, 135, 45, 135] |
(The first three presets are there as regression checks. They reduce exactly to the earlier scalar-sign results — which is the easy way to confirm the 2D extension is equivalent on the inline/flat subset.)
3. The hero result — V8 flat-plane vs cross-plane
Both engines have the same eight cylinders, the same bore axes, and the same positions along the crankshaft. The only thing that differs is which crankpin each piston sits on — four pins in an inline-4 pattern (flat-plane) vs four pins in a 90°-spaced pattern (cross-plane).
At rpm = 10, R = 1 m, L = 2 m (so R/L = 0.5), and cylinder
half-spacing a = 100 mm:
V8 flat-plane
| n | [N] | [N] | [N·m] | [N·m] | |---|---:|---:|---:|---:| | 1× | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | 2× | 23.2559 | 0.0000 | 0.0000 | 0.0000 | | 3× | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | 4× | 0.0000 | 1.6686 | 0.0000 | 0.0000 |
V8 cross-plane
| n | [N] | [N] | [N·m] | [N·m] | |---|---:|---:|---:|---:| | 1× | 0.0000 | 0.0000 | 2.7915 | 2.7915 | | 2× | 0.0000 | 0.0000 | 1.1628 | 0.0000 | | 3× | 0.0000 | 0.0000 | 0.0000 | 0.0000 | | 4× | 0.0000 | 1.6686 | 0.0000 | 0.0000 |

The comparison at a glance:
- Flat-plane V8 = two I4s side by side. Each bank independently has the I4 signature — 1× cancelled, 2× reinforced at four times the per-cylinder force. With 8 cylinders total, this compounds to a 23 N secondary shake at 2× in the world-x direction. This is the characteristic flat-plane V8 buzz — Ferrari F40, 488, every Formula-1 V8 of the 1990s–2000s. It is loud, high-frequency, and the cost of admission for the flat-plane layout’s even firing pattern across each bank.
- Cross-plane V8 kills the 2× force entirely. The four 90°-spaced pins create a phasor pattern that cancels at 2× along both world axes. In exchange, the engine picks up a 1× rocking couple of 2.8 N·m about both pitch and yaw axes — the block wants to rock side-to-side at crank frequency. American V8s solve this with heavy crankshaft counterweights (which adds rotating mass and caps rev ceilings), and the driver hears the smooth 4× firing rumble that Detroit sells as “character”.
Same engine geometry, same number of cylinders, same bank angle — different pin arrangement, completely different NVH signature. The table above is the full numerical story.
4. V-twin 90° and V6 60°
The Ducati-style 90° V-twin has two cylinders sharing a single crank pin, banks at 45° and 135° from world +x. Running the preset:
| n | [N] | [N] | [N·m] | [N·m] | |---|---:|---:|---:|---:| | 1× | 9.870 | 9.870 | 0.000 | 0.000 | | 2× | 4.111 | 0.000 | 0.000 | 0.000 | | 3× | 0.000 | 0.000 | 0.000 | 0.000 | | 4× | 0.295 | 0.295 | 0.000 | 0.000 |
Two cylinders isn’t enough to cancel anything. 1× forces appear in both world-x and world-y, at magnitude each — because the two banks at ±45° distribute the primary force equally onto both axes. (Positions are both zero so no moments.) A 90° V-twin is a characterful engine precisely because this primary shake lives in two orthogonal directions simultaneously, giving the rocking motion its distinctive diagonal bias.
The V6 60° with its three shared pins at 0°, 120°, 240° on the crank:
| n | [N] | [N] | [N·m] | [N·m] | |---|---:|---:|---:|---:| | 1× | 0.000 | 0.000 | 0.000 | 1.710 | | 2× | 0.000 | 0.000 | 0.000 | 0.711 | | 3× | 0.000 | 0.000 | 0.000 | 0.000 | | 4× | 0.000 | 0.000 | 0.000 | 0.051 |
Forces cancel at every harmonic — that is the payoff of the three 120°-offset pin pairs. But the V6 still has a yaw rocking couple at 1× () because its three pin pairs are distributed along the crankshaft and their primary forces project onto world-y (the V-symmetry axis) at different positions. This is the V6 60°‘s own version of the straight-3’s rocking couple, just rotated into a different axis of the moment plane. Production V6s handle this with a balance shaft rotating counter to the crank.
5. Where V-engines sit in the (Force, Moment) matrix
Adding V-engines to the classification from the summary chapter:
| Force = 0 | Force ≠ 0 | |
|---|---|---|
| Moment = 0 | Boxer-4, Straight-6, V8 flat-plane @ 1× | I4 (2× shake), V8 flat-plane (2× shake), V-twin 90° |
| Moment ≠ 0 | Straight-3, Non-boxer flat-4, real Boxer-4, V6 60°, V8 cross-plane | V-twin 90° (a shake AND a moment at 1× if pins offset), asymmetric layouts |
The main new entries:
- V8 flat-plane lives in the shakes corner just like the I4 — and for the same reason (it’s literally two I4s side by side). Moments cancel at every harmonic because of the symmetric x-layout of its pin pairs.
- V8 cross-plane lives in the rocks corner. No external shake leaves the engine; instead it rocks about pitch and yaw at crank frequency. This is why American V8s feel “solid” on the mounts but need big crankshaft counterweights.
- V6 60° lives in the rocks corner too — forces cancel, but a 1× yaw rocking couple remains.
- V-twin 90° is too small to cancel; it shakes.
6. Sanity checks
All three sanity-check presets reduce exactly to the earlier scalar-sign results:
| Preset | Expected (from earlier chapters) | V-engine framework result |
|---|---|---|
I4_flat_plane | 2× force = , M = 0 | F_x[2×] = 16.44 N, all M = 0 ✓ |
boxer_4 | All F, M cancel at every n | All F, M = 0 ✓ |
flat4_non_boxer | 1× moment = | ✓ |
Running the 2D phasor code on inline/flat layouts is identical to running the scalar-sign code — which is exactly what the generality argument promised. The 2D extension doesn’t lose the old cases; it adds the V cases.
7. FAQ
Q1. Why is the V8 cross-plane’s 2× moment non-zero at 1.16 N·m when its 2× force is zero?
Force and moment are independent phasor sums. The 2× force sum cancels across the full 8-cylinder layout because the cross-plane’s pin distribution combined with alternating bank assignments makes the bank-projected 2× phasors close to zero (four phasors pointing at cardinal directions on the complex plane add to zero). But the moment sum weights each phasor by its or before summing; the cylinder positions are , , so the weights aren’t symmetric about zero in the same way as the unweighted phasors. The result is a residual 2× pitch moment of .
This is subtle enough that real cross-plane V8s usually don’t bother damping the 2× moment — it’s small enough to live with.
Q2. Why does the flat-plane V8 have a 4× world-y force component?
Look at the 4× row: 1.67 N of F_y, zero F_x. That’s the 4× harmonic of the slider-crank kinematic expansion leaking into the sum. Per-cylinder, the 4× force is already non-zero (about 0.30 N here at R/L = 0.5) because the exact slider-crank expansion has a small -scale 4× term. When you sum eight cylinders at the flat-plane phase pattern, the 4× phasors happen to cancel in the -weighted sum (bank alternates ) but reinforce in the -weighted sum (all banks contribute , so eight of them add constructively). Eight cylinders × × . It’s a small harmonic, but it’s real, and it’s why flat-plane V8s have a recognisable buzz at even higher frequencies than their 2× fundamental.
Q3. Does this framework handle Y-mounted V-engines, transverse layouts, or inline-V hybrids (W12, VR6)?
Yes, mechanically — as long as each cylinder has a well-defined bank
angle and position. The preset format is three lists:
(phases_deg, bank_angles_deg, positions). For a W12 (three banks
of four, each bank offset by 15°), you list 12 cylinders with their
per-cylinder bank angles and positions, and the framework sums them
the same way. No new physics, just more bookkeeping.
The things the framework currently does not handle:
- Gravity-on operation — the per-cylinder simulation is gravity-off by design; adding gravity reintroduces DC terms that muddy the cancellation argument.
- Non-identical cylinders — mass asymmetry between banks, for example, or banks with different rod ratios. Each non-identical cylinder would need its own per-cylinder simulation (not one for the whole engine). Extension: 50 more lines, solver runs per “class” of cylinder.
- Firing-pressure forcing — the combustion pulse. Still in the same “one user-force term, added on top” extension described in the summary chapter FAQ Q3.
Q4. Could the framework be used to design a new crankshaft — given a desired vibration fingerprint, solve for pin positions?
In principle yes — the phasor sums are all closed-form in
, so inverse design (minimise the sum of ,
, , over chosen harmonics with
bank-angle and position constraints fixed by manufacturing) is a
small nonlinear optimisation over the phase list. 8 unknowns for a
V8, 4 objectives per harmonic, multiple harmonics of interest. Off-
the-shelf scipy.optimize.minimize would handle it. Not something
the library does today, but a natural “what’s next” script.
Q5. Why do I need a separate simulation for a V-engine vs an inline engine?
You don’t — that’s the whole point. One per-cylinder simulation
at the chosen (rpm, R, L, masses) gives you for every
harmonic. That same single run is phasor-summed into every engine
configuration (I4, boxer, V-twin, V6, V8 flat-plane, V8 cross-plane, …)
via different (phases, bank_angles, positions) lists. The V-engine
script reuses the same single_cylinder_harmonics() call that the
earlier scripts used; the only difference is the summation step.
8. What’s next
- W-engines and inline-V hybrids (W12, VR6, W16). Same three-list format; just more cylinders to describe.
- Non-90° V angles — 60° V6 is included, but 72° V10 (Lamborghini), 60° V8 (rare), 45° V16 (Cadillac V16 of the 1930s). Each is a fresh preset.
- Inverse design — FAQ Q4. Given a target vibration spectrum, back out a pin arrangement. Probably a new script using the same phasor machinery.
- Crankshaft counterweights — real engines add rotating masses along the crank to cancel 1× moments. Those masses contribute their own rotating phasor at 1× (magnitude ) that can be subtracted from the net moment. Another clean extension, still no solver change.
Each of these is an afternoon’s work plus documentation, following the same pattern as this chapter.
Files and scripts referenced
v_engine_analysis.py— the V-engine analysis script. Includes the 7-preset catalogue, theanalyse_one()/compare()/list_presets()entry points, and the plotting function that producesv_engine_comparison.png._common.py— extended withphasor_sum_2d()andphasor_moment_sum_2d(), the 2D generalisations of the scalar-sign phasor sum.- The five earlier chapters in the series (I4, Boxer-4, Rocking Couples, Non-boxer Flat-4, Summary) are all linked in the series navigation at the top of this page.