Coverage for C:\checkouts\github\OpenQTSim\openqtsim\simulation.py: 14%

# Coverage for C:\checkouts\github\OpenQTSim\openqtsim\simulation.py : 14%

## 111 statements 16 run 95 missing 0 excluded

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1 import simpy

2 import pandas as pd

3 import numpy as np

4 import datetime

5 import time

6 from scipy import stats

7 from collections import namedtuple

8 import matplotlib.pyplot as plt

9

10

11 class Simulation:

12 """

13 A discrete event simulation that simulates the queue.

14 - queue is a queue based on the queue class

15 - seed is a random seed to have retraceable simulations

16 """

17

18 def __init__(self, queue, max_arr=100, priority=False, seed=None):

19 """

20 Initialization (the basic time unit is hours)

21 """

22

23 self.queue = queue

24 self.max_arr = max_arr

25

26 # set simulation time and epoch

27 self.sim_start = datetime.datetime.now()

28 self.env = simpy.Environment(initial_time=time.mktime(self.sim_start.timetuple()))

29 self.env.epoch = time.mktime(self.sim_start.timetuple())

30

31 # initialise counters and logs

32 self.c_s = 0 # people in the system

33 self.c_q = 0 # people in the queue

34 self.system_state = {

35 "t": [0],

36 "c_s": [0],

37 "c_q": [0]}

38

39 self.customer_nr = 0

40 self.log = {

41 "c_id": [], # c_id = customer id

42 "IAT": [], # IAT = inter arrival time

43 "ST": [], # ST = service time

44 "AT": [], # AT = now + IAT

45 "TSB": [], # TSB = time service begins

46 "TSE": [], # TSE = time service ends

47 "TCSS": [], # TCSS = time customer spends in the system

48 "TCWQ": [], # TCWQ = time customer waits in the queue

49 "ITS": [], # ITS = idle time of the server

50 "s_id": []} # s_id = server id

51

52 # activate random seed

53 np.random.seed(seed)

54

55 # define arrival and service processes

56 if not priority:

57

58 # --- arrival distribution ---

59 if self.queue.A.symbol == "M":

60 # define the average inter arrival time and add distribution with appropriate scaling

61 aver_IAT = 1 / self.queue.A.arr_rate

62 self.queue.A.arrival_distribution = stats.expon(scale=aver_IAT)

63

64 elif self.queue.A.symbol == "E2":

65 # define the average inter arrival time and add distribution with appropriate scaling

66 aver_IAT = 1 / self.queue.A.arr_rate

67 self.queue.A.arrival_distribution = stats.erlang(2, scale=aver_IAT)

68

69 elif self.queue.A.symbol == "D":

70 # the deterministic type expects arr_rate to contain a dataframe with columns ["name","IAT","AT"]

71 self.queue.A.arrival_distribution = self.queue.A.arr_rate

72

73 # --- service distribution ---

74 self.env.servers = simpy.FilterStore(self.env, capacity=self.queue.c)

75 self.env.servers.items = [] # to be filled in the next steps depending on S.symbol

76 self.env.server_info = {} # to be filled in the next steps depending on S.symbol

77 Server = namedtuple('Server', 'service_distribution, last_active, id')

78

79 if self.queue.S.symbol == "M":

80 # define the average service time and add distribution with appropriate scaling for each server

81 aver_ST = 1 / self.queue.S.srv_rate

82 for i in range(1, self.queue.c + 1):

83 self.env.servers.items.append(Server(stats.expon(scale=aver_ST), self.env.now, i))

84 self.env.server_info.update({i: {'last_active': self.env.now}})

85

86 elif self.queue.S.symbol == "E2":

87 # define the average service time and add distribution with appropriate scaling for each server

88 aver_ST = 1 / self.queue.S.srv_rate

89 for i in range(1, self.queue.c + 1):

90 self.env.servers.items.append(Server(stats.erlang(2, scale=aver_ST), self.env.now, i))

91 self.env.server_info.update({i: {'last_active': self.env.now}})

92

93 elif self.queue.A.symbol == "D":

94 if self.queue.c == 1:

95 # for 1 server the deterministic type expects srv_rate to contain a dataframe

96 # with columns: ["name","ST"]

97 self.env.servers.items.append(Server(self.queue.S.srv_rate, self.env.now, 1))

98 self.env.server_info.update({1: {'last_active': self.env.now}})

99 else:

100 # for n servers the deterministic type expects srv_rate to contain a dataframe

101 # with columns: ["name","ST","s_id"]

102 for i in range(1, self.queue.c + 1):

103 self.env.servers.items.append(Server(self.queue.S.srv_rate[self.queue.S.srv_rate["s_id"] == i],

104 self.env.now, i))

105 self.env.server_info.update({i: {'last_active': self.env.now}})

106

107 else:

108 pass

109 # Todo: add the option of having priority arrivals?

110

111 # initiate queue populating process

112 self.env.process(self.queue.populate(self.env, self))

113

114 def run(self, max_arr=1000):

115 """

116 Run simulation

117 """

118

119 self.max_arr = max_arr

120

121 self.env.run()

122

123 def log_customer_state(self, customer_id, IAT, AT, ST, TSB, TSE, ITS, s_id):

124 """

125 # the following items are logged per customer that enters the system:

126 # c = customer id

127 # IAT = inter arrival time

128 # ST = service time

129 # AT = arrival time

130 # TSB = time service begins

131 # TSE = time service ends

132 # TCSS = time customer spends in the system

133 # TCWQ = time customer waits in the queue

134 # ITS = idle time of the server

135 # s_id = id of server assigned to customer

136 """

137

138 self.log["c_id"].append(customer_id)

139 self.log["IAT"].append(IAT)

140 self.log["ST"].append(ST)

141 self.log["AT"].append(AT)

142 self.log["TSB"].append(TSB)

143 self.log["TSE"].append(TSE)

144 self.log["TCWQ"].append(TSB - AT)

145 self.log["TCSS"].append(TSE - AT)

146 self.log["ITS"].append(ITS)

147 self.log["s_id"].append(s_id)

148

149 def log_system_state(self, t, c_s, c_q):

150 """

151 # the following items are logged for the state of the system:

152 # t = time (from start of simulation)

153 # c_s = number of customers in the system

154 # c_q = number of customers in the queue

155 """

156

157 self.system_state["t"].append(t)

158 self.system_state["c_s"].append(c_s)

159 self.system_state["c_q"].append(c_q)

160

161 def return_log(self):

162 """

163 Return the log in the form of a pandas data frame.

164 """

165

166 # convert self.log to dataframe

167 df_cust = pd.DataFrame.from_dict(self.log)

168 df_cust = df_cust.sort_values(by=['AT'], ascending=[True])

169

170 # convert self.system_state to dataframe

171 df_sys = pd.DataFrame.from_dict(self.system_state)

172 df_sys = df_sys.sort_values(by=['t'], ascending=[True])

173

174 return df_cust, df_sys

175

176 def get_stats(self):

177 """

178 Post processing of logs to print basic simulation statistics

179 """

180

181 df_cust, df_sys = self.return_log()

182

183 value = np.mean(df_cust["TCWQ"]) / np.mean(df_cust["ST"])

184 print('Waiting time over service time: {:.4f}'.format(value))

185 print('')

186

187 value = (df_cust["TSE"].iloc[-1] - np.sum(df_cust["ITS"])) / df_cust["TSE"].iloc[-1]

188 print('Rho_system: system utilisation: {:.4f}'.format(value))

189 value = (df_cust["TSE"].iloc[-1] - (np.sum(df_cust["ITS"])/self.queue.c)) / df_cust["TSE"].iloc[-1]

190 print('Rho_server: server utilisation: {:.4f}'.format(value))

191

192 value = np.sum(df_cust["ITS"]) / df_cust["TSE"].iloc[-1]

193 print('P_0: probability nobody in the system: {:.4f}'.format(value))

194 print('')

195

196 value = np.mean(df_sys['c_s'])

197 print('L_s: average nr of customers in the system: {}'.format(value))

198 value = np.mean(df_sys['c_q'])

199

200 print('L_q: average nr of customers in the queue: {}'.format(value))

201 value = np.mean(df_cust["TCSS"])

202 print('W_s: the long term average time spent in the system: {:.4f}'.format(value))

203 value = np.mean(df_cust["TCWQ"])

204 print('W_q: the long term average time spent in the queue: {:.4f}'.format(value))

205 print('')

206

207 value = df_cust["AT"].iloc[-1]/(len(df_cust["ST"])-1)

208 print('IAT: average inter arrival time: {:.4f}'.format(value))

209

210 value = np.sum(df_cust["ST"])/(len(df_cust["ST"]))

211 print('ST: average service time: {:.4f}'.format(value))

212 print('')

213

214 def plot_system_state(self, fontsize=20):

215 """

216 Plot number of customers in the system and in the queue as a function of time

217 """

218

219 df_cust, df_sys = self.return_log()

220

221 fig, ax = plt.subplots(figsize=(14, 5))

222 ax.plot(df_sys['t'].values, df_sys['c_s'].values, '-bo', markersize=.1, label='c_s')

223 ax.plot(df_sys['t'].values, df_sys['c_q'].values, '-ro', markersize=.1, label='c_q')

224

225 ax.set_xlabel('Time [hours]', fontsize=fontsize)

226 ax.set_ylabel('nr of customers', fontsize=fontsize)

227 ax.set_title('System state: {}'.format(self.queue.kendall_notation), fontsize=fontsize)

228 ax.legend(loc='upper right', fontsize=fontsize)

229