opentnsim 2.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opentnsim/__init__.py +23 -0
- opentnsim/cli.py +31 -0
- opentnsim/constants.py +5 -0
- opentnsim/core/__init__.py +28 -0
- opentnsim/core/container.py +63 -0
- opentnsim/core/logutils.py +182 -0
- opentnsim/core/misc.py +48 -0
- opentnsim/core/movable.py +683 -0
- opentnsim/core/plotutils.py +69 -0
- opentnsim/core/resource.py +46 -0
- opentnsim/core/vessel_properties.py +285 -0
- opentnsim/data/Correctionfactors.csv +32 -0
- opentnsim/data/KarpovSmoothCurves.csv +30 -0
- opentnsim/energy/__init__.py +16 -0
- opentnsim/energy/algorithms.py +63 -0
- opentnsim/energy/calculations.py +1175 -0
- opentnsim/energy/logutils.py +169 -0
- opentnsim/energy/mixins.py +1485 -0
- opentnsim/fis.py +55 -0
- opentnsim/graph/__init__.py +4 -0
- opentnsim/graph/mixins.py +818 -0
- opentnsim/graph/utils.py +407 -0
- opentnsim/lock/__init__.py +24 -0
- opentnsim/lock/calculations.py +155 -0
- opentnsim/lock/lock.py +3310 -0
- opentnsim/lock/lock_chamber.py +1623 -0
- opentnsim/lock/logutils.py +311 -0
- opentnsim/lock/utils.py +162 -0
- opentnsim/model.py +308 -0
- opentnsim/output.py +206 -0
- opentnsim/plot.py +470 -0
- opentnsim/strategy.py +449 -0
- opentnsim/utils.py +75 -0
- opentnsim/vessel_traffic_service/__init__.py +4 -0
- opentnsim/vessel_traffic_service/hydrodanamic_data_manager.py +142 -0
- opentnsim/vessel_traffic_service/vessel_traffic_service.py +1609 -0
- opentnsim-2.2.2.dist-info/METADATA +189 -0
- opentnsim-2.2.2.dist-info/RECORD +42 -0
- opentnsim-2.2.2.dist-info/WHEEL +4 -0
- opentnsim-2.2.2.dist-info/entry_points.txt +2 -0
- opentnsim-2.2.2.dist-info/licenses/AUTHORS.rst +13 -0
- opentnsim-2.2.2.dist-info/licenses/LICENSE.txt +21 -0
opentnsim/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
3
|
+
|
|
4
|
+
import opentnsim.core as core # now contains mixin class consumes energy, also the modified vessel properties
|
|
5
|
+
import opentnsim.energy as energy
|
|
6
|
+
import opentnsim.graph.mixins as mixins
|
|
7
|
+
import opentnsim.lock as lock
|
|
8
|
+
import opentnsim.model as model
|
|
9
|
+
import opentnsim.plot as plot
|
|
10
|
+
import opentnsim.strategy as strategy
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
"""Top-level package for OpenTNSim."""
|
|
14
|
+
|
|
15
|
+
__author__ = """Mark van Koningsveld"""
|
|
16
|
+
__email__ = "M.vanKoningsveld@tudelft.nl"
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
__version__ = version("opentnsim")
|
|
20
|
+
except PackageNotFoundError:
|
|
21
|
+
# Package is not installed yet (e.g., running tests from source without an install)
|
|
22
|
+
# This value will be replaced by the build system when the package is installed
|
|
23
|
+
__version__ = "uninstalled"
|
opentnsim/cli.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""Console script for opentnsim."""
|
|
4
|
+
# package(s) related to the command line interface
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
import opentnsim.server
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def cli(args=None):
|
|
14
|
+
"""OpenTNSim simulation."""
|
|
15
|
+
click.echo("Replace this message by putting your code into " "opentnsim.cli.main")
|
|
16
|
+
click.echo("See click documentation at http://click.pocoo.org/")
|
|
17
|
+
return 0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@cli.command()
|
|
21
|
+
@click.option("--host", default="0.0.0.0")
|
|
22
|
+
@click.option("--port", default=5000, type=int)
|
|
23
|
+
@click.option("--debug/--no-debug", default=False)
|
|
24
|
+
def serve(host, port, debug, args=None):
|
|
25
|
+
"""Run a flask server with the backend code"""
|
|
26
|
+
app = opentnsim.server.app
|
|
27
|
+
app.run(host=host, debug=debug, port=port)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
sys.exit(cli()) # pragma: no cover
|
opentnsim/constants.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from openclsim.core import Identifiable, Locatable, Log, SimpyObject
|
|
2
|
+
|
|
3
|
+
from .container import HasContainer
|
|
4
|
+
from .misc import ExtraMetadata, Neighbours
|
|
5
|
+
from .movable import ContainerDependentMovable, Movable, Routable, Routeable
|
|
6
|
+
from .resource import HasResource
|
|
7
|
+
from .vessel_properties import HasLength, HasLoad, VesselProperties
|
|
8
|
+
from . import logutils, plotutils
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Identifiable",
|
|
12
|
+
"Locatable",
|
|
13
|
+
"Log",
|
|
14
|
+
"SimpyObject",
|
|
15
|
+
"HasContainer",
|
|
16
|
+
"ExtraMetadata",
|
|
17
|
+
"Neighbours",
|
|
18
|
+
"ContainerDependentMovable",
|
|
19
|
+
"Movable",
|
|
20
|
+
"Routable",
|
|
21
|
+
"Routeable",
|
|
22
|
+
"HasResource",
|
|
23
|
+
"HasLength",
|
|
24
|
+
"HasLoad",
|
|
25
|
+
"VesselProperties",
|
|
26
|
+
"logutils",
|
|
27
|
+
"plotutils",
|
|
28
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mixin classes related to containers.
|
|
3
|
+
|
|
4
|
+
The following classes are provided:
|
|
5
|
+
- HasContainer
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
# package(s) for documentation, debugging, saving and loading
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
# package(s) related to the simulation
|
|
12
|
+
import simpy
|
|
13
|
+
|
|
14
|
+
# use OpenCLSim objects for core objects (identifiable is imported for later use)
|
|
15
|
+
from openclsim.core import SimpyObject
|
|
16
|
+
|
|
17
|
+
# get logger
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class HasContainer(SimpyObject):
|
|
22
|
+
"""Mixin class: Something with a container, modelled as a storage capacity
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
-----------
|
|
26
|
+
capacity: float
|
|
27
|
+
the capacity of the container, which may either be continuous (like water) or discrete (like apples)
|
|
28
|
+
level: int, default=0
|
|
29
|
+
level of the container at the beginning of the simulation
|
|
30
|
+
total_requested: int, default=0
|
|
31
|
+
total amount that has been requested at the beginning of the simulation
|
|
32
|
+
args, kwargs:
|
|
33
|
+
passed to SimpyObject. Must at least contain parameter env: simpy.Environment.
|
|
34
|
+
|
|
35
|
+
Attributes
|
|
36
|
+
-----------
|
|
37
|
+
container: simpy.Container
|
|
38
|
+
the container that is used to limit the amount that can be requested.
|
|
39
|
+
total_requested: int
|
|
40
|
+
total amount that has been requested.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, capacity: float, level: float = 0, total_requested: float = 0, *args, **kwargs):
|
|
44
|
+
super().__init__(*args, **kwargs)
|
|
45
|
+
"""Initialization"""
|
|
46
|
+
self.container = simpy.Container(self.env, capacity, init=level)
|
|
47
|
+
self.total_requested = total_requested
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def is_loaded(self):
|
|
51
|
+
"""Return if the container is loaded"""
|
|
52
|
+
return True if self.container.level > 0 else False
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def filling_degree(self):
|
|
56
|
+
"""return the filling degree of the container"""
|
|
57
|
+
return self.container.level / self.container.capacity
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def max_load(self):
|
|
61
|
+
"""return the maximum cargo to load"""
|
|
62
|
+
# independent of trip
|
|
63
|
+
return self.container.capacity - self.container.level
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core utiltities related to logging.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# %% IMPORT DEPENDENCIES
|
|
6
|
+
# generic
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
from shapely import Point
|
|
10
|
+
|
|
11
|
+
# internal
|
|
12
|
+
import opentnsim.graph.mixins as mixins
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# # %% CONVERT LOG TO EVENT TABLE
|
|
16
|
+
# def logbook2eventtable(objs):
|
|
17
|
+
# """
|
|
18
|
+
# Transform object logbooks into a 'minimum event table'.
|
|
19
|
+
|
|
20
|
+
# Implements the basic 'event table' concept as proposed by Van der Werff:
|
|
21
|
+
|
|
22
|
+
# Van der Werff, S.E., F. Baart and M. van Koningsveld (2025). “Merging Multiple
|
|
23
|
+
# System Perspectives: The Key to Effective Inland Shipping Emission-Reduction
|
|
24
|
+
# Policy Design.” Journal of Marine Science and engineering 13(4), 716.
|
|
25
|
+
# https://doi.org/10.3390/jmse13040716
|
|
26
|
+
|
|
27
|
+
# Van der Werff, S.E., S. Eppenga, A. van der Hout, F. Baart and M. van
|
|
28
|
+
# Koningsveld (2025). “Multi-perspective nautical safety risk assessment of
|
|
29
|
+
# allisions with offshore wind parks.” Applied Ocean Research 158(2025),104564.
|
|
30
|
+
# https://doi.org/10.1016/j.apor.2025.104564
|
|
31
|
+
|
|
32
|
+
# For waterborne traffic over a network, a unique event is defined by:
|
|
33
|
+
# - a unique vessel,
|
|
34
|
+
# - a specific section of the waterway,
|
|
35
|
+
# - a specific time.
|
|
36
|
+
|
|
37
|
+
# Parameters
|
|
38
|
+
# ----------
|
|
39
|
+
# objs: list
|
|
40
|
+
# List of OpenTNSim simulation objects with log information.
|
|
41
|
+
|
|
42
|
+
# Returns
|
|
43
|
+
# -------
|
|
44
|
+
# eventtable: pandas.DataFrame
|
|
45
|
+
# DataFrame with all events from obj.logbook attributes in objs.
|
|
46
|
+
# """
|
|
47
|
+
# # check if all objects have a logbook with expected structure
|
|
48
|
+
# for obj in objs:
|
|
49
|
+
# if (
|
|
50
|
+
# not hasattr(obj, "logbook")
|
|
51
|
+
# or not hasattr(obj, "id")
|
|
52
|
+
# or not hasattr(obj, "name")
|
|
53
|
+
# ):
|
|
54
|
+
# raise ValueError(
|
|
55
|
+
# f"Object {obj} does not have a logbook or id/name attributes."
|
|
56
|
+
# )
|
|
57
|
+
|
|
58
|
+
# # construct all logged events
|
|
59
|
+
# events = []
|
|
60
|
+
# for obj in objs:
|
|
61
|
+
# df = pd.DataFrame.from_dict(obj.logbook)
|
|
62
|
+
|
|
63
|
+
# unique_activities = df["Message"].str.replace(" start", "").str.replace(" stop", "").unique()
|
|
64
|
+
# for activity in unique_activities:
|
|
65
|
+
# start_time = df[df.Message == activity + " start"]["Timestamp"].values[0]
|
|
66
|
+
# stop_time = df[df.Message == activity + " stop"]["Timestamp"].values[0]
|
|
67
|
+
|
|
68
|
+
# start_location = df[df.Message == activity + " start"]["Geometry"].values[0]
|
|
69
|
+
# stop_location = df[df.Message == activity + " stop"]["Geometry"].values[0]
|
|
70
|
+
|
|
71
|
+
# duration_seconds = (stop_time - start_time) / pd.Timedelta(seconds=1)
|
|
72
|
+
|
|
73
|
+
# if isinstance(start_location, Point):
|
|
74
|
+
# distance_meters = opentnsim.graph.calculate_distance(start_location, stop_location)
|
|
75
|
+
# else:
|
|
76
|
+
# distance_meters = None
|
|
77
|
+
|
|
78
|
+
# events.append(
|
|
79
|
+
# {
|
|
80
|
+
# "object id": obj.id,
|
|
81
|
+
# "object name": obj.name,
|
|
82
|
+
# "activity name": activity,
|
|
83
|
+
# "start location": start_location,
|
|
84
|
+
# "stop location": stop_location,
|
|
85
|
+
# "start time": start_time,
|
|
86
|
+
# "stop time": stop_time,
|
|
87
|
+
# "distance (m)": distance_meters,
|
|
88
|
+
# "duration (s)": duration_seconds,
|
|
89
|
+
# }
|
|
90
|
+
# )
|
|
91
|
+
|
|
92
|
+
# # Final DataFrame
|
|
93
|
+
# eventtable = pd.DataFrame(events)
|
|
94
|
+
|
|
95
|
+
# return eventtable
|
|
96
|
+
|
|
97
|
+
def logbook2eventtable(objs):
|
|
98
|
+
"""
|
|
99
|
+
Transform object logbooks into a 'minimum event table'.
|
|
100
|
+
|
|
101
|
+
Implements the basic 'event table' concept as proposed by Van der Werff:
|
|
102
|
+
|
|
103
|
+
Van der Werff, S.E., F. Baart and M. van Koningsveld (2025). “Merging Multiple
|
|
104
|
+
System Perspectives: The Key to Effective Inland Shipping Emission-Reduction
|
|
105
|
+
Policy Design.” Journal of Marine Science and engineering 13(4), 716.
|
|
106
|
+
https://doi.org/10.3390/jmse13040716
|
|
107
|
+
|
|
108
|
+
Van der Werff, S.E., S. Eppenga, A. van der Hout, F. Baart and M. van
|
|
109
|
+
Koningsveld (2025). “Multi-perspective nautical safety risk assessment of
|
|
110
|
+
allisions with offshore wind parks.” Applied Ocean Research 158(2025),104564.
|
|
111
|
+
https://doi.org/10.1016/j.apor.2025.104564
|
|
112
|
+
|
|
113
|
+
For waterborne traffic over a network, a unique event is defined by:
|
|
114
|
+
- a unique vessel,
|
|
115
|
+
- a specific section of the waterway,
|
|
116
|
+
- a specific time.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
objs: list
|
|
121
|
+
List of OpenTNSim simulation objects with log information.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
eventtable: pandas.DataFrame
|
|
126
|
+
DataFrame with all events from obj.logbook attributes in objs.
|
|
127
|
+
"""
|
|
128
|
+
# check if all objects have a logbook with expected structure
|
|
129
|
+
for obj in objs:
|
|
130
|
+
if (
|
|
131
|
+
not hasattr(obj, "logbook")
|
|
132
|
+
or not hasattr(obj, "id")
|
|
133
|
+
or not hasattr(obj, "name")
|
|
134
|
+
):
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f"Object {obj} does not have a logbook or id/name attributes."
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# construct all logged events
|
|
140
|
+
events = []
|
|
141
|
+
for obj in objs:
|
|
142
|
+
df = pd.DataFrame.from_dict(obj.logbook)
|
|
143
|
+
df.sort_values(by="Timestamp", inplace=True)
|
|
144
|
+
|
|
145
|
+
for i in range(0, len(df)):
|
|
146
|
+
start_row = df.iloc[i]
|
|
147
|
+
if start_row["Message"].endswith(" start"):
|
|
148
|
+
activity = start_row["Message"].replace(" start", "")
|
|
149
|
+
else:
|
|
150
|
+
continue # skip non-start messages
|
|
151
|
+
|
|
152
|
+
stop_row = df[(df["Message"] == activity + " stop") & (df["Timestamp"] > df.iloc[i]["Timestamp"])].iloc[0]
|
|
153
|
+
|
|
154
|
+
start_time = start_row["Timestamp"]
|
|
155
|
+
stop_time = stop_row["Timestamp"]
|
|
156
|
+
start_location = start_row["Geometry"]
|
|
157
|
+
stop_location = stop_row["Geometry"]
|
|
158
|
+
|
|
159
|
+
duration_seconds = (stop_time - start_time).total_seconds()
|
|
160
|
+
if isinstance(start_location, Point):
|
|
161
|
+
distance_meters = mixins.calculate_distance(start_location, stop_location)
|
|
162
|
+
else:
|
|
163
|
+
distance_meters = None
|
|
164
|
+
|
|
165
|
+
events.append(
|
|
166
|
+
{
|
|
167
|
+
"object id": obj.id,
|
|
168
|
+
"object name": obj.name,
|
|
169
|
+
"activity name": activity,
|
|
170
|
+
"start location": start_location,
|
|
171
|
+
"stop location": stop_location,
|
|
172
|
+
"start time": start_time,
|
|
173
|
+
"stop time": stop_time,
|
|
174
|
+
"distance (m)": distance_meters,
|
|
175
|
+
"duration (s)": duration_seconds,
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Final DataFrame
|
|
180
|
+
eventtable = pd.DataFrame(events)
|
|
181
|
+
|
|
182
|
+
return eventtable
|
opentnsim/core/misc.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Miscellaneous mixin classes for object in OpenTNSim.
|
|
3
|
+
|
|
4
|
+
The following classes are provided:
|
|
5
|
+
- Neighbours
|
|
6
|
+
- ExtraMetadata
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
# package(s) for documentation, debugging, saving and loading
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Union
|
|
12
|
+
|
|
13
|
+
# math packages
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
# get logger
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Neighbours:
|
|
21
|
+
"""Mixin class: Can be added to a locatable object (list)
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
travel_to: list
|
|
26
|
+
list of locatables to which can be travelled
|
|
27
|
+
args, kwargs:
|
|
28
|
+
passed to SimpyObject. Must at least contain parameter env: simpy.Environment.
|
|
29
|
+
|
|
30
|
+
Attributes
|
|
31
|
+
-----------
|
|
32
|
+
neighbours: list
|
|
33
|
+
list of locatables to which can be travelled.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def ___init(self, travel_to: list, *args, **kwargs):
|
|
37
|
+
super().__init__(*args, **kwargs)
|
|
38
|
+
"""Initialization"""
|
|
39
|
+
self.neighbours = travel_to
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ExtraMetadata:
|
|
43
|
+
"""Mixin class: store all leftover keyword arguments as metadata property (use as last mixin)"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, *args, **kwargs):
|
|
46
|
+
super().__init__(*args)
|
|
47
|
+
# store all other properties as metadata
|
|
48
|
+
self.metadata = kwargs
|