{ "cells": [ { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "\n", "*This notebook contains course material from [CBE40455](https://jckantor.github.io/CBE40455) by\n", "Jeffrey Kantor (jeff at nd.edu); the content is available [on Github](https://github.com/jckantor/CBE40455.git).\n", "The text is released under the [CC-BY-NC-ND-4.0 license](https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode),\n", "and code is released under the [MIT license](https://opensource.org/licenses/MIT).*" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "\n", "< [Spankys Pizzeria](http://nbviewer.jupyter.org/github/jckantor/CBE40455/blob/master/notebooks/02.06-Spankys-Pizzeria.ipynb) | [Contents](toc.ipynb) | [Geometric Brownian Price Process](http://nbviewer.jupyter.org/github/jckantor/CBE40455/blob/master/notebooks/02.08-Geometric-Brownian-Price-Process.ipynb) >
" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "# Warehouse Fulfullment Operations" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "This notebook demonstrates use of the [SimPy](http://simpy.readthedocs.org/en/latest/) to simulate the order fulfillment operations of a hypothetical warehouse." ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "## Order Fulfillment Model" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "http://kevingue.wordpress.com/2012/02/13/discrete-time-modeling-for-order-picking/" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "### Order Processing" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "The order fulfillment operation is a sequence of eight events\n", "\n", "1. Order sent to the fulfillment center.\n", "1. Picker requested for the order.\n", "1. Order picked from the warehouse.\n", "1. Picker released.\n", "1. Inspector requested to review the order prior to shipping.\n", "1. Order reviewed.\n", "1. Reviewer released.\n", "1. Order shipped." ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "### Order Generator" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "`orderGenerator` creates a sequence of orders following a Poisson distribution at an average rate 1/T_ORDER. Each order is tagged with a unique name using `itertools.count()`." ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "### Event Logging" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "Events are logged and stored in a dictionary attached to an instance of the fulfillment class. The keys are an (orderId,event) tuple with the time stamp stored as the value. The raw data trace can be accessed as the `._data` field, or as a pandas Dataframe in the `.log` field." ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "### Fulfillment Class" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "pycharm": {} }, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "import random\n", "import simpy\n", "import itertools\n", "import pandas as pd\n", "from IPython.core.display import display, HTML\n", "\n", "# Global variables\n", "T_ORDER = 10 # average time between orders (Poisson)\n", "T_PICK = 15 # mean time to pick order from warehouse (beta)\n", "T_REVIEW = 2 # mean time to review order prior to shipment (beta)\n", "T_SHIP_MIN = 8; T_SHIP_MAX = 12 # mean time to ship order (uniform)\n", "ALPHA = 2.0; BETA = 5.0 # beta distribution parameters\n", "\n", "# Global defaults\n", "T_SIM = 1440 # simulation period\n", "N_PICKER = 2 # number of product pickers\n", "N_REVIEWER = 1 # number of product reviewers\n", "\n", "\n", "\n", "class fulfillment(object):\n", " def __init__(self, n_picker=N_PICKER, n_reviewer=N_REVIEWER):\n", " self.env = simpy.Environment()\n", " self._data = dict()\n", " self.picker = simpy.Resource(self.env,n_picker)\n", " self.reviewer = simpy.Resource(self.env,n_reviewer)\n", " self.env.process(self.orderGenerator())\n", " \n", " def writeLog(self,orderId,event):\n", " self._data[orderId,event] = self.env.now\n", " \n", " @property\n", " def log(self):\n", " df = pd.DataFrame([[a,b,self._data[a,b]] for (a,b) in self._data.keys()]) \n", " df = df.pivot(index=0,columns=1,values=2).reset_index()\n", " return df[list(df.columns)[1:]] \n", " \n", " @property\n", " def stats(self):\n", " tdelta = pd.DataFrame()\n", " for c in self.log.columns:\n", " tdelta[c] = self.log[c] - self.log['Ordered']\n", " return pd.DataFrame([tdelta.mean(),tdelta.std()],index=['mean','std'])\n", " \n", " def order(self,orderId):\n", " self.writeLog(orderId,'Ordered')\n", " with self.picker.request() as preq:\n", " yield preq\n", " self.writeLog(orderId,'Pick Assigned')\n", " yield self.env.timeout(T_PICK*((ALPHA+BETA)/ALPHA)*random.betavariate(ALPHA,BETA))\n", " self.writeLog(orderId,'Picked')\n", " self.picker.release(preq)\n", " with self.reviewer.request() as rreq:\n", " yield rreq\n", " self.writeLog(orderId,'Review Assigned')\n", " yield self.env.timeout(T_REVIEW*((ALPHA+BETA)/ALPHA)*random.betavariate(ALPHA,BETA))\n", " self.writeLog(orderId,'Reviewed')\n", " self.reviewer.release(rreq)\n", " \n", " yield self.env.timeout(random.uniform(T_SHIP_MIN,T_SHIP_MAX))\n", " self.writeLog(orderId,'Shipped')\n", " \n", " def orderGenerator(self):\n", " for orderId in itertools.count():\n", " yield self.env.timeout(random.expovariate(1.0/T_ORDER))\n", " self.env.process(self.order(orderId))\n", " \n", " def run(self,t_sim = T_SIM):\n", " self.env.run(until=t_sim)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "### Simulation" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false, "pycharm": {} }, "outputs": [ { "data": { "text/html": [ "
1 | \n", "Ordered | \n", "Pick Assigned | \n", "Picked | \n", "Review Assigned | \n", "Reviewed | \n", "Shipped | \n", "
---|---|---|---|---|---|---|
0 | \n", "7.229992 | \n", "7.229992 | \n", "11.739807 | \n", "11.739807 | \n", "15.226160 | \n", "25.083620 | \n", "
1 | \n", "15.197544 | \n", "15.197544 | \n", "18.556609 | \n", "18.556609 | \n", "19.667813 | \n", "29.122926 | \n", "
2 | \n", "25.125070 | \n", "25.125070 | \n", "27.794269 | \n", "27.794269 | \n", "31.321191 | \n", "41.420071 | \n", "
3 | \n", "31.641760 | \n", "31.641760 | \n", "35.951135 | \n", "35.951135 | \n", "37.746525 | \n", "49.240719 | \n", "
4 | \n", "55.818810 | \n", "55.818810 | \n", "76.585644 | \n", "76.585644 | \n", "78.323934 | \n", "88.024557 | \n", "
\n", " | Ordered | \n", "Pick Assigned | \n", "Picked | \n", "Review Assigned | \n", "Reviewed | \n", "Shipped | \n", "
---|---|---|---|---|---|---|
mean | \n", "0.0 | \n", "14.006331 | \n", "29.228007 | \n", "29.405602 | \n", "31.449083 | \n", "41.502859 | \n", "
std | \n", "0.0 | \n", "18.272545 | \n", "20.163448 | \n", "20.145527 | \n", "20.217277 | \n", "20.208458 | \n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Python [conda root]", "language": "python", "name": "conda-root-py" }, "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.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }