How to seize multiple resources at the same time

63 views
Skip to first unread message

Dan Ng

unread,
Nov 6, 2022, 11:52:13 PM11/6/22
to simmer-devel
Hi Inaki,
I tried to simulate an operating room where a patient has to seize one bed, one doctor, and one  nurse at the same time. If any resource is not available then the patient will have to wait until all resources are free. 

For example there are 1 doctor, 2 nurses and 3 beds and patients are arriving at 1,2,3,4,5 and the operation time will be 3 minutes.

It can be done if the seize sequence is setup in the order which the least resource is seized first and so on:

library(simmer)
library(simmer.bricks)
library(simmer.plot)

env <- simmer()
get_available<-function(env,resources){
  return(get_capacity(env,resources) - get_server_count(env,resources))
}

pat <- trajectory() %>%
  log_(function() paste0(now(env),": arrived")) %>%
  log_(function() paste0(now(env),": nurse serving = ",get_server_count(env,'nurse'),' bed serving = ', get_server_count(env,'bed'),' doctor serving = ',get_server_count(env,'doctor')))%>%
  log_(function() paste0(now(env),": seizing bed")) %>%
  log_(function() paste0(now(env),": seizing nurse")) %>%
  log_(function() paste0(now(env),": seizing doc")) %>%
  seize('doctor',1) %>% log_(function() paste0(now(env),": seized doctor")) %>%
  seize('nurse',1) %>% log_(function() paste0(now(env),": seized nurse"))  %>%
  seize('bed',1)  %>%  log_(function() paste0(now(env),": seized bed")) %>%
  timeout(3) %>% release('doctor',1) %>% log_(function() paste0(now(env),": released doc")) %>%
  release('nurse',1) %>% log_(function() paste0(now(env),": released nurse")) %>%
  release('bed',1) %>% log_(function() paste0(now(env),": released bed"))

env %>%
  add_resource('doctor',1)%>%
  add_resource('bed',3)%>%
  add_resource('nurse',2)%>%
  add_generator("patient", pat, at(seq(1,5)))
test<-env %>%  run(until=50) %>% get_mon_arrivals()


However, is  there any other way to make sure all resources have to be seized at the same time without using the seize order? 

I tried with a rollback but it does not work properly:

library(simmer)
library(simmer.bricks)
library(simmer.plot)


env <- simmer()

get_available<-function(env,resources){
  return(get_capacity(env,resources) - get_server_count(env,resources))
}

pat <- trajectory() %>%
  log_(function() paste0(now(env),": arrived")) %>%
  log_(function() paste0(now(env),": nurse serving = ",get_server_count(env,'nurse'),' bed serving = ', get_server_count(env,'bed'),' doctor serving = ',get_server_count(env,'doctor')))%>%
  log_(function() paste0(now(env),": seizing bed")) %>%
  log_(function() paste0(now(env),": seizing nurse")) %>%
  log_(function() paste0(now(env),": seizing doc")) %>%
  seize('bed',1)  %>%
  branch(option=function() ifelse(get_available(env,'doctor')==0 | get_available(env,'nurse')==0,1,0),
         continue=TRUE,
         trajectory() %>% log_(function() paste0(now(env),": Doctor and Nurse not available")) %>% release('bed',1) %>% timeout(1) %>% rollback(5,Inf)) %>%
  log_(function() paste0(now(env),": seized bed")) %>%
  seize('doctor',1) %>% log_(function() paste0(now(env),": seized doctor")) %>%
  seize('nurse',1) %>% log_(function() paste0(now(env),": seized nurse"))  %>%
  timeout(3) %>% release('doctor',1) %>% log_(function() paste0(now(env),": released doc")) %>%
  release('bed',1) %>% log_(function() paste0(now(env),": released bed")) %>%
  release('nurse',1) %>% log_(function() paste0(now(env),": released nurse"))

env %>%
  add_resource('doctor',1)%>%
  add_resource('bed',3)%>%
  add_resource('nurse',2)%>%
  add_generator("patient", pat, at(seq(1,5)))
test<-env %>%  run(until=50) %>% get_mon_arrivals()



Thank you!

Iñaki Ucar

unread,
Nov 7, 2022, 5:27:28 AM11/7/22
to simmer...@googlegroups.com
On Mon, 7 Nov 2022 at 05:52, Dan Ng <dungngu...@gmail.com> wrote:
Hi Inaki,
I tried to simulate an operating room where a patient has to seize one bed, one doctor, and one  nurse at the same time. If any resource is not available then the patient will have to wait until all resources are free. 

For example there are 1 doctor, 2 nurses and 3 beds and patients are arriving at 1,2,3,4,5 and the operation time will be 3 minutes.

It can be done if the seize sequence is setup in the order which the least resource is seized first and so on:

If I understand this correctly, you don't want an arrival to queue in the middle of the three seizes. You want to ensure that you can seize all or none, right? We could implement a simmer.brick for this, because it's an interesting pattern. Related to this: https://github.com/r-simmer/simmer.bricks/issues/17

Basically, you need a branch, and check whether get_capacity()-get_server_count() is positive for the three resources.
  • If it is, then you just proceed.
  • If not, you could trap a signal and wait, then rollback to the branch again when the signal is received. The signal should be sent by the arrivals after releasing all resources.
Let me know if this works for you, and then we could implement it in simmer.bricks in a general way.

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/0b11623a-41d9-4c75-b779-41f8f4940b50n%40googlegroups.com.


--
Iñaki Úcar

Dan Ng

unread,
Nov 7, 2022, 5:51:18 PM11/7/22
to simmer-devel
Thank you very much  Iñaki ! With your hints I have tried it and it works :)  
I think this is a very useful function (for example a patient can be only pushed into a operation room when all resources are ready concurently) so it would be great if you could implement it in simmer.bricks. Thank you.

library(simmer)
env <- simmer()
check_resource_available<-function(env,resources){
  return(get_capacity(env,resources) - get_server_count(env,resources)>0)
}
signal<-'go'

pat <- trajectory() %>%
  log_(function() paste0(now(env),": arrived")) %>%
  log_(function() paste0(now(env),": seizing bed/nurse/doc")) %>%
  branch(option=function() ifelse( (check_resource_available(env,'doctor') & check_resource_available(env,'nurse') & check_resource_available(env,'bed') ),0,1),
         continue=c(TRUE),
         trajectory() %>% trap( signal) %>% wait() %>% rollback(4,Inf)
  ) %>%
  seize('bed',1)  %>% log_(function() paste0(now(env),": seized bed")) %>%
  seize('doctor',1) %>% log_(function() paste0(now(env),": seized doctor")) %>%
  seize('nurse',1) %>% log_(function() paste0(now(env),": seized nurse")) %>%  
  timeout(3) %>%
  release('doctor',1) %>% log_(function() paste0(now(env),": released doc")) %>%
  release('bed',1) %>% log_(function() paste0(now(env),": released bed")) %>%
  release('nurse',1) %>% log_(function() paste0(now(env),": released nurse")) %>% send(signal)


env %>%
  add_resource('doctor',1)%>%
  add_resource('bed',3)%>%
  add_resource('nurse',2)%>%
  add_generator("patient", pat, at(seq(1,5)))

test<-env %>%  run(until=20)
test %>% get_mon_arrivals(per_resource=TRUE) 

Dan Ng

unread,
Nov 8, 2022, 11:14:18 PM11/8/22
to simmer-devel
There is a problem with the above code when I added a timeout before seizing bed/nurse/doc (assuming they have to see receptionist in3 minutes):

library(simmer)
env <- simmer()
check_resource_available<-function(env,resources){
  return(get_capacity(env,resources) - get_server_count(env,resources)>0)
}
signal<-'go'

pat <- trajectory() %>%
  log_(function() paste0(now(env),": arrived")) %>%
  timeout(3) %>%

  log_(function() paste0(now(env),": seizing bed/nurse/doc")) %>%
  branch(option=function() ifelse( (check_resource_available(env,'doctor') & check_resource_available(env,'nurse') & check_resource_available(env,'bed') ),0,1),
         continue=c(TRUE),
         trajectory() %>% trap( signal) %>% wait() %>% rollback(4,Inf)
  ) %>%
  seize('bed',1)  %>% log_(function() paste0(now(env),": seized bed")) %>%
  seize('doctor',1) %>% log_(function() paste0(now(env),": seized doctor")) %>%
  seize('nurse',1) %>% log_(function() paste0(now(env),": seized nurse")) %>%  
  timeout(3) %>%
  release('doctor',1) %>% log_(function() paste0(now(env),": released doc")) %>%
  release('bed',1) %>% log_(function() paste0(now(env),": released bed")) %>%
  release('nurse',1) %>% log_(function() paste0(now(env),": released nurse")) %>% send(signal)


env %>%
  add_resource('doctor',1)%>%
  add_resource('bed',3)%>%
  add_resource('nurse',2)%>%
  add_generator("patient", pat, at(seq(1,5)))

test<-env %>%  run(until=20)
test %>% get_mon_arrivals(per_resource=TRUE)


then when a patient arrives at a the righ time (just after the resources are released) then the patient will take the resources even though the others are waiting for the signal.

Does it make sense? 
Thank you.

Iñaki Ucar

unread,
Nov 9, 2022, 5:07:05 AM11/9/22
to simmer...@googlegroups.com
Yes, so activities are executed with a given priority system (see
Table 1 in the JSS paper) that solves many issues with simultaneous
events, but not all. So my recommendation is to avoid integers at all
costs (both for times of arrival and timeouts). Draw those numbers
from random distributions and these problems will disappear.

Iñaki
> To view this discussion on the web visit https://groups.google.com/d/msgid/simmer-devel/e03ca28a-e6e1-4884-924e-e1650cc9a8a4n%40googlegroups.com.



--
Iñaki Úcar

Dan Ng

unread,
Nov 9, 2022, 4:10:27 PM11/9/22
to simmer...@googlegroups.com
Yes I tried with random numbers and the problem solved. Thank you very much Inaki.
Dan

You received this message because you are subscribed to a topic in the Google Groups "simmer-devel" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/simmer-devel/7M52Ivj4p6g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to simmer-devel...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/simmer-devel/CALEXWq1dVAv8e9LwfSxdU2hgj5HTxCGisi7e6c0038Tx_7xvrQ%40mail.gmail.com.

Dan Ng

unread,
Nov 11, 2022, 1:32:40 AM11/11/22
to simmer-devel
Hi Inaki,
I am not sure if there is a bug in simmer but the patient2 in the code below has less activity time than the others (to note: with random numbers of arrivals there is still the same error). Thank you.

library(simmer)
env <- simmer()
check_resource_available<-function(env,resources){
  return(get_capacity(env,resources) - get_server_count(env,resources)>0)
}
signal<-'go'
pat <- trajectory() %>%
  log_(function() paste0(now(env),": arrived")) %>%
  log_(function() paste0(now(env),": seizing bed/nurse/doc")) %>%
  branch(option=function() ifelse( (check_resource_available(env,'doctor') & check_resource_available(env,'nurse') & check_resource_available(env,'bed') ),0,1),
         continue=c(TRUE),
         trajectory() %>% trap(signal) %>% wait() %>% rollback(4,Inf)

  ) %>%
  seize('bed',1)  %>% log_(function() paste0(now(env),": seized bed")) %>%
  seize('doctor',1) %>% log_(function() paste0(now(env),": seized doctor")) %>%
  seize('nurse',1) %>% log_(function() paste0(now(env),": seized nurse")) %>%  
  timeout(3) %>%
  release('doctor',1) %>% log_(function() paste0(now(env),": released doc")) %>% send(signal) %>%
  release('bed',1) %>% log_(function() paste0(now(env),": released bed")) %>% send(signal) %>%

  release('nurse',1) %>% log_(function() paste0(now(env),": released nurse")) %>% send(signal)


env %>%
  add_resource('doctor',2)%>%

  add_resource('bed',3)%>%
  add_resource('nurse',2)%>%
  add_generator("patient", pat, function() 1)

test<-env %>%  run(until=15)
test %>% get_mon_arrivals(per_resource=TRUE) 

Iñaki Ucar

unread,
Nov 11, 2022, 3:30:33 AM11/11/22
to simmer-devel
Try to untrap(signal) after wait(). It could be receiving multiple signals.

Iñaki

Dan Ng

unread,
Nov 12, 2022, 4:37:26 PM11/12/22
to simmer...@googlegroups.com

Dan Ng

unread,
Nov 13, 2022, 3:58:50 AM11/13/22
to simmer...@googlegroups.com
Hi Iñaki,
Is it possible to add up the wait time inside a trap into the activity time ? For example when a patient arrives in an operation room then waits at the "waiting area" until a "corridor" is free then the patient will be laid down in the corridor waiting for 1 bed / 1 doctor / 1 nurse all free then the patient will be moved inside the operation room. Note that the "waiting area" and "corridor" can be considered at the queue size of the operation room, i.e. if the resources are free then the patient will be immediately moved into the operation room without waiting in the corridor. The reason is we need to calculate the waiting time at both "waiting area" and "corridor", respectively.
To do that I consider the corridor as a resource with timeout =0 and queue size = Inf (for waiting area). The following code works well but we can't identify how long the patients are waiting at the waiting area vs corridor because the activity time of the corridor is always equal to zero. Currently if we consider "waiting_area" as the resource (not the queue of the corridor) then we can calculate the waiting time of both the waiting_area and corridor. Thank you!

library(simmer)
env <- simmer()
check_resource_available<-function(env,resources){
  return(get_capacity(env,resources) - get_server_count(env,resources)>0)
}
signal<-'try again for bed/nurse/doctor'
# seize corridor (where queue size of corridor is unlimited, ie. if there is a queue in corridor then patients are waiting outside)
# this works but we can't distinguish time spent outside corridor vs in corridor
pat_v2 <-trajectory() %>%
  log_(function() paste0(now(env),": arrived and seizing corridor")) %>%
  seize('corridor',1) %>%
  log_(function() paste0(now(env),": corridor seized")) %>%
  #then immediately try to seize cubical/nurse/doc at the same time
  log_(function() paste0(now(env),": seizing cubical/nurse/doc")) %>%
  branch(option=function() ifelse( (check_resource_available(env,'doctor') & check_resource_available(env,'nurse') & check_resource_available(env,'cubical') ),0,1),
         continue=c(TRUE),
         trajectory() %>% trap(signal) %>% wait() %>% untrap(signal) %>% rollback(5,Inf)
  ) %>%
  #only release corridor if cubical/nurse/doc are free altogether
  release('corridor',1) %>% log_(function() paste0(now(env),": corridor released")) %>%
  seize('cubical',1)  %>% log_(function() paste0(now(env),": seized cubical")) %>%
  seize('doctor',1) %>% log_(function() paste0(now(env),": seized doctor")) %>%
  seize('nurse',1) %>% log_(function() paste0(now(env),": seized nurse")) %>%  
  timeout(4) %>%
  release('doctor',1) %>% log_(function() paste0(now(env),": released doc")) %>% send(signal) %>%
  timeout(1) %>%
  release('nurse',1) %>% log_(function() paste0(now(env),": released nurse")) %>% send(signal) %>%
  release('cubical',1) %>% log_(function() paste0(now(env),": released cubical")) %>% send(signal) 


env %>%
  add_resource('corridor',2,queue_size = Inf)%>%
  add_resource('doctor',1)%>%
  add_resource('cubical',3)%>%
  add_resource('nurse',2)%>%
  add_generator("patient", pat_v2, at(seq(1,7)))

test<-env %>%  run()
test %>% get_mon_arrivals(per_resource=TRUE)  %>% arrange(name)


Iñaki Ucar

unread,
Nov 18, 2022, 2:23:27 PM11/18/22
to simmer...@googlegroups.com
What about adding a "corridor" resource, with infinite capacity, that you seize-release before-after the wait()?



--
Iñaki Úcar

Dan Ng

unread,
Nov 19, 2022, 6:58:06 PM11/19/22
to simmer...@googlegroups.com
Thanks Inaki! 
Yes I did it that way and it works but it will take more code lines :)
Thank you again for your time ! 
PS. I love the package.

Reply all
Reply to author
Forward
0 new messages