Theme
Guards, Inhibitors, and Capacity
Real systems operate under conditional rules and physical constraints: waiting rooms have limited seats, connection pools reject calls when exhausted, and batch tasks wait for prerequisites. Jupiter provides three mechanisms to model these constraints: guard conditions, inhibitor arcs, and capacity places.
Guard Conditions
A guard is a boolean expression evaluated on a transition. In addition to requiring sufficient tokens across all input places, the transition's guard expression must evaluate to true for the transition to fire.
Syntax
Guards inspect the instantaneous token count of places:
#Queue < 3
#Slots > 0
#IdleServers = 0The # prefix followed by the place name (#PlaceName) retrieves the exact number of tokens in that place at the moment of evaluation.
Available Operators
| Type | Operators | Meaning |
|---|---|---|
| Comparison | >, >=, <, <=, =, != | Greater, greater/equal, less, less/equal, equal, not equal |
| Logical | AND, OR, NOT | Conjunction, disjunction, and negation |
Group expressions using parentheses:
(#Queue > 0) AND (#Worker = 0)
(#Slots > 0) AND NOT (#Maintenance > 0)
#Queue < MAX_CAPACITYWhen to use guards
- Multi-place conditions: When decisions depend on multiple places at once (
(#Queue > 0) AND (#PowerOk > 0)). - Batch processing thresholds: Transitions that only fire when a minimum batch size is met (
#Queue >= BATCH_SIZE). - Load-based routing: Splitting flows dynamically:
- Transition A:
#Queue > 10(overflow path) - Transition B:
#Queue <= 10(normal path)
- Transition A:
Avoid logical gaps
When splitting flows with guards, ensure all possible values are covered. If Transition A checks #Queue > 10 and Transition B checks #Queue < 10, the exact case #Queue = 10 has no valid path, permanently freezing tokens.
Inhibitor Arcs
An inhibitor arc prevents a transition from firing as long as the connected place holds tokens.
- Visual symbol: A line terminating with a small circle (bubble) instead of an arrow.
- Zero token consumption: It checks the presence of tokens without consuming them when the transition fires.
Multiplicity on Inhibitors
With multiplicity 1 (default), the inhibitor blocks firing if the place has 1 or more tokens.
With multiplicity $N$, it blocks firing only when the place holds $N$ or more tokens.
Inhibitor vs. Guard
For basic thresholds, both approaches produce identical simulation results:
| Approach | Implementation | Mean queue | Throughput |
|---|---|---|---|
| Inhibitor arc (weight 3) | Visual arc with circle | 0.355 | 0.0968 |
Guard #Queue < 3 | Expression inside transition | 0.355 | 0.0968 |
Prefer inhibitor arcs for simple rules
Inhibitor arcs are directly visible on the canvas diagram. Anyone reviewing the model immediately understands the constraint. Guards remain hidden inside transition properties and can easily be overlooked.
Limiting Capacity with Slots
Real queues have finite capacity: 20 buffer slots, 50 seats, or 10 database connections.
The "One Token Per Slot" Pattern
To constrain capacity, add a place (e.g., Slots) initialized with the total capacity limit:
- The arrival transition consumes 1 token from
Slots. - The completion transition returns 1 token to
Slots. - When
Slotsreaches 0, the arrival transition loses its enabling condition and cannot fire until an item finishes and departs.
json
{
"modelName": "Fila com sala de espera limitada",
"definitions": [
{ "name": "TEMPO_ENTRE_CHEGADAS", "type": "DOUBLE", "value": "10" },
{ "name": "TEMPO_DE_ATENDIMENTO", "type": "DOUBLE", "value": "5" },
{ "name": "VAGAS", "type": "INTEGER", "value": "3" }
],
"places": [
{ "name": "Vagas", "initialMarking": 3, "stringMarking": "VAGAS", "initialStringMarking": "VAGAS" },
{ "name": "Fila", "initialMarking": 0, "stringMarking": "0", "initialStringMarking": "0" },
{ "name": "Atendente", "initialMarking": 1, "stringMarking": "1", "initialStringMarking": "1" },
{ "name": "EmAtendimento", "initialMarking": 0, "stringMarking": "0", "initialStringMarking": "0" }
],
"rewardMeasures": [
{ "name": "cheio_pct", "expression": "P{#Vagas = 0} * 100" },
{ "name": "vazao", "expression": "E{#EmAtendimento} / TEMPO_DE_ATENDIMENTO" },
{ "name": "no_sistema", "expression": "E{#Fila} + E{#EmAtendimento}" },
{ "name": "vagas_livres","expression": "E{#Vagas}" }
],
"transitions": [
{ "name": "Chegada", "type": "TIMED", "firingPolicy": "SINGLE_SERVER",
"raceType": "RACE_WITH_ENABLING_MEMORY", "priority": 1,
"distribution": { "type": "exponential", "parameters": { "Mean delay": "TEMPO_ENTRE_CHEGADAS" } },
"inputArcs": [{ "type": "INPUT", "place": "Vagas", "multiplicity": 1 }],
"outputArcs": [{ "type": "OUTPUT", "place": "Fila", "multiplicity": 1 }],
"inhibitorArcs": [] },
{ "name": "Inicio", "type": "IMMEDIATE", "firingPolicy": "SINGLE_SERVER",
"raceType": "RACE_WITH_ENABLING_MEMORY", "priority": 1, "weight": 1,
"inputArcs": [{ "type": "INPUT", "place": "Fila", "multiplicity": 1 },
{ "type": "INPUT", "place": "Atendente", "multiplicity": 1 }],
"outputArcs": [{ "type": "OUTPUT", "place": "EmAtendimento", "multiplicity": 1 }],
"inhibitorArcs": [] },
{ "name": "Fim", "type": "TIMED", "firingPolicy": "INFINITY_SERVER",
"raceType": "RACE_WITH_ENABLING_MEMORY", "priority": 1,
"distribution": { "type": "exponential", "parameters": { "Mean delay": "TEMPO_DE_ATENDIMENTO" } },
"inputArcs": [{ "type": "INPUT", "place": "EmAtendimento", "multiplicity": 1 }],
"outputArcs": [{ "type": "OUTPUT", "place": "Atendente", "multiplicity": 1 },
{ "type": "OUTPUT", "place": "Vagas", "multiplicity": 1 }],
"inhibitorArcs": [] }
]
}Exact timing of token return
A capacity token must return to Slots only when an item completes service and leaves the system. Returning it early allows the queue to overflow.
Trade-off Between Response Time and Rejections
Evaluating capacity on a queue with arrivals every 10 minutes and 5-minute service:
| Slots | % Time full | Throughput | Items in system | Response time |
|---|---|---|---|---|
| 1 | 33.4% | 0.067 | 0.33 | 5.0 min |
| 2 | 14.3% | 0.086 | 0.57 | 6.6 min |
| 3 | 6.7% | 0.093 | 0.73 | 7.9 min |
| 5 | 1.6% | 0.098 | 0.91 | 9.3 min |
| 10 | 0.1% | 0.100 | 1.00 | 10.0 min |
Three core insights:
- Low capacity degrades throughput: With only 1 slot, the system rejects 33% of arrivals and throughput drops from 0.10 to 0.067 items/min.
- Diminishing returns: Increasing from 1 to 3 slots drops rejections from 33.4% to 6.7%. Beyond 5 slots, gains are minimal.
- A short queue does not mean good service: With 1 slot, response time is only 5 minutes — but that is because a third of the workload was discarded!
Next steps
Learn how to configure multi-server concurrency in Parallelism and Servers.