'How can I keep track of the information of multiple objects where there are various events in Simpy?
I tried to build a simulation model as below, but I got error. Here's my scenario. I have two big areas of hospital.
EU (EU1 and EU2)
IU (IU1 and IU2)
Each big area has two care areas and patient transfer from EU to IU (EU1 to IU1 or EU1 to IU2 or EU2 to IU1 or EU2 to IU2)
Transporter move the patient from EU to IU
Cleaner clean all beds, but it doesn't matter about the care areas.
However, the beds that have been cleaned, the bed should put each care area clean lit.
Sometimes, the bed that the patient needs and the bed that will be cleaned are different. -> I can say this is "Not matched case". For example, if a EU1 patient needs a EU1 bed cleaning, but the top of queue bed is a IU1 bed(or EU2 or IU2, NOT EU1 bed). So, the EU1 patient should wait until the EU1 bed come to queue and the bed is cleaned.
This is also happen to when a IU patient need IU bed cleaning
Maybe the order of cleaning bed queue should changeable by cleaning request later
import simpy
import random
class Pre_Define:
eu_beds= 10
iu_beds= 10
class Bed():
def __init__(self, id, request, care_area, name):
self.id = id
self.request = request
self.care_area = care_area
self.name = name
self.pt_info = None
self.priority = 2
class Patients:
def __init__(self, p_id):
self.id = p_id
self.request = None
self.edbed_info = None
self.iubed_info = None
self.care_area = None
self.admission_decision = ""
self.priority = 2
def admin_decision(self):
admin_decision_prob = random.uniform(0, 1)
if admin_decision_prob <= 0.3:
self.admission_decision = "DIS"
elif admin_decision_prob > 0.3 and admin_decision_prob <= 0.6:
self.admission_decision = "IU1"
elif admin_decision_prob > 0.6:
self.admission_decision = "IU2"
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_cleaner = simpy.Resource(self.env, capacity = 7)
self.bed_transporter = simpy.Resource(self.env, capacity = 5)
self.iu1_bed_clean = simpy.Store(self.env, capacity = 5)
self.iu2_bed_clean = simpy.Store(self.env, capacity = 5)
self.eu1_bed_clean = simpy.Store(self.env, capacity = 5)
self.eu2_bed_clean = simpy.Store(self.env, capacity = 5)
self.iu1_bed_dirty = simpy.Store(self.env, capacity = 5)
self.iu2_bed_dirty = simpy.Store(self.env, capacity = 5)
self.eu1_bed_dirty = simpy.Store(self.env, capacity = 5)
self.eu2_bed_dirty = simpy.Store(self.env, capacity = 5)
self.clean_request_q = simpy.Store(self.env, capacity = Pre_Define.eu_beds+Pre_Define.iu_beds)
self.bed_dirty = simpy.Store(self.env)
self.clean_request = simpy.Store(self.env)
self.pt_q = simpy.PriorityStore(self.env)
def generate_beds(self):
self.eu1_bed_clean.items = [Bed(i+1, "EU", "EU1", f'EU1bed_{i}') for i in range(Pre_Define.eu_beds)]
self.eu2_bed_clean.items = [Bed(i+1, "EU", "EU2", f'EU2bed_{i}') for i in range(Pre_Define.eu_beds)]
self.iu1_bed_clean.items = [Bed(i+1, "IU", "IU1", f'IU1bed_{i}') for i in range(Pre_Define.iu_beds)]
self.iu2_bed_clean.items = [Bed(i+1, "IU", "IU2", f'IU2bed_{i}') for i in range(Pre_Define.iu_beds)]
def generate_pt_arrivals(self):
while True:
self.pt_counter += 1
pt = Patients(self.pt_counter)
carearea_prob = random.uniform(0, 1)
if carearea_prob <= 0.5:
pt.care_area = "EU1"
pt.request = "EU"
else:
pt.care_area = "EU2"
pt.request = "EU"
yield self.env.timeout(5)
self.env.process(self.ed_process(pt))
def clean_beds_process(self, cleaner_id):
while True:
bed = yield self.clean_request_q.get()
print(f'{self.env.now:.2f} Case chedk!! patient {bed.pt_info.id} request bed {bed.pt_info.request} bed in queue {bed.request}')
# This is the matched case, patient request bed and the first queue in being cleaned bed
if (bed.pt_info.request == 'EU' and bed.request == 'EU') or (bed.pt_info.request == 'IU' and bed.request == 'IU'):
print(f'{self.env.now:.2f} Matched case patient {bed.pt_info.id} get matched bed {bed.pt_info.request} = {bed.request}')
with self.bed_cleaner.request() as bedreq:
yield bedreq
if bed.care_area == 'EU1':
yield self.eu1_bed_clean.put(bed)
elif bed.care_area == 'EU2':
yield self.eu2_bed_clean.put(bed)
elif bed.care_area == 'IU1':
yield self.iu1_bed_clean.put(bed)
else:
yield self.iu2_bed_clean.put(bed)
# This is the NOT matched case, so just clean the the first in queue bed and the patient should wait until matching the bed
else:
print(f'{self.env.now:.2f} NOT matched case patient {bed.pt_info.id} get matched bed {bed.pt_info.request} != {bed.request}')
with self.bed_cleaner.request() as bedreq:
yield bedreq
if bed.care_area == 'EU1':
yield self.eu1_bed_clean.put(bed)
continue
elif bed.care_area == 'EU2':
yield self.eu2_bed_clean.put(bed)
continue
if bed.care_area == 'IU1':
yield self.iu1_bed_clean.put(bed)
continue
else:
yield self.iu2_bed_clean.put(bed)
continue
def ed_process(self, pt):
with self.tg.request() as req:
yield req
yield self.env.timeout(15)
print(f'{self.env.now:.2f} patient {pt.id} has been triaged {pt.care_area}')
#yield self.pt_q.put(pt)
#pt = yield self.pt_q.get()
# EU1 case
if pt.care_area == 'EU1':
# The case: clean EU bed
if self.eu1_bed_clean.items != []:
edbed = yield self.eu1_bed_clean.get()
# Copy pt info when comparing pt care area and bed care area at "clean_beds_process" function
edbed.pt_info = pt
pt.edbed_info = edbed
# The case: don't have clean EU bed, but have dirty EU bed
elif self.eu1_bed_dirty.items != []:
edbed = yield self.eu1_bed_dirty.get()
edbed.pt_info = pt
yield self.clean_request_q.put(edbed)
edbed = yield self.eu1_bed_clean.get()
edbed.pt_info = pt
pt.edbed_info = edbed
# The case: don't have not only clean EU beds, but also dirty EU beds, so we should wait until any EU dirty bed come to list
else:
pass # I don't know what I have to do!!!
# EU2 case
elif pt.care_area == 'EU2':
if self.eu2_bed_clean.items != []:
edbed = yield self.eu2_bed_clean.get()
edbed.pt_info = pt
pt.edbed_info = edbed
elif self.eu2_bed_dirty.items != []:
edbed = yield self.eu2_bed_dirty.get()
edbed.pt_info = pt
yield self.clean_request_q.put(edbed)
edbed = yield self.eu2_bed_clean.get()
edbed.pt_info = pt
pt.edbed_info = edbed
# The case: don't have not only clean EU beds, but also dirty EU beds, so we should wait until any EU dirty bed come to list
else:
pass # I don't know what I have to do!!!
with self.physician.request() as req:
yield req
yield self.env.timeout(25)
pt.admin_decision()
# IU1 case
print(f'{self.env.now:.2f} patient {pt.id} IU case {pt.admission_decision}')
if pt.admission_decision == "IU1":
pt.request = "IU"
# The case that already clean IU beds available
if self.iu1_bed_clean.items != []:
iubed = yield self.iu1_bed_clean.get()
# Copy pt info when comparing pt care area and bed care area
iubed.pt_info = pt
pt.iubed_info = iubed
print(f'{self.env.now:.2f} patient {pt.id} get IU1 bed {iubed.id} direct')
# The case don't have clean IU beds, so request one of them is cleaned
elif self.iu1_bed_dirty.items != []:
iubed = yield self.iu1_bed_dirty.get()
# Copy pt info when comparing pt care area and bed care area
iubed.pt_info = pt
yield self.clean_request_q.put(iubed)
iubed = yield self.iu1_bed_clean.get()
iubed.pt_info = pt
pt.iubed_info = iubed
print(f'{self.env.now:.2f} patient {pt.id} get IU1 bed {iubed.id} after cleaning')
# The case don't have not only clean IU beds, but also dirty IU beds, so we should wait until IU dirty bed come to list
else:
pass # I don't know what I have to do!!!
# The case EU bed return base on the care area
if pt.care_area == 'EU1':
print(f'{self.env.now:.2f} patient {pt.id} return edbed {pt.edbed_info.id}')
yield self.eu1_bed_dirty.put(pt.edbed_info)
else:
print(f'{self.env.now:.2f} patient {pt.id} return edbed {pt.edbed_info.id}')
yield self.eu2_bed_dirty.put(pt.edbed_info)
# After waiting bed transporter for moving from EU and IU
with self.bed_transporter.request() as transreq:
yield transreq
# After using IU bed, return, put the iubed dirty queue list by care area
yield self.env.timeout(30)
if iubed.care_area == 'IU1':
print(f'{self.env.now:.2f} patient {pt.id} return iubed {pt.edbed_info.id}')
yield self.iu1_bed_dirty.put(pt.iubed_info)
else:
print(f'{self.env.now:.2f} patient {pt.id} return iubed {pt.edbed_info.id}')
yield self.iu2_bed_dirty.put(pt.iubed_info)
# IU2 case
elif pt.admission_decision == "IU2":
pt.request = "IU"
if self.iu2_bed_clean.items != []:
iubed = yield self.iu2_bed_clean.get()
# Copy pt info when comparing pt care area and bed care area
iubed.pt_info = pt
pt.iubed_info = iubed
print(f'{self.env.now:.2f} patient {pt.id} get IU2 bed {iubed.id} direct')
# The case don't have clean IU beds, so request one of them is cleaned
elif self.iu2_bed_dirty.items != []:
iubed = yield self.iu2_bed_dirty.get()
# Copy pt info when comparing pt care area and bed care area
iubed.pt_info = pt
yield self.clean_request_q.put(iubed)
iubed = yield self.iu2_bed_clean.get()
iubed.pt_info = pt
pt.iubed_info = iubed
print(f'{self.env.now:.2f} patient {pt.id} get IU2 bed {iubed.id} after cleaning')
# The case don't have not only clean IU beds, but also dirty IU beds, so we should wait until IU dirty bed come to list
else:
pass # I don't know what I have to do!!!
if pt.care_area == 'EU1':
yield self.eu1_bed_dirty.put(pt.edbed_info)
else:
yield self.eu2_bed_dirty.put(pt.edbed_info)
# After waiting bed transporter for moving from EU and IU
with self.bed_transporter.request() as transreq:
yield transreq
# After using IU bed, return, put the iubed dirty queue list
yield self.env.timeout(30)
if iubed.care_area == 'IU1':
print(f'{self.env.now:.2f} patient {pt.id} return iubed {pt.edbed_info.id}')
yield self.iu1_bed_dirty.put(pt.iubed_info)
else:
print(f'{self.env.now:.2f} patient {pt.id} return iubed {pt.edbed_info.id}')
yield self.iu2_bed_dirty.put(pt.iubed_info)
# Discharge case don't need IU bed, just discharge! and return EU bed
else:
# Return EU bed and put the eubed dirty queue list
if pt.care_area == 'EU1':
print(f'{self.env.now:.2f} patient {pt.id} return edbed {pt.edbed_info.id}')
yield self.eu1_bed_dirty.put(pt.edbed_info)
else:
print(f'{self.env.now:.2f} patient {pt.id} return edbed {pt.edbed_info.id}')
yield self.eu2_bed_dirty.put(pt.edbed_info)
def run(self):
self.env.process(self.generate_pt_arrivals())
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 = 300)
run_model = Model(0)
run_model.run()
I implemented the scenario, but got an error as below.
AttributeError: 'NoneType' object has no attribute 'id'
I think it's because the patient and bed information is not being monitored. Please give me some opinions or ideas to solve this problem. Thank you for your time and work.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|