Skip to content
← Writing
SIMULATION22 AUG 2026

I simulated every car in Guatemala City

186,131 journeys, 85,138 streets and one morning replayed thirteen different ways. Nothing I tried made the city move more people and move them faster. Here is what did work, and everything I got wrong on the way.

Derek Urizar38 min read · 14 figures

A journey across Guatemala City in the morning takes 109 minutes in my simulation. Villa Nueva to the centre — twenty-one kilometres — is two and a half hours in real life. That is 8.4 km/h. You could cycle it. Some mornings you could nearly walk it.

Everybody who lives here knows this. What nobody has is a way to ask what if and get an answer that is not a guess. What if work started at staggered hours? What if we charged to come in? What if we finished the ring road? Those are expensive questions to answer by trying them.

So I built the city inside a computer. Every street, every traffic light, every one of the roughly 186,000 journeys that start on a weekday morning. Then I ran the same morning thirteen times, changing exactly one thing each time.

Not one of the thirteen made the city move more people and move them faster. Two of them made it move the same people with less time lost, and both are close to free.

This is the whole story, from the first version that was so wrong it was funny to the fourteen simulations running at once on rented computers. It assumes you know nothing about traffic engineering or programming, because the interesting parts do not require either.

VIDEOP1

A real Guatemala City rush hour — dashcam through Calzada Roosevelt or Aguilar Batres, or a fixed shot from an overpass. Thirty seconds is plenty. This is the hook: the thing everyone recognises before any of the abstraction starts.

What it means to simulate traffic

Most traffic planning treats a road as an average. This treats it as several thousand individual drivers, each deciding for itself — which is the only way a jam can appear out of nothing.

There are two ways to model traffic. You can treat a road like a pipe with water in it: so many vehicles per hour, an average speed, a formula relating the two. That is fast, it is how most planning is done, and it works fine for questions like how much traffic will this road carry.

Or you can give every single vehicle its own position, its own speed and its own driver, and let each of them decide, several times a second, what to do next. That is called a microscopic simulation, and it is enormously more expensive — one morning of Guatemala City takes four to five hours of computing.

It is worth it because an average cannot be stuck in traffic. It can tell you the road is moving at 21 km/h. It cannot tell you that a quarter of the people on it are doing walking pace while everybody else is fine, and that is usually the thing you actually wanted to know.

FIG 1The same road, two ways of modelling itLOOPING
AS AN AVERAGE
45km/h
AS FORTY DRIVERS

Both panels currently average 45 km/h. The slowest vehicle on the right is doing 45.

On the left, traffic as a single average — steady, tidy, and the way most planning is done. On the right, the same road with every vehicle deciding for itself. They report the same average. Only one of them can tell you that somebody on that road is doing walking pace.

How one car decides

Every vehicle asks the same three questions once a second and takes the smallest answer. That is the entire model.

This is the part people expect to be complicated, and it is not. Once a second, every vehicle in the simulation asks three questions:

  • How fast do I want to go? The speed limit, or my own top speed, whichever is lower.
  • How fast can I go and still stop? If the car in front slammed its brakes right now, could I stop before hitting it? This is the one that matters.
  • How fast can I physically get to this second? An engine can only do so much.

It takes the smallest of the three, subtracts a little bit of randomness because real people do not hold a speed exactly, and that is its new speed. Repeat for every vehicle, every second, for five hours of simulated morning.

The figure below is not a diagram of this. It is this — the same equation the real simulation runs, with the same numbers: a car in this model accelerates at 2.60 m/s², brakes at 4.50, wants a one-second gap and never gets closer than 2.20 metres when stopped. Drag the slider and watch which of the three answers is currently the smallest.

FIG 2One car, one decision, every secondINTERACTIVE
B
A
21.3 m
IT PICKS THE SMALLEST OF THREEBRAKING
the speed it wants70
the speed it can still stop from40
the speed it can reach this second60

Right now the limit is the speed it can stop from.

GAP21.3 m
ITS SPEED50 km/h
PEDAL-3.63 m/s²
The follower runs the real Krauss model with the simulation's real car parameters: 2.60 m/s² of acceleration, 4.50 m/s² of braking, a 1.00 s headway and a 2.20 m standstill gap. Nothing here is illustrative.Drag the slider to change how fast the car in front is going.

A jam that nothing caused

Take away the crash, the traffic light and the merge, and a queue still forms — purely because no human holds a steady speed.

That last detail — the small random subtraction, because people do not hold a speed exactly — is the most important number in this entire article, and it is the one that looks like a rounding error.

Here is why. One driver drifts a little slow. The driver behind has to brake slightly harder to keep a safe gap. The one behind that brakes harder still. Six or seven cars back, somebody is at a standstill — and they will tell you, honestly, that traffic just stopped for no reason. They are right. It did.

The queue then travels backwards along the road, at roughly 15 km/h, while every car in it is moving forwards. You have sat in this. Everyone has. It is why you crawl for ten minutes, reach the front, and find absolutely nothing there.

FIG 3A jam that nothing causedINTERACTIVE
SLOWEST CAR0 km/h
FASTEST CAR0 km/h
IN THE QUEUE0 / 22
Twenty-two cars on a closed loop. No crash, no traffic light, nothing entering or leaving. The only variable is how imperfectly drivers hold a steady speed — sigma, in the model. Above roughly 0.5, a standing queue forms and then travels backwards around the ring while every single car is still moving forwards.The simulation's real cars use sigma 0.55. Motorcycles use 0.70.

This is why the model has to be microscopic. An equation about average flow cannot produce that queue, because the queue is not in the average — it is in the variation. And the variation is what ruins your morning.

Building the map

85,138 street segments taken from OpenStreetMap, then rebuilt four times because each version was broken in a way the previous check could not see.

The city comes from OpenStreetMap — the same volunteer-drawn map a lot of apps use. Converting it into something a simulator can drive on gives you 85,138 street segments, 354 traffic lights and about 5,965 kilometres of road.

Getting there took four builds. The interesting one is the third, because of how the second one was broken. A field survey had corrected the lane counts on 108 stretches of road, and the build checked its own work: exactly 108 changed, nothing else moved, all 354 traffic lights intact. Clean pass.

Except 67 of those 108 had gained lanes that dead-ended at the next junction. Cars could enter them and then had nowhere to go. A gate that had just been widened from two lanes to three let in exactly the same number of vehicles as before, because physically it was still a two-lane gate.

FIG 4Four passes over the mapFIGURE
  1. 1
    Import the mapgtcity

    Pull the roads out of OpenStreetMap and turn them into something a simulator can drive on.

    Lane counts agreed with reality 94.7% of the time. Every major road present, none split in two.

  2. 2
    Fix the lane countsgtcity4

    A 25-point field survey corrected 108 stretches of road that the map had wrong.

    Checked that exactly 108 differed and nothing else moved. This build is known-defective and is never used on its own.

  3. 3
    Reconnect the new lanesgtcity5

    67 of those 108 had gained lanes that dead-ended at the next junction. A three-lane gate was physically still a two-lane gate.

    One gate went from letting 72.6% of its traffic in to letting 100% in.

  4. 4
    Delete a roundabout that was not theregtcity6

    The importer had decided a main road was part of a roundabout, so the road was yielding to itself.

    Six checks, including asserting that exactly four connections changed state across the entire city. The first attempt failed its own check, because the tool re-detected the roundabout it had just been told to delete.

The finished network is 85,138 street segments, 354 traffic lights and 5,965 kilometres of road. It took four builds to get there, and each one exists because the one before it was wrong in a specific, findable way.
IMAGEP2

The finished gtcity6 network rendered whole — the 85,138 segments as a single shape, so the reader sees the city as the model sees it. Ideally light-on-dark to match the page.

Who is actually on the road

Three in ten vehicles in the model are motorcycles. That number is an estimate nobody has ever counted, and it changes everything downstream.

A simulation needs to know what is driving, not just how many. A motorcycle is 2.20 metres long and will sit one metre off your bumper. A car is 4.50 metres and wants 2.20. A bus is eleven metres, and accelerates at less than half a car's rate — which is why one pulling away from a stop holds up everything behind it.

The model's fleet is 51.3% cars and 30% motorcycles. And here is the uncomfortable part: that 30% is an estimate. There is no classified vehicle count for Guatemala City in this project, because as far as I can tell there is no published one at all. It was 25% before, on my own guess; it is 30% now, on the project owner's.

FIG 5What a hundred vehicles occupyINTERACTIVE
motorcycle 30.0%car 51.3%delivery 6.5%taxi 5.6%bus 3.8%truck 2.8%
QUEUE, 100 VEHICLES621 m
VS THE MODEL'S FLEET0 m

Moving five percent of the fleet from cars to motorcycles takes about five and a half metres of queue out of every hundred vehicles. Across a city that is a real amount of road — which is exactly why an unmeasured estimate sitting on that number is uncomfortable.

A hundred stopped vehicles, queued nose to tail, using each type's real length and standstill gap. The model's fleet is 30% motorcycles — an estimate from the project owner, not a traffic count, and one of only two numbers the whole model leans on that nobody has measured.Careful: this changes proportions, not counts. Turning cars into motorcycles is not the same as removing cars.

There is a trap hiding in that slider, and the real build had to solve it. Those are proportions. Sliding motorcycles up does not remove a single car from the road — it turns cars into motorcycles. When I later wanted to model ten percent fewer cars, changing that number would have given me the same total traffic with more buses and lorries than I started with. And the report would have looked completely normal.

Three hundred and fifty-four traffic lights

A signal is fine as long as the green time can clear what arrives. Below that line the queue does not have a length, it has a growth rate.

Cars arrive at a junction steadily and can only leave on green. So there is a line: the green share, times how fast a queue discharges, against how fast cars arrive. Above the line, the queue builds on red and clears on green, which is what a working signal looks like. Below it, the queue never clears — it grows until it blocks the junction behind it, and then that one blocks the one behind it.

FIG 6One traffic light, one queueINTERACTIVE
WAITING0
CAN CLEAR810/h
ARRIVING900/h

Below the line. More cars arrive each cycle than can ever leave, so this queue does not have a length — it has a growth rate.

Cars arrive steadily; they can only leave on green. The queue clears if the green share times the discharge rate beats the arrival rate — and if it does not, the queue never clears at all, it just grows until the next thing upstream jams. Nine of the model's 354 signals were generated below that line.Drag the green share below about 50% and watch the queue stop clearing.
IMAGEP3

A real Guatemala City intersection photographed from above or at eye level, beside the same junction as the model draws it. This is the moment to show that the abstraction has a real place underneath it.

Nine of the model's 354 signals were generated below that line by the map converter, giving a main road under 20% of the green time. I fixed one as proof — Calle Martí — and it went from 14 times slower than reality to 1.84 times. The junction's throughput went from 192 vehicles an hour to 943. Its jams went from 211 to 4.

I did not fix the other eight, and the reason is worth stating: measured together, they carry about one percent of the problem. Seven of them are on a single avenue. Spending a week on them would have been work I could point at, on a number nobody would notice.

Where do all the cars come from?

Not from inside. 1.2 million vehicles a day cross into the city through 24 surveyed entry points, and that single realisation is what took the model from useless to useful.

The first version of this model had four trips in it. Zone 15 to zone 10, zone 14 to zone 10, zone 10 to zone 1, zone 1 to zone 12 — seven thousand vehicles, every single one starting and ending inside the city.

It ran beautifully. Every major road at nearly the speed limit, 47 km/h across the network, no congestion anywhere. It was also completely useless, and for a reason that is obvious the moment you say it out loud: the model contained no traffic coming into the city from outside. The commute it was supposed to explain did not exist inside it.

The fix is what makes the rest of this article possible. A survey of the city's periphery gives 24 entry points and how many vehicles cross each one per day. Together they carry 1,201,000 vehicles. Feed those in at the edges instead of inventing trips in the middle, and the model stops being a diagram of the city and starts being a model of the commute.

FIG 7The 24 ways into the cityINTERACTIVE
THE CITY

Every one of the 24 is radial — it points at the centre. Not one of them feeds a ring road, which is a limitation worth remembering when the ring turns up later.

Each circle is a surveyed entry point, at its real coordinates, sized by how many vehicles cross it in a 16-hour day. Together they carry 1,201,000 vehicles. The centre of the map is deliberately empty: this model's traffic is overwhelmingly people coming in from outside, not people circulating within.Hover or tap a gate.

One assumption is doing a lot of work in there. The survey counts a whole sixteen-hour day; the simulation needs one morning hour. The fraction connecting them — 0.095 — is not measured. It is reasoned from how counts of this shape usually distribute, and it was tested: swinging it from 0.08 to 0.11 moves the headline result by about 16%, which is enough to matter and not enough to be the explanation for anything.

The first run was wrong, and so were the next six

Build the scoreboard before you touch the model. Every version of this thing has a score, including the versions that scored worse than the one before.

The single best decision in this project was building the way of checking the model before improving the model. Three independent ways, in fact, because they can each be fooled and they are fooled by different things.

  • Four real commutes, timed door to door. Tests whether traffic moves at the right speed.
  • Twelve counting points inside the city. Tests whether the right amount of traffic is on the right streets — much harder to fake.
  • Twenty-two routes checked against Google's live traffic times, all of them entirely inside the simulated area.

Why three? Because a model can reproduce four corridor speeds perfectly while having completely the wrong traffic on completely the wrong roads. This project shipped exactly that failure once.

FIG 8Every attempt, including the ones that went backwardsFIGURE
0The first run4.65×

no traffic entered the city from outside at all -- four intra-city trip pairs, 7,000 trips

1The cordon model1.85×

model the periphery pouring in. The single largest accuracy gain in the project

2A bigger map2.26×

it got WORSE, and that is the good part: the queue moved OUTSIDE the simulated area, where nothing measures it

3Cordon v22.29×

score-neutral, model more defensible

4First traffic counts2.26×

the second instrument arrives: how much traffic, not just how fast

624 gates1.85×

25% more demand on a 60% bigger network bought 6% more traffic. Demand volume eliminated

7gtcity62.25×

lane connections repaired, phantom roundabout removed, one signal retimed

8Calibrated2.46×

junction gap acceptance and route diversity. CURRENT.

The blue line is where a perfect model would sit. The instrument that replaced this one splits the error in two: 2.0833× too fast on an empty road, which geometry alone explains and which the calibration could not move — and 2.0797× on reproducing the slow-down, which it did move.

How many times too fast the simulation was, at each stage. The big fall is the cordon model — the moment the city stopped being a closed box. The two rises afterwards are not the model getting worse: one is the queue moving outside the measured area where nothing can see it, and the other is a change of instrument.

Read that chart carefully, because the obvious reading is wrong. The two rises near the end are not the model getting worse. The first is the moment the map got bigger and the queue of vehicles waiting to enter moved outside the measured area, where no instrument could see it. The second is a change of instrument, on the weakest of the three suites — the one where only 14 to 49% of each measured journey even happens inside the simulation.

Five things this model still gets wrong

Stated before any result, not after. The biggest one has nothing to do with traffic at all.

The dominant error is not about congestion. With no traffic on it at all, the simulation drives about twice as fast as the real empty city. A road signposted 70 km/h really runs at 22 to 37 km/h when it is empty — because of signals, pedestrian crossings, buses stopping in the lane, cars parked where they should not be, and topes.

Every road in the model matches its map speed limit exactly, so this is not an import bug. It is that a speed limit is not an operating speed, and nothing in OpenStreetMap knows the difference. It cannot be fixed by adding more traffic — I tried — and the calibration proved it cannot be fixed by modelling congestion better either: that number moved, and this one did not budge.

There are four more, and the second-worst is the strangest: a third of the traffic never gets into the network at all. It queues at the edge of the map, where nothing measures it, and contributes no delay to any number in this article. Which sits oddly next to the first error, because a model running twice too fast should have more room, not less.

IMAGEP4

The validation scorecard — the ledger table from VALIDATION.md row 8, or a screenshot of the harness output. Something that shows the model being graded rather than described.

The graveyard of good explanations

Nine plausible causes, killed by measurement. Two of them I had already published as findings before the measurement arrived.

This is the part I would keep if I had to throw the rest away. Not because the answers are interesting — most of them are no — but because the shape of being wrong is the same everywhere, and almost nobody publishes it.

FIG 9Nine good explanations, all deadINTERACTIVE

MEASURED · 25% more demand on a 60% bigger network produced 6% more traffic. Achieved inflow actually FELL, 56.6% to 52.7%

SO · the constraint was how much traffic could physically get in, not how much was asked for

Every one of these was plausible, several were mine, and two had already been published before the measurement that killed them. They are listed because a project that only reports its successes is not a project, it is an advertisement.Open any one.

The two marked retracted had already been written up as findings. One of them, journey times are within 8% of reality, survived exactly one day. It was the best number this project ever produced and it was an artefact: one group of corridors was running at half the real time, another at nearly double, and averaging them landed on 1.08. That looks like accuracy. It is two large errors crossing.

That is why nothing in this article quotes an average of those two groups, ever. They are reported separately or not at all.

Thirteen policies

Same city, same journeys, same random seed. One thing changed each time — which is the experiment no real city can run.

Now the point of building any of this. Closing a boulevard for two hours to see what happens costs the city a morning. In the model it costs a line of configuration and a few hours of computing, and you can do it again with the closure moved three hundred metres to see whether that matters.

More importantly, you can run the identical morning twice. Same streets, same 182,531 journeys, same random seed, with exactly one thing different. No real city can offer that, because no real city gets to have Tuesday twice.

FIG 10Thirteen policies, one morning eachINTERACTIVE
Staggered hoursNOT STRICTLY CONTROLLED

staggered work and school hours -- same trips, different departure times

baselinethis policychange
mean speed21.0 km/h21.9 km/h+4.3%
mean trip time108.9 min101.2 min-7.1%
mean time lost5590 s5148 s-7.9%
trips completed122652123023+0.3%
trips >2x free flow70.6%68.9%-2.4%

NOT strictly controlled, and deliberately so: a demand-side scenario means a different route file, which is the whole question. Everything else is pinned -- same gtcity6 network, same 07:00-12:00 clock, same seed 42, same vtypes, same signal override, same shared ambient background traffic. The demand is the only thing that moves.

Every one of these ran the same 182,531 journeys on the same streets with the same random seed, changing exactly one thing. That is the part no real city can offer: the identical morning, twice.Pick one.
VIDEOP6

Screen recording of a scenario actually running — vehicles moving on the real map in the dashboard. This is the payoff shot for the whole article; twenty to forty seconds, ideally showing a corridor filling up.

Where a policy needs a human response — how many people actually stop driving when you charge them — I did not invent the number. Those come from evaluations in London, Stockholm, Milan and Bogotá, and because a number from London is not a number from Guatemala, each one runs twice: a pessimistic response and an optimistic one. The range is the honest answer.

Correcting it meant dropping that peak-hour fraction from 0.095 to 0.0731. The result: 182,586 journeys against the baseline's 182,531 — a difference of 0.03%. That scenario moves departure times and nothing else, which is exactly what I wanted to measure.

The results, and the trap in reading them

Judge a policy on one axis and you can always win by deleting people from the map. It takes two axes to tell an improvement from a subtraction.

The best-looking result in the whole set is the strong congestion charge: 14.3% faster, 20.5% less time per journey. If I stopped there, that is a headline.

It also completed 9.9% fewer journeys. Those trips did not get faster. They stopped happening. Somebody did not go to work, or to the clinic, or to see their mother.

If you measure only speed, you win by removing people from the road. Every policy in this table can be made to look good that way.

So everything gets two axes at once: how many journeys got delivered, and what each journey cost. Only more-journeys-and-less-delay counts as an improvement.

FIG 11Every policy, on both axes at onceINTERACTIVE
fewer journeysmore journeysless delaymore delay

The diamond in the middle is the untouched morning. The shaded strip is the measured noise floor: ±0.63%, established by a crash scenario that finished with more journeys than the baseline it was sabotaging.

Rightwards is more journeys delivered. Upwards is less delay on each one. Only the top-right quadrant is an improvement — and nothing is in it. The two highlighted points sitting on the centre line deliver the same journeys with less delay, which is the closest thing here to something for nothing.Tap or hover any point.

Nothing lands there. Not one of the thirteen. What two of them do is deliver the same journeys with measurably less delay — staggered hours, which saves about 14,500 vehicle-hours in a single morning and asks nobody to give up a trip, and repairing the traffic signals, which costs nothing at all and is the only intervention here that requires no behaviour change from anyone.

Two more things that should deflate the table. Combining policies did not compound: the charge alone gave 14.3%, and the charge plus staggered hours plus a bus shift gave 15.7%. Three levers bought 1.4 points more than the best single one.

And every demand-cutting scenario is partly flattered by the model's own defect. When there is less traffic, more of it fits — the share that gets in rises from 65.9% to 74.3%. So some of that 'improvement' is vehicles that previously could not enter the map and now can, rather than streets that got emptier.

Doing nothing is the expensive option

Guatemala's vehicle fleet grew 8.8% in one year. Two years of that costs more than any policy here buys back.

Every scenario so far needed somebody to decide something. This one is the default. Guatemala's registered fleet grew 8.8% in a single year — over six million vehicles — while the road network grows around 1.4% a year.

FIG 12The cost of doing nothingINTERACTIVE
109 minaverage journey
21.0km/h average
37,380vehicles that got stuck
Two years of Guatemala's actual vehicle growth, with every other thing in the model held identical. Nobody has to vote for this one — it is what happens by default.

Eighteen percent more vehicles, nothing else changed. It is the largest single effect in this entire article, larger than any policy I tested, and it is the one that happens if nobody does anything at all.

The ring road

There is one ring and it is half-built. The other half already exists under nine different street names, and closing it needs 3.1 kilometres.

Somebody asked whether a complete ring road would help, which meant first counting how many the city has. The answer is one, and it does not go all the way round: the Anillo Periférico covers about 150 degrees of arc, on the north and the west. Both its ends simply stop being called Periférico and carry on as some other street.

Which is the interesting bit — because they carry on as the streets that complete the circle. Martí, La Paz, Austríaco, Landívar, Vista Hermosa, Próceres, Las Américas, Hincapié, Atanasio Tzul. Nine names, one ring, and almost all of them already touch.

FIG 13The ring that is nearly thereINTERACTIVE
THE CITY
Anillo Periférico — existsthe same route, nine other namesthe two gaps
ZONA 6 · 1,874 m

Calle Marti → Calzada La Paz

ZONA 13 · 1,265 m

Avenida Hincapie → Via Alterna del Sur — routed around La Aurora airport, not across the runway

Measured on the routes themselves: 71.2% of the morning's journeys already run along this circle, though only 14.9% touch the stretch that is actually called Periférico.

The Anillo Periférico is 185 road segments covering about 150° of arc — the north and the west, and nothing else. The rest of the circle already exists under nine other street names. Closing it needs 3.1 km of new road, in two pieces.Schematic, not a map. The measurements are real; the drawing is a diagram.

So closing the ring is not forty kilometres of construction. It is 3.1 kilometres, in two pieces. I built them into the model and re-routed the same 182,531 journeys. The result is not the one people expect: 1,413 more journeys completed, routes 2.3% shorter — and every journey taking 2.5% longer.

The ring buys capacity, not speed. It lets more people into a network that was already full, and those extra vehicles cost everybody else a little time. Whether that is worth building depends entirely on which of the two you were trying to buy — and that is a question the simulation can inform and cannot answer.

Fourteen cities at the same time

Thirteen simulations run one after another is most of a week. Run in parallel on rented machines it was about $35, and two mistakes of mine.

One simulation of the whole city takes four to five hours. Thirteen of them, one after another, is most of a working week — and if you find a mistake in scenario three, you start again.

So I ran them all at once, on rented machines of the cheap kind that can be taken away from you mid-run. The whole experiment cost about $35. Less than filling a tank.

FIG 14Fourteen cities at the same timeLOOPING
morning-rush
charge-pess
charge-opt
staggered
remote-05
remote-10
remote-15
growth-2yr
pkgA-pess
pkgA-opt
pkgB-ops
ring-complete
accident
incident-fast
32 GiB of memory per job

roughly 16x oversized. No instance in the chosen family fits two such jobs, so Batch placed one per 16-vCPU machine and a 14-job matrix needed 224 vCPU

the queue wedged with 13 of 14 jobs waiting

then 7 GiB, which was too little

a job was killed for running out of memory nine and a half hours in, 55% done. The run report said peak memory 5.1 GiB, comfortably under the cap -- but that number is the largest SINGLE process, and python and its SUMO child are two processes sharing one limit

reads about 35% low. The real requirement is around 8 GiB; the setting is now 12

a 10-hour timeout left in place after the jobs got slower

the timeout was sized when jobs had 8 processors and took about 5 hours. The jobs were later cut to 2 processors to fit an account quota, which pushed them to about 11 hours. The timeout was not revisited

five jobs killed at 85-90% complete, ten hours of compute each

both failures were the same mistake: a parameter changed without checking what else depended on it

One simulation of the whole city takes four to five hours. Thirteen of them one after another is most of a week. Run in parallel on rented machines that can be taken away mid-run, the whole experiment cost about $35 — less than filling a tank.

I am including the failures because they are the same failure twice, and it is the most ordinary mistake in engineering: I changed one number and did not check what else depended on it. Both times it cost ten hours of computing that had to be thrown away.

IMAGEP7

The AWS Batch console with the fourteen jobs running in parallel, or the watch-runs.sh output mid-matrix. Whatever shows fourteen things happening at once most legibly.

What this cannot tell you

It compares futures against each other. It does not predict any of them.

Every number in this article is a difference between two simulations, not a forecast. When I write that staggered hours cut delay by 7.9%, that is 7.9% in the model — a model that is twice too fast on an empty road and cannot get a third of its traffic through the front door.

What a model like this is good for is direction and ranking. A simulation that is uniformly too fast can still tell you which of two interventions helps more, and where, and who pays for it. It cannot tell you that your commute will be eleven minutes shorter, and I have tried hard not to write a sentence that implies it will.

Two numbers this whole thing leans on have never been measured: the fraction of the day that lands in the morning peak, and the share of the fleet that is motorcycles. Real signal timings and real classified counts from the city's traffic authority would replace both, and are the only route from a model that illustrates to a model that predicts.

What I am confident about is smaller and, I think, more useful. The city's problem is not internal circulation, it is a million vehicles a day crossing its edge. Nothing I tested moves more people faster. Two things move the same people with less time lost, and both are cheap. And doing nothing for two years costs more than any of them buy back.

IMAGEP5

The live dashboard mid-run — map, statistics and the scenario controls together. It shows that this is a thing somebody can drive, not a batch job that emits a table.

The model is wrong in five documented ways and I have retracted two of my own findings in public. That is not a disclaimer at the end. It is the only reason to trust anything above it.