This post walks through modeling a coffee shop as a Petri net using go-pflow. The model demonstrates three key concepts: weighted arcs that encode recipes as consumption amounts, conservation laws that guarantee nothing is created or destroyed, and ODE simulation for capacity planning.
Try the interactive demo at pilot.pflow.xyz/coffeeshop.
A coffee shop has limited inventory: beans, water, milk, and cups. Different drinks consume these resources at different rates:
| Drink | Beans | Water | Milk | Cups |
|---|---|---|---|---|
| Espresso | 18g | 30ml | — | 1 |
| Americano | 18g | 200ml | — | 1 |
| Latte | 18g | 30ml | 180ml | 1 |
| Cappuccino | 18g | 30ml | 120ml | 1 |
| Mocha | 18g | 30ml | 150ml | 1 |
Starting inventory: 1,000g beans, 10,000ml water, 5,000ml milk, 100 cups.
Orders arrive continuously. When will we run out? Which drinks should we prioritize? The ODE simulation answers these questions through dynamics rather than explicit rules.
The model has two categories of places. Ingredient places hold current stock:
| Place | Initial | Role |
|---|---|---|
coffee_beans |
1000g | Raw ingredient |
water |
10000ml | Raw ingredient |
milk |
5000ml | Raw ingredient |
cups |
100 | Consumable |
Consumption tracking places count what's been used (all start at 0): beans_used, water_used, milk_used, cups_used. This paired structure creates conservation laws — every token consumed from an ingredient place appears in the corresponding tracking place.
Each drink requires specific quantities. The make_espresso transition has:
coffee_beans (weight 18), water (weight 30), cups (weight 1)beans_used (weight 18), water_used (weight 30), cups_used (weight 1)The arc weights encode recipes directly in the model structure. No separate configuration needed — the Petri net is the specification. The conservation law follows from the structure: coffee_beans + beans_used = 1000 for all time.
For ODE simulation, each transition has a rate constant (drinks per minute):
| Transition | Rate | Drinks/hr |
|---|---|---|
make_espresso |
0.5 | 30 |
make_americano |
0.3 | 18 |
make_latte |
0.8 | 48 (most popular) |
make_cappuccino |
0.4 | 24 |
make_mocha |
0.2 | 12 |
The continuous dynamics follow mass-action kinetics:
flux = rate × ∏[input places]
When input places have tokens, transitions fire proportionally to their rates and available inputs. As resources deplete, rates drop naturally — no explicit scheduling logic needed.
Running the ODE simulation reveals resource depletion trajectories:
The simulation predicts:
The conservation laws hold throughout: coffee_beans + beans_used = 1000 is verifiable from the simulation output at any point in time.
By adjusting rates, we can model different scenarios:
| Scenario | Change | First Bottleneck |
|---|---|---|
| Slow day | Halve all rates | Nothing depletes during a shift |
| Normal | Baseline rates | Cups deplete first |
| Rush hour | Double all rates | Cups deplete in half the time |
| Latte promotion | Triple latte rate | Milk becomes first bottleneck |
Each scenario is the same net with different rate constants. The structure — places, arcs, weights — stays the same. Only the dynamics change.
The full Petri net has ingredient places, tracking places, and one transition per drink type:
Places:
coffee_beans, water, milk, cups — Ingredients
beans_used, water_used, — Consumption tracking
milk_used, cups_used
Transitions:
make_espresso, make_americano, — Drink preparation
make_latte, make_cappuccino,
make_mocha
The bipartite structure is clean: ingredient places connect to transitions (input arcs), and transitions connect to tracking places (output arcs). The conservation laws — one per ingredient — guarantee that nothing is created or destroyed.
| Concept | Coffee Shop Example |
|---|---|
| Conservation laws | beans + beans_used = 1000 for all time |
| Weighted arcs | Recipes (18g beans per espresso) |
| Transition rates | Production speeds (30 espressos/hr) |
| ODE simulation | Resource depletion prediction |
| Bottleneck analysis | Cups deplete first at baseline rates |
The coffee shop model shows how Petri nets naturally encode resource constraints. Weighted arcs specify consumption, conservation laws guarantee integrity, and rates enable continuous simulation.
The ODE doesn't just predict when resources deplete — it reveals why. Changing rates reveals different bottleneck sequences: cups at baseline, milk during a latte promotion. This structural insight guides operational decisions without explicit optimization.
For the theory behind continuous simulation: Declarative Differential Models
This topic is covered in depth in Chapter 5: Resource Modeling of Petri Nets as a Universal Abstraction.