A bonding cell
Every optional mechanism at once: people with shifts and breaks, material that runs out, a batch oven, unplanned breakdowns and planned maintenance. This is what a real model looks like.
Any unfamiliar word is in the glossary.
The plant
Panels are prepared by hand at one of two benches, then cured in an oven.
- Prep takes about 15 minutes and needs one of two technicians to load and unload it. The technicians take a half-hour break every eight hours.
- Prep consumes adhesive. The store holds 400 units, reorders 280 when it drops below 120, and the supplier takes two hours.
- The oven cures a load for three hours. It holds between 30 and 60 panels, so it waits for a load to build. It breaks down roughly every two days for about ninety minutes, and gets a ninety-minute planned service every week.
- Panels arrive as lots of 10 every 45 minutes or so.
How the real world maps to the model
How the model interacts
A panel must clear three separate constraints at prep, and a fourth at the oven.
Panels waiting for a load to form are not queueing for the oven — the oven may be free. They
are waiting for each other. That is why the oven's mean_queue reads 0.00 below while
panels are demonstrably waiting: the waiting is in the pool, and it shows up in cycle time.
The model
# yaml-language-server: $schema=https://clockwright.dev/schema/model.json
name: "bonding cell"
horizon_seconds: 2592000.0
warmup_seconds: 259200.0
operators:
- id: technicians
count: 2
breaks:
- name: lunch
every_seconds: 28800.0
duration_seconds: { kind: constant, value: 1800 }
first_at_seconds: 14400.0
consumables:
- id: adhesive
capacity: 400.0
initial: 400.0
reorder_at: 120.0
reorder_quantity: 280.0
lead_time_seconds: { kind: constant, value: 7200 }
resources:
- { id: prep, servers: 2, load_seconds: 60.0, unload_seconds: 60.0 }
- id: oven
servers: 1
downtime:
breakdown:
mttf_seconds: { kind: exponential, mean: 172800.0 }
mttr_seconds: { kind: exponential, mean: 5400.0 }
maintenance:
- name: weekly
trigger: { kind: calendar, every_seconds: 604800.0 }
duration_seconds: { kind: constant, value: 5400 }
first_at_seconds: 302400.0
products:
- id: panel
pieces: 10
steps:
- resource: prep
process_seconds: { kind: erlang, k: 3, mean: 900.0 }
operator:
attendance: { load: 1.0, unload: 1.0 }
pool: technicians
consumes:
- consumable: adhesive
quantity: { kind: constant, value: 4.0 }
- resource: oven
process_seconds: { kind: constant, value: 10800 }
batch: { min_pieces: 30, max_pieces: 60, group: cure }
releases:
- product: panel
interarrival_seconds: { kind: exponential, mean: 2700.0 }
What comes out
{
"released": 943,
"completed": 941,
"cycle_seconds_mean": 19350.9,
"resources": {
"prep": {
"utilisation_pct": 18.69, "mean_queue": 0.01,
"mean_wait_seconds": 34.86, "served": 943,
"state_pct": { "Idle": 81.3, "Load": 1.1, "Processing": 16.5, "Unload": 1.1 }
},
"oven": {
"utilisation_pct": 93.15, "mean_queue": 0.0,
"mean_wait_seconds": 0.0, "served": 213,
"state_pct": { "Idle": 6.5, "Processing": 88.8,
"ScheduledDown": 1.0, "UnscheduledDown": 3.6 }
}
},
"consumables": {
"adhesive": {
"mean_level": 249.82, "min_level": 104.0, "final_level": 156.0,
"stockouts": 0, "mean_wait_seconds": 0.0,
"total_taken": 3772.0, "total_replenished": 3640.0, "waiting_at_end": 0
}
},
"batch_entries": 941,
"batch_wait_seconds_total": 6483178.0
}
Three things in this document are worth pointing at, because they only appear once a model uses the optional mechanisms. Every field is explained in Reading the results.
The oven served only 213. The other resources served ~941. That is not an error: the oven
processes batches, so it performed 213 firings to cure 941 panels. A resource that batches
counts loads, not entities.
The oven's mean_queue is 0.00, and panels still wait for hours. Waiting for a batch to
form is not queueing for the machine — the panels are in the batch pool, not in the oven's
queue. batch_wait_seconds_total is where that time is: 6,483,178 seconds over 941 panels,
about 1.9 hours each, which is most of the 5.4-hour cycle time. If you only read mean_queue
you would conclude nothing ever waits.
The adhesive never ran short (stockouts: 0) and got down to 104 units of a 400-unit store.
Note total_taken exceeds total_replenished — 3,772 against 3,640 — which is simply the
store drawing down stock it already held. The identity that must hold is:
stock when measuring started + total_replenished - total_taken == final_level
"When measuring started" means the end of the warm-up, not the initial
level you declared. This store declares 400 units, and 400 + 3640 - 3772 is 268, not the 156
reported — because warm-up resets the counters without resetting the stock. Working back,
the store held 288 units when measuring began.
This is worth knowing because it is an easy way to convince yourself of a leak that is not there. If you want to check conservation, take the baseline from the moment the accounting started, never from the model document.
What it says
ok: bonding cell (2 resources, 1 products, 1 releases)
model_hash: c86779b82c2a7f18
| busy | down | mean queue | |
|---|---|---|---|
| prep | 16.5% | — | 0.01 |
| oven | 88.8% | 4.6% | 0.00 |
- released / completed: 943 / 941
- mean cycle time: 5.38 hours
One machine is the plant
Prep is idle five sixths of the time. Two technicians are ample, the adhesive never ran short, and neither is worth another thought. Everything is decided by one oven that is busy 89% of the time and out of service for another 5%.
That is a useful answer and it is not the obvious one: the cell has four separate constraints modelled, and three of them turn out not to bind. Adding a third prep bench or a third technician would change nothing measurable, and that is the sort of thing worth knowing before the money is spent.
The oven is at 88.8% busy. Add the downtime and it is effectively unavailable 93% of the time it could be working — and at that load the queue is extremely sensitive to any further loss. A breakdown that gets slightly worse, or a service that runs slightly long, moves cycle time far more than the raw percentage suggests.
A second oven is the obvious move and probably the expensive one. Cheaper questions first:
does a larger minimum batch help or hurt? Does moving the weekly service to a quieter
hour matter? cwright compare prices each of them against the same
baseline, and says when it cannot tell the difference.