S

Simpy Smart

Powerful skill for process, based, discrete, event. Includes structured workflows, validation checks, and reusable patterns for scientific.

SkillClipticsscientificv1.0.0MIT
0 views0 copies

Simpy Smart

Build and run discrete-event simulations using SimPy, a process-based simulation framework for Python. This skill covers process modeling, resource management, event scheduling, statistics collection, and simulation of real-world systems like manufacturing lines, service queues, and logistics networks.

When to Use This Skill

Choose Simpy Smart when you need to:

  • Simulate queuing systems (call centers, hospital ERs, checkout lines)
  • Model manufacturing and production line throughput with resource constraints
  • Analyze logistics and supply chain scenarios with stochastic demand
  • Evaluate system capacity and bottleneck identification

Consider alternatives when:

  • You need continuous-time simulation (use scipy.integrate or custom ODE solvers)
  • You need agent-based modeling (use Mesa or NetLogo)
  • You need Monte Carlo risk analysis without events (use numpy random sampling)

Quick Start

pip install simpy numpy
import simpy import numpy as np def customer(env, name, server, service_time_mean): """A customer arrives, waits for the server, gets served.""" arrival_time = env.now print(f'{name} arrives at {arrival_time:.1f}') with server.request() as req: yield req wait_time = env.now - arrival_time print(f'{name} starts service at {env.now:.1f} (waited {wait_time:.1f})') service_time = np.random.exponential(service_time_mean) yield env.timeout(service_time) print(f'{name} leaves at {env.now:.1f}') def customer_generator(env, server, arrival_rate, service_time_mean): """Generate customers at random intervals.""" i = 0 while True: yield env.timeout(np.random.exponential(1.0 / arrival_rate)) i += 1 env.process(customer(env, f'Customer-{i}', server, service_time_mean)) # Setup and run np.random.seed(42) env = simpy.Environment() server = simpy.Resource(env, capacity=2) # 2 service counters env.process(customer_generator(env, server, arrival_rate=1.5, service_time_mean=1.0)) env.run(until=20)

Core Concepts

SimPy Components

ComponentClassPurpose
Environmentsimpy.Environment()Simulation clock and event scheduler
Processenv.process(generator)Active entity in the simulation
Resourcesimpy.Resource(capacity=N)Shared resource with queue
PriorityResourcesimpy.PriorityResource()Resource with priority queue
PreemptiveResourcesimpy.PreemptiveResource()Resource allowing preemption
Containersimpy.Container(capacity=N)Bulk resource (tanks, bins)
Storesimpy.Store(capacity=N)Queue for discrete items
FilterStoresimpy.FilterStore()Store with item selection
Eventenv.event()Custom synchronization events
Timeoutenv.timeout(delay)Wait for duration

Manufacturing Line Simulation

import simpy import numpy as np from dataclasses import dataclass, field from typing import List @dataclass class SimStats: """Collect simulation statistics.""" throughput: int = 0 wait_times: List[float] = field(default_factory=list) cycle_times: List[float] = field(default_factory=list) utilization: dict = field(default_factory=dict) def manufacturing_line(env, stats): """Simulate a 3-stage manufacturing line.""" # Define workstations cutting = simpy.Resource(env, capacity=2) assembly = simpy.Resource(env, capacity=3) inspection = simpy.Resource(env, capacity=1) def part(name): start = env.now # Stage 1: Cutting with cutting.request() as req: yield req yield env.timeout(np.random.normal(4.0, 0.5)) # Stage 2: Assembly with assembly.request() as req: t_wait = env.now yield req stats.wait_times.append(env.now - t_wait) yield env.timeout(np.random.normal(6.0, 1.0)) # Stage 3: Inspection (10% fail rate) with inspection.request() as req: yield req yield env.timeout(np.random.normal(2.0, 0.3)) if np.random.random() > 0.10: # 90% pass stats.throughput += 1 stats.cycle_times.append(env.now - start) def part_generator(): i = 0 while True: yield env.timeout(np.random.exponential(2.0)) i += 1 env.process(part(f'Part-{i}')) env.process(part_generator()) # Run simulation np.random.seed(42) env = simpy.Environment() stats = SimStats() manufacturing_line(env, stats) env.run(until=480) # 8-hour shift (480 minutes) print(f"Parts completed: {stats.throughput}") print(f"Avg cycle time: {np.mean(stats.cycle_times):.1f} min") print(f"Avg assembly wait: {np.mean(stats.wait_times):.1f} min")

Configuration

ParameterDescriptionDefault
sim_durationTotal simulation timeProblem-specific
resource_capacityNumber of units in a resource1
random_seedSeed for reproducibility42
warmup_periodTime to discard initial transients0
num_replicationsNumber of independent simulation runs10
arrival_distributionInter-arrival time distributionExponential
service_distributionService time distributionExponential
priority_levelsNumber of priority classes1

Best Practices

  1. Run multiple replications with different seeds — A single simulation run produces a single sample path. Run 10-30 replications with different random seeds and report mean ± confidence interval. This accounts for stochastic variation and gives reliable performance estimates.

  2. Include a warmup period to eliminate initialization bias — Simulations start empty, which isn't representative of steady-state. Discard statistics from the first 10-20% of simulation time. Calculate warmup length by plotting a metric over time and identifying when it stabilizes.

  3. Validate against analytical solutions for simple cases — Before simulating complex systems, validate your SimPy code against M/M/1 or M/M/c queueing theory results. If the simple case matches theory, you can trust the framework for complex scenarios where analytical solutions don't exist.

  4. Use generators for processes, not functions — SimPy processes must be Python generators using yield. Each yield hands control back to the simulation clock. Forgetting yield before env.timeout() or resource.request() causes the simulation to skip events entirely without errors.

  5. Collect statistics in a separate data structure — Don't print or log every event — it slows simulation and creates enormous output. Use a statistics collector (dataclass or dict) and record only the metrics you need: wait times, queue lengths at intervals, and throughput counters.

Common Issues

Simulation finishes instantly with no output — The process generator wasn't started with env.process(). Just defining the generator function doesn't register it. Call env.process(my_process(env, ...)) to schedule it. Also ensure the process contains yield statements — a function without yields runs to completion immediately.

Resource deadlock — processes wait forever — This happens when processes hold one resource while requesting another in circular dependency. Ensure resources are requested and released in a consistent order, or use simpy.PreemptiveResource if priority-based access is needed. Add timeout-based fallbacks to detect deadlocks.

Statistics are biased by startup transients — The first customers experience no queue because the system starts empty. This inflates throughput and deflates wait times. Implement a warmup by collecting statistics only after a warmup period: if env.now > warmup_time: stats.wait_times.append(wait).

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates