From 4f9b0f9270da9069921d5003d3c160ea73488a26 Mon Sep 17 00:00:00 2001 From: TomMonks Date: Tue, 17 Mar 2026 11:14:08 +0000 Subject: [PATCH 1/5] feat(store): added basic store example draft --- content/13_warm_up.ipynb | 2 +- content/14_initial_conditions.ipynb | 2 +- content/15_resource_stores.ipynb | 420 ++++++++++++++++++++++++++++ content/sim_utility.py | 36 +++ 4 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 content/15_resource_stores.ipynb create mode 100644 content/sim_utility.py diff --git a/content/13_warm_up.ipynb b/content/13_warm_up.ipynb index ddd6971..dacf063 100644 --- a/content/13_warm_up.ipynb +++ b/content/13_warm_up.ipynb @@ -514,7 +514,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.11.15" } }, "nbformat": 4, diff --git a/content/14_initial_conditions.ipynb b/content/14_initial_conditions.ipynb index 4cac409..b760af2 100644 --- a/content/14_initial_conditions.ipynb +++ b/content/14_initial_conditions.ipynb @@ -588,7 +588,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.11.15" } }, "nbformat": 4, diff --git a/content/15_resource_stores.ipynb b/content/15_resource_stores.ipynb new file mode 100644 index 0000000..5886d47 --- /dev/null +++ b/content/15_resource_stores.ipynb @@ -0,0 +1,420 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4da08d47-1de7-4634-a8fc-fc05657475f7", + "metadata": {}, + "source": [ + "# Advanced Resources: Stores\n", + "\n", + "## Limitations of `Resource`\n", + "\n", + "Up to this point the models we have looked have have used a basic `simpy.Resource`. One downside to `Resource` is that it does not allow you to give individual resources attributes or any type of complex behaviour. For many models, this is sufficient, but there may be instances where you need to track and control individual resources. For example, ambulances, or different types of staff. In this notebook we will explore how to add more complex behaviour using the `Store` and `FilterStore` objects provided by `simpy`." + ] + }, + { + "cell_type": "markdown", + "id": "8d4bd324-bdd2-4b60-ac94-862446c29aca", + "metadata": {}, + "source": [ + "## 1. Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bcedfd8f-49ac-4e0c-bf11-2e4d244db365", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import itertools\n", + "import simpy" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f1a26662-34e6-4ac7-b186-00c013183f16", + "metadata": {}, + "outputs": [], + "source": [ + "# to reduce code these classes can be found in distribution.py\n", + "# you can also pip `install sim-tools`\n", + "from distributions import (\n", + " Exponential, \n", + ")\n", + "\n", + "from sim_utility import set_trace, trace" + ] + }, + { + "cell_type": "markdown", + "id": "b03d45cf-d0c9-4370-8922-561ea89e2f02", + "metadata": {}, + "source": [ + "## 2. Using a `simpy.Store`\n", + "\n", + "A store is simpy a \"box\" that we can `put` or `get` our own custom resource objects from in a First In First Out (FIFO) basis.\n", + "\n", + "### 2.1 Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d5b3eb67-9c8a-4fd8-9312-d337a65c52ee", + "metadata": {}, + "outputs": [], + "source": [ + "NUM_AMBULANCES = 10\n", + "MEAN_SERVICE_TIME = 60.0 # minutes per call\n", + "TRAFFIC_INTENSITY = 0.95 # ρ = λ / (c · μ)\n", + "RUN_LENGTH = 1_000 # minutes\n", + "RANDOM_SEED = 42\n", + "\n", + "# exponential IAT and service time so I have used traffic intensity to control IAT\n", + "# ρ = λ / (c · μ) → mean_interarrival = mean_service / (ρ · c)\n", + "MEAN_INTERARRIVAL = MEAN_SERVICE_TIME / (TRAFFIC_INTENSITY * NUM_AMBULANCES)\n", + "# → 60 / (0.8 × 10) = 7.5 minutes between calls" + ] + }, + { + "cell_type": "markdown", + "id": "03114c1d-faf6-4c95-b7d2-64d6a17003a8", + "metadata": {}, + "source": [ + "### 2.2 Entity classes" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "7956189c-c777-4c0a-afcc-ed06ec24ee3e", + "metadata": {}, + "outputs": [], + "source": [ + "class Ambulance:\n", + " \"\"\"\n", + " An ambulance resource\n", + "\n", + " Parameters:\n", + " ----------\n", + " ambulance_id: int\n", + " Unique id of the ambulance\n", + " \"\"\"\n", + " def __init__(self, ambulance_id: int):\n", + " self.ambulance_id = ambulance_id\n", + " self.total_jobs = 0\n", + " # cumulative busy time\n", + " self.total_busy = 0.0 \n", + " \n", + " def __repr__(self):\n", + " \"\"\"\n", + " A text representation of the ambulance to help debugging\n", + " \"\"\"\n", + " return f\"Ambulance(id={self.ambulance_id})\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "fa690897-a9e5-473b-8f01-35991b1b4213", + "metadata": {}, + "outputs": [], + "source": [ + "class Patient:\n", + " \"\"\"\n", + " A class to hold patient attributes\n", + "\n", + " Parameters\n", + " ----------\n", + " patient_id: int\n", + " Unique patient id\n", + "\n", + " arrival_time: float\n", + " Time of arrival to the simulation \n", + " \"\"\"\n", + " def __init__(self, patient_id: int, arrival_time: float):\n", + " self.patient_id = patient_id\n", + " self.arrival_time = arrival_time\n", + "\n", + " def __repr__(self):\n", + " \"\"\"\n", + " A text representation of the Patient for debug\n", + " \"\"\"\n", + " return f\"Patient(id={self.patient_id})\"" + ] + }, + { + "cell_type": "markdown", + "id": "bc5b1808-3f8e-43e2-b395-151de78bb718", + "metadata": {}, + "source": [ + "## 2.3 Ambulance dispatch and service process" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "08e53cbb-66dc-4e75-bcab-5ff1c6bf2a8b", + "metadata": {}, + "outputs": [], + "source": [ + "def dispatch_ambulance(\n", + " env: simpy.Environment,\n", + " store: simpy.Store,\n", + " patient: Patient,\n", + " dists: dict,\n", + " log: dict\n", + ") -> None:\n", + " \"\"\"Simulate ambulance dispatch and service: \n", + " \n", + " 1. queues a patient up for the next free Ambulance (FIFO), \n", + " 2. simulates service (as a single distribution), \n", + " 3. returns the Ambulance to the store.\n", + "\n", + " Parameters:\n", + " ----------\n", + " env: simpy.Environment\n", + " The simpy environment for the simulation\n", + " store: simpy.Store\n", + " A store of Ambulance objects\n", + " dists: dict\n", + " Contains the \"service\" distribution\n", + " log: dict\n", + " Audit dictionary\n", + " \"\"\"\n", + "\n", + " # Wait for an available Ambulance\n", + " # note we `get()` an Ambulance from the store\n", + " ambulance: Ambulance = yield store.get()\n", + "\n", + " wait_time = env.now - patient.arrival_time\n", + " log[\"wait_times\"].append(wait_time)\n", + "\n", + " # Service (travel + on-scene + return)\n", + " service_time = dists[\"service\"].sample()\n", + "\n", + " # debug\n", + " trace(\n", + " f\"{env.now:.1f} {patient} → {ambulance} \"\n", + " f\"(waited {wait_time:.1f} min, service {service_time:.1f} min)\"\n", + " )\n", + "\n", + " yield env.timeout(service_time)\n", + "\n", + " # Update ambulance stats and return (put) to store\n", + " ambulance.total_jobs += 1\n", + " ambulance.total_busy += service_time\n", + " store.put(ambulance)\n", + "\n", + " log[\"service_times\"].append(service_time)\n", + " log[\"assignments\"].append((patient.patient_id, ambulance.ambulance_id))" + ] + }, + { + "cell_type": "markdown", + "id": "af2955b7-fc92-474d-8238-6b3f78b47f0c", + "metadata": {}, + "source": [ + "### 2.4 Patient arrival generator" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "92544964-085b-4309-b138-0b7c8e9a1c14", + "metadata": {}, + "outputs": [], + "source": [ + "def patient_arrivals_generator(\n", + " env: simpy.Environment,\n", + " store: simpy.Store,\n", + " distributions: dict,\n", + " results: dict\n", + ") -> None:\n", + " \"\"\"\n", + " Arrival process for patients to the ambulance sim.\n", + "\n", + " Parameters:\n", + " ------\n", + " env: simpy.Environment\n", + " The simpy environment for the simulation\n", + "\n", + " store: simpy.Store\n", + " A store of Ambulance objects\n", + "\n", + " distributions: dict\n", + " Contains \"arrival\" and \"service\" distributions\n", + "\n", + " results: dict\n", + " Results dictionary\n", + " \"\"\"\n", + " for patient_id in itertools.count(start=1):\n", + "\n", + " # time until next patient arrival\n", + " inter_arrival_time = distributions[\"arrival\"].sample()\n", + " yield env.timeout(inter_arrival_time)\n", + "\n", + " results[\"n_arrivals\"] += 1\n", + " patient = Patient(patient_id, env.now)\n", + "\n", + " # debug info\n", + " trace(f\"{env.now:.1f}: Arrival. {patient}\")\n", + "\n", + " # create ambulance dispatch + service process\n", + " env.process(dispatch_ambulance(env, store, patient, distributions, results))" + ] + }, + { + "cell_type": "markdown", + "id": "9dcc1021-dbdb-4c39-a847-f263f09f6d54", + "metadata": {}, + "source": [ + "### 2.5 Single run function" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "a5d11438-61b0-4345-9ecc-83db0db32d17", + "metadata": {}, + "outputs": [], + "source": [ + "def single_run(\n", + " mean_iat: float = MEAN_INTERARRIVAL,\n", + " mean_service: float = MEAN_SERVICE_TIME,\n", + " n_ambulances: int = NUM_AMBULANCES,\n", + " run_length: float = RUN_LENGTH, \n", + " random_seed: int = 1\n", + "):\n", + " \"\"\"\n", + " Set up and perform a single replication of the MMS model\n", + " \"\"\"\n", + "\n", + " # 1. distribution objects\n", + " dists = {\n", + " \"arrival\": Exponential(mean_iat, random_seed=1),\n", + " \"service\": Exponential(mean_service, random_seed=2),\n", + " }\n", + "\n", + " # 2. simpy environment \n", + " env = simpy.Environment()\n", + "\n", + " # 3. Initialise Store\n", + " # 3.1 Create empty Store with sufficient slots\n", + " store = simpy.Store(env, capacity=n_ambulances)\n", + "\n", + " # 3.2 Create Ambulance objects\n", + " ambulances = [Ambulance(i + 1) for i in range(n_ambulances)]\n", + "\n", + " # 3.3 `put` Ambulance objects into the store\n", + " for amb in ambulances:\n", + " store.put(amb)\n", + "\n", + " # 4. results dictionary\n", + " log = {\"n_arrivals\": 0, \"wait_times\": [], \"service_times\": [], \"assignments\": []}\n", + "\n", + " env.process(patient_arrivals_generator(env, store, dists, log))\n", + " env.run(until=run_length)\n", + "\n", + " return ambulances, log" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "0506ba12-8039-4d13-a3a1-4d7e08efa44f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Simulation tracing set to: False\n", + "\n", + "═══════════════════════════════════════════════════════\n", + " Mean inter-arrival : 6.32 min\n", + "───────────────────────────────────────────────────────\n", + " Patients arrived : 149\n", + " Patients served : 149\n", + " Mean wait time : 4.97 min\n", + " P(wait > 0) : 39.60%\n", + " 95th pct wait : 22.91 min\n", + "───────────────────────────────────────────────────────\n", + " Ambulance Jobs Utilisation\n", + "───────────────────────────────────────────────────────\n", + " Ambulance 1 12 74.71%\n", + " Ambulance 2 11 58.18%\n", + " Ambulance 3 13 80.96%\n", + " Ambulance 4 18 77.13%\n", + " Ambulance 5 18 82.55%\n", + " Ambulance 6 8 70.16%\n", + " Ambulance 7 15 71.77%\n", + " Ambulance 8 14 78.69%\n", + " Ambulance 9 10 81.75%\n", + " Ambulance 10 20 69.14%\n", + "═══════════════════════════════════════════════════════\n" + ] + } + ], + "source": [ + "set_trace(False)\n", + "# no warm-up.\n", + "ambulances, log = single_run(random_seed=42)\n", + "\n", + "waits = np.array(log[\"wait_times\"])\n", + "services = np.array(log[\"service_times\"])\n", + "n = len(waits)\n", + "\n", + "print(\"\\n\" + \"═\" * 55)\n", + "print(f\" Mean inter-arrival : {MEAN_INTERARRIVAL:.2f} min\")\n", + "print(\"─\" * 55)\n", + "print(f\" Patients arrived : {log['n_arrivals']}\")\n", + "print(f\" Patients served : {n}\")\n", + "print(f\" Mean wait time : {waits.mean():.2f} min\")\n", + "print(f\" P(wait > 0) : {(waits > 0).mean():.2%}\")\n", + "print(f\" 95th pct wait : {np.percentile(waits, 95):.2f} min\")\n", + "print(\"─\" * 55)\n", + "print(f\" {'Ambulance':<15} {'Jobs':>6} {'Utilisation':>12}\")\n", + "print(\"─\" * 55)\n", + "\n", + "# calculate utilisation of each individual ambulance\n", + "for amb in ambulances:\n", + " util = amb.total_busy / RUN_LENGTH\n", + " print(f\" Ambulance {amb.ambulance_id:<5} {amb.total_jobs:>6} {util:>8.2%}\")\n", + "print(\"═\" * 55)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "afeef96b-a39a-402f-ba30-964fab4c3c14", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.15" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/content/sim_utility.py b/content/sim_utility.py new file mode 100644 index 0000000..57dbf0a --- /dev/null +++ b/content/sim_utility.py @@ -0,0 +1,36 @@ +# Internal module state for tracing (default to True) +_trace_state = {"enabled": True} + +# ---------------------------------------------------------------------------- +# HELPER FUNCTIONS & DISTRIBUTIONS +# ---------------------------------------------------------------------------- + +def set_trace(state: bool): + ''' + Toggles the simulation trace output on or off. + + Usage: + ------ + Call this before running your simulation to enable/disable printing. + + >>> set_trace(True) + + Params: + ------- + state: bool + True to enable print output, False to disable. + ''' + _trace_state["enabled"] = state + print(f"Simulation tracing set to: {state}") + +def trace(msg): + ''' + Prints an event message if tracing is enabled. + + Params: + ------- + msg: str + string to print to screen. + ''' + if _trace_state["enabled"]: + print(msg) From f26f03e1adebb3e44ad53495acab199c94d896ff Mon Sep 17 00:00:00 2001 From: TomMonks Date: Tue, 17 Mar 2026 16:28:53 +0000 Subject: [PATCH 2/5] feat(store): updated Store + FilterStore draft --- content/15_resource_stores.ipynb | 885 +++++++++++++++++++++++++++++-- content/sim_utility.py | 51 ++ 2 files changed, 887 insertions(+), 49 deletions(-) diff --git a/content/15_resource_stores.ipynb b/content/15_resource_stores.ipynb index 5886d47..367dadb 100644 --- a/content/15_resource_stores.ipynb +++ b/content/15_resource_stores.ipynb @@ -5,11 +5,13 @@ "id": "4da08d47-1de7-4634-a8fc-fc05657475f7", "metadata": {}, "source": [ - "# Advanced Resources: Stores\n", + "# Advanced Resources\n", "\n", "## Limitations of `Resource`\n", "\n", - "Up to this point the models we have looked have have used a basic `simpy.Resource`. One downside to `Resource` is that it does not allow you to give individual resources attributes or any type of complex behaviour. For many models, this is sufficient, but there may be instances where you need to track and control individual resources. For example, ambulances, or different types of staff. In this notebook we will explore how to add more complex behaviour using the `Store` and `FilterStore` objects provided by `simpy`." + "Up to this point the models we have looked have have used a basic `simpy.Resource`. This has been very useful to model queues, but one potential downside to `Resource` is that it *does not allow you to model individual resource attributes or any type of complex behaviour*. For many models in healthcare, this is sufficient, but there may be instances where you need to track and control individual resources. For example, ambulances, or different types of staff. In this notebook we will explore how to add more complex behaviour using the `Store` and `FilterStore` objects provided by `simpy`.\n", + "\n", + "The good news is that both `Store` and `FilterStore` are easy to use. Usually this is within the context of a complex simulation, but we will keep our models simple here and focus on how to use them." ] }, { @@ -28,8 +30,10 @@ "outputs": [], "source": [ "import numpy as np\n", + "import pandas as pd\n", "import itertools\n", - "import simpy" + "import simpy\n", + "import math" ] }, { @@ -43,9 +47,10 @@ "# you can also pip `install sim-tools`\n", "from distributions import (\n", " Exponential, \n", + " DiscreteEmpirical\n", ")\n", "\n", - "from sim_utility import set_trace, trace" + "from sim_utility import set_trace, trace, spawn_seeds" ] }, { @@ -53,30 +58,62 @@ "id": "b03d45cf-d0c9-4370-8922-561ea89e2f02", "metadata": {}, "source": [ - "## 2. Using a `simpy.Store`\n", + "## 2. PART 1: Using a `simpy.Store`\n", + "\n", + "A `Store` can be thought of as a \"box\" that we can `put` or `get` our own custom resource objects from in a First In First Out (FIFO) basis. **The objects do not need to be of the same type.**\n", + "\n", + "We create an instance of a `Store` as follows (`env` is the `simpy.Environment`):\n", + "\n", + "```python\n", + "store = simpy.Store(env, capacity=2)\n", + "```\n", + "We have to `put` something in the store. For example, let's add some ambulances.\n", + "\n", + "```python\n", + "ambulances = [\n", + " Ambulance(ambulance_id=1, vehicle_type=\"rrv\"), \n", + " Ambulance(ambulance_id=2, vehicle_type=\"type_1\")\n", + "]\n", + "\n", + "# loop through list of ambulances and put them in the store object\n", + "for amb in ambulances:\n", + " store.put(amb)\n", + "\n", + "```\n", "\n", - "A store is simpy a \"box\" that we can `put` or `get` our own custom resource objects from in a First In First Out (FIFO) basis.\n", + "To get an an ambulance we used the `get()` method along with the `yield` keyword.\n", "\n", + "```python\n", + "ambulance = yield store.get()\n", + "```\n", + "\n", + "Like normal `Resource` objects the use of `yield` means that we can simulate queues when no `Ambulance` objects are left in the store.\n", + " \n", "### 2.1 Parameters" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 4, "id": "d5b3eb67-9c8a-4fd8-9312-d337a65c52ee", "metadata": {}, "outputs": [], "source": [ - "NUM_AMBULANCES = 10\n", - "MEAN_SERVICE_TIME = 60.0 # minutes per call\n", - "TRAFFIC_INTENSITY = 0.95 # ρ = λ / (c · μ)\n", - "RUN_LENGTH = 1_000 # minutes\n", - "RANDOM_SEED = 42\n", + "NUM_AMBULANCES = 10\n", + "FLEET = {\n", + " \"rrv\": 4,\n", + " \"type_1\": 6,\n", + "}\n", + "\n", + "RRV_SERVICE_TIME = 50.0 # minutes\n", + "TYPE1_SERVICE_TIME = 65.0 \n", + "TRAFFIC_INTENSITY = 0.95 # ρ = λ / (c · μ)\n", + "RUN_LENGTH = 1_000 # minutes\n", + "RANDOM_SEED = 42\n", "\n", "# exponential IAT and service time so I have used traffic intensity to control IAT\n", "# ρ = λ / (c · μ) → mean_interarrival = mean_service / (ρ · c)\n", - "MEAN_INTERARRIVAL = MEAN_SERVICE_TIME / (TRAFFIC_INTENSITY * NUM_AMBULANCES)\n", - "# → 60 / (0.8 × 10) = 7.5 minutes between calls" + "MEAN_INTERARRIVAL = 6 # → 60 / (0.8 × 10) = 7.5 minutes between calls" ] }, { @@ -89,7 +126,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "id": "7956189c-c777-4c0a-afcc-ed06ec24ee3e", "metadata": {}, "outputs": [], @@ -103,8 +140,9 @@ " ambulance_id: int\n", " Unique id of the ambulance\n", " \"\"\"\n", - " def __init__(self, ambulance_id: int):\n", + " def __init__(self, ambulance_id: int, vehicle_type: str):\n", " self.ambulance_id = ambulance_id\n", + " self.vehicle_type = vehicle_type\n", " self.total_jobs = 0\n", " # cumulative busy time\n", " self.total_busy = 0.0 \n", @@ -118,7 +156,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "id": "fa690897-a9e5-473b-8f01-35991b1b4213", "metadata": {}, "outputs": [], @@ -156,7 +194,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "08e53cbb-66dc-4e75-bcab-5ff1c6bf2a8b", "metadata": {}, "outputs": [], @@ -193,12 +231,14 @@ " wait_time = env.now - patient.arrival_time\n", " log[\"wait_times\"].append(wait_time)\n", "\n", - " # Service (travel + on-scene + return)\n", - " service_time = dists[\"service\"].sample()\n", + " # Service varies by vehicle type (travel + on-scene + return)\n", + " service_dist = dists[ambulance.vehicle_type]\n", + " service_time = service_dist.sample()\n", "\n", " # debug\n", " trace(\n", - " f\"{env.now:.1f} {patient} → {ambulance} \"\n", + " f\"{env.now:.1f} {patient} → {ambulance} \"\n", + " f\"[{ambulance.vehicle_type}] \"\n", " f\"(waited {wait_time:.1f} min, service {service_time:.1f} min)\"\n", " )\n", "\n", @@ -210,7 +250,9 @@ " store.put(ambulance)\n", "\n", " log[\"service_times\"].append(service_time)\n", - " log[\"assignments\"].append((patient.patient_id, ambulance.ambulance_id))" + " log[\"assignments\"].append(\n", + " (patient.patient_id, ambulance.ambulance_id, ambulance.vehicle_type)\n", + " )" ] }, { @@ -223,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "id": "92544964-085b-4309-b138-0b7c8e9a1c14", "metadata": {}, "outputs": [], @@ -277,15 +319,17 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 9, "id": "a5d11438-61b0-4345-9ecc-83db0db32d17", "metadata": {}, "outputs": [], "source": [ "def single_run(\n", " mean_iat: float = MEAN_INTERARRIVAL,\n", - " mean_service: float = MEAN_SERVICE_TIME,\n", + " mean_rrv_service: float = RRV_SERVICE_TIME,\n", + " mean_type_1_service: float = TYPE1_SERVICE_TIME,\n", " n_ambulances: int = NUM_AMBULANCES,\n", + " fleet: list = FLEET,\n", " run_length: float = RUN_LENGTH, \n", " random_seed: int = 1\n", "):\n", @@ -293,10 +337,14 @@ " Set up and perform a single replication of the MMS model\n", " \"\"\"\n", "\n", + " # generate 3 rng seeds\n", + " seeds = spawn_seeds(n_streams=3, main_seed=random_seed)\n", + " \n", " # 1. distribution objects\n", " dists = {\n", - " \"arrival\": Exponential(mean_iat, random_seed=1),\n", - " \"service\": Exponential(mean_service, random_seed=2),\n", + " \"arrival\": Exponential(mean_iat, random_seed=seeds[0]),\n", + " \"rrv\": Exponential(mean_rrv_service, random_seed=seeds[1]),\n", + " \"type_1\": Exponential(mean_type_1_service, random_seed=seeds[2]),\n", " }\n", "\n", " # 2. simpy environment \n", @@ -307,8 +355,14 @@ " store = simpy.Store(env, capacity=n_ambulances)\n", "\n", " # 3.2 Create Ambulance objects\n", - " ambulances = [Ambulance(i + 1) for i in range(n_ambulances)]\n", - "\n", + " ambulances = []\n", + " ambulance_id = 0\n", + " \n", + " for vehicle_type, count in fleet.items():\n", + " for _ in range(count):\n", + " ambulance_id += 1\n", + " ambulances.append(Ambulance(ambulance_id, vehicle_type))\n", + " \n", " # 3.3 `put` Ambulance objects into the store\n", " for amb in ambulances:\n", " store.put(amb)\n", @@ -324,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 11, "id": "0506ba12-8039-4d13-a3a1-4d7e08efa44f", "metadata": {}, "outputs": [ @@ -332,35 +386,360 @@ "name": "stdout", "output_type": "stream", "text": [ - "Simulation tracing set to: False\n", + "Simulation tracing set to: True\n", + "5.8: Arrival. Patient(id=1)\n", + "5.8 Patient(id=1) → Ambulance(id=1) [rrv] (waited 0.0 min, service 9.7 min)\n", + "16.8: Arrival. Patient(id=2)\n", + "16.8 Patient(id=2) → Ambulance(id=2) [rrv] (waited 0.0 min, service 4.4 min)\n", + "19.5: Arrival. Patient(id=3)\n", + "19.5 Patient(id=3) → Ambulance(id=3) [rrv] (waited 0.0 min, service 24.1 min)\n", + "21.2: Arrival. Patient(id=4)\n", + "21.2 Patient(id=4) → Ambulance(id=4) [rrv] (waited 0.0 min, service 7.6 min)\n", + "40.8: Arrival. Patient(id=5)\n", + "40.8 Patient(id=5) → Ambulance(id=5) [type_1] (waited 0.0 min, service 12.3 min)\n", + "41.8: Arrival. Patient(id=6)\n", + "41.8 Patient(id=6) → Ambulance(id=6) [type_1] (waited 0.0 min, service 22.9 min)\n", + "42.1: Arrival. Patient(id=7)\n", + "42.1 Patient(id=7) → Ambulance(id=7) [type_1] (waited 0.0 min, service 8.4 min)\n", + "43.0: Arrival. Patient(id=8)\n", + "43.0 Patient(id=8) → Ambulance(id=8) [type_1] (waited 0.0 min, service 91.4 min)\n", + "69.8: Arrival. Patient(id=9)\n", + "69.8 Patient(id=9) → Ambulance(id=9) [type_1] (waited 0.0 min, service 47.0 min)\n", + "70.8: Arrival. Patient(id=10)\n", + "70.8 Patient(id=10) → Ambulance(id=10) [type_1] (waited 0.0 min, service 74.7 min)\n", + "71.1: Arrival. Patient(id=11)\n", + "71.1 Patient(id=11) → Ambulance(id=1) [rrv] (waited 0.0 min, service 93.0 min)\n", + "120.9: Arrival. Patient(id=12)\n", + "120.9 Patient(id=12) → Ambulance(id=2) [rrv] (waited 0.0 min, service 38.6 min)\n", + "121.7: Arrival. Patient(id=13)\n", + "121.7 Patient(id=13) → Ambulance(id=4) [rrv] (waited 0.0 min, service 14.2 min)\n", + "125.9: Arrival. Patient(id=14)\n", + "125.9 Patient(id=14) → Ambulance(id=3) [rrv] (waited 0.0 min, service 2.2 min)\n", + "126.1: Arrival. Patient(id=15)\n", + "126.1 Patient(id=15) → Ambulance(id=7) [type_1] (waited 0.0 min, service 61.5 min)\n", + "134.9: Arrival. Patient(id=16)\n", + "134.9 Patient(id=16) → Ambulance(id=5) [type_1] (waited 0.0 min, service 20.8 min)\n", + "145.9: Arrival. Patient(id=17)\n", + "145.9 Patient(id=17) → Ambulance(id=6) [type_1] (waited 0.0 min, service 72.5 min)\n", + "146.5: Arrival. Patient(id=18)\n", + "146.5 Patient(id=18) → Ambulance(id=9) [type_1] (waited 0.0 min, service 79.2 min)\n", + "149.1: Arrival. Patient(id=19)\n", + "149.1 Patient(id=19) → Ambulance(id=3) [rrv] (waited 0.0 min, service 76.0 min)\n", + "161.6: Arrival. Patient(id=20)\n", + "161.6 Patient(id=20) → Ambulance(id=8) [type_1] (waited 0.0 min, service 60.8 min)\n", + "169.9: Arrival. Patient(id=21)\n", + "169.9 Patient(id=21) → Ambulance(id=4) [rrv] (waited 0.0 min, service 5.0 min)\n", + "171.2: Arrival. Patient(id=22)\n", + "171.2 Patient(id=22) → Ambulance(id=10) [type_1] (waited 0.0 min, service 7.3 min)\n", + "174.2: Arrival. Patient(id=23)\n", + "174.2 Patient(id=23) → Ambulance(id=5) [type_1] (waited 0.0 min, service 234.3 min)\n", + "176.2: Arrival. Patient(id=24)\n", + "176.2 Patient(id=24) → Ambulance(id=2) [rrv] (waited 0.0 min, service 119.7 min)\n", + "188.7: Arrival. Patient(id=25)\n", + "188.7 Patient(id=25) → Ambulance(id=1) [rrv] (waited 0.0 min, service 30.0 min)\n", + "189.6: Arrival. Patient(id=26)\n", + "189.6 Patient(id=26) → Ambulance(id=4) [rrv] (waited 0.0 min, service 20.6 min)\n", + "191.1: Arrival. Patient(id=27)\n", + "191.1 Patient(id=27) → Ambulance(id=10) [type_1] (waited 0.0 min, service 12.5 min)\n", + "195.8: Arrival. Patient(id=28)\n", + "195.8 Patient(id=28) → Ambulance(id=7) [type_1] (waited 0.0 min, service 146.8 min)\n", + "199.4: Arrival. Patient(id=29)\n", + "199.8: Arrival. Patient(id=30)\n", + "203.7 Patient(id=29) → Ambulance(id=10) [type_1] (waited 4.2 min, service 179.5 min)\n", + "204.5: Arrival. Patient(id=31)\n", + "209.6: Arrival. Patient(id=32)\n", + "210.2 Patient(id=30) → Ambulance(id=4) [rrv] (waited 10.4 min, service 157.3 min)\n", + "218.4 Patient(id=31) → Ambulance(id=6) [type_1] (waited 13.8 min, service 36.3 min)\n", + "218.7 Patient(id=32) → Ambulance(id=1) [rrv] (waited 9.1 min, service 0.2 min)\n", + "219.1: Arrival. Patient(id=33)\n", + "219.1 Patient(id=33) → Ambulance(id=1) [rrv] (waited 0.0 min, service 50.3 min)\n", + "219.3: Arrival. Patient(id=34)\n", + "222.4 Patient(id=34) → Ambulance(id=8) [type_1] (waited 3.1 min, service 13.8 min)\n", + "230.4: Arrival. Patient(id=35)\n", + "230.4 Patient(id=35) → Ambulance(id=3) [rrv] (waited 0.0 min, service 127.0 min)\n", + "235.7: Arrival. Patient(id=36)\n", + "235.7 Patient(id=36) → Ambulance(id=9) [type_1] (waited 0.0 min, service 9.9 min)\n", + "236.3: Arrival. Patient(id=37)\n", + "236.3 Patient(id=37) → Ambulance(id=8) [type_1] (waited 0.0 min, service 146.3 min)\n", + "237.7: Arrival. Patient(id=38)\n", + "244.5: Arrival. Patient(id=39)\n", + "245.6 Patient(id=38) → Ambulance(id=9) [type_1] (waited 7.9 min, service 8.2 min)\n", + "245.9: Arrival. Patient(id=40)\n", + "246.8: Arrival. Patient(id=41)\n", + "253.8 Patient(id=39) → Ambulance(id=9) [type_1] (waited 9.3 min, service 19.3 min)\n", + "254.6 Patient(id=40) → Ambulance(id=6) [type_1] (waited 8.7 min, service 82.8 min)\n", + "265.4: Arrival. Patient(id=42)\n", + "269.4 Patient(id=41) → Ambulance(id=1) [rrv] (waited 22.6 min, service 0.3 min)\n", + "269.7 Patient(id=42) → Ambulance(id=1) [rrv] (waited 4.2 min, service 77.6 min)\n", + "270.8: Arrival. Patient(id=43)\n", + "273.1 Patient(id=43) → Ambulance(id=9) [type_1] (waited 2.2 min, service 95.4 min)\n", + "273.8: Arrival. Patient(id=44)\n", + "274.7: Arrival. Patient(id=45)\n", + "275.5: Arrival. Patient(id=46)\n", + "280.0: Arrival. Patient(id=47)\n", + "284.8: Arrival. Patient(id=48)\n", + "285.9: Arrival. Patient(id=49)\n", + "287.8: Arrival. Patient(id=50)\n", + "295.9 Patient(id=44) → Ambulance(id=2) [rrv] (waited 22.1 min, service 39.8 min)\n", + "297.7: Arrival. Patient(id=51)\n", + "303.3: Arrival. Patient(id=52)\n", + "316.4: Arrival. Patient(id=53)\n", + "328.3: Arrival. Patient(id=54)\n", + "335.7 Patient(id=45) → Ambulance(id=2) [rrv] (waited 61.0 min, service 24.9 min)\n", + "337.4 Patient(id=46) → Ambulance(id=6) [type_1] (waited 62.0 min, service 48.9 min)\n", + "342.6 Patient(id=47) → Ambulance(id=7) [type_1] (waited 62.6 min, service 0.9 min)\n", + "343.5 Patient(id=48) → Ambulance(id=7) [type_1] (waited 58.7 min, service 103.1 min)\n", + "345.2: Arrival. Patient(id=55)\n", + "346.5: Arrival. Patient(id=56)\n", + "347.3 Patient(id=49) → Ambulance(id=1) [rrv] (waited 61.3 min, service 25.9 min)\n", + "355.1: Arrival. Patient(id=57)\n", + "357.4 Patient(id=50) → Ambulance(id=3) [rrv] (waited 69.6 min, service 6.9 min)\n", + "359.4: Arrival. Patient(id=58)\n", + "360.6 Patient(id=51) → Ambulance(id=2) [rrv] (waited 62.9 min, service 50.6 min)\n", + "364.3 Patient(id=52) → Ambulance(id=3) [rrv] (waited 61.1 min, service 225.2 min)\n", + "366.8: Arrival. Patient(id=59)\n", + "367.5 Patient(id=53) → Ambulance(id=4) [rrv] (waited 51.1 min, service 176.5 min)\n", + "368.5 Patient(id=54) → Ambulance(id=9) [type_1] (waited 40.2 min, service 22.5 min)\n", + "373.2 Patient(id=55) → Ambulance(id=1) [rrv] (waited 27.9 min, service 12.3 min)\n", + "379.4: Arrival. Patient(id=60)\n", + "381.8: Arrival. Patient(id=61)\n", + "382.6 Patient(id=56) → Ambulance(id=8) [type_1] (waited 36.2 min, service 68.4 min)\n", + "383.1 Patient(id=57) → Ambulance(id=10) [type_1] (waited 28.1 min, service 34.7 min)\n", + "385.5 Patient(id=58) → Ambulance(id=1) [rrv] (waited 26.1 min, service 8.7 min)\n", + "386.4 Patient(id=59) → Ambulance(id=6) [type_1] (waited 19.5 min, service 39.0 min)\n", + "391.0 Patient(id=60) → Ambulance(id=9) [type_1] (waited 11.6 min, service 34.7 min)\n", + "394.2 Patient(id=61) → Ambulance(id=1) [rrv] (waited 12.4 min, service 3.4 min)\n", + "401.3: Arrival. Patient(id=62)\n", + "401.3 Patient(id=62) → Ambulance(id=1) [rrv] (waited 0.0 min, service 34.2 min)\n", + "402.8: Arrival. Patient(id=63)\n", + "403.1: Arrival. Patient(id=64)\n", + "408.2: Arrival. Patient(id=65)\n", + "408.6 Patient(id=63) → Ambulance(id=5) [type_1] (waited 5.8 min, service 49.2 min)\n", + "409.1: Arrival. Patient(id=66)\n", + "411.2 Patient(id=64) → Ambulance(id=2) [rrv] (waited 8.1 min, service 23.8 min)\n", + "412.1: Arrival. Patient(id=67)\n", + "413.0: Arrival. Patient(id=68)\n", + "415.4: Arrival. Patient(id=69)\n", + "417.8 Patient(id=65) → Ambulance(id=10) [type_1] (waited 9.6 min, service 90.4 min)\n", + "418.1: Arrival. Patient(id=70)\n", + "421.3: Arrival. Patient(id=71)\n", + "423.5: Arrival. Patient(id=72)\n", + "425.3 Patient(id=66) → Ambulance(id=6) [type_1] (waited 16.2 min, service 16.5 min)\n", + "425.7: Arrival. Patient(id=73)\n", + "425.7 Patient(id=67) → Ambulance(id=9) [type_1] (waited 13.6 min, service 27.6 min)\n", + "430.5: Arrival. Patient(id=74)\n", + "434.9: Arrival. Patient(id=75)\n", + "435.1 Patient(id=68) → Ambulance(id=2) [rrv] (waited 22.1 min, service 42.3 min)\n", + "435.4 Patient(id=69) → Ambulance(id=1) [rrv] (waited 20.0 min, service 11.0 min)\n", + "437.9: Arrival. Patient(id=76)\n", + "438.8: Arrival. Patient(id=77)\n", + "441.9 Patient(id=70) → Ambulance(id=6) [type_1] (waited 23.8 min, service 10.2 min)\n", + "446.4 Patient(id=71) → Ambulance(id=1) [rrv] (waited 25.1 min, service 49.6 min)\n", + "446.6 Patient(id=72) → Ambulance(id=7) [type_1] (waited 23.0 min, service 48.4 min)\n", + "451.1 Patient(id=73) → Ambulance(id=8) [type_1] (waited 25.4 min, service 61.8 min)\n", + "452.1: Arrival. Patient(id=78)\n", + "452.1 Patient(id=74) → Ambulance(id=6) [type_1] (waited 21.6 min, service 14.7 min)\n", + "453.3 Patient(id=75) → Ambulance(id=9) [type_1] (waited 18.3 min, service 41.8 min)\n", + "454.7: Arrival. Patient(id=79)\n", + "455.7: Arrival. Patient(id=80)\n", + "457.7 Patient(id=76) → Ambulance(id=5) [type_1] (waited 19.9 min, service 78.5 min)\n", + "461.4: Arrival. Patient(id=81)\n", + "466.8 Patient(id=77) → Ambulance(id=6) [type_1] (waited 28.0 min, service 2.4 min)\n", + "468.7: Arrival. Patient(id=82)\n", + "469.2 Patient(id=78) → Ambulance(id=6) [type_1] (waited 17.1 min, service 40.4 min)\n", + "472.6: Arrival. Patient(id=83)\n", + "477.4 Patient(id=79) → Ambulance(id=2) [rrv] (waited 22.7 min, service 50.6 min)\n", + "477.6: Arrival. Patient(id=84)\n", + "478.8: Arrival. Patient(id=85)\n", + "481.3: Arrival. Patient(id=86)\n", + "485.8: Arrival. Patient(id=87)\n", + "486.1: Arrival. Patient(id=88)\n", + "488.4: Arrival. Patient(id=89)\n", + "493.1: Arrival. Patient(id=90)\n", + "493.3: Arrival. Patient(id=91)\n", + "495.0 Patient(id=80) → Ambulance(id=7) [type_1] (waited 39.2 min, service 93.5 min)\n", + "495.1 Patient(id=81) → Ambulance(id=9) [type_1] (waited 33.7 min, service 20.3 min)\n", + "496.0 Patient(id=82) → Ambulance(id=1) [rrv] (waited 27.2 min, service 44.3 min)\n", + "503.2: Arrival. Patient(id=92)\n", + "508.2 Patient(id=83) → Ambulance(id=10) [type_1] (waited 35.6 min, service 87.1 min)\n", + "509.6 Patient(id=84) → Ambulance(id=6) [type_1] (waited 32.1 min, service 38.4 min)\n", + "512.9 Patient(id=85) → Ambulance(id=8) [type_1] (waited 34.0 min, service 43.4 min)\n", + "513.8: Arrival. Patient(id=93)\n", + "515.4 Patient(id=86) → Ambulance(id=9) [type_1] (waited 34.2 min, service 86.2 min)\n", + "525.1: Arrival. Patient(id=94)\n", + "525.5: Arrival. Patient(id=95)\n", + "528.0 Patient(id=87) → Ambulance(id=2) [rrv] (waited 42.1 min, service 69.6 min)\n", + "531.8: Arrival. Patient(id=96)\n", + "536.3 Patient(id=88) → Ambulance(id=5) [type_1] (waited 50.2 min, service 132.0 min)\n", + "540.0: Arrival. Patient(id=97)\n", + "540.2 Patient(id=89) → Ambulance(id=1) [rrv] (waited 51.9 min, service 118.8 min)\n", + "541.4: Arrival. Patient(id=98)\n", + "544.0 Patient(id=90) → Ambulance(id=4) [rrv] (waited 50.9 min, service 7.4 min)\n", + "548.0 Patient(id=91) → Ambulance(id=6) [type_1] (waited 54.7 min, service 39.8 min)\n", + "550.3: Arrival. Patient(id=99)\n", + "551.5 Patient(id=92) → Ambulance(id=4) [rrv] (waited 48.3 min, service 249.4 min)\n", + "555.6: Arrival. Patient(id=100)\n", + "556.3 Patient(id=93) → Ambulance(id=8) [type_1] (waited 42.5 min, service 78.1 min)\n", + "559.4: Arrival. Patient(id=101)\n", + "560.2: Arrival. Patient(id=102)\n", + "568.1: Arrival. Patient(id=103)\n", + "582.8: Arrival. Patient(id=104)\n", + "587.8 Patient(id=94) → Ambulance(id=6) [type_1] (waited 62.7 min, service 42.4 min)\n", + "588.5 Patient(id=95) → Ambulance(id=7) [type_1] (waited 63.0 min, service 2.7 min)\n", + "588.9: Arrival. Patient(id=105)\n", + "589.2: Arrival. Patient(id=106)\n", + "589.5 Patient(id=96) → Ambulance(id=3) [rrv] (waited 57.7 min, service 61.9 min)\n", + "591.2 Patient(id=97) → Ambulance(id=7) [type_1] (waited 51.2 min, service 194.1 min)\n", + "595.4 Patient(id=98) → Ambulance(id=10) [type_1] (waited 54.0 min, service 18.8 min)\n", + "597.6 Patient(id=99) → Ambulance(id=2) [rrv] (waited 47.3 min, service 19.5 min)\n", + "600.4: Arrival. Patient(id=107)\n", + "601.7 Patient(id=100) → Ambulance(id=9) [type_1] (waited 46.1 min, service 74.3 min)\n", + "603.1: Arrival. Patient(id=108)\n", + "608.6: Arrival. Patient(id=109)\n", + "609.3: Arrival. Patient(id=110)\n", + "614.2 Patient(id=101) → Ambulance(id=10) [type_1] (waited 54.7 min, service 127.6 min)\n", + "617.1 Patient(id=102) → Ambulance(id=2) [rrv] (waited 56.9 min, service 26.8 min)\n", + "629.8: Arrival. Patient(id=111)\n", + "630.2 Patient(id=103) → Ambulance(id=6) [type_1] (waited 62.2 min, service 228.2 min)\n", + "634.3 Patient(id=104) → Ambulance(id=8) [type_1] (waited 51.5 min, service 21.9 min)\n", + "635.3: Arrival. Patient(id=112)\n", + "640.9: Arrival. Patient(id=113)\n", + "643.9 Patient(id=105) → Ambulance(id=2) [rrv] (waited 55.0 min, service 73.2 min)\n", + "651.5 Patient(id=106) → Ambulance(id=3) [rrv] (waited 62.2 min, service 84.7 min)\n", + "653.3: Arrival. Patient(id=114)\n", + "656.3 Patient(id=107) → Ambulance(id=8) [type_1] (waited 55.8 min, service 10.1 min)\n", + "659.1 Patient(id=108) → Ambulance(id=1) [rrv] (waited 56.0 min, service 150.4 min)\n", + "666.3: Arrival. Patient(id=115)\n", + "666.4 Patient(id=109) → Ambulance(id=8) [type_1] (waited 57.7 min, service 73.4 min)\n", + "668.3 Patient(id=110) → Ambulance(id=5) [type_1] (waited 59.0 min, service 22.4 min)\n", + "669.8: Arrival. Patient(id=116)\n", + "674.4: Arrival. Patient(id=117)\n", + "676.0 Patient(id=111) → Ambulance(id=9) [type_1] (waited 46.2 min, service 85.9 min)\n", + "690.4: Arrival. Patient(id=118)\n", + "690.5: Arrival. Patient(id=119)\n", + "690.7 Patient(id=112) → Ambulance(id=5) [type_1] (waited 55.4 min, service 150.0 min)\n", + "693.3: Arrival. Patient(id=120)\n", + "693.7: Arrival. Patient(id=121)\n", + "699.7: Arrival. Patient(id=122)\n", + "700.2: Arrival. Patient(id=123)\n", + "717.2 Patient(id=113) → Ambulance(id=2) [rrv] (waited 76.3 min, service 73.6 min)\n", + "723.7: Arrival. Patient(id=124)\n", + "732.3: Arrival. Patient(id=125)\n", + "736.2 Patient(id=114) → Ambulance(id=3) [rrv] (waited 82.9 min, service 21.7 min)\n", + "738.0: Arrival. Patient(id=126)\n", + "739.8 Patient(id=115) → Ambulance(id=8) [type_1] (waited 73.5 min, service 9.4 min)\n", + "741.7 Patient(id=116) → Ambulance(id=10) [type_1] (waited 71.9 min, service 65.6 min)\n", + "749.2 Patient(id=117) → Ambulance(id=8) [type_1] (waited 74.8 min, service 99.1 min)\n", + "749.5: Arrival. Patient(id=127)\n", + "757.5: Arrival. Patient(id=128)\n", + "757.9 Patient(id=118) → Ambulance(id=3) [rrv] (waited 67.5 min, service 60.5 min)\n", + "761.9 Patient(id=119) → Ambulance(id=9) [type_1] (waited 71.4 min, service 237.9 min)\n", + "778.6: Arrival. Patient(id=129)\n", + "780.5: Arrival. Patient(id=130)\n", + "781.5: Arrival. Patient(id=131)\n", + "782.6: Arrival. Patient(id=132)\n", + "785.2 Patient(id=120) → Ambulance(id=7) [type_1] (waited 91.9 min, service 120.9 min)\n", + "787.4: Arrival. Patient(id=133)\n", + "788.0: Arrival. Patient(id=134)\n", + "790.8 Patient(id=121) → Ambulance(id=2) [rrv] (waited 97.1 min, service 8.0 min)\n", + "798.8: Arrival. Patient(id=135)\n", + "798.8 Patient(id=122) → Ambulance(id=2) [rrv] (waited 99.1 min, service 74.6 min)\n", + "800.9 Patient(id=123) → Ambulance(id=4) [rrv] (waited 100.7 min, service 156.5 min)\n", + "807.3 Patient(id=124) → Ambulance(id=10) [type_1] (waited 83.6 min, service 117.9 min)\n", + "809.5 Patient(id=125) → Ambulance(id=1) [rrv] (waited 77.2 min, service 13.1 min)\n", + "812.4: Arrival. Patient(id=136)\n", + "812.8: Arrival. Patient(id=137)\n", + "818.4 Patient(id=126) → Ambulance(id=3) [rrv] (waited 80.3 min, service 56.7 min)\n", + "822.6 Patient(id=127) → Ambulance(id=1) [rrv] (waited 73.1 min, service 58.1 min)\n", + "835.8: Arrival. Patient(id=138)\n", + "838.3: Arrival. Patient(id=139)\n", + "840.7 Patient(id=128) → Ambulance(id=5) [type_1] (waited 83.3 min, service 56.9 min)\n", + "840.7: Arrival. Patient(id=140)\n", + "846.6: Arrival. Patient(id=141)\n", + "847.3: Arrival. Patient(id=142)\n", + "848.3 Patient(id=129) → Ambulance(id=8) [type_1] (waited 69.7 min, service 40.2 min)\n", + "858.5 Patient(id=130) → Ambulance(id=6) [type_1] (waited 78.0 min, service 39.4 min)\n", + "871.6: Arrival. Patient(id=143)\n", + "873.4 Patient(id=131) → Ambulance(id=2) [rrv] (waited 91.9 min, service 35.0 min)\n", + "875.1 Patient(id=132) → Ambulance(id=3) [rrv] (waited 92.5 min, service 24.0 min)\n", + "880.7 Patient(id=133) → Ambulance(id=1) [rrv] (waited 93.3 min, service 121.3 min)\n", + "888.5 Patient(id=134) → Ambulance(id=8) [type_1] (waited 100.5 min, service 0.3 min)\n", + "888.8 Patient(id=135) → Ambulance(id=8) [type_1] (waited 90.0 min, service 87.8 min)\n", + "890.9: Arrival. Patient(id=144)\n", + "894.3: Arrival. Patient(id=145)\n", + "897.6 Patient(id=136) → Ambulance(id=5) [type_1] (waited 85.2 min, service 67.6 min)\n", + "897.9 Patient(id=137) → Ambulance(id=6) [type_1] (waited 85.0 min, service 34.9 min)\n", + "899.1 Patient(id=138) → Ambulance(id=3) [rrv] (waited 63.3 min, service 87.5 min)\n", + "900.6: Arrival. Patient(id=146)\n", + "904.8: Arrival. Patient(id=147)\n", + "906.2 Patient(id=139) → Ambulance(id=7) [type_1] (waited 67.8 min, service 16.1 min)\n", + "908.4 Patient(id=140) → Ambulance(id=2) [rrv] (waited 67.7 min, service 90.9 min)\n", + "910.9: Arrival. Patient(id=148)\n", + "911.2: Arrival. Patient(id=149)\n", + "916.7: Arrival. Patient(id=150)\n", + "921.4: Arrival. Patient(id=151)\n", + "922.3 Patient(id=141) → Ambulance(id=7) [type_1] (waited 75.7 min, service 0.6 min)\n", + "922.9 Patient(id=142) → Ambulance(id=7) [type_1] (waited 75.6 min, service 29.7 min)\n", + "924.2: Arrival. Patient(id=152)\n", + "925.2 Patient(id=143) → Ambulance(id=10) [type_1] (waited 53.6 min, service 4.8 min)\n", + "926.8: Arrival. Patient(id=153)\n", + "930.0 Patient(id=144) → Ambulance(id=10) [type_1] (waited 39.1 min, service 41.0 min)\n", + "932.8 Patient(id=145) → Ambulance(id=6) [type_1] (waited 38.4 min, service 2.6 min)\n", + "935.4 Patient(id=146) → Ambulance(id=6) [type_1] (waited 34.8 min, service 166.5 min)\n", + "936.3: Arrival. Patient(id=154)\n", + "936.6: Arrival. Patient(id=155)\n", + "936.7: Arrival. Patient(id=156)\n", + "939.3: Arrival. Patient(id=157)\n", + "940.4: Arrival. Patient(id=158)\n", + "941.0: Arrival. Patient(id=159)\n", + "947.0: Arrival. Patient(id=160)\n", + "950.1: Arrival. Patient(id=161)\n", + "952.6 Patient(id=147) → Ambulance(id=7) [type_1] (waited 47.8 min, service 8.4 min)\n", + "954.1: Arrival. Patient(id=162)\n", + "957.4 Patient(id=148) → Ambulance(id=4) [rrv] (waited 46.5 min, service 51.1 min)\n", + "957.5: Arrival. Patient(id=163)\n", + "959.0: Arrival. Patient(id=164)\n", + "961.0 Patient(id=149) → Ambulance(id=7) [type_1] (waited 49.8 min, service 38.1 min)\n", + "965.2 Patient(id=150) → Ambulance(id=5) [type_1] (waited 48.5 min, service 54.1 min)\n", + "965.6: Arrival. Patient(id=165)\n", + "971.0 Patient(id=151) → Ambulance(id=10) [type_1] (waited 49.6 min, service 126.2 min)\n", + "976.6 Patient(id=152) → Ambulance(id=8) [type_1] (waited 52.4 min, service 3.2 min)\n", + "979.8 Patient(id=153) → Ambulance(id=8) [type_1] (waited 53.0 min, service 25.9 min)\n", + "981.8: Arrival. Patient(id=166)\n", + "986.6 Patient(id=154) → Ambulance(id=3) [rrv] (waited 50.3 min, service 14.6 min)\n", + "989.9: Arrival. Patient(id=167)\n", + "992.3: Arrival. Patient(id=168)\n", + "999.0 Patient(id=155) → Ambulance(id=7) [type_1] (waited 62.4 min, service 55.0 min)\n", + "999.4 Patient(id=156) → Ambulance(id=2) [rrv] (waited 62.7 min, service 38.6 min)\n", + "999.8 Patient(id=157) → Ambulance(id=9) [type_1] (waited 60.5 min, service 30.0 min)\n", "\n", "═══════════════════════════════════════════════════════\n", - " Mean inter-arrival : 6.32 min\n", + " Mean inter-arrival : 6.00 min\n", "───────────────────────────────────────────────────────\n", - " Patients arrived : 149\n", - " Patients served : 149\n", - " Mean wait time : 4.97 min\n", - " P(wait > 0) : 39.60%\n", - " 95th pct wait : 22.91 min\n", + " Patients arrived : 168\n", + " Patients served : 157\n", + " Mean wait time : 38.29 min\n", + " P(wait > 0) : 78.98%\n", + " 95th pct wait : 90.39 min\n", "───────────────────────────────────────────────────────\n", " Ambulance Jobs Utilisation\n", "───────────────────────────────────────────────────────\n", - " Ambulance 1 12 74.71%\n", - " Ambulance 2 11 58.18%\n", - " Ambulance 3 13 80.96%\n", - " Ambulance 4 18 77.13%\n", - " Ambulance 5 18 82.55%\n", - " Ambulance 6 8 70.16%\n", - " Ambulance 7 15 71.77%\n", - " Ambulance 8 14 78.69%\n", - " Ambulance 9 10 81.75%\n", - " Ambulance 10 20 69.14%\n", + " Ambulance 1 19 79.09%\n", + " Ambulance 2 18 86.62%\n", + " Ambulance 3 13 85.86%\n", + " Ambulance 4 9 79.45%\n", + " Ambulance 5 10 82.41%\n", + " Ambulance 6 18 81.24%\n", + " Ambulance 7 15 87.30%\n", + " Ambulance 8 17 90.95%\n", + " Ambulance 9 15 89.03%\n", + " Ambulance 10 13 86.19%\n", "═══════════════════════════════════════════════════════\n" ] } ], "source": [ - "set_trace(False)\n", + "set_trace(True)\n", "# no warm-up.\n", "ambulances, log = single_run(random_seed=42)\n", "\n", @@ -387,10 +766,418 @@ "print(\"═\" * 55)" ] }, + { + "cell_type": "markdown", + "id": "17ba6461-a625-42d4-b4cf-13fd546c7df5", + "metadata": {}, + "source": [ + "## 3. PART 2: Using a `FilterStore`\n", + "\n", + "In this example we will modify the ambulance dispatch simulation so that patients and ambulances are located within one of five **nodes** that are specificed by coordinates.\n", + "\n", + "| ID | Label | Coordinates |\n", + "|---|---|---|\n", + "| 0 | South-West | `(0, 0)` |\n", + "| 1 | South-East | `(4, 0)` |\n", + "| 2 | North-West | `(0, 4)` |\n", + "| 3 | North-East | `(4, 4)` |\n", + "| 4 | Centre | `(2, 2)` |\n", + "| 5 | Hospital | `(2, 0)` |\n", + "\n", + "\n", + "\n", + "Patients in need will be assigned the closest available ambulance. If not ambulances are available they are assigned the next available ambulance when it has returned to its home node." + ] + }, + { + "cell_type": "markdown", + "id": "468a41d0-42e9-4fd1-813c-76b7457452b0", + "metadata": {}, + "source": [ + "### 3.1 Geographic information" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0eabc7f2-bb0e-4368-a245-2a123c0ba420", + "metadata": {}, + "outputs": [], + "source": [ + "# All locations share a single distance matrix: nodes 0-4 plus hospital (5)\n", + "HOSPITAL_ID = 5\n", + "\n", + "LOCATIONS = {\n", + " 0: (0.0, 0.0), # South-West\n", + " 1: (4.0, 0.0), # South-East\n", + " 2: (0.0, 4.0), # North-West\n", + " 3: (4.0, 4.0), # North-East\n", + " 4: (2.0, 2.0), # Centre\n", + " HOSPITAL_ID: (2.0, 0.0), # Hospital (south-centre)\n", + "}\n", + "\n", + "# nodes patients can appear in\n", + "PATIENT_NODES = [0, 1, 2, 3, 4] \n", + "NODE_PROBS = [0.15, 0.25, 0.20, 0.20, 0.20]\n", + "\n", + "# Pre-compute ALL pairwise distances before simulation\n", + "DISTANCES = {\n", + " (i, j): math.dist(LOCATIONS[i], LOCATIONS[j])\n", + " for i in LOCATIONS\n", + " for j in LOCATIONS\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "88223687-d608-457b-8464-542c020c594d", + "metadata": {}, + "source": [ + "### 3.2 Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1079ec2e-1d51-447d-99b1-11d9f081e36e", + "metadata": {}, + "outputs": [], + "source": [ + "NUM_AMBULANCES = 10\n", + "TRAVEL_SPEED = 0.25 # distance units per minute\n", + "MEAN_SCENE_TIME = 20.0 \n", + "MEAN_INTERARRIVAL = 6 \n", + "RUN_LENGTH = 1_000\n", + "RANDOM_SEED = 42\n", + "\n", + "# Ambulance positioning parameter\n", + "# 2 ambulances stationed at each of the 5 nodes\n", + "AMBULANCE_HOME_NODES = [\n", + " node\n", + " for node in PATIENT_NODES\n", + " for _ in range(NUM_AMBULANCES // len(PATIENT_NODES))\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "16800dd7-7f83-4c42-bde6-0473fa15a1de", + "metadata": {}, + "source": [ + "### 3.3 Entity Classes\n", + "\n", + "The `Ambulance` and `Patient` classes have been slightly modified to include a `node` attribute" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "723855d2-1389-46cc-9a7f-663369dbd686", + "metadata": {}, + "outputs": [], + "source": [ + "class Ambulance:\n", + " def __init__(self, ambulance_id: int, home_node: int):\n", + " self.ambulance_id = ambulance_id\n", + " # modification = a home node or 'base' for the ambulance\n", + " self.home_node = home_node\n", + " self.total_jobs = 0\n", + " self.total_busy = 0.0\n", + "\n", + " def __repr__(self):\n", + " return f\"Ambulance(id={self.ambulance_id}, node={self.home_node})\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7031697-436e-4b85-bbde-91bde6ed3806", + "metadata": {}, + "outputs": [], + "source": [ + "class Patient:\n", + " def __init__(self, patient_id: int, arrival_time: float, node: int):\n", + " self.patient_id = patient_id\n", + " self.arrival_time = arrival_time\n", + " self.node = node\n", + "\n", + " def __repr__(self):\n", + " return f\"Patient(id={self.patient_id}, node={self.node})\"" + ] + }, + { + "cell_type": "markdown", + "id": "5cd315ba-0cf9-45bd-bee0-9c6796159622", + "metadata": {}, + "source": [ + "### 3.4 Travel functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a6c9fe6-4c86-4b6c-a758-2dba9fa622de", + "metadata": {}, + "outputs": [], + "source": [ + "def travel_time(loc_a: int, loc_b: int) -> float:\n", + " \"\"\"Minutes to travel between any two location IDs.\"\"\"\n", + " return DISTANCES[loc_a, loc_b] / TRAVEL_SPEED" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17c05a56-2f6a-4ffa-b65b-02d8ecc94671", + "metadata": {}, + "outputs": [], + "source": [ + "def closest_ambulance_id(\n", + " available: list[Ambulance],\n", + " patient_node: int\n", + ") -> int | None:\n", + " \n", + " if not available:\n", + " return None\n", + " \n", + " return min(available, key=lambda a: DISTANCES[a.home_node, patient_node]).ambulance_id" + ] + }, + { + "cell_type": "markdown", + "id": "9d7f1394-e263-4f61-ab8b-f27c22ffc86f", + "metadata": {}, + "source": [ + "### 3.5 Modified simpy processes\n", + "\n", + "The biggest update is to our ambulance dispatch function that will now use a `FilterStore` to take account of `Patient` and `Ambulance` node location." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "383ad331-bba1-4571-9386-28d60d16635e", + "metadata": {}, + "outputs": [], + "source": [ + "def dispatch_ambulance(env, store, patient, dists, log):\n", + " \"\"\"Modified ambulance dispatch process\n", + " \"\"\"\n", + " \n", + " # find the closest ambulance in the store\n", + " # note we pass store.items which is a list of Ambulance objects\n", + " # it may be empty!\n", + " best_id = closest_ambulance_id(store.items, patient.node)\n", + "\n", + " if best_id is not None:\n", + " # if an ambulance is available get the closest ambulance from the store\n", + " ambulance = yield store.get(lambda a: a.ambulance_id == best_id)\n", + " else:\n", + " # other wise just wait for the next available ambulance. FIFO\n", + " ambulance = yield store.get(lambda a: True)\n", + "\n", + " wait_time = env.now - patient.arrival_time\n", + "\n", + " # Leg 1: travel from home node to patient \n", + " t_to_patient = travel_time(ambulance.home_node, patient.node)\n", + "\n", + " # Leg 2: on scene\n", + " scene_time = dists[\"on_scene\"].sample()\n", + "\n", + " # Leg 3: transport patient to hospital\n", + " t_to_hospital = travel_time(patient.node, HOSPITAL_ID)\n", + "\n", + " # Leg 4: return to home base \n", + " t_to_base = travel_time(HOSPITAL_ID, ambulance.home_node)\n", + "\n", + " # total turnaround time\n", + " total_service = t_to_patient + scene_time + t_to_hospital + t_to_base\n", + " yield env.timeout(total_service)\n", + " \n", + " ambulance.total_jobs += 1\n", + " ambulance.total_busy += total_service\n", + " store.put(ambulance)\n", + "\n", + " log[\"wait_times\"].append(wait_time)\n", + "\n", + " # create row in the assignments table\n", + " log[\"assignments\"].append(\n", + " dict(patient_id=patient.patient_id,\n", + " patient_node=patient.node,\n", + " ambulance_id=ambulance.ambulance_id,\n", + " ambulance_node=ambulance.home_node,\n", + " dispatch_dist=DISTANCES[ambulance.home_node, patient.node],\n", + " t_to_patient=t_to_patient,\n", + " scene_time=scene_time,\n", + " t_to_hospital=t_to_hospital,\n", + " t_to_base=t_to_base,\n", + " total_service=total_service,\n", + " immediate_dispatch=best_id is not None,\n", + " wait=wait_time)\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4155f0a2-4ee7-4c69-954d-a6aee59a0a12", + "metadata": {}, + "outputs": [], + "source": [ + "def patient_arrivals_generator(\n", + " env: simpy.Environment,\n", + " store: simpy.Store,\n", + " dists: dict,\n", + " log: dict\n", + ") -> None: \n", + " \"\"\"Modified Arrival process for patients to the ambulance sim\n", + " Now includes arrival node\"\"\"\n", + " for patient_id in itertools.count(start=1):\n", + "\n", + " # time until next patient arrival\n", + " inter_arrival_time = dists[\"arrival\"].sample()\n", + " yield env.timeout(inter_arrival_time)\n", + "\n", + " log[\"n_arrivals\"] += 1\n", + " node = dists[\"arrival_node\"].sample()\n", + " patient = Patient(patient_id, env.now, node)\n", + "\n", + " # debug info\n", + " trace(f\"{env.now:.1f}: Arrival. {patient}\")\n", + "\n", + " # create ambulance dispatch + service process\n", + " env.process(dispatch_ambulance(env, store, patient, dists, log))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df33aaf6-df42-4fb8-918f-fefae73fc9eb", + "metadata": {}, + "outputs": [], + "source": [ + "def single_run(\n", + " mean_iat: float = MEAN_INTERARRIVAL,\n", + " mean_on_scene: float = MEAN_SCENE_TIME,\n", + " n_ambulances: int = NUM_AMBULANCES,\n", + " run_length: float = RUN_LENGTH, \n", + " random_seed: int = 42\n", + "):\n", + " \"\"\"\n", + " Set up and perform a single replication of the MMS model\n", + " \"\"\"\n", + "\n", + " # generate 3 rng seeds\n", + " seeds = spawn_seeds(n_streams=3, main_seed=random_seed)\n", + " \n", + " # 1. distribution objects\n", + " # We have included a new arrival_node an on_scene dists\n", + " dists = {\n", + " \"arrival\": Exponential(mean_iat, random_seed=seeds[0]),\n", + " \"arrival_node\": DiscreteEmpirical(\n", + " values=[0, 1, 2, 3, 4], \n", + " freq=[p * 100 for p in NODE_PROBS],\n", + " random_seed=seeds[1]),\n", + " \"on_scene\": Exponential(mean_on_scene, random_seed=seeds[2]),\n", + " }\n", + "\n", + " # 2. simpy environment \n", + " env = simpy.Environment()\n", + "\n", + " # 3. Initialise Store\n", + " # 3.1 Create empty Store with sufficient slots\n", + " store = simpy.FilterStore(env, capacity=n_ambulances)\n", + "\n", + " # 3.2 Create Ambulance objects now with home nodes\n", + " ambulances = [Ambulance(i + 1, home) for i, home in enumerate(AMBULANCE_HOME_NODES)]\n", + "\n", + " # 3.3 `put` Ambulance objects into the filter store\n", + " for amb in ambulances:\n", + " store.put(amb)\n", + "\n", + " # 4. results dictionary\n", + " log = {\"n_arrivals\": 0, \"wait_times\": [], \"service_times\": [], \"assignments\": []}\n", + "\n", + " env.process(patient_arrivals_generator(env, store, dists, log))\n", + " env.run(until=run_length)\n", + "\n", + " return ambulances, log" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1aa60d11-3dfb-4d2a-9bd1-b75259f9aab5", + "metadata": {}, + "outputs": [], + "source": [ + "def single_run_summary(assignments, ambulances):\n", + " waits = assignments[\"wait\"].to_numpy()\n", + " \n", + " # immediate versus delayed dispatches of Ambulance\n", + " immediate = assignments[assignments[\"immediate_dispatch\"]]\n", + " delayed = assignments[~assignments[\"immediate_dispatch\"]]\n", + " \n", + " print(\"\\n\" + \"═\" * 65)\n", + " print(f\" Patients served : {len(assignments)}\")\n", + " print(f\" Immediate Dispatch : {len(immediate)} ({len(immediate)/len(assignments):.1%})\")\n", + " print(f\" Delayed Dispatch : {len(delayed)} ({len(delayed)/len(assignments):.1%})\")\n", + " print(f\" Overall mean wait (min) : {waits.mean():.2f}\")\n", + " print(\"─\" * 65)\n", + " print(f\" {'':<30} {'Ambulance Dispatch':>20}\")\n", + " print(f\" {'Metric':<30} {'Immediate':>10} {'Delayed':>10}\")\n", + " print(\"─\" * 65)\n", + " for metric, col in [(\"Mean wait (min)\", \"wait\"),\n", + " (\"Mean dispatch dist\", \"dispatch_dist\"),\n", + " (\"Mean travel to patient\", \"t_to_patient\"),\n", + " (\"Mean scene time\", \"scene_time\"),\n", + " (\"Mean travel to hospital\", \"t_to_hospital\"),\n", + " (\"Mean return to base\", \"t_to_base\"),\n", + " (\"Mean total service\", \"total_service\")]:\n", + " c = immediate[col].mean() if len(immediate) else float(\"nan\")\n", + " f = delayed[col].mean() if len(delayed) else float(\"nan\")\n", + " \n", + " print(f\" {metric:<30} {c:>10.2f} {f:>10.2f}\")\n", + " print(\"─\" * 65)\n", + " print(f\" {'Ambulance':<14} {'Node':>10} {'Jobs':>8} {'Util':>10}\")\n", + " print(\"─\" * 65)\n", + " for amb in ambulances:\n", + " print(f\" Ambulance {amb.ambulance_id:<4} \"\n", + " f\"{amb.home_node:>5} {amb.total_jobs:>6} \"\n", + " f\"{amb.total_busy / RUN_LENGTH:>8.2%}\")\n", + " print(\"═\" * 65)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19c9b8e8-7a2b-4d51-b070-9ba71463f44f", + "metadata": {}, + "outputs": [], + "source": [ + "# run model\n", + "ambulances, log = single_run(random_seed=42)\n", + "\n", + "# get all of the patient to ambulance assignment details\n", + "assignments = pd.DataFrame(log[\"assignments\"])\n", + "assignments.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90dc1713-9d25-4e2c-adc8-f51a123c4534", + "metadata": {}, + "outputs": [], + "source": [ + "ambulances, log = single_run(random_seed=42)\n", + "single_run_summary(assignments, ambulances)" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "afeef96b-a39a-402f-ba30-964fab4c3c14", + "id": "4090cd6c-535b-4e2e-aad1-b5db8c2b8941", "metadata": {}, "outputs": [], "source": [] diff --git a/content/sim_utility.py b/content/sim_utility.py index 57dbf0a..906ddc5 100644 --- a/content/sim_utility.py +++ b/content/sim_utility.py @@ -1,3 +1,7 @@ +import numpy as np +from typing import Optional + + # Internal module state for tracing (default to True) _trace_state = {"enabled": True} @@ -34,3 +38,50 @@ def trace(msg): ''' if _trace_state["enabled"]: print(msg) + +def spawn_seeds(n_streams: int, main_seed: Optional[int] = None) -> list[np.random.SeedSequence]: + """ + Taken from `sim-tools` + + Generate multiple statistically independent random seeds. + + This function creates a set of SeedSequence objects that are guaranteed + to produce independent streams of random numbers. This is crucial for + ensuring that multiple random number generators don't produce correlated + outputs, which could bias simulation results. + + Parameters + ---------- + n_streams : int + The number of independent seed sequences to generate. + Must be a positive integer. + + main_seed : Optional[int], default=None + Master seed that determines all generated sequences. + If None, a random entropy source is used, making results + non-reproducible across runs. Providing a value enables + reproducible sequences. + + Returns + ------- + List[np.random.SeedSequence] + A list of n_streams SeedSequence objects that can be used + to initialize random number generators with independent streams. + + Notes + ----- + This approach is preferred over manually creating seeds because + it uses NumPy's entropy pool management to guarantee statistical + independence between streams, avoiding subtle correlations that + might occur with manually chosen seeds. + + Examples + -------- + >>> seeds = spawn_seeds(3, main_seed=12345) + >>> rng1 = np.random.default_rng(seeds[0]) + >>> rng2 = np.random.default_rng(seeds[1]) + >>> rng3 = np.random.default_rng(seeds[2]) + """ + seed_sequence = np.random.SeedSequence(main_seed) + seeds = seed_sequence.spawn(n_streams) + return seeds From b0ddcbda3cc97003810c1bf55e13bb676c19b941 Mon Sep 17 00:00:00 2001 From: TomMonks Date: Wed, 18 Mar 2026 12:47:22 +0000 Subject: [PATCH 3/5] feat(store): image + cleaned up markdown --- content/10_multiple_arrival_processes.ipynb | 5 +- content/15_resource_stores.ipynb | 849 ++++++++++---------- content/img/two_resource_types.drawio | 188 +++++ content/img/two_resource_types.png | Bin 0 -> 167384 bytes 4 files changed, 601 insertions(+), 441 deletions(-) create mode 100644 content/img/two_resource_types.drawio create mode 100644 content/img/two_resource_types.png diff --git a/content/10_multiple_arrival_processes.ipynb b/content/10_multiple_arrival_processes.ipynb index 522c90e..2c9560f 100644 --- a/content/10_multiple_arrival_processes.ipynb +++ b/content/10_multiple_arrival_processes.ipynb @@ -11,7 +11,8 @@ "\n", "We will work with a hypothetical hospital that provides emergency orthopedic surgery to different classes of patient.\n", "\n", - "![model image](img/multiple_arrivals.png \"Urgent care call centre\")\n", + "\"model\n", + "\n", "\n", "| ID | Arrival Type | Distribution | Mean (mins) |\n", "|----|-----------------|--------------|-------------|\n", @@ -811,7 +812,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.11.14" } }, "nbformat": 4, diff --git a/content/15_resource_stores.ipynb b/content/15_resource_stores.ipynb index 367dadb..32d3966 100644 --- a/content/15_resource_stores.ipynb +++ b/content/15_resource_stores.ipynb @@ -9,9 +9,17 @@ "\n", "## Limitations of `Resource`\n", "\n", - "Up to this point the models we have looked have have used a basic `simpy.Resource`. This has been very useful to model queues, but one potential downside to `Resource` is that it *does not allow you to model individual resource attributes or any type of complex behaviour*. For many models in healthcare, this is sufficient, but there may be instances where you need to track and control individual resources. For example, ambulances, or different types of staff. In this notebook we will explore how to add more complex behaviour using the `Store` and `FilterStore` objects provided by `simpy`.\n", + "Up to this point the models we have used a basic `simpy.Resource`. This has been very useful to model queues, but one potential downside to `Resource` is that it *does not allow you to model individual resource attributes or any type of complex behaviour*. For many models in healthcare, this is sufficient, but there may be instances where you need to track and control individual resources. For example, ambulances, or different types of staff. In this notebook we will explore how to add more complex behaviour using the `Store` and `FilterStore` objects provided by `simpy`.\n", "\n", - "The good news is that both `Store` and `FilterStore` are easy to use. Usually this is within the context of a complex simulation, but we will keep our models simple here and focus on how to use them." + "🎓 The good news is that both `Store` and `FilterStore` are easy to use. Usually this is within the context of a complex simulation, but we will keep our models simple here and focus on how to use them.\n", + "\n", + "## When to use each SimPy resource type\n", + "\n", + "| Type | Best used for | Key idea |\n", + "|---|---|---|\n", + "| `Resource` | Identical servers | Only capacity matters. |\n", + "| `Store` | Individual objects | Retrieve the next available object. |\n", + "| `FilterStore` | Individual objects with selection rules | Retrieve an object matching a condition. |\n" ] }, { @@ -60,14 +68,21 @@ "source": [ "## 2. PART 1: Using a `simpy.Store`\n", "\n", - "A `Store` can be thought of as a \"box\" that we can `put` or `get` our own custom resource objects from in a First In First Out (FIFO) basis. **The objects do not need to be of the same type.**\n", + "A `Store` can be thought of as a container that holds Python objects. We can `put()` objects into the store and later `get()` them back out again, usually on a First In First Out (FIFO) basis.\n", + "\n", + "This is useful when resources are not all identical. With a normal `simpy.Resource`, we know how many units are available, but we do not know which specific unit is being used. With a `Store`, we can model individual resources with their own attributes.\n", "\n", - "We create an instance of a `Store` as follows (`env` is the `simpy.Environment`):\n", + "We create a `Store` as follows (`env` is a `simpy.Environment`):\n", "\n", "```python\n", "store = simpy.Store(env, capacity=2)\n", "```\n", - "We have to `put` something in the store. For example, let's add some ambulances.\n", + "We have to `put` something in the store. Let's take a very simple example where people experiencing a medical emergency are assigned either a Rapid Response Vehicle or a \"normal\" Type 1 Ambulance. A process flow describing use of the `Store` is below.\n", + "\n", + "\"model\n", + "\n", + "This is a two part process. We first create the instances of ambulances and then we call `Store.put()`\n", + "\n", "\n", "```python\n", "ambulances = [\n", @@ -88,13 +103,18 @@ "```\n", "\n", "Like normal `Resource` objects the use of `yield` means that we can simulate queues when no `Ambulance` objects are left in the store.\n", + "\n", + "The code below implements the full model. In the model we will track\n", + "\n", + "* The utilisation and number of emergencies handled by each individual vehicle.\n", + "* Overall patient waiting time and associated statistics.\n", " \n", "### 2.1 Parameters" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "d5b3eb67-9c8a-4fd8-9312-d337a65c52ee", "metadata": {}, "outputs": [], @@ -107,13 +127,11 @@ "\n", "RRV_SERVICE_TIME = 50.0 # minutes\n", "TYPE1_SERVICE_TIME = 65.0 \n", - "TRAFFIC_INTENSITY = 0.95 # ρ = λ / (c · μ)\n", "RUN_LENGTH = 1_000 # minutes\n", "RANDOM_SEED = 42\n", "\n", - "# exponential IAT and service time so I have used traffic intensity to control IAT\n", - "# ρ = λ / (c · μ) → mean_interarrival = mean_service / (ρ · c)\n", - "MEAN_INTERARRIVAL = 6 # → 60 / (0.8 × 10) = 7.5 minutes between calls" + "# exponential IAT \n", + "MEAN_INTERARRIVAL = 6" ] }, { @@ -121,12 +139,14 @@ "id": "03114c1d-faf6-4c95-b7d2-64d6a17003a8", "metadata": {}, "source": [ - "### 2.2 Entity classes" + "### 2.2 Entity classes\n", + "\n", + "We will model Ambulances using a simple Python class called `Ambulance`. For simplicity we will give the ambulance an attribute called `vehicle_type`. We will use that to look up the service time distribution from a Python dictionary parameter." ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "7956189c-c777-4c0a-afcc-ed06ec24ee3e", "metadata": {}, "outputs": [], @@ -139,24 +159,36 @@ " ----------\n", " ambulance_id: int\n", " Unique id of the ambulance\n", + " vehicle_type: str\n", + " Set to either \"rrv\" or \"type_1\"\n", " \"\"\"\n", " def __init__(self, ambulance_id: int, vehicle_type: str):\n", " self.ambulance_id = ambulance_id\n", + "\n", + " # vehicle type can be \"rrv\" or \"type_1\"\n", " self.vehicle_type = vehicle_type\n", + " \n", " self.total_jobs = 0\n", " # cumulative busy time\n", - " self.total_busy = 0.0 \n", + " self.total_busy = 0.0 \n", + "\n", + " self.emojis = {\n", + " \"rrv\": \"🏎️\",\n", + " \"type_1\": \"🚑\"\n", + " }\n", + " \n", + " self.emoji = self.emojis[self.vehicle_type]\n", " \n", " def __repr__(self):\n", " \"\"\"\n", " A text representation of the ambulance to help debugging\n", " \"\"\"\n", - " return f\"Ambulance(id={self.ambulance_id})\"" + " return f\"Ambulance({self.ambulance_id}, {self.emoji})\"" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "fa690897-a9e5-473b-8f01-35991b1b4213", "metadata": {}, "outputs": [], @@ -181,7 +213,7 @@ " \"\"\"\n", " A text representation of the Patient for debug\n", " \"\"\"\n", - " return f\"Patient(id={self.patient_id})\"" + " return f\"Patient({self.patient_id})\"" ] }, { @@ -189,12 +221,27 @@ "id": "bc5b1808-3f8e-43e2-b395-151de78bb718", "metadata": {}, "source": [ - "## 2.3 Ambulance dispatch and service process" + "## 2.3 Ambulance dispatch and service process\n", + "\n", + "The code looks a little different from when using a standard `Resource` because we do not use a `with` context manager when using a `Store`. This means we need to explicitly \"release\" an `Ambulance` by putting it back into the `Store`,\n", + "\n", + "The function `dispatch_ambulance` is a `simpy` process that accepts a `dict` called `dists`. It contains a service time distribution for each type of ambulance i.e. \"rrv\" and \"type_1\". For example if we had a rapid response vehicle we could sample a service time as follows:\n", + "\n", + "For example:\n", + "\n", + "```python\n", + "ambulance = Ambulance(1, \"rrv\")\n", + "\n", + "# vehicle type is a str with value \"rrv\" It returns a Exponential(50)\n", + "service_dist = dists[ambulance.vehicle_type]\n", + "service_time = service_dist.sample()\n", + "\n", + "```" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "08e53cbb-66dc-4e75-bcab-5ff1c6bf2a8b", "metadata": {}, "outputs": [], @@ -237,8 +284,7 @@ "\n", " # debug\n", " trace(\n", - " f\"{env.now:.1f} {patient} → {ambulance} \"\n", - " f\"[{ambulance.vehicle_type}] \"\n", + " f\"{env.now:.1f}: {ambulance.emoji} {ambulance.ambulance_id} → {patient} \"\n", " f\"(waited {wait_time:.1f} min, service {service_time:.1f} min)\"\n", " )\n", "\n", @@ -260,12 +306,14 @@ "id": "af2955b7-fc92-474d-8238-6b3f78b47f0c", "metadata": {}, "source": [ - "### 2.4 Patient arrival generator" + "### 2.4 Patient arrival generator\n", + "\n", + "Our patient generator process follows the simple template approach we have used before: an infinite loop where we sample the time until the next arrival and then create and schedule new `dispatch_ambulance` process." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "92544964-085b-4309-b138-0b7c8e9a1c14", "metadata": {}, "outputs": [], @@ -273,8 +321,8 @@ "def patient_arrivals_generator(\n", " env: simpy.Environment,\n", " store: simpy.Store,\n", - " distributions: dict,\n", - " results: dict\n", + " dists: dict,\n", + " log: dict\n", ") -> None:\n", " \"\"\"\n", " Arrival process for patients to the ambulance sim.\n", @@ -287,26 +335,26 @@ " store: simpy.Store\n", " A store of Ambulance objects\n", "\n", - " distributions: dict\n", + " dists: dict\n", " Contains \"arrival\" and \"service\" distributions\n", "\n", - " results: dict\n", + " log: dict\n", " Results dictionary\n", " \"\"\"\n", " for patient_id in itertools.count(start=1):\n", "\n", " # time until next patient arrival\n", - " inter_arrival_time = distributions[\"arrival\"].sample()\n", + " inter_arrival_time = dists[\"arrival\"].sample()\n", " yield env.timeout(inter_arrival_time)\n", "\n", - " results[\"n_arrivals\"] += 1\n", + " log[\"n_arrivals\"] += 1\n", " patient = Patient(patient_id, env.now)\n", "\n", " # debug info\n", - " trace(f\"{env.now:.1f}: Arrival. {patient}\")\n", + " trace(f\"{env.now:.1f}: 📞 {patient}\")\n", "\n", " # create ambulance dispatch + service process\n", - " env.process(dispatch_ambulance(env, store, patient, distributions, results))" + " env.process(dispatch_ambulance(env, store, patient, dists, log))" ] }, { @@ -314,12 +362,14 @@ "id": "9dcc1021-dbdb-4c39-a847-f263f09f6d54", "metadata": {}, "source": [ - "### 2.5 Single run function" + "### 2.5 Single run function\n", + "\n", + "The `single_run` function creates our dictionary of distributions, ambulances, `Store` resource and runs the simulation returning a log of results and the ambulances that we will use the pass to a simple function called `results_summary` that will print out some formatted statistics." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "id": "a5d11438-61b0-4345-9ecc-83db0db32d17", "metadata": {}, "outputs": [], @@ -329,7 +379,7 @@ " mean_rrv_service: float = RRV_SERVICE_TIME,\n", " mean_type_1_service: float = TYPE1_SERVICE_TIME,\n", " n_ambulances: int = NUM_AMBULANCES,\n", - " fleet: list = FLEET,\n", + " fleet: dict = FLEET,\n", " run_length: float = RUN_LENGTH, \n", " random_seed: int = 1\n", "):\n", @@ -378,7 +428,57 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 9, + "id": "2b32b8dc-59cb-4680-afa2-064ee5bd3c99", + "metadata": {}, + "outputs": [], + "source": [ + "def results_summary(\n", + " log: dict,\n", + " ambulances: list[Ambulance],\n", + " run_length: float,\n", + " mean_iat: float\n", + "):\n", + " waits = np.array(log[\"wait_times\"])\n", + " n_served = len(waits)\n", + "\n", + " print(\"\\n\" + \"═\" * 55)\n", + " print(f\" Mean inter-arrival : {mean_iat:.2f} min\")\n", + " print(\"─\" * 55)\n", + " print(f\" Patients arrived : {log['n_arrivals']}\")\n", + " print(f\" Patients served : {n_served}\")\n", + "\n", + " if n_served > 0:\n", + " print(f\" Mean wait time : {waits.mean():.2f} min\")\n", + " print(f\" P(wait > 0) : {(waits > 0).mean():.2%}\")\n", + " print(f\" 95th pct wait : {np.percentile(waits, 95):.2f} min\")\n", + " else:\n", + " print(\" No patients were served.\")\n", + "\n", + " print(\"─\" * 55)\n", + " print(f\" {'Ambulance':<15} {'Jobs':>6} {'Utilisation':>12}\")\n", + " print(\"─\" * 55)\n", + "\n", + " for amb in ambulances:\n", + " util = amb.total_busy / run_length\n", + " print(f\" {amb.emoji} {amb.ambulance_id:<5} {amb.total_jobs:>6} {util:>11.2%}\")\n", + "\n", + " print(\"═\" * 55)" + ] + }, + { + "cell_type": "markdown", + "id": "3c663f15-1df1-4652-b202-90378d4e81fb", + "metadata": {}, + "source": [ + "### 2.6 Run the model and view results \n", + "\n", + "Use `set_trace` to toggle the printing of the model event debug on and off. " + ] + }, + { + "cell_type": "code", + "execution_count": 10, "id": "0506ba12-8039-4d13-a3a1-4d7e08efa44f", "metadata": {}, "outputs": [ @@ -386,384 +486,38 @@ "name": "stdout", "output_type": "stream", "text": [ - "Simulation tracing set to: True\n", - "5.8: Arrival. Patient(id=1)\n", - "5.8 Patient(id=1) → Ambulance(id=1) [rrv] (waited 0.0 min, service 9.7 min)\n", - "16.8: Arrival. Patient(id=2)\n", - "16.8 Patient(id=2) → Ambulance(id=2) [rrv] (waited 0.0 min, service 4.4 min)\n", - "19.5: Arrival. Patient(id=3)\n", - "19.5 Patient(id=3) → Ambulance(id=3) [rrv] (waited 0.0 min, service 24.1 min)\n", - "21.2: Arrival. Patient(id=4)\n", - "21.2 Patient(id=4) → Ambulance(id=4) [rrv] (waited 0.0 min, service 7.6 min)\n", - "40.8: Arrival. Patient(id=5)\n", - "40.8 Patient(id=5) → Ambulance(id=5) [type_1] (waited 0.0 min, service 12.3 min)\n", - "41.8: Arrival. Patient(id=6)\n", - "41.8 Patient(id=6) → Ambulance(id=6) [type_1] (waited 0.0 min, service 22.9 min)\n", - "42.1: Arrival. Patient(id=7)\n", - "42.1 Patient(id=7) → Ambulance(id=7) [type_1] (waited 0.0 min, service 8.4 min)\n", - "43.0: Arrival. Patient(id=8)\n", - "43.0 Patient(id=8) → Ambulance(id=8) [type_1] (waited 0.0 min, service 91.4 min)\n", - "69.8: Arrival. Patient(id=9)\n", - "69.8 Patient(id=9) → Ambulance(id=9) [type_1] (waited 0.0 min, service 47.0 min)\n", - "70.8: Arrival. Patient(id=10)\n", - "70.8 Patient(id=10) → Ambulance(id=10) [type_1] (waited 0.0 min, service 74.7 min)\n", - "71.1: Arrival. Patient(id=11)\n", - "71.1 Patient(id=11) → Ambulance(id=1) [rrv] (waited 0.0 min, service 93.0 min)\n", - "120.9: Arrival. Patient(id=12)\n", - "120.9 Patient(id=12) → Ambulance(id=2) [rrv] (waited 0.0 min, service 38.6 min)\n", - "121.7: Arrival. Patient(id=13)\n", - "121.7 Patient(id=13) → Ambulance(id=4) [rrv] (waited 0.0 min, service 14.2 min)\n", - "125.9: Arrival. Patient(id=14)\n", - "125.9 Patient(id=14) → Ambulance(id=3) [rrv] (waited 0.0 min, service 2.2 min)\n", - "126.1: Arrival. Patient(id=15)\n", - "126.1 Patient(id=15) → Ambulance(id=7) [type_1] (waited 0.0 min, service 61.5 min)\n", - "134.9: Arrival. Patient(id=16)\n", - "134.9 Patient(id=16) → Ambulance(id=5) [type_1] (waited 0.0 min, service 20.8 min)\n", - "145.9: Arrival. Patient(id=17)\n", - "145.9 Patient(id=17) → Ambulance(id=6) [type_1] (waited 0.0 min, service 72.5 min)\n", - "146.5: Arrival. Patient(id=18)\n", - "146.5 Patient(id=18) → Ambulance(id=9) [type_1] (waited 0.0 min, service 79.2 min)\n", - "149.1: Arrival. Patient(id=19)\n", - "149.1 Patient(id=19) → Ambulance(id=3) [rrv] (waited 0.0 min, service 76.0 min)\n", - "161.6: Arrival. Patient(id=20)\n", - "161.6 Patient(id=20) → Ambulance(id=8) [type_1] (waited 0.0 min, service 60.8 min)\n", - "169.9: Arrival. Patient(id=21)\n", - "169.9 Patient(id=21) → Ambulance(id=4) [rrv] (waited 0.0 min, service 5.0 min)\n", - "171.2: Arrival. Patient(id=22)\n", - "171.2 Patient(id=22) → Ambulance(id=10) [type_1] (waited 0.0 min, service 7.3 min)\n", - "174.2: Arrival. Patient(id=23)\n", - "174.2 Patient(id=23) → Ambulance(id=5) [type_1] (waited 0.0 min, service 234.3 min)\n", - "176.2: Arrival. Patient(id=24)\n", - "176.2 Patient(id=24) → Ambulance(id=2) [rrv] (waited 0.0 min, service 119.7 min)\n", - "188.7: Arrival. Patient(id=25)\n", - "188.7 Patient(id=25) → Ambulance(id=1) [rrv] (waited 0.0 min, service 30.0 min)\n", - "189.6: Arrival. Patient(id=26)\n", - "189.6 Patient(id=26) → Ambulance(id=4) [rrv] (waited 0.0 min, service 20.6 min)\n", - "191.1: Arrival. Patient(id=27)\n", - "191.1 Patient(id=27) → Ambulance(id=10) [type_1] (waited 0.0 min, service 12.5 min)\n", - "195.8: Arrival. Patient(id=28)\n", - "195.8 Patient(id=28) → Ambulance(id=7) [type_1] (waited 0.0 min, service 146.8 min)\n", - "199.4: Arrival. Patient(id=29)\n", - "199.8: Arrival. Patient(id=30)\n", - "203.7 Patient(id=29) → Ambulance(id=10) [type_1] (waited 4.2 min, service 179.5 min)\n", - "204.5: Arrival. Patient(id=31)\n", - "209.6: Arrival. Patient(id=32)\n", - "210.2 Patient(id=30) → Ambulance(id=4) [rrv] (waited 10.4 min, service 157.3 min)\n", - "218.4 Patient(id=31) → Ambulance(id=6) [type_1] (waited 13.8 min, service 36.3 min)\n", - "218.7 Patient(id=32) → Ambulance(id=1) [rrv] (waited 9.1 min, service 0.2 min)\n", - "219.1: Arrival. Patient(id=33)\n", - "219.1 Patient(id=33) → Ambulance(id=1) [rrv] (waited 0.0 min, service 50.3 min)\n", - "219.3: Arrival. Patient(id=34)\n", - "222.4 Patient(id=34) → Ambulance(id=8) [type_1] (waited 3.1 min, service 13.8 min)\n", - "230.4: Arrival. Patient(id=35)\n", - "230.4 Patient(id=35) → Ambulance(id=3) [rrv] (waited 0.0 min, service 127.0 min)\n", - "235.7: Arrival. Patient(id=36)\n", - "235.7 Patient(id=36) → Ambulance(id=9) [type_1] (waited 0.0 min, service 9.9 min)\n", - "236.3: Arrival. Patient(id=37)\n", - "236.3 Patient(id=37) → Ambulance(id=8) [type_1] (waited 0.0 min, service 146.3 min)\n", - "237.7: Arrival. Patient(id=38)\n", - "244.5: Arrival. Patient(id=39)\n", - "245.6 Patient(id=38) → Ambulance(id=9) [type_1] (waited 7.9 min, service 8.2 min)\n", - "245.9: Arrival. Patient(id=40)\n", - "246.8: Arrival. Patient(id=41)\n", - "253.8 Patient(id=39) → Ambulance(id=9) [type_1] (waited 9.3 min, service 19.3 min)\n", - "254.6 Patient(id=40) → Ambulance(id=6) [type_1] (waited 8.7 min, service 82.8 min)\n", - "265.4: Arrival. Patient(id=42)\n", - "269.4 Patient(id=41) → Ambulance(id=1) [rrv] (waited 22.6 min, service 0.3 min)\n", - "269.7 Patient(id=42) → Ambulance(id=1) [rrv] (waited 4.2 min, service 77.6 min)\n", - "270.8: Arrival. Patient(id=43)\n", - "273.1 Patient(id=43) → Ambulance(id=9) [type_1] (waited 2.2 min, service 95.4 min)\n", - "273.8: Arrival. Patient(id=44)\n", - "274.7: Arrival. Patient(id=45)\n", - "275.5: Arrival. Patient(id=46)\n", - "280.0: Arrival. Patient(id=47)\n", - "284.8: Arrival. Patient(id=48)\n", - "285.9: Arrival. Patient(id=49)\n", - "287.8: Arrival. Patient(id=50)\n", - "295.9 Patient(id=44) → Ambulance(id=2) [rrv] (waited 22.1 min, service 39.8 min)\n", - "297.7: Arrival. Patient(id=51)\n", - "303.3: Arrival. Patient(id=52)\n", - "316.4: Arrival. Patient(id=53)\n", - "328.3: Arrival. Patient(id=54)\n", - "335.7 Patient(id=45) → Ambulance(id=2) [rrv] (waited 61.0 min, service 24.9 min)\n", - "337.4 Patient(id=46) → Ambulance(id=6) [type_1] (waited 62.0 min, service 48.9 min)\n", - "342.6 Patient(id=47) → Ambulance(id=7) [type_1] (waited 62.6 min, service 0.9 min)\n", - "343.5 Patient(id=48) → Ambulance(id=7) [type_1] (waited 58.7 min, service 103.1 min)\n", - "345.2: Arrival. Patient(id=55)\n", - "346.5: Arrival. Patient(id=56)\n", - "347.3 Patient(id=49) → Ambulance(id=1) [rrv] (waited 61.3 min, service 25.9 min)\n", - "355.1: Arrival. Patient(id=57)\n", - "357.4 Patient(id=50) → Ambulance(id=3) [rrv] (waited 69.6 min, service 6.9 min)\n", - "359.4: Arrival. Patient(id=58)\n", - "360.6 Patient(id=51) → Ambulance(id=2) [rrv] (waited 62.9 min, service 50.6 min)\n", - "364.3 Patient(id=52) → Ambulance(id=3) [rrv] (waited 61.1 min, service 225.2 min)\n", - "366.8: Arrival. Patient(id=59)\n", - "367.5 Patient(id=53) → Ambulance(id=4) [rrv] (waited 51.1 min, service 176.5 min)\n", - "368.5 Patient(id=54) → Ambulance(id=9) [type_1] (waited 40.2 min, service 22.5 min)\n", - "373.2 Patient(id=55) → Ambulance(id=1) [rrv] (waited 27.9 min, service 12.3 min)\n", - "379.4: Arrival. Patient(id=60)\n", - "381.8: Arrival. Patient(id=61)\n", - "382.6 Patient(id=56) → Ambulance(id=8) [type_1] (waited 36.2 min, service 68.4 min)\n", - "383.1 Patient(id=57) → Ambulance(id=10) [type_1] (waited 28.1 min, service 34.7 min)\n", - "385.5 Patient(id=58) → Ambulance(id=1) [rrv] (waited 26.1 min, service 8.7 min)\n", - "386.4 Patient(id=59) → Ambulance(id=6) [type_1] (waited 19.5 min, service 39.0 min)\n", - "391.0 Patient(id=60) → Ambulance(id=9) [type_1] (waited 11.6 min, service 34.7 min)\n", - "394.2 Patient(id=61) → Ambulance(id=1) [rrv] (waited 12.4 min, service 3.4 min)\n", - "401.3: Arrival. Patient(id=62)\n", - "401.3 Patient(id=62) → Ambulance(id=1) [rrv] (waited 0.0 min, service 34.2 min)\n", - "402.8: Arrival. Patient(id=63)\n", - "403.1: Arrival. Patient(id=64)\n", - "408.2: Arrival. Patient(id=65)\n", - "408.6 Patient(id=63) → Ambulance(id=5) [type_1] (waited 5.8 min, service 49.2 min)\n", - "409.1: Arrival. Patient(id=66)\n", - "411.2 Patient(id=64) → Ambulance(id=2) [rrv] (waited 8.1 min, service 23.8 min)\n", - "412.1: Arrival. Patient(id=67)\n", - "413.0: Arrival. Patient(id=68)\n", - "415.4: Arrival. Patient(id=69)\n", - "417.8 Patient(id=65) → Ambulance(id=10) [type_1] (waited 9.6 min, service 90.4 min)\n", - "418.1: Arrival. Patient(id=70)\n", - "421.3: Arrival. Patient(id=71)\n", - "423.5: Arrival. Patient(id=72)\n", - "425.3 Patient(id=66) → Ambulance(id=6) [type_1] (waited 16.2 min, service 16.5 min)\n", - "425.7: Arrival. Patient(id=73)\n", - "425.7 Patient(id=67) → Ambulance(id=9) [type_1] (waited 13.6 min, service 27.6 min)\n", - "430.5: Arrival. Patient(id=74)\n", - "434.9: Arrival. Patient(id=75)\n", - "435.1 Patient(id=68) → Ambulance(id=2) [rrv] (waited 22.1 min, service 42.3 min)\n", - "435.4 Patient(id=69) → Ambulance(id=1) [rrv] (waited 20.0 min, service 11.0 min)\n", - "437.9: Arrival. Patient(id=76)\n", - "438.8: Arrival. Patient(id=77)\n", - "441.9 Patient(id=70) → Ambulance(id=6) [type_1] (waited 23.8 min, service 10.2 min)\n", - "446.4 Patient(id=71) → Ambulance(id=1) [rrv] (waited 25.1 min, service 49.6 min)\n", - "446.6 Patient(id=72) → Ambulance(id=7) [type_1] (waited 23.0 min, service 48.4 min)\n", - "451.1 Patient(id=73) → Ambulance(id=8) [type_1] (waited 25.4 min, service 61.8 min)\n", - "452.1: Arrival. Patient(id=78)\n", - "452.1 Patient(id=74) → Ambulance(id=6) [type_1] (waited 21.6 min, service 14.7 min)\n", - "453.3 Patient(id=75) → Ambulance(id=9) [type_1] (waited 18.3 min, service 41.8 min)\n", - "454.7: Arrival. Patient(id=79)\n", - "455.7: Arrival. Patient(id=80)\n", - "457.7 Patient(id=76) → Ambulance(id=5) [type_1] (waited 19.9 min, service 78.5 min)\n", - "461.4: Arrival. Patient(id=81)\n", - "466.8 Patient(id=77) → Ambulance(id=6) [type_1] (waited 28.0 min, service 2.4 min)\n", - "468.7: Arrival. Patient(id=82)\n", - "469.2 Patient(id=78) → Ambulance(id=6) [type_1] (waited 17.1 min, service 40.4 min)\n", - "472.6: Arrival. Patient(id=83)\n", - "477.4 Patient(id=79) → Ambulance(id=2) [rrv] (waited 22.7 min, service 50.6 min)\n", - "477.6: Arrival. Patient(id=84)\n", - "478.8: Arrival. Patient(id=85)\n", - "481.3: Arrival. Patient(id=86)\n", - "485.8: Arrival. Patient(id=87)\n", - "486.1: Arrival. Patient(id=88)\n", - "488.4: Arrival. Patient(id=89)\n", - "493.1: Arrival. Patient(id=90)\n", - "493.3: Arrival. Patient(id=91)\n", - "495.0 Patient(id=80) → Ambulance(id=7) [type_1] (waited 39.2 min, service 93.5 min)\n", - "495.1 Patient(id=81) → Ambulance(id=9) [type_1] (waited 33.7 min, service 20.3 min)\n", - "496.0 Patient(id=82) → Ambulance(id=1) [rrv] (waited 27.2 min, service 44.3 min)\n", - "503.2: Arrival. Patient(id=92)\n", - "508.2 Patient(id=83) → Ambulance(id=10) [type_1] (waited 35.6 min, service 87.1 min)\n", - "509.6 Patient(id=84) → Ambulance(id=6) [type_1] (waited 32.1 min, service 38.4 min)\n", - "512.9 Patient(id=85) → Ambulance(id=8) [type_1] (waited 34.0 min, service 43.4 min)\n", - "513.8: Arrival. Patient(id=93)\n", - "515.4 Patient(id=86) → Ambulance(id=9) [type_1] (waited 34.2 min, service 86.2 min)\n", - "525.1: Arrival. Patient(id=94)\n", - "525.5: Arrival. Patient(id=95)\n", - "528.0 Patient(id=87) → Ambulance(id=2) [rrv] (waited 42.1 min, service 69.6 min)\n", - "531.8: Arrival. Patient(id=96)\n", - "536.3 Patient(id=88) → Ambulance(id=5) [type_1] (waited 50.2 min, service 132.0 min)\n", - "540.0: Arrival. Patient(id=97)\n", - "540.2 Patient(id=89) → Ambulance(id=1) [rrv] (waited 51.9 min, service 118.8 min)\n", - "541.4: Arrival. Patient(id=98)\n", - "544.0 Patient(id=90) → Ambulance(id=4) [rrv] (waited 50.9 min, service 7.4 min)\n", - "548.0 Patient(id=91) → Ambulance(id=6) [type_1] (waited 54.7 min, service 39.8 min)\n", - "550.3: Arrival. Patient(id=99)\n", - "551.5 Patient(id=92) → Ambulance(id=4) [rrv] (waited 48.3 min, service 249.4 min)\n", - "555.6: Arrival. Patient(id=100)\n", - "556.3 Patient(id=93) → Ambulance(id=8) [type_1] (waited 42.5 min, service 78.1 min)\n", - "559.4: Arrival. Patient(id=101)\n", - "560.2: Arrival. Patient(id=102)\n", - "568.1: Arrival. Patient(id=103)\n", - "582.8: Arrival. Patient(id=104)\n", - "587.8 Patient(id=94) → Ambulance(id=6) [type_1] (waited 62.7 min, service 42.4 min)\n", - "588.5 Patient(id=95) → Ambulance(id=7) [type_1] (waited 63.0 min, service 2.7 min)\n", - "588.9: Arrival. Patient(id=105)\n", - "589.2: Arrival. Patient(id=106)\n", - "589.5 Patient(id=96) → Ambulance(id=3) [rrv] (waited 57.7 min, service 61.9 min)\n", - "591.2 Patient(id=97) → Ambulance(id=7) [type_1] (waited 51.2 min, service 194.1 min)\n", - "595.4 Patient(id=98) → Ambulance(id=10) [type_1] (waited 54.0 min, service 18.8 min)\n", - "597.6 Patient(id=99) → Ambulance(id=2) [rrv] (waited 47.3 min, service 19.5 min)\n", - "600.4: Arrival. Patient(id=107)\n", - "601.7 Patient(id=100) → Ambulance(id=9) [type_1] (waited 46.1 min, service 74.3 min)\n", - "603.1: Arrival. Patient(id=108)\n", - "608.6: Arrival. Patient(id=109)\n", - "609.3: Arrival. Patient(id=110)\n", - "614.2 Patient(id=101) → Ambulance(id=10) [type_1] (waited 54.7 min, service 127.6 min)\n", - "617.1 Patient(id=102) → Ambulance(id=2) [rrv] (waited 56.9 min, service 26.8 min)\n", - "629.8: Arrival. Patient(id=111)\n", - "630.2 Patient(id=103) → Ambulance(id=6) [type_1] (waited 62.2 min, service 228.2 min)\n", - "634.3 Patient(id=104) → Ambulance(id=8) [type_1] (waited 51.5 min, service 21.9 min)\n", - "635.3: Arrival. Patient(id=112)\n", - "640.9: Arrival. Patient(id=113)\n", - "643.9 Patient(id=105) → Ambulance(id=2) [rrv] (waited 55.0 min, service 73.2 min)\n", - "651.5 Patient(id=106) → Ambulance(id=3) [rrv] (waited 62.2 min, service 84.7 min)\n", - "653.3: Arrival. Patient(id=114)\n", - "656.3 Patient(id=107) → Ambulance(id=8) [type_1] (waited 55.8 min, service 10.1 min)\n", - "659.1 Patient(id=108) → Ambulance(id=1) [rrv] (waited 56.0 min, service 150.4 min)\n", - "666.3: Arrival. Patient(id=115)\n", - "666.4 Patient(id=109) → Ambulance(id=8) [type_1] (waited 57.7 min, service 73.4 min)\n", - "668.3 Patient(id=110) → Ambulance(id=5) [type_1] (waited 59.0 min, service 22.4 min)\n", - "669.8: Arrival. Patient(id=116)\n", - "674.4: Arrival. Patient(id=117)\n", - "676.0 Patient(id=111) → Ambulance(id=9) [type_1] (waited 46.2 min, service 85.9 min)\n", - "690.4: Arrival. Patient(id=118)\n", - "690.5: Arrival. Patient(id=119)\n", - "690.7 Patient(id=112) → Ambulance(id=5) [type_1] (waited 55.4 min, service 150.0 min)\n", - "693.3: Arrival. Patient(id=120)\n", - "693.7: Arrival. Patient(id=121)\n", - "699.7: Arrival. Patient(id=122)\n", - "700.2: Arrival. Patient(id=123)\n", - "717.2 Patient(id=113) → Ambulance(id=2) [rrv] (waited 76.3 min, service 73.6 min)\n", - "723.7: Arrival. Patient(id=124)\n", - "732.3: Arrival. Patient(id=125)\n", - "736.2 Patient(id=114) → Ambulance(id=3) [rrv] (waited 82.9 min, service 21.7 min)\n", - "738.0: Arrival. Patient(id=126)\n", - "739.8 Patient(id=115) → Ambulance(id=8) [type_1] (waited 73.5 min, service 9.4 min)\n", - "741.7 Patient(id=116) → Ambulance(id=10) [type_1] (waited 71.9 min, service 65.6 min)\n", - "749.2 Patient(id=117) → Ambulance(id=8) [type_1] (waited 74.8 min, service 99.1 min)\n", - "749.5: Arrival. Patient(id=127)\n", - "757.5: Arrival. Patient(id=128)\n", - "757.9 Patient(id=118) → Ambulance(id=3) [rrv] (waited 67.5 min, service 60.5 min)\n", - "761.9 Patient(id=119) → Ambulance(id=9) [type_1] (waited 71.4 min, service 237.9 min)\n", - "778.6: Arrival. Patient(id=129)\n", - "780.5: Arrival. Patient(id=130)\n", - "781.5: Arrival. Patient(id=131)\n", - "782.6: Arrival. Patient(id=132)\n", - "785.2 Patient(id=120) → Ambulance(id=7) [type_1] (waited 91.9 min, service 120.9 min)\n", - "787.4: Arrival. Patient(id=133)\n", - "788.0: Arrival. Patient(id=134)\n", - "790.8 Patient(id=121) → Ambulance(id=2) [rrv] (waited 97.1 min, service 8.0 min)\n", - "798.8: Arrival. Patient(id=135)\n", - "798.8 Patient(id=122) → Ambulance(id=2) [rrv] (waited 99.1 min, service 74.6 min)\n", - "800.9 Patient(id=123) → Ambulance(id=4) [rrv] (waited 100.7 min, service 156.5 min)\n", - "807.3 Patient(id=124) → Ambulance(id=10) [type_1] (waited 83.6 min, service 117.9 min)\n", - "809.5 Patient(id=125) → Ambulance(id=1) [rrv] (waited 77.2 min, service 13.1 min)\n", - "812.4: Arrival. Patient(id=136)\n", - "812.8: Arrival. Patient(id=137)\n", - "818.4 Patient(id=126) → Ambulance(id=3) [rrv] (waited 80.3 min, service 56.7 min)\n", - "822.6 Patient(id=127) → Ambulance(id=1) [rrv] (waited 73.1 min, service 58.1 min)\n", - "835.8: Arrival. Patient(id=138)\n", - "838.3: Arrival. Patient(id=139)\n", - "840.7 Patient(id=128) → Ambulance(id=5) [type_1] (waited 83.3 min, service 56.9 min)\n", - "840.7: Arrival. Patient(id=140)\n", - "846.6: Arrival. Patient(id=141)\n", - "847.3: Arrival. Patient(id=142)\n", - "848.3 Patient(id=129) → Ambulance(id=8) [type_1] (waited 69.7 min, service 40.2 min)\n", - "858.5 Patient(id=130) → Ambulance(id=6) [type_1] (waited 78.0 min, service 39.4 min)\n", - "871.6: Arrival. Patient(id=143)\n", - "873.4 Patient(id=131) → Ambulance(id=2) [rrv] (waited 91.9 min, service 35.0 min)\n", - "875.1 Patient(id=132) → Ambulance(id=3) [rrv] (waited 92.5 min, service 24.0 min)\n", - "880.7 Patient(id=133) → Ambulance(id=1) [rrv] (waited 93.3 min, service 121.3 min)\n", - "888.5 Patient(id=134) → Ambulance(id=8) [type_1] (waited 100.5 min, service 0.3 min)\n", - "888.8 Patient(id=135) → Ambulance(id=8) [type_1] (waited 90.0 min, service 87.8 min)\n", - "890.9: Arrival. Patient(id=144)\n", - "894.3: Arrival. Patient(id=145)\n", - "897.6 Patient(id=136) → Ambulance(id=5) [type_1] (waited 85.2 min, service 67.6 min)\n", - "897.9 Patient(id=137) → Ambulance(id=6) [type_1] (waited 85.0 min, service 34.9 min)\n", - "899.1 Patient(id=138) → Ambulance(id=3) [rrv] (waited 63.3 min, service 87.5 min)\n", - "900.6: Arrival. Patient(id=146)\n", - "904.8: Arrival. Patient(id=147)\n", - "906.2 Patient(id=139) → Ambulance(id=7) [type_1] (waited 67.8 min, service 16.1 min)\n", - "908.4 Patient(id=140) → Ambulance(id=2) [rrv] (waited 67.7 min, service 90.9 min)\n", - "910.9: Arrival. Patient(id=148)\n", - "911.2: Arrival. Patient(id=149)\n", - "916.7: Arrival. Patient(id=150)\n", - "921.4: Arrival. Patient(id=151)\n", - "922.3 Patient(id=141) → Ambulance(id=7) [type_1] (waited 75.7 min, service 0.6 min)\n", - "922.9 Patient(id=142) → Ambulance(id=7) [type_1] (waited 75.6 min, service 29.7 min)\n", - "924.2: Arrival. Patient(id=152)\n", - "925.2 Patient(id=143) → Ambulance(id=10) [type_1] (waited 53.6 min, service 4.8 min)\n", - "926.8: Arrival. Patient(id=153)\n", - "930.0 Patient(id=144) → Ambulance(id=10) [type_1] (waited 39.1 min, service 41.0 min)\n", - "932.8 Patient(id=145) → Ambulance(id=6) [type_1] (waited 38.4 min, service 2.6 min)\n", - "935.4 Patient(id=146) → Ambulance(id=6) [type_1] (waited 34.8 min, service 166.5 min)\n", - "936.3: Arrival. Patient(id=154)\n", - "936.6: Arrival. Patient(id=155)\n", - "936.7: Arrival. Patient(id=156)\n", - "939.3: Arrival. Patient(id=157)\n", - "940.4: Arrival. Patient(id=158)\n", - "941.0: Arrival. Patient(id=159)\n", - "947.0: Arrival. Patient(id=160)\n", - "950.1: Arrival. Patient(id=161)\n", - "952.6 Patient(id=147) → Ambulance(id=7) [type_1] (waited 47.8 min, service 8.4 min)\n", - "954.1: Arrival. Patient(id=162)\n", - "957.4 Patient(id=148) → Ambulance(id=4) [rrv] (waited 46.5 min, service 51.1 min)\n", - "957.5: Arrival. Patient(id=163)\n", - "959.0: Arrival. Patient(id=164)\n", - "961.0 Patient(id=149) → Ambulance(id=7) [type_1] (waited 49.8 min, service 38.1 min)\n", - "965.2 Patient(id=150) → Ambulance(id=5) [type_1] (waited 48.5 min, service 54.1 min)\n", - "965.6: Arrival. Patient(id=165)\n", - "971.0 Patient(id=151) → Ambulance(id=10) [type_1] (waited 49.6 min, service 126.2 min)\n", - "976.6 Patient(id=152) → Ambulance(id=8) [type_1] (waited 52.4 min, service 3.2 min)\n", - "979.8 Patient(id=153) → Ambulance(id=8) [type_1] (waited 53.0 min, service 25.9 min)\n", - "981.8: Arrival. Patient(id=166)\n", - "986.6 Patient(id=154) → Ambulance(id=3) [rrv] (waited 50.3 min, service 14.6 min)\n", - "989.9: Arrival. Patient(id=167)\n", - "992.3: Arrival. Patient(id=168)\n", - "999.0 Patient(id=155) → Ambulance(id=7) [type_1] (waited 62.4 min, service 55.0 min)\n", - "999.4 Patient(id=156) → Ambulance(id=2) [rrv] (waited 62.7 min, service 38.6 min)\n", - "999.8 Patient(id=157) → Ambulance(id=9) [type_1] (waited 60.5 min, service 30.0 min)\n", + "Simulation tracing set to: False\n", "\n", "═══════════════════════════════════════════════════════\n", - " Mean inter-arrival : 6.00 min\n", + " Mean inter-arrival : 6.00 min\n", "───────────────────────────────────────────────────────\n", - " Patients arrived : 168\n", - " Patients served : 157\n", - " Mean wait time : 38.29 min\n", - " P(wait > 0) : 78.98%\n", - " 95th pct wait : 90.39 min\n", + " Patients arrived : 168\n", + " Patients served : 157\n", + " Mean wait time : 38.29 min\n", + " P(wait > 0) : 78.98%\n", + " 95th pct wait : 90.39 min\n", "───────────────────────────────────────────────────────\n", - " Ambulance Jobs Utilisation\n", + " Ambulance Jobs Utilisation\n", "───────────────────────────────────────────────────────\n", - " Ambulance 1 19 79.09%\n", - " Ambulance 2 18 86.62%\n", - " Ambulance 3 13 85.86%\n", - " Ambulance 4 9 79.45%\n", - " Ambulance 5 10 82.41%\n", - " Ambulance 6 18 81.24%\n", - " Ambulance 7 15 87.30%\n", - " Ambulance 8 17 90.95%\n", - " Ambulance 9 15 89.03%\n", - " Ambulance 10 13 86.19%\n", + " 🏎️ 1 19 79.09%\n", + " 🏎️ 2 18 86.62%\n", + " 🏎️ 3 13 85.86%\n", + " 🏎️ 4 9 79.45%\n", + " 🚑 5 10 82.41%\n", + " 🚑 6 18 81.24%\n", + " 🚑 7 15 87.30%\n", + " 🚑 8 17 90.95%\n", + " 🚑 9 15 89.03%\n", + " 🚑 10 13 86.19%\n", "═══════════════════════════════════════════════════════\n" ] } ], "source": [ - "set_trace(True)\n", - "# no warm-up.\n", - "ambulances, log = single_run(random_seed=42)\n", + "set_trace(False)\n", "\n", - "waits = np.array(log[\"wait_times\"])\n", - "services = np.array(log[\"service_times\"])\n", - "n = len(waits)\n", - "\n", - "print(\"\\n\" + \"═\" * 55)\n", - "print(f\" Mean inter-arrival : {MEAN_INTERARRIVAL:.2f} min\")\n", - "print(\"─\" * 55)\n", - "print(f\" Patients arrived : {log['n_arrivals']}\")\n", - "print(f\" Patients served : {n}\")\n", - "print(f\" Mean wait time : {waits.mean():.2f} min\")\n", - "print(f\" P(wait > 0) : {(waits > 0).mean():.2%}\")\n", - "print(f\" 95th pct wait : {np.percentile(waits, 95):.2f} min\")\n", - "print(\"─\" * 55)\n", - "print(f\" {'Ambulance':<15} {'Jobs':>6} {'Utilisation':>12}\")\n", - "print(\"─\" * 55)\n", - "\n", - "# calculate utilisation of each individual ambulance\n", - "for amb in ambulances:\n", - " util = amb.total_busy / RUN_LENGTH\n", - " print(f\" Ambulance {amb.ambulance_id:<5} {amb.total_jobs:>6} {util:>8.2%}\")\n", - "print(\"═\" * 55)" + "ambulances, log = single_run(run_length=RUN_LENGTH, random_seed=42)\n", + "results_summary(log, ambulances, run_length=RUN_LENGTH, mean_iat=MEAN_INTERARRIVAL)" ] }, { @@ -773,7 +527,7 @@ "source": [ "## 3. PART 2: Using a `FilterStore`\n", "\n", - "In this example we will modify the ambulance dispatch simulation so that patients and ambulances are located within one of five **nodes** that are specificed by coordinates.\n", + "In this example we will modify the ambulance dispatch simulation so that patients and ambulances are located within one of five **nodes** that are specified by coordinates.\n", "\n", "| ID | Label | Coordinates |\n", "|---|---|---|\n", @@ -785,8 +539,11 @@ "| 5 | Hospital | `(2, 0)` |\n", "\n", "\n", + "Patients in need will be assigned the closest available ambulance. This is why we use a `FilterStore`. We iterate through items in the store to locate the closest ambulance.\n", + "\n", + "An assigned ambulance has to travel to the patient (constant speed), pick them up (Exponential), travel to the hospital, and then travel back to their home node.\n", "\n", - "Patients in need will be assigned the closest available ambulance. If not ambulances are available they are assigned the next available ambulance when it has returned to its home node." + "If no ambulances are available they are assigned the next available ambulance when it has returned to its home node." ] }, { @@ -799,7 +556,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "0eabc7f2-bb0e-4368-a245-2a123c0ba420", "metadata": {}, "outputs": [], @@ -838,7 +595,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "1079ec2e-1d51-447d-99b1-11d9f081e36e", "metadata": {}, "outputs": [], @@ -866,12 +623,12 @@ "source": [ "### 3.3 Entity Classes\n", "\n", - "The `Ambulance` and `Patient` classes have been slightly modified to include a `node` attribute" + "The `Ambulance` and `Patient` classes have been slightly modified to include a `node` attribute. For simplicity all ambulances have the same type" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "723855d2-1389-46cc-9a7f-663369dbd686", "metadata": {}, "outputs": [], @@ -890,7 +647,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "e7031697-436e-4b85-bbde-91bde6ed3806", "metadata": {}, "outputs": [], @@ -915,7 +672,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "7a6c9fe6-4c86-4b6c-a758-2dba9fa622de", "metadata": {}, "outputs": [], @@ -927,7 +684,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "17c05a56-2f6a-4ffa-b65b-02d8ecc94671", "metadata": {}, "outputs": [], @@ -936,10 +693,16 @@ " available: list[Ambulance],\n", " patient_node: int\n", ") -> int | None:\n", - " \n", + " \"\"\"\n", + " Finds the closest ambulance and return the ID.\n", + " Note this code is not designed for efficiency. \n", + " \"\"\"\n", + "\n", + " # if list of ambulance is empty\n", " if not available:\n", " return None\n", - " \n", + "\n", + " # return the closest ambulance id\n", " return min(available, key=lambda a: DISTANCES[a.home_node, patient_node]).ambulance_id" ] }, @@ -950,12 +713,27 @@ "source": [ "### 3.5 Modified simpy processes\n", "\n", - "The biggest update is to our ambulance dispatch function that will now use a `FilterStore` to take account of `Patient` and `Ambulance` node location." + "The biggest update to our ambulance dispatch function is that it will now use a `FilterStore` to take account of `Patient` and `Ambulance` node location.\n", + "\n", + "Our code also needs to be able to handle a situation where all ambulances are in service. In these instances a `Patient` would just wait for the next available ambulance on a FIFO basis. \n", + "\n", + "Examples. Let's assume ambulance 2 is the closest to the patient. We `get` that ambulance from the filter store like so\n", + "\n", + "```python\n", + "best_id = 2\n", + "ambulance = yield store.get(lambda a: a.ambulance_id == best_id)\n", + "```\n", + "\n", + "In an instance where there are no ambulances available we can force the `FilterStore` to work on a FIFO basis like so\n", + "\n", + "```python\n", + "ambulance = yield store.get(lambda a: True)\n", + "```" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "383ad331-bba1-4571-9386-28d60d16635e", "metadata": {}, "outputs": [], @@ -966,14 +744,16 @@ " \n", " # find the closest ambulance in the store\n", " # note we pass store.items which is a list of Ambulance objects\n", - " # it may be empty!\n", + " # it may be empty! This means all ambulances are in use.\n", " best_id = closest_ambulance_id(store.items, patient.node)\n", "\n", + " # filter store code\n", " if best_id is not None:\n", " # if an ambulance is available get the closest ambulance from the store\n", " ambulance = yield store.get(lambda a: a.ambulance_id == best_id)\n", " else:\n", - " # other wise just wait for the next available ambulance. FIFO\n", + " # otherwise just wait for the next available ambulance. FIFO\n", + " # we are forcing a FilterStore to behave like a standard Store\n", " ambulance = yield store.get(lambda a: True)\n", "\n", " wait_time = env.now - patient.arrival_time\n", @@ -992,6 +772,13 @@ "\n", " # total turnaround time\n", " total_service = t_to_patient + scene_time + t_to_hospital + t_to_base\n", + "\n", + " # debug\n", + " trace(\n", + " f\"{env.now:.1f}: 🚑 {ambulance.ambulance_id} → {patient} \"\n", + " f\"(waited {wait_time:.1f} min, service {total_service:.1f} min)\"\n", + " )\n", + "\n", " yield env.timeout(total_service)\n", " \n", " ambulance.total_jobs += 1\n", @@ -1019,7 +806,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "4155f0a2-4ee7-4c69-954d-a6aee59a0a12", "metadata": {}, "outputs": [], @@ -1031,7 +818,7 @@ " log: dict\n", ") -> None: \n", " \"\"\"Modified Arrival process for patients to the ambulance sim\n", - " Now includes arrival node\"\"\"\n", + " Patient now includes arrival node\"\"\"\n", " for patient_id in itertools.count(start=1):\n", "\n", " # time until next patient arrival\n", @@ -1043,7 +830,7 @@ " patient = Patient(patient_id, env.now, node)\n", "\n", " # debug info\n", - " trace(f\"{env.now:.1f}: Arrival. {patient}\")\n", + " trace(f\"{env.now:.1f}: 📞 {patient}\")\n", "\n", " # create ambulance dispatch + service process\n", " env.process(dispatch_ambulance(env, store, patient, dists, log))" @@ -1051,7 +838,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "id": "df33aaf6-df42-4fb8-918f-fefae73fc9eb", "metadata": {}, "outputs": [], @@ -1076,7 +863,7 @@ " \"arrival\": Exponential(mean_iat, random_seed=seeds[0]),\n", " \"arrival_node\": DiscreteEmpirical(\n", " values=[0, 1, 2, 3, 4], \n", - " freq=[p * 100 for p in NODE_PROBS],\n", + " freq=[p * 100 for p in NODE_PROBS], # need to freqs not probs\n", " random_seed=seeds[1]),\n", " \"on_scene\": Exponential(mean_on_scene, random_seed=seeds[2]),\n", " }\n", @@ -1106,12 +893,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "1aa60d11-3dfb-4d2a-9bd1-b75259f9aab5", "metadata": {}, "outputs": [], "source": [ - "def single_run_summary(assignments, ambulances):\n", + "def single_run_summary(assignments, ambulances, run_length):\n", " waits = assignments[\"wait\"].to_numpy()\n", " \n", " # immediate versus delayed dispatches of Ambulance\n", @@ -1144,19 +931,171 @@ " for amb in ambulances:\n", " print(f\" Ambulance {amb.ambulance_id:<4} \"\n", " f\"{amb.home_node:>5} {amb.total_jobs:>6} \"\n", - " f\"{amb.total_busy / RUN_LENGTH:>8.2%}\")\n", + " f\"{amb.total_busy / run_length:>8.2%}\")\n", " print(\"═\" * 65)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "id": "19c9b8e8-7a2b-4d51-b070-9ba71463f44f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Simulation tracing set to: False\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
patient_idpatient_nodeambulance_idambulance_nodedispatch_distt_to_patientscene_timet_to_hospitalt_to_basetotal_serviceimmediate_dispatchwait
15215201042.82842711.31370817.3922338.0000008.00000044.705942True0.000000
1531493830.0000000.00000036.45605317.88854417.88854472.233141True0.000000
1541553624.00000016.0000000.48310717.88854417.88854452.260194True0.000000
1551601204.00000016.0000000.0670888.0000008.00000032.067088False13.713547
1561511310.0000000.00000055.8669578.0000008.00000071.866957True0.000000
\n", + "
" + ], + "text/plain": [ + " patient_id patient_node ambulance_id ambulance_node dispatch_dist \\\n", + "152 152 0 10 4 2.828427 \n", + "153 149 3 8 3 0.000000 \n", + "154 155 3 6 2 4.000000 \n", + "155 160 1 2 0 4.000000 \n", + "156 151 1 3 1 0.000000 \n", + "\n", + " t_to_patient scene_time t_to_hospital t_to_base total_service \\\n", + "152 11.313708 17.392233 8.000000 8.000000 44.705942 \n", + "153 0.000000 36.456053 17.888544 17.888544 72.233141 \n", + "154 16.000000 0.483107 17.888544 17.888544 52.260194 \n", + "155 16.000000 0.067088 8.000000 8.000000 32.067088 \n", + "156 0.000000 55.866957 8.000000 8.000000 71.866957 \n", + "\n", + " immediate_dispatch wait \n", + "152 True 0.000000 \n", + "153 True 0.000000 \n", + "154 True 0.000000 \n", + "155 False 13.713547 \n", + "156 True 0.000000 " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ + "set_trace(False)\n", + "\n", "# run model\n", - "ambulances, log = single_run(random_seed=42)\n", + "ambulances, log = single_run(run_length=RUN_LENGTH, random_seed=42)\n", "\n", "# get all of the patient to ambulance assignment details\n", "assignments = pd.DataFrame(log[\"assignments\"])\n", @@ -1165,22 +1104,54 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "90dc1713-9d25-4e2c-adc8-f51a123c4534", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Simulation tracing set to: False\n", + "\n", + "═════════════════════════════════════════════════════════════════\n", + " Patients served : 157\n", + " Immediate Dispatch : 73 (46.5%)\n", + " Delayed Dispatch : 84 (53.5%)\n", + " Overall mean wait (min) : 13.34\n", + "─────────────────────────────────────────────────────────────────\n", + " Ambulance Dispatch\n", + " Metric Immediate Delayed\n", + "─────────────────────────────────────────────────────────────────\n", + " Mean wait (min) 0.00 24.93\n", + " Mean dispatch dist 1.35 2.79\n", + " Mean travel to patient 5.38 11.16\n", + " Mean scene time 20.32 19.71\n", + " Mean travel to hospital 11.12 11.30\n", + " Mean return to base 11.25 11.41\n", + " Mean total service 48.07 53.58\n", + "─────────────────────────────────────────────────────────────────\n", + " Ambulance Node Jobs Util\n", + "─────────────────────────────────────────────────────────────────\n", + " Ambulance 1 0 17 76.70%\n", + " Ambulance 2 0 18 85.23%\n", + " Ambulance 3 1 18 81.13%\n", + " Ambulance 4 1 14 82.45%\n", + " Ambulance 5 2 13 83.17%\n", + " Ambulance 6 2 14 78.95%\n", + " Ambulance 7 3 13 77.54%\n", + " Ambulance 8 3 13 76.57%\n", + " Ambulance 9 4 19 79.82%\n", + " Ambulance 10 4 18 79.39%\n", + "═════════════════════════════════════════════════════════════════\n" + ] + } + ], "source": [ + "set_trace(False)\n", "ambulances, log = single_run(random_seed=42)\n", - "single_run_summary(assignments, ambulances)" + "single_run_summary(assignments, ambulances, RUN_LENGTH)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4090cd6c-535b-4e2e-aad1-b5db8c2b8941", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -1199,7 +1170,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.15" + "version": "3.11.14" } }, "nbformat": 4, diff --git a/content/img/two_resource_types.drawio b/content/img/two_resource_types.drawio new file mode 100644 index 0000000..91f54a7 --- /dev/null +++ b/content/img/two_resource_types.drawio @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/content/img/two_resource_types.png b/content/img/two_resource_types.png new file mode 100644 index 0000000000000000000000000000000000000000..a2b65144c5dd871c9526ce4b48473dc2761dcf79 GIT binary patch literal 167384 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 z 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�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 OH|L#sGfQX0r2hl2LCePg literal 0 HcmV?d00001 From f1e5da492b741d178cea6757b29d52d476c61ff3 Mon Sep 17 00:00:00 2001 From: TomMonks Date: Wed, 18 Mar 2026 12:51:16 +0000 Subject: [PATCH 4/5] fix(store): removed potenital for mistake in final cell --- content/15_resource_stores.ipynb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/content/15_resource_stores.ipynb b/content/15_resource_stores.ipynb index 32d3966..184aeaf 100644 --- a/content/15_resource_stores.ipynb +++ b/content/15_resource_stores.ipynb @@ -1104,7 +1104,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "id": "90dc1713-9d25-4e2c-adc8-f51a123c4534", "metadata": {}, "outputs": [ @@ -1112,7 +1112,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "Simulation tracing set to: False\n", "\n", "═════════════════════════════════════════════════════════════════\n", " Patients served : 157\n", @@ -1148,10 +1147,16 @@ } ], "source": [ - "set_trace(False)\n", - "ambulances, log = single_run(random_seed=42)\n", "single_run_summary(assignments, ambulances, RUN_LENGTH)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97b66363-bbda-4a91-b24a-4a10689bee5a", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From b9fc547aaafd35d16794afe4a5044a841f78da7a Mon Sep 17 00:00:00 2001 From: TomMonks Date: Wed, 18 Mar 2026 12:54:56 +0000 Subject: [PATCH 5/5] docs(changes): +nbs 13-15 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bc927a..fc7fa8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `12_arrival_classes.ipynb`: simulate unique processes for different classes of arrival to the model. * `distributions.py`: module containing some distributions to reduce code in notebooks. * `basic_model.py`: contains a single activity version of the call centre model to use with `07_exercise.ipynb` +* `13_warm_up.ipynb`: contains explanation of implementing warm up +* `14_initial_conditions.ipynb`: contains explanation of manually setting up processes before a model is run +* `15_resource_store.ipynb`: introduction to `Store` and `FilterStore` for advanced resource modelling +* `sim_utility.py`: added `trace`, `set_trace`, and `spawn_seeds` functions to use across notebooks. ## [v0.2.0 - 11/02/2024](https://github.com/pythonhealthdatascience/intro-open-sim/releases/tag/v0.2.0) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.14849934.svg)](https://doi.org/10.5281/zenodo.14849934)