wait.free() is freeing multiple agents instead of 1 - anylogic

I currently have an event that is checked every 30 minutes and calls the function, which is used to check if conditions are met to free an agent Patientthat is in wait1
The event uses the code below and theoretically causes all of the agents in the wait1 to be checked.:
if (wait1.size() > 0){
for (int i = 0; i < wait1.size(); i++)
function(wait1.get(i));}
The function is seen below:
The problem is that although patient should theoretically only go from the wait1 block to the selectOutput3and eventually a seize if they satisfy one of the conditions in the function, it happens that if multiple patient satisfy the same condition, they both get free'd. However, there is only 1 SurgeonMorning and 1 SurgeonAfternoon, so once a patient is freed from the wait, it should seize the surgeon and not allow any of the other patients to be freed.

Yes, this happens because of the order of things happen
Anylogic first does the loop through all the agents in your wait block, and during the loop, the surgeon hasn't been seized yet so idle is always equal to true for all the agents in the wait block
you could so something like this
if (wait1.size() > 0){
for (int i = 0; i < wait1.size(); i++){
if(surgeonMorning.isReserved==false){
surgeonMorning.isReserved=true;
function(wait1.get(i));
}
}
}
then on the release, you can do surgeonMorning.isReserved=false when the time comes.
Nevertheless, your whole model could just be replaced by 1 service block from what I see. You are overdoing the amount of code needed.

Related

remove agent from assembler delay part

I have a problem to manually remove/delete the agents from my assembler block (delay part). I would like to remove/delete the agents in the assembler with the click of a button. I think the problem is that remove() only removes the agents in the queue part of the assembler and not of the delay part right?
The "Reset" button should remove all agents from the two queue blocks and the assembler block.
I wrote the following code in the action of the button. Unfortunately the if condition doesn't work. Nevertheless, the agent remains in the delay part of the assembler. Does anyone have a solution suggestion for me?
while(Lager_Oberteile.size() > 0) {
Agent agent = Lager_Oberteile.removeFirst();
}
while(Lager_Plättchen.size() > 0) {
Agent agent = Lager_Plättchen.removeFirst();
}
if (Klebestation.delaySize() == 1) {
Klebestation.remove(Oberteil_Plättchen_geklebt) ;
}
offene_Aufträge = 0;
Thanks.
First you need to know what agent is in the delay of the assember, one way is to do this:
Agent a=assembler.delayGet(0); //the first agent in the delay
assembler.remove(a);
Done
Another way is to access the Delay within the service within the assembler (these are nested blocks essentially) and remove the agent from the delay:
So to remove the oldest agent in the assembler's delay, you can use
myAssemblerBlock.service.delay.stopDelay(agent);

How to batch and Unbatch agents based on the same value of parameter

I am building simulation model for collection process. The problem I am facing is we have invoices arriving on daily basis. These invoices needs to be combined into single agent based on the account ID . And then needs to be allocated to different people ( 10 Resources ) . I created code based on the answer for other question
Anylogic: how to Batch agents with similar parameters?
But the challenge I have is my batch size is not constant and varies based on the no of invoices received for any account . Below is the code I used but the batch is not created as expected. I just tried to use the same code for my problem. Could you please point out what is wrong here.
I want to un batch these invoice after it is allocated to resource.
for (int i = 0; i < wait.size(); i ++){
Invoice invoice = wait.get(i);
int ID = invoice.Account_ID;
if (!productsWaiting.containsKey(ID)) productsWaiting.put(ID, new ArrayList<Invoice>());
productsWaiting.get(ID).add(invoice);
//Check the batch size if sufficient we release it
if (productsWaiting.get(ID).size() > 1) {
for (Invoice p:productsWaiting.get(ID)) {
wait.free(p);
}
return; // we exit the loop since we have released a batch
}
for (int i = 1; i < 6; i ++) {
if (productsWaiting.get(i) == null) continue;
for (Invoice p:productsWaiting.get(i)) {
wait.free(p);
}
batch.set_batchSize(productsWaiting.get(i).size()); //Since the batch is less than the standard we need to change it to what ever we are releasing
return; // we exit the loop since we have released a batch
}
You can unbatch a batch using the unbatch block from the PML library, but only if you deselected the permanent batch from the batch block
I cant see anything wrong with your code. Can you please advise what is the actual behavior vs the expected behavior? This code will only release a single batch at a time. That is why in the original question you need to call this function at the On Exit code of the Batch block.

How can I have different initial state for one agent type?

I'm working on an epidemic model. In this model, there are 2 types of susceptible states, S1 and S2. At the start of the model, I want there to be agents in S1 state as well as S2 state.
For example, I have total 1000 agents at first, and I want there to be 200 in S1 and 800 in S2.
I tried to set S1 as the start state and create a message transition from S1 to S2, then coded in main as:
for(int i = 0; i < 1000*0.8; i++)
send("s1 to s2", people.random());
But it will cause repeat message to the same agent, thus there won't be 800 in S2 state. Also I think it's not a good design.
You need some code to do selection-without-replacement so you don't choose the same agent multiple times. There's nothing built-in to AnyLogic to do this for you, so you'd want code something like the below (assuming your agents are of type Person).
// Create selection list with all Person agents in
List<Person> selectionList = new ArrayList<Person>();
for (Person p : people) {
selectionList.add(p);
}
// Select one 800 times (removing them from our selection list as we go)
for (int i = 0; i < 800; i++) {
int randomIndex = uniform_discr(0, selectionList.size() - 1);
send ("s1 to s2", selectionList.get(randomIndex));
selectionList.remove(randomIndex);
}
The crucial thing to remember (and something that many AnyLogic modellers don't understand) is that messages to statecharts are processed asynchronously (in hidden timeout 0 events which you can see on the Events View at runtime) however you send them (e.g., whether you use send, receive or the statechart's fireEvent function --- these just affect the number of such intermediate events and whether the "On message received" action of the Connections element is triggered or not).
Thus an agent will never have changed state in a block of code which sends messages to them. (It will only do so after this block of code --- and the current event it is triggered from --- completes.)
P.S. As per your comment to Emile's (incorrect) answer, the other way to do this is via dynamic events so that each message is sent after previous ones have been processed. But then you have to be very careful that you get the at-the-same-sim-time event ordering correct (loads of subtle detail here) and you'd still have to do the filtering in Emile's answer which is very wasteful; much easier to do it the more conceptually correct way where you whittle down the set of agents you're sampling from as you go.

Anylogic: Queue timeout & condition

In the default settings it is possible to set a time in the queue after which an agent leaves the queue via outTimeOut. However, only a fixed time can be entered in the corresponding field, e.g. 12 hours. Is there a possibility to link these 12 hours with a condition? In my case, the agent should only leave the queue after 12 hours via outTimeOut if a certain condition is also met. In my case, if a variable varIN == 1.
Collect the time in queue statistics for each agent. Create a parameter called entryTime. When they enter the block, set agent.entryTime=time();
You can create an event that iterates through the queue every 1 second and removes the agents that meet your conditions from the queue (by using remove(Agent agent) function). That means if (time()-agent.entryTime>12)&&(agent.varIN==1), you will remove that agent.
Loop will look like this:
for (int i=0; i< yourQueue.size(); i++) {
YourAgentType currentAgent = ((YourAgentType)yourQueue.get(i));
if ((time()-currentAgent.entryTime>12)&&(currentAgent.varIN==1)){
yourQueue.remove(currentAgent);
}
}
You can use a function (returning a double) to define and calculate the most complex logic you like. If you provide an argument of type Agent (or the specific agent type flowing through the blocks), it can even account for your agent characteristics.
In the queue block timeout, simply call the function.

Anylogic, mutate the capacity of the resource dynamically

I have a model with a queue and two machines, one of which is used just in case of overcrowding of the queue in front of these resources.
My model has a simple Queue and a Delay block and I tried to mutate the Delay capacity based on a previous queue length using a function like this (written in Delay block capacity text field):
if (queue.size() > 5)
return 2;
else
return 1;
But it doesn't seem to work... is it possible to change the number of resources dynamically based on a condition?
the capacity value in the delay block is only considered in the beginning of the simulation, so it can only be considered as the initial value...
To change the capacity later, you can put some code in the on enter and on exit of the queue block:
delay.set_capacity(queue.size() > 5 ? 2 : 1);
Something like that.