parallel coordinator

36 views
Skip to first unread message

Dan Ng

unread,
Aug 8, 2024, 4:29:08 AM8/8/24
to simmer-devel

Hi Iñaki,

I am trying to simulate patient arrivals to the Emergency Department. Each patient will go through several steps, and at the end, the patient will wait for discharge, which is handled by a consultant.

The hospital has a rule that the length of stay (from arrival to discharge) should be less than N minutes. If the length of stay from arrival to the time the patient is waiting for discharge is approaching N, a coordinator will alert the consultant to prioritize the patient so they can be discharged before N minutes.

To simulate the coordinator, I created a parallel trajectory that acts as a coordinator. This trajectory loops to check the length of stay for each patient waiting to be discharged. When the time is approaching N, the patient's priority is upgraded so they can preempt the consultants.

Below is the code. However, it is not possible to change the patient's priority in the parallel trajectory. Additionally, in the parallel trajectory, it is not possible to send a signal to stop the coordinator from watching. Would you please shed some light on what's wrong? 

From the the example below, patient 2 should be discharged before patient 1: The set_prioritization is excuted but it did not work. 
<pre><code>

library(simmer)
library(simmer.bricks)
library(simmer.plot)
env<-simmer()

coordinator_watch<- trajectory() %>%
  renege_if(function() paste0("STOP",get_attribute(env,"UID")),out=trajectory()%>%log_("stop watch"))%>%
  branch(option= function() ifelse( now(env) - get_attribute(env,"arrival_time") > 10   ,1,0),continue = FALSE,
         trajectory()%>%
              log_("try to increase priority and stop the coordinator watch but it does not work")%>%
              set_prioritization(c(1,NA,F))%>%
              send(function() paste0("STOP",get_attribute(env,"UID")))
           ,tag="one"
  )%>%
  timeout(1)%>% rollback("one")


plot(coordinator_watch)

patient_path <-trajectory() %>%
  seize('consultant',1) %>%
  renege_abort()%>%
  timeout(10) %>%
  release('consultant',1) %>%
  send(function() paste0("STOP",get_attribute(env,"UID")))



ed_path<-trajectory() %>%
  set_attribute("arrival_time", function() now(env)) %>%
  #log_(function() paste0("arrival:",get_attribute(env,"arrival_time")))%>%
  set_attribute("UID",function() rnorm(1,0,1))%>%
  simmer::clone(n=2,
                coordinator_watch,
                patient_path
  ) %>%
  synchronize(wait=FALSE) %>%log_("discharged")
plot(ed_path)
test<-env%>%
  add_resource("consultant", 1,preemptive = TRUE) %>%
  add_generator("patient", ed_path, at(c(1,10,15))) %>%
  run(until=80)
test %>% get_mon_arrivals(per_resource=TRUE)  

</code></pre>

Dan Ng

unread,
Aug 9, 2024, 4:16:49 AM8/9/24
to simmer-devel
Hi  Iñaki,

I just found your article here Queueing Systems • simmer | DES for R (r-simmer.org) and it aligns closely with what I'm trying to achieve. However, I want to modify the model so that when a patient's priority is changed, they can preempt other patients who have already seized the resource. I thought I could accomplish this by setting preemptive=TRUE for the resource. While it technically works, the monitored data doesn't seem to be accurate.

The code below is adapted from your example. I changed the prioritization for the arrival at time 16, so it should preempt the previous one. However, the end_time doesn't seem to be correct. Could you help me understand why this is happening?

[Your code]
env <- simmer()
custom <- trajectory() %>%
  set_attribute("arrival time", function() now(env)) %>%
  renege_if(
    "recompute priority",
    out = trajectory() %>%
      # e.g., increase priority if wait_time < 3
      set_prioritization(function() {
        if ( get_attribute(env,"arrival time") ==16)
          c(1, NA, F)     # only change the priority
        else c(NA, NA, NA) # don't change anything
      }, mod="+") %>%
      # go 2 steps back to renege_if
      rollback("renege"),tag="renege") %>%
  seize("resource") %>%
  renege_abort() %>%
  log_("processing") %>%
  timeout(10) %>%
  # trigger this before releasing the resource
  send("recompute priority") %>%  
  timeout(0) %>%
  release("resource")

test<-env %>%
  add_resource("resource",preemptive = TRUE) %>%
  add_generator("dummy", custom, at(0,8,16)) %>%
  run()

test %>% get_mon_arrivals()  

   name start_time end_time activity_time finished replication
1 dummy0          0       10            10     TRUE           1
2 dummy2         16      30            10     TRUE           1
3 dummy1          8       30            10     TRUE           1

Should the dummy2's end_time be 26?

Thanks in advance!



Iñaki Ucar

unread,
Aug 14, 2024, 9:33:00 AM8/14/24
to simmer...@googlegroups.com
Hi,

On Fri, 9 Aug 2024 at 10:17, Dan Ng <dungngu...@gmail.com> wrote:
Hi  Iñaki,

I just found your article here Queueing Systems • simmer | DES for R (r-simmer.org) and it aligns closely with what I'm trying to achieve. However, I want to modify the model so that when a patient's priority is changed, they can preempt other patients who have already seized the resource. I thought I could accomplish this by setting preemptive=TRUE for the resource. While it technically works, the monitored data doesn't seem to be accurate.

Yes, I was going to point you in that direction. Prioritization values cannot be set from "outside", but each arrival can change its own values using the set_prioritization activity. So a way to do this is to set this reneging mechanism that removes everyone from the queue, they recalculate their values and reenter the queue by calling seize again. Admittedly, this is a bit cumbersome to set up, and we may consider in the future some other mechanism to tell a resource to recompute the arrivals' priorities. But this is how it's done for now.

The code below is adapted from your example. I changed the prioritization for the arrival at time 16, so it should preempt the previous one. However, the end_time doesn't seem to be correct. Could you help me understand why this is happening?

I'm not sure what you are trying to achieve in this example, but basically
  • dummy1 is served at t=10.
  • dummy2 enters the queue at t=16 (the priority is not changed, because that's in the "out" trajectory, when a signal comes.
  • dummy1 finishes the timeout at t=20 and sends a signal. This triggers the priority change.
  • dummy2 increases the priority and preempts dummy1, so dummy2 is served at t=20 before dummy1 leaves the resource.
  • dummy2 spends a timeout of 10 units there and leaves at t=30.
  • dummy1 gets back into the server and leaves also at t=30 (because the first timeout was already completed).
Iñaki
 
--
You received this message because you are subscribed to the Google Groups "simmer-devel" group.
To unsubscribe from this group and stop receiving emails from it, send an email to simmer-devel...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/simmer-devel/48a44bac-afbf-4741-88c0-d2c8853c51d5n%40googlegroups.com.


--
Iñaki Úcar

Dan Ng

unread,
Aug 17, 2024, 4:24:14 AM8/17/24
to simmer-devel

Hi Iñaki,

Thank you so much for your help. I now understand that the signal is sent only from a dummy when the dummy is about to release the resource. In our case, dummy2 meets a condition to have its priority upgraded, but since dummy1 is still holding the resource, the signal isn't sent, preventing dummy2 from preempting dummy1.

What I need is for any dummy to have its priority upgraded at any point in time if it meets certain conditions. It's similar to a real-world scenario where a monitor continuously checks the queue to see if any dummy satisfies the conditions.

I've modified your code by adding a "monitor" trajectory. The monitor's only function is to continuously send out a signal. It seems to solve my problem, but the issue is that I had to use a timeout to repeatedly send the signal from the monitor. Is there a way to do this without relying on the timeout in the loop? thanks alot.


library(simmer)
env <- simmer()
monitor <- trajectory() %>%
  send("recompute priority",tag="send_signal")%>%
  timeout(0.001)%>%   ### THE SMALLER THE TIMEOUT THE MORE ACCURATE BUT  SLOW, HOW CAN WE FIX THIS?
  rollback("send_signal",check=function() ifelse(now(env)<100,TRUE,FALSE))



custom <- trajectory() %>%
  set_attribute("arrival time", function() now(env)) %>%
  renege_if(
    "recompute priority",
    out = trajectory() %>%
      # e.g., increase priority if wait_time < 3
      set_prioritization(function() {
        if ( get_attribute(env,"arrival time") ==16)
          c(1, NA, F)     # only change the priority
        else c(NA, NA, NA) # don't change anything
      }, mod="+") %>%
      # go 2 steps back to renege_if
      rollback("renege"),tag="renege") %>%
  seize("resource") %>%
  renege_abort() %>%
  log_("processing") %>%
  timeout(10) %>%
  # trigger this before releasing the resource

  #send("recompute priority") %>%  
  #timeout(0) %>%


  release("resource")

test<-env %>%
  add_resource("resource",preemptive = TRUE) %>%
  add_generator("dummy", custom, at(0,8,16)) %>%

  add_generator("monitor", monitor, at(0)) %>%


  run()

test %>% get_mon_arrivals()

      name start_time end_time activity_time finished replication
1   dummy0          0   10.000            10     TRUE           1
2   dummy2         16   26.001            10     TRUE           1     : GOOD, dummy2 is upgraded so it preempt dummy1 at the time=16.     26.001        is due to the timeout by the monitor
3   dummy1          8   30.000            10     TRUE           1      : dummy1 is preemtped by dummy2 at time =16, and being resumed at time =26, and finish 4 minutes 
4 monitor0          0  100.000           100     TRUE           1

Iñaki Ucar

unread,
Aug 21, 2024, 7:27:05 AM8/21/24
to simmer...@googlegroups.com
On Sat, 17 Aug 2024 at 10:24, Dan Ng <dungngu...@gmail.com> wrote:

Hi Iñaki,

Thank you so much for your help. I now understand that the signal is sent only from a dummy when the dummy is about to release the resource. In our case, dummy2 meets a condition to have its priority upgraded, but since dummy1 is still holding the resource, the signal isn't sent, preventing dummy2 from preempting dummy1.

What I need is for any dummy to have its priority upgraded at any point in time if it meets certain conditions. It's similar to a real-world scenario where a monitor continuously checks the queue to see if any dummy satisfies the conditions.

I've modified your code by adding a "monitor" trajectory. The monitor's only function is to continuously send out a signal. It seems to solve my problem,

Yeap, that's the way to do it if this is the behavior you want.

but the issue is that I had to use a timeout to repeatedly send the signal from the monitor. Is there a way to do this without relying on the timeout in the loop? thanks alot.

There's no way around this. If you want the monitor to send a signal every t interval of time, then it means that an event is executed every t, and the simulation is slower as t decreases. The best way to do this is with the timeout, as you did, and you'll have to live with a slower simulation.

Iñaki

 

Dan Ng

unread,
Aug 22, 2024, 12:15:30 AM8/22/24
to simmer-devel
Many many thanks for your help! 
Reply all
Reply to author
Forward
0 new messages