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 (m3m^3, por exemplo), com um estoque inicial de 50 unidades, por meio do seguinte código:

import simpy

env = simpy.Environment()
# cria um tanque de 100 m3 de capacidade, com 50 m3 no início da simulação
tanque = 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 m3m^3 (ou 100.000 litros) de combustível e que o tanque já contém 50 m3m^3 armazenado.

Criaremos uma função, enchimentoTanque, que enche o tanque com 50 m3m^3 sempre que um novo caminhão de reabastecimento de combustível chega ao posto:

import simpy
import random        

TANQUE_CAMINHAO = 50       # capacidade de abastecimento do caminhão

def enchimentoTanque(env, qtd, tanque):  
    # enche o tanque
    print("%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ção
tanque = 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 m3
0 Tanque enchido com 50.0 m3. Nível atual: 100.0 m3

Se você iniciar o tanque do posto a sua plena capacidade (100 m3m^3), 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 m3m^3.

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 simpy
import random        

TANQUE_CAMINHAO = 50        # capacidade de abastecimento do caminhão
TANQUE_VEICULO = 0.10       # capacidade do veículo
TEMPO_CHEGADAS = 5          # tempo entre chegadas sucessivas de veículos

def chegadasVeiculos(env, tanque):
    # gera chegadas de veículos por produto

def esvaziamentoTanque(env, qtd, tanque):
    # esvazia o tanque

def enchimentoTanque(env, qtd, tanque):  
    # enche o tanque
    print("%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ção
tanque = 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:

def chegadasVeiculos(env, tanque):
    # gera chegadas de veículos por produto
    while True:
        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:

def esvaziamentoTanque(env, qtd, tanque):
    # esvazia o tanque
    print("%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 simpy
import random        

TANQUE_CAMINHAO = 50        # capacidade de abastecimento do caminhão
TANQUE_VEICULO = 0.10       # capacidade do veículo
TEMPO_CHEGADAS = 5          # tempo entre chegadas sucessivas de veículos

def chegadasVeiculos(env, tanque):
    # gera chegadas de veículos por produto
    while True:
        yield env.timeout(TEMPO_CHEGADAS)
        # carrega veículo
        env.process(esvaziamentoTanque(env, TANQUE_VEICULO, tanque))

def esvaziamentoTanque(env, qtd, tanque):
    # esvazia o tanque
    print("%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))

def enchimentoTanque(env, qtd, tanque):  
    # enche o tanque
    print("%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ção
tanque = 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 m3
5 Veículo atendido de 0.10 m3.   Nível atual:  49.9 m3
10 Novo veículo de 0.10 m3.      Nível atual:  49.9 m3
10 Veículo atendido de 0.10.     Nível atual:  49.8 m3
15 Novo veículo de 0.10 m3.      Nível atual:  49.8 m3
15 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 m3m^3. 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 tanque
TEMPO_CONTROLE = 1          # tempo entre verificações do nível do tanque

def sensorTanque(env, tanque):
    # quando o tanque baixar se certo nível, dispara o enchimento
    while True:
        if tanque.level <= NIVEL_MINIMO:
            # dispara pedido de enchimento
            yield env.process(enchimentoTanque(env, TANQUE_CAMINHAO, tanque))
        # aguarda um tempo para fazer a nova chegagem do nível do tanque
        yield 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 simpy
import random        

TANQUE_CAMINHAO = 50        # capacidade de abastecimento do caminhão
TANQUE_VEICULO = 0.10       # capacidade do veículo
TEMPO_CHEGADAS = 5          # tempo entre chegadas sucessivas de veículos
NIVEL_MINIMO = 50           # nível mínimo de reabastecimento do tanque
TEMPO_CONTROLE = 1          # tempo entre verificações do nível do tanque

def sensorTanque(env, tanque):
    # quando o tanque baixar se certo nível, dispara o enchimento
    while True:
        if tanque.level <= NIVEL_MINIMO:
            # dispara pedido de enchimento
            yield env.process(enchimentoTanque(env, TANQUE_CAMINHAO, tanque))
        # aguarda um tempo para fazer a nova chegagem do nível do tanque
        yield env.timeout(TEMPO_CONTROLE)

def chegadasVeiculos(env, tanque):
    # gera chegadas de veículos por produto
    while True:
        yield env.timeout(TEMPO_CHEGADAS)
        # carrega veículo
        env.process(esvaziamentoTanque(env, TANQUE_VEICULO, tanque))

def esvaziamentoTanque(env, qtd, tanque):
    # esvazia o tanque
    print("%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))

def enchimentoTanque(env, qtd, tanque):  
    # enche o tanque
    print("%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ção
tanque = 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 m3
0 Tanque enchido com 50.0 m3.    Nível atual: 100.0 m3
5 Novo veículo de 0.10 m3.       Nível atual: 100.0 m3
5 Veículo atendido de 0.10 m3.   Nível atual:  99.9 m3
10 Novo veículo de 0.10 m3.      Nível atual:  99.9 m3
10 Veículo atendido de 0.10 m3.  Nível atual:  99.8 m3
15 Novo veículo de 0.10 m3.      Nível atual:  99.8 m3
15 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).

Conceitos desta seção

Conteúdo

Descrição

meuContainer = simpy.Container(env, capacity=capacity, init=init

cria um container meuContainercom 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.

Last updated