Hello all and happy holidays.
I want to simulate this order fulfilment system on simpy. let me first describe the systems. There is n number of order and each of a size mi where i =1,2,..,n. All the items for the n orders are available and ready at the beginning of the simulation, so there is no waiting on them. There is a number pf loading resources (loading people) who start scanning and sending the items for packaging. The process the items one at the time. Once they process an item and send it for packaging, the item is added to this cart that associated with the order of that item. Note, we have the exact number of carts as orders and that resource has no capacity issue and can handle any amount of items in an order. Once the number of items in a cart reaches a specific threshold (let's say 10% of the items of an orders have been added to that order cart already) once that threshold reached, the packaging people start packaging there orders one item at the time. So the packaging people can work on multiple packages but at each time the handle one item and add it to the package of its order.
Now let's look at my code that I tried to follow the examples of this video
from itertools import repeat
import simpy
import random
import itertools
num_order = 2
size_order = [10, 8]
items = []
for i in range(num_order):
ob = list(zip(repeat(i+1), (range(1,size_order[i]+1))))
items.append(ob)
items = list(itertools.chain(*items))
What I'm doing here is creating a list of tuples of the items in each order. Here we have 2 orders of size 10 and 8. The list "items" at the end would have these tuples (1,1), (1,2), (1,3), ...,(2,7), (2,8) i.e (order number, item number)
Now we define a function that generates those items
def gen_items(env,mean_load,mean_pack,items,load_op,pack_op,cart,size_order):
# Choose a random item from the list of all items,
#give it an id of it's tuple name, and remove that item from the list
if items:
item_id = random.choice(items)
items.remove(item_id)
while True:
w = gen_activity(env,mean_load,mean_pack,item_id,load_op,pack_op,cart,size_order)
env.process(w)
yield env.timeout(0) #items are ready and don't take time to be available for the operator
Here we just pick a random item from the list of items only if there are items in the list. Then we simply call the function that does all the activities. It takes the same arguments as the item generation function except that one item id instead of the entier item list. load_op,pack_op,cart are resources defined later.
def gen_activity(env,mean_load,mean_pack,item_id,load_op,pack_op,cart,size_order):
time_enter_load_op_queue = env.now #time item is ready for loading
with load_op.request() as req:
yield req # we request an operator
time_queue_load_op = env.now
time_in_load_op_queue = time_queue_load_op - time_enter_load_op_queue
print("item" +str(item_id[1])+ " of order" + str(item_id[0])+" entered load at" + str(time_enter_load_op_queue)+ " and spent"+ str(time_in_load_op_queue))
# print the time the item spent in the queue waiting for loading operator
load_time = random.expovariate(1/mean_load)
yield env.timeout(load_time)
# once the item is processed, it then is loaded to the cart of that order in 45 time untis
yield cart[item_id[0]-1].put(1)
yield env.timeout(45)
time_enter_pack_op_queue = env.now #now the item at the packaging location
with pack_op.request() as req:
if cart[item_id[0]-1].level >= 0.1*(size_order[item_id[0]-1]):
yield req # request a packaging operator to start packaging only if there is 10% of the order in the cart
time_queue_pack_op = env.now
time_in_pack_op_queue = time_queue_pack_op - time_enter_pack_op_queue
print("item" +str(item_id[1])+ " of order" + str(item_id[0])+" entered pack at" + str(time_enter_pack_op_queue)+ " and spent"+ str(time_in_pack_op_queue))
pack_time = random.expovariate(1/mean_pack)
yield env.timeout(pack_time) # time to pack
As the example in the video, loading operator is called to scan that item we selected at random from the item list. Then the item is added to the cart of that order and it takes 45 time units to reach the packaging station. Then we check if the number of item in an order cart is above that threshold of 10% or not. If so then the packaging operator start working that order. Note that, we need to check that threshold once. If there are more than 10% of the order in the cart, we start packaging even if we we have packed all the available items and we now wait for the rest of the order to come, once even one item comes in we added to the order. So we only check this threshold once to start working that order.
Finally we create the resources and run the model
env = simpy.Environment()
load_op = simpy.Resource(env,capacity=10)
pack_op = simpy.Resource(env,capacity=2)
cart = []
for i in range(len(size_order)):
c = simpy.Container(env,capacity=size_order[i],init=0)
cart.append(c)
mean_load,mean_pack = 3,2
env.process(gen_items(env,mean_load,std_load,mean_pack,std_pack,items,load_op,pack_op,chute,size_order))
env.run(until=1000)
So the loading and packaging operators are straightforward. We then create as many container carts as the orders we have and put them on a list. That why you see me calling the cart above by the index.
For example in this run, the model chose item 2 of order 2 at the beginning. This what the model printed
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
item2 of order2 entered load at0 and spent0
which it does every time I run it. It chooses an item and print this everytiem and print it 10 times and then it freezes. I would appreciated it if any of you can help me here. I tried to put all the details here, but let me know if you need any more clarification. Happy holidays and thank you
It looks like procedure gen_items() picks one item, and then enters a infinite loop where it tries to pack that same item over and over. Since there is a limit to the cart capacity, eventually you hit a cart.put that is over that capacity, and everything freezes because that cart.put is blocking the release of the loader.
Try this
def gen_items(env,mean_load,mean_pack,items,load_op,pack_op,cart,size_order):
# Choose a random item from the list of all items,
#give it an id of it's tuple name, and remove that item from the list
while items:
item_id = random.choice(items)
items.remove(item_id)
#while True:
w = gen_activity(env,mean_load,mean_pack,item_id,load_op,pack_op,cart,size_order)
env.process(w)
yield env.timeout(0) #items are ready and don't take time to be available for the operator