Widget Assembly: model notes
Widget Assembly Line
A closed-population simulation demonstrating the behavior of CONWIP (Constant Work-In-Process) systems.
Problem
A set of \(K\) pallets circulates indefinitely through three sequential stations: load, machine, and unload. Each station is a single-server resource. The "machine" station is the system's bottleneck (longest service time). The primary question is how the system's throughput and cycle time vary as the number of pallets \(K\) increases. This illustrates the principle that there is a "knee" in the throughput curve: once the bottleneck is saturated, adding more pallets only increases the average cycle time without increasing output.
This is based on the closed-population exercise in Schriber-GPSS/H and is a standard demonstration of the concepts discussed in Factory Physics (Hopp & Spearman).
Model in this directory
The model implements a fixed-population loop:
Palletpuck (lines 49-73): These pucks never terminate. They transition through a sequence of stations (index 0 \(\rightarrow\) 1 \(\rightarrow\) 2 \(\rightarrow\) 0), seizing and releasing the corresponding facility at each step.Shopstruct (lines 34-50): Manages a fixed array ofsim.Facilityresources and collects system-wide statistics like total cycles completed and average cycle time.Pallet.Enteringstate (lines 84-101): Implements a "try-then-wait" pattern. If a pallet cannot immediately seize a station, it joins the facility's wait list and suspends.
Why this shape
The la-closed population is modeled by initializing exactly \(K\) pucks at simulation start (lines 143-152) and ensuring that no puck ever calls eng.terminate. This creates a permanent conservation of "entities" in the system.
The warmup_end parameter (line 41) is used to model the effect of GPSS CLEAR. Instead of resetting the simulation state, the model simply ignores statistics collected before this time. This allows the system to reach a steady state before metrics are recorded, which is essential for closed-population models where the initial "filling" of the line creates a transient bias.
Alternatives considered
- Open Population: An open system (where pallets arrive and leave) would have different dynamics and would not naturally demonstrate the CONWIP saturation point.
- Coordinate-Based Movement: One could have used a single "Pallet Manager" to move pucks, but the current decentralized approach—where each pallet independently seeks the next station—is more idiomatic to the puck pattern.
What this example teaches
- Closed-Population Modeling: How to simulate a fixed number of entities circulating in a system.
- Bottleneck Analysis: Identifying the saturation point (the "knee") of a production line.
- CONWIP Principles: Demonstrating the relationship between Work-In-Process (WIP), throughput, and cycle time (Little's Law).
- Steady-State Analysis: Implementing a warmup period to exclude transient data.
CLI
./widget_assembly [options]
Add --json to emit the uniform envelope (metadata, execution_stats,
metrics, details) instead of the default text output.
| Flag | Type | Default | Description |
|---|---|---|---|
--k |
int | 6 |
Number of circulating pallets (WIP level). |
--end |
float | 2880 |
End time in minutes (48 hours). |
--warmup |
float | 120 |
Warmup cutoff in minutes; stats before this are ignored. |
--seed |
int | 42 |
RNG seed. |
--sweep |
bool | false |
Run K=1..12 sweep instead of a single K. |
--json |
bool | false |
Emit uniform JSON envelope instead of text. |
With --sweep, the model ignores --k and runs K = 1, 2, ..., 12 in
sequence so the output shows the throughput-vs-WIP curve and its
saturation knee.
Example runs:
./widget_assembly # default text run (K=6)
./widget_assembly --json # uniform envelope
./widget_assembly --k=4 --end=480 --warmup=60 --json # custom params
./widget_assembly --sweep --json # K=1..12 sweep as JSON
Running it
odin run examples/widget_assembly
See also
- Factory Physics (Hopp & Spearman).
- Schriber-GPSS/H (closed-population / CLEAR chapter).