A closed-population simulation demonstrating the behavior of CONWIP (Constant Work-In-Process) systems.
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).
The model implements a fixed-population loop:
Pallet puck (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.Shop struct (lines 34-50): Manages a fixed array of sim.Facility resources and collects system-wide statistics like total cycles completed and average cycle time.Pallet.Entering state (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.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.
./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
odin run examples/widget_assembly