Search code examples
eventsqueuesimulationsimpydiscrete

Simpy Discrete Event Simulation: Customer/entity request never fulfilled in system with server blocking blocking when queue of next server is full


I have been struggling with coding this Discrete Event Simulation for a couple of days now. I got some answers of of this website, but still cannot run my simulation properly.

The problem that arises is: The first customer (thus every customer) does get through the queue of the first service, but can never actually enter the server for some reason. I hope some experts are able to pinpoint the problem quickly, as it is probably just a stupid mistake. The customer does go through the zero'th server and the queue for server 1.

I added #### just before and after the line where the mistake is.

Thanks in advance.

class Entity(object):
    pass

def process0(env, entity, process_0_res, process_1_q):
    print(f' {env.now} customer {entity.id} is in system')
    with process_0_res.request() as res_req:
        yield res_req
        print(f'{env.now} customer {entity.id} is in queue 1')
        yield process_1_q.put(entity)

def process1_broker(env, process_1_q, process_1_res):
    while True:
        # is resource available?
        res_req = process_1_res.request()
        yield res_req
        # is customer available?
        entity = yield process_1_q.get()
        # save resource request to release later
        entity.res_req = res_req
        # start process
        env.process(process1(env,entity,process_1_res, process_2_q))

def process1(env, entity, process_1_res, process_2_q):
    with process_1_res.request() as res_req:
        print(f'{env.now} customer {entity.id} should now request server 1')
        #########
        yield res_req
        #########
        print(f' {env.now} customer {entity.id} in process 1')
        yield env.timeout(2)
        yield process_2_q.put(entity)

def process2_broker(env, process_2_q, process_2_res):
    while True:
        res_req = process_2_res.request()
        yield res_req
        entity = yield process_2_q.get()
        entity.res_req = res_req
        env.process(process2(env,entity,process_2_res, process_3_q))

def process2(env, entity, process_2_res, process_3_q):
    print(f' {env.now} customer {entity.id} in process 2')
    with process_2_res.request() as res_req:
        yield res_req
        yield env.timeout(np.random.exponential(mu[1]))
        yield process_3_q.put(entity)
        
def process3_broker(env, process_3_q, process_3_res):
    while True:
        res_req = process_3_res.request()
        yield res_req
        entity = yield process_3_q.get()
        entity.res_req = res_req
        env.process(process3(env,entity,process_3_res, process_4_q))

def process3(env, entity, process_3_res, process_4_q):
    print(f' {env.now} customer {entity.id} in process 3')
    with process_3_res.request() as res_req:
        yield res_req
        yield env.timeout(np.random.exponential(mu[2]))
        yield process_4_q.put(entity)

def process4_broker(env, process_4_q, process_4_res):
    while True:
        res_req = process_4_res.request()
        yield res_req
        entity = yield process_3_q.get()
        entity.res_req = res_req
        env.process(process4(env,entity,process_4_res))

def process4(env, entity, process_4_res):
    print(f' {env.now} customer {entity.id} in process 4')
    with process_4_res.request() as res_req:
        yield res_req
        yield env.timeout(np.random.exponential(mu[3]))
        yield process_4_res.release(entity.res_req)
        print(f' {env.now} customer {entity.id} leaves system')
        
def gen_entities(env, process_0_res, process_1_q):
    next_id = 1
    while True:
        yield env.timeout(np.random.exponential(labda))
        entity = Entity()
        entity.id = next_id
        next_id += 1
        env.process(process0(env, entity, process_0_res, process_1_q))

env = simpy.Environment()
process_0_res = simpy.Resource(env, capacity = 1)
process_1_res = simpy.Resource(env, capacity = 1)
process_2_res = simpy.Resource(env, capacity = 1)
process_3_res = simpy.Resource(env, capacity = 1)
process_4_res = simpy.Resource(env, capacity = 1)

process_1_q = simpy.Store(env, capacity = 5)
process_2_q = simpy.Store(env, capacity = 4)
process_3_q = simpy.Store(env, capacity = 3)
process_4_q = simpy.Store(env, capacity = 2)

env.process(gen_entities(env, process_0_res, process_1_q))

env.process(process1_broker(env, process_1_q, process_1_res))
env.process(process2_broker(env, process_2_q, process_2_res))
env.process(process3_broker(env, process_3_q, process_3_res))
env.process(process4_broker(env, process_4_q, process_4_res)) 

env.run(10)

Solution

  • In your code you are seizing resources twice, once in the broker, and then again in the process. so if you only have one resource, the second seize will never happen. also, you are only releasing one of the two resources, so you will run out of resources at some point.

    Since the broker already seizes a resource and saves it in the entity, the process does not need to do a second seize. However, the process will still need to release the resource that was seized in the broker.

    I think I fixed your code

    import simpy
    import random
    import numpy as np
    
    class Entity(object):
        pass
    
    def process0(env, entity, process_0_res, process_1_q):
        print(f' {env.now} customer {entity.id} is in system')
        with process_0_res.request() as res_req:
            yield res_req
            
            yield process_1_q.put(entity)
            print(f'{env.now} customer {entity.id} is in queue 1')
    
    def process1_broker(env, process_1_q, process_1_res):
        while True:
            # is resource available?
            res_req = process_1_res.request()
            yield res_req
            # is customer available?
            entity = yield process_1_q.get()
            # save resource request to release later
            entity.res_req = res_req
            # start process
            env.process(process1(env,entity,process_1_res, process_2_q))
    
    def process1(env, entity, process_1_res, process_2_q):
        # the resouce for process 1 has already been seized by the broker
        print(f' {env.now} customer {entity.id} in process 1')
        with entity.res_req:
           
            #print(f'{env.now} customer {entity.id} should now request server 1')
            #########
            #yield res_req
            #########
            #print(f' {env.now} customer {entity.id} in process 1')
            yield env.timeout(2)
            yield process_2_q.put(entity)
    
    def process2_broker(env, process_2_q, process_2_res):
        
        while True:
            res_req = process_2_res.request()
            yield res_req
            entity = yield process_2_q.get()
            entity.res_req = res_req
            env.process(process2(env,entity,process_2_res, process_3_q))
    
    def process2(env, entity, process_2_res, process_3_q):
        print(f' {env.now} customer {entity.id} in process 2')
        with entity.res_req:
            #yield res_req
            yield env.timeout(np.random.exponential(2))
            yield process_3_q.put(entity)
            
    def process3_broker(env, process_3_q, process_3_res):
        while True:
            res_req = process_3_res.request()
            yield res_req
            entity = yield process_3_q.get()
            entity.res_req = res_req
            env.process(process3(env,entity,process_3_res, process_4_q))
    
    def process3(env, entity, process_3_res, process_4_q):
        print(f' {env.now} customer {entity.id} in process 3')
        with entity.res_req:
            #yield res_req
            yield env.timeout(np.random.exponential(2))
            yield process_4_q.put(entity)
    
    def process4_broker(env, process_4_q, process_4_res):
        while True:
            res_req = process_4_res.request()
            yield res_req
            entity = yield process_4_q.get()
            entity.res_req = res_req
            env.process(process4(env,entity,process_4_res))
    
    def process4(env, entity, process_4_res):
        print(f' {env.now} customer {entity.id} in process 4')
        with entity.res_req:
            #yield res_req
            yield env.timeout(np.random.exponential(2))
            #yield process_4_res.release(entity.res_req)
            print(f' {env.now} customer {entity.id} leaves system')
            
    def gen_entities(env, process_0_res, process_1_q):
        next_id = 1
        while True:
            yield env.timeout(np.random.exponential(2))
            entity = Entity()
            entity.id = next_id
            next_id += 1
            env.process(process0(env, entity, process_0_res, process_1_q))
    
    env = simpy.Environment()
    process_0_res = simpy.Resource(env, capacity = 1)
    process_1_res = simpy.Resource(env, capacity = 1)
    process_2_res = simpy.Resource(env, capacity = 1)
    process_3_res = simpy.Resource(env, capacity = 1)
    process_4_res = simpy.Resource(env, capacity = 1)
    
    process_1_q = simpy.Store(env, capacity = 5)
    process_2_q = simpy.Store(env, capacity = 4)
    process_3_q = simpy.Store(env, capacity = 3)
    process_4_q = simpy.Store(env, capacity = 2)
    
    env.process(gen_entities(env, process_0_res, process_1_q))
    
    env.process(process1_broker(env, process_1_q, process_1_res))
    env.process(process2_broker(env, process_2_q, process_2_res))
    env.process(process3_broker(env, process_3_q, process_3_res))
    env.process(process4_broker(env, process_4_q, process_4_res)) 
    
    env.run(100)