Watch “This is Formula One”
The method · engineering detail

How the model computes a lap.

Nothing here is hidden. A quasi-static point-mass solver walks the racing line under a load-sensitive tyre and a friction-ellipse limit, deploys the hybrid, integrates the lap, and commits the result to a hash before the session runs. Every step is below, with the maths that goes with it.

Two inputs, a track and one lap, run through the frozen model and come out as a single number you can check. Colour marks where each step comes from: most of the lap is first-principles physics, and only the tyre (2 params) and the offset are anchored to data. Click any step to jump to its maths.

03 · The solver
Quasi-static point mass

The corner speed is a fixed point.

The car is a point mass tracing the line, parametrised by distance s, with local curvature κ(s) (radius R = 1/κ). Cornering needs lateral acceleration alat = v²κ, and the tyre can only supply alat ≤ μ·N/m.

The catch is that normal load N isn’t constant. Downforce grows with speed, so grip grows with speed, so the maximum corner speed ends up depending on itself. It gets solved as a fixed-point iteration: guess v, compute load and grip, get a new v, repeat until it settles. In practice that takes a few passes.

vcorner = √( μ(N)·N / (m·κ) )
where N = m·g + ½·ρ·CL·A·v² Downforce ½ρC_L A v² adds load with v² → grip rises with speed → v appears on both sides. Iterate to convergence.
Two passes

Backward brakes, forward drives.

With every corner’s limit known, two sweeps stitch the profile together. A backward pass walks upstream from each apex applying the maximum braking the tyre allows; a forward pass walks downstream applying the maximum acceleration (engine + hybrid, grip-limited). The realised speed at every point is the pointwise minimum of the three: corner limit, brake limit, drive limit.

Integrating gives the lap: sum the time to cross each segment at the local speed.

v(s) = min( vcorner, vbrake↤, vdrive↦ )
lap t = Σ Δs / v(s) Backward + forward passes under the ellipse below; the min envelope is the fastest physically-consistent lap on that line.
The grip budget
Friction ellipse

You can’t brake and turn at 100% at once.

Longitudinal and lateral grip share one budget. A tyre at the limit of cornering has nothing left for braking, and the other way round, so the usable combinations trace out an ellipse. That is what couples the two passes: at corner entry the model trades braking away for the lateral grip the corner is already asking for, so brake pressure has to bleed off as steering goes on and the two trade smoothly rather than fighting.

( along / along,max )² + ( alat / alat,max )² ≤ 1 The trajectory rides the edge of this ellipse through the whole braking-to-apex phase, rather than snapping at a fixed brake point.
The two numbers
Pacejka, load-sensitive

Grip drops as load rises.

Peak grip is D = μy·Fz, but the coefficient itself falls with vertical load: μy = pdy1 + pdy2·dfz, with dfz the normalised load and pdy2 < 0. That negative slope is why piling on downforce buys you less than linear grip, and a lap simulator lives or dies on it.

Exactly two parameters, pdy1 and pdy2, are fit, to one real qualifying lap. Everything else is first principles. Then they get frozen for the season.

D = μy·Fz
μy = pdy1 + pdy2·dfz,   dfz = (Fz − Fz0)/Fz0 Two knobs, frozen once fitted. They now sit at p_dy1 = 1.36, p_dy2 = −0.12; the summer re-freeze moved them down from 1.8 / −0.17, because removing a bias in the racing line took away work the tyre had been quietly doing.
Where this stops being identifiable

Lap time only pins down the product μ·load. Downforce CL can swing 3.5 → 8.0 and, as long as μ moves the other way, the lap comes out the same. So the product is what gets reported. Quoting a downforce number here would be false precision.

The line
Minimum curvature

Smoothest, within the white lines.

Given the track’s usable width, the line is whichever lateral offset minimises total squared curvature ∫κ²ds, which is the geometrically smoothest path available. Since vcorner ∝ κ−1/2, less curvature means more speed everywhere.

Since the summer re-freeze this generated line is history: calls from Zandvoort onward build the line from the venue’s real 2025 pole lap GPS, filtered, with no generated geometry and no survey map. This section stays as written because it documents the method the early calls, Spa included, actually ran on. The full why is in the repo’s re-freeze document.

minimise ∫ κ(s)² ds  s.t.  line ∈ track width v_corner ∝ κ^(−1/2): straightening the line is the single biggest lever on lap time.
Where this bit us: Spa

A minimum-curvature line can be smoother than any human drives. At Spa the generated line was 36.6% flatter than Antonelli’s and 29 m longer, so the model called the lap 2.4 s too fast. On his real line the same physics is −0.36%. See the debrief →

Top speed & hybrid
V_CAP

Last year’s top speed, times a measured ratio.

There is no claim here about predicting terminal velocity from aero and power maps. It gets anchored to last year instead, using a measured 1.0106 top-speed ratio (2026 over 2025, from the seven pairs that ran after the mid-season power change; the earlier 1.0037 mixed pre- and post-change sessions and was replaced at the re-freeze for exactly that reason), and the solver’s straight-line speed is capped at V_CAP = 92.2 m/s (≈ 331.9 km/h). On a grip circuit like Budapest the entire top-speed term is worth about 3.5% of the lap, so the cap barely moves anything. It is a carry-over, and it is labelled as one everywhere it appears.

solver cap V_CAP = 92.2 m/s ≈ 331.9 km/h
2026/2025 ratio = 1.0106  (7 post-change pairs, ±2.35%) Top speed is carried over rather than predicted. On this circuit it is about 3.5% of the lap, so it doesn’t drive the time. The regulation change lives in the corners and the energy.
ERS: greedy against DP

Where to spend 4 MJ.

The battery deploys a fixed budget per lap, and where it goes matters. A Bellman dynamic program over the state (position, speed, energy) finds the true optimum. The frozen config uses the simpler greedy deploy anyway, because it is canonical, its offset is near zero, and it is easier to defend to someone who doesn’t trust you. DP stays in as a secondary result with tighter scatter.

Since the summer re-freeze the fixed tank is gone too: the prediction path now walks a state-of-charge ledger in track order, draining under deployment and recovering under braking at up to 350 kW, all constants from the published 2026 spec and not one of them fitted. Deployment per lap comes out at 7–9 MJ rather than 4, which is the world the technical directive actually describes (the re-freeze →).

state = (position, speed, energy)
J*(x) = minu [ Δt(x,u) + J*(x’) ] Bellman recursion over the deploy decision u at each step. Greedy is the shipped default; DP the theoretical bound.
Why you can trust the number
Determinism → commitment

Bit-exact, then hashed before the session.

The integrator is semi-implicit (symplectic) Euler at a 1 ms substep, in float32 with floating-point contraction off. Same input gives the same bits, which gives the same lap, which gives the same SHA-256. So the prediction string gets hashed and the hash gets published before every qualifying session. After that the number can’t be moved by anyone, us included. That is the difference between a falsifiable call and an opinion.

prediction → SHA-256b22f8479 Committed pre-session. Reproduce the frozen call bit-for-bit (Budapest = the hash above; the early Spa lock predates the single-run process, declared here):
$ predict_race.py Budapest
Model → prediction

One offset, honest bands.

The blind model time gets corrected by a single frozen offset, μ = +0.102%, and the scatter around it is σ = 1.414%. Both come from a seven-circuit leave-one-out: fit the tyre on six, predict the seventh, repeat seven times, so every error in the average was measured on a circuit its own fit had never seen. That is what makes these numbers out-of-sample rather than a description of the training set.

Bands are Student-t predictive intervals (ν = 6) rather than a flat ±σ, because with seven points σ is itself an estimate: the bands have to pay for where the next error lands and for how well the scatter is even known. The 68% band works out near ±1.639% of lap time and the 95% near ±3.699%, wider than ±σ and ±2σ, and honestly so. The earlier four-circuit figure (μ −0.39%, σ 1.11%) was in-sample and is superseded; the Spa and Budapest calls were made under it and are scored under it, permanently (the full re-freeze →).

pole = tblind / (1 + μ),   μ = +0.102%
band68 = pole · (1 ± T68·σ),   T68 = 1.159
band95 = pole · (1 ± T95·σ),   T95 = 2.616 Offset and σ come from the seven-circuit leave-one-out. Frozen, never re-tuned mid-season.
Parc fermé

The frozen configuration, in full.

Everything a v4 call depends on is fixed before the session and quoted inside the string that gets hashed. So this table is less a summary of the model than the model itself: change any row and the published SHA-256 stops matching.

FrozenValueHow it was obtained
tyre pair (pdy1, pdy2)1.36, −0.12fitted on seven circuits, in-sample
offset μ+0.102%out-of-fold, seven-fold leave-one-out
scatter σ1.414%out-of-fold, seven-fold leave-one-out; rose from 1.32% to 1.46% at the v3 re-freeze when a cancelling defect was removed, then fell to 1.414% at v4 when a leak in the calibration itself was closed
top-speed ratio1.0106measured, seven 2026/2025 pairs
band multipliersT₆₈ 1.159 · T₉₅ 2.616derived: Student-t, ν = 6, ×√(1+1/7)
line filterSavitzky-Golay, window 9 + box half-width 3chosen; the spread across both filters (windows 7/11, half-width ±1) is declared in every call as s1
MGU-K350 kW · 4 MJ store · 7 MJ/lap harvest2026 regulations

The first row and the two after it are doing opposite jobs, and keeping them apart is the reason the bands mean anything. The tyre pair saw all seven circuits, so it is the best available description of what already happened. μ and σ never saw the circuit they were scored on, so they are the estimate of what happens next. Only the second kind belongs anywhere near a band, and a model that quotes its in-sample residual as its accuracy has quietly swapped one for the other.

Two engines sit behind this: a high-fidelity 3-DOF C++ core for telemetry and line optimisation, and the QSS point-mass layer above, which is the one that makes the published call. Run it on a circuit →