Search code examples
pythonsimulationsimpy

SimPy, How to incorporate a break for resources?


At the moment I am creating doctors as following:

doctor = simpy.Resource(self.env, capacity=3)

These doctors are requested for a flow of patients using the following:

with self.doctor.request() as req:
    yield req2
    *patient encounter*

What I would like to do, is initialize the doctors with some kind of fatigue level (and other characteristics) like:

# Class representing a doctor and their characteristics
class Doctor(object):
    def __init__(self, env, id, exp_level, fatigue):
        self.env = env
        self.id = id
        self.exp_level = exp_level
        self.fatigue = fatigue 
        self.isAvailable = True

Then in the encounter I would like to access the doctor to add fatigue such as:

with self.doctor.request() as req:
    yield req2
    *patient encounter*
    self.doctor.fatigue += 5

Where once the fatigue crosses a threshold the doctor goes on break and the fatigue is reset, such as:

def monitor_break(self): 
    while True:
        if Doctor.fatigue > 9:
            Doctor.fatigue = 0
            yield self.env.timeout(15)

Any idea on how to do this?


Solution

  • First I would use a store for your docs instead of using a resource. Second I would decorate the store with a get override to check if the doc needs a nap

    something like this

    """
        A simple sim where Doctors put themselves on break when they get tired
    
        Programmer Michael R. Gibbs
    """
    
    import simpy
    import random
    
    class Doctor():
        """
            Doctor that get tired
        """
    
        def __init__(self, env, id):
            self.env = env
            self.id = id
            self.fatigue = 0
    
    class Doc_Pool():
        """
            decorates a simpy store
            to chekc a doc's fatigue level
            and doing a timeout if fatigue is too 
            high before returning the doc to the pool
        """
    
        def __init__(self, env, num_of_docs, max_fatigue):
    
            self.env = env
    
            self.store = simpy.Store(env)
            self.store.items = [Doctor(env, i+1) for i in range(num_of_docs)]
            self.max_fatigue = max_fatigue
    
        def get(self):
            """
                get a doc from the real store
            """
    
            return self.store.get() 
    
        def put(self, doc):
            """
                put a doc in the store
                unless the doc needs a break
            """
    
            if doc.fatigue > self.max_fatigue:
                # needs a break
                self.env.process(self._doc_timeout(doc))
    
            else:
                self.store.put(doc)
    
    
        def _doc_timeout(self, doc):
            """
                gives the doc a break, then put in store
            """
    
            print(f'{self.env.now:.0f} doctor {doc.id} starting break with fatigue {doc.fatigue:0.2f}')
    
            yield self.env.timeout(doc.fatigue)
    
            doc.fatigue = 0
    
            self.store.put(doc)
    
            print(f'{self.env.now:.0f} doctor {doc.id} ending break')
    
    
    class Patient():
        """
            Simple class to track patients
        """
    
        next_id = 1
    
        def __init__(self):
    
            self.id = self.get_next_id()
    
        @classmethod
        def get_next_id (cls):
            id =cls.next_id
            cls.next_id += 1
            return id
    
    def gen_pats(env, doc_store):
        """
            Generates the arrival off patients
            and queue them up for a doctor visit
        """
    
        while True:
            pat = Patient()
            print(f'{env.now:.0f} patient {pat.id} has arrived')
    
            yield env.timeout(random.uniform(1,5))
    
            env.process(doc_visit(env, pat, doc_store))
    
    def doc_visit(env, pat, doc_store):
        """
            gets a doc, do visit, add fatigue to doc, end visit
        """
    
        doc = yield doc_store.get()
    
        print(f'{env.now:.0f} patient {pat.id} is visiting doctor {doc.id}')
    
        env.timeout(random.uniform(1,6))
    
        doc.fatigue += random.uniform(1,5)
    
        doc_store.put(doc)
    
        print(f'{env.now:.0f} patient {pat.id} visiting doctor {doc.id} has ended')
    
    
    # boot up sim
    env = simpy.Environment()
    
    doc_store = Doc_Pool(env, 3, 10)
    
    env.process(gen_pats(env, doc_store))
    
    env.run(200)
    
    print('end of simulation')