In my simulation, certain resources are on a capacity schedule, which alternates between 0 and 1 based on the time of day. If a resource cannot complete its task by the time its capacity goes to zero, the arrival should be dropped, wait a short period, and rollback to try to select an available resource. If no resource is available, the arrival goes through a timeout --> rollback loop until a resource becomes available.
The relevant code is as follows:
# Use a reject trajectory used to go back and select a new resource if the
# current resource runs out of time (i.e., its shift ends)
... |>
simmer::seize_selected(
continue = TRUE,
reject = simmer::trajectory('dropped') |>
simmer::timeout(\() runif(1, min = 0.07, max = 0.1)) |>
simmer::rollback(target = 'select_resource')
) |>
# Clear the queue of the selected resource when its capacity is zero
simmer::renege_if(
signal = 'clear_queue'
) |>
simmer::send(
signals = \() {
cap <- simmer::get_capacity_selected(env)
res_name <- simmer::get_selected(env)
now <- simmer::now(env)
queue_count <- simmer::get_queue_count_selected(env)
if (res_name == 'Operator_Bill' & now > 41 & queue_count > 0) {
browser()
}
if (cap == 0) {
'clear_queue'
} else {
''
}
}
) |> ...
The debug condition is never triggered for some reason, but on inspecting mon_resources it shows that arrivals are being served/in queue when capacity == 0. What would be the correct code to handle this situation?
Here's a simplified example of your question:
library(simmer)
t <- trajectory() %>%
seize("res") %>%
timeout(Inf)
simmer() %>%
add_resource("res", capacity=schedule(c(0, 5), c(1, 0))) %>%
add_generator("dummy", t, at(0)) %>%
run(10)
#> simmer environment: anonymous | now: 10 | next: Inf
#> { Monitor: in memory }
#> { Resource: res | monitored: TRUE | server status: 1(0) | queue status: 0(Inf) }
#> { Source: dummy | monitored: 1 | n_generated: 1 }
In the example above, an arrival seizes a resource and stays there. The resource "closes" at t=5
thanks to the specified schedule. But then in the simulation status at t=10
we can see that the arrival is still in the server. And this is what you are trying to achieve:
t <- trajectory() %>%
handle_unfinished(trajectory() %>% log_("dropped!")) %>%
seize("res") %>%
timeout(Inf)
simmer() %>%
add_resource("res", capacity=schedule(c(0, 5), c(1, 0)),
queue_size=0, queue_size_strict=TRUE, preemptive=TRUE) %>%
add_generator("dummy", t, at(0)) %>%
run(10)
#> 5: dummy0: dropped!
#> simmer environment: anonymous | now: 5 | next:
#> { Monitor: in memory }
#> { Resource: res | monitored: TRUE | server status: 0(0) | queue status: 0(0) }
#> { Source: dummy | monitored: 1 | n_generated: 1 }
And now the arrival is dropped. Several things to note:
preemptive=TRUE
flag.queue_size=0
, but also queue_size_strict=TRUE
, because preempted arrivals are by default allowed to exceed the queue size. And now the arrival is effectively dropped from the resource.seize
's reject
argument won't work, because the arrival was not rejected: it was dropped after accessing the resource. The last piece of the puzzle is the handle_unfinished()
activity, which was designed to handle these special cases.