Simulating traffic passing multiple locks#

In this notebook, we simulate two locks on a network which vessels from opposing direction shave to pass. Vessels are locked together if they can fit inside the lock, and arrive within the clustering time window.

0. Import libraries#

# package(s) used for creating and geo-locating the graph
import networkx as nx
import pyproj
from shapely.geometry import Point, LineString
from shapely.ops import transform

# package(s) related to the simulation (creating the vessel, running the simulation)
import datetime
import simpy
import opentnsim
from opentnsim.core.logutils import logbook2eventtable
from opentnsim.core.plotutils import generate_vessel_gantt_chart
from scipy.stats import norm, uniform, expon

# import of modules important for locking
from opentnsim.lock import lock as lock_module
from opentnsim.vessel_traffic_service import vessel_traffic_service as vessel_traffic_service_module
from opentnsim.graph import mixins as graph_module
# package(s) needed for inspecting the output
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

print("This notebook is executed with OpenTNSim version {}".format(opentnsim.__version__))
This notebook is executed with OpenTNSim version 1.3.7

1. Define object classes#

# make your preferred Vessel class out of available mix-ins.
Vessel = type(
    "Vessel", 
    (
        lock_module.PassesLockComplex,             # allows to interact with a lock
        opentnsim.core.Identifiable,               # allows to give the object a name and a random ID,
        opentnsim.core.Movable,                    # allows the object to move, with a fixed speed, while logging this activity
        opentnsim.core.VesselProperties,           # allows vessel to have dimensions, namely a length (L), width (B), and draught (T)
        opentnsim.core.ExtraMetadata,              # allow additional information, such as an arrival time (required for passing a lock)
        graph_module.HasMultiDiGraph,           # allow to operate on a graph that can include parallel edges from and to the same nodes
        opentnsim.output.HasOutput,                # allow additional output to be stored
    ), 
    {}
)

2. Create graph#

# define reference systems
wgs84eqd = pyproj.CRS('4087')
wgs84rad = pyproj.CRS('4326')

# define transformer functions
wgs84eqd_to_wgs84rad = pyproj.transformer.Transformer.from_crs(wgs84eqd,wgs84rad,always_xy=True).transform #equidistant wgs84 to radial wgs84
wgs84rad_to_wgs84eqd = pyproj.transformer.Transformer.from_crs(wgs84rad,wgs84eqd,always_xy=True).transform #radial wgs84 to equidistant wgs84

# create a directed graph
graph = nx.DiGraph()

# add nodes
graph.add_node('-2',geometry=transform(wgs84eqd_to_wgs84rad,Point(-350600,0)))
graph.add_node('-1',geometry=transform(wgs84eqd_to_wgs84rad,Point( -15000,0)))
graph.add_node('0',geometry=transform(wgs84eqd_to_wgs84rad,Point(   -5000,0)))
graph.add_node('1',geometry=transform(wgs84eqd_to_wgs84rad,Point(    5000,0)))
graph.add_node('+1',geometry=transform(wgs84eqd_to_wgs84rad,Point(  15000,0)))
graph.add_node('+2',geometry=transform(wgs84eqd_to_wgs84rad,Point( 350600,0)))

# add edges
graph.add_edge('-2','-1', weight=1)
graph.add_edge('-1','-2', weight=1)

graph.add_edge('-1','0', weight=1)
graph.add_edge('0','-1', weight=1)

graph.add_edge('0','1', weight=1)
graph.add_edge('1','0', weight=1)

graph.add_edge('1','+1', weight=1)
graph.add_edge('+1','1', weight=1)

graph.add_edge('+2','+1', weight=1)
graph.add_edge('+1','+2', weight=1)
graph_module.plot_graph(graph)

3. Run simulation#

def generate_vessel(
    env,
    name,
    start_node,
    end_node,
    arrival_time,
    vessel_speed=4,
    vessel_length=100,
    vessel_beam=20,
    vessel_draft=10,
    vessel_type="tanker"):
    """
    Creates and returns a Vessel object with a computed route through the environment graph.

    Parameters:
    ----------
    env : Environment
        The simulation environment containing the graph and other context.
    name : str
        Human readabile identifier for the vessel.
    start_node : str or int
        The starting node in the graph (converted to string).
    end_node : str or int
        The destination node in the graph (converted to string).
    arrival_time : pd.Timestamp
        The scheduled arrival time of the vessel at the start node.
    vessel_speed : float, optional
        Speed of the vessel in knots or simulation units (default is 4).
    vessel_length : float, optional
        Length of the vessel in meters (default is 100).
    vessel_beam : float, optional
        Beam (width) of the vessel in meters (default is 20).
    vessel_draft : float, optional
        Draught (depth below waterline) of the vessel in meters (default is 10).
    vessel_type : str, optional
        Type of vessel (e.g., "tanker", "cargo", "container") (default is "tanker").

    Returns:
    -------
    Vessel or None
        A Vessel object initialized with the given parameters and route.
        Returns None if no valid path exists between start_node and end_node.
    """
    
    # Ensure nodes are strings
    start_node = str(start_node)
    end_node = str(end_node)

    try:
        route = nx.dijkstra_path(env.graph, start_node, end_node)
    except nx.NetworkXNoPath:
        print(f"⚠️ No path from {start_node} to {end_node}. Vessel {name} not created.")
        return None

    geometry = env.graph.nodes[start_node]['geometry']

    data_vessel = {
        "env": env,
        "name": name,
        "geometry": geometry,
        "route": route,
        "v": vessel_speed,
        "L": vessel_length,
        "B": vessel_beam,
        "T": vessel_draft,
        "type": vessel_type,
        "arrival_time": arrival_time,
    }

    vessel = Vessel(**data_vessel)

    return vessel
def generate_vessels_with_distributions(
    env,
    num_vessels,
    start_time,
    arrival_dist_up=None,
    arrival_dist_down=None,
    seed_up=None,
    seed_down=None):
    """
    Generates a list of vessels with interarrival times drawn from specified distributions
    for upward and downward directions. Supports independent seeding for reproducibility.

    Parameters
    ----------
    env : Environment
        The simulation environment containing the graph and vessel context.
    num_vessels : int
        Total number of vessels to generate. Vessels alternate between up and down directions.
    start_time : pd.Timestamp
        The initial timestamp from which vessel arrivals begin.
    arrival_dist_up : callable, optional
        A function returning interarrival times (in minutes) for upward-moving vessels.
        If None, defaults to an exponential distribution with mean 20 minutes.
    arrival_dist_down : callable, optional
        A function returning interarrival times (in minutes) for downward-moving vessels.
        If None, defaults to an exponential distribution with mean 20 minutes.
    seed_up : int or None, optional
        Seed for the random number generator used in upward direction.
    seed_down : int or None, optional
        Seed for the random number generator used in downward direction.

    Returns
    -------
    list of Vessel
        A list of Vessel objects with assigned routes and arrival times.
        Vessels for which no valid path exists are skipped.
    """

    vessels = []

    # Create independent random generators
    rng_up = np.random.default_rng(seed_up)
    rng_down = np.random.default_rng(seed_down)

    # Default to exponential distribution with mean 20 minutes
    if arrival_dist_up is None:
        arrival_dist_up = lambda: rng_up.exponential(scale=20)
    if arrival_dist_down is None:
        arrival_dist_down = lambda: rng_down.exponential(scale=20)

    up_time = start_time
    down_time = start_time

    for i in range(num_vessels):
        if i % 2 == 0:
            # Upward direction: -1 → +1
            start_node, end_node = "-2", "+2"
            delta_minutes = arrival_dist_up()
            arrival_time = up_time + pd.Timedelta(minutes=delta_minutes)
            up_time = arrival_time
        else:
            # Downward direction: +1 → -1
            start_node, end_node = "+2", "-2"
            delta_minutes = arrival_dist_down()
            arrival_time = down_time + pd.Timedelta(minutes=delta_minutes)
            down_time = arrival_time

        vessel = generate_vessel(
            env=env,
            name=f"Vessel {i + 1}",
            start_node=start_node,
            end_node=end_node,
            arrival_time=arrival_time
        )

        if vessel:
            vessels.append(vessel)

    return vessels
def mission(env, vessel):
    """
    Method that defines the mission of the vessel.
    
    In this case: 
        keep moving along the path until its end point is reached
    """
    while True:
        yield from vessel.move()
        
        if vessel.geometry == nx.get_node_attributes(env.graph, "geometry")[vessel.route[-1]]:
            break
# start simpy environment
simulation_start = datetime.datetime(2025, 1, 1, 0, 0, 0)
env = simpy.Environment(initial_time=simulation_start.timestamp())
env.epoch = simulation_start

# add graph to environment
env.graph = graph

# add components important for locking to the environment
env.vessel_traffic_service = vessel_traffic_service_module.VesselTrafficService(graph=graph)

lock_1 = lock_module.IsLockComplex(
    env=env,
    name='Lock_1',
    node_open='-1',
    node_A = '-1',
    node_B = '0',
    distance_lock_doors_A_to_waiting_area_A = 4800,
    distance_lock_doors_B_to_waiting_area_B = 4800,
    distance_from_start_node_to_lock_doors_A = 4800,
    distance_from_end_node_to_lock_doors_B = 4800,
    lock_length = 400,
    lock_width = 50,
    lock_depth = 15,
    levelling_time = 300,
    sailing_distance_to_crossing_point = 1800,
    doors_opening_time= 300,
    doors_closing_time= 300,
    speed_reduction_factor_lock_chamber=0.5,
    sailing_in_time_gap_through_doors = 300,
    sailing_in_speed_sea = 1.5,
    sailing_in_speed_canal = 1.5,
    sailing_out_time_gap_through_doors = 120,
    sailing_time_before_opening_lock_doors = 600,
    sailing_time_before_closing_lock_doors = 120,
    registration_nodes = ['-2','1'],
    predictive=False
)

lock_2 = lock_module.IsLockComplex(
    env=env,
    name='Lock_2',
    node_open='1',
    node_A = '1',
    node_B = '+1',
    distance_lock_doors_A_to_waiting_area_A = 4800,
    distance_lock_doors_B_to_waiting_area_B = 4800,
    distance_from_start_node_to_lock_doors_A = 4800,
    distance_from_end_node_to_lock_doors_B = 4800,
    lock_length = 400,
    lock_width = 50,
    lock_depth = 15,
    levelling_time = 300,
    sailing_distance_to_crossing_point = 1800,
    doors_opening_time= 300,
    doors_closing_time= 300,
    speed_reduction_factor_lock_chamber=0.5,
    sailing_in_time_gap_through_doors = 300,
    sailing_in_speed_sea = 1.5,
    sailing_in_speed_canal = 1.5,
    sailing_out_time_gap_through_doors = 120,
    sailing_time_before_opening_lock_doors = 600,
    sailing_time_before_closing_lock_doors = 120,
    registration_nodes = ['0','+2'],
    predictive=False
)
# create vessels from dict 
data_vessel_1 = {
    "env": env,                                          # needed for simpy simulation
    "name": "Vessel 1",                                  # required by Identifiable
    "geometry": env.graph.nodes['-2']['geometry'],       # required by Locatable
    "route": nx.dijkstra_path(env.graph, "-2", "+2"),    # required by Routeable
    "v": 4,                                              # required by Movable, 4 m/s to check if the distance is covered in the expected time
    "L": 100,                                            # required by VesselProperties, interacts with the lock capacity
    "B": 20,                                             # required by VesselProperties
    "T": 10,                                             # required by VesselProperties
    "type": 'tanker',                                    # required by VesselProperties
    "arrival_time": pd.Timestamp('2025-01-01 00:00:00')  # required by PassesLockComplex
}  
vessel_1 = Vessel(**data_vessel_1)
vessel_1.name = 'Vessel 1'

data_vessel_2 = {
    "env": env,                                          # needed for simpy simulation
    "name": "Vessel 2",                                  # required by Identifiable
    "geometry": env.graph.nodes['+2']['geometry'],       # required by Locatable
    "route": nx.dijkstra_path(env.graph, "+2", "-2"),    # required by Routeable
    "v": 4,                                              # required by Movable, 4 m/s to check if the distance is covered in the expected time
    "L": 100,                                            # required by VesselProperties, interacts with the lock capacity
    "B": 20,                                             # required by VesselProperties
    "T": 10,                                             # required by VesselProperties
    "type": 'tanker',                                    # required by VesselProperties
    "arrival_time": pd.Timestamp('2025-01-01 00:05:00')  # required by PassesLockComplex
}  
vessel_2 = Vessel(**data_vessel_2)
vessel_2.name = 'Vessel 2'

#start the simulation
env.process(mission(env, vessel_1));
env.process(mission(env, vessel_2));

env.run()
df = pd.DataFrame(vessel_1.logbook)
df
Message Timestamp Value Geometry
0 Sailing from node -2 to node -1 start 2025-01-01 00:00:00.000000 0 POINT (-3.149493386123042 0)
1 Sailing from node -2 to node -1 stop 2025-01-01 23:18:20.000000 335600.0 POINT (-0.1347472926179282 0)
2 Sailing from node -1 to node 0 start 2025-01-01 23:18:20.000000 335600.0 POINT (-0.1347472926179282 0)
3 Sailing to first lock doors start 2025-01-01 23:18:20.000000 {'origin': '', 'destination': '', 'route': [],... POINT (-0.1347472926179282 0)
4 Sailing to first lock doors stop 2025-01-01 23:38:20.000000 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0916281589801912 0)
5 Sailing to position in lock start 2025-01-01 23:38:20.000000 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0916281589801912 0)
6 Sailing to position in lock stop 2025-01-01 23:44:00.172786 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0884840554857729 0)
7 Levelling start 2025-01-01 23:49:00.172786 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0884840554857729 0)
8 Levelling stop 2025-01-01 23:54:00.172786 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0884840554857729 0)
9 Sailing to second lock doors start 2025-01-01 23:59:00.172786 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0884840554857729 0)
10 Sailing to second lock doors stop 2025-01-01 23:59:48.768899 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0880348978437131 0)
11 Sailing to lock complex exit start 2025-01-01 23:59:48.768899 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0880348978437131 0)
12 Sailing to lock complex exit stop 2025-01-02 00:19:48.768899 {'origin': '', 'destination': '', 'route': [],... POINT (-0.0449157642059761 0)
13 Sailing from node -1 to node 0 stop 2025-01-02 00:19:48.768899 335600.0 POINT (-0.0449157642059761 0)
14 Sailing from node 0 to node 1 start 2025-01-02 00:19:48.768899 335600.0 POINT (-0.0449157642059761 0)
15 Sailing from node 0 to node 1 stop 2025-01-02 01:01:28.768899 345600.0 POINT (0.0449157642059761 0)
16 Sailing from node 1 to node +1 start 2025-01-02 01:01:28.768899 345600.0 POINT (0.0449157642059761 0)
17 Sailing to first lock doors start 2025-01-02 01:01:28.768899 {'origin': '', 'destination': '', 'route': [],... POINT (0.0449157642059761 0)
18 Sailing to first lock doors stop 2025-01-02 01:21:28.768899 {'origin': '', 'destination': '', 'route': [],... POINT (0.0880348978437131 0)
19 Sailing to position in lock start 2025-01-02 01:21:28.768899 {'origin': '', 'destination': '', 'route': [],... POINT (0.0880348978437131 0)
20 Sailing to position in lock stop 2025-01-02 01:27:08.941685 {'origin': '', 'destination': '', 'route': [],... POINT (0.0911790013381314 0)
21 Levelling start 2025-01-02 01:32:09.000000 {'origin': '', 'destination': '', 'route': [],... POINT (0.0911790013381314 0)
22 Levelling stop 2025-01-02 01:37:09.000000 {'origin': '', 'destination': '', 'route': [],... POINT (0.0911790013381314 0)
23 Sailing to second lock doors start 2025-01-02 01:42:09.000000 {'origin': '', 'destination': '', 'route': [],... POINT (0.0911790013381314 0)
24 Sailing to second lock doors stop 2025-01-02 01:42:57.596112 {'origin': '', 'destination': '', 'route': [],... POINT (0.0916281589801912 0)
25 Sailing to lock complex exit start 2025-01-02 01:42:57.596112 {'origin': '', 'destination': '', 'route': [],... POINT (0.0916281589801912 0)
26 Sailing to lock complex exit stop 2025-01-02 02:02:57.596112 {'origin': '', 'destination': '', 'route': [],... POINT (0.1347472926179282 0)
27 Sailing from node 1 to node +1 stop 2025-01-02 02:02:57.596112 345600.0 POINT (0.1347472926179282 0)
28 Sailing from node +1 to node +2 start 2025-01-02 02:02:57.596112 345600.0 POINT (0.1347472926179282 0)
29 Sailing from node +1 to node +2 stop 2025-01-03 01:21:17.596112 681200.0 POINT (3.149493386123042 0)
vessels = [vessel_1, vessel_2]

4. Inspect output#

# load the logbook data into a dataframe
lock_df = pd.DataFrame.from_dict(lock_1.lock_chamber.logbook)

print("'{}' logbook data:".format(lock_1.name))  
print('')

display(lock_df)
'Lock_1' logbook data:
Message Timestamp Value Geometry
0 Lock doors closing start 2025-01-01 23:44:00.172786 {} -1
1 Lock doors closing stop 2025-01-01 23:49:00.172786 {} -1
2 Lock chamber converting start 2025-01-01 23:49:00.172786 {} -1
3 Lock chamber converting stop 2025-01-01 23:54:00.172786 {} 0
4 Lock doors opening start 2025-01-01 23:54:00.172786 {} 0
5 Lock doors opening stop 2025-01-01 23:59:00.172786 {} 0
6 Lock doors closing start 2025-01-02 01:32:09.000000 {} 0
7 Lock doors closing stop 2025-01-02 01:37:09.000000 {} 0
8 Lock chamber converting start 2025-01-02 01:37:09.000000 {} 0
9 Lock chamber converting stop 2025-01-02 01:42:09.000000 {} -1
10 Lock doors opening start 2025-01-02 01:42:09.000000 {} -1
11 Lock doors opening stop 2025-01-02 01:47:09.000000 {} -1
# load the logbook data into a dataframe
lock_df = pd.DataFrame.from_dict(lock_2.lock_chamber.logbook)

print("'{}' logbook data:".format(lock_2.name))  
print('')

display(lock_df)
'Lock_2' logbook data:
Message Timestamp Value Geometry
0 Lock doors closing start 2025-01-01 00:05:00.000000 {} 1
1 Lock doors closing stop 2025-01-01 00:10:00.000000 {} 1
2 Lock chamber converting start 2025-01-01 00:10:00.000000 {} 1
3 Lock chamber converting stop 2025-01-01 00:15:00.000000 {} +1
4 Lock doors opening start 2025-01-01 00:15:00.000000 {} +1
5 Lock doors opening stop 2025-01-01 00:20:00.000000 {} +1
6 Lock doors closing start 2025-01-01 23:49:00.172786 {} +1
7 Lock doors closing stop 2025-01-01 23:54:00.172786 {} +1
8 Lock chamber converting start 2025-01-01 23:54:00.172786 {} +1
9 Lock chamber converting stop 2025-01-01 23:59:00.172786 {} 1
10 Lock doors opening start 2025-01-01 23:59:00.172786 {} 1
11 Lock doors opening stop 2025-01-02 00:04:00.172786 {} 1
12 Lock doors closing start 2025-01-02 01:27:09.000000 {} 1
13 Lock doors closing stop 2025-01-02 01:32:09.000000 {} 1
14 Lock chamber converting start 2025-01-02 01:32:09.000000 {} 1
15 Lock chamber converting stop 2025-01-02 01:37:09.000000 {} +1
16 Lock doors opening start 2025-01-02 01:37:09.000000 {} +1
17 Lock doors opening stop 2025-01-02 01:42:09.000000 {} +1

Gantt chart of event table#

df_eventtable = opentnsim.core.logutils.logbook2eventtable([*vessels, lock_1.lock_chamber, lock_2.lock_chamber])
fig = generate_vessel_gantt_chart(df_eventtable)

Time-distance diagram of vessels passing the lock and planning info#

def cm_to_pixels(cm):
    return cm * 37.8 # Set figure height to 10 cmfig.update_layout(height=cm_to_pixels(10))

# We can plot the time-distance diagram
fig = lock_1.create_time_distance_plot(vessels = vessels, 
                                       xlimmin = -6050, 
                                       xlimmax = 6050,
                                       ylimmin = pd.Timestamp('2025-01-01 22:00:00'),
                                       ylimmax = pd.Timestamp('2025-01-02 09:00:00'),
                                       method='Plotly')

fig.update_layout(height=cm_to_pixels(20))
def cm_to_pixels(cm):
    return cm * 37.8 # Set figure height to 10 cmfig.update_layout(height=cm_to_pixels(10))

# We can plot the time-distance diagram
fig = lock_2.create_time_distance_plot(vessels = vessels, 
                                       xlimmin = -6050, 
                                       xlimmax = 6050,
                                       ylimmin = pd.Timestamp('2025-01-01 22:00:00'),
                                       ylimmax = pd.Timestamp('2025-01-02 09:00:00'),
                                       method='Plotly')

fig.update_layout(height=cm_to_pixels(20))

Vessel delays: individual delays and overall average#

delays = []
for vessel in vessels:
    vessel_df = pd.DataFrame(vessel.logbook)
    waiting_stop = vessel_df[vessel_df.Message == "Waiting stop"]
    if not waiting_stop.empty:
        delay = (waiting_stop.Timestamp-vessel.metadata["arrival_time"]).iloc[0]
    else:
        delay = pd.Timedelta(seconds=0)
    delays.append(delay)
print(f"The average vessel delay is {np.round(np.average(delays).total_seconds()/60,1)} minutes")
The average vessel delay is 0.0 minutes

Simulated intensity (vessels per hour)#

‘get_vessels_during_leveling’:

  • Identify locking cycles (by looking at the lock logbook)

  • From the vessel list identify which vessels were in the lock during that locking cycle

‘calculate_cycle_looptimes’:

  • Using the info derived from ‘get_vessels_during_leveling’ calculate looptimes

‘calculate_detailed_cycle_time’:

  • using the info from ‘get_vessels_during_leveling’ and ‘calculate_cycle_looptimes’ calculate detailed cycle times

leveling_cycles = opentnsim.lock.logutils.get_vessels_during_leveling(lock_1.lock_chamber, vessels)
looptimes_df = opentnsim.lock.logutils.calculate_cycle_looptimes(leveling_cycles, vessels)
Tc_df = opentnsim.lock.logutils.calculate_detailed_cycle_time(lock_1.lock_chamber, vessels, leveling_cycles)

display(pd.DataFrame(leveling_cycles))
display(looptimes_df)
display(Tc_df)
leveling_start leveling_stop vessels_present
0 2025-01-01 23:49:00.172786 2025-01-01 23:54:00.172786 [Vessel 1, Vessel 2]
1 2025-01-02 01:37:09.000000 2025-01-02 01:42:09.000000 [Vessel 1, Vessel 2]
cycle looptime_seconds
0 1 0.000000
1 2 -7777.596112
t_l_up sum_t_i_up T_close_up T_waterlevel_up T_open_up sum_t_u_up t_l_down sum_t_i_down T_close_down T_waterlevel_down T_open_down sum_t_u_down Tc_seconds up_vessels down_vessels I_s
0 0 6828.941685 300.0 300.0 300.0 6537.423326 -7777.596112 6828.941685 300.0 300.0 300.0 6537.423326 20755.13391 [Vessel 1, Vessel 2] [Vessel 1, Vessel 2] 0.693804
for index, row in Tc_df.iterrows():
    print('Locking cycle {} has an intensity of {:.2f} vessels per hour'.format(index+1, row['I_s']))
Locking cycle 1 has an intensity of 0.69 vessels per hour

Estimated capacity (vessels per hour)#

n_max = 4
vessel_speed_outside_of_lock = 4

# Part III, Ch3, Eq. 3.2 (NB: de helft van de looptime wordt hier effectief geimplementeerd door de sailing to lock te berekenen)
t_sailing_to_lock = lock_1.sailing_distance_to_crossing_point/vessel_speed_outside_of_lock
T_entering = t_sailing_to_lock + (n_max-1)*lock_1.sailing_in_time_gap_through_doors + 50/2

# Part III, Ch3, Eq. 3.3
T_operation = lock_1.lock_chamber.doors_closing_time + lock_1.lock_chamber.levelling_time + lock_1.lock_chamber.doors_opening_time

# Part III, Ch3, Eq. 3.4 (NB: de helft van de looptime wordt hier effectief geimplementeerd door de sailing out of lock te berekenen)
t_sailing_out_of_lock = lock_1.sailing_distance_to_crossing_point/vessel_speed_outside_of_lock
T_exiting = t_sailing_out_of_lock + (n_max-1)*lock_1.sailing_out_time_gap_through_doors + 350/2

# Part III, Ch3, Eq. 3.1
T_locking = T_entering + T_operation + T_exiting
T_c = 2 * T_locking

C_s = 2*n_max / (T_c/3600)

print(f"The capacity of the lock is {np.round(C_s,1)} vessels per hour")
The capacity of the lock is 4.4 vessels per hour
lock_1.vessel_planning.iloc[-1]
id                                        01685b5a-fc5b-4643-85f9-5486b5875a1f
node_from                                                                    0
node_to                                                                     -1
lock_chamber                                                               NaN
L                                                                          100
B                                                                           20
T                                                                           10
operation_index                                                              1
time_of_registration                                2025-01-02 00:24:48.768899
time_of_acceptance                                  2025-01-02 00:24:48.768899
time_potential_lock_door_opening_stop               2025-01-02 01:16:28.768899
time_arrival_at_waiting_area                        2025-01-02 01:06:28.768899
time_arrival_at_lineup_area                                                NaN
time_lock_passing_start                             2025-01-02 01:18:58.768899
time_lock_entry_start                               2025-01-02 01:26:28.768899
time_lock_entry_stop                             2025-01-02 01:32:08.941685470
time_lock_departure_start                        2025-01-02 01:47:08.941685470
time_lock_departure_stop                         2025-01-02 01:47:57.537797822
time_lock_passing_stop                           2025-01-02 01:55:27.537797822
time_potential_lock_door_closure_start           2025-01-02 01:32:08.941685470
direction                                                                  1.0
delay                                                          0 days 00:00:00
Name: 1, dtype: object
lock_1.operation_planning.iloc[1]
node_from                                                                            0
node_to                                                                             -1
direction                                                                            1
lock_chamber                                                                    Lock_1
vessels                                   [<__main__.Vessel object at 0x7f2adb13d6d0>]
capacity_L                                                                         300
capacity_B                                                                          30
time_potential_lock_door_opening_stop                       2025-01-02 01:16:28.768899
time_operation_start                                        2025-01-02 01:18:58.768899
time_entry_start                                            2025-01-02 01:26:28.768899
time_entry_stop                                          2025-01-02 01:32:08.941685470
time_door_closing_start                                  2025-01-02 01:32:08.941685470
time_door_closing_stop                                   2025-01-02 01:37:08.941685470
time_levelling_start                                     2025-01-02 01:37:08.941685470
time_levelling_stop                                      2025-01-02 01:42:08.941685470
time_door_opening_start                                  2025-01-02 01:42:08.941685470
time_door_opening_stop                                   2025-01-02 01:47:08.941685470
time_departure_start                                     2025-01-02 01:47:08.941685470
time_departure_stop                                      2025-01-02 01:47:57.537797822
time_operation_stop                                      2025-01-02 01:55:27.537797822
time_potential_lock_door_closure_start                   2025-01-02 01:49:57.537797822
wlev_A                                                                             NaN
wlev_B                                                                             NaN
maximum_individual_delay                                               0 days 00:00:00
total_delay                                                            0 days 00:00:00
status                                                                     unavailable
Name: 1, dtype: object
lock_1.operation_planning.iloc[1]
node_from                                                                            0
node_to                                                                             -1
direction                                                                            1
lock_chamber                                                                    Lock_1
vessels                                   [<__main__.Vessel object at 0x7f2adb13d6d0>]
capacity_L                                                                         300
capacity_B                                                                          30
time_potential_lock_door_opening_stop                       2025-01-02 01:16:28.768899
time_operation_start                                        2025-01-02 01:18:58.768899
time_entry_start                                            2025-01-02 01:26:28.768899
time_entry_stop                                          2025-01-02 01:32:08.941685470
time_door_closing_start                                  2025-01-02 01:32:08.941685470
time_door_closing_stop                                   2025-01-02 01:37:08.941685470
time_levelling_start                                     2025-01-02 01:37:08.941685470
time_levelling_stop                                      2025-01-02 01:42:08.941685470
time_door_opening_start                                  2025-01-02 01:42:08.941685470
time_door_opening_stop                                   2025-01-02 01:47:08.941685470
time_departure_start                                     2025-01-02 01:47:08.941685470
time_departure_stop                                      2025-01-02 01:47:57.537797822
time_operation_stop                                      2025-01-02 01:55:27.537797822
time_potential_lock_door_closure_start                   2025-01-02 01:49:57.537797822
wlev_A                                                                             NaN
wlev_B                                                                             NaN
maximum_individual_delay                                               0 days 00:00:00
total_delay                                                            0 days 00:00:00
status                                                                     unavailable
Name: 1, dtype: object
vessels[0].__dict__
{'output': {'origin': '',
  'destination': '',
  'route': [],
  'bound': '',
  'anchorage': '',
  'turning_basin': '',
  'terminal': '',
  'berth': '',
  'length': nan,
  'beam': nan,
  'draught': nan,
  'sailing_distance': nan,
  'sailing_time': nan,
  'waiting_time': {'Anchorage': [], 'Terminal': []},
  'turning_times': [],
  '(un)loading_times': [],
  'current_node': '',
  'next_node': '',
  'speed': nan,
  'heading': nan,
  'water_level': nan,
  'MBL': nan,
  'net_ukc': nan,
  'gross_ukc': nan,
  'ship_related_ukc_factors': {},
  'available_water_depth': nan,
  'required_water_depth': nan,
  'limiting current velocity': nan,
  'sailed_routes': [],
  'visited_anchorages': [],
  'visited_turning_basins': [],
  'visited_terminals': [],
  'visited_berths': [],
  'visited_waiting_areas': [],
  'visited_lineup_areas': [],
  'visited_lock_chambers': []},
 'metadata': {'arrival_time': Timestamp('2025-01-01 00:00:00')},
 'type': 'tanker',
 'B': 20,
 'L': 100,
 '_T': 10,
 '_h_min': None,
 'safety_margin': None,
 'h_squat': None,
 'payload': None,
 'vessel_type': None,
 'renewable_fuel_mass': None,
 'renewable_fuel_volume': None,
 'renewable_fuel_required_space': None,
 'bound': 'inbound',
 'env': <simpy.core.Environment at 0x7f2add4d97f0>,
 'logbook': [{'Message': 'Sailing from node -2 to node -1 start',
   'Timestamp': datetime.datetime(2025, 1, 1, 0, 0),
   'Value': 0,
   'Geometry': <POINT (-3.149 0)>},
  {'Message': 'Sailing from node -2 to node -1 stop',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 18, 20),
   'Value': 335600.0,
   'Geometry': <POINT (-0.135 0)>},
  {'Message': 'Sailing from node -1 to node 0 start',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 18, 20),
   'Value': 335600.0,
   'Geometry': <POINT (-0.135 0)>},
  {'Message': 'Sailing to first lock doors start',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 18, 20),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.135 0)>},
  {'Message': 'Sailing to first lock doors stop',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 38, 20),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.092 0)>},
  {'Message': 'Sailing to position in lock start',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 38, 20),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.092 0)>},
  {'Message': 'Sailing to position in lock stop',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 44, 0, 172786),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.088 0)>},
  {'Message': 'Levelling start',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 49, 0, 172786),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.088 0)>},
  {'Message': 'Levelling stop',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 54, 0, 172786),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.088 0)>},
  {'Message': 'Sailing to second lock doors start',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 59, 0, 172786),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.088 0)>},
  {'Message': 'Sailing to second lock doors stop',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 59, 48, 768899),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.088 0)>},
  {'Message': 'Sailing to lock complex exit start',
   'Timestamp': datetime.datetime(2025, 1, 1, 23, 59, 48, 768899),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.088 0)>},
  {'Message': 'Sailing to lock complex exit stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 0, 19, 48, 768899),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (-0.045 0)>},
  {'Message': 'Sailing from node -1 to node 0 stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 0, 19, 48, 768899),
   'Value': 335600.0,
   'Geometry': <POINT (-0.045 0)>},
  {'Message': 'Sailing from node 0 to node 1 start',
   'Timestamp': datetime.datetime(2025, 1, 2, 0, 19, 48, 768899),
   'Value': 335600.0,
   'Geometry': <POINT (-0.045 0)>},
  {'Message': 'Sailing from node 0 to node 1 stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 1, 28, 768899),
   'Value': 345600.0,
   'Geometry': <POINT (0.045 0)>},
  {'Message': 'Sailing from node 1 to node +1 start',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 1, 28, 768899),
   'Value': 345600.0,
   'Geometry': <POINT (0.045 0)>},
  {'Message': 'Sailing to first lock doors start',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 1, 28, 768899),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.045 0)>},
  {'Message': 'Sailing to first lock doors stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 21, 28, 768899),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.088 0)>},
  {'Message': 'Sailing to position in lock start',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 21, 28, 768899),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.088 0)>},
  {'Message': 'Sailing to position in lock stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 27, 8, 941685),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.091 0)>},
  {'Message': 'Levelling start',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 32, 9),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.091 0)>},
  {'Message': 'Levelling stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 37, 9),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.091 0)>},
  {'Message': 'Sailing to second lock doors start',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 42, 9),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.091 0)>},
  {'Message': 'Sailing to second lock doors stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 42, 57, 596112),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.092 0)>},
  {'Message': 'Sailing to lock complex exit start',
   'Timestamp': datetime.datetime(2025, 1, 2, 1, 42, 57, 596112),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.092 0)>},
  {'Message': 'Sailing to lock complex exit stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 2, 2, 57, 596112),
   'Value': {'origin': '',
    'destination': '',
    'route': [],
    'bound': '',
    'anchorage': '',
    'turning_basin': '',
    'terminal': '',
    'berth': '',
    'length': nan,
    'beam': nan,
    'draught': nan,
    'sailing_distance': nan,
    'sailing_time': nan,
    'waiting_time': {'Anchorage': [], 'Terminal': []},
    'turning_times': [],
    '(un)loading_times': [],
    'current_node': '',
    'next_node': '',
    'speed': nan,
    'heading': nan,
    'water_level': nan,
    'MBL': nan,
    'net_ukc': nan,
    'gross_ukc': nan,
    'ship_related_ukc_factors': {},
    'available_water_depth': nan,
    'required_water_depth': nan,
    'limiting current velocity': nan,
    'sailed_routes': [],
    'visited_anchorages': [],
    'visited_turning_basins': [],
    'visited_terminals': [],
    'visited_berths': [],
    'visited_waiting_areas': [],
    'visited_lineup_areas': [],
    'visited_lock_chambers': []},
   'Geometry': <POINT (0.135 0)>},
  {'Message': 'Sailing from node 1 to node +1 stop',
   'Timestamp': datetime.datetime(2025, 1, 2, 2, 2, 57, 596112),
   'Value': 345600.0,
   'Geometry': <POINT (0.135 0)>},
  {'Message': 'Sailing from node +1 to node +2 start',
   'Timestamp': datetime.datetime(2025, 1, 2, 2, 2, 57, 596112),
   'Value': 345600.0,
   'Geometry': <POINT (0.135 0)>},
  {'Message': 'Sailing from node +1 to node +2 stop',
   'Timestamp': datetime.datetime(2025, 1, 3, 1, 21, 17, 596112),
   'Value': 681200.0,
   'Geometry': <POINT (3.149 0)>}],
 'route': ['-2', '-1', '0', '1', '+1', '+2'],
 'position_on_route': 5,
 'complete_path': None,
 'geometry': <POINT (3.149 0)>,
 'node': None,
 'wgs84': Geod(ellps='WGS84'),
 'v': 4,
 'distance': 681200.0,
 'on_pass_node_functions': [<bound method PassesLockComplex.register_to_lock_master of <__main__.Vessel object at 0x7f2adb2fda90>>],
 'on_pass_edge_functions': [<bound method PassesLockComplex.sail_to_waiting_area of <__main__.Vessel object at 0x7f2adb2fda90>>],
 'on_complete_pass_edge_functions': [],
 'on_look_ahead_to_node_functions': [],
 'req': None,
 'resource': None,
 'distance_left_on_edge': 335600.0,
 'name': 'Vessel 1',
 'id': '70723064-4cc5-4bb2-a2c1-11ccf0bec8a4',
 'overruled_speed': Empty DataFrame
 Columns: [Speed]
 Index: [],
 'waiting_area_request': <Request() object at 0x7f2adb3e24e0>,
 'distance_position_from_first_lock_doors': 350.0,
 'position_in_lock': <POINT (0.091 0)>}