--- title: "Advanced Trajectory Usage" author: "IƱaki Ucar, Bart Smeets" description: > Master the definition and manipulation of trajectories and activities. date: "`r Sys.Date()`" output: rmarkdown::html_vignette: toc: yes vignette: > %\VignetteIndexEntry{03. Advanced Trajectory Usage} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, cache = FALSE, include=FALSE} knitr::opts_chunk$set(collapse = T, comment = "#>", fig.width = 6, fig.height = 4, fig.align = "center") ``` ```{r, message=FALSE} library(simmer) ``` ## Available set of activities When a generator creates an arrival, it couples the arrival to a given trajectory. A trajectory is defined as an interlinkage of activities which together form the arrivals' lifetime in the system. Once an arrival is coupled to the trajectory, it will (in general) start processing activities in the specified order and, eventually, leave the system. Consider the following: ```{r} traj <- trajectory() %>% seize(resource = "doctor", amount = 1) %>% timeout(task = 3) %>% release(resource = "doctor", amount = 1) ``` Here we create a trajectory where a patient _seizes_ a doctor for 3 minutes and then _releases_ him again. This is a very straightforward example, however, most of the trajectory-related functions allow for more advanced usage. Usage examples are provided in the help page for each activity. The complete set of activities can be found and navigated in the [reference page](https://r-simmer.org/reference/#section-add-activities-to-trajectories), or you can list them as follows: ```{r} methods(class="trajectory") ``` Additionally, you may want to try the `simmer.bricks` package, a plugin for `simmer` which provides helper methods for trajectories. Each *brick* wraps a common activity pattern that can be used to build trajectories more conveniently (see the [_Introduction to `simmer.bricks`_](https://r-simmer.org/extensions/bricks/articles/introduction.html)). ## Dynamic arguments Many activities accept functions as arguments to be evaluated dynamically during the simulation. For example, see `help(timeout)`: > task: the timeout duration supplied by either passing a numeric or a callable object (a function) which must return a numeric. Be aware that if you want the `timeout()`'s `task` parameter to be evaluated dynamically, you should supply a callable function. For example in `timeout(function() rexp(1, 10))`, `rexp(1, 10)` will be evaluated every time the timeout activity is executed. However, if you supply it in the form of `timeout(rexp(1, 10))`, it will only be evaluated once when the trajectory is defined, and will remain static after that. ```{r} trajectory() %>% timeout(rexp(1, 10)) %>% # fixed timeout(function() rexp(1, 10)) # dynamic ``` Of course, this `task`, supplied as a function, may be as complex as you need and, for instance, it may check the status of a particular resource, interact with other entities in your simulation model... The same applies to all the activities when they accept a function as a parameter. ## Interaction with the environment Dynamic arguments may interact with the environment to extract parameters of interest such as the current simulation time (see `?now`), status of resources (see `?get_capacity`), status of generators (see `?get_n_generated`), or directly to gather the history of monitored values (see `?get_mon`). The only requirement is that the simulation environment must be in the scope of the trajectory. Therefore, this will not work: ```{r, error = TRUE} traj <- trajectory() %>% log_(function() as.character(now(env))) env <- simmer() %>% add_generator("dummy", traj, function() 1) %>% run(4) ``` because the global `env` is not available at runtime: the simulation runs _and then_ the resulting object is assigned to `env`. For `env` to be in the scope of `t` during this simulation, it is enough to detach the `run()` method from the definition pipe: ```{r} traj <- trajectory() %>% log_(function() as.character(now(env))) env <- simmer() %>% add_generator("dummy", traj, function() 1) env %>% run(4) %>% invisible ``` And we get the expected output. However, as a general rule of good practice, __it is recommended to instantiate the environment always in the first place__ to avoid possible mistakes, and because the code becomes more readable: ```{r} # first, instantiate the environment env <- simmer() # here I'm using it traj <- trajectory() %>% log_(function() as.character(now(env))) # and finally, run it env %>% add_generator("dummy", traj, function() 1) %>% run(4) %>% invisible ``` ## Trajectory toolbox: joining and subsetting The `join(...)` method is very useful to concatenate together any number of trajectories. It may be used as a standalone function as follows: ```{r} t1 <- trajectory() %>% seize("dummy", 1) t2 <- trajectory() %>% timeout(1) t3 <- trajectory() %>% release("dummy", 1) t0 <- join(t1, t2, t3) t0 ``` Or it may operate inline, like another activity: ```{r} t0 <- trajectory() %>% join(t1) %>% timeout(1) %>% join(t3) t0 ``` You can think about a trajectory object as a list of activities that has a length ```{r} length(t0) ``` and can be subset using the standard operator `[`. For instance, you can select the activities you want with a logical vector: ```{r} t0[c(TRUE, FALSE, TRUE)] ``` Or a set of indices that respect the order given: ```{r} t0[c(1, 3)] t0[c(3, 1)] ``` Or a set of indices to remove from the selection: ```{r} t0[-2] ``` Or by name, but note that this does **not** respect the order given though, because it performs a match: ```{r} t0[c("seize", "release")] t0[c("release", "seize")] ``` If you provide no indices, the whole trajectory is returned: ```{r} t0[] ``` In fact, you are **cloning** the trajectory with the latter command. It is equivalent to `t0[1:length(t0)]` or `join(t0)`. The generics `head()` and `tail()` use the `[` operator under the hood, thus you can use them as well: ```{r} head(t0, 2) tail(t0, -1) ``` The `[[` operator can also be used to extract **only one** element: ```{r} t0[[2]] ``` which is equivalent to `t0[2]`. If a string is provided, it ensures that only the first match is returned: ```{r} join(t0, t0)["timeout"] join(t0, t0)[["timeout"]] ```