Enchendo ou esvaziando tanques, caixas ou estoques
Um tipo especial de recurso no SimPy é o container. Intuitivamente, um container seria um taque ou caixa em que se armazenam coisas. Você pode encher ou esvaziar em quantidade, como se fosse um tanque de água ou uma caixa de laranjas.
A sua utilização é bastante simples, por exemplo, podemos modelar um tanque de 100 unidades de capacidade (m3, por exemplo), com um estoque inicial de 50 unidades, por meio do seguinte código:
import simpyenv = simpy.Environment()# cria um tanque de 100 m3 de capacidade, com 50 m3 no início da simulaçãotanque = simpy.Container(env, capacity=100, init=50)
O containerpossui três comandos importantes:
Para encher: tanque.put(quantidade)
Para esvaziar: tanque.get(quantidade)
Para obter o nível atual: tanque.level
Enchendo o meu container yield meuContainer.put(quantidade)
Considere que um posto de gasolina possui um tanque com capacidade de 100 m3 (ou 100.000 litros) de combustível e que o tanque já contém 50 m3 armazenado.
Criaremos uma função, enchimentoTanque, que enche o tanque com 50 m3 sempre que um novo caminhão de reabastecimento de combustível chega ao posto:
import simpyimport random TANQUE_CAMINHAO =50# capacidade de abastecimento do caminhãodefenchimentoTanque(env,qtd,tanque): # enche o tanqueprint("%d Novo caminhão com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))yield tanque.put(qtd)print("%d Tanque enchido com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))random.seed(150)env = simpy.Environment()#cria um tanque de 100 m3, com 50 m3 no início da simulaçãotanque = simpy.Container(env, capacity=100, init=50)env.process(enchimentoTanque(env, TANQUE_CAMINHAO, tanque))env.run(until =500)
A saída do programa é bastante simples, afinal o processo de enchimento do tanque é executado apenas uma vez:
0 Novo caminhão de combustível com 50.0 m3. Nível atual:50.0 m30 Tanque enchido com 50.0 m3. Nível atual:100.0 m3
Se você iniciar o tanque do posto a sua plena capacidade (100 m3), o caminhão tentará abastecer, mas não conseguirá por falta de espaço, virtualmente aguardando espaço no tanque na linha:
yield tanque.put(qtd)
Dentro da função enchimentoTanque.
Esvaziando o meu container: yield meuContainer.get(quantidade)
Considere que o posto atende automóveis que chegam em intervalos constantes de 5 minutos entre si e que cada veículo abastece 100 litros ou 0,10 m3.
Partindo do modelo anterior, vamos criar duas funções: uma para gerar os veículos e outra para transferir o combustível do tanque para o veículo.
Uma possível máscara para o modelo seria:
import simpyimport random TANQUE_CAMINHAO =50# capacidade de abastecimento do caminhãoTANQUE_VEICULO =0.10# capacidade do veículoTEMPO_CHEGADAS =5# tempo entre chegadas sucessivas de veículosdefchegadasVeiculos(env,tanque):# gera chegadas de veículos por produtodefesvaziamentoTanque(env,qtd,tanque):# esvazia o tanquedefenchimentoTanque(env,qtd,tanque): # enche o tanqueprint("%d Novo caminhão com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))yield tanque.put(qtd)print("%d Tanque enchido com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))random.seed(150)env = simpy.Environment()# cria um tanque de 100 m3, com 50 m3 no início da simulaçãotanque = simpy.Container(env, capacity=100, init=50)env.process(chegadasVeiculos(env, tanque))env.run(until =20)
A função chegadasVeiculosgera os veículos que buscam abastecimento no posto e que, a seguir, chamam a função esvaziamentoTanque responsável por provocar o esvaziamento do tanque do posto na quantidade desejada pelo veículo:
defchegadasVeiculos(env,tanque):# gera chegadas de veículos por produtowhileTrue:yield env.timeout(TEMPO_CHEGADAS)# carrega veículo env.process(esvaziamentoTanque(env, TANQUE_VEICULO, tanque))
A função que representa o processo de esvaziamento do tanque é semelhante a de enchimento da seção anterior, a menos da opção get(qtd), que retira a quantidade qtd do container tanque:
defesvaziamentoTanque(env,qtd,tanque):# esvazia o tanqueprint("%d Novo veículo de %3.2f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))yield tanque.get(qtd)print("%d Veículo atendido de %3.2f.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))
O modelo de simulação completo do posto de gasolina fica:
import simpyimport random TANQUE_CAMINHAO =50# capacidade de abastecimento do caminhãoTANQUE_VEICULO =0.10# capacidade do veículoTEMPO_CHEGADAS =5# tempo entre chegadas sucessivas de veículosdefchegadasVeiculos(env,tanque):# gera chegadas de veículos por produtowhileTrue:yield env.timeout(TEMPO_CHEGADAS)# carrega veículo env.process(esvaziamentoTanque(env, TANQUE_VEICULO, tanque))defesvaziamentoTanque(env,qtd,tanque):# esvazia o tanqueprint("%d Novo veículo de %3.2f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))yield tanque.get(qtd)print("%d Veículo atendido de %3.2f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))defenchimentoTanque(env,qtd,tanque): # enche o tanqueprint("%d Novo caminhão com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))yield tanque.put(qtd)print("%d Tanque enchido com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))random.seed(150)env = simpy.Environment()#cria um tanque de 100 m3, com 50 m3 no início da simulaçãotanque = simpy.Container(env, capacity=100, init=50)env.process(chegadasVeiculos(env, tanque))env.run(until =200)
Quando por 200 minutos, o modelo anterior fornece como saída:
5 Novo veículo de 0.10 m3. Nível atual:50.0 m35 Veículo atendido de 0.10 m3. Nível atual:49.9 m310 Novo veículo de 0.10 m3. Nível atual:49.9 m310 Veículo atendido de 0.10. Nível atual:49.8 m315 Novo veículo de 0.10 m3. Nível atual:49.8 m315 Veículo atendido de 0.10 m3. Nível atual:49.7 m3
Criando um sensor para o nível atual do container
Ainda no exemplo do posto, vamos chamar um caminhão de reabastecimento sempre que o tanque atingir o nível de 50 m3. Para isso, criaremos uma função sensorTanquecapaz de reconhecer o instante exato em que o nível do tanque abaixou do valor desejado e, portanto, deve ser enviado um caminhão de reabastecimento.
Inicialmente, para identificar se o nível do tanque abaixou além no nível mínimo, precisamos verificar qual o nível atual. Contudo, esse processo de verificação não é contínuo no tempo e deve ter o seu intervalo entre verificações pré-definido no modelo.
Assim, são necessários dois parâmetros: um para o nível mínimo e outro para o intervalo entre verificações do nível do tanque. Uma possível codificação para a função sensorTanqueseria:
NIVEL_MINIMO =50# nível mínimo de reabastecimento do tanqueTEMPO_CONTROLE =1# tempo entre verificações do nível do tanquedefsensorTanque(env,tanque):# quando o tanque baixar se certo nível, dispara o enchimentowhileTrue:if tanque.level <= NIVEL_MINIMO:# dispara pedido de enchimentoyield env.process(enchimentoTanque(env, TANQUE_CAMINHAO, tanque))# aguarda um tempo para fazer a nova chegagem do nível do tanqueyield env.timeout(TEMPO_CONTROLE)
A função sensorTanqueé um laço infinito (while True) que a cada 1 minuto (configurável na constante TEMPO_CONTROLE) verifica se o nível atual do tanque está abaixo ou igual ao nível mínimo (configurável na constante `NIVEL_MINIMO).
O modelo completo com a implentação do sensor fica:
import simpyimport random TANQUE_CAMINHAO =50# capacidade de abastecimento do caminhãoTANQUE_VEICULO =0.10# capacidade do veículoTEMPO_CHEGADAS =5# tempo entre chegadas sucessivas de veículosNIVEL_MINIMO =50# nível mínimo de reabastecimento do tanqueTEMPO_CONTROLE =1# tempo entre verificações do nível do tanquedefsensorTanque(env,tanque):# quando o tanque baixar se certo nível, dispara o enchimentowhileTrue:if tanque.level <= NIVEL_MINIMO:# dispara pedido de enchimentoyield env.process(enchimentoTanque(env, TANQUE_CAMINHAO, tanque))# aguarda um tempo para fazer a nova chegagem do nível do tanqueyield env.timeout(TEMPO_CONTROLE)defchegadasVeiculos(env,tanque):# gera chegadas de veículos por produtowhileTrue:yield env.timeout(TEMPO_CHEGADAS)# carrega veículo env.process(esvaziamentoTanque(env, TANQUE_VEICULO, tanque))defesvaziamentoTanque(env,qtd,tanque):# esvazia o tanqueprint("%d Novo veículo de %3.2f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))yield tanque.get(qtd)print("%d Veículo atendido de %3.2f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))defenchimentoTanque(env,qtd,tanque): # enche o tanqueprint("%d Novo caminhão com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))yield tanque.put(qtd)print("%d Tanque enchido com %4.1f m3.\t Nível atual: %5.1f m3"% (env.now, qtd, tanque.level))random.seed(150)env = simpy.Environment()#cria um tanque de 100 m3, com 50 m3 no início da simulaçãotanque = simpy.Container(env, capacity=100, init=50)env.process(chegadasVeiculos(env, tanque))env.process(sensorTanque(env, tanque))env.run(until =20)
Note a criação do processo do sensorTanque na penúltima linha do programa:
env.process(sensorTanque(env, tanque))
Este processo garante que o sensor estará operante ao longo de toda a simulação. Quando executado, o programa anterior retorna:
0 Novo caminhão com 50.0 m3. Nível atual:50.0 m30 Tanque enchido com 50.0 m3. Nível atual:100.0 m35 Novo veículo de 0.10 m3. Nível atual:100.0 m35 Veículo atendido de 0.10 m3. Nível atual:99.9 m310 Novo veículo de 0.10 m3. Nível atual:99.9 m310 Veículo atendido de 0.10 m3. Nível atual:99.8 m315 Novo veículo de 0.10 m3. Nível atual:99.8 m315 Veículo atendido de 0.10 m3. Nível atual:99.7 m3
Observação 1: Note que o enchimento ou esvaziamento dos tanques é instantâneo, isto é: não existe nenhuma taxa de enchimento ou esvaziamento associada aos processos. Cabe ao programador modelar situações em que a taxa de transferência é relevante (veja o Desafio 17, a seguir).
Observação 2: O tanque pode ser esvaziado ou enchido simultaneamente. Novamente cabe ao programador modelar a situação em que isto não se verifica (veja o Desafio 18, a seguir).
cria um containermeuContainercom capacidade capacitye quantidade inicial de init
yield meuContainer.put(quantidade)
adiciona uma dada quantidadeao meuContainer, se houver espaço suficiente, caso contrário aguarda até que o espaço esteja disponível
yield meuContainer.get(quantidade)
retira uma dada quantidadeao meuContainer, se houver quantidade suficiente, caso contrário aguarda até que a quantidade esteja disponível
meuContainer.level
retorna a quantidade disponível atualmente em meuContainer
Desafios
Desafio 17: considere, no exemplo do posto, que a taxa de enchimento do tanque é de 1 litro/min e a de esvaziamento é de 2 litros/min. Altere o modelo para que ele incorpore os tempos de enchimento e esvaziamento, bem como forneça o tempo que o veículo aguardou na fila por atendimento.
Desafio 18: continuando o exemplo, modifique o modelo de modo que ele represente a situação em que o tanque não pode ser enchido e esvaziado simultaneamente.