{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# PRO5765 Modelagem e Simulação de Sistemas de Produção\n", "## A06 - Laboratório de Simulação com SimPy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "SimPy is a ***process-based discrete-event simulation*** framework based on standard Python.\n", "- https://simpy.readthedocs.io/en/latest/\n", "\n", "Processes in SimPy are defined by Python ***generator functions*** and may, for example, be used to model active components like customers, vehicles or agents. SimPy also provides various types of ***shared resources*** to model limited capacity congestion points (like servers, checkout counters and tunnels)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### generator\n", "A function which returns a generator iterator. It looks like a normal function except that it contains ***yield expressions*** for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### generator iterator\n", "An object created by a generator function. Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### shared resources:\n", "\n", "- Resources – Resources that can be used by a limited number of processes at a time (e.g., a gas station with a limited number of fuel pumps).\n", "- Containers – Resources that model the production and consumption of a homogeneous, undifferentiated bulk. It may either be continuous (like water) or discrete (like apples).\n", "- Stores – Resources that allow the production and consumption of Python objects." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Corrida: lebre x tartaruga (ambiente e processos)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import simpy" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def corrida(env, corredor, tempo):\n", " print(env.now, corredor,'partiu')\n", " for i in range(5):\n", " yield env.timeout(tempo) # delay do processo\n", " print(env.now, corredor,'volta',i+1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "env=simpy.Environment()\n", "env.process(corrida(env,'lebre',5.0))\n", "env.process(corrida(env,'tartaruga',10.0))\n", "env.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Processos de Chegada" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Chegadas em intervalos regulares" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def chegada(env, ic, n):\n", " for i in range(n):\n", " yield env.timeout(ic)\n", " print('{:6.1f} chegada do cliente {:d}'.format(env.now, i))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "env=simpy.Environment()\n", "env.process(chegada(env,5.0,10)) # simular 10 chegadas, uma a cada 5 min\n", "env.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. Chegadas pré-determinadas" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "r=[1.0, 3.0, 6.0, 10.0, 12.5, 15.0, 18.0, 20.0] # instantes de chegada" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def chegada(env,r):\n", " n=len(r)\n", " for i in range(n): # n chegadas\n", " ic=r[i]-env.now\n", " yield env.timeout(ic)\n", " print('{:6.1f} chegada do cliente {:d}'.format(env.now, i))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "env=simpy.Environment()\n", "env.process(chegada(env,r))\n", "env.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. Chegadas Poisson, n chegadas" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy.random as rd" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def chegada(env, lb, n):\n", " for i in range(n):\n", " yield env.timeout(rd.exponential(1.0/lb)) # input é a média\n", " print('{:6.2f} chegada do cliente {:d}'.format(env.now, i))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "rd.seed(10) # deixar em branco para simulações diferentes\n", "env=simpy.Environment()\n", "env.process(chegada(env,1.0/5.0,10)) # uma chegada a cada 5.0 min\n", "env.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4. Chegadas Poisson, intervalo de 0 a t" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def chegada(env, taxa):\n", " k=0 # contador de chegadas\n", " while True:\n", " yield env.timeout(rd.exponential(1.0/taxa))\n", " print('{:6.2f} chegada do cliente {:d}'.format(env.now, k))\n", " k+=1" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "rd.seed() # deixar em branco para simulações diferentes\n", "env=simpy.Environment()\n", "env.process(chegada(env,1/5.0)) # taxa de chegadas (1 a cada 5 min)\n", "env.run(until=60) # simula por 60 minutos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. M/M/$\\infty $" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def chegada(env):\n", " k=0\n", " while True:\n", " yield env.timeout(rd.exponential(1.0/taxaChegada))\n", " nome = 'Cliente {}'.format(k)\n", " print('{:7.2f} chegada do {}'.format(env.now, nome))\n", " env.process(atend(env, nome)) # chama o processo atendimento\n", " k+=1" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def atend(env, nome):\n", " print('{:7.2f} início do {}'.format(env.now, nome))\n", " yield env.timeout(rd.exponential(1.0/taxaAtend))\n", " print('{:7.2f} fim do {}'.format(env.now, nome))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "taxaChegada = 1 / 5.0 # 1 chegada a cada 5 min\n", "taxaAtend = 1 / 15.0 # 1 atendimento a cada 15 min\n", "\n", "rd.seed() # Deixar em branco para simulações diferentes\n", "env=simpy.Environment()\n", "env.process(chegada(env))\n", "env.run(60) # Simula por 60 minutos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Intervalo de 10 min" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. M/M/c (recursos)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. Modelo básico" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def cheg(env,n):\n", " for i in range(n):\n", " yield env.timeout(rd.exponential(1.0/taxaCheg))\n", " cod = 'Job {}'.format(i)\n", " print('{:7.2f} chegada do {}'.format(env.now, cod))\n", " env.process(prod(env, cod))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def prod(env, cod): # recurso: request, timeout e release\n", " maqReq = centro.request()\n", " yield maqReq\n", " print('{:7.2f} início do {}'.format(env.now, cod))\n", " yield env.timeout(rd.exponential(1.0/taxaProd))\n", " centro.release(maqReq)\n", " print('{:7.2f} fim do {}'.format(env.now, cod))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "taxaCheg = 1 / 6.0 # 1 chegada a cada 6 min\n", "taxaProd = 1 / 15.0 # 1 processamento a cada 15 min\n", "numMaq = 3 # Número de máquinas no centro\n", "\n", "rd.seed(10)\n", "env = simpy.Environment()\n", "centro = simpy.Resource(env, capacity=numMaq)\n", "env.process(cheg(env,10))\n", "env.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. Classe (atribuitos do cliente)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Job:\n", " def __init__(self,n):\n", " self.num=n\n", " self.chg=0.0\n", " self.ini=0.0\n", " self.fim=0.0\n", " \n", "def cheg(env,n):\n", " for i in range(n):\n", " yield env.timeout(rd.exponential(1.0/taxaCheg))\n", " job=Job(i)\n", " jobs.append(job)\n", " job.chg=env.now\n", " env.process(prod(env, job))\n", "\n", "def prod(env, job):\n", " maqReq = centro.request()\n", " yield maqReq\n", " job.ini=env.now\n", " yield env.timeout(rd.exponential(1.0/taxaProd))\n", " centro.release(maqReq)\n", " job.fim=env.now\n", " #env.process(saida(env, job))\n", "\n", "#def saida(env, job):\n", "# yield env.timeout(0)\n", "# env.exit()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "taxaCheg = 1 / 6.0 # 1 chegada a cada 6 min\n", "taxaProd = 1 / 15.0 # 1 processamento a cada 15 min\n", "numMaq = 3 # Número de máquinas centro\n", "numJob = 10 # Número de jobs\n", "\n", "jobs=[]\n", "rd.seed()\n", "env = simpy.Environment()\n", "centro = simpy.Resource(env, capacity=numMaq)\n", "env.process(cheg(env,numJob))\n", "env.run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "jobs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for job in jobs:\n", " print('{:2d} {:6.2f} {:6.2f} {:6.2f}'.format(job.num, job.chg, job.ini, job.fim))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. Status dos recursos" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "estados=[(0,0),(1,1),(2,2),(3,1),(5,0),(6,0)] # estado: (tempo, número de clientes no sistema)\n", "\n", "t=[s[0] for s in estados] # tempos\n", "y=[s[1] for s in estados] # estados\n", "\n", "print('t =',t)\n", "print('y =',y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Gráfico *step*" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.step(t,y,where='post')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Coleta de dados de estado na código" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Job:\n", " def __init__(self,n):\n", " self.num=n\n", " self.chg=0.0\n", " self.ini=0.0\n", " self.fim=0.0\n", " \n", "def cheg(env,n):\n", " for i in range(n):\n", " yield env.timeout(rd.exponential(1.0/taxaCheg)) # tempo exponencial\n", " #yield env.timeout(1.0/taxaCheg) # tempo determinado\n", " job=Job(i+1) # cria um job com número i+1\n", " jobs.append(job)\n", " env.process(prod(env, job))\n", " k=estados[-1][1] # último estado\n", " estados.append((env.now,k+1)) # chegada, aumenta um\n", "\n", "def prod(env, job):\n", " job.chg=env.now\n", " maqReq = centro.request()\n", " yield maqReq\n", " job.ini=env.now\n", " yield env.timeout(rd.exponential(1.0/taxaProd))\n", " #yield env.timeout(1.0/taxaProd)\n", " centro.release(maqReq)\n", " job.fim=env.now\n", " k=estados[-1][1]\n", " estados.append((env.now,k-1)) # partida, diminui um" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "taxaCheg = 1 / 6.0 # 1 chegada a cada 6 min\n", "taxaProd = 1 / 15.0 # 1 processamento a cada 15 min\n", "numMaq = 3 # Número de máquinas centro\n", "numJob = 10 # Número de jobs\n", "\n", "jobs=[]\n", "estados=[(0,0)] # estado: (tempo, número de clientes no sistema)\n", "rd.seed()\n", "env = simpy.Environment()\n", "centro = simpy.Resource(env, capacity=numMaq)\n", "env.process(cheg(env,numJob))\n", "env.run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t=[s[0] for s in estados]\n", "y=[s[1] for s in estados]\n", "plt.step(t,y,where='post')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(estados)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5. Máquinas Paralelas, com tempos determinados (exercício 1 da lista 6)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Job:\n", " def __init__(self,cod,r,p,d):\n", " self.cod=cod\n", " self.r=r\n", " self.p=p\n", " self.d=d\n", " self.chg=0.0\n", " self.maq=0\n", " self.ini=0.0\n", " self.fim=0.0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def chegada(env):\n", " n=len(r)\n", " for i in range(n): # n chegadas\n", " ic=r[i]-env.now\n", " yield env.timeout(ic)\n", " job=Job(i,r[i],p[i],d[i]) # cria um job com código, ready time, processing time and due date dados\n", " jobs.append(job)\n", " job.chg=env.now\n", " print('{:7.2f} chegada do job {}'.format(env.now,job.cod))\n", " env.process(processo(env,job))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def processo(env,job):\n", " maqReq = centro.request()\n", " yield maqReq\n", " job.ini=env.now\n", " job.maq=centro.count\n", " print('{:7.2f} início do job {}'.format(env.now,job.cod))\n", " yield env.timeout(job.p)\n", " print('{:7.2f} fim do job {}'.format(env.now,job.cod))\n", " job.fim=env.now\n", " centro.release(maqReq)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n=8 # jobs\n", "m=3 # máquinas\n", "r=[0,0,0,0,0,5,5,5] # disponibilidade dos jobs - ready time\n", "p=[4,5,3,2,4,4,5,3] # tempos de processamento\n", "d=[5,5,5,5,5,10,10,10] # prazos para entrega - due date\n", "\n", "jobs=[]\n", "env=simpy.Environment()\n", "centro=simpy.Resource(env,capacity=m)\n", "env.process(chegada(env))\n", "env.run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for job in jobs:\n", " print(job.cod, job.r, job.p, job.d, job.chg, job.ini, job.fim)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "Cmax=max([job.fim for job in jobs])\n", "Fbar=sum([(job.fim-job.r) for job in jobs])/len(jobs)\n", "T=[max(job.fim-job.d,0) for job in jobs]\n", "TT=sum(T)\n", "Tmax=max(T)\n", "nT=sum([1 if t>0 else 0 for t in T])\n", "\n", "print('Cmax =',Cmax)\n", "print('Fbar =',Fbar)\n", "print('TT =',TT)\n", "print('Tmax =',Tmax)\n", "print('nT =',nT)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6 .Interface com Excel" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import xlwings as xw\n", "import pandas as pd\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://docs.xlwings.org/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Dados" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tab1=[['a1','a2','a3'],\n", " [41.6,39.9,39.8],\n", " [40.6,40.9,39.1],\n", " [39.7,38.6,38.7],\n", " [39.7,41.9,40.9],\n", " [41.0,41.7,41.7],\n", " [39.7,39.2,39.9]]\n", "tab1" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n=len(tab1)-1\n", "m=len(tab1[0])\n", "n,m" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tab2={'a1': [41.6, 40.6, 39.7, 39.7, 41.0, 39.7],\n", " 'a2': [39.9, 40.9, 38.6, 41.9, 41.7, 39.2],\n", " 'a3': [39.8, 39.1, 38.7, 40.9, 41.7, 39.9]}\n", "tab2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tab3=pd.DataFrame(tab2)\n", "tab3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Gravar" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wb1 = xw.Book()\n", "I=wb1.sheets.count\n", "plan=wb1.sheets.add('A06 - Planilha 1',after=I)\n", "plan.range('A10').value=tab1\n", "plan.range('A5').value=['m',m]\n", "plan.range('A6').value=['n',n]\n", "wb1.save('A06 - Pasta 1.xlsx')\n", "#wb1.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Ler" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wb1 = xw.Book('A06 - Pasta 1.xlsx')\n", "plan=wb1.sheets['A06 - Planilha 1']\n", "m=plan.range('B5').value\n", "n=plan.range('B6').value\n", "tab=plan.range('A11').expand('table').value\n", "print(m,n)\n", "print(tab)\n", "#wb1.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Próxima Aula: Simulação com o software AnyLogic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- https://www.anylogic.com/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- https://www.anylogic.com/downloads/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Instalar a versão PLE" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }