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)
<- trajectory() %>%
traj0 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.
<- trajectory() %>%
traj1 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
andrestart
, and we can use them inside theset_priorization()
function. - Interaction with resources: The two main activities for interacting with resources are
seize()
andrelease()
. 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()
ordeactivate()
a source, but also modify withset_trajectory()
the trajectory to which it attaches the arrivals created, or set a new inter-arrival distribution withset_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.
Thebranch()
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, theclone()
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 andwait()
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.