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

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.

Related

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

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.

Anylogic: Queue TimeOut blocks flow

I have a pretty simple Anylogic DE model where POs are launched regularly, and a certain amount of material gets to the incoming Queue in one shot (See Sample Picture below). Then the Manufacturing process starts using that material at a regular rate, but I want to check if the material in the queue gets outdated, so I'm using the TimeOut option of that queue, in order to scrap the outdated material (older than 40wks).
The problem is that every time that some material gets scrapped through this Timeout exit, the downstream Manufacturing process "stops" pulling more material, instead of continuing, and it does not get restarted until a new batch of material gets received into the Queue.
What am I doing wrong here? Thanks a lot in advance!!
Kindest regards
Your situation is interesting because there doesn't seem to be anything wrong with what you're doing. So even though what you are doing seems to be correct, I will provide you with a workaround. Instead of the Queue block, use a Wait block. You can assign a timeout and link the timeout port just like you did for the queue (seem image at the end of the answer).
In the On Enter field of the wait block (which I will assume is named Fridge), write the following code:
if( MFG.size() < MFG.capacity ) {
self.free(agent);
}
In the On Enter of MFG block write the following:
if( self.size() < self.capacity && Fridge.size() > 0 ) {
Fridge.free(Fridge.get(0));
}
And finally, in the On Exit of your MFG block write the following:
if( Fridge.size() > 0 ) {
Fridge.free(Fridge.get(0));
}
What we are doing in the above, is we are manually pushing the agents. Each time an agent is processed, the model checks if there is capacity to send more, if yes, a new agent is sent.
I know this is an unpleasant workaround, but it provides you with a solution until AnyLogic support can figure it out.

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.

"While loop" not working in my Anylogic model

I have the model which I posted before on Stack. I am currently running the iterations through 5 Flow Chart blocks contain enter block and service block. when agent fill service block 5 in flow chart 5, the exit block should start to fill block one and so on. I have used While infinite loop to loop between the five flow chart blocks but it isn't working.
while(true)
{
for (Curing_Drying currProcess : collection) {
if (currProcess.allowedDay == (int)time(DAY)) {
currProcess.enter.take(agent);
}
}
if (queue10.size() <= Throughtput1){
break;
}
}
Image for further illustration 1
Image for further illustration 2
Wondering if someone can tell me what is wrong in the code.
Based on the description and the pictures provided, it isn't clear why the while loop is necessary. The On exit action is executed for each Agent arrival to the Exit block. It seems that the intention is to find the appropriate Curing_Drying block based on number of days since the model start time? If so, then just iterating through the collection is enough.
Also, it is generally a good practice to provide more meaningful names to collections. Using simply collection doesn't say anything about the contents and can get pretty confusing later on.

Play 1.2.3 framework - Right way to commit transaction

We have a HTTP end-point that takes a long time to run and can also be called concurrently by users. As part of this request, we update the model inside a synchronized block so that other (possibly concurrent) requests pick up that change.
E.g.
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
//Long running operation continues here. It can involve further changes to instance "m"
The reason for the synchronized block is to ensure that even concurrent requests get to pick up the latest status. However, the underlying JPA does not commit my changes (m.save()) until the request is complete. Since this is a long-running request, I do not want to wait until the request is complete and still want to ensure that other callers are notified of the change in status. I tried to call "m.em().flush(); JPA.em().getTransaction().commit();" after m.save(), but that makes the transaction unavailable for the subsequent action as part of the same request. Can I just given "JPA.em().getTransaction().begin();" and let Play handle the transaction from then on? If not, what is the best way to handle this use-case?
UPDATE:
Based on the response, I modified my code as follows:
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
new MyModelUpdateJob(m.id).now();
And in my job, I have the following line:
doJob() {
MyModel m = MyModel.findById(id);
print m.status; //This still prints the old status as-if m.save() had no effect...
}
What am I missing?
Put your update code in a job an call
new MyModelUpdateJob(id).now().get();
thus the update will be done in another transaction that is commited at the end of the job
ouch, as soon as you add more play servers, you will be in trouble. You may want to play with optimistic locking in your example or and I advise against it pessimistic locking....ick.
HOWEVER, looking at your code, maybe read the article Building on Quicksand. I am not sure you need a synchronized block in that case at all...try to go after being idempotent.
In your case if
1. user 1 and user 2 both call that method and it is pending, then it goes to active(Idempotent)
If user 1 or user 2 wins, well that would be like you had the synchronization block anyways.
I am sure however you have a more complex scenario not shown here, BUT READ that article Building on Quicksand as it really changes the traditional way of thinking and is how google and amazon and very large scale systems operate.
Another option for distributed transactions across play servers is zookeeper which the big large nosql guys use BUT only as a last resort ;) ;)
later,
Dean