simpy · diff

git:20260611.1b8fae3 to v1.1

211 added, 354 removed. Audit A to A.

---
name: simpy
- description: Process-based discrete-event simulation framework in Python. Use this skill when building simulations of systems with processes, queues, resources, and time-based events such as manufacturing systems, service operations, network traffic, logistics, or any system where entities interact with shared resources over time.
- license: MIT license
- metadata: {"version": "1.0", "skill-author": "K-Dense Inc."}
+ description: Build, inspect, test, and analyze bounded process-based discrete-event simulations with SimPy, including events, resources, interrupts, monitoring, replications, warm-up, and reproducible output analysis.
+ license: MIT
+ compatibility: Upstream SimPy 4.1.2 supports Python 3.8+; bundled CLIs require Python 3.10+, uv, and SimPy 4.1.2. They use only SimPy and the standard library, operate on local bounded inputs, and make no network calls.
+ allowed-tools: Read, Write, Edit, Bash, Glob
+ metadata:
+ version: "1.1"
+ skill-author: K-Dense Inc.
---
- # SimPy - Discrete-Event Simulation
-
- ## Overview
-
- SimPy is a process-based discrete-event simulation framework based on standard Python. Use SimPy to model systems where entities (customers, vehicles, packets, etc.) interact with each other and compete for shared resources (servers, machines, bandwidth, etc.) over time.
-
- **Core capabilities:**
- - Process modeling using Python generator functions
- - Shared resource management (servers, containers, stores)
- - Event-driven scheduling and synchronization
- - Real-time simulations synchronized with wall-clock time
- - Comprehensive monitoring and data collection
-
- ## When to Use This Skill
-
- Use the SimPy skill when:
-
- 1. **Modeling discrete-event systems** - Systems where events occur at irregular intervals
- 2. **Resource contention** - Entities compete for limited resources (servers, machines, staff)
- 3. **Queue analysis** - Studying waiting lines, service times, and throughput
- 4. **Process optimization** - Analyzing manufacturing, logistics, or service processes
- 5. **Network simulation** - Packet routing, bandwidth allocation, latency analysis
- 6. **Capacity planning** - Determining optimal resource levels for desired performance
- 7. **System validation** - Testing system behavior before implementation
-
- **Not suitable for:**
- - Continuous simulations with fixed time steps (consider SciPy ODE solvers)
- - Independent processes without resource sharing
- - Pure mathematical optimization (consider SciPy optimize)
-
- ## Quick Start
-
- ### Basic Simulation Structure
-
- ```python
- import simpy
-
- def process(env, name):
- """A simple process that waits and prints."""
- print(f'{name} starting at {env.now}')
- yield env.timeout(5)
- print(f'{name} finishing at {env.now}')
-
- # Create environment
- env = simpy.Environment()
-
- # Start processes
- env.process(process(env, 'Process 1'))
- env.process(process(env, 'Process 2'))
-
- # Run simulation
- env.run(until=10)
- ```
-
- ### Resource Usage Pattern
-
- ```python
- import simpy
-
- def customer(env, name, resource):
- """Customer requests resource, uses it, then releases."""
- with resource.request() as req:
- yield req # Wait for resource
- print(f'{name} got resource at {env.now}')
- yield env.timeout(3) # Use resource
- print(f'{name} released resource at {env.now}')
-
- env = simpy.Environment()
- server = simpy.Resource(env, capacity=1)
-
- env.process(customer(env, 'Customer 1', server))
- env.process(customer(env, 'Customer 2', server))
- env.run()
- ```
-
- ## Core Concepts
-
- ### 1. Environment
-
- The simulation environment manages time and schedules events.
-
- ```python
- import simpy
-
- # Standard environment (runs as fast as possible)
- env = simpy.Environment(initial_time=0)
-
- # Real-time environment (synchronized with wall-clock)
- import simpy.rt
- env_rt = simpy.rt.RealtimeEnvironment(factor=1.0)
-
- # Run simulation
- env.run(until=100) # Run until time 100
- env.run() # Run until no events remain
- ```
+ # SimPy
- ### 2. Processes
+ ## Scope
- Processes are defined using Python generator functions (functions with `yield` statements).
+ Use this skill for process-based discrete-event models where active entities yield
+ events and contend for resources: queues, production systems, logistics, networks,
+ service operations, inventory, and other event-driven systems.
- ```python
- def my_process(env, param1, param2):
- """Process that yields events to pause execution."""
- print(f'Starting at {env.now}')
+ SimPy supplies an event scheduler and modeling primitives. It does **not** choose a
+ scientifically valid conceptual model, input distribution, warm-up, run length,
+ replication count, estimand, or causal interpretation. Treat those as simulation-study
+ methodology, not SimPy API behavior.
- # Wait for time to pass
- yield env.timeout(5)
+ ## Current release and installation
- print(f'Resumed at {env.now}')
+ Verified **2026-07-23**:
- # Wait for another event
- yield env.timeout(3)
+ - Latest stable: **SimPy 4.1.2**, released on PyPI 2026-05-24; source tag
+ `4.1.2` points to commit `f4381649`.
+ - Package metadata requires Python **>=3.8** and classifies CPython 3.8-3.14
+ plus PyPy. SimPy has no runtime dependencies.
+ - 4.1.2 adds Python 3.13/3.14 support and modern-interpreter test fixes.
+ - Upstream and this skill are MIT-licensed.
- print(f'Done at {env.now}')
- return 'result'
+ Create a reproducible environment:
- # Start the process
- env.process(my_process(env, 'value1', 'value2'))
+ ```bash
+ uv venv --python 3.13
+ source .venv/bin/activate
+ uv pip install "simpy==4.1.2"
+ python -c "import importlib.metadata; print(importlib.metadata.version('simpy'))"
```
- ### 3. Events
-
- Events are the fundamental mechanism for process synchronization. Processes yield events and resume when those events are triggered.
-
- **Common event types:**
- - `env.timeout(delay)` - Wait for time to pass
- - `resource.request()` - Request a resource
- - `env.event()` - Create a custom event
- - `env.process(func())` - Process as an event
- - `event1 & event2` - Wait for all events (AllOf)
- - `event1 | event2` - Wait for any event (AnyOf)
-
- ## Resources
+ Do not silently substitute the `latest` documentation build: it may describe an
+ unreleased development revision. Use the versioned 4.1.2 links in
+ `references/sources.md`.
- SimPy provides several resource types for different scenarios. For comprehensive details, see `references/resources.md`.
+ ## Model workflow
- ### Resource Types Summary
+ 1. **Define purpose and estimands.** State the decision/question, system boundary,
+ entities, resources, state, outputs, time units, and terminating event or
+ steady-state target.
+ 2. **Write a conceptual model first.** Record assumptions, distributions,
+ routing, priorities, initial conditions, and omitted mechanisms.
+ 3. **Implement generators.** A SimPy process is an event-yielding Python generator.
+ Register the generator object with `env.process(...)`.
+ 4. **Bound execution.** Give every production run explicit time, entity, event, and
+ replication caps. Never call `env.run()` on a model containing an endless process.
+ 5. **Separate random streams.** Use local RNG instances for logically distinct
+ stochastic sources; retain a seed manifest.
+ 6. **Instrument deliberately.** Observe state after the transition of interest,
+ close time-weighted intervals at the horizon, and test that monitoring does not
+ alter event order.
+ 7. **Verify and validate.** Test deterministic edge cases, conservation identities,
+ traces, queue discipline, and analytical benchmarks; compare against system or
+ expert evidence for the stated purpose.
+ 8. **Run independent replications.** Make intervals from replication-level
+ estimates, not correlated entities within one run.
+ 9. **Report limitations.** Include initialization, unfinished entities, run length,
+ seeds/streams, precision, sensitivity, and validation evidence. Never convert
+ simulation association into a causal claim.
- | Resource Type | Use Case |
- |---------------|----------|
- | Resource | Limited capacity (servers, machines) |
- | PriorityResource | Priority-based queuing |
- | PreemptiveResource | High-priority can interrupt low-priority |
- | Container | Bulk materials (fuel, water) |
- | Store | Python object storage (FIFO) |
- | FilterStore | Selective item retrieval |
- | PriorityStore | Priority-ordered items |
+ Read `references/simulation-methodology.md` before making inferential claims.
- ### Quick Reference
+ ## Minimal bounded model
```python
+ import random
import simpy
+ HORIZON = 480.0
+ arrival_rng = random.Random(101)
+ service_rng = random.Random(202)
env = simpy.Environment()
-
- # Basic resource (e.g., servers)
- resource = simpy.Resource(env, capacity=2)
-
- # Priority resource
- priority_resource = simpy.PriorityResource(env, capacity=1)
-
- # Container (e.g., fuel tank)
- fuel_tank = simpy.Container(env, capacity=100, init=50)
-
- # Store (e.g., warehouse)
- warehouse = simpy.Store(env, capacity=10)
- ```
-
- ## Common Simulation Patterns
-
- ### Pattern 1: Customer-Server Queue
-
- ```python
- import simpy
- import random
+ server = simpy.Resource(env, capacity=2)
+ completed = []
- def customer(env, name, server):
- arrival = env.now
- with server.request() as req:
- yield req
+ def customer(arrival):
+ with server.request() as request:
+ yield request
wait = env.now - arrival
- print(f'{name} waited {wait:.2f}, served at {env.now}')
- yield env.timeout(random.uniform(2, 4))
+ yield env.timeout(service_rng.expovariate(1 / 6.0))
+ completed.append((env.now, wait))
- def customer_generator(env, server):
- i = 0
- while True:
- yield env.timeout(random.uniform(1, 3))
- i += 1
- env.process(customer(env, f'Customer {i}', server))
+ def arrivals():
+ for _ in range(10_000): # Entity cap.
+ delay = arrival_rng.expovariate(1 / 4.0)
+ if env.now + delay >= HORIZON:
+ return
+ yield env.timeout(delay)
+ env.process(customer(env.now))
- env = simpy.Environment()
- server = simpy.Resource(env, capacity=2)
- env.process(customer_generator(env, server))
- env.run(until=20)
+ env.process(arrivals())
+ env.run(until=HORIZON)
```
- ### Pattern 2: Producer-Consumer
+ The numeric horizon is half-open: normal events scheduled exactly at `480.0` are
+ not processed. Report unfinished entities rather than silently treating them as
+ completed observations.
- ```python
- import simpy
+ ## Core semantics
- def producer(env, store):
- item_id = 0
- while True:
- yield env.timeout(2)
- item = f'Item {item_id}'
- yield store.put(item)
- print(f'Produced {item} at {env.now}')
- item_id += 1
+ ### Environment and deterministic ordering
- def consumer(env, store):
- while True:
- item = yield store.get()
- print(f'Consumed {item} at {env.now}')
- yield env.timeout(3)
+ `Environment` is single-threaded. The queue is ordered by simulation time, event
+ priority, then a strictly increasing event ID. Same-time, same-priority events are
+ therefore processed FIFO in scheduling order. Model processes may represent
+ concurrency, but callbacks execute sequentially and deterministically.
- env = simpy.Environment()
- store = simpy.Store(env, capacity=10)
- env.process(producer(env, store))
- env.process(consumer(env, store))
- env.run(until=20)
- ```
+ - `env.now`: unitless simulation clock; choose and document one unit.
+ - `env.peek()`: next event time or infinity.
+ - `env.step()`: process one event; raises `EmptySchedule` when empty.
+ - `env.active_process`: currently executing process, otherwise `None`.
+ - `env.run()`: drain the queue; unsafe with recurring or endless processes.
- ### Pattern 3: Parallel Task Execution
+ `env.run(until=number)` and `env.run(until=event)` are not interchangeable at
+ boundaries:
- ```python
- import simpy
+ - A numeric value schedules an urgent stop event and excludes ordinary events at
+ that exact time.
+ - An Event criterion returns that event's value when its stop callback fires.
+ Other same-time ordering depends on priority and scheduling order.
+ - In 4.1.2, `Environment.step()` preserves callbacks remaining after
+ `StopSimulation` by rescheduling the target. Consequently, after
+ `env.run(until=target)`, `target.processed` can remain `False` until one more
+ `step()`/`run()` even though its value was returned. Do not use `processed` as the
+ sole post-run completion test.
- def task(env, name, duration):
- print(f'{name} starting at {env.now}')
- yield env.timeout(duration)
- print(f'{name} done at {env.now}')
- return f'{name} result'
+ See `references/events.md` and `references/monitoring.md`.
- def coordinator(env):
- # Start tasks in parallel
- task1 = env.process(task(env, 'Task 1', 5))
- task2 = env.process(task(env, 'Task 2', 3))
- task3 = env.process(task(env, 'Task 3', 4))
+ ### Event, Timeout, Process, and Condition
- # Wait for all to complete
- results = yield task1 & task2 & task3
- print(f'All done at {env.now}')
+ - An `Event` moves once through not-triggered -> triggered/scheduled -> processed.
+ `succeed(value)` or `fail(exception)` triggers it once.
+ - A `Timeout` triggers when created, is scheduled for `now + delay`, and cannot be
+ manually succeeded again.
+ - `env.process(generator)` creates a `Process`; the generator resumes with the
+ yielded event value. Returning from the generator succeeds the Process with that
+ return value. Uncaught exceptions fail it.
+ - `AnyOf` / `a | b` and `AllOf` / `a & b` yield a `ConditionValue`: an ordered,
+ dict-like mapping from **event objects** to their values. Test membership using
+ the original event objects; do not assume a scalar result.
+ - `AnyOf` does not cancel losing events. Explicitly cancel pending resource
+ requests when abandoning them; ordinary timeouts remain scheduled.
- env = simpy.Environment()
- env.process(coordinator(env))
- env.run()
- ```
+ ### Interrupts
- ## Workflow Guide
+ `process.interrupt(cause)` schedules an urgent interruption that throws
+ `simpy.Interrupt` into the target generator. Catch it around the yielded work that
+ may be interrupted, inspect `interrupt.cause`, update remaining work, then either
+ resume, re-yield the original event, or terminate.
- ### Step 1: Define the System
+ Interrupting a process removes its resume callback from its current target; it does
+ not cancel that target event. A process cannot interrupt itself or a terminated
+ process. See `references/process-interaction.md`.
- Identify:
- - **Entities**: What moves through the system? (customers, parts, packets)
- - **Resources**: What are the constraints? (servers, machines, bandwidth)
- - **Processes**: What are the activities? (arrival, service, departure)
- - **Metrics**: What to measure? (wait times, utilization, throughput)
+ ## Shared resources
- ### Step 2: Implement Process Functions
+ | Type | Semantics |
+ |---|---|
+ | `Resource` | FIFO semaphore-like usage slots |
+ | `PriorityResource` | Queued requests sorted by lower numeric priority first |
+ | `PreemptiveResource` | Priority queue plus optional preemption of a current user |
+ | `Container` | Homogeneous numeric level; `put`/`get` wait for capacity/material |
+ | `Store` | FIFO Python objects |
+ | `FilterStore` | First available item satisfying the request's predicate |
+ | `PriorityStore` | Comparable items returned in priority order |
- Create generator functions for each process type:
+ Use a request context manager:
```python
- def entity_process(env, name, resources, parameters):
- # Arrival logic
- arrival_time = env.now
-
- # Request resources
- with resource.request() as req:
- yield req
-
- # Service logic
- service_time = calculate_service_time(parameters)
- yield env.timeout(service_time)
-
- # Departure logic
- collect_statistics(env.now - arrival_time)
+ def job(env, resource):
+ with resource.request() as request:
+ yield request
+ yield env.timeout(3)
```
- ### Step 3: Set Up Monitoring
-
- Use monitoring utilities to collect data. See `references/monitoring.md` for comprehensive techniques.
+ On exit it releases an acquired request or cancels a still-pending one, including
+ during exception unwinding. For a manually retained pending `put`/`get`/request,
+ call `cancel()` if an interrupt or timeout makes the process abandon it.
- ```python
- from scripts.resource_monitor import ResourceMonitor
+ `PreemptiveResource.request(priority=..., preempt=True)` uses lower numbers as
+ higher priority. The preempted process receives an `Interrupt` whose cause is a
+ `Preempted` object: `cause.by` is the preempting Process,
+ `cause.usage_since` is when use began, and `cause.resource` is the resource.
+ Queued priority takes precedence over the `preempt` flag; mixing preempting and
+ non-preempting requests needs explicit tests.
- # Create and monitor resource
- resource = simpy.Resource(env, capacity=2)
- monitor = ResourceMonitor(env, resource, "Server")
+ Read `references/resources.md` for blocked operations, queue rules, and examples.
- # After simulation
- monitor.report()
- ```
+ ## Monitoring and stepping
- ### Step 4: Run and Analyze
+ Prefer explicit domain observations at state transitions. For generic resource
+ monitoring, wrappers or subclasses can inspect `count`, `queue`, `level`, `items`,
+ `put_queue`, and `get_queue`. For event tracing, `schedule()` and `step()` are the
+ central hooks.
- ```python
- # Run simulation
- env.run(until=simulation_time)
+ Queue measurements are timing-sensitive:
- # Generate reports
- monitor.report()
- stats.report()
+ - A request method's pre-state, post-call state, grant callback, and release
+ callback can all differ at the same simulation timestamp.
+ - Sample averages weight event observations, not time. Compute area under the
+ left-continuous state path and divide by elapsed time.
+ - Add initial and final samples; close the last interval at the analysis horizon.
+ - `env._queue`, resource `_env`, and monkey-patching are implementation details.
+ Pin SimPy, isolate the instrumentation, and regression-test after upgrades.
+ - Tracing every event changes runtime and memory use; cap trace records.
- # Export data for further analysis
- monitor.export_csv('results.csv')
- ```
+ Use `scripts/resource_monitor.py` and `references/monitoring.md`.
- ## Advanced Features
+ ## Real-time execution
- ### Process Interaction
+ `simpy.rt.RealtimeEnvironment(initial_time=0, factor=1.0, strict=True)` maps one
+ simulation unit to `factor` wall-clock seconds. In strict mode, `step()`/`run()`
+ raises `RuntimeError` when computation falls behind. `strict=False` tolerates lag;
+ it does not restore timing accuracy. Develop logic with `Environment`, then run
+ separate timing tests with generous platform-aware tolerances. See
+ `references/real-time.md`.
- Processes can interact through events, process yields, and interrupts. See `references/process-interaction.md` for detailed patterns.
+ ## Bundled safe CLIs
- **Key mechanisms:**
- - **Event signaling**: Shared events for coordination
- - **Process yields**: Wait for other processes to complete
- - **Interrupts**: Forcefully resume processes for preemption
+ All CLIs use a fixed built-in queue model or summarize local artifacts. They reject
+ unknown JSON keys, URLs, symlinks, non-finite numbers, oversized inputs, and
+ unbounded time/events/entities/replications. They never evaluate config text,
+ execute user Python, import plugins, or call a network service.
- ### Real-Time Simulations
+ ```bash
+ # Inspect all options.
+ python skills/simpy/scripts/bounded_queue_scenario.py --help
+ python skills/simpy/scripts/replication_runner.py --help
+ python skills/simpy/scripts/event_trace_summary.py --help
+ python skills/simpy/scripts/validate_simulation_config.py --help
- Synchronize simulation with wall-clock time for hardware-in-the-loop or interactive applications. See `references/real-time.md`.
+ # Deterministic built-in scenario.
+ python skills/simpy/scripts/bounded_queue_scenario.py
- ```python
- import simpy.rt
+ # Independent replications with replication-level Student-t intervals.
+ python skills/simpy/scripts/replication_runner.py
- env = simpy.rt.RealtimeEnvironment(factor=1.0) # 1:1 time mapping
- # factor=0.5 means 1 sim unit = 0.5 seconds (2x faster)
+ # Validate only; no simulation runs.
+ python skills/simpy/scripts/validate_simulation_config.py config.json
```
- ### Comprehensive Monitoring
-
- Monitor processes, resources, and events. See `references/monitoring.md` for techniques including:
- - State variable tracking
- - Resource monkey-patching
- - Event tracing
- - Statistical collection
-
- ## Scripts and Templates
-
- ### basic_simulation_template.py
-
- Complete template for building queue simulations with:
- - Configurable parameters
- - Statistics collection
- - Customer generation
- - Resource usage
- - Report generation
-
- **Usage:**
- ```python
- from scripts.basic_simulation_template import SimulationConfig, run_simulation
-
- config = SimulationConfig()
- config.num_resources = 2
- config.sim_time = 100
- stats = run_simulation(config)
- stats.report()
- ```
+ The replication runner refuses one-replication intervals. Its intervals quantify
+ Monte Carlo uncertainty under the configured model; they neither validate the model
+ nor identify causal effects. See `references/cli-guide.md`.
- ### resource_monitor.py
+ ## Testing
- Reusable monitoring utilities:
- - `ResourceMonitor` - Track single resource
- - `MultiResourceMonitor` - Monitor multiple resources
- - `ContainerMonitor` - Track container levels
- - Automatic statistics calculation
- - CSV export functionality
+ Use deterministic unit tests for ordering, boundary times, conditions, interrupts,
+ all resource disciplines, conservation, event/entity limits, seed reproducibility,
+ and monitor non-interference. Add stochastic tests only as broad distributional
+ checks with fixed seeds; avoid brittle exact sample estimates.
- **Usage:**
- ```python
- from scripts.resource_monitor import ResourceMonitor
+ Run the bundled suite in the exact pinned environment without bytecode artifacts:
- monitor = ResourceMonitor(env, resource, "My Resource")
- # ... run simulation ...
- monitor.report()
- monitor.export_csv('data.csv')
+ ```bash
+ PYTHONDONTWRITEBYTECODE=1 uv run --isolated --no-project \
+ --python 3.13 --with "simpy==4.1.2" \
+ python -m unittest discover -s skills/simpy/tests -v
```
- ## Reference Documentation
-
- Detailed guides for specific topics:
-
- - **`references/resources.md`** - All resource types with examples
- - **`references/events.md`** - Event system and patterns
- - **`references/process-interaction.md`** - Process synchronization
- - **`references/monitoring.md`** - Data collection techniques
- - **`references/real-time.md`** - Real-time simulation setup
-
- ## Best Practices
-
- 1. **Generator functions**: Always use `yield` in process functions
- 2. **Resource context managers**: Use `with resource.request() as req:` for automatic cleanup
- 3. **Reproducibility**: Set `random.seed()` for consistent results
- 4. **Monitoring**: Collect data throughout simulation, not just at the end
- 5. **Validation**: Compare simple cases with analytical solutions
- 6. **Documentation**: Comment process logic and parameter choices
- 7. **Modular design**: Separate process logic, statistics, and configuration
-
- ## Common Pitfalls
-
- 1. **Forgetting yield**: Processes must yield events to pause
- 2. **Event reuse**: Events can only be triggered once
- 3. **Resource leaks**: Use context managers or ensure release
- 4. **Blocking operations**: Avoid Python blocking calls in processes
- 5. **Time units**: Stay consistent with time unit interpretation
- 6. **Deadlocks**: Ensure at least one process can make progress
-
- ## Example Use Cases
-
- - **Manufacturing**: Machine scheduling, production lines, inventory management
- - **Healthcare**: Emergency room simulation, patient flow, staff allocation
- - **Telecommunications**: Network traffic, packet routing, bandwidth allocation
- - **Transportation**: Traffic flow, logistics, vehicle routing
- - **Service operations**: Call centers, retail checkout, appointment scheduling
- - **Computer systems**: CPU scheduling, memory management, I/O operations
+ ## References
+ - `references/events.md` — scheduler, lifecycle, run boundaries, conditions
+ - `references/process-interaction.md` — generators, shared events, interrupts
+ - `references/resources.md` — all Resource, Container, and Store variants
+ - `references/monitoring.md` — time weighting, queue timing, tracing, stepping
+ - `references/real-time.md` — factor, strict mode, drift, timing tests
+ - `references/simulation-methodology.md` — replications, warm-up, validation, CI
+ - `references/cli-guide.md` — schemas, bounds, outputs, and safe CLI examples
+ - `references/sources.md` — dated official and primary-method sources