Defining a basic simulation on a real graph#

Imports#

Import the required libraries

import opentnsim
print('This notebook has been tested with OpenTNSim version {}'.format(opentnsim.__version__))
This notebook has been tested with OpenTNSim version 1.3.6
# Used for mathematical functions
# package(s) related to time, space and id
import math             
import datetime
import time
import platform
import random
import os
import pathlib
import warnings

# you need these dependencies (you can get these from anaconda)
# package(s) related to the simulation
import simpy

# spatial libraries 
import pyproj
import shapely.geometry
from simplekml import Kml, Style

# package(s) for data handling
import numpy as np
import matplotlib.pyplot as plt

# OpenTNSIM
import opentnsim
import opentnsim.core as core
import opentnsim.utils


# Used for making the graph to visualize our problem
import networkx as nx  
import pandas as pd

from shapely.errors import ShapelyDeprecationWarning
warnings.filterwarnings("ignore", category=ShapelyDeprecationWarning) 
src_dir = pathlib.Path(opentnsim.__file__).parent.parent

# Graph location
location_graph = src_dir / "notebooks"
name_graph = location_graph / "Shape-Files" / "Rotterdam-Antwerpen-corridor" / "edges_2.shp"

Create graph#

We will now visualize the network based on the file edges_2.shp Important:

If you use windows and get the following error “ImportError: read_shp requires OGR: http://www.gdal.org/”, you probably have this issue. Solving it is possible by running the following commands in your terminal (as explained here:

#Create a new virtual environment
conda create -n testgdal -c conda-forge gdal vs2015_runtime=14

#Activate virtual environment
activate testgdal

#Open Jupyter notebook
jupyer notebook
# Read the shape-file
FG = opentnsim.utils.read_shp(str(name_graph), simplify=True)

# Draw the shape-file to get a first impression
plt.figure(figsize=(18,18))
nx.draw(FG)

# Show the drawing
plt.show()
../_images/08f717b083fa819af0b89ce5d9e8f614d6d978a3463759eb937a823bcfe49f3a.png
# calculate distance between two points
def calculate_distance(orig, dest):
    wgs84 = pyproj.Geod(ellps='WGS84')
    orig = shapely.geometry.shape(orig)
    dest = shapely.geometry.shape(dest)
    
    distance = wgs84.inv(orig.x, orig.y, 
                         dest.x, dest.y)[2]
    
    return distance

H_G = nx.Graph()

lat_lon_to_index = {}
edge_id_counter = 0

for i, node in enumerate(FG.nodes(data = True)):
    H_G.add_node(i, pos = node[0], 
                    name = "Node {}".format(i),
                    geometry = shapely.geometry.Point(node[0][0], node[0][1]))
    lat_lon_to_index[node[0]] = i

    
for edge in FG.edges(data=True): 
    H_G.add_edge(lat_lon_to_index[edge[0]], 
                 lat_lon_to_index[edge[1]],
                 dis= calculate_distance(nx.get_node_attributes(H_G, 'geometry')[lat_lon_to_index[edge[1]]], 
                                         nx.get_node_attributes(H_G, 'geometry')[lat_lon_to_index[edge[0]]]))

FG = H_G.to_directed()
plt.figure(figsize=(18,18))
nx.draw(FG, nx.get_node_attributes(FG, 'pos'), with_labels=True, node_size=0.5, font_size = 2, width = 0.2, arrowsize = 3)
plt.show()
../_images/2723f4d282ab0590e986612d984bbe023d0a639ba2b2939bb8da18da526d3546.png

Create vessels#

Vessel without graph, but with shortest path.

# Make a class out of mix-ins
TransportResource = type(
    'TransportResource', 
                         (core.Identifiable, core.Movable, 
                          core.HasResource, core.Routeable, core.HasContainer, core.ExtraMetadata), {})



# For testing purposes we only want v to be 1
def compute_v_provider(v_empty, v_full):
    return lambda x: 1

data_vessel = {"env": None,
               "name": "Vessel number 1",
               "route": None,
               "geometry": shapely.geometry.Point(4.0092314, 51.4598245),  # lon, lat
               "capacity": 1_000,
               "v": 1}

# create the transport processing resource
vessel = TransportResource(**data_vessel)
/tmp/ipykernel_4293/3728699768.py:21: DeprecationWarning:

Call to deprecated class Routeable. (Use Routable instead of Routeable)

Define paths#

# select 2 nodes on the graph
source = 272
target = 373
path = nx.dijkstra_path(FG, source, target)
print(path)
[272, 209, 208, 210, 278, 277, 354, 353, 373]

Run simulation#

Now we run a simulation for the two constructed paths

def start(env, vessel):
    while True:
        vessel.log_entry("Start sailing", env.now, "", vessel.geometry)
        yield from vessel.move()
        vessel.log_entry("Stop sailing", env.now, "", vessel.geometry)
        print(vessel.geometry)
        if vessel.geometry == nx.get_node_attributes(FG, "geometry")[vessel.route[-1]]:
            break
# Start simpy environment
simulation_start = datetime.datetime.now()
env = simpy.Environment(initial_time = time.mktime(simulation_start.timetuple()))
env.epoch = time.mktime(simulation_start.timetuple())

# Add graph to environment
env.FG = FG

# Add environment and path to the vessel
vessel.env = env
vessel.route = path

# Start the simulation
env.process(start(env, vessel))
env.run()

print("Simulation of path {} took {} seconds".format(path, int(env.now - env.epoch)))
POINT (4.0132076 51.4431779)
Simulation of path [272, 209, 208, 210, 278, 277, 354, 353, 373] took 1881 seconds
/tmp/ipykernel_4293/3716411016.py:3: DeprecationWarning:

Call to deprecated method log_entry. (Use .log_entry_v0 instead)

/tmp/ipykernel_4293/3716411016.py:5: DeprecationWarning:

Call to deprecated method log_entry. (Use .log_entry_v0 instead)

Output#

We can now show the output of the the vessel. It should sail from source to target in about half an hour.

pd.DataFrame(vessel.log)
Timestamp
0 2024-05-15 11:52:44.000000
1 2024-05-15 11:52:44.000000
2 2024-05-15 11:58:04.482582
3 2024-05-15 11:58:04.482582
4 2024-05-15 12:02:52.857297
5 2024-05-15 12:02:52.857297
6 2024-05-15 12:04:54.671188
7 2024-05-15 12:04:54.671188
8 2024-05-15 12:12:12.512212
9 2024-05-15 12:12:12.512212
10 2024-05-15 12:15:44.913521
11 2024-05-15 12:15:44.913521
12 2024-05-15 12:17:44.492497
13 2024-05-15 12:17:44.492497
14 2024-05-15 12:22:11.837196
15 2024-05-15 12:22:11.837196
16 2024-05-15 12:24:05.867350
17 2024-05-15 12:24:05.867350