# mm1queue.py # ============================================================================= # # This file is part of SimElements. # ---------------------------------- # # SimElements is a software package designed to be used for continuous and # discrete event simulation. It requires Python 3.0 or later versions. # # Copyright (C) 2010 Nils A. Kjellbert # E-mail: # # SimElements is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # SimElements is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # #------------------------------------------------------------------------------ """ In order for this demo program to work you will probably have to type in one of the following two paragraphs - or both - depending on your platform and os: #! /usr/local/bin/python # ...or what's needed on your computer from sys import path # Slashes or backslashes...: path.append('/Path of directory where you place simelements/simelements') """ #------------------------------------------------------------------------------ """ This example is a demo of the use of discrete event simulation using SimElements' Line class and two of its random number generation classes. A M/M/1 queue is simulated, i.e.: - one server, - Poisson arrival (exponentially distributed interarrival times), - exponentially distributed service time, - FIFO queue organization. The output is sent to stdout. The theoretical result given the preset inputs below is a 9 minute average waiting time, an average of 3 customers in the system and 2.25 customers waiting in line, and a 0.25 server idle time fraction. The CPU time required on Nils Kjellbert's MacBook is approximately 0.08 seconds per realization using the Line class (does not include the postprocessing). """ # ----------------------------------------------------------------------------- import time from line import Line #from linestack import LineStack as Line # Alternative line handling from cumrandstrm import CumulRandomStream from genrandstrm import GeneralRandomStream from statlib.stats import aritmean # ----------------------------------------------------------------------------- # -- Simulation clock time in minutes -- # Number of servers nservers = 1 # Arrival rate (input to the cumulative exponential generator) arrivalrate = 15.0/60.0 # Number of new customers per minute # Service time (inputs to the exponential distribution generator) meanservtime = 3.0 # Mean service time (minutes) servtimemax = 400.0 # extreme outliers may screw up the statistics... # Time when each realization is closed (long enough so that equilibrium # may be assumed to prevail during most of each realization) closingtime = 1000.0 * max(1.0/arrivalrate, meanservtime) # Number of realizations (one is not enough...) nrealizations = 500 # Initiation of random streams (separate for arrival and service is nice...) nseeda = 201005231325 # seed for the arrival times rstrmarriv = CumulRandomStream(nseeda) # arrival time stream nseeds = nseeda + 2**26 - 1 # seed for the service times rstrmservt = GeneralRandomStream(nseeds) # service time stream # The below is for collection of accumulated waiting time statistics sumdictq = {} sumdicts = {} sumdicti = {} waitingtimeall = [] timeall = nrealizations*closingtime # ---------------------------------------------------------------------- def new_customer(tim): """ Actions when a new customer arrives: 1) The customer is placed in line. 2) If the service station is free, then - the customer is called directly (+the service station gets busy), and - the customer gets served (the service time is sampled), and - the event "Customer served" is placed in the event schedule; Else - no further action. """ queue.place_last_in_line(tim) if queue.nfreeserv > 0: queue.call_next_in_line(tim) servtime = rstrmservt.rexpo(meanservtime, servtimemax) queue.put_event("Customer served", tim + servtime) # end of new_customer # ------------------------------------------------------------ def customer_served(tim): """ Actions when a customer just has been served: 1) The service station is freed up. 2) If there are customers waiting in line, then - the next customer is called (and the service station gets busy), and - the next customer gets served (the service time is sampled), and - the event "Customer served" is placed in the event schedule; Else - no further action. """ queue.server_freed_up(tim) if queue.ninline > 0: queue.call_next_in_line(tim) servtime = rstrmservt.rexpo(meanservtime, servtimemax) queue.put_event("Customer served", tim + servtime) # end of customer_served # ------------------------------------------------------------------------ tim1 = time.time() for nrun in range(0, nrealizations): # --------------------------------- # Sample all the arrivals and place in two lists eventvector = [] timevector = [] rstrmarriv.reset() # Reset the cumulative arrival time random stream # Customers arrive with interarrival times exponentially distributed while True: timea = rstrmarriv.rexpo_cum(arrivalrate) if timea < closingtime: eventvector.append("New customer") timevector.append(timea) else: eventvector.append(None) timevector.append(closingtime) break # Create a queue object, initiate the service station and # place the arrivals in the event schedule queue = Line(nservers, eventvector, timevector) # Fetch events from the schedule until there are no events left # (some events will raise new events to be placed in the schedule - # this is handled by the functions new_customer and customer_served). # All this is controlled by the main time variable "clocktime", i.e. # the next time present in the event schedule while True: event, clocktime = queue.get_next_event() if event == "New customer": new_customer(clocktime) elif event == "Customer served": customer_served(clocktime) elif event == None: break # Gather statistics cumulatively ------------------- # Statistics for number of customers in queue statdictq = queue.line_stats() for keynumber in statdictq: try: sumdictq[keynumber] += statdictq[keynumber] except KeyError: sumdictq[keynumber] = statdictq[keynumber] # Statistics for number of customers in system statdicts = queue.system_stats() for keynumber in statdicts: try: sumdicts[keynumber] += statdicts[keynumber] except KeyError: sumdicts[keynumber] = statdicts[keynumber] # Statistics for number of idle servers statdicti = queue.idle_stats() for keynumber in statdicti: try: sumdicti[keynumber] += statdicti[keynumber] except KeyError: sumdicti[keynumber] = statdicti[keynumber] # All waiting times wt = queue.waiting_times_all() waitingtimeall.extend(wt) del queue # --------------------------------------------------------------------------- tim2 = time.time() # Compute and print out final result print("") print(" DEMO M/M/1 QUEUE") print("======================================================") print("") print("Average waiting time (min): ", aritmean(waitingtimeall)[0]) print("") print("------------------------------------------------------") print("") print("Number of customers in line/fraction of time:") keylist = list(sumdictq.keys()) keylist.sort() summ = 0.0 for keynumber in keylist: x = sumdictq[keynumber] summ += keynumber*x print(keynumber, "\t", x/timeall) print("") print("Average number of customers in line: ", summ/timeall) print("") print("------------------------------------------------------") print("") print("Number of customers in system/fraction of time:") keylist = list(sumdicts.keys()) keylist.sort() summ = 0.0 for keynumber in keylist: x = sumdicts[keynumber] summ += keynumber*x print(keynumber, "\t", x/timeall) print("") print("Average number of customers in system: ", summ/timeall) print("") print("------------------------------------------------------") print("") print("Number of servers idle/fraction of time:") keylist = list(sumdicti.keys()) keylist.sort() summ = 0.0 for keynumber in keylist: x = sumdicti[keynumber] summ += keynumber*x print(keynumber, "\t", sumdicti[keynumber]/timeall) print("") print("Average number of servers idle: ", summ/timeall) print("") print("------------------------------------------------------") print("") print("CPU time per realization = ", (tim2-tim1)/nrealizations, "s") print("") #------------------------------------------------------------------------------