7.2 The Trajectory Object

A trajectory can be defined as a recipe and consists of an ordered set of activities. The trajectory() method instantiates the object, and activities can be appended using the %>% operator:

library(simmer)
library(magrittr)

traj0 <- trajectory() %>%
  log_("Entering the trajectory") %>%
  timeout(10) %>%
  log_("Leaving the trajectory")

The trajectory above illustrates the two most basic activities available: displaying a message (log_()) and spending some time in the system (timeout()).

An arrival attached to this trajectory will execute the activities in the given order, i.e., it will display “Entering the trajectory,” then it will spend 10 units of (simulated) time, and finally it will display “Leaving the trajectory.”

The example uses fixed parameters: a string and a numeric value respectively. However, at least the main parameter for all activities can also be what we will call a dynamical parameter, i.e., a function.

traj1 <- trajectory() %>%
  log_(function() "Entering the trajectory") %>%
  timeout(10) %>%
  log_(function() "Leaving the trajectory")

Let’s check what happens if we print the trajectory:

traj1
## trajectory: anonymous, 3 activities
## { Activity: Log          | message: function(), level: 0 }
## { Activity: Timeout      | delay: 10 }
## { Activity: Log          | message: function(), level: 0 }

We see the three activities we have created in our trajectory: message + delay + message. There are many activities available. We will briefly review them by categorizing them into different topics.

  • Arrival properties: Arrivals are able to store attributes and modify these using set_attribute(). Arrivals also hold a set of three prioritization values for accessing resources, priority, preemptible and restart, and we can use them inside the set_priorization() function.
  • Interaction with resources: The two main activities for interacting with resources are seize() and release(). In their most basic usage, they seize/release a given amount of a resource specified by name.
  • Interaction with sources: There are four activities specifically intended to modify arrival sources. An arrival may activate() or deactivate() a source, but also modify with set_trajectory() the trajectory to which it attaches the arrivals created, or set a new inter-arrival distribution with set_source().
  • Branching: A branch is a point in a trajectory in which one or more sub-trajectories may be followed. Two types of branching are supported in simmer. The branch() activity places the arrival in one of the sub-trajectories depending on some condition evaluated in a dynamical parameter called option. On the other hand, the clone() activity is a parallel branch. It does not take any option.
  • Loops: There is a mechanism, rollback(), for going back in a trajectory and thus executing loops over a number of activities. This activity causes the arrival to step back a given amount of activities a number of times.
  • Batching: Batching consists of collecting a number of arrivals before they can continue their path in the trajectory as a unit 2 . This means that if, for instance, 10 arrivals in a batch try to seize a unit of a certain resource, only one unit may be seized, not 10.
  • Asynchronous programming: There are a number of methods enabling asynchronous events. The send() activity broadcasts one or more signals to all the arrivals subscribed to them. Signals can be triggered immediately or after some delay. Arrivals are able to block and wait() until a certain signal is received.
  • Reneging: Besides being rejected while trying to seize a resource, arrivals are also able to leave the trajectory at any moment, synchronously or asynchronously. Namely, reneging means that an arrival abandons the trajectory at a given moment. The most simple activity enabling this is leave, which immediately triggers the action given some probability.