Reputation: 321
I am exploring SimPy
to model patient flows through care processes. In a mock model, patients can either receive treatment or die. Obviously, these are competing risks, and patients should not be able to receive treatment after dying. However, I am very new to SimPy and I have yet to discover how to model a sink so that these deceased patients cannot continue through the simulation.
I am more familiar with the simmer
package in R
, in which you can branch()
the model structure and specify which branch of the model structure is a dead end. Is there similar functionality in SimPy? Or are there other elegant options?
To illustrate what I mean, below is the mock model:
from random import seed, randint
seed(123)
MAX_CYCLES = 5
PROB_DEATH = 0.2
class Patient:
def __init__(self, env, treatment_cycles = 0):
self.env = env
self.treatment_cycles = treatment_cycles
self.treatment_proc = env.process(self.get_treatment(env))
def get_treatment(self, env):
while self.treatment_cycles < MAX_CYCLES:
self.treatment_cycles += 1
rand = random.uniform(0, 1)
if (rand < PROB_DEATH):
yield env.timeout(random.randint(20, 40))
print('I have died at {}'.format(env.now))
### Once this point has been reached, patients should not be able to continue ###
else:
yield env.timeout(randint(20, 80))
print("I have completed a full cycle of treatment after {} days.".format(int(env.now)))
env = simpy.Environment()
pat = Patient(env)
env.run(until=250)
This results in obviously undesired output:
I have died at 22
I have completed a full cycle of treatment after 59 days.
I have died at 80
I have completed a full cycle of treatment after 135 days.
I have completed a full cycle of treatment after 209 days.
Upvotes: 0
Views: 202
Reputation: 1969
here is a more process and less agent base way to code it where the processes are the stations
"""
Show how to use a state variable to change behavior
less agent, more process
programmer: Michael R. Gibbs
"""
from random import seed, randint, uniform
import simpy
seed(123)
MAX_CYCLES = 5
PROB_DEATH = 0.2
def died(env, patient):
"""
patien dies, and no more processing
"""
yield env.timeout(randint(20, 80))
patient.sate = 'Dead'
print(f"Patient {patient.id} has died on day {env.now}")
def provide_treatment(env, patient):
"""
determine fate of patient
if lives provide treatment and send to next treatment
if dies, send to death process
"""
if patient.treatment_cycles < MAX_CYCLES:
patient.treatment_cycles += 1
rand = uniform(0, 1)
if (rand < PROB_DEATH):
# patient has died
env.process(died(env,patient))
else:
# patient lives
yield env.timeout(randint(20, 80))
print(f"Patient {patient.id} completed a full cycle of treatment after {env.now} days.")
# send to next treatment
env.process(provide_treatment(env,patient))
else:
# done with all treatments, do not send anywhere
print(f"Patient {patient.id} has completed all treatments after {env.now} days")
class Patient:
"""
patient seeking treatment
"""
next_id = 1
def __init__(self, env, treatment_cycles = 0):
self.state = 'Live' # starts out as a living patient
self.id = Patient.next_id
Patient.next_id += 1
self.env = env
self.treatment_cycles = treatment_cycles
env = simpy.Environment()
pat = Patient(env)
env.process(provide_treatment(env,pat))
pat = Patient(env)
env.process(provide_treatment(env,pat))
pat = Patient(env)
env.process(provide_treatment(env,pat))
pat = Patient(env)
env.process(provide_treatment(env,pat))
env.run(until=250)
Upvotes: 0
Reputation: 1969
There are two main types of simulation The classic entity flow where stations have all the logic and the entity has very little and the stations decide the fate of the entity. Agent base where the entity/agent has all the code and decides it's own fate.
The classic example for entity flow is manufacturing production lines where the stations/machines decide what to do to the entity and where to send it (branch to) next. Your R code sounds like it uses this paradigm
Your example is more agent like where the entity/patient controls its own behavior.
To fix this you just need to add a "state" attribute to the class to track if the patient is live or dead. Then check the patient's state in your while loop
see below:
"""
Show how to use a state variable to change behavior
programmer: Michael R. Gibbs
"""
from random import seed, randint, uniform
import simpy
seed(123)
MAX_CYCLES = 5
PROB_DEATH = 0.2
class Patient:
"""
patient seeking treatment
"""
def __init__(self, env, treatment_cycles = 0):
self.state = 'Live' # starts out as a living patient
self.env = env
self.treatment_cycles = treatment_cycles
self.treatment_proc = env.process(self.get_treatment(env))
def get_treatment(self, env):
"""
gets a treatment that can change the patient's
state from live, to dead
"""
while self.treatment_cycles < MAX_CYCLES and self.state=='Live':
# treat untile done, or patient has died
self.treatment_cycles += 1
rand = uniform(0, 1)
if (rand < PROB_DEATH):
# patient has died
yield env.timeout(randint(20, 40))
print('I have died at {}'.format(env.now))
# update state to dead
self.state = 'Dead'
### Once this point has been reached, patients should not be able to continue ###
else:
yield env.timeout(randint(20, 80))
print("I have completed a full cycle of treatment after {} days.".format(int(env.now)))
env = simpy.Environment()
pat = Patient(env)
env.run(until=250)
Upvotes: 2