This is a special guest blog post by Yash Mishra giving a quick overview of how you can manage errors inside an AnyLogic simulation model. He shows an example of standard error handling architecture available inside Java and gives an example of how you can practically use it inside AnyLogic with a GIS map in trying to find a route. He also shows how you can throw your own errors in the code to create a more robust model that warns you when things are not going as expected.
Note: This post is just a quick overview of error handling. Watch this space for when we will be releasing the Advanced Java for AnyLogic course later this year, which will cover this, and many other topics in more detail. If you are still a beginner, you can use our Java for AnyLogic course to get you started.
While building a simulation model, we often encounter errors. Sometimes they occur when compiling the model, compilation errors, and sometimes during model execution, runtime errors. When these errors occur, we look into the console for error details and work on correcting them. This post is going to focus on how you can handle runtime errors.
Technical note: Errors and Exceptions are two different classes in Java but more on this in the Advanced Java for AnyLogic course. In this post, we will be using errors and exceptions interchangeably for simplicity's sake.
Runtime errors can occur due to various reasons, but when they occur, your model execution will stop immediately. There are some runtime errors like OutOfMemoryError, VirtualMachineError, etc., which are not recoverable. Still, there are many runtime errors that can be handled, like dividingByZero, indexOutOfBounds etc., as well as the famous NullPointerException. Sometimes we don't want the model to fail when these errors occur but rather catch them, handle them and control the following steps once the error occurs.
All such errors can be handled through a powerful mechanism known as exception handling. In Java, it is done through try-and-catch blocks.
try {
//Do some actions that might cause an error, e.g. divide by zero
} catch(Exception e) {
//print cannot divide by zero
}
Just by placing the code that might throw an error inside a try-and-catch block, the model will be able to handle the error itself and avoid the model execution being stopped abruptly.
Some functions in Java will force you to apply error handling. Try typing in the code below and see the compilation error you get. See if you can resolve it using a try-catch method and let us know in the comments if you succeeded ;-)
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
Date myDate = dateFormatter.parse("2021-02-01");
Real-world use case in AnyLogic
Let us dive deeper into the concept and see a real-world implementation of exception handling. Recently I was building a model for an e-commerce company, and within the shipping department, there was a requirement to find the best route for delivering an order. The preference for delivering an order was using a rail route, if available, and if not, then road.
To start the model, I use the map object available in the Space Markup palette.
Since the preferred route is by rail, we selected the Road type to "Rail" on the map object parameters
In order to determine the best route, we needed to get the distance between two locations. On the map, we can calculate the distance between a source and destination address and get a route by using the getDistanceByRoute functionality provided with the map object.
map.getDistanceByRoute(latFrom, lonFrom, latTo, lonTo)
For a simple demo, I would create a button control and apply the function stated above in the on-click section in the button control to find a distance. I will use New York as the source (40.71, 74.00) and Washington D.C. as the destination (38.90, 77.03).
We can see that it returns the distance by rail as per the rail route. That’s awesome. Isn’t it?
Now let’s look at another example where our source is somewhere in the outskirts of Baltimore (39.76, -77.51) and the destination is a remote location near Toronto (43.95, -81.27).
It is showing me a straight line... that doesn’t look like a rail route. Something is not right....
So, when I took a closer look at the map properties, I found that I need to select show error dialog if the rail route doesn’t exist.
However, if I select the show error dialog it will produce an error when no rail route exists which will not solve my problem because when an error is thrown, my model will stop abruptly. While we actually want to find an alternative route by road...
So along with the show error dialog, I need something which handles the error produced by the map object when there is no rail route available. To overcome this we will use the error handling methodology using try-and-catch blocks.
So, in my button control on-click section, I wrote the following code to handle the error and safeguard the model from stopping abruptly.
try {
message = "The Rail distance is: " +
format(map.getDistanceByRoute(39.76, -77.51, 43.95,
-81.27)/1000) + "KM";
} catch(Error | Exception e) {
message = "couldn't find the route by rail, will deliver by road";
}
P.S. the code "Error | Exception" simply means "...catch either Errors or Exceptions". You can add as many throwable class types you want to catch and simply separate them with a "|"
Thanks to error handling, we can keep the model running, but the business needs to deliver the products by road if there is no rail route available. For all practical purposes, a map object in AnyLogic can produce a route either by rail or road but not by both.
So, the first solution that comes to mind is adding another map object and calculating the distance by road from it, in case the first map object throws an error due to the unavailability of the rail route we will then ask the second map object....
Overcoming another roadblock
Adding two map objects on the same agent is not allowed in AnyLogic.
To overcome this, AnyLogic provides a Route Provider object which would use the map object to find the distances.
So, now we can have two route provider objects (which is allowed 😋) on the same agent. One Route Provider looking for a route by rail, another looking for a route by road. This route provider object has a getRoute() function which takes the map object and the source and destination coordinates as input and returns the route as output.
With this route provider sorted, we can set up our desired framework, which first looks for a route by rail and if it doesn’t exist, it will give us a route by road. We can use the try-and-catch error handling methodology within the button control as shown below.
try {
traceln("The distance by Rail is :" +
routeProviderRail.getRoute(map, gisPointSource,
gisPointDestination).length(LENGTH_UNIT_KILOMETER));
} catch(Error | Exception e) {
traceln("The distance by Road is :" +
routeProviderCar.getRoute(map, gisPointSource,
gisPointDestination).length(LENGTH_UNIT_KILOMETER));
}
One may use the gisPoint object available within the Space Markup library instead of location coordinates as used in the code above. It can be very convenient to place them as markers on the map and it automatically picks up the location coordinates from the map object.
Download the example model below or view it on the cloud here
Bonus section
Creating your own errors
Option A: Creating an error inside an agent
We can also explicitly throw an error during runtime by using the error function provided by the AnyLogic Utilities class. This will throw an error that is handled by the AnyLogic engine. When encountered, like any other error, this will also abruptly stop the model if not handled in a try-catch environment.
//generating 1 with 90% and 0 with 10% probability
int x = binomial(0.9, 1);
//if x is 0 produce an error
if(x==0) {
error("value of x is 0, which is invalid. Hence, error");
} else {
traceln("Happy life - No errors!!!");
}
The code above is placed in a cyclic event - as soon as x becomes 0 the model will produce an error and stop abruptly. And, when you review the console you will be able to see the error string mentioned within the if block.
Option B: Creating an error inside a Java class
Naturally, the Utilities class is not available inside a Java class unless you pass some agent as an argument when creating an instance of the Java class, since agents have access to the Utilities class. To create an error or exception inside a Java class you need to not only create a new error class but also "throw" it.
if (fuelLevel < 0) {
throw new RuntimeException("Fuellevel can never be negative");
}
There are multiple types of errors and exceptions available e.g. RuntimeException, ParseError etc.
Summary
In conclusion: being able to handle and create runtime errors inside your simulation is a very useful skill. Some functions require you to handle the errors they might throw, while with others, you might want to catch them and then do some action to give you more insight about the error or even try an alternative action.
Yash Mishra
Yash Mishra is a guest writer for the AnyLogic Modeler. Feel free to connect with him over LinkedIn.
This post is just a quick overview of error handling. Watch this space for when we will be releasing the Advanced Java for AnyLogic course later this year. If you are still a beginner you can check our Java for AnyLogic course to get you started.
What next?
If you liked this post, you are welcome to read more posts by following the links above to similar posts. Why not subscribe to our blog or follow us on any of the social media accounts for future updates. The links are in the Menu bar at the top, or the footer at the bottom. You can also join the mobile app here!
If you want to contact us for some advice, maybe a potential partnership or project or just to say "Hi!", feel free to get in touch here, and we will get back to you soon!