This post has been transferred from another blog platform and could have dead links / incorrect lay-out.
Please note: the syntax described here is no longer up-to-date, please refer to the readme at simmer’s GitHub page.
The simmer package grew out of a personal need for a simple rapid development discrete event simulation (DES) framework. I work in the hospital sector and at times use a DES approach to simulate hospital processes / patient trajectories. DES can give you a quick look at process bottlenecks and test out the impact of alternative process set-ups.
Tools such as SimPy and SimJulia.jl served as inspiration for this package. What I wanted to achieve most was to develop a simple rapid development DES environment which had all (or most) important analysis/plotting functions easily at hand. Aiming for this rapid development DES package and working from a specific business process background has most likely tainted the development of the package to an extent that it won’t be as dynamic to be applicable to everyone’s specific needs.
Speed & performance
While R is arguably not known as a fast language the current performance bottleneck of the simmer package is in simmer‘s own design. Future development will focus more on the performance of the package, where there is definitely room for improvement. If the package will gain popularity, I might be persuaded to move some of the fundamental parts of the simulation engine to C++ to achieve further performance gains.
Let’s say we want to simulate a very simple patient trajectory; a patient enters the hospital and has to register at the desk, next, is seen by a nurse for an intake and finally is met by the doctor for a consult.
Below this trajectory is written as a ‘trajectory dataframe’.
trajectory <- read.table(header=T, text= "event_id description resource amount duration successor 1 registration administration 1 runif(1,3,10) 2 2 intake nurse 1 runif(1,10,20) 3 3 consultation doctor 1 runif(1,5,15) NA" )
This gives the following data frame:
As you can see, the durations given here are in the form of an R command. Any function call or value which generates a (single) number can be used for the duration and sucessor values. These values are re-evaluated for every entity that get assigned to this trajectory.
Next we build up a simple simulation with 10 patients, each starting about 10 minutes apart, to be precise
rnorm(1,10) apart. We decide let this run for 15 replications to get a view on the variability.
library(simmer) sim <- create_simulator(name = "SuperDuperSim") %>% add_trajectory(name = "simple_trajectory", trajectory_df = trajectory) %>% add_resource(name = "administration", capacity = 1) %>% add_resource(name = "nurse", capacity = 1) %>% add_resource(name = "doctor", capacity = 2) %>% add_entities_with_interval(n = 10, name_prefix = "patient", trajectory_name = "simple_trajectory", interval = "rnorm(1,10)") %>% replicator(15)
All resources required have to be added using the
add_resource command. Note that we add 2 doctors to the simulation here. Once the simulation object
sim has been built up, we can start up the simulation and let it
simmer for a while.
Once the simmering stops we can plot some of the results. We can for example have a look at the resource utilization of the doctor resource.
plot_resource_usage(sim, resource_name = "doctor")
Or have a look at the utilization of the different resources in the simulation.
Maybe we want a view on the evolution of the entities’ flow, waiting or activity times.
plot_evolution_entity_times(sim, type = "flow_time")
plot_evolution_entity_times(sim, type = "activity_time")
plot_evolution_entity_times(sim, type = "waiting_time")
Try it out!
Installation instructions, further documentation and extra examples can be found at https://github.com/Bart6114/simmer. For issues / bugs / improvement suggestions create an issue on GitHub or send me a mail at [email protected].
Have fun with it: feedback & tips are always welcome!