Why isn't my Simpy resource keeping a queue?

364 views Asked by At

I've been working for around a week to learn SimPy for a discrete simulation I have to run. I've done my best, but I'm just not experienced enough to figure it out quickly. I am dying. Please help.

The system in question goes like this:

order arrives -> resource_1 (there are 2) performs take_order -> order broken into items -> resource_2 (there are 10) performs process_item

My code runs and performs the simulation, but I'm having a lot of trouble getting the queues on the resources to function. As in, queues do not build up on either resource when I run it, and I cannot find the reason why. I try resource.get_queue and get empty lists. There should absolutely be queues, as the orders arrive faster than they can be processed.

I think it has something to do with the logic for requesting resources, but I can't figure it out. Here's how I've structured the code:

import simpy
import random 
import numpy as np 



total_items = []


total_a = []
total_b = []
total_c = []

order_Q = [] 
item_Q = []
skipped_visits = []

order_time_dict = {}
order_time_dict2 = {}
total_order_time_dict = {}
var = []

class System:
    def __init__(self,env,num_resource_1,num_resource_2):
        self.env = env
        self.resource_1 = simpy.Resource(env,num_resource_1)
        self.resource_2 = simpy.Resource(env,num_resource_2)

    def take_order(self, order): 
        self.time_to_order = random.triangular(30/60,60/60,120/60)
        arrive = self.env.now
        yield self.env.timeout(self.time_to_order)

    def process_item(self,item): 
        total_process_time = 0
        current = env.now
        order_num = item[1][0]

        for i in range(1,item[1][1]): 
            if 'a' in item[0]: 
                total_process_time +=  random.triangular(.05,7/60,1/6) #bagging time only
                #here edit order time w x
            if 'b' in item[0]: 
                total_process_time += random.triangular(.05,.3333,.75)

            if 'c' in item[0]: 
                total_process_time +=  random.triangular(.05,7/60,1/6)

            #the following is handling time: getting to station, waiting on car to arrive at window after finished, handing to cust
            total_process_time += random.triangular(.05, 10/60, 15/60)

            item_finish_time = current + total_process_time
            if order_num in order_time_dict2.keys(): 
                start = order_time_dict2[order_num][0]
                if order_time_dict2[order_num][1] < item_finish_time:
                    order_time_dict2[order_num] = (start, item_finish_time) 
            else: 
                order_time_dict2[order_num] = (current, item_finish_time)

        yield self.env.timeout(total_process_time)


class Order: 
    def __init__(self, order_dict,order_num): 
        self.order_dict = order_dict
        self.order_num = order_num
        self.order_stripped =  {}
        for x,y in list(self.order_dict.items()): 
            if x != 'total':
                if y != 0: 
                    self.order_stripped[x] = (order_num,y) #this gives dictionary format {item: (order number, number items) } but only including items in order
        
        self.order_list = list(self.order_stripped.items()) 

def generate_order(num_orders): 
    print('running generate_order')

    a_demand = .1914 ** 3
    a_stdev = 43.684104

    b_demand = .1153
    b_stdev =  28.507782

    c_demand = .0664
    c_stdev = 15.5562624349

    num_a = abs(round(np.random.normal(a_demand)))
    num_b = abs(round(np.random.normal(b_demand)))
    num_c = abs(round(np.random.normal(c_demand)))
    total = num_orders 
    
    total_a.append(num_a)
    total_b.append(num_b)
    total_c.append(num_c)
    total_num_items = num_a + num_b + num_c
    total_items.append(total_num_items)
    

    order_dict = {'num_a':num_a, 'num_b':num_b,'num_c':num_c, 'total': total}
     
    return order_dict

def order_process(order_instance,system): 
    enter_system_at = system.env.now
    print("order " + str(order_instance.order_num) + " arrives at " + str(enter_system_at))
    if len(system.resource_1.get_queue) > 1: 
        print("WORKING HERE ******************")
    if len(system.resource_1.get_queue) <= 25: 
        with system.resource_1.request() as req: 
            order_Q.append(order_instance)
            yield req
            yield env.process(system.take_order(order_instance))
            order_Q.pop()
        enter_workstation_at = system.env.now
        print("order num " + str(order_instance.order_num) + " enters workstation at " + str(enter_workstation_at))


        for item in order_instance.order_list: 
            item_Q.append(item)
            with system.resource_2.request() as req: 
                yield req
                yield env.process(system.process_item(item))
            if len(system.resource_2.get_queue) >1: 
                var.append(1)

            item_Q.pop()
        leave_workstation_at = system.env.now
        print("Order num " + str(order_instance.order_num) + " leaves at " + str(leave_workstation_at))

        order_time_dict[order_instance.order_num] = leave_workstation_at-enter_workstation_at
        total_order_time_dict[order_instance.order_num]=leave_workstation_at-enter_system_at
    else: 
        skipped_visits.append(1)


def setup(env):
    system = System(env,2,15)
    order_num = 0
    while True: 
        next_order = random.expovariate(3.5) #where 20 is order arrival mean (lambda)
        yield env.timeout(next_order)
        order_num+=1
        env.process(order_process(Order(generate_order(order_num),order_num),system))

env = simpy.Environment()
env.process(setup(env))
env.run(until=15*60)
print("1: \n", order_time_dict)
1

There are 1 answers

3
Michael On BEST ANSWER

I think you are looking at the wrong queue.

the api for getting queued requests for resources is just attribute queue so try using len(system.resource_1.queue)

get_queue and put_queue is from the base class and used to derive new resource classes.

but wait they are not what any reasonable person would assume, and I find this confusing too, but the doc says Requesting a resources is modeled as “putting a process’ token into the resources” which means when you call request() the process is put into the put_queue, not the get_queue. And with resource, release always succeeds immediately so its queue (which is the get_queue) is always empty

I think queue is just a alias for the put_queue, but queue is much less confussing