class MMSQueueModel(object): ''' MMS Queue Model Very simple queuing simulation model. ''' def __init__(self, args): self.env = simpy.Environment() self.mean_arrivals = args.mean_arrivals self.mean_delay = args.mean_delay self.servers = simpy.Resource(self.env, capacity=args.n_servers) self.entities = [] self._rand = RandomState() def source(self, warm_up): """Create new entities until the sim time reaches end""" while True: iat = self._rand.exponential(self.mean_arrivals) yield self.env.timeout(iat) arrival = Entity(self.env, self.servers, self.mean_delay) if self.env.now >= warm_up: self.entities.append(arrival) self.env.process(arrival.enter_queue()) def run(self, run_length=1440, warm_up=0): """Start model run""" self.q_lengths = [] self.env.process(self.source(warm_up)) self.env.process(observe_queue(self.env, self.servers, 120, self.q_lengths)) self.env.run(until=run_length+warm_up)
class OBPatientGenerator(object): """ Generates patients. Set the "out" member variable to the resource at which patient generated. Parameters ---------- env : simpy.Environment the simulation environment adist : function a no parameter function that returns the successive inter-arrival times of the packets initial_delay : number Starts generation after an initial delay. Default = 0 stoptime : number Stops generation at the stoptime. Default is infinite """ def __init__(self, env, arr_stream, arr_rate, initial_delay=0, stoptime=250, debug=False): self.id = id self.env = env self.arr_rate = arr_rate self.arr_stream = arr_stream self.initial_delay = initial_delay self.stoptime = stoptime self.debug = debug self.num_patients_created = 0 self.prng = RandomState(RNG_SEED) self.action = env.process( self.run()) # starts the run() method as a SimPy process def run(self): """The patient generator. """ # Delay for initial_delay yield self.env.timeout(self.initial_delay) # Main generator loop that terminates when stoptime reached while self.env.now < self.stoptime: # Delay until time for next arrival # Compute next interarrival time iat = self.prng.exponential(1.0 / self.arr_rate) yield self.env.timeout(iat) self.num_patients_created += 1 # Create new patient obp = OBpatient(self.env.now, self.arr_stream, patient_id=self.num_patients_created, prng=self.prng) # Create a new flow instance for this patient. The OBpatient object carries all necessary info. obflow = obpatient_flow(env, obp, self.debug) # Register the new flow instance as a SimPy process. self.env.process(obflow)
class Entity(object): ''' Entity that moves through queuing and service in the model. ''' def __init__(self, env, servers, mean_delay, random_state=None): self.env = env self.servers = servers self.mean_delay = mean_delay self.wait = 0.0 self._rand = RandomState(seed=random_state) def enter_queue(self): self.arrive = self.env.now with self.servers.request() as server: yield server self.wait = self.env.now - self.arrive delay = self._rand.exponential(1 / self.mean_delay) yield self.env.timeout(delay)
class SimpleMonteCarloED(object): ''' SimpleMonteCarloED. A very simple monte carlo ED For a given no. of patients simulate 1. Triage assessment and treatment process time 2. Admit or not admit 3. For admitted patients only - delay in admission. ''' def __init__(self, params, random_state=None): ''' Constructor for SimpleMonteCarlosED Parameters: ------- params - ScenarioParmaeters, dataclass for sim model random_state - int, random seed. Allows for common random numbers to be used across multiple versions of the same model and reduces the noise in comparisons. (detault=None.) ''' self._params = params self._rs = RandomState(random_state) def simulate(self, n_patients): '''Performa a single replications/run of the simuation model Params: ------- n_patients - int, no. of patients to simulate. ''' process_times = self._simulate_ed_process_times(n_patients) admissions = self._simulate_admission(n_patients) admit_delays = self._simulate_dta_times(n_patients) #total time in ED for admitted patients ed_times_admit = process_times[admissions == 1] \ + admit_delays[admissions == 1] #total time in ED for non-admitted patients ed_times_not_admit = process_times[admissions == 0] #distribution of ed times. return np.append(ed_times_admit, ed_times_not_admit) def _simulate_ed_process_times(self, n_patients): ''' simulate ed process times for n patients ''' return self._rs.exponential(self._params.mean_process_time, size=n_patients) def _simulate_admission(self, n_patients): '''simulate admission Y/N for n patients ''' return self._rs.binomial(n=1, p=self._params.p_admit, size=n_patients) def _simulate_dta_times(self, n_patients): '''simulate admission delays for n patients ''' return self._rs.exponential(self._params.mean_dta, size=n_patients)
class OBPatientGenerator(object): """ Generates patients. Set the "out" member variable to resource at which patient generated. Parameters ---------- env : simpy.Environment the simulation environment arr_rate : float Poisson arrival rate (expected number of arrivals per unit time) patient_type : int Patient type id (default 1). Currently just one patient type. In our prior research work we used a scheme with 11 patient types. arr_stream : int Arrival stream id (default 1). Currently there is just one arrival stream corresponding to the one patient generator class. In future, likely to be be multiple generators for generating random and scheduled arrivals initial_delay : float Starts generation after an initial delay. (default 0.0) stoptime : float Stops generation at the stoptime. (default Infinity) max_arrivals : int Stops generation after max_arrivals. (default Infinity) debug: bool If True, status message printed after each patient created. (default False) """ def __init__(self, env, arr_rate, patient_type=1, arr_stream=1, initial_delay=0, stoptime=simpy.core.Infinity, max_arrivals=simpy.core.Infinity, debug=False): self.env = env self.arr_rate = arr_rate self.patient_type = patient_type self.arr_stream = arr_stream self.initial_delay = initial_delay self.stoptime = stoptime self.max_arrivals = max_arrivals self.debug = debug self.out = None self.num_patients_created = 0 self.prng = RandomState(RNG_SEED) self.action = env.process( self.run()) # starts the run() method as a SimPy process def run(self): """The patient generator. """ # Delay for initial_delay yield self.env.timeout(self.initial_delay) # Main generator loop that terminates when stoptime reached while self.env.now < self.stoptime and \ self.num_patients_created < self.max_arrivals: # Delay until time for next arrival # Compute next interarrival time iat = self.prng.exponential(1.0 / self.arr_rate) yield self.env.timeout(iat) self.num_patients_created += 1 # Create new patient obp = OBPatient(self.env.now, self.num_patients_created, self.patient_type, self.arr_stream, prng=self.prng) if self.debug: print("Patient {} created at {:.2f}.".format( self.num_patients_created, self.env.now)) # Set out member to OBunit object representing next destination self.out = obunits[obp.planned_route_stop[1]] # Initiate process of requesting first bed in route self.env.process(self.out.put(obp))
def exponential_lik(X: ndarray, rng: RandomState): return rng.exponential(X)