Skip to main content

A petrol station

Four pumps, one kiosk till, and a tank in the ground that a tanker refills when it runs low.

This example is here because the answer is not the one the equipment list suggests, and because it shows a consumable — something the plant spends rather than borrows.

Any unfamiliar word is in the glossary.

A petrol station: cars queue for four pumps, then for one till

The real place, and the model​

Two things are worth pausing on.

Four pumps are one resource, not four. "servers": 4 means one queue feeding four pumps, which is what a forecourt is — you pull into whichever pump is free. It is not four separate queues. The grocery checkout example shows what happens when you get that distinction wrong.

The tank is a consumable, not a resource. Nobody queues for the tank. Fuel leaves it and does not come back. That is the difference between the two ideas: a pump is occupied and then released; fuel is spent and has to be replaced.

What a car does​

The model​

# yaml-language-server: $schema=https://clockwright.dev/schema/model.json
name: "petrol station"
horizon_seconds: 604800.0
warmup_seconds: 86400.0
resources:
- { id: pumps, servers: 4 }
- { id: till, servers: 1 }
consumables:
- id: tank
capacity: 30000.0
initial: 30000.0
reorder_at: 3000.0
reorder_quantity: 25000.0
lead_time_seconds: { kind: erlang, k: 4, mean: 2700.0 }
products:
- id: car
pieces: 1
steps:
- resource: pumps
process_seconds: { kind: erlang, k: 3, mean: 240.0 }
consumes:
- consumable: tank
quantity: { kind: erlang, k: 4, mean: 45.0 }
- resource: till
process_seconds: { kind: erlang, k: 2, mean: 75.0 }
releases:
- product: car
interarrival_seconds: { kind: exponential, mean: 90.0 }

A week of trading, with the first day discarded as warm-up. Cars arrive on average every 90 seconds — 40 an hour. Filling takes about four minutes and draws about 45 litres; paying takes about 75 seconds.

The tank holds 30,000 litres. When it falls to 3,000 — 10% of the tank — the station orders 25,000 litres, and the tanker takes about 45 minutes to arrive.

note

Every duration is a distribution, not a fixed number. erlang with k: 3 means "about four minutes, and genuinely variable, but not wildly so". Using a fixed four minutes would remove all the queueing, because queues are caused by variability as much as by load.

Running it​

cwright validate petrol-station.yaml
cwright simulate petrol-station.yaml --replication 0

What comes out​

cwright simulate petrol-station.yaml --replication 0
{
"released": 6651,
"completed": 6640,
"cycle_seconds_mean": 609.79,
"resources": {
"pumps": {
"utilisation_pct": 65.34,
"mean_queue": 0.47,
"mean_wait_seconds": 42.93,
"served": 6651
},
"till": {
"utilisation_pct": 83.3,
"mean_queue": 2.8,
"mean_wait_seconds": 253.62,
"served": 6640
}
},
"consumables": {
"tank": {
"mean_level": 14003.79,
"min_level": 570.06,
"final_level": 13572.39,
"stockouts": 0,
"total_taken": 298279.25,
"total_replenished": 300000.0
}
}
}
busyqueuewait
pumps (4)65.3%0.470.7 min
till (1)83.3%2.804.2 min
  • 6,640 cars served out of 6,651 released — the eleven missing are still on the forecourt when the week ends, which is exactly what it should look like.
  • Mean visit: 10.2 minutes, of which about 5.3 is actually filling up and paying.

What it says​

The till is the constraint, not the pumps​

A customer spends four times longer queueing for the kiosk than for a pump. The four pumps are comfortable at 65% and hardly ever have anyone waiting. The single till is at 83% with nearly three people in the queue.

This is the useful answer, and it is not the obvious one. The pumps are the expensive equipment, they are what the site is built around, and they are fine. A second till is cheap and would halve the total visit time. Nobody would have got there from the equipment list, because the constraint is the smallest, cheapest thing on the forecourt.

The tank looks comfortable and is not​

Here is the tank's week:

average stock14,004 L
stock at the end of the week13,572 L
lowest it ever got570 L
times a car had to wait for fuel0

Neither the average nor the final level tells you anything useful. Both are around 14,000 litres and both sound comfortable. The tank got down to 570 litres — under 2% of capacity. The station never ran dry, but the margin was one busy hour, not the 3,000 litres the reorder point implies.

The reason is the 45-minute delivery. When the tank hits 3,000 litres the tanker is still three quarters of an hour away, and the station keeps selling the whole time.

So how low can the reorder point go?​

This is the kind of question a simulation answers and arithmetic does not. Same model, only reorder_at changed, three replications each:

call the tanker atlowest stock reachedcars made to waitmean wait for fuel
20% of the tank3,616 L0—
10% of the tank570 L0—
5% of the tank7 L344.0 s
2% of the tank0 L20132.8 s

10% is the last setting that works, and it works by 570 litres. At 5% the station runs essentially dry — seven litres — and starts turning customers into waiting customers. There is a cliff between the two, and it is not where a rule of thumb would put it.

Notice also that the 5% row is the dangerous one to read casually: 34 cars out of 6,640 is half a percent, and the average wait is four seconds. Those numbers look like nothing. What they actually describe is a forecourt that hit empty.

Things worth trying​

  • Add a second till ("servers": 2 on till) and watch where the constraint moves.
  • Make the tanker slower (mean: 5400.0) and see whether 10% still holds.
  • Set "kind": "constant" on the pump time and see how much of the queueing was variability rather than load.

Use cwright compare rather than eyeballing two runs — see Reading the results for why, and what --indifference is for.

  • Consumables — every option on a store
  • Resources — servers, and what one queue feeding four pumps means
  • A grocery checkout — the same servers idea, where getting it wrong costs a factor of two and a half