The engine

Eight cylinders, a cross-plane crank, a cam, two exhaust manifolds — and what each file is arguing with.

1One cylinder, as an open system

The physics has to be honest or the thesis proves nothing, so I could not take the shortcut. Each cylinder is integrated in crank angle, five terms of the first law per radian.

mcvdTdθ=
pdVdθthe piston, taking or giving work
+dQburndθthe fire — wiebe.hpp
dQwalldθthe coolant, stealing — woschni.hpp
+dmidθhigas arriving, carrying enthalpy — port.hpp
udmdθthe same gas, accounted as mass

That is the whole model. Five terms, and each one is a file already written.

Open and not closed, because everything anyone cares about happens in the half of the cycle the air-standard Otto cycle deletes — the half where the valves are open and the mass is a variable. The textbook is taught first because a closed system gives you η=1r1γ in one line, and that line is genuinely why compression ratio matters. It is also the last thing it can tell you. Pumping loss, volumetric efficiency, cam timing, reversion, the entire reason a throttle costs you anything: all of it lives in the half thrown away to make the integral tidy.

The third term is where a third of the fuel goes, and it is also the one place in this project where the SI rule bends. Woschni’s 1967 correlation is not dimensionally homogeneous — the leading constant carries whatever units are needed to balance the two sides.

h=3.26B0.2p0.8T0.55w0.8
B in metres, p in KILOPASCALS, T in kelvin, w in metres per second — and it returns watts per square metre kelvin.

Feed it pascals instead of kilopascals and it returns a number four thousand times too large, silently, and the engine runs stone cold and makes far too much power. So woschni.hpp converts at its own boundary and says so loudly in a comment. This is the difference between a law and a correlation: everything else here is a statement about how the world is, and this is a statement about what Woschni measured.

γ varies with temperature too, falling from 1.400 in a cold intake charge to 1.246 in combustion products. Compute a cycle at a constant 1.4 and it will promise you a fifth more thermal efficiency than any engine has ever delivered. That gap is not a mystery. It is a constant somebody left alone.

2Two delay lines make a pipe

The exhaust is a digital waveguide. A pipe is two delay lines, one per direction, which is d’Alembert’s 1747 result sampled at 176 kHz rather than in the continuum. Junctions scatter by continuity of pressure and conservation of volume flow.

The loop is closed: each cylinder’s exhaust boundary is the pressure standing in its own primary pipe, so a header has a length that matters and not merely a note. Only by about two percent here, and I would rather hand you the measurement of why than the excuse.

3What is in it

Every header opens with prose explaining why the part exists and what it is arguing with — not what the code does, because the code already does that. I was aiming for a service manual written by someone who likes the machine. They are meant to be read in this order, each one assuming the last.

s(θ)=(a+l)acosθl2a2sin2θV(θ)=Vc+As(θ)
s is how far the piston has come down from top dead centre; a is the crank throw, l the rod, A the bore area, and Vc the clearance volume.

That is geometry.hpp, and it is the entire machine. A piston does not travel sinusoidally — the root is the rod swinging off the centreline, and everything people find surprising about an engine, including why the secondary shaking forces exist at all, comes out of that one term.

si.hpp
The one rule everything depends on: inside the engine, everything is SI. Read it first or nothing later makes sense.
geometry.hpp
The crank-slider, which is the entire machine. One equation; every other file is decoration on it.
crankshaft.hpp
The thesis. Why a cross-plane V8 burbles, and why it refuses to let you type in a firing order.
balance.hpp
The other half of the thesis: what the cross-plane crank cost, and why almost nobody buys the alternative.

Stop there and you have the argument. Everything below is the machine that makes it audible.

charge.hpp
The working fluid. An engine does not burn petrol, it heats air.
wiebe.hpp
How fast the fire spreads — a curve fitted in the 1950s that outlived everything meant to replace it.
woschni.hpp
The third of the fuel that goes into the coolant. Also the one sanctioned exception to the SI rule, and it says so loudly.
camshaft.hpp
The only part of the engine that decides anything. Two engines identical but for this make peak power 2500 rpm apart.
port.hpp
The bottleneck. A naturally aspirated engine is a pump forbidden from using a pump.
cylinder.hpp
All of the above, assembled: five terms of the first law per radian of crank.
induction.hpp
The throttle, and the vacuum behind it — the most wasteful control mechanism never replaced.
ignition.hpp
Deciding when to light it, with no way of knowing. Flyweights and a rubber diaphragm.
knock.hpp
The constraint that actually decides compression ratio. Its number is an index, not a verdict, and the file explains why.
friction.hpp
The tax. At idle it is all of it.
exhaust.hpp
Four pipes, a collector, and the noise. Where the project cashes in.
riemann.hpp
Its foil: the nonlinear solver that says what the one above costs. Verified, and not wired in.
engine.hpp
The assembly, and the loop — a running engine is two statements chasing each other.
windsor.hpp
The engine itself, as built. Every figure is Ford’s except the few marked otherwise.

4The instruments

The engine is a sealed mechanism that turns and gets hot and knows nothing about being observed. Six instruments are bolted to it afterwards, and none of them is allowed to reach into the physics to make its own job easier. That rule cost me an afternoon twice and was worth it both times.

./windsor spec       the shop manual page
./windsor dyno       a water brake on the flywheel
./windsor run        a gauge cluster, wired to the sensors
./windsor card       let it draw its own indicator diagram, as Watt did
./windsor record     three feet behind the tailpipe, in stereo
./windsor verify     the inspection sheet

Read verify.cpp last. Its entry point is the contents page of everything the project claims, and every claim there is checked against something outside it. What it checks →

5What it does not model

Stated plainly, because an unstated simplification is a lie and a stated one is a design decision. Each one is named in the file where it bites, so nobody has to find it the hard way.

wrist-pin offset
Real pistons carry 0.5–1.5 mm toward the thrust side. It buys a rattle you cannot hear and costs a closed-form solution.
runner tuning
Built three times, shipped none. Length mattered more than the junction, and a rising torque curve turned out to be an unstable equilibrium for the brake.
nonlinear gas
The solver exists, is verified, and is not wired in. A real blowdown front clocks at 1409 m/s — Mach 2.4 — and the shipped model cannot make one. It says so with a number instead of an apology.
and the rest
Blow-by, oil temperature, crankshaft torsion, dissociation above 2000 K. None of these would change the shape of the project.

6Building a different one

Everything the engine is made of is in windsor.hpp, and nothing else needs touching to build another. The source marks the distinction that matters: a specification is Ford’s — a bore, a rod length, a cam card, a carburettor, measured off the engine. A calibration is a number fitted because nothing else could supply it. Every calibration is marked in the source and says what it was fitted to. There are five. Anything unmarked is Ford’s.

short_block()          4.000 × 3.000, 5.090 in rods, 9.5:1
stock_cam()            266°/256°, 0.426/0.425 in lift, 117° ICL, 110.5° LSA

cross_plane_crank()    the factory forging  →  1-5-4-2-6-3-7-8
cross_plane_crank_ho() the same forging, 1982 cam  →  1-3-7-2-6-5-4-8
flat_plane_crank()     a billet flat crank, same block, same rods

Grind the throws into one plane and every bank interval goes to 180°. Hand Camshaft::from_card what a catalogue prints and it converts the centreline at its own boundary, because cam cards quote centrelines about the gas-exchange top dead centre, 360° from the firing one everything else here uses.

Then run ./windsor verify. Several of its checks belong to this engine — the flow bench, the cam card, the firing orders — and will fail honestly if you have built a different one. That is the check doing its job. Edit the expectation to match the engine you meant to build, and not before you are sure which of you is wrong.