Orchestrating the searoute Python Library from AIMMS
The vast and growing Python ecosystem offers powerful libraries that can add valuable functionality to your Operations Research applications developed in AIMMS. The AIMMS Python-Bridge connects these two environments, providing a crucial two-way link:
Using an AIMMS model from a Python application.
Using Python libraries from an AIMMS application.
This article focuses on the second, more common scenario, demonstrating how to use
the open-source searoute library to enhance a strategic maritime application.
Please refer to the Vessel Scheduling example to follow along with this article.
Exploring the searoute Library
The running example for this article is the Vessel Scheduling application, which uses the Python searoute library for two key purposes:
To calculate more accurate distances between harbors, significantly improving optimization quality.
To visualize realistic vessel routes on a map, providing better user insight.
The Python library searoute accepts coordinates in the order of [longitude, latitude] and
returns a standard geojson object containing the route information.
A typical call within a Python script looks like this:
1origin = [-44.37, -2.565] # Ponta da Madeira, Brazil
2destination = [-74.22, 11.07] # Puerto Prodeco, Colombia
3route = sr.searoute(origin,destination)
The resulting geojson object contains the useful data within its properties and
geometry attributes:
properties.length: The accurate distance in kilometers.geometry.coordinates: The list of[lon, lat]coordinates that describe the route’s waypoints.
In the Vessel Scheduling application, we are
primarily interested in the distance (from properties.length) and the actual
route waypoints (from geometry.coordinates).
Integrating with AIMMS
Utilizing the searoute functionality from AIMMS requires two main setup steps:
specifying the necessary Python packages and establishing the AIMMS-specific linkage.
Dependency Specification
The
pyproject.tomlfile ensures that the correct versions of the required libraries, including the coresearoutelibrary, are loaded automatically:1[project] 2name = "vessel_scheduling_with_sea_route" 3version = "0.1.0" 4description = "Use Python library Sea Route to compute usable sea routes for large vessels" 5requires-python = "==3.13.*" 6dependencies = [ 7 "aimmspy", 8 "pandas>=2.3.2", 9 "searoute", 10]
AIMMS Project Linkage
The AIMMS project must link to the Python code containing the calls to
searoute. Crucially, the pyaimms repository library is essential, as it provides the Python client allowing your script to access and interact with the AIMMS model’s data.To enable the AIMMS compiler to link and scan the Python code (in this case,
leverage-sea-route.py), you use thepy::run_python_scriptstatement:1! Execute the specified Python script 2py::run_python_script("leverage-sea-route.py");
Using searoute to Compute Distances
The difference between a simple geometric Haversine distance and the
searoute distance can be dramatic. For a real-world example, the straight-line
Haversine distance between Caldera (Chile) and Bahia Blanca (Argentina) is only 1520 km,
whereas searoute calculates a more realistic route of 5180 km: a crucial difference
for accurate optimization.
To compute an entire distance matrix, the Python script first retrieves location data from AIMMS,
iterates through all pairs to calculate the routes, and then sends the results back via a pandas DataFrame.
1def searoute_distance_matrix():
2 """Calculates a distance matrix for all locations retrieved from AIMMS and assigns it back."""
3
4 # Step 1: Retrieve location data from the AIMMS model
5 # Gets location index ('i_loc'), latitude, and longitude for all known locations.
6 df_location_overview = aimms_model.multi_data(["i_loc","p_latitude","p_longitude"])
7 distance_mat_list = []
8
9 # ... code for iterating through locations and calculating distances ...
10
11 # Step 2: Build a DataFrame with the calculated distances
12 # Structure columns to match the AIMMS 2D parameter: indices first, then the value.
13 distance_columns = ['i_loc_from','i_loc_to','p_distance_searoute']
14 distance_df = pd.DataFrame(distance_mat_list,columns=distance_columns)
15
16 # Step 3: Pass the DataFrame back to the AIMMS model
17 # Assigns the distance matrix to the parameter 'p_distance_searoute'.
18 aimms_model.p_distance_searoute.assign(distance_df)
Hint
Calculating many routes can be time-consuming. To prevent unnecessary recalculations
during development or multiple runs, the distance matrix is often cached in a
.parquet file within the AIMMS model’s environment.
Visualizing Routes
To obtain the actual path as a list of GPS waypoints between two harbors, an AIMMS procedure is used. This procedure has the following prototype:
1Procedure pr_getRoute {
2 Arguments: (ep_from, ep_to, p_routeLength, s_waypoints, p_latWP, p_lonWP);
Here, the input is ep_from and ep_to (element parameters from the set of named locations s_locations).
The output parameters are:
p_routeLength: The total length of the computed route.s_waypoints,p_latWP,p_lonWP: The unnamed GPS waypoints (index, latitude, longitude) that define the path.
This procedure, which manages the communication, is typically placed in a module.
The actual call to the Python function searoute_route2 is made using the py::run_python_statement:
1! --- External Python Route Calculation ---
2! Prototype searoute_route2 (for reference)
3! def searoute_route2(fromLat:float,fromLon:float,toLat:float,toLon:float,
4! indexName:str,latParName:str,lonParName:str,lenParName:str):
5
6! Prepare Python call string, injecting coordinates and AIMMS output identifiers
7! Five decimals (%12.5n) provides a location precision of roughly 1 meter, sufficient for maritime routes.
8_sp_pyCall := formatString("searoute_route2(%12.5n,%12.5n,%12.5n,%12.5n,\"lpa::i_globWayPoint\",\"lpa::p_globLat\",\"lpa::p_globLon\",\"lpa::p_globRouteLength\")",
9 _p_fromLat, _p_fromLon, _p_toLat, _p_toLon );
10pr_scanPythonSource(); ! Ensure Python environment is ready
11py::run_python_statement(_sp_pyCall); ! Execute the external route calculation
The Python function searoute_route2 performs the route calculation
and uses two separate aimms_model.multi_assign calls to send the results back: the scalar distance and the list of waypoints.
1def searoute_route2(fromLat: float, fromLon: float, toLat: float, toLon: float,
2 indexName: str, latParName: str, lonParName: str, lenParName: str):
3 """Calculates sea route and assigns results to dynamically named AIMMS parameters."""
4
5 origin = [fromLon, fromLat]
6 destination = [toLon, toLat]
7 route = sr.searoute(origin, destination)
8
9 # --- Assign Dynamic Length Parameter ---
10 route_length = route.properties["length"]
11 route_length_df = pd.DataFrame({lenParName: [route_length]})
12 aimms_model.multi_assign(route_length_df, options={"return_type": DataReturnTypes.PANDAS})
13
14 # --- Assign Dynamic Waypoint Parameters ---
15 route_columns = [lonParName, latParName]
16 route_df = pd.DataFrame(route.geometry.coordinates, columns=route_columns)
17
18 # Add dynamic index column.
19 route_df[indexName] = route_df.index
20
21 # Reorder columns: Index column must be first.
22 route_columns = route_df.columns.tolist()
23 route_df = route_df[route_columns[-1:] + route_columns[:-1]]
24
25 # Assign waypoints DataFrame to AIMMS.
26 aimms_model.multi_assign(route_df, options={"return_type": DataReturnTypes.PANDAS})
Once the waypoints for all legs of the optimal solution are computed and concatenated
using a utility AIMMS procedure (e.g., ui::pr_openPageSeaRouteMap),
the application can display a far more realistic route on a map,
offering superior visual insights into the optimized schedule.
The AIMMS procedure ui::pr_openPageSeaRouteMap concatenates the waypoints computed by the searoute library
for each of the routes in the optimal solution.