{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import simpy\n",
"import pandas as pd\n",
"import numpy as np\n",
"import math"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**SimPy** is a process-based discrete-event simulation framework based on standard Python. Its event dispatcher is based on the time of events and the processes it contains. It provides the modeller with components of a simulation model, including processes, for active components like vehicles or customers, and resources, for passive components like servers or counters. SimPy also provides monitors to aid in understanding the model. \n",
"\n",
"**Features of SimPy:**\n",
"- Processes: SimPy has a built-in concept of processes, which are the active components of a simulation. They can be used to model active entities like customers, vehicles, or messages.\n",
"- Events: SimPy uses events to schedule processes. Events are triggered at a specific time and can be used to model interactions between processes.\n",
"- Resources: SimPy provides resources, which are used to model passive components like servers, counters, or channels. Resources can be used by processes and can be limited in their capacity.\n",
"- Monitors: SimPy provides monitors, which are used to observe and analyze the simulation. Monitors can be used to collect data on processes, resources, and events.\n",
"- Randomness: SimPy has built-in support for randomness, which is essential for simulation models. It uses the Mersenne Twister random number generator.\n",
"- Easy to Learn: SimPy is built on top of Python and uses a syntax that is easy to learn, even for those without prior experience with simulation or Python.\n",
"- Fast: SimPy is fast and can handle large simulations with millions of events.\n",
"- Extensive Libraries: SimPy has extensive libraries for various domains like manufacturing, healthcare, and logistics.\n",
"- Visualization: SimPy provides tools for visualization, which can be used to visualize the simulation and insights.\n",
"\n",
"**To install:** \n",
"> pip intall simpy"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The code below demonstrates how you can capture simulation data and a method to then query statistical data about the simulation."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class P:\n",
" ''' initialize variables '''\n",
" parking_duration = 5 # how long is the car parked\n",
" trip_duration = 2 # how long is the car driving for\n",
" max_sim_time = 105 # how long to run the dimulation for\n",
" \n",
"class Report():\n",
" ''' capture simulation data '''\n",
" def __init__(self):\n",
" self.data = []\n",
" self.numcars = 0\n",
"\n",
" def updatestats(self):\n",
" ''' Update statistical counters before calculating summaries\n",
"\n",
" After completion of simulation, updatestats() prior to calculating\n",
" statistics.\n",
" Returns a Pandas dataframe of delta time, number of active servers,\n",
" and number in queue.\n",
" If pandas is not installed. It will return a list of\n",
" tuples of (elapsed time, active servers, number in queue)\n",
" '''\n",
" elapseddata = [(self.data[i][0] - self.data[i-1][0], self.data[i][1]) if i > 0 else (self.data[i][0], self.data[i][1]) for i in range(0, len(self.data))]\n",
"\n",
" try:\n",
" self.carwashdata = pd.DataFrame(self.data, columns=['time','total_wait_time'])\n",
" self.elapseddata = pd.DataFrame(elapseddata, columns=['elapsedtime','total_wait_time'])\n",
" return self.carwashdata\n",
" except:\n",
" return elapseddata\n",
"\n",
" def countcars(self):\n",
" ''' Number of cars that have gone through the simulation\n",
" '''\n",
" return self.numcars\n",
" \n",
" def timeAverage(self, elapsedtime, values):\n",
" return ((np.sum(elapsedtime * values)) / elapsedtime.sum())\n",
"\n",
" def timeVariance(self, elapsedtime, values):\n",
" ''' Time weighted unbiased estimator of variance\n",
" '''\n",
" weightedmu = self.timeAverage(elapsedtime, values)\n",
" V = elapsedtime.sum()\n",
" weightsumsquared = sum([elapsedtime[i] *\n",
" math.pow(values[i] - weightedmu, 2)\n",
" for i in range(len(elapsedtime))])\n",
" timevariance = weightsumsquared / (V - 1)\n",
" return timevariance\n",
"\n",
" def actTimeAverage(self):\n",
" ''' Time weighted average of number of servers in use\n",
" '''\n",
" return self.timeAverage(self.elapseddata['elapsedtime'],\n",
" self.elapseddata['total_wait_time'])\n",
"\n",
" def waitTimeAverage(self):\n",
" ''' Time weighted average of number of cars in queue\n",
" '''\n",
" return self.timeAverage(self.elapseddata['elapsedtime'],\n",
" self.elapseddata['total_wait_time'])\n",
"\n",
" def actTimeVariance(self):\n",
" ''' Time weighted unbiased variance of number of servers in operation\n",
" '''\n",
" timevariance = self.timeVariance(self.elapseddata['elapsedtime'],\n",
" self.elapseddata['total_wait_time'])\n",
" return timevariance\n",
"\n",
" def waitTimeVariance(self):\n",
" ''' Time weighted unbiased variance of number of cars in queue\n",
" '''\n",
" timevariance = self.timeVariance(self.elapseddata['elapsedtime'],\n",
" self.elapseddata['total_wait_time'])\n",
" return timevariance \n",
"\n",
"class Car(object):\n",
" ''' stop and drive the car '''\n",
" def __init__(self, env, parking_duration, trip_duration, simdata):\n",
" self.env = env\n",
" self.parking_duration = parking_duration\n",
" self.trip_duration = trip_duration\n",
" \n",
" # this is how we accept the simulation data object\n",
" self.simdata = simdata\n",
" \n",
" # start the run process everytime an instance is created.\n",
" self.action = env.process(self.park_and_drive())\n",
" \n",
" def park_and_drive(self):\n",
" while True:\n",
" \n",
" # get current time\n",
" wait_start = self.env.now\n",
" \n",
" # keep track of the number of car entities flowing through the simulation\n",
" self.simdata.numcars += 1\n",
" \n",
" print('Start parking at %d' % self.env.now)\n",
" yield self.env.timeout(self.parking_duration)\n",
"\n",
" print('Start driving at %d' % self.env.now)\n",
" yield self.env.timeout(self.trip_duration) \n",
" \n",
" # collect simulation data\n",
" self.simdata.data.append((self.env.now, self.env.now - wait_start))\n",
" \n",
"def model(): \n",
" ''' run simulation ''' \n",
" # create an environment \n",
" env = simpy.Environment()\n",
" \n",
" # capture simulation data\n",
" simdata = Report()\n",
"\n",
" # pass the simdata object to the Car class\n",
" car = Car(env, P.parking_duration, P.trip_duration, simdata)\n",
"\n",
" # start simulation\n",
" env.run(until=P.max_sim_time) \n",
" \n",
" return simdata"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Start parking at 0\n",
"Start driving at 5\n",
"Start parking at 7\n",
"Start driving at 12\n",
"Start parking at 14\n",
"Start driving at 19\n",
"Start parking at 21\n",
"Start driving at 26\n",
"Start parking at 28\n",
"Start driving at 33\n",
"Start parking at 35\n",
"Start driving at 40\n",
"Start parking at 42\n",
"Start driving at 47\n",
"Start parking at 49\n",
"Start driving at 54\n",
"Start parking at 56\n",
"Start driving at 61\n",
"Start parking at 63\n",
"Start driving at 68\n",
"Start parking at 70\n",
"Start driving at 75\n",
"Start parking at 77\n",
"Start driving at 82\n",
"Start parking at 84\n",
"Start driving at 89\n",
"Start parking at 91\n",
"Start driving at 96\n",
"Start parking at 98\n",
"Start driving at 103\n"
]
}
],
"source": [
"d = model()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We need to run the ***updatestats*** function to have access all the other statistical functions"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" time | \n",
" total_wait_time | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 7 | \n",
" 7 | \n",
"
\n",
" \n",
" 1 | \n",
" 14 | \n",
" 7 | \n",
"
\n",
" \n",
" 2 | \n",
" 21 | \n",
" 7 | \n",
"
\n",
" \n",
" 3 | \n",
" 28 | \n",
" 7 | \n",
"
\n",
" \n",
" 4 | \n",
" 35 | \n",
" 7 | \n",
"
\n",
" \n",
" 5 | \n",
" 42 | \n",
" 7 | \n",
"
\n",
" \n",
" 6 | \n",
" 49 | \n",
" 7 | \n",
"
\n",
" \n",
" 7 | \n",
" 56 | \n",
" 7 | \n",
"
\n",
" \n",
" 8 | \n",
" 63 | \n",
" 7 | \n",
"
\n",
" \n",
" 9 | \n",
" 70 | \n",
" 7 | \n",
"
\n",
" \n",
" 10 | \n",
" 77 | \n",
" 7 | \n",
"
\n",
" \n",
" 11 | \n",
" 84 | \n",
" 7 | \n",
"
\n",
" \n",
" 12 | \n",
" 91 | \n",
" 7 | \n",
"
\n",
" \n",
" 13 | \n",
" 98 | \n",
" 7 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" time total_wait_time\n",
"0 7 7\n",
"1 14 7\n",
"2 21 7\n",
"3 28 7\n",
"4 35 7\n",
"5 42 7\n",
"6 49 7\n",
"7 56 7\n",
"8 63 7\n",
"9 70 7\n",
"10 77 7\n",
"11 84 7\n",
"12 91 7\n",
"13 98 7"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d.updatestats()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"text/plain": [
"7.0"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d.actTimeAverage()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> Run a second simulation"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Start parking at 0\n",
"Start driving at 5\n",
"Start parking at 7\n",
"Start driving at 12\n",
"Start parking at 14\n",
"Start driving at 19\n",
"Start parking at 21\n",
"Start driving at 26\n",
"Start parking at 28\n",
"Start driving at 33\n",
"Start parking at 35\n",
"Start driving at 40\n",
"Start parking at 42\n",
"Start driving at 47\n",
"Start parking at 49\n",
"Start driving at 54\n",
"Start parking at 56\n",
"Start driving at 61\n",
"Start parking at 63\n",
"Start driving at 68\n",
"Start parking at 70\n",
"Start driving at 75\n",
"Start parking at 77\n",
"Start driving at 82\n",
"Start parking at 84\n",
"Start driving at 89\n",
"Start parking at 91\n",
"Start driving at 96\n",
"Start parking at 98\n",
"Start driving at 103\n"
]
}
],
"source": [
"d2 = model()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" time | \n",
" total_wait_time | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 7 | \n",
" 7 | \n",
"
\n",
" \n",
" 1 | \n",
" 14 | \n",
" 7 | \n",
"
\n",
" \n",
" 2 | \n",
" 21 | \n",
" 7 | \n",
"
\n",
" \n",
" 3 | \n",
" 28 | \n",
" 7 | \n",
"
\n",
" \n",
" 4 | \n",
" 35 | \n",
" 7 | \n",
"
\n",
" \n",
" 5 | \n",
" 42 | \n",
" 7 | \n",
"
\n",
" \n",
" 6 | \n",
" 49 | \n",
" 7 | \n",
"
\n",
" \n",
" 7 | \n",
" 56 | \n",
" 7 | \n",
"
\n",
" \n",
" 8 | \n",
" 63 | \n",
" 7 | \n",
"
\n",
" \n",
" 9 | \n",
" 70 | \n",
" 7 | \n",
"
\n",
" \n",
" 10 | \n",
" 77 | \n",
" 7 | \n",
"
\n",
" \n",
" 11 | \n",
" 84 | \n",
" 7 | \n",
"
\n",
" \n",
" 12 | \n",
" 91 | \n",
" 7 | \n",
"
\n",
" \n",
" 13 | \n",
" 98 | \n",
" 7 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" time total_wait_time\n",
"0 7 7\n",
"1 14 7\n",
"2 21 7\n",
"3 28 7\n",
"4 35 7\n",
"5 42 7\n",
"6 49 7\n",
"7 56 7\n",
"8 63 7\n",
"9 70 7\n",
"10 77 7\n",
"11 84 7\n",
"12 91 7\n",
"13 98 7"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# update second models statistics\n",
"d2.updatestats()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> You can glue together the two simulation results for analysis\n",
"\n",
"There is not much to see here as the two results are identical"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" time | \n",
" total_wait_time | \n",
" time | \n",
" total_wait_time | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 7 | \n",
" 7 | \n",
" 7 | \n",
" 7 | \n",
"
\n",
" \n",
" 1 | \n",
" 14 | \n",
" 7 | \n",
" 14 | \n",
" 7 | \n",
"
\n",
" \n",
" 2 | \n",
" 21 | \n",
" 7 | \n",
" 21 | \n",
" 7 | \n",
"
\n",
" \n",
" 3 | \n",
" 28 | \n",
" 7 | \n",
" 28 | \n",
" 7 | \n",
"
\n",
" \n",
" 4 | \n",
" 35 | \n",
" 7 | \n",
" 35 | \n",
" 7 | \n",
"
\n",
" \n",
" 5 | \n",
" 42 | \n",
" 7 | \n",
" 42 | \n",
" 7 | \n",
"
\n",
" \n",
" 6 | \n",
" 49 | \n",
" 7 | \n",
" 49 | \n",
" 7 | \n",
"
\n",
" \n",
" 7 | \n",
" 56 | \n",
" 7 | \n",
" 56 | \n",
" 7 | \n",
"
\n",
" \n",
" 8 | \n",
" 63 | \n",
" 7 | \n",
" 63 | \n",
" 7 | \n",
"
\n",
" \n",
" 9 | \n",
" 70 | \n",
" 7 | \n",
" 70 | \n",
" 7 | \n",
"
\n",
" \n",
" 10 | \n",
" 77 | \n",
" 7 | \n",
" 77 | \n",
" 7 | \n",
"
\n",
" \n",
" 11 | \n",
" 84 | \n",
" 7 | \n",
" 84 | \n",
" 7 | \n",
"
\n",
" \n",
" 12 | \n",
" 91 | \n",
" 7 | \n",
" 91 | \n",
" 7 | \n",
"
\n",
" \n",
" 13 | \n",
" 98 | \n",
" 7 | \n",
" 98 | \n",
" 7 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" time total_wait_time time total_wait_time\n",
"0 7 7 7 7\n",
"1 14 7 14 7\n",
"2 21 7 21 7\n",
"3 28 7 28 7\n",
"4 35 7 35 7\n",
"5 42 7 42 7\n",
"6 49 7 49 7\n",
"7 56 7 56 7\n",
"8 63 7 63 7\n",
"9 70 7 70 7\n",
"10 77 7 77 7\n",
"11 84 7 84 7\n",
"12 91 7 91 7\n",
"13 98 7 98 7"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pd.concat([d.carwashdata,d2.carwashdata], axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": true
}
},
"source": [
"# Resources \n",
"\n",
"I originally found the code here:\n",
"* [Report class came from a Simpy Pull request](https://bitbucket.org/simpy/simpy/pull-requests/83/created-monitored-resource-example-carwash/diff)\n",
"\n",
"Since that repo no longer exists, the example seems to be in the link below:\n",
"* https://www.studocu.com/en-us/document/university-of-pittsburgh/digital-systems-simulation/python-simpy-3/92667217"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": true
}
},
"source": [
"This tutorial was created by HEDARO
"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 4
}