Search code examples
pythonsimulationsimpy

How can I indented in multiple with statement in Simpy?


I tried to build a simpy model with multiple 'with' statement as below. However, based on indentation, the result was different. Please let me know what is the clear rule of indentation.

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 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_transporter = simpy.Resource(self.env, capacity = 10) 
        # 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(5)
            self.env.process(self.ed_process(pt))

    def clean_beds_process(self, cleaner_id):
        while True:
            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
            yield self.env.timeout(10)
        #print(f'{self.env.now:.2f}  patient  {pt.id} has been triaged')
        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:
                print(f'{self.env.now:.2f} patient {pt.id} is waiting IU bed')
                yield req
                with self.bed_transporter.request() as transreq:
                    print(f'{self.env.now:.2f} patient {pt.id} is waiting transporter')
                    yield transreq
                    yield self.env.timeout(15)   
                    
                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 and give up {dirty_bed_name}')
    
                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()

There is no error, but the result was different by indentation.

For example, Case1>

if pt.admission_decision == "DIS":
            with self.IU_bed.request() as req:
                print(f'{self.env.now:.2f} patient {pt.id} is waiting IU bed')
                yield req
                with self.bed_transporter.request() as transreq:
                    print(f'{self.env.now:.2f} patient {pt.id} is waiting transporter')
                    yield transreq
                    yield self.env.timeout(15)   
                    
                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 and give up {dirty_bed_name}')
    
                yield self.env.timeout(600)
                print(f'{self.env.now:.2f} IU bed is available')

Case2>

                if pt.admission_decision == "DIS":
            with self.IU_bed.request() as req:
                print(f'{self.env.now:.2f} patient {pt.id} is waiting IU bed')
                yield req
                with self.bed_transporter.request() as transreq:
                    print(f'{self.env.now:.2f} patient {pt.id} is waiting transporter')
                    yield transreq
                    yield self.env.timeout(15)   
                    
                    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 and give up {dirty_bed_name}')
                yield self.env.timeout(600)
                print(f'{self.env.now:.2f} IU bed is available')

Case3>

        if pt.admission_decision == "DIS":
            with self.IU_bed.request() as req:
                print(f'{self.env.now:.2f} patient {pt.id} is waiting IU bed')
                yield req
                with self.bed_transporter.request() as transreq:
                    print(f'{self.env.now:.2f} patient {pt.id} is waiting transporter')
                    yield transreq
                    yield self.env.timeout(15)   
                    
                    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 and give up {dirty_bed_name}')
                    yield self.env.timeout(600)
                    print(f'{self.env.now:.2f} IU bed is available')

I'm not sure how indent after "with"...


Solution

  • the resource is is released at the end of the indicted code. with any "with", close is called at the end of the indentation. The other common use case for "with" is reading files or streams. Since the dirty bed bed store is infinite I think case 1 and 2 would run the same. The tech difference is in case 1 the transporter is released after the yield self.env.timeout(15) line. while in case 2 it is release after the print(f'{self.env.now:.2f}..... statement. and in case 3 both the IU bed and the transporter are released at the end, keeping the transported for the timeout(600)

    I think case 1 is what you want