In this example, the model is a modification of the original call centre model to include caller waiting time patience - i.e., they hang up if the call is not answered quickly.
The JSON for this built-in example can be loaded using json2ciw.datasets.load_renege_call_model .
Imports
import json
import ciw
from rich import print
from json2ciw.datasets import load_renege_call_model
from json2ciw.engine import CiwConverter, multiple_replications
from json2ciw.results import summarise_results, tidy_to_wide_format
from json2ciw.schema import ProcessModel
Load JSON
json_network = load_renege_call_model()
print (json.dumps(json_network, indent= 2 ))
{
"name" : "Call Handling Process with Renege." ,
"description" : "Discrete Event Simulation of a Patient call handling process where calling hang up if call is not
answered quickly." ,
"activities" : [
{
"name" : "Call Triage" ,
"type" : "activity" ,
"resource" : {
"name" : "Operator" ,
"capacity" : 13
} ,
"service_distribution" : {
"type" : "triangular" ,
"parameters" : {
"min" : 5 ,
"mode" : 7 ,
"max" : 10
}
} ,
"arrival_distribution" : {
"type" : "exponential" ,
"parameters" : {
"mean" : 0.6
}
} ,
"renege_distribution" : {
"type" : "uniform" ,
"parameters" : {
"min" : 1.0 ,
"max" : 10.0
}
}
} ,
{
"name" : "Nurse Consultation" ,
"type" : "activity" ,
"resource" : {
"name" : "Nurse" ,
"capacity" : 9
} ,
"service_distribution" : {
"type" : "uniform" ,
"parameters" : {
"min" : 10 ,
"max" : 20
}
}
}
] ,
"transitions" : [
{
"from" : "Call Triage" ,
"to" : "Nurse Consultation" ,
"probability" : 0.4
} ,
{
"from" : "Call Triage" ,
"to" : "Exit" ,
"probability" : 0.6
} ,
{
"from" : "Nurse Consultation" ,
"to" : "Exit" ,
"probability" : 1.0
}
]
}
model_instance = ProcessModel(** json_network)
ProcessModel (
name ='Call Handling Process with Renege.' ,
description ='Discrete Event Simulation of a Patient call handling process where calling hang up if call is not
answered quickly.' ,
activities =[
Activity (
name ='Call Triage' ,
type ='activity' ,
resource =Resource ( name ='Operator' , capacity =13 ) ,
service_distribution =Distribution (
type ='triangular' ,
parameters ={ 'min' : 5.0 , 'mode' : 7.0 , 'max' : 10.0 }
) ,
arrival_distribution =Distribution ( type ='exponential' , parameters ={ 'mean' : 0.6 }) ,
renege_distribution =Distribution ( type ='uniform' , parameters ={ 'min' : 1.0 , 'max' : 10.0 })
) ,
Activity (
name ='Nurse Consultation' ,
type ='activity' ,
resource =Resource ( name ='Nurse' , capacity =9 ) ,
service_distribution =Distribution ( type ='uniform' , parameters ={ 'min' : 10.0 , 'max' : 20.0 }) ,
arrival_distribution =None ,
renege_distribution =None
)
] ,
transitions =[
Transition ( source ='Call Triage' , target ='Nurse Consultation' , probability =0.4 ) ,
Transition ( source ='Call Triage' , target ='Exit' , probability =0.6 ) ,
Transition ( source ='Nurse Consultation' , target ='Exit' , probability =1.0 )
]
)
model_instance.save_diagram("callcentre.mmd" , include_resources= False )
flowchart TD
Arrivals_Call_Triage("Time between arrivals<br/>Exponential(mean=0.6)")
Call_Triage["Call Triage<br/>('Triangular(5.0, 7.0, 10.0)',)"]
Nurse_Consultation["Nurse Consultation<br/>Uniform(10.0, 20.0)"]
Renege_Call_Triage{{"Renege<br/>Uniform(1.0, 10.0)"}}
Exit(["Exit"])
Arrivals_Call_Triage --> Call_Triage
Call_Triage -.-> Renege_Call_Triage
Call_Triage -->|40%| Nurse_Consultation
Call_Triage -->|60%| Exit
Nurse_Consultation --> Exit
model_instance.save_diagram("callcentre_resources.mmd" , include_resources= True )
flowchart TD
Arrivals_Call_Triage("Time between arrivals<br/>Exponential(mean=0.6)")
Call_Triage["Call Triage<br/>('Triangular(5.0, 7.0, 10.0)',)"]
Nurse_Consultation["Nurse Consultation<br/>Uniform(10.0, 20.0)"]
Renege_Call_Triage{{"Renege<br/>Uniform(1.0, 10.0)"}}
Resource_Operator(("Operator<br/>(13)"))
Resource_Nurse(("Nurse<br/>(9)"))
Exit(["Exit"])
Arrivals_Call_Triage --> Call_Triage
Resource_Operator -.Seize.-> Call_Triage
Call_Triage -.Release.-> Resource_Operator
Resource_Nurse -.Seize.-> Nurse_Consultation
Nurse_Consultation -.Release.-> Resource_Nurse
Call_Triage -.-> Renege_Call_Triage
Call_Triage -->|40%| Nurse_Consultation
Call_Triage -->|60%| Exit
Nurse_Consultation --> Exit
model_instance.get_distributions_df()
Activity
Phase
Distribution Type
Parameters
0
Call Triage
Arrival
Exponential
mean=0.6
1
Call Triage
Service
Triangular
min=5.0, mode=7.0, max=10.0
2
Call Triage
Renege
Uniform
min=1.0, max=10.0
3
Nurse Consultation
Service
Uniform
min=10.0, max=20.0
model_instance.get_routing_matrix_df()
Call Triage
Nurse Consultation
Exit
Source Activity
Call Triage
0.0
0.4
0.6
Nurse Consultation
0.0
0.0
1.0
model_instance.get_resources_df()
Resource
Activity
Count
0
Operator
Call Triage
13
1
Nurse
Nurse Consultation
9
Convert to ciw parameters
adapter = CiwConverter(model_instance)
network_params = adapter.generate_params()
print (network_params)
{
'number_of_servers' : [ 13 , 9 ] ,
'arrival_distributions' : [ Exponential ( rate =1.6666666666666667 ) , None ] ,
'service_distributions' : [ Triangular ( lower =5.0 , mode =7.0 , upper =10.0 ) , Uniform ( lower =10.0 , upper =20.0 )] ,
'reneging_time_distributions' : [ Uniform ( lower =1.0 , upper =10.0 ) , None ] ,
'routing' : [[ 0.0 , 0.4 ] , [ 0.0 , 0.0 ]]
}
Build and run the ciw model
network = ciw.create_network(** network_params)
sim = ciw.Simulation(network)
sim.simulate_until_max_time(50 )
print ("Quick simulation run worked!" )
Quick simulation run worked!
Run the model for multiple replications
df_reps = multiple_replications(
network,
model_instance,
num_reps= 5 ,
runtime= 2880 ,
warmup= 1440 ,
n_jobs=- 1 ,
)
df_reps.head()
rep
node_id
activity_name
resource_name
resource_capacity
n_service
mean_wait
mean_service
utilisation
mean_Lq
n_renege
mean_wait_renege
mean_wait_all
0
0
1
Call Triage
Operator
13
2251
0.703426
7.360011
90.784999
1.243545
117.0
1.771724
0.756210
1
0
2
Nurse Consultation
Nurse
9
797
168.985355
15.028666
98.944327
93.528700
NaN
NaN
NaN
2
1
1
Call Triage
Operator
13
2245
0.825940
7.342525
88.484260
1.467736
135.0
1.920765
0.888042
3
1
2
Nurse Consultation
Nurse
9
828
70.782173
15.035560
98.179626
40.699750
NaN
NaN
NaN
4
2
1
Call Triage
Operator
13
2241
0.572294
7.305171
89.163630
0.967762
62.0
1.791386
0.605114
Summarise results
summary = summarise_results(df_reps)
summary.round (1 )
activity
Metric
Call Triage (Operator)
Nurse Consultation (Nurse)
0
Mean completed services
2260.0
801.6
1
Mean waiting time
0.8
115.1
2
Mean service time
7.3
15.0
3
Mean utilisation
89.5
98.8
4
Mean queue length
1.4
63.2
5
Mean reneges
116.8
NaN
6
Mean reneging wait
1.9
NaN
7
Mean wait (all completed)
0.8
NaN