Consider a queuing network composed of three queues, with the time distribution between entering the first queue and the service time for all queues determined by their unique exponential rates. The goal is to write a simulation program that, after the model reaches a steady state, calculates its performance metrics.
The scripts are located in "prog" folder
A set of plots from a test run are shown here
- Python 3.11
- Pandas 2.1.2
- Numpy 1.26.1
- Matplotlib 3.8.1
- Seaborn 0.13.0
The code is divided into 3 scripts: main, functions and plotting; and as their name suggest, they are responsible for the main execution, the classes and functions, and creating plots respectively.
Number of users is given as a variable. There is no limit to the amount of users. The time durations either for service time or user entry intervals. Two dictionaries are created in order to be used for dataframe creation. ** is zeroed initially because it is directly the first row in its dataframe.
All time intervals and service times are generated ahead of time for this simulation accordingly.
Statistical Counter dataframe is created 3 different times, referring to our 3 different queues; each having their own dataframe. but Performance Evaluation is only created once; for we only need 3 rows for our queues.
The main script is divided into 3 sections, which all 3 are similar in terms of function calling and initializing.
In each section, an eventList is constructed, the snapshot creating function is called and the required plots are drawn. For this simulation, it is needed to make the first snapshot manually. Then it is passed to snapshotCreate function to generate the rest of the snapshots.
Also, all users' average service time is also handled in the main script.
Snapshots refer to the state of our simulation in each point of interest/change in time.
Two classes are defined; eventList and branches.
eventList consists of 2 indicators. One shows the next timestamp where a new user enters our queue. The other one shows the time the current service gets done and finished. We also add 2 variables to hold their index numbers.
branches have the responsibility to hold the time intervals for second and third queue.
snapshotCreate is where the magic happens! This Loop calculates the time every user expects to start getting serviced. This is considered in an ideal scenario where every user will start being serviced the moment they enter the queue. Branching argument indicates whether we need to branch to other queues or not.
The function is divided into 3 groups:
- Queue is empty and we must wait for an entry.
- Queue is not empty and another user is being serviced. But an entry occurs.
- Queue is not empty and the previous user's service is finished.
First, the minimum between the next user entry and the next service finish time is calculated and assigned as currentTime, showing the timestamp we should proceed to for our next snapshot.
infinity is also assigned to our eventList whenever the appropriate number should not be selected. For example, if the queue is empty and the next entry is yet to happen in future timestamps, we do not add a service time to our eventList. Hence, assigning infinity.
In order to know the state we are in the mentioned groups, we check to see if the currentTime overlaps our entry time or service time. If endOfService is an infinity, we must proceed to our next entry time. Whenever an entry occurs, our queue is added by 1.
In terms of Statistical Counters, we also calculate the accumulated values to modify our dataframe; adding a new row to it. The values needed for the next snapshot are calculated in stat_acc_calculation and then they are added to the dataframe in statisticalCountersModify.
The index number are also iteration based on the state we are at.
After the end of each user's service, based on a certain probability, it is decided whether the user should go to queue 2 or queue 3.
entryPlot and statisticalCountersPlot are created for each queue. A performanceEvaluationPlot is also created showing a bar plot for all three queues.
The y-axis in Q(t) plot shows the value of Q(t) in each snapshot/timestamp. So it is the accumulated value.
A set of plots from a test run are shown here