Simmer in R for discrete-event simulation

There are various simulation techniques of importance to supply chain data analysis and modelling. One of them is discrete-event simulation. Discrete-event simulation is especially useful for higly dynamic and interdependent systems, as is often the case in complex manufacturing systems but also operational processes. In this post I will provide a conceptual introduction to simmer. Simmer is a simulation package in R for discrete-event simulation. As I will explain, simmer was originally developed for healthcare related process improvement. However, simmer can also be applied to e.g. job shop problems, as I will briefly demonstrate in this post. The article comprises coding examples implementing simmer in R.

simmer in R is rather abstract and process oriented

I have already compared discrete-event simulation with other simulation techniques. Discrete-event simulation is usually applied on a less strategic level. I.e. discrete-event simulation has a more detailed scope of focus vs. e.g. agent-based simulation. However, there are many different levels of abstraction in which discrete-event simulation can be applied. When working with simmer in R one will typically apply this type of simulation technique on a rather abstract and strategic level. At its core, however, simmer remains process oriented.

simmer in R was developed for healthcare process optimization

Discrete-event simulation is a popular technique in healthcare-related process optimization and facility planning. A large share of the need for developing a discrete-event simulation framework in R originated from this specific domain.

One characteristic of simmer is that it allows for the modelling of trajectories that describe a path or multiple alternative chains of activities in a system. These trajectories e.g. seize and release resources. It is a handy feature for modelling processes and workflows and thus very applicable to high-level process modelling such as e.g. healthcare related process optimization. However, this use of trajectories makes simmer an applicable tool for solving e.g. job shop problems too. Imagine a job shop with specified work centers and various product-related routings. Each routing could e.g. be modelled as a trajectory, and each work center as a member of a ressource category or single ressource object.

simmer in R has a C++ simulator at its core

simmer is efficient, also because its simulation engine is developed in C++. In other words, simmer represents a package that forms an interface around this C++ simulation core. C++ code is compiled before runtime, making it faster and more resource efficient. R code, on the other hand, is parsed and interpreted at runtime. This contributes to R being a slow language.

Relevant terminology for discrete-event simulation in R

There exists a vocabulary around simmer that has to be understood in order for analysts to be able to apply simmer and understand its model outputs:

  • Resource: Contains a server, representing the resource itself. Resource are occupied by active entities. The resource itself represents a passive entitiy. Lastly, resources also comprise a queue which is prioritized and buffers the tasks to be executed by the passive resource.
  • Manager: Can adjust resource properties while the simulation is running, i.e. being executed.
  • Source: Creates new arrivals based on some interval time. Arrivals are inserted into the simulation model by the source. Interval times can be modelled to follow e.g. some random distribution.
  • Arrival: Interacts with resources or other simulation model entities. Has a limited lifetime and may have attributes or associated prioritizations. When created arrivals are forwarded to a trajectory.
  • Trajectory: Activity chain for arrivals, i.e. to be undergone by arrivals. In other words, a series of actions that must be executed and that are triggered by arrivals.
  • Activity: Action representing a modelling component enabling implementation of arrival-resource interactions. Also allows for implementation of processing times or other durations / dwell times in the simulation model.

When working with R some of these terms will not be necessary, since R encapsulates most of simmer`s inner workings in C++.

Passive and active simulation model entities

One important distinguishment in simmer is the one between passive and active model entities. Passive entities are resources. Active entities are sources, arrivals and managers. Arrivals are the main actors of any simmer simulation model and they are created by sources. Managers are merely used for dynamically changing resource properties such as capacity and queue size.

Trajectories as core modelling elements for discrete-event simulation modelling in R

As stated trajectories represent an ordered activity chain. Using simmer in R trajectories are instantiated using the trajectory() function. Once instantiated activities can be appended to the trajectory object.

The following activities can be added to a simmer trajectory:

  • log_: Displays a message inserted into the function as input parameter
  • timeout: Spends some time in the simulation system
  • set_attribute: Set attribute values per arrival. Function parameter no. 1 is the simulation environment, no. 2 is the attribute key, and function parameter no. 3 is the value to set
  • get_attribute: Obtain attribute value, using the simulation environment as function parameter no. 1 and attribute value key as function parameter no. 2
  • seize: Used for seizing a resource
  • post.seize: Allows for modelling interactions with resources in the form of sub-trajectories that are, in this case, entered upon successful resource seize by the arrival
  • reject: Allows for modelling itneractions with resources in the form of sub-trajectories that, in this case, are entered when an arrival could not seize a resource but was instead rejected by the resource
  • release: Used for releasing a resource
  • select: Select resources dynamically (with a dynamic input parameter for specifying the resource to select) and immediately exhaust activity. Needs resource list and policy as input, policies being e.g. random selection or round-robin selection
  • set_queue_size: Set queue size of a resource
  • set_capacity: Set capacity of a resource
  • seize_selected: In combination with dynamic resource selection this function enables seizing dynamically selected resources
  • set_capacity_selected: Enables dynamic modification of selected resource capacities
  • branch: Allows for branching of trajectories into sub-trajectories. Alternative branching, meaning this is like an if-else statement
  • clone: Allows for parallel sub-trajectories, instead of alternative sub-trajectories such as the ones implemented with the branch function
  • synchronize: Removes all sub-trajectories but one
  • activate: Activate arrival source
  • deactivate: Deactivates arrival source
  • set_trajectory: Adjust the trajectory which arrivals from a source are attached to
  • set_source: Allows the model developer to set a new distribution for modelling interarrival time distributions
  • rollback: Allows jumping back to a previous activity within a trajectory, allowing for looping inside trajectories
  • batch: A number of arrivals have to be accumulated before the batch of them is allowed to proceed in the trajectory. Batch may be splitted or not, depending on parameter settings
  • separate: Used for splitting a batch, if it may be splitted
  • trap: Subscribes arrival to signals. Allows for handlers to be attached to arrivals that subscribe a signal. Upon receiving the signal the attached handler is executed while the current activity is stopped
  • wait: Waits until a certain signal is received
  • send: Send a signal to all subscribed arrivals, either immediately or after some specified delay
  • untrap: Unsubscribes arrivals from signals
  • leave: Activity triggering immediate arrival to abandon trajectory, based on some probability distribution
  • renege_in: Activity for letting arrival abandon trajectory after some specified time
  • renege_if: Activity for letting arrival abandon trajectory upon receiving a specified signal
  • reneg_abort: Activity of abandoning trajectory can be aborted with this activity, allowing for activity abort before receiving the signal

I want to implement an exemplary trajectory to demonstrate some of above functionality. I provide an illustration of the trajectory and its interaction with resources in the figure below:

understanding trajectories in simmer in R

Here is a small coding example in R, setting up a simulation model that contains above trajectory:

# importing simmer
library(simmer)

# set seed for random number generation
set.seed(42)
# declarate / create simulation environment
env = simmer("Production")

# setup a production trajectory
production = trajectory("production process") %>%
  # product processing requires one machine
  seize("machine", 1) %>%
  # processing time is normally distributed, with mean 39 sec and standard deviation 3 sec
  timeout(function() rnorm(n=1, mean=39, sd=3)) %>%
  # machine is free again after processing time has ended
  release("machine", 1) %>%
  # product needs to be packed
  seize("packaging", 1) %>%
  # the packaging process has a normally distributed processing time; mean 20 sec, standard deviation 5 sec
  timeout(function() rnorm(1, 20, 5)) %>%
  release("packaging", 1) %>%
  # last process step in the workflow is shipment
  seize("shipping", 1) %>%
  # shipping is has a normally distributed processing time of 10 sec, with a standard deviation of 5 sec
  timeout(function() rnorm(1, 10, 5)) %>%
  release("shipping", 1)

# adding ressources to the simulation environment
env %>%
  # there are 4 machines
  add_resource("machine", 4) %>%
  # there are 2 packaging lines
  add_resource("packaging", 2) %>%
  # there is one shipping process, i.e. one shipment area
  add_resource("shipping", 1)

# adding generator to the simulation environment and assigning production trajectory to generator
env %>%
  add_generator(name_prefix = "production",
                trajectory = production,
                distribution = function() rnorm(n=1,mean=20,sd=10))

In above coding example, resources and generators are added to the simulation environment, in addition to the trajectory.

Generators generate arrivals and forward them to the trajectory

A generator generates arrivals. These arrivals represent events that initiate a trajectory. In other words one can think of the generator forwarding its arrivals to the connected trajectory.

Generators can be added to a simulation environment using the add_generator function.

Resources comprise a server and a priority queue, representing seizable capacity

Resources can be seized and released, thereby occupying their capacity for a time. Resources comprise a server and a queue. The server is the one that can be seized and released. The server has a defined capacity. The queue is a priority queue and it has a defined size. The queue allows for arrivals to be queued before they are processed by the server.

In the coding example above the resources added are used to model a machine park, packaging line and shipping process with defined capacity.

Simmer resources can be added to a simulation environment using the add_resource function.

Trajectory models support various aspects of process simulation modelling

As demonstrated by me in above example, the listed activites result in simmer trajectory models effectively supporting various aspects of simulation modelling:

  1. Ressource interaction and ressource pool utilization modelling
  2. Source interaction and source implementation
  3. Branching
  4. Looping
  5. Batching
  6. Asynchonous modelling and programming
  7. Reneging

Understanding state changes and state monitoring in simmer in R

One important set of components comprised by the simmer simulation framework is the Monitor class and therefrom derived monitoring counters.

Monitoring counters register every change of state generated during simulation time. Simulation state changes are recorded by the record_ methods defined in the Monitor class. The following state changes are recorded:

  • Arrival enters ressource, i.e. is accepted into ressource
  • Arrival departs from ressource, i.e. leaves ressource
  • Ressource settings are adjusted during simulation time, i.e. during runtime. Can take the form of capacity or queue size modifications
  • Arrival triggers attribute value modification by sending the new attribute value
  • Arrival exits trajectory. Reasons for this can be that it has undergone all activities associated with the trajectory or that it has e.g. been rejected by a ressource. The arrival object informs the monitoring record method about its start, end and also activity-related times.

Data retrieval and data monitoring

Upon having set up a simulation environment and conducted simulation runs one can obtain data from the run by using the following function calls:

  • get_mon_arrivals: Returns information on arrivals, such as name, start time and e.g. whether it is finished or not
  • get_mon_resources: Returns information in state changes in a resource such as e.g. the timestamp when a state change occured or was attempted (time of event triggering state change in resource)
  • get_mon_attributes: Returns state changes in attributes and thereto related information, such as e.g. the time when event triggering attribute state change took place

In the coding example below I execute a simulation run.

# start simulation run for 500 time units
env %>%
  run(500)
## simmer environment: Production | now: 448.121920423977 | next: 
## { Monitor: in memory }
## { Resource: machine | monitored: TRUE | server status: 0(4) | queue status: 0(Inf) }
## { Resource: packaging | monitored: TRUE | server status: 0(2) | queue status: 0(Inf) }
## { Resource: shipping | monitored: TRUE | server status: 0(1) | queue status: 0(Inf) }
## { Source: production | monitored: 1 | n_generated: 16 }

I could also e.g. use the get_n_generated function to see how many arrivals the generator generated:

env %>% get_n_generated("production")
## [1] 16

And to get resource queue related data I could e.g. have used get_mon_resources:

env %>% 
  get_mon_resources()

Visualizing simulation results with simmer.plot

For visualization purposes we can use the simmer.plot library. I provide an example below, plotting the usage of machine resources:

library(simmer.plot)
plot(env,what="resources",metric="usage",c("machine"))
resource usage resulting simmer in R simulation

Below I provide another visualization example, visualizing the utilization of all ressources:

plot(env,what="resources",metric="utilization")

resource utilization resulting from simmer in R simulation

Other important functions and methods comprised by simmer

In addition to the trajectory function listed under the trajectory documentation of this post there are various other useful functions that can be called on e.g. the simulation environment. Here are some examples:

  • now: Takes the simulation environment as input value and returns current simulation time
  • peek: Takes the simulation environment as input value and returns an overview of future scheduled events
  • reset: Resets time, event queue, resource obejcts , sources and statistics from simulation environment

Reset the following components of a simulation environment: Time, event queue, resources, sources and statistics.

There are also various getter and setter function for obtaining e.g. resource and environment data, and for setting the same. For example, resource capacity can be adjusted with a setter-function and obtained with a getter-function.

References to articles with content related to simmer in R

I end this article by listing relevant links to content related to simmer in R and discrete-event simulation:

You May Also Like

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.