{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.6670482907975878\n" ] } ], "source": [ "import random\n", "boxes = [(0,0), (0,1), (1,1)]\n", "a = 0\n", "b = 0\n", "for n in range(1000000):\n", " box = random.choice(boxes) # choose a random box\n", " ball = random.choice(box) # choose a random ball from that box\n", " if ball == 1: # if the chosen ball is green\n", " a += 1 # count the number of times this happened\n", " if box == (1,1): # if the chosen box contain two greens\n", " b += 1 # count the number of times this happened\n", "print(b/a)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Carwash\n", "Car 0 arrives at the carwash at 0.00.\n", "Car 1 arrives at the carwash at 0.00.\n", "Car 2 arrives at the carwash at 0.00.\n", "Car 3 arrives at the carwash at 0.00.\n", "Car 0 enters the carwash at 0.00.\n", "Car 1 enters the carwash at 0.00.\n", "Car 4 arrives at the carwash at 5.00.\n", "Carwash removed 97% of Car 0's dirt.\n", "Carwash removed 67% of Car 1's dirt.\n", "Car 0 leaves the carwash at 5.00.\n", "Car 1 leaves the carwash at 5.00.\n", "Car 2 enters the carwash at 5.00.\n", "Car 3 enters the carwash at 5.00.\n", "Car 5 arrives at the carwash at 10.00.\n", "Carwash removed 64% of Car 2's dirt.\n", "Carwash removed 58% of Car 3's dirt.\n", "Car 2 leaves the carwash at 10.00.\n", "Car 3 leaves the carwash at 10.00.\n", "Car 4 enters the carwash at 10.00.\n", "Car 5 enters the carwash at 10.00.\n", "Carwash removed 97% of Car 4's dirt.\n", "Carwash removed 56% of Car 5's dirt.\n", "Car 4 leaves the carwash at 15.00.\n", "Car 5 leaves the carwash at 15.00.\n", "Car 6 arrives at the carwash at 16.00.\n", "Car 6 enters the carwash at 16.00.\n" ] } ], "source": [ "\"\"\"\n", "Carwash example.\n", "\n", "Covers:\n", "\n", "- Waiting for other processes\n", "- Resources: Resource\n", "\n", "Scenario:\n", " A carwash has a limited number of washing machines and defines\n", " a washing processes that takes some (random) time.\n", "\n", " Car processes arrive at the carwash at a random time. If one washing\n", " machine is available, they start the washing process and wait for it\n", " to finish. If not, they wait until they can use one.\n", "\n", "\"\"\"\n", "import itertools\n", "import random\n", "import simpy\n", "\n", "RANDOM_SEED = 42\n", "NUM_MACHINES = 2 # Number of machines in the carwash\n", "WASHTIME = 5 # Minutes it takes to clean a car\n", "T_INTER = 7 # Create a car every ~7 minutes\n", "SIM_TIME = 20 # Simulation time in minutes\n", "\n", "arrivals=0 ; enters=0 ; leaves=0\n", "\n", "class Carwash:\n", " \"\"\"A carwash has a limited number of machines (``NUM_MACHINES``) to\n", " clean cars in parallel.\n", "\n", " Cars have to request one of the machines. When they got one, they\n", " can start the washing processes and wait for it to finish (which\n", " takes ``washtime`` minutes).\n", "\n", " \"\"\"\n", "\n", " def __init__(self, env, num_machines, washtime):\n", " self.env = env\n", " self.machine = simpy.Resource(env, num_machines)\n", " self.washtime = washtime\n", "\n", " def wash(self, car):\n", " \"\"\"The washing processes. It takes a ``car`` processes and tries\n", " to clean it.\"\"\"\n", " yield self.env.timeout(self.washtime)\n", " pct_dirt = random.randint(50, 99)\n", " print(f\"Carwash removed {pct_dirt}% of {car}'s dirt.\")\n", "\n", "\n", "def car(env, name, cw,arrivals,enters,leaves):\n", " \"\"\"The car process (each car has a ``name``) arrives at the carwash\n", " (``cw``) and requests a cleaning machine.\n", "\n", " It then starts the washing process, waits for it to finish and\n", " leaves to never come back ...\n", "\n", " \"\"\"\n", " print(f'{name} arrives at the carwash at {env.now:.2f}.')\n", " arrivals=arrivals+1\n", " with cw.machine.request() as request:\n", " yield request\n", "\n", " print(f'{name} enters the carwash at {env.now:.2f}.')\n", " enters=enters+1\n", " yield env.process(cw.wash(name))\n", "\n", " print(f'{name} leaves the carwash at {env.now:.2f}.')\n", " leaves=leaves+1\n", "\n", "\n", "def setup(env, num_machines, washtime, t_inter):\n", " \"\"\"Create a carwash, a number of initial cars and keep creating cars\n", " approx. every ``t_inter`` minutes.\"\"\"\n", " # Create the carwash\n", " carwash = Carwash(env, num_machines, washtime)\n", "\n", " car_count = itertools.count()\n", "\n", " # Create 4 initial cars\n", " for _ in range(4):\n", " env.process(car(env, f'Car {next(car_count)}', carwash,arrivals,enters,leaves))\n", "\n", " # Create more cars while the simulation is running\n", " while True:\n", " yield env.timeout(random.randint(t_inter - 2, t_inter + 2))\n", " env.process(car(env, f'Car {next(car_count)}', carwash,arrivals,enters,leaves))\n", "\n", "\n", "# Setup and start the simulation\n", "print('Carwash')\n", "random.seed(RANDOM_SEED) # This helps to reproduce the results\n", "\n", "# Create an environment and start the setup process\n", "env = simpy.Environment()\n", "env.process(setup(env, NUM_MACHINES, WASHTIME, T_INTER))\n", "\n", "# Execute!\n", "env.run(until=SIM_TIME)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Gas Station refuelling\n", " 87.0 s: Car 0 arrived at gas station\n", " 99.3 s: Car 0 refueled with 37.0L\n", " 129.0 s: Car 1 arrived at gas station\n", " 141.7 s: Car 1 refueled with 38.0L\n", " 284.0 s: Car 2 arrived at gas station\n", " 298.0 s: Car 2 refueled with 42.0L\n", " 385.0 s: Car 3 arrived at gas station\n", " 394.0 s: Car 3 refueled with 27.0L\n", " 459.0 s: Car 4 arrived at gas station\n", " 460.0 s: Calling tank truck\n", " 473.7 s: Car 4 refueled with 44.0L\n", " 705.0 s: Car 5 arrived at gas station\n", " 750.0 s: Car 6 arrived at gas station\n", " 760.0 s: Tank truck arrived and refuelled station with 188.0L\n", " 772.7 s: Car 6 refueled with 38.0L\n", " 774.3 s: Car 5 refueled with 43.0L\n", " 891.0 s: Car 7 arrived at gas station\n", " 899.7 s: Car 7 refueled with 26.0L\n" ] } ], "source": [ "\"\"\"\n", "Gas Station Refueling example\n", "\n", "Covers:\n", "\n", "- Resources: Resource\n", "- Resources: Container\n", "- Waiting for other processes\n", "\n", "Scenario:\n", " A gas station has a limited number of gas pumps that share a common\n", " fuel reservoir. Cars randomly arrive at the gas station, request one\n", " of the fuel pumps and start refueling from that reservoir.\n", "\n", " A gas station control process observes the gas station's fuel level\n", " and calls a tank truck for refueling if the station's level drops\n", " below a threshold.\n", "\n", "\"\"\"\n", "import itertools\n", "import random\n", "import simpy\n", "\n", "RANDOM_SEED = 42\n", "STATION_TANK_SIZE = 200 # Size of the gas station tank (liters)\n", "THRESHOLD = 25 # Station tank minimum level (% of full)\n", "CAR_TANK_SIZE = 50 # Size of car fuel tanks (liters)\n", "CAR_TANK_LEVEL = [5, 25] # Min/max levels of car fuel tanks (liters)\n", "REFUELING_SPEED = 3 # Rate of refuelling car fuel tank (liters / second)\n", "TANK_TRUCK_TIME = 300 # Time it takes tank truck to arrive (seconds)\n", "T_INTER = [30, 300] # Interval between car arrivals [min, max] (seconds)\n", "SIM_TIME = 1000 # Simulation time (seconds)\n", "\n", "\n", "def car(name, env, gas_station, station_tank):\n", " \"\"\"A car arrives at the gas station for refueling.\n", "\n", " It requests one of the gas station's fuel pumps and tries to get the\n", " desired amount of fuel from it. If the station's fuel tank is\n", " depleted, the car has to wait for the tank truck to arrive.\n", "\n", " \"\"\"\n", " car_tank_level = random.randint(*CAR_TANK_LEVEL)\n", " print(f'{env.now:6.1f} s: {name} arrived at gas station')\n", " with gas_station.request() as req:\n", " # Request one of the gas pumps\n", " yield req\n", "\n", " # Get the required amount of fuel\n", " fuel_required = CAR_TANK_SIZE - car_tank_level\n", " yield station_tank.get(fuel_required)\n", "\n", " # The \"actual\" refueling process takes some time\n", " yield env.timeout(fuel_required / REFUELING_SPEED)\n", "\n", " print(f'{env.now:6.1f} s: {name} refueled with {fuel_required:.1f}L')\n", "\n", "\n", "def gas_station_control(env, station_tank):\n", " \"\"\"Periodically check the level of the gas station tank and call the tank\n", " truck if the level falls below a threshold.\"\"\"\n", " while True:\n", " if station_tank.level / station_tank.capacity * 100 < THRESHOLD:\n", " # We need to call the tank truck now!\n", " print(f'{env.now:6.1f} s: Calling tank truck')\n", " # Wait for the tank truck to arrive and refuel the station tank\n", " yield env.process(tank_truck(env, station_tank))\n", "\n", " yield env.timeout(10) # Check every 10 seconds\n", "\n", "\n", "def tank_truck(env, station_tank):\n", " \"\"\"Arrives at the gas station after a certain delay and refuels it.\"\"\"\n", " yield env.timeout(TANK_TRUCK_TIME)\n", " amount = station_tank.capacity - station_tank.level\n", " station_tank.put(amount)\n", " print(\n", " f'{env.now:6.1f} s: Tank truck arrived and refuelled station with {amount:.1f}L'\n", " )\n", "\n", "\n", "def car_generator(env, gas_station, station_tank):\n", " \"\"\"Generate new cars that arrive at the gas station.\"\"\"\n", " for i in itertools.count():\n", " yield env.timeout(random.randint(*T_INTER))\n", " env.process(car(f'Car {i}', env, gas_station, station_tank))\n", "\n", "\n", "# Setup and start the simulation\n", "print('Gas Station refuelling')\n", "random.seed(RANDOM_SEED)\n", "\n", "# Create environment and start processes\n", "env = simpy.Environment()\n", "gas_station = simpy.Resource(env, 2)\n", "station_tank = simpy.Container(env, STATION_TANK_SIZE, init=STATION_TANK_SIZE)\n", "env.process(gas_station_control(env, station_tank))\n", "env.process(car_generator(env, gas_station, station_tank))\n", "\n", "# Execute!\n", "env.run(until=SIM_TIME)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "114.3768500000003\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhsAAAGkCAYAAACYU+eAAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/SrBM8AAAACXBIWXMAAAxOAAAMTgF/d4wjAAAgVElEQVR4nO3dfXBU5eG38e+GJBsJJG5MEXGpsUknJBKRYej4QpEBLNCgBEjaKhQwlTKDBLCSiqhAAxGsEBjQ6FBlQFCkVHgAg8wAYlNsqykMTdsEgoIEBCWJNJG8LCE5zx8O++sKCcQ9N7ur12dm/9hzNnvfuXtKLnfPnnVYlmUJAADAkLBATwAAAHy7ERsAAMAoYgMAABhFbAAAAKOIDQAAYBSxAQAAjAoP9AT+V2trq06dOqWuXbvK4XAEejoAAOAqWJalL7/8Uj169FBY2KWvYwRVbJw6dUo9e/YM9DQAAMA3cOLECbnd7ku2B1VsdO3aVdJXk42JiQnwbAAAwNWoq6tTz549vX/Hvy6oYuPiWycxMTHEBgAAIaatUyA4QRQAABhFbAAAAKOIDQAAYBSxAQAAjOpQbFRXVys3N1dTp071bmtubla3bt3kcDh8bmPHjvX52cGDB3v3xcfHq6mpyZ7fAAAABLWr/jRKc3Oz9u3bp61bt+ruu+/2bi8qKtKQIUM0ePBgRURESJLefPNNPfDAA97H7N+/X263W8uWLZMkJScnKyoqyq7fAQAABLGrjo2IiAhlZGRo06ZNPttdLpc2bNjgs+3FF1/0iY3CwkKtXLlSsbGxfk4XAACEmg6fs3Hx1YuL7r33Xp/7x44dk8vlksvl8t5fu3atevXqpZycHNXV1fkxXQAAEGpsP0F08+bNGj169P8NEBamtWvXKjMzU6tXr9Ydd9yh06dPt/scKSkpcrvdcrvdKigosHuKAADgGnJYlmV15AcmTZokSVqzZs1l9w8cOFAbN27UTTfddMm+jz/+WIMGDdLAgQP1+uuvX7K/rq5OsbGxqq2t5QqiAACEiCv9/bb1lY3PPvtMki4bGpKUmJiolStXavfu3XYOCwAAgpitsbF161aNGjWq3ccMGTJEHo/HzmEBAEAQszU2tmzZojFjxrT7mBMnTmjkyJF2DgsAAIJYh2OjpaVFra2tl2z/73//q7Nnz+rWW2/12b5ixQqtWbNGlmWppqZGCxYs0NKlS7/5jAEAQEjpUGxs3LhRxcXF+stf/nLJ9TaKioou+4pFY2Ojpk+frqSkJM2aNUvLly/XjTfe6N+sAQBAyOjwp1FM4tMowLdbwuyiQE+hwz5ZnB7oKQBB75p+GgUAAODriA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGBUeKAnAOCbSZhdFOgpAMBV4ZUNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwKjwjjy4urpazz33nOrr61VYWOizLy8vT/PmzfPe//DDD9W/f39J0pEjRzRv3jzFxcVJkpYuXSqn0+nv3AHbJMwuCvQUAOBb66pjo7m5Wfv27dPWrVt19913++xramrSgQMHtGzZMklSdHS0NzTq6+v105/+VO+8846SkpL0wgsvaMaMGXr55Zdt/DUAAECwuurYiIiIUEZGhjZt2nTJvjVr1mjatGkaOnToJftefvllud1uJSUlSZIeeughfe9739Ps2bOVkJDwzWcOAABCQofP2YiIiPC539raqoKCAmVkZGjs2LE6fPiwz/5t27apd+/e3vtxcXG6+eabVVTEy9YAAHwX+H2CaGNjo/Lz85Wbm6uSkhL169dPe/bs8e4vKytTfHy8z8+4XC5VVFT4OzQAAAgBfsdGdHS0srKyNG/ePB06dEh33XWXsrOz1dLSIkmqra31nhh6kdPpVENDQ5vPmZKSIrfbLbfbrYKCAn+nCAAAAqhDn0a5ks6dO2vdunW69dZbVVFRoZSUFMXFxcnj8fg8rrGxUS6Xq83nKS8vV0xMjJ1TAwAAAWL7dTa6d++u1NRUb2AkJiaqurra5zFVVVVKTk62e2gAABCEbI+NlpYWRUZGKi0tTZKUmZmpkpIS7/6amhrV1NRo2LBhdg8NAACCUIdjo6WlRa2trd77u3fv1vz581VfX68LFy7oySef1MKFC9WpUydJ0qRJk/TRRx/p888/lyStX79eU6ZMkdvttulXAAAAwaxD52xs3LhRxcXFCgsL06ZNm5SVlSVJWrVqlV588UXdeeedmjt3rveCXtJXnzx56623NHPmTPXo0UMOh8N78S8AAPDt57Asywr0JC6qq6tTbGysamtrOUEU1xSXK0dbPlmcHugpAEHvSn+/+SI2AABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwKjwQE8AAIJZwuyiQE+hwz5ZnB7oKQA+eGUDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjOhQb1dXVys3N1dSpU3227927V7fffrtiYmI0YsQIffLJJ5f87ODBg+VwOORwOBQfH6+mpia/Jg4AAEJD+NU+sLm5Wfv27dPWrVt19913e7efOnVKc+bM0cyZM1VfX6/f/e53Sk9P1z//+U+Fh3/19Pv375fb7dayZcskScnJyYqKirL5VwEAAMHoqmMjIiJCGRkZ2rRpk8/2d999V9u3b1d8fLwkKTExUenp6frPf/6jPn36SJIKCwu1cuVKxcbG2jh1AAAQCjp8zkZERITP/Z/97Gfe0JCkQYMGSZKcTqck6dixY1q7dq169eqlnJwc1dXV+TFdAAAQavw+QTQyMtLn/meffabbbrtNvXr1+mqAsDCtXbtWmZmZWr16te644w6dPn3a32EBAECIsP3TKOvXr9fzzz/vvX/LLbdo3LhxWrlypUpLS9Xc3KxZs2a1+xwpKSlyu91yu90qKCiwe4oAAOAauupzNq5GZWWlPB6PRowYcdn9iYmJWrlypaZMmdLu85SXlysmJsbOqQEAgACxLTYaGxv10ksvKS8vr93HDRkyRB6Px65hAQBAkLPlbZSWlhYtXrxYs2fP9p5AWl9ff9nHnjhxQiNHjrRjWAAAEAI6/MpGS0uLHA6Hz/1f//rXGjBggP72t79Jks6cOaNDhw7p2Wef1YoVKxQTE6OJEyfqiy++0IIFC7R8+XLbfgEAABDcOhQbGzduVHFxscLCwrRp0yZlZWUpOztbr732mlavXu3z2LfeekvSV2+vPP3001qwYIEGDhyo5cuX68Ybb7TvNwAAAEHNYVmWFehJXFRXV6fY2FjV1tZygiiuqYTZRYGeAmCbTxanB3oK+I650t9vvogNAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHhgZ4AAMBeCbOLAj2FDvtkcXqgpwCDeGUDAAAYRWwAAACjiA0AAGBUh87ZqK6u1nPPPaf6+noVFhZ6tzc0NGjGjBm67rrr9MUXXyg/P1+33HKLd/8HH3yg5cuXy+Vy6frrr1d+fr4cDod9vwUAAAhaV/3KRnNzs/bt26etW7eqoaHBZ9+ECRP04x//WCtWrNATTzyh+++/X83NzZKkTz/9VA8++KAKCwtVWFio8+fP67nnnrP3twAAAEHrqmMjIiJCGRkZ6t+/v8/2/fv3q6ioSD//+c8lSWlpaXI4HNqwYYMkafHixbr33nvlcrkkSePGjVN+fr7q6+vt+h0AAEAQ6/A5GxERET73t23bpqSkJDmdTu+2Pn36aPv27d79vXv39u5LS0tTQ0OD3nvvvW84ZQAAEEr8PkG0rKxM8fHxPttcLpcqKipUX1+vyspKn/3h4eHq0qWLKioq/B0aAACEAL9jo7a2VnFxcT7bnE6nGhoaVFtbK0lt7m9LSkqK3G633G63CgoK/J0iAAAIIL+vIBoXF6dz5875bGtsbJTL5fJGhsfjuez+tpSXlysmJsbfqQEAgCDgd2wkJiZqz549PtuqqqqUnJysqKgo9ejRQ9XV1d59jY2NOnfunJKTk/0dGkEoFC+TDAAwy++3UTIzM1VaWurz6kVZWZnS09O9+0tKSrz7ysvLFRsbq3vuucffoQEAQAjocGy0tLSotbXVe79v374aMGCAdu7cKUk6ePCgwsLClJWVJUnKycnR3r171dTUJElat26dnn76aUVFRdkxfwAAEOQ69DbKxo0bVVxcrLCwMG3atMkbFG+88YYee+wxvf/++6qurtaOHTvUqVMnSVJSUpIKCwv1yCOPKC4uTt26ddOsWbPs/00AAEBQcliWZQV6EhfV1dUpNjZWtbW1nCAaojhnA8A3wVfMh7Yr/f3mi9gAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADDKltjIyMiQw+Hwud1www3e/Xl5eT77SkpK7BgWAACEgHB/n6CqqkrV1dV64YUXFB0dLUk6ePCgamtrJUlNTU06cOCAli1bJkmKjo5W//79/R0WAACECL9j49ixY9q1a5euu+4677aZM2dqzJgxkqQ1a9Zo2rRpGjp0qL9DAQCAEOT32yg/+tGPfEJDkvbs2aOf/OQnam1tVUFBgTIyMjR27FgdPnzY3+EAAECIsf0E0f379ys1NVVOp1ONjY3Kz89Xbm6uSkpK1K9fP+3Zs+eKz5GSkiK32y23262CggK7pwgAAK4hv99G+botW7Z430KJjo5WVlaWJCk3N1ejRo1Sdna2jh49qk6dOrX5HOXl5YqJibF7agAAIABsf2Vjx44dSk9Pv2R7586dtW7dOp05c0YVFRV2DwsAAIKUrbFx+PBhud1udenS5bL7u3fvrtTUVHk8HjuHBQAAQczW2Ni8ebNGjx7d5v6WlhZFRkYqLS3NzmEBAEAQszU2tm/frgceeMB7f/fu3Zo/f77q6+t14cIFPfnkk1q4cGG752sAAIBvF9ti4+TJk4qOjva5cqgkrVq1SgkJCRo9erSysrI0ZMgQu4YEAAAhwLZPo7jdbu3atctn29ChQ3Xq1Cm7hgAAACGIL2IDAABGERsAAMAoYgMAABhFbAAAAKOIDQAAYJTt340C+yTMLgr0FAAA8BuvbAAAAKOIDQAAYBSxAQAAjCI2AACAUcQGAAAwitgAAABGERsAAMAoYgMAABhFbAAAAKOIDQAAYBSxAQAAjCI2AACAUcQGAAAwitgAAABGERsAAMAoYgMAABhFbAAAAKOIDQAAYBSxAQAAjCI2AACAUcQGAAAwitgAAABGERsAAMAoYgMAABhFbAAAAKOIDQAAYFR4oCcAAEDC7KJAT6HDPlmcHugphAxe2QAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADDK1tgoLi6Ww+Hw3p5//nlJUkNDgyZPnqzp06dr/PjxOn78uJ3DAgCAIGbrFUTXrFmjZcuWee9PnDhRkjRhwgQ98MADmjBhgv71r3/p/vvv1/79+xUREWHn8AAAIAjZFhvl5eVyu92aOXOmz/b9+/erqKhIr7/+uiQpLS1NDodDGzZs0IQJE+waHgAABCnb3kZZsmSJnn32WQ0cOFA7duzwbt+2bZuSkpLkdDq92/r06aPt27fbNTQAAAhitsVGZmamlixZIo/Ho/T0dC1atEiSVFZWpvj4eJ/HulwuVVRUtPlcKSkpcrvdcrvdKigosGuKAAAgAGx7G2XEiBEaMWKEZsyYoWeeeUZz587V6NGjVVtbq7i4OJ/HOp1ONTQ0tPlc5eXliomJsWtqAAAggGz/6KvD4dCCBQvUu3dv7d27V3FxcfJ4PD6PaWxslMvlsntoAAAQhIxcZ8PhcGjw4MHyeDxKTExUdXW1z/6qqiolJyebGBoAAAQZYxf1On36tIYPH67MzEyVlpb6vLpRVlam9PR0U0MDAIAgYktsVFZWKicnR5WVlZKkjRs36rbbblOvXr3Ut29fDRgwQDt37pQkHTx4UGFhYcrKyrJjaAAAEORsiY3w8HD9+c9/Vmpqqu69915duHBBTz31lHf/G2+8oT/96U/67W9/qxUrVmjHjh3q1KmTHUMDAIAgZ8unUXr06KHS0tI298fHx2vdunV2DAUAAEIMX8QGAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo8IDPQEAAEJRwuyiQE+hwz5ZnB6QcXllAwAAGGVLbBw/flzDhg1T165d1bdvX+3du9dnf15enhwOh/dWUlJix7AAACAE+P02imVZmjx5su677z6NHz9eS5cu1ciRI1VaWqrExEQ1NTXpwIEDWrZsmSQpOjpa/fv393viAAAgNPgdG//+97/1xBNPaMiQIZKkYcOG6Qc/+IHefvttzZgxQ2vWrNG0adM0dOhQvycLAABCj99voyQnJ3tDQ5K6deum1NRUOZ1Otba2qqCgQBkZGRo7dqwOHz7s73AAACDE+B0bkZGRl2w7e/asRo4cqcbGRuXn5ys3N1clJSXq16+f9uzZc8XnTElJkdvtltvtVkFBgb9TBAAAAWT7R1+Li4s1ZswYud1uSVJWVpYkKTc3V6NGjVJ2draOHj2qTp06tfkc5eXliomJsXtqAAAgAGz96Gtzc7M2bNigvLy8S/Z17txZ69at05kzZ1RRUWHnsAAAIIjZGhtLly7VnDlz5HQ6L7u/e/fuSk1NlcfjsXNYAAAQxGyLjVdeeUXDhw9Xz549JUkej0ctLS0+j2lpaVFkZKTS0tLsGhYAAAQ5W87ZKCws1MmTJ+V2u7Vz5041NDRoy5Yt+uUvf6m//vWvys3NldPp1Jw5c7Rw4cJ2z9cAAADfLn7HxurVq/Xoo49KkhYtWuTdnpOTo7CwMK1atUovvvii7rzzTs2dO5cLegEA8B3jd2xkZ2crOzu7zf2nTp3ydwgAABDC+CI2AABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABglC3fjRIKEmYXBXoKAAB8J/HKBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwiNgAAgFHEBgAAMIrYAAAARhEbAADAKGIDAAAYRWwAAACjiA0AAGAUsQEAAIwKv1YDLVq0SEePHlVDQ4N+9atfafDgwddqaAAAEEDX5JWNF154QUeOHNEf/vAHvfrqq5o6dao+/vjjazF0SKr7cEugpxDSWD//sH7+Yf38w/r5J1jXz3hsnD9/XvPnz9fEiRMlSVFRURo2bJjy8/NNDx2y6kr+X6CnENJYP/+wfv5h/fzD+vknWNfPeGy8//77qqmpUe/evb3b+vTpo7ffftv00AAAIAgYP2ejrKxMYWFhcrlc3m0ul0tVVVU6e/asz3bLsiRJdXV1ts+j1dNg+3OaY4XYfIMN6+cf1s8/rJ9/WD//tL9+Jv6+/u/zXvw7fgnLsPz8fCsuLs5nW1FRkSXJOnnypM/2EydOWJK4cePGjRs3biF4O3HixGVbwPgrG3FxcfJ4PD7bGhsbJcnnVQ1J6tGjh06cOKGuXbvK4XCYnhoAALCBZVn68ssv1aNHj8vuNx4biYmJqq+vV1NTk6KioiRJVVVVcrvd6ty5s89jw8LC5Ha7TU8JAADYLDY2ts19xk8QHTRokOLj41VSUuLdVlZWpvT0dNNDAwCAIGA8NiIiIvTYY49p8+bNkqT6+nrt2rVLs2fPNj00AAAIAtfkol5PPPGEwsPDNWPGDD366KN65ZVXlJCQcC2GDjmLFi3S5MmTNW7cOL377ruBnk7QKy4ulsPh8N6ef/55SVJDQ4MmT56s6dOna/z48Tp+/HiAZxo8qqurlZubq6lTp/psv9KaffDBB3rwwQc1depUzZkzp+2zzr/F2lo7ScrLy/M5Fv/31dwjR47ooYce0rRp0zRt2rRLzmP7Ljh+/LiGDRumrl27qm/fvtq7d693H8felbW3flIIHH9mPoOCb2LlypXWww8/bFmWZTU2NlrJycnWRx99FOBZBbeHH37YWrZsmff2xRdfWJZlWWPHjrXWrl1rWZZllZaWWmlpadb58+cDOdWgcP78eWvLli3WD3/4Q2vixIk++9pbs5MnT1q33nqrd30ff/xxa9GiRdd07oHW3to1NjZao0aN8h6Hq1at8u47d+6clZSUZB05csSyrK/+fz5lypRrOfWAa21tte677z7r97//vfXaa69Zffr0sTp37uz9941jr31XWr9QOP6IjSDh8XisG264wXrvvfe826ZPn+6ND1yqrKzMeuaZZy7Z/o9//MOKioqympqavNtuv/127z9msKyHHnrI5w/mldZs2rRp1qRJk7z7Dhw4YHXp0sU6d+7cNZtzsPj62lmWZb300kvWrl27Lvv4JUuWWIMGDfLer6mpscLCwqxjx44ZnGVwKS0ttXbv3u29//nnn1vR0dHW8uXLOfauQnvrZ1mhcfzxra9BgiutdtySJUv07LPPauDAgdqxY4d3+7Zt25SUlCSn0+nd1qdPH23fvj0Q0wxKERERPvevtGbbtm3zOTbT0tLU0NCg995775rMN5h8fe1aW1tVUFCgjIwMjR07VocPH/bZ//W1i4uL080336yioqJrMt9gkJycrCFDhnjvd+vWTampqXI6nRx7V6G99QuV44/YCBJXutIqLpWZmaklS5bI4/EoPT1dixYtkvTVWsbHx/s81uVyqaKiIhDTDAntrVl9fb0qKyt99oeHh6tLly6sqb66blB+fr5yc3NVUlKifv36ac+ePd79HI9SZGTkJdvOnj2rkSNHcuxdhfbWL1SOP2IjSNTW1ur6669XWNj//U9ysfQbGrh07+WMGDFCM2fO1N///nc99dRTmjt3rg4dOqTa2lrFxcX5PNbpdLKO7WhvzWprayWJNW1DdHS0srKyNG/ePB06dEh33XWXsrOz1dLSIqn9tf2uKi4u1pgxY+R2uzn2voH/Xb9QOf6IjSDRkSutwpfD4dCCBQvUu3dv7d27t821ZB3b1t6aXfyHijW9ss6dO2vdunU6c+aM978cOR59NTc3a8OGDcrLy5PEsddRX1+//xXMxx+xEST+90qrF7V1pVVcyuFwaPDgwfJ4PEpMTFR1dbXP/qqqKiUnJwdodsGvvTWLiopSjx49fPY3Njbq3LlzrOlldO/eXampqd5/4DkefS1dulRz5szxvnLLsdcxX1+/rwvW44/YCBJcadV/p0+f1vDhw5WZmanS0lKfmmct23elNcvMzPQ5NsvLyxUbG6t77rnnms812LW0tCgyMlJpaWmSLl27mpoa1dTUaNiwYYGaYsC88sorGj58uHr27Cnpq1csxowZw7F3lS63fhffLrkoWI8/YiNIcKXVjqmsrFROTo4qKyslSRs3btRtt92mXr16qW/fvhowYIB27twpSTp48KDCwsKUlZUVyCkHlZaWFrW2tnrvX2nNcnJytHfvXu8rb+vWrdPTTz/t/b6j75Kvr93u3bs1f/581dfX68KFC3ryySe1cOFCderUSZI0adIkffTRR/r8888lSevXr9eUKVO+c98DVVhYqKNHj+qzzz7Tzp07tXnzZj3yyCMce1eprfXbs2dPaBx/1/SDtmjXhQsXrFmzZlnTp0+3Jk6caO3bty/QUwpan376qZWWlmZFR0dbAwcOtNavX++zv6qqyho/fryVm5trPfzww9ann34aoJkGnzfffNP6/ve/byUkJFh//OMfvduvtGbvvPOONW7cOCsnJ8dasGDBtZ52ULjc2u3atcu66aabrPj4eGvkyJHWhx9+eMnPlZSUWL/4xS+s3/zmN9bjjz/+nbvA3KuvvnrZryPPycmxLItj70raW79QOf4clvUdvO4rAAC4ZngbBQAAGEVsAAAAo4gNAABgFLEBAACMIjYAAIBRxAYAADCK2AAAAEYRGwAAwChiAwAAGEVsAAAAo4gNAABg1P8HfwxIzOGwKEEAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "\"\"\"\n", "Gas Station Refueling example\n", "\n", "Covers:\n", "\n", "- Resources: Resource\n", "- Resources: Container\n", "- Waiting for other processes\n", "\n", "Scenario:\n", " A gas station has a limited number of gas pumps that share a common\n", " fuel reservoir. Cars randomly arrive at the gas station, request one\n", " of the fuel pumps and start refueling from that reservoir.\n", "\n", " A gas station control process observes the gas station's fuel level\n", " and calls a tank truck for refueling if the station's level drops\n", " below a threshold.\n", "\n", "\"\"\"\n", "import itertools\n", "import random\n", "import simpy\n", "import numpy as np\n", "\n", "RANDOM_SEED = 42\n", "STATION_TANK_SIZE = 200 # Size of the gas station tank (liters)\n", "THRESHOLD = 25 # Station tank minimum level (% of full)\n", "CAR_TANK_SIZE = 50 # Size of car fuel tanks (liters)\n", "CAR_TANK_LEVEL = [5, 25] # Min/max levels of car fuel tanks (liters)\n", "REFUELING_SPEED = 3 # Rate of refuelling car fuel tank (liters / second)\n", "TANK_TRUCK_TIME = 300 # Time it takes tank truck to arrive (seconds)\n", "T_INTER = [30, 300] # Interval between car arrivals [min, max] (seconds)\n", "SIM_TIME = 28800 # Simulation time (seconds)\n", "COST = 1. # dollars per liter\n", "NUM_PUMPS = 4 # number of pumps, you need one employee for every pump\n", "\n", "total_cars = 0 ; liters_sold = [] ; liters_bought = [STATION_TANK_SIZE]\n", "\n", "def car(name, env, gas_station, station_tank):\n", " global total_cars,liters_sold,liters_bought\n", " \"\"\"A car arrives at the gas station for refueling.\n", "\n", " It requests one of the gas station's fuel pumps and tries to get the\n", " desired amount of fuel from it. If the station's fuel tank is\n", " depleted, the car has to wait for the tank truck to arrive.\n", "\n", " \"\"\"\n", " car_tank_level = random.randint(*CAR_TANK_LEVEL)\n", "# print(f'{env.now:6.1f} s: {name} arrived at gas station')\n", " with gas_station.request() as req:\n", " # Request one of the gas pumps\n", " yield req\n", "\n", " # Get the required amount of fuel\n", " fuel_required = CAR_TANK_SIZE - car_tank_level\n", " liters_sold.append(fuel_required)\n", " yield station_tank.get(fuel_required)\n", "\n", " # The \"actual\" refueling process takes some time\n", " yield env.timeout(fuel_required / REFUELING_SPEED)\n", "\n", "# print(f'{env.now:6.1f} s: {name} refueled with {fuel_required:.1f}L')\n", " total_cars=total_cars+1\n", "\n", "\n", "def gas_station_control(env, station_tank):\n", " global total_cars,liters_sold,liters_bought\n", " \"\"\"Periodically check the level of the gas station tank and call the tank\n", " truck if the level falls below a threshold.\"\"\"\n", " while True:\n", " if station_tank.level / station_tank.capacity * 100 < THRESHOLD:\n", " # We need to call the tank truck now!\n", "# print(f'{env.now:6.1f} s: Calling tank truck')\n", " # Wait for the tank truck to arrive and refuel the station tank\n", " yield env.process(tank_truck(env, station_tank))\n", "\n", " yield env.timeout(10) # Check every 10 seconds\n", "\n", "\n", "def tank_truck(env, station_tank):\n", " global total_cars,liters_sold,liters_bought\n", " \"\"\"Arrives at the gas station after a certain delay and refuels it.\"\"\"\n", " yield env.timeout(TANK_TRUCK_TIME)\n", " amount = station_tank.capacity - station_tank.level\n", " liters_bought.append(amount)\n", " station_tank.put(amount)\n", "# print(\n", "# f'{env.now:6.1f} s: Tank truck arrived and refuelled station with {amount:.1f}L'\n", "# )\n", "\n", "\n", "def car_generator(env, gas_station, station_tank):\n", " global total_cars,liters_sold,liters_bought\n", " \"\"\"Generate new cars that arrive at the gas station.\"\"\"\n", " for i in itertools.count():\n", " yield env.timeout(random.randint(*T_INTER))\n", " env.process(car(f'Car {i}', env, gas_station, station_tank))\n", "\n", "\n", "# Setup and start the simulation\n", "random.seed(RANDOM_SEED)\n", "\n", "tc=[] ; tl=[] ; tb=[] ; profit=[]\n", "price=1.00 ; cost=0.95 ; npeople=1 ; rate=15.\n", "\n", "for i in range(1000):\n", " total_cars = 0 ; liters_sold = [] ; liters_bought = [STATION_TANK_SIZE]\n", " env = simpy.Environment()\n", " gas_station = simpy.Resource(env, 2)\n", " station_tank = simpy.Container(env, STATION_TANK_SIZE, init=STATION_TANK_SIZE)\n", " env.process(gas_station_control(env, station_tank))\n", " env.process(car_generator(env, gas_station, station_tank))\n", " env.run(until=SIM_TIME)\n", " tc.append(total_cars)\n", " tl.append(sum(liters_sold))\n", " tb.append(sum(liters_bought))\n", " profit.append(price*sum(liters_sold)-cost*sum(liters_bought)-npeople*rate*8.)\n", "\n", "profit=np.array(profit)\n", "print(profit.mean())\n", "plt.hist(profit);" ] }, { "cell_type": "code", "execution_count": null, "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.2" } }, "nbformat": 4, "nbformat_minor": 4 }