How to Set Maximium Queue Capacities

A maximum queueing capacity can be set for each node. This means that once the number of people waiting at that node reaches the capacity, then that node cannot receive any more customers until some individuals leave the node. This affects newly arriving customers and customers transitioning from another node differently:

  • Newly arriving customers who wish to enter the node once capacity is reached are rejected. They instead leave the system immediately, and have a data record written that records this rejection (see below).

  • Customers wishing to transition to the node after finishing service at another node are blocked (see below). This means thet remain at their original node, with a server whi is unable to begin another customer’s service, until space becomes available.

In order to implement this, we use the queue_capacities keyworks when creating the network:

>>> import ciw
>>> N = ciw.create_network(
...     arrival_distributions=[ciw.dists.Exponential(rate=2),
...                            ciw.dists.Exponential(rate=1)],
...     service_distributions=[ciw.dists.Exponential(rate=1),
...                            ciw.dists.Exponential(rate=1)],
...     routing=[[0.0, 0.5],
...              [0.0, 0.0]],
...     number_of_servers=[3, 2],
...     queue_capacities=[float('inf'), 10]
... )

>>> ciw.seed(0)
>>> Q = ciw.Simulation(N)
>>> Q.simulate_until_max_time(100)

In the example above, the second node has 2 servers and a queueing capacity of 10, and so once there are 12 customers present, it will not accept any more customers. In the first node, there is infinite queueing capacity, and so no blocking or rejection will occur here.

If the queue_capacities keyword is omitted, then infinite capacities are assumed, that is an uncapacitated system.

Rejection Records

Any customer that is rejected and leaves the system will be recorded by producing a data record of the rejection. This will look like this:

>>> recs = Q.get_all_records(only=['rejection'])
>>> dr = recs[0]
>>> dr
Record(id_number=283, customer_class='Customer', original_customer_class='Customer', node=2, arrival_date=86.79600309018552, waiting_time=nan, service_start_date=nan, service_time=nan, service_end_date=nan, time_blocked=nan, exit_date=86.79600309018552, destination=nan, queue_size_at_arrival=12, queue_size_at_departure=nan, server_id=nan, record_type='rejection')

These records will have field record_type as the string 'rejection'; will have information about the customer such as their ID number, and customer class; will have the node they were rejected from; they will have equal arrival_date and exit_date representing the date they arrived and got rejected; and they will have a queue_size_at_arrival showing the number of people at the queue when they got rejected. All other fields will have nan values.

Blocking Mechanism

In Ciw, Type I blocking (blocking after service) is implemented for restricted networks.

After service, a customer’s next destination is sampled from the transition matrix. If there is space at the destination node, that customer will join the queue there. Else if the destination node’s queueing capacity is full, then that customer will be blocked. That customer remains at that node, with its server, until space becomes available at the destination. This means the server that was serving that customer remains attached to that customer, being unable to serve anyone else until that customer is unblocked.

At the time of blockage, information about this customer is added to the destination node’s blocked_queue, a virtual queue containing information about all the customers blocked to that node, and the order in which they became blocked. Therefore, when space becomes available, the customer to be unblocked will be the customer who was blocked first. That is, the sequence of unblockages happen in the order which customers were blocked.

Circular blockages can lead to deadlock.

Information about blockages are visible in the service data records:

>>> recs = Q.get_all_records(only=['service'])
>>> dr = recs[381]
>>> dr
Record(id_number=281, customer_class='Customer', original_customer_class='Customer', node=1, arrival_date=86.47159..., waiting_time=0.23440..., service_start_date=86.70600..., service_time=0.60807..., service_end_date=87.31407..., time_blocked=0.75070..., exit_date=88.06478..., destination=2, queue_size_at_arrival=4, queue_size_at_departure=2, server_id=3, record_type='service')

In the case above, the customer ended service at date 87.31407..., but didn’t exit until date 88.06478..., giving a time_blocked of 0.75070....