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.
Files changed (42) hide show
  1. opentnsim/__init__.py +23 -0
  2. opentnsim/cli.py +31 -0
  3. opentnsim/constants.py +5 -0
  4. opentnsim/core/__init__.py +28 -0
  5. opentnsim/core/container.py +63 -0
  6. opentnsim/core/logutils.py +182 -0
  7. opentnsim/core/misc.py +48 -0
  8. opentnsim/core/movable.py +683 -0
  9. opentnsim/core/plotutils.py +69 -0
  10. opentnsim/core/resource.py +46 -0
  11. opentnsim/core/vessel_properties.py +285 -0
  12. opentnsim/data/Correctionfactors.csv +32 -0
  13. opentnsim/data/KarpovSmoothCurves.csv +30 -0
  14. opentnsim/energy/__init__.py +16 -0
  15. opentnsim/energy/algorithms.py +63 -0
  16. opentnsim/energy/calculations.py +1175 -0
  17. opentnsim/energy/logutils.py +169 -0
  18. opentnsim/energy/mixins.py +1485 -0
  19. opentnsim/fis.py +55 -0
  20. opentnsim/graph/__init__.py +4 -0
  21. opentnsim/graph/mixins.py +818 -0
  22. opentnsim/graph/utils.py +407 -0
  23. opentnsim/lock/__init__.py +24 -0
  24. opentnsim/lock/calculations.py +155 -0
  25. opentnsim/lock/lock.py +3310 -0
  26. opentnsim/lock/lock_chamber.py +1623 -0
  27. opentnsim/lock/logutils.py +311 -0
  28. opentnsim/lock/utils.py +162 -0
  29. opentnsim/model.py +308 -0
  30. opentnsim/output.py +206 -0
  31. opentnsim/plot.py +470 -0
  32. opentnsim/strategy.py +449 -0
  33. opentnsim/utils.py +75 -0
  34. opentnsim/vessel_traffic_service/__init__.py +4 -0
  35. opentnsim/vessel_traffic_service/hydrodanamic_data_manager.py +142 -0
  36. opentnsim/vessel_traffic_service/vessel_traffic_service.py +1609 -0
  37. opentnsim-2.2.2.dist-info/METADATA +189 -0
  38. opentnsim-2.2.2.dist-info/RECORD +42 -0
  39. opentnsim-2.2.2.dist-info/WHEEL +4 -0
  40. opentnsim-2.2.2.dist-info/entry_points.txt +2 -0
  41. opentnsim-2.2.2.dist-info/licenses/AUTHORS.rst +13 -0
  42. 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,5 @@
1
+ """This module contains constants used in the simulation."""
2
+
3
+ # Constants
4
+ knots_to_ms = knots = 0.514444444
5
+ gravitational_acceleration = 9.81
@@ -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