-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy patharrival_node.py
More file actions
175 lines (154 loc) · 6.69 KB
/
arrival_node.py
File metadata and controls
175 lines (154 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
from random import random
from .individual import Individual
class ArrivalNode:
"""Class for the arrival node of the network.
See Also
--------
ciw.Simulation : Main simulation class.
Notes
-----
This class handles arrivals, baulking, and rejections of individuals.
"""
def __init__(self, simulation):
"""
Initialise the arrvial node.
Parameters
----------
simulation : object
The simulation to which this arrival node belongs.
"""
self.simulation = simulation
self.number_of_individuals = 0
self.number_of_individuals_per_class = {clss: 0 for clss in self.simulation.network.customer_class_names}
self.number_accepted_individuals = 0
self.number_accepted_individuals_per_class = {clss: 0 for clss in self.simulation.network.customer_class_names}
self.system_capacity = self.simulation.network.system_capacity
self.event_dates_dict = {
node + 1: {clss: False for clss in self.simulation.network.customer_class_names
} for node in range(self.simulation.network.number_of_nodes)
}
def initialise(self):
self.initialise_event_dates_dict()
self.find_next_event_date()
def __repr__(self):
"""
Representation of an arrival node.
"""
return "Arrival Node"
def decide_baulk(self, next_node, next_individual):
"""
Either makes an individual baulk, or sends the individual
to the next node.
"""
if next_node.baulking_functions[self.next_class] is None:
self.send_individual(next_node, next_individual)
else:
rnd_num = random()
if rnd_num < next_node.baulking_functions[self.next_class](next_node.number_of_individuals, Q=self.simulation, next_ind=next_individual, next_node=next_node):
self.record_baulk(next_node, next_individual)
self.simulation.nodes[-1].accept(next_individual, completed=False)
else:
self.send_individual(next_node, next_individual)
def find_next_event_date(self):
"""
Finds the time of the next arrival.
"""
minnd = None
minclss = None
mindate = float("Inf")
for node in self.event_dates_dict:
for clss in self.event_dates_dict[node]:
if self.event_dates_dict[node][clss] < mindate:
minnd = node
minclss = clss
mindate = self.event_dates_dict[node][clss]
self.next_node = minnd
self.next_class = minclss
self.next_event_date = mindate
def have_event(self):
"""
Finds a batch size. Creates that many Individuals and send
them to the relevant node. Then updates the event_dates_dict.
"""
batch = self.batch_size(self.next_node, self.next_class)
for _ in range(batch):
self.number_of_individuals += 1
self.number_of_individuals_per_class[self.next_class] += 1
priority_class = self.simulation.network.priority_class_mapping[self.next_class]
next_individual = self.simulation.IndividualType(
self.number_of_individuals,
self.next_class,
priority_class,
simulation=self.simulation,
)
next_node = self.simulation.transitive_nodes[self.next_node - 1]
next_individual.starting_node = next_node.id_number
self.simulation.routers[next_individual.customer_class].initialise_individual(next_individual)
self.release_individual(next_node, next_individual)
self.event_dates_dict[self.next_node][self.next_class] = self.increment_time(
self.event_dates_dict[self.next_node][self.next_class],
self.inter_arrival(self.next_node, self.next_class),
)
self.find_next_event_date()
def increment_time(self, original, increment):
"""
Increments the original time by the increment.
"""
return original + increment
def initialise_event_dates_dict(self):
"""
Initialises the next event dates dictionary
with random times for each node and class.
"""
for node in self.event_dates_dict:
for clss in self.event_dates_dict[node]:
if self.simulation.inter_arrival_times[node][clss] is not None:
self.event_dates_dict[node][clss] = self.inter_arrival(node, clss)
else:
self.event_dates_dict[node][clss] = float("inf")
def inter_arrival(self, node, customer_class):
"""
Samples the inter-arrival time for next class and node.
"""
return self.simulation.inter_arrival_times[node][customer_class]._sample(t=self.simulation.current_time)
def batch_size(self, node, customer_class):
"""
Samples the batch size for next class and node.
Raises error if a positive integer is not sampled.
"""
batch = self.simulation.batch_sizes[node][customer_class]._sample(t=self.simulation.current_time)
if isinstance(batch, int) and batch >= 0:
return batch
raise ValueError("Batch sizes must be positive integers.")
def record_baulk(self, next_node, individual):
"""
Adds an individual to the baulked dictionary.
"""
next_node.write_baulking_or_rejection_record(individual, record_type="baulk")
def record_rejection(self, next_node, individual):
"""
Adds an individual to the rejection dictionary.
"""
next_node.write_baulking_or_rejection_record(individual, record_type="rejection")
def release_individual(self, next_node, next_individual):
"""
Either rejects the next_individual die to lack of capacity,
or sends that individual to baulk or not.
"""
if (next_node.number_of_individuals >= next_node.node_capacity) or (self.simulation.number_of_individuals >= self.system_capacity):
self.record_rejection(next_node, next_individual)
self.simulation.nodes[-1].accept(next_individual, completed=False)
else:
self.decide_baulk(next_node, next_individual)
def send_individual(self, next_node, next_individual):
"""
Sends the next_individual to the next_node.
"""
self.number_accepted_individuals += 1
self.number_accepted_individuals_per_class[next_individual.customer_class] += 1
next_node.accept(next_individual)
def update_next_event_date(self):
"""
Passes, as updating next event happens at time of event.
"""
pass