I asked a different question about this simpy simulation model before, and Michael answered it and helped me tremendously. Now I have a new issue with this model, and I hope you guys can help me out. You can find that question here
Let me first describe the model with some new modifications I have made since the last question.
I have orders to process and fulfill. These orders are in a dataframe. Each row of the dataframe is an item of those orders. The columns of the dataframe are "order_id, item_id, box_id, scanned, order_quantity".
each order consists of multiple items. The items are located in different boxes. When processing the orders, all the boxes that have all the items for all the orders are ready. There are some operators that scan the box first, then scan each item in that box after that. So, each box is scanned only once. After the items are scanned, they are put on a conveyor belt. At the end of the conveyor there are there are carts. For now, these are as many carts as orders. Hence, the items of each order go to the cart designated for that order. When some percentage of the total number of items of an order are already in the order cart- say 90% of the total number of items, some operators start processing the items and remove them from the carts until all the items are processed and emptied. For instance, if an order has 10 items, once there are 5 of these items in that order's cart, the operators start emptying that cart as the other five items are on the way to the cart. They keep emptying until all 10 items are processed.
Now let's look at a sample dataframe and orders.
Here we have 18 items that belong to 4 orders and these items are located in 3 boxes. The column scanned indicate if the box has been scanned or not.
For the simulation, I iterate over the rows(items) one by one, and check if the box of that order has been scanned or not, then scan it and scan the item after that. I have a simpy container for the conveyor belt that all items are added to. Finally, the items go to the carts that they belong to and the carts are simpy containers as well.
env = simpy.Environment()
scan_op = simpy.Resource(env, capacity=5) # operators who scanned boxes and items
cart_op = simpy.Resource(env, capacity=3) # operators who process items and empty the carts
belt = simpy.boxainer(env, init=0) # conveyor belt
carts = {}
for order in order_list: # order_list is a list of tuples that have (order_id, order quantity)
carts[order[0]]= simpy.boxainer(env, capacity=order[1], init=0)
Now for how the system is models
def activity(df,env,scan_op,cart_op,belt,carts,\
mean_box, std_box,mean_item, std_item,\
mean_cart, std_cart,\
belt_dist,belt_speed, per):
for i in range(len(df)): # df is the dataframe described above
# Here we check if the box has been scanned or not. If not, then it is scanned and all the
rows that have that box in the box_id column are changed to True
if not df.loc[i,'Scanned']:
with scan_op.request() as req:
yield req
box_scan_time = abs(random.normalvariate(mean_box, std_box))
yield env.timeout(box_scan_time)
df.loc[df['box_id'] == df.loc[i,'box_id'], 'Scanned'] = True
# Now the item is scanned
with scan_op.request() as req:
yield req
item_scan_time =abs(random.normalvariate(mean_item, std_item))
yield env.timeout(item_scan_time)
# Now we add the item to the conveyor belt and remove it after some time
yield belt.put(1)
yield env.timeout(belt_dist/belt_speed)
yield belt.get(1)
# Now we add the item to the cart of the order the item belongs to
yield carts[df.loc[i,'order_id']].put(1)
print(f"item {df.loc[i,'item_id']} of order {df.loc[i,'order_id']} has entered the cart "
with cart_op.request() as req:
# Check of the desired fraction of the items are in the cart. let's use per = 0.9
if carts[df.loc[i,'order_id']].level >= df.loc[i,'quantity']*per:
yield req
cart_time =abs(random.normalvariate(mean_cart, std_cart))
yield env.timeout(cart_time)
print(f"item {df.loc[i,'items_id']} of {df.loc[i,'order_id']} has left the cart")
mean_item, std_item, mean_cart, std_cart, mean_box, std_box = 3, 1.5, 2, 1, 10, 2
per, belt_speed,belt_dist = 0.9, 5,10
env.process(activity(df,env,scan_op,cart_op,belt,carts,\
mean_box, std_box,mean_item, std_item,\
mean_cart, std_cart,\
belt_dist,belt_speed, per))
env.run()
As you can see I don't remove the items from the cart to cart using get() because if 90% of the items are in the cart then the operators start processing, and if I remove items that condition might hold next time. So once that percentage is reached, they start emptying as more item arrive. I guess I could use get() and remove them, but use different condition- like a bool variable for each cart, that turns True when 90% of the items reach the cart but honestly I tried to do that with now success. So my first question is how can I do that?
Now, the most important question is how to let the model run until all items are cleared from that cart. When I run this model with the provided data, all 18 items reach their correspondent carts, but only 4 of them get removed.
here is the output I get and it seems like only the item that arrives and meet the percentage condition gets removed and the process doesn't go further
item 76464851 of order 34 has entered the cart
item 76464842 of order 34 has entered the cart
item 76464841 of order 34 has entered the cart
item 76464841 of 34 has left the cart
item 81333558 of order 133 has entered the cart
item 81333557 of order 133 has entered the cart
item 81333556 of order 133 has entered the cart
item 81333556 of 133 has left the cart
item 81333554 of order 956 has entered the cart
item 81333553 of order 956 has entered the cart
item 81333552 of order 956 has entered the cart
item 81333551 of order 956 has entered the cart
item 77026821 of order 956 has entered the cart
item 77026822 of order 956 has entered the cart
item 77026823 of order 956 has entered the cart
item 77026823 of 956 has left the cart
item 77026824 of order 420 has entered the cart
item 77026825 of order 420 has entered the cart
item 77026826 of order 420 has entered the cart
item 77026828 of order 420 has entered the cart
item 77026829 of order 420 has entered the cart
item 77026829 of 420 has left the cart
One thing I tried is making that as a process as follows. This code goes after the items are added to the cart with put()
print(f"item {df.loc[i,'item_id']} of order {df.loc[i,'order_id']} has entered the cart ")
if carts[df.loc[i,'order_id']].level >= df.loc[i,'quantity']*per:
p = emptying(env,df,i,mean_cart, std_cart)
env.process(p)
def emptying(env,df, i, mean_cart, std_cart):
while True:
with cart_op.request() as req:
yield req
cart_time =abs(random.normalvariate(mean_cart, std_cart))
yield env.timeout(cart_time)
print(f"item {df.loc[i,'items_id']} of {df.loc[i,'order_id']} has left the cart")
This process still result in the same output as the original script.
Your help is hightly appreciated as I'm struggling with this simulation and I can't think of any new ideas to fix it after having been trying for the past week