|
5 | 5 | "id": "50446585-a13f-4c90-9700-58015b670d9c", |
6 | 6 | "metadata": {}, |
7 | 7 | "source": [ |
8 | | - "# A first look at simpy\n", |
| 8 | + "# An introduction to SimPy\n", |
9 | 9 | "\n", |
10 | | - "In this tutorial we will make use of **Free and Open Source Software (FOSS)** for discrete-event simulation called `simpy`. \n", |
| 10 | + "In this tutorial, we will use **SimPy**, a free and open-source software (FOSS) framework for discrete-event simulation.\n", |
11 | 11 | "\n", |
12 | | - "## Why FOSS and simpy?\n", |
13 | | - "An strength of `simpy` is its simplicity and flexibility. As it is part of Python, it is often straightforward to use `simpy` to model complex logic and make use of the SciPy stack! Initially, you will need to write a lot of code (or borrow code from other simulation studies you find online!). But don't worry. As you use `simpy` you will build your own library of reusable code that you can draw on (and build on) for future simulation projects. As `simpy` is FOSS it is useful for research: the model logic is transparent, can be readily shared with others, and can easily link to other data science tools such as those from Machine Learning (e.g. `sklearn` or `pytorch`). \n" |
| 12 | + "## 1. Why choose FOSS and SimPy?\n", |
| 13 | + "\n", |
| 14 | + "💪 A strength of SimPy is its **simplicity and flexibility**.\n", |
| 15 | + "\n", |
| 16 | + "* As it is part of Python, it is often straightforward to use SimPy to model complex logic and make use of the [SciPy stack](https://projects.scipy.org/stackspec.html)!\n", |
| 17 | + "\n", |
| 18 | + "📝 You will initially need to **write lots of code** - or borrow code from existing simulation studies online. Do not worry though! As you use SimPy, you will build up your own library of reusable code that you can draw upon and build on for future simulation projects.\n", |
| 19 | + "\n", |
| 20 | + "♻️ SimPy is **FOSS** - the benefits of this for research are that:\n", |
| 21 | + "\n", |
| 22 | + "* Model logic is **transparent**\n", |
| 23 | + "* It can be readily **shared** with others\n", |
| 24 | + "* It can easily **link to other data science tools** (e.g. `sklearn` or `pytorch` for machine learning)" |
14 | 25 | ] |
15 | 26 | }, |
16 | 27 | { |
17 | 28 | "cell_type": "markdown", |
18 | 29 | "id": "71ec6d31-c791-4945-83fd-c96670e4b492", |
19 | 30 | "metadata": {}, |
20 | 31 | "source": [ |
21 | | - "## 1. Imports\n", |
| 32 | + "## 2. Imports\n", |
22 | 33 | "\n", |
23 | | - "The first library we will import is `simpy`. The typical style is to import the whole package as follows:" |
| 34 | + "For `simpy`, the typical style is to import the whole package as follows:" |
24 | 35 | ] |
25 | 36 | }, |
26 | 37 | { |
|
33 | 44 | "import simpy" |
34 | 45 | ] |
35 | 46 | }, |
36 | | - { |
37 | | - "cell_type": "markdown", |
38 | | - "id": "dda6f531-3e2d-4e65-8ad6-0703f09df810", |
39 | | - "metadata": {}, |
40 | | - "source": [ |
41 | | - "We will also need a few other packages in our simulation model. " |
42 | | - ] |
43 | | - }, |
44 | | - { |
45 | | - "cell_type": "code", |
46 | | - "execution_count": 2, |
47 | | - "id": "9f083908-66a9-47ea-af29-c2baaaeef4c4", |
48 | | - "metadata": {}, |
49 | | - "outputs": [], |
50 | | - "source": [ |
51 | | - "import numpy as np\n", |
52 | | - "import pandas as pd\n", |
53 | | - "import matplotlib.pyplot as plt\n", |
54 | | - "import itertools\n", |
55 | | - "import math" |
56 | | - ] |
57 | | - }, |
58 | 47 | { |
59 | 48 | "cell_type": "markdown", |
60 | 49 | "id": "968d7a3d-763d-48a0-9915-421631d1f650", |
61 | 50 | "metadata": {}, |
62 | 51 | "source": [ |
63 | | - "## 2. A first example: a hospital pharmacy\n", |
| 52 | + "## 3. An example: a hospital pharmacy\n", |
64 | 53 | "\n", |
65 | | - "In this first example, let's assume (unrealistically) that prescriptions arrive **exactly** 5 minutes apart. To build this model we need the following components:\n", |
| 54 | + "In this first example, let's assume (unrealistically) that prescriptions arrive **exactly** 5 minutes apart.\n", |
66 | 55 | "\n", |
67 | | - "#### **A simpy environment**\n", |
| 56 | + "\n", |
| 57 | + "\n", |
| 58 | + "## 4. The model building blocks\n", |
| 59 | + "\n", |
| 60 | + "To build our model, we will need the following components...\n", |
| 61 | + "\n", |
| 62 | + "### 4.1 A SimPy environment\n", |
68 | 63 | "\n", |
69 | 64 | "`simpy` has process based worldview. These processes take place in an environment. You can create a environment with the following line of code:\n", |
70 | 65 | "\n", |
71 | 66 | "```python\n", |
72 | 67 | "env = simpy.Environment()\n", |
73 | 68 | "```\n", |
74 | 69 | "\n", |
75 | | - "#### **simpy timeouts**\n", |
| 70 | + "### 4.2 SimPy timeouts\n", |
76 | 71 | "\n", |
77 | | - "We can introduce **delays** or **activities** into a process. For example these might be the duration of a stay on a ward, or the duration of a operation. In this case we are going to introduce a delay between arrivals (inter-arrival time). In `simpy` you control this with the following method:\n", |
| 72 | + "We can introduce **delays** or **activities** into a process. For example these might be the duration of a stay on a ward, or the duration of a operation - or, in this case, a **delay between arrivals (inter-arrival time)**. In `simpy` you control this with the following method:\n", |
78 | 73 | "\n", |
79 | 74 | "```python\n", |
80 | | - "activity_duration = 20\n", |
81 | | - "env.timeout(activity_duration)\n", |
| 75 | + "env.timeout(5.0)\n", |
82 | 76 | "```\n", |
83 | 77 | "\n", |
84 | | - "#### **generators**\n", |
| 78 | + "### 4.3 Generators\n", |
| 79 | + "\n", |
| 80 | + "The events in the DES are modelled and scheduled in `simpy` using python **generators** (i.e. they are the \"event-processing mechanism\"). A generator is a function that behaves like an iterator, meaning it can yield a **sequence of values** when iterated over.\n", |
85 | 81 | "\n", |
86 | | - "The event process mechanism in `simpy` is implemented using python generators. A basic generator function that yields a new arrival every 5 minutes looks like this:\n", |
| 82 | + "For example, below is a basic generator function that yields a new arrival every 5 minutes. It takes the **environment** as a parameter. It then internally calls the `env.timeout()` method in an infinite loop.\n", |
87 | 83 | "\n", |
88 | 84 | "```python\n", |
89 | 85 | "def prescription_arrival_generator(env):\n", |
90 | 86 | " while True:\n", |
91 | 87 | " yield env.timeout(5.0)\n", |
92 | 88 | "```\n", |
93 | 89 | "\n", |
94 | | - "Notice that the generator takes the environment as a parameter. It then internally calls the `env.timeout()` method in an infinite loop.\n", |
95 | | - "\n", |
96 | | - "#### **running a `simpy` model**\n", |
| 90 | + "### 4.4 SimPy process and run\n", |
97 | 91 | "\n", |
98 | 92 | "Once we have coded the model logic and created an environment instance, there are two remaining instructions we need to code.\n", |
99 | 93 | "\n", |
100 | | - "1. set the generator up as a simpy process\n", |
| 94 | + "1. Set the generator up as a **SimPy process** using `env.process()`\n", |
101 | 95 | "\n", |
102 | 96 | "```python\n", |
103 | 97 | "env.process(prescription_arrival_generator(env))\n", |
104 | 98 | "```\n", |
105 | 99 | "\n", |
106 | | - "2. run the environment for a user specified run length\n", |
| 100 | + "2. Run the environment for a user specified **run length** using `env.run()`\n", |
107 | 101 | "\n", |
108 | 102 | "```python\n", |
109 | 103 | "env.run(until=25)\n", |
110 | 104 | "```\n", |
111 | 105 | "\n", |
112 | 106 | "The run method handle the infinite loop we set up in `prescription_arrival_generator`. The simulation model has an internal concept of time. It will end execution when its internal clock reaches 25 time units.\n", |
113 | 107 | "\n", |
| 108 | + "## 5. Create the model\n", |
| 109 | + "\n", |
114 | 110 | "**Now that we have covered the basic building blocks, let's code the actual model.** It makes sense to create our model logic first. The code below will generate arrivals every 5 minutes. Note that the function takes an environment object as a parameter." |
115 | 111 | ] |
116 | 112 | }, |
117 | 113 | { |
118 | 114 | "cell_type": "code", |
119 | | - "execution_count": 3, |
| 115 | + "execution_count": 2, |
120 | 116 | "id": "a6fd524c-7dc4-41c0-876d-3507ce480dfb", |
121 | 117 | "metadata": {}, |
122 | 118 | "outputs": [], |
|
149 | 145 | "id": "aa6042f3-b7a7-4c3a-a5d8-7eb7f3796bf2", |
150 | 146 | "metadata": {}, |
151 | 147 | "source": [ |
152 | | - "Now that we have our generator function we can setup the environment, process and call run. We will create a `RUN_LENGTH` parameter that you can change to run the model for different time lengths. What would happen if this was set to 50?" |
| 148 | + "Now that we have our generator function we can setup the environment, process and call run. We will create a `RUN_LENGTH` parameter that you can change to run the model for different time lengths.\n", |
| 149 | + "\n", |
| 150 | + "**Consider:** What would happen if we set `RUN_LENGTH` to 50?" |
153 | 151 | ] |
154 | 152 | }, |
155 | 153 | { |
156 | 154 | "cell_type": "code", |
157 | | - "execution_count": 6, |
| 155 | + "execution_count": 3, |
158 | 156 | "id": "f6f74ff5-4c95-400e-8494-42e438b18b90", |
159 | 157 | "metadata": {}, |
160 | 158 | "outputs": [ |
|
190 | 188 | "id": "c3aa041b-6b8f-4b15-becc-bd966d0eb794", |
191 | 189 | "metadata": {}, |
192 | 190 | "source": [ |
193 | | - "## Exercise\n", |
| 191 | + "## 6. Exercise\n", |
| 192 | + "\n", |
| 193 | + "Before we learn anything more about `simpy`, have a go at the [generators exercise](./03a_exercise1.ipynb).\n", |
194 | 194 | "\n", |
195 | | - "Before we learn anything more about `simpy` have a go at the generators exercise. In the exercise you will need to modify the `prescription_arrival_generator` so that it has random arrivals. This exercise test that you have understood the basics of `simpy` and random sampling in `numpy`\n" |
| 195 | + "In the exercise you will need to modify the `prescription_arrival_generator` so that it has random arrivals. This exercise tests that you have understood the basics of `simpy` and random sampling in `numpy`\n" |
196 | 196 | ] |
197 | 197 | } |
198 | 198 | ], |
|
212 | 212 | "name": "python", |
213 | 213 | "nbconvert_exporter": "python", |
214 | 214 | "pygments_lexer": "ipython3", |
215 | | - "version": "3.11.9" |
| 215 | + "version": "3.1.undefined" |
216 | 216 | } |
217 | 217 | }, |
218 | 218 | "nbformat": 4, |
|
0 commit comments