simpy-python
Use for writing, reviewing, debugging, testing, or analyzing Python SimPy discrete-event simulations. Trigger on Environment, Event, Process, timeout, Resource, PriorityResource, PreemptiveResource, C
Files4
View on GitHubSKILL.mdskills/simpy-python/SKILL.md
GitHubSimPy Python
Build reproducible discrete-event models whose clock, state transitions, resource ownership, stopping rule, randomness, and observations represent the real system rather than an accidental execution order.
Boundary and core objects
| Object | Meaning | Use it for |
|---|---|---|
Environment |
Scheduler, simulation clock, and event queue. | One simulated world/replication. |
Event |
A future state that becomes triggered, processed, and optionally carries a value/failure. | Synchronization and completion signals. |
Process |
An event wrapping a generator's lifecycle. | Active entities that yield events. |
Timeout |
An event scheduled after simulated delay. | Duration, not wall-clock sleep. |
Resource family |
Capacity tokens with queues. | Exclusive/shared service and congestion. |
Container |
Homogeneous numeric level. | Bulk inventory or fuel. |
Store |
Queued Python objects. | Items, messages, or jobs with identity. |
A process function is a generator recipe; env.process(generator) registers it
and returns a Process event. yield event suspends simulated activity until
that event is processed. Calling a generator normally does not run the model.
Read the event and process model before mixing
callbacks, interrupts, conditions, or resource requests.
Ordered workflow
- Define the question, time unit, warm-up, horizon/stopping event, and measured population before modeling implementation details.
- Separate domain state from the environment and random-number generator.
- Express every delay, wait, acquisition, release, failure, and cancellation as an event transition.
- Select
Resource,Container, orStorefrom what is constrained—not by superficial queue vocabulary. - Record observations at defined transitions and with clear denominators.
- Run independent replications with controlled seeds; report uncertainty, not one trace, when making stochastic claims.
- Test tiny deterministic timelines, contention, boundary times, interruption, empty/full queues, and the stopping rule.
Decision rules
- Use
Resourcewhen entities compete for interchangeable usage slots. Use awith resource.request() as request: yield requestblock so ownership is released on normal exit or interruption. - Use
PriorityResourcewhen waiting order follows an explicit priority. State whether lower numeric values mean higher priority and define tie behavior. - Use
PreemptiveResourceonly when an admitted user can be interrupted. Handlesimpy.Interrupt, remaining work, cleanup, and measurement bias. - Use
Containerfor an aggregate quantity without item identity. UseStorewhen objects and selection/filtering matter. - Use
env.timeout(duration)for simulated time. Reject negative durations and define whether zero delay is a legitimate same-time event. - Use
env.any_of/event1 | event2for races andall_of/&for barriers. Inspect which events completed; a losing event may still need cancellation or cleanup. - Use
env.run(until=t)knowing events scheduled exactly attare not a vague “through time t” promise; write a boundary test for the chosen stopping form. - Use
RealtimeEnvironmentonly when coupling to wall-clock behavior is an explicit requirement. It is not a way to make simulation more accurate.
Read resources, time, and experiments for ownership, queues, monitoring, randomness, and replication rules.
Canonical anchor
from collections.abc import Generator
from dataclasses import dataclass
import simpy
@dataclass(frozen=True)
class Visit:
customer: int
queued_at: float
started_at: float
finished_at: float
def customer(
env: simpy.Environment,
server: simpy.Resource,
customer_id: int,
service_time: float,
visits: list[Visit],
) -> Generator[simpy.Event, object, None]:
queued_at = env.now
with server.request() as request:
yield request
started_at = env.now
yield env.timeout(service_time)
visits.append(Visit(customer_id, queued_at, started_at, env.now))
The record captures state-transition times rather than sampling a mutable queue. Arrival creation and random service draws belong outside this process so a test can inject a deterministic schedule.
High-risk rules
- Do not mutate state “after a delay” without yielding that delay.
- Do not hold a resource while waiting for unrelated work unless the real entity occupies it during that time.
- Do not manually release a context-managed request twice.
- Never share one mutable RNG implicitly across replications. Derive and record per-replication seeds; common random numbers require an intentional design.
- Avoid global
randomcalls inside processes. Inject distributions or an RNG. - A queue-length time average requires time weighting between changes; averaging observations at arrivals answers a different question.
- Distinguish censored entities still in the system at the horizon from completed entities. Do not silently discard them from denominators.
- Same-time events are ordered by scheduler rules and creation sequence. If the answer must not depend on that order, encode priorities or redesign the event.
- An interrupted process must release resources and decide whether work resumes, restarts, or is lost. Catching and ignoring the interrupt is not a model.
Version grounding and completion
The authoring baseline is official SimPy 4.1.2 stable documentation; the local
foundry has not yet executed the package. Check the installed version with
simpy.__version__ and inspect signatures before relying on exception fields, resource variants, or
environment behavior. Read verification.
Completion requires an explicit clock/stopping contract, correct resource ownership, injected randomness, traceable observations, deterministic micro-tests, stochastic replication checks where relevant, and evidence that the result does not depend accidentally on global state or one seed.