'How to simulation allow to wait until the next process is resolved?

< This is brief scenario > Patients arrive at ED(Emergency Department). When patients arrive at the ED, they first receive a triage from the nurse. After the triage, patients will be assigned an ED bed, but if patients don't have a clean bed, they have to wait until get a dirty bed. (Dirty beds occur when the patient is discharged or goes to the IU(Inpatient Unit).) The meaning of waiting here has two cases, the first is that when the patient arrives, if there is only a dirty bed(not cleaned), system should give an order of cleaning. The second is that if there is no dirty bed, this means dirty beds are being cleaned now or wait until patients are discharged or goes to the IU(the event of a dirty bed occurs.) When an ED bed is assigned, the patient is treated by a doctor. After examination of doctor, the patient will either go to the IU or be discharged. When the patient goes to the IU, if there is a clean bed in IU, the ED bed is returned immediately, but if not, the patient must continue to wait in the ED bed. And if the patient is discharged from the hospital, the ED bed will be returned, but cleaning will not proceed immediately.(Leave it dirty until the cleaning order is issued) Dirty ED beds are cleaned on request and then assigned to new arriving patients. Anyway, the part where I'm having a hard time implementing my program right now is that the part where assigning the ED bed is not being implemented as I intended.

So, my main point of the problem is that

  1. Patients should get cleaned ED bed.
  2. If there is no cleaned bed, patient wait until a cleaned bed available. (Waiting patients will continue to wait, but the process of other patients will continue.)
  3. When a patient leave the ED, the bed used by the patient remains dirty, but if there is a request to clean for new patient, the dirty bed should be cleaned.

I added 'Bed_Mgmt' class to solve this, but I couldn't. Please give me some guidelines.

import simpy
import random

class Pre_Define:
    warmup_period = 1440
    sim_duration = 14400
    number_of_runs = 3
    n_edbed = 77

class Patients:
    def __init__(self, p_id):
        self.id = p_id
        self.bed_name = ""
        self.admission_decision = ""
    def admin_decision(self):
        admin_decision_prob = random.uniform(0, 1)
        if admin_decision_prob <= 0.7:
            self.admission_decision = "DIS"
        else:
            self.admission_decision = "IU"

class Bed_Mgmt:
    def __init__(self, env):
        self.env = env
        
    def check_q(self, Pt_id, pt_ed_q, bed_clean, bed_dirty, bed_cleaner): #, pt_ed_q, pt_ed_q, dirty_bed_q
        if pt_ed_q != [] and bed_clean != []:
            Patients.bed_name = bed_clean.get()
            Pt_id = pt_ed_q.get()
        elif pt_ed_q != [] and bed_clean == [] and bed_dirty != []:
            get_dirty_bed_name = bed_dirty.get() 
            with bed_cleaner.request() as req:
                yield req
                yield self.env.timeout(50)
                Patients.bed_name = get_dirty_bed_name
        else: # self.pt_ed_q != [] and self.clean_bed_q == [] and self.dirty_bed_q == []:
            pass  # generate patient

class Model:
    def __init__(self, run_number):
        self.env = simpy.Environment()
        self.pt_ed_q = simpy.Store(self.env )
        self.pt_counter = 0
        self.tg = simpy.Resource(self.env, capacity = 4)
        self.physician = simpy.Resource(self.env, capacity = 4)
        self.bed_clean = simpy.Store(self.env, capacity = Pre_Define.n_edbed)
        self.bed_dirty = simpy.Store(self.env, capacity = Pre_Define.n_edbed)
        self.IU_bed = simpy.Resource(self.env, capacity = 50)   
        self.bed_cleaner = simpy.Resource(self.env, capacity = 2)
        self.run_number = run_number
        
    def generate_beds(self):
        for i in range(Pre_Define.n_edbed):
            yield self.env.timeout(0)
            yield self.bed_clean.put(f'bed{i}')
        print(self.bed_clean.items)
       
    def generate_pt_arrivals(self):
        while True:
            self.pt_counter += 1
            pt = Patients(self.pt_counter)
            yield self.env.timeout(1/7)
            self.env.process(self.ED_Process(pt))
            #self.pt_ed_q.put(pt.id)

    def Process(self, Patients, Bed_Mgmt):
        with self.tg.request() as req:
            yield req
            triage_service_time = random.expovariate(1.0/18)
            yield self.env.timeout(triage_service_time)
        
        yield self.pt_ed_q.put(Patients.id)
        pt_id = yield self.pt_ed_q.get()
        Bed_Mgmt.check_q(pt_id, self.pt_ed_q, self.bed_clean, self.bed_dirty, self.bed_cleaner)

        with self.physician.request() as req:
            yield req
            yield self.env.timeout(10)
            Patients.admin_decision()
            
        if Patients.admission_decision == "DIS":
            with self.IU_bed.request() as req:
                yield req
                Bed_Mgmt.check_q(pt_id, self.pt_ed_q, self.bed_clean, self.bed_dirty, self.bed_cleaner)
                yield self.env.timeout(600)
                get_dirty_bed_name = Patients.bed_name
                yield self.bed_dirty.put(get_dirty_bed_name)
        else:
            get_dirty_bed_name = Patients.bed_name
            Bed_Mgmt.check_q(pt_id, self.pt_ed_q, self.bed_clean, self.bed_dirty, self.bed_cleaner)
            yield self.bed_dirty.put(get_dirty_bed_name)

    def run(self):
        self.env.process(self.generate_pt_arrivals())
        self.env.process(self.generate_beds())
        self.env.run(until = Pre_Define.warmup_period + Pre_Define.sim_duration)

for run in range(Pre_Define.number_of_runs):
    run_model = Model(run)
    run_model.run()
    print()



Solution 1:[1]

So This cleans up a few things. You will see at the start of the log that a patient get s a bed right after triage finishes, because there are beds in the queue. A the end of the log you will see patients do not get a bed until a cleaner finishes cleaning a bed, showing the clean bed queue is empty. One of the nice things about simpy.Store is it manages the requests for you so you do not need to do any checking if the queue is empty. Also https://realpython.com/ is a good site for learning python.

import simpy
import random

class Pre_Define:
    warmup_period = 1440
    sim_duration = 14400
    number_of_runs = 1 #3
    n_edbed = 77

class Patients:
    def __init__(self, p_id):
        self.id = p_id
        self.bed_name = ""
        self.admission_decision = ""

    def admin_decision(self):
        admin_decision_prob = random.uniform(0, 1)
        if admin_decision_prob <= 0.7:
            self.admission_decision = "DIS"
        else:
            self.dmission_decision = "IU"

        return self.admission_decision

    

# class Bed_Mgmt:
#     def __init__(self, env):
#         self.env = env
        
#     def check_q(self, Pt_id, pt_ed_q, bed_clean, bed_dirty, bed_cleaner): #, pt_ed_q, pt_ed_q, dirty_bed_q
#         if pt_ed_q != [] and bed_clean != []:
#             Patients.bed_name = bed_clean.get()
#             Pt_id = pt_ed_q.get()
#         elif pt_ed_q != [] and bed_clean == [] and bed_dirty != []:
#             get_dirty_bed_name = bed_dirty.get() 
#             with bed_cleaner.request() as req:
#                 yield req
#                 yield self.env.timeout(50)
#                 Patients.bed_name = get_dirty_bed_name
#         else: # self.pt_ed_q != [] and self.clean_bed_q == [] and self.dirty_bed_q == []:
#             pass  # generate patient

class Model:
    def __init__(self, run_number):
        self.env = simpy.Environment()
        self.pt_ed_q = simpy.Store(self.env )
        self.pt_counter = 0
        self.tg = simpy.Resource(self.env, capacity = 4)
        self.physician = simpy.Resource(self.env, capacity = 4)
        self.bed_clean = simpy.Store(self.env)
        self.bed_dirty = simpy.Store(self.env)
        self.IU_bed = simpy.Resource(self.env, capacity = 50)   
        # self.bed_cleaner = simpy.Resource(self.env, capacity = 2)
        self.run_number = run_number
        
    def generate_beds(self):
        for i in range(Pre_Define.n_edbed):
            yield self.env.timeout(0)
            yield self.bed_clean.put(f'bed{i}')
        print(self.bed_clean.items)
       
    def generate_pt_arrivals(self):
        while True:
            self.pt_counter += 1
            pt = Patients(self.pt_counter)
            #yield self.env.timeout(1/7)
            yield self.env.timeout(5)
            self.env.process(self.ed_process(pt))
            #self.pt_ed_q.put(pt.id)

    def clean_beds_process(self, cleaner_id):
        """
            contious process for cleaning beds
            feeds off of the dirty bed queue
            if the queue is empty, will wait 
            until a bed is added to the queue.
            Clean beds are returned to the clean bed queue

            This process is the cleaner.  
            It one instance of th process is started for each cleaner
        """

        while True:

            # wait untile there is a bed in the dirty bead queue
            bed = yield self.bed_dirty.get()

            print(f'{self.env.now:.2f}  cleaner {cleaner_id} has started to clean bed {bed}')

            # clean bed
            yield self.env.timeout(50)

            print(f'{self.env.now:.2f}  cleaner {cleaner_id} has finish cleaning bed {bed}')

            # put bed in clean bed queue, loop to wait for next dirty bead
            yield self.bed_clean.put(bed)


    def ed_process(self, pt):
        # process for treating a patient

        print(f'{self.env.now:.2f}  patient  {pt.id} has arrived')
        with self.tg.request() as req:
            yield req
            triage_service_time = random.expovariate(1.0/18)
            yield self.env.timeout(triage_service_time)
        
        print(f'{self.env.now:.2f}  patient  {pt.id} has been triaged')
        
        # yield self.pt_ed_q.put(Patients.id)
        # pt_id = yield self.pt_ed_q.get()
        # Bed_Mgmt.check_q(pt_id, self.pt_ed_q, self.bed_clean, self.bed_dirty, self.bed_cleaner)

        bed = yield self.bed_clean.get()
        pt.bed_name = bed
        print(f'{self.env.now:.2f}  patient {pt.id} has a clean EU bed {bed}')

        with self.physician.request() as req:
            yield req
            yield self.env.timeout(10)
            pt.admin_decision()

            print(f'{self.env.now:.2f}  patient {pt.id} has admission descesion of  {pt.admission_decision}')
            
        if pt.admission_decision == "DIS":
            with self.IU_bed.request() as req:
                yield req
                # Bed_Mgmt.check_q(pt_id, self.pt_ed_q, self.bed_clean, self.bed_dirty, self.bed_cleaner)
                
                # have IU bed can give up EU bed
                dirty_bed_name = pt.bed_name
                yield self.bed_dirty.put(dirty_bed_name)

                print(f'{self.env.now:.2f}  patient {pt.id} moved to IU giving up bed {dirty_bed_name}')

                # time until IU bed becomes becone available 
                yield self.env.timeout(600)
                print(f'{self.env.now:.2f}  IU bed is available')
        else:
            # patient leaves EU
            dirty_bed_name = pt.bed_name
            #Bed_Mgmt.check_q(pt_id, self.pt_ed_q, self.bed_clean, self.bed_dirty, self.bed_cleaner)
            yield self.bed_dirty.put(dirty_bed_name)

            print(f'{self.env.now:.2f}  patient {pt.id} left EU giving up bed {dirty_bed_name}')


    def run(self):
        self.env.process(self.generate_pt_arrivals())
        self.env.process(self.generate_beds())

        # creating and starting two cleaners
        for i in range(2):
            self.env.process(self.clean_beds_process(i+1))
        
        #self.env.run(until = Pre_Define.warmup_period + Pre_Define.sim_duration)
        self.env.run(until = 650)

for run in range(Pre_Define.number_of_runs):
    run_model = Model(run)
    run_model.run()
    print()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michael