Calendar profiles
A model without a profile runs at one average for ever. Real demand does not: a shop is busier at the weekend, a charging hub peaks morning and evening, a line runs three shifts on weekdays and one on Saturday.
A profile is a named, repeating, piecewise-constant factor over a cycle. Several different things can name one, and the factor means something different to each:
| who names it | key | the factor is |
|---|---|---|
| a release | rate_profile | a multiplier on its arrival rate |
| a step | scale_profile | a multiplier on its process duration |
| a resource or an operator pool | roster_profile | ⚠️ a count of how many are on shift |
| a store's lead time | lead_time_profile | a multiplier on how long an order takes |
⚠️⚠️ The first two are reciprocals of each other, which is why they are separate keys rather
than one profile: 1.6 on a rate means 60% more arrivals, so gaps get shorter, while
1.6 on a duration means takes 60% longer. A single key would be ambiguous on exactly the
field where getting it backwards is easiest.
profiles:
- id: week
cycle_seconds: 604800.0
segments:
- { until_seconds: 432000.0, factor: 1.0 }
- { until_seconds: 604800.0, factor: 2.5 }
Five days at the base rate, then two days at two and a half times it. until_seconds is measured
from the start of the cycle, so the document states boundaries rather than durations.
⭐⭐ What the flat average hides
The point is not that the numbers differ a little. Below are two models of the same shop, with the same mean arrival rate, the same three tills and the same service time. One spreads its arrivals evenly; the other concentrates them into the weekend.
| mean cycle time | completed | |
|---|---|---|
| the flat average | 307.29 s | 76,820 |
| ⚠️ the same demand, profiled | 23,633.75 s | 76,842 |
⭐ Seventy-seven times longer, for the same amount of work. Both runs complete the same number of shoppers — the difference is entirely when they arrive.
The arithmetic says why, and it needs no simulator:
| arrival rate | ρ | |
|---|---|---|
| on average | 0.015873 /s | 0.794 — comfortable |
| ⚠️ at the weekend peak | 0.027778 /s | 1.389 — above 1 |
A stationary model of this shop reports a utilisation of 79% and a queue of a few minutes, and every number in it is correct about the average. ⚠️ The shop still collapses every Saturday, because for two days in seven it is asked for more than it can do, and the queue it builds takes the rest of the week to drain. Sizing the tills on the average answers a question nobody asked.
The two models
The flat average, run with cwright run --replications 12:
# yaml-language-server: $schema=https://clockwright.dev/schema/model.json
name: corner-shop-flat
horizon_seconds: 4838400.0
warmup_seconds: 604800.0
resources:
- { id: till, servers: 3, queue_capacity: 65536 }
products:
- id: shopper
steps:
- resource: till
process_seconds: { kind: exponential, mean: 150.0 }
releases:
- product: shopper
interarrival_seconds: { kind: exponential, mean: 63.0 }
The same demand, profiled, run the same way:
# yaml-language-server: $schema=https://clockwright.dev/schema/model.json
name: corner-shop
horizon_seconds: 4838400.0
warmup_seconds: 604800.0
resources:
- { id: till, servers: 3, queue_capacity: 65536 }
products:
- id: shopper
steps:
- resource: till
process_seconds: { kind: exponential, mean: 150.0 }
releases:
- product: shopper
interarrival_seconds: { kind: exponential, mean: 90.0 }
rate_profile: week
profiles:
- id: week
cycle_seconds: 604800.0
segments:
- { until_seconds: 432000.0, factor: 1.0 }
- { until_seconds: 604800.0, factor: 2.5 }
The flat model's interarrival of 63 s is not a coincidence: it is 90 s divided by the profile's
time-average factor of (5×1.0 + 2×2.5) / 7 = 1.428571, so the two models really do receive the
same number of shoppers.
The fields
⭐ The keys that point at a profile are documented where they live — on
a release, a step, a resource and
an operator pool. One profile may be named by any number of them, in any
sense: a weekly cycle that shuts a line at the weekend and stops releasing into it is one
profiles entry, named twice.