Source code for opentnsim.core

"""Main module."""

# package(s) related to time, space and id
import json
import logging
import uuid
import pathlib
import datetime
import time

# you need these dependencies (you can get these from anaconda)
# package(s) related to the simulation
import simpy
import random
import networkx as nx
import numpy as np
import math
import pandas as pd

# spatial libraries
import pyproj
import shapely.geometry

# additional packages

import opentnsim.energy
import opentnsim.graph_module

logger = logging.getLogger(__name__)


[docs]class SimpyObject: """General object which can be extended by any class requiring a simpy environment - env: a simpy Environment """ def __init__(self, env, *args, **kwargs): super().__init__(*args, **kwargs) self.env = env
[docs]class HasResource(SimpyObject): """Something that has a resource limitation, a resource request must be granted before the object can be used. - nr_resources: nr of requests that can be handled simultaneously """ def __init__(self, nr_resources=1, priority=False, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.resource = ( simpy.PriorityResource(self.env, capacity=nr_resources) if priority else simpy.Resource(self.env, capacity=nr_resources) )
[docs]class Identifiable: """Mixin class: Something that has a name and id - name: a name - id: a unique id generated with uuid """ def __init__(self, name, id=None, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.name = name # generate some id, in this case based on m self.id = id if id else str(uuid.uuid1())
[docs]class Locatable: """Mixin class: Something with a geometry (geojson format) - geometry: can be a point as well as a polygon """ def __init__(self, geometry, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.geometry = geometry self.node = None
[docs]class Neighbours: """Can be added to a locatable object (list) - travel_to: list of locatables to which can be travelled """ def ___init(self, travel_to, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.neighbours = travel_to
[docs]class HasLength(SimpyObject): """Mixin class: Something with a storage capacity capacity: amount the container can hold level: amount the container holds initially total_requested: a counter that helps to prevent over requesting """ def __init__(self, length, remaining_length=0, total_requested=0, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.length = simpy.Container(self.env, capacity=length, init=remaining_length) self.pos_length = simpy.Container( self.env, capacity=length, init=remaining_length )
[docs]class HasContainer(SimpyObject): """Mixin class: Something with a storage capacity capacity: amount the container can hold level: amount the container holds initially container: a simpy object that can hold stuff total_requested: a counter that helps to prevent over requesting""" def __init__(self, capacity, level=0, total_requested=0, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.container = simpy.Container(self.env, capacity, init=level) self.total_requested = total_requested @property def is_loaded(self): return True if self.container.level > 0 else False @property def filling_degree(self): return self.container.level / self.container.capacity
[docs]class Log(SimpyObject): """Mixin class: Something that has logging capability log: log message [format: 'start activity' or 'stop activity'] t: timestamp value: a value can be logged as well geometry: value from locatable (lat, lon)""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.log = {"Message": [], "Timestamp": [], "Value": [], "Geometry": []}
[docs] def log_entry(self, log, t, value, geometry_log): """Log""" self.log["Message"].append(log) self.log["Timestamp"].append(datetime.datetime.fromtimestamp(t)) self.log["Value"].append(value) self.log["Geometry"].append(geometry_log)
[docs] def get_log_as_json(self): json = [] for msg, t, value, geometry_log in zip( self.log["Message"], self.log["Timestamp"], self.log["Value"], self.log["Geometry"], ): json.append( dict(message=msg, time=t, value=value, geometry_log=geometry_log) ) return json
[docs]class VesselProperties: """Mixin class: Something that has vessel properties This mixin is updated to better accommodate the ConsumesEnergy mixin - type: can contain info on vessel type (avv class, cemt_class or other) - B: vessel width - L: vessel length - h_min: vessel minimum water depth, can also be extracted from the network edges if they have the property ['Info']['GeneralDepth'] - T: actual draught - C_B: block coefficient ('fullness') [-] - safety_margin : the water area above the waterway bed reserved to prevent ship grounding due to ship squatting during sailing, the value of safety margin depends on waterway bed material and ship types. For tanker vessel with rocky bed the safety margin is recommended as 0.3 m based on Van Dorsser et al. The value setting for safety margin depends on the risk attitude of the ship captain and shipping companies. - h_squat: the water depth considering ship squatting while the ship moving (if set to False, h_squat is disabled) - payload: cargo load [ton], the actual draught can be determined by knowing payload based on van Dorsser et al's method.(https://www.researchgate.net/publication/344340126_The_effect_of_low_water_on_loading_capacity_of_inland_ships) - vessel_type: vessel type can be selected from "Container","Dry_SH","Dry_DH","Barge","Tanker". ("Dry_SH" means dry bulk single hull, "Dry_DH" means dry bulk double hull), based on van Dorsser et al's paper.(https://www.researchgate.net/publication/344340126_The_effect_of_low_water_on_loading_capacity_of_inland_ships) Alternatively you can specify draught based on filling degree - H_e: vessel height unloaded - H_f: vessel height loaded - T_e: draught unloaded - T_f: draught loaded """ # TODO: add blockage factor S to vessel properties def __init__( self, type, B, L, h_min=None, T=None, C_B=None, H_e=None, H_f=None, T_e=None, T_f=None, safety_margin=None, h_squat=None, payload=None, vessel_type=None, *args, **kwargs, ): super().__init__(*args, **kwargs) """Initialization """ self.type = type self.B = B self.L = L # hidden because these can also computed on the fly self._T = T self._h_min = h_min self.C_B = C_B # alternative options self.H_e = H_e self.H_f = H_f self.T_e = T_e self.T_f = T_f self.safety_margin = safety_margin self.h_squat = h_squat self.payload = payload self.vessel_type = vessel_type @property def T(self): """Compute the actual draught There are 3 ways to get actual draught - by directly providing actual draught values in the notebook - Or by providing ship draughts in fully loaded state and empty state, the actual draught will be computed based on filling degree """ if self._T is not None: # if we were passed a T value, use tha one T = self._T elif self.T_f is not None and self.T_e is not None: # base draught on filling degree T = self.filling_degree * (self.T_f - self.T_e) + self.T_e # elif self.payload is not None and self.vessel_type is not None: # else: # T = opentnsim.strategy.Payload2T(self, Payload_strategy = self.payload, vessel_type = self.vessel_type, bounds=(0, 40)) # this need to be tested # todo: for later possibly include payload2T return T @property def h_min(self): if self._h_min is not None: h_min = self._h_min else: h_min = opentnsim.graph_module.get_minimum_depth( graph=self.env.FG, route=self.route ) return h_min
[docs] def calculate_max_sinkage(self, v, h_0): """Calculate the maximum sinkage of a moving ship the calculation equation is described in Barrass, B. & Derrett, R.'s book (2006), Ship Stability for Masters and Mates, chapter 42. https://doi.org/10.1016/B978-0-08-097093-6.00042-6 some explanation for the variables in the equation: - h_0: water depth - v: ship velocity relative to the water - 150: Here we use the standard width 150 m as the waterway width """ max_sinkage = ( (self.C_B * ((self.B * self._T) / (150 * h_0)) ** 0.81) * ((v * 1.94) ** 2.08) / 20 ) return max_sinkage
[docs] def calculate_h_squat(self, v, h_0): if self.h_squat: h_squat = h_0 - self.calculate_max_sinkage(v, h_0) else: h_squat = h_0 return h_squat
@property def H(self): """Calculate current height based on filling degree""" return self.filling_degree * (self.H_f - self.H_e) + self.H_e
[docs] def get_route( self, origin, destination, graph=None, minWidth=None, minHeight=None, minDepth=None, randomSeed=4, ): """Calculate a path based on vessel restrictions""" graph = graph if graph else self.env.FG minWidth = minWidth if minWidth else 1.1 * self.B minHeight = minHeight if minHeight else 1.1 * self.H minDepth = minDepth if minDepth else 1.1 * self.T # Check if information on restrictions is added to the edges random.seed(randomSeed) edge = random.choice(list(graph.edges(data=True))) edge_attrs = list(edge[2].keys()) # IMPROVE THIS TO CHECK ALL EDGES AND COMBINATIONS OF RESTRICTIONS if all(item in edge_attrs for item in ["Width", "Height", "Depth"]): edges = [] nodes = [] for edge in graph.edges(data=True): if ( edge[2]["Width"] >= minWidth and edge[2]["Height"] >= minHeight and edge[2]["Depth"] >= minDepth ): edges.append(edge) nodes.append(graph.nodes[edge[0]]) nodes.append(graph.nodes[edge[1]]) subGraph = graph.__class__() for node in nodes: subGraph.add_node( node["name"], name=node["name"], geometry=node["geometry"], position=(node["geometry"].x, node["geometry"].y), ) for edge in edges: subGraph.add_edge(edge[0], edge[1], attr_dict=edge[2]) try: return nx.dijkstra_path(subGraph, origin, destination) # return nx.bidirectional_dijkstra(subGraph, origin, destination) except: raise ValueError( "No path was found with the given boundary conditions." ) # If not, return shortest path else: return nx.dijkstra_path(graph, origin, destination)
[docs]class Routeable: """Mixin class: Something with a route (networkx format) - route: a networkx path """ def __init__(self, route, complete_path=None, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.route = route self.complete_path = complete_path
[docs]class IsLockWaitingArea(HasResource, Identifiable, Log): """Mixin class: Something has lock object properties - properties in meters - operation in seconds """ def __init__(self, node, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization """ waiting_area_resources = 100 self.waiting_area = { node: simpy.PriorityResource(self.env, capacity=waiting_area_resources), }
# departure_resources = 4 # self.departure = { # node: simpy.PriorityResource(self.env, capacity=departure_resources), # }
[docs]class IsLockLineUpArea(HasResource, HasLength, Identifiable, Log): """Mixin class: Something has lock object properties - properties in meters - operation in seconds """ def __init__(self, node, lineup_length, *args, **kwargs): super().__init__( length=lineup_length, remaining_length=lineup_length, *args, **kwargs ) """Initialization""" self.lock_queue_length = 0 # Lay-Out self.enter_line_up_area = { node: simpy.PriorityResource(self.env, capacity=1), } self.line_up_area = { node: simpy.PriorityResource(self.env, capacity=100), } self.converting_while_in_line_up_area = { node: simpy.PriorityResource(self.env, capacity=1), } self.pass_line_up_area = { node: simpy.PriorityResource(self.env, capacity=1), }
[docs]class HasLockDoors(SimpyObject): def __init__(self, node_1, node_3, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization """ self.doors_1 = { node_1: simpy.PriorityResource(self.env, capacity=1), } self.doors_2 = { node_3: simpy.PriorityResource(self.env, capacity=1), }
[docs]class IsLock(HasResource, HasLength, HasLockDoors, Identifiable, Log): """Mixin class: Something has lock object properties - properties in meters - operation in seconds """ def __init__( self, node_1, node_2, node_3, lock_length, lock_width, lock_depth, doors_open, doors_close, wlev_dif, disch_coeff, grav_acc, opening_area, opening_depth, simulation_start, operating_time, *args, **kwargs, ): """Initialization""" # Properties self.lock_length = lock_length self.lock_width = lock_width self.lock_depth = lock_depth self.wlev_dif = wlev_dif self.disch_coeff = disch_coeff self.grav_acc = grav_acc self.opening_area = opening_area self.opening_depth = opening_depth self.simulation_start = simulation_start.timestamp() self.operating_time = operating_time # Operating self.doors_open = doors_open self.doors_close = doors_close # Water level assert node_1 != node_3 self.node_1 = node_1 self.node_3 = node_3 self.water_level = random.choice([node_1, node_3]) super().__init__( length=lock_length, remaining_length=lock_length, node_1=node_1, node_3=node_3, *args, **kwargs, )
[docs] def operation_time(self, environment): if type(self.wlev_dif) == list: operating_time = ( 2 * self.lock_width * self.lock_length * abs( self.wlev_dif[1][ np.abs( self.wlev_dif[0] - (environment.now - self.simulation_start) ).argmin() ] ) ) / ( self.disch_coeff * self.opening_area * math.sqrt(2 * self.grav_acc * self.opening_depth) ) elif type(self.wlev_dif) == float or type(self.wlev_dif) == int: operating_time = ( 2 * self.lock_width * self.lock_length * abs(self.wlev_dif) ) / ( self.disch_coeff * self.opening_area * math.sqrt(2 * self.grav_acc * self.opening_depth) ) assert not isinstance( operating_time, complex ), f"operating_time number should not be complex: {operating_time}" return operating_time
[docs] def convert_chamber(self, environment, new_level, number_of_vessels): """Convert the water level""" # Close the doors self.log_entry( "Lock doors closing start", environment.now, number_of_vessels, self.water_level, ) yield environment.timeout(self.doors_close) self.log_entry( "Lock doors closing stop", environment.now, number_of_vessels, self.water_level, ) # Convert the chamber self.log_entry( "Lock chamber converting start", environment.now, number_of_vessels, self.water_level, ) # Water level will shift self.change_water_level(new_level) yield environment.timeout(self.operation_time(environment)) self.log_entry( "Lock chamber converting stop", environment.now, number_of_vessels, self.water_level, ) # Open the doors self.log_entry( "Lock doors opening start", environment.now, number_of_vessels, self.water_level, ) yield environment.timeout(self.doors_open) self.log_entry( "Lock doors opening stop", environment.now, number_of_vessels, self.water_level, )
[docs] def change_water_level(self, side): """Change water level and priorities in queue""" self.water_level = side for request in self.resource.queue: request.priority = -1 if request.priority == 0 else 0 if request.priority == -1: self.resource.queue.insert( 0, self.resource.queue.pop(self.resource.queue.index(request)) ) else: self.resource.queue.insert( -1, self.resource.queue.pop(self.resource.queue.index(request)) )
[docs]class Movable(Locatable, Routeable, Log): """Mixin class: Something can move Used for object that can move with a fixed speed - geometry: point used to track its current location - v: speed """ def __init__(self, v, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.v = v self.edge_functions = [] self.wgs84 = pyproj.Geod(ellps="WGS84")
[docs] def move(self): """determine distance between origin and destination, and yield the time it takes to travel it Assumption is that self.path is in the right order - vessel moves from route[0] to route[-1]. """ self.distance = 0 speed = self.v # Check if vessel is at correct location - if not, move to location if ( self.geometry != nx.get_node_attributes(self.env.FG, "geometry")[self.route[0]] ): orig = self.geometry dest = nx.get_node_attributes(self.env.FG, "geometry")[self.route[0]] logger.debug("Origin: {orig}") logger.debug("Destination: {dest}") self.distance += self.wgs84.inv( shapely.geometry.shape(orig).x, shapely.geometry.shape(orig).y, shapely.geometry.shape(dest).x, shapely.geometry.shape(dest).y, )[2] yield self.env.timeout(self.distance / self.current_speed) self.log_entry("Sailing to start", self.env.now, self.distance, dest) # Move over the path and log every step for node in enumerate(self.route): self.node = node[1] if node[0] + 2 <= len(self.route): origin = self.route[node[0]] destination = self.route[node[0] + 1] if "Waiting area" in self.env.FG.nodes[destination].keys(): locks = self.env.FG.nodes[destination]["Waiting area"] for lock in locks: loc = self.route.index(destination) for r in self.route[loc:]: if "Line-up area" in self.env.FG.nodes[r].keys(): wait_for_waiting_area = self.env.now access_waiting_area = lock.waiting_area[ destination ].request() yield access_waiting_area if wait_for_waiting_area != self.env.now: waiting = self.env.now - wait_for_waiting_area self.log_entry( "Waiting to enter waiting area start", wait_for_waiting_area, 0, nx.get_node_attributes(self.env.FG, "geometry")[ origin ], ) self.log_entry( "Waiting to enter waiting area stop", self.env.now, waiting, nx.get_node_attributes(self.env.FG, "geometry")[ origin ], ) if "Waiting area" in self.env.FG.nodes[origin].keys(): locks = self.env.FG.nodes[origin]["Waiting area"] for lock in locks: loc = self.route.index(origin) for r in self.route[loc:]: if "Line-up area" in self.env.FG.nodes[r].keys(): locks2 = self.env.FG.nodes[r]["Line-up area"] for r2 in self.route[loc:]: if "Lock" in self.env.FG.nodes[r2].keys(): locks3 = self.env.FG.nodes[r2]["Lock"] break self.lock_name = [] for lock3 in locks3: if ( lock3.water_level == self.route[self.route.index(r2) - 1] ): for lock2 in locks2: if lock2.name == lock3.name: if lock2.lock_queue_length == 0: self.lock_name = lock3.name break lock_queue_length = [] if self.lock_name == []: for lock2 in locks2: lock_queue_length.append(lock2.lock_queue_length) self.lock_name = locks2[ lock_queue_length.index(min(lock_queue_length)) ].name for lock2 in locks2: if lock2.name == self.lock_name: lock2.lock_queue_length += 1 for lock2 in locks2: if lock2.name == self.lock_name: self.v = 0.5 * speed break wait_for_lineup_area = self.env.now lock.waiting_area[origin].release(access_waiting_area) if self.route[self.route.index(r2) - 1] == lock3.node_1: if ( lock3.doors_2[lock3.node_3].users != [] and lock3.doors_2[lock3.node_3].users[0].priority == -1 ): if self.L < lock2.length.level + lock3.length.level: access_lineup_length = lock2.length.get(self.L) elif self.L < lock2.length.level: if lock2.length.level == lock2.length.capacity: access_lineup_length = lock2.length.get( self.L ) elif ( lock2.line_up_area[r].users != [] and lock3.length.level < lock2.line_up_area[r].users[0].length ): access_lineup_length = lock2.length.get( self.L ) else: if lock2.length.get_queue == []: access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) else: total_length_waiting_vessels = 0 for q in reversed( range(len(lock2.length.get_queue)) ): if ( lock2.length.get_queue[q].amount == lock2.length.capacity ): break for q2 in range( q, len(lock2.length.get_queue) ): total_length_waiting_vessels += ( lock2.length.get_queue[ q2 ].length ) if ( self.L > lock2.length.capacity - total_length_waiting_vessels ): access_lineup_length = ( lock2.length.get( lock2.length.capacity ) ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) else: access_lineup_length = ( lock2.length.get(self.L) ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length else: if lock2.length.get_queue == []: access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[-1].length = self.L yield access_lineup_length correct_lineup_length = lock2.length.put( lock2.length.capacity - self.L ) else: total_length_waiting_vessels = 0 for q in reversed( range(len(lock2.length.get_queue)) ): if ( lock2.length.get_queue[q].amount == lock2.length.capacity ): break for q2 in range( q, len(lock2.length.get_queue) ): total_length_waiting_vessels += ( lock2.length.get_queue[q2].length ) if ( self.L > lock2.length.capacity - total_length_waiting_vessels ): access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) else: access_lineup_length = lock2.length.get( self.L ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length else: if lock2.length.level == lock2.length.capacity: access_lineup_length = lock2.length.get(self.L) elif ( lock2.line_up_area[r].users != [] and self.L < lock2.line_up_area[r].users[-1].lineup_dist - 0.5 * lock2.line_up_area[r].users[-1].length ): access_lineup_length = lock2.length.get(self.L) else: if lock2.length.get_queue == []: access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[-1].length = self.L yield access_lineup_length correct_lineup_length = lock2.length.put( lock2.length.capacity - self.L ) else: total_length_waiting_vessels = 0 for q in reversed( range(len(lock2.length.get_queue)) ): if ( lock2.length.get_queue[q].amount == lock2.length.capacity ): break for q2 in range( q, len(lock2.length.get_queue) ): total_length_waiting_vessels += ( lock2.length.get_queue[q2].length ) if ( self.L > lock2.length.capacity - total_length_waiting_vessels ): access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) else: access_lineup_length = lock2.length.get( self.L ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length elif self.route[self.route.index(r2) - 1] == lock3.node_3: if ( lock3.doors_1[lock3.node_1].users != [] and lock3.doors_1[lock3.node_1].users[0].priority == -1 ): if self.L < lock2.length.level + lock3.length.level: access_lineup_length = lock2.length.get(self.L) elif self.L < lock2.length.level: if lock2.length.level == lock2.length.capacity: access_lineup_length = lock2.length.get( self.L ) elif ( lock2.line_up_area[r].users != [] and lock3.length.level < lock2.line_up_area[r].users[0].length ): access_lineup_length = lock2.length.get( self.L ) else: if lock2.length.get_queue == []: access_lineup_length = lock2.length.get( lock2.length.capacity ) yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) yield correct_lineup_length else: total_length_waiting_vessels = 0 for q in reversed( range(len(lock2.length.get_queue)) ): if ( lock2.length.get_queue[q].amount == lock2.length.capacity ): break for q2 in range( q, len(lock2.length.get_queue) ): total_length_waiting_vessels += ( lock2.length.get_queue[ q2 ].length ) if ( self.L > lock2.length.capacity - total_length_waiting_vessels ): access_lineup_length = ( lock2.length.get( lock2.length.capacity ) ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) else: access_lineup_length = ( lock2.length.get(self.L) ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length else: if lock2.length.get_queue == []: access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[-1].length = self.L yield access_lineup_length correct_lineup_length = lock2.length.put( lock2.length.capacity - self.L ) else: total_length_waiting_vessels = 0 for q in reversed( range(len(lock2.length.get_queue)) ): if ( lock2.length.get_queue[q].amount == lock2.length.capacity ): break for q2 in range( q, len(lock2.length.get_queue) ): total_length_waiting_vessels += ( lock2.length.get_queue[q2].length ) if ( self.L > lock2.length.capacity - total_length_waiting_vessels ): access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) else: access_lineup_length = lock2.length.get( self.L ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length else: if lock2.length.level == lock2.length.capacity: access_lineup_length = lock2.length.get(self.L) elif ( lock2.line_up_area[r].users != [] and self.L < lock2.line_up_area[r].users[-1].lineup_dist - 0.5 * lock2.line_up_area[r].users[-1].length ): access_lineup_length = lock2.length.get(self.L) else: if lock2.length.get_queue == []: access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[-1].length = self.L yield access_lineup_length correct_lineup_length = lock2.length.put( lock2.length.capacity - self.L ) else: total_length_waiting_vessels = 0 for q in reversed( range(len(lock2.length.get_queue)) ): if ( lock2.length.get_queue[q].amount == lock2.length.capacity ): break for q2 in range( q, len(lock2.length.get_queue) ): total_length_waiting_vessels += ( lock2.length.get_queue[q2].length ) if ( self.L > lock2.length.capacity - total_length_waiting_vessels ): access_lineup_length = lock2.length.get( lock2.length.capacity ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length correct_lineup_length = ( lock2.length.put( lock2.length.capacity - self.L ) ) else: access_lineup_length = lock2.length.get( self.L ) lock2.length.get_queue[ -1 ].length = self.L yield access_lineup_length if len(lock2.line_up_area[r].users) != 0: self.lineup_dist = ( lock2.line_up_area[r].users[-1].lineup_dist - 0.5 * lock2.line_up_area[r].users[-1].length - 0.5 * self.L ) else: self.lineup_dist = lock2.length.capacity - 0.5 * self.L self.wgs84 = pyproj.Geod(ellps="WGS84") [ lineup_area_start_lat, lineup_area_start_lon, lineup_area_stop_lat, lineup_area_stop_lon, ] = [ self.env.FG.nodes[self.route[self.route.index(r)]][ "geometry" ].x, self.env.FG.nodes[self.route[self.route.index(r)]][ "geometry" ].y, self.env.FG.nodes[self.route[self.route.index(r) + 1]][ "geometry" ].x, self.env.FG.nodes[self.route[self.route.index(r) + 1]][ "geometry" ].y, ] fwd_azimuth, _, _ = self.wgs84.inv( lineup_area_start_lat, lineup_area_start_lon, lineup_area_stop_lat, lineup_area_stop_lon, ) [ self.lineup_pos_lat, self.lineup_pos_lon, _, ] = self.wgs84.fwd( self.env.FG.nodes[self.route[self.route.index(r)]][ "geometry" ].x, self.env.FG.nodes[self.route[self.route.index(r)]][ "geometry" ].y, fwd_azimuth, self.lineup_dist, ) access_lineup_area = lock2.line_up_area[r].request() lock2.line_up_area[r].users[-1].length = self.L lock2.line_up_area[r].users[-1].id = self.id lock2.line_up_area[r].users[ -1 ].lineup_pos_lat = self.lineup_pos_lat lock2.line_up_area[r].users[ -1 ].lineup_pos_lon = self.lineup_pos_lon lock2.line_up_area[r].users[ -1 ].lineup_dist = self.lineup_dist lock2.line_up_area[r].users[-1].n = len( lock2.line_up_area[r].users ) lock2.line_up_area[r].users[-1].v = 0.25 * speed lock2.line_up_area[r].users[-1].wait_for_next_cycle = False yield access_lineup_area enter_lineup_length = lock2.enter_line_up_area[r].request() yield enter_lineup_length lock2.enter_line_up_area[r].users[0].id = self.id if wait_for_lineup_area != self.env.now: self.v = 0.25 * speed waiting = self.env.now - wait_for_lineup_area self.log_entry( "Waiting in waiting area start", wait_for_lineup_area, 0, nx.get_node_attributes(self.env.FG, "geometry")[ origin ], ) self.log_entry( "Waiting in waiting area stop", self.env.now, waiting, nx.get_node_attributes(self.env.FG, "geometry")[ origin ], ) break if "Line-up area" in self.env.FG.nodes[destination].keys(): locks = self.env.FG.nodes[destination]["Line-up area"] for lock in locks: if lock.name == self.lock_name: loc = self.route.index(destination) orig = shapely.geometry.Point( self.lineup_pos_lat, self.lineup_pos_lon ) for r in self.route[loc:]: if "Lock" in self.env.FG.nodes[r].keys(): locks = self.env.FG.nodes[r]["Lock"] for lock2 in locks: for q in range( len(lock.line_up_area[destination].users) ): if ( lock.line_up_area[destination].users[q].id == self.id ): if ( self.route[self.route.index(r) - 1] == lock2.node_1 ): if ( lock2.doors_2[lock2.node_3].users != [] and lock2.doors_2[lock2.node_3] .users[0] .priority == -1 ): if q <= 1 and lock.line_up_area[ destination ].users[q].n != lock.line_up_area[ destination ].users[ q ].n - len( lock2.resource.users ): self.lineup_dist = ( lock.length.capacity - 0.5 * self.L ) elif ( self.route[self.route.index(r) - 1] == lock2.node_3 ): if ( lock2.doors_1[lock2.node_1].users != [] and lock2.doors_1[lock2.node_1] .users[0] .priority == -1 ): if q <= 1 and lock.line_up_area[ destination ].users[q].n != lock.line_up_area[ destination ].users[ q ].n - len( lock2.resource.users ): self.lineup_dist = ( lock.length.capacity - 0.5 * self.L ) [ self.lineup_pos_lat, self.lineup_pos_lon, _, ] = self.wgs84.fwd( self.env.FG.nodes[ self.route[ self.route.index(destination) ] ]["geometry"].x, self.env.FG.nodes[ self.route[ self.route.index(destination) ] ]["geometry"].y, fwd_azimuth, self.lineup_dist, ) lock.line_up_area[destination].users[ q ].lineup_pos_lat = self.lineup_pos_lat lock.line_up_area[destination].users[ q ].lineup_pos_lon = self.lineup_pos_lon lock.line_up_area[destination].users[ q ].lineup_dist = self.lineup_dist break if "Line-up area" in self.env.FG.nodes[origin].keys(): locks = self.env.FG.nodes[origin]["Line-up area"] for lock in locks: if lock.name == self.lock_name: loc = self.route.index(origin) orig = shapely.geometry.Point( self.lineup_pos_lat, self.lineup_pos_lon ) for r in self.route[loc:]: if "Lock" in self.env.FG.nodes[r].keys(): locks = self.env.FG.nodes[r]["Lock"] lock.enter_line_up_area[origin].release( enter_lineup_length ) for q in range(len(lock.line_up_area[origin].users)): if lock.line_up_area[origin].users[q].id == self.id: if q > 0: _, _, distance = self.wgs84.inv( orig.x, orig.y, lock.line_up_area[origin] .users[0] .lineup_pos_lat, lock.line_up_area[origin] .users[0] .lineup_pos_lon, ) yield self.env.timeout(distance / self.v) break for lock2 in locks: if lock2.name == self.lock_name: self.v = 0.25 * speed wait_for_lock_entry = self.env.now for r2 in self.route[(loc + 1) :]: if ( "Line-up area" in self.env.FG.nodes[r2].keys() ): locks = self.env.FG.nodes[r2][ "Line-up area" ] for lock3 in locks: if lock3.name == self.lock_name: break break if ( self.route[self.route.index(r) - 1] == lock2.node_1 ): if ( len(lock2.doors_2[lock2.node_3].users) != 0 ): if ( lock2.doors_2[lock2.node_3] .users[0] .priority == -1 ): if ( self.L > ( lock2.resource.users[ -1 ].lock_dist - 0.5 * lock2.resource.users[ -1 ].length ) or lock2.resource.users[ -1 ].converting == True ): access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request(priority=-1) ) yield access_lock_door2 lock2.doors_2[ lock2.node_3 ].release(access_lock_door2) wait_for_next_cycle = ( lock3.pass_line_up_area[ r2 ].request() ) yield wait_for_next_cycle lock3.pass_line_up_area[ r2 ].release(wait_for_next_cycle) if ( lock.converting_while_in_line_up_area[ origin ].users != [] ): waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request( priority=-1 ) yield waiting_during_converting lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) elif ( len( lock2.doors_1[ lock2.node_1 ].users ) == 0 or ( len( lock2.doors_1[ lock2.node_1 ].users ) != 0 and lock2.doors_1[ lock2.node_1 ] .users[0] .priority != -1 ) ) and self.route[ self.route.index(r) - 1 ] != lock2.water_level: waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request() yield waiting_during_converting yield from lock2.convert_chamber( self.env, self.route[ self.route.index(r) - 1 ], 0, ) lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) access_lock_door1 = lock2.doors_1[ lock2.node_1 ].request() yield access_lock_door1 if ( lock2.doors_2[ lock2.node_3 ].users != [] and lock2.doors_2[lock2.node_3] .users[0] .priority == -1 ): access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request(priority=-1) ) lock2.doors_2[ lock2.node_3 ].release( lock2.doors_2[ lock2.node_3 ].users[0] ) yield access_lock_door2 lock2.doors_2[ lock2.node_3 ].users[0].id = self.id else: access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request(priority=-1) ) yield access_lock_door2 lock2.doors_2[ lock2.node_3 ].users[0].id = self.id else: if ( lock3.converting_while_in_line_up_area[ r2 ].users != [] ): waiting_during_converting = lock3.converting_while_in_line_up_area[ r2 ].request() yield waiting_during_converting lock3.converting_while_in_line_up_area[ r2 ].release( waiting_during_converting ) access_lock_door1 = lock2.doors_1[ lock2.node_1 ].request() yield access_lock_door1 if ( lock2.doors_2[ lock2.node_3 ].users != [] and lock2.doors_2[lock2.node_3] .users[0] .priority == -1 ): access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request(priority=-1) ) lock2.doors_2[ lock2.node_3 ].release( lock2.doors_2[ lock2.node_3 ].users[0] ) yield access_lock_door2 lock2.doors_2[ lock2.node_3 ].users[0].id = self.id else: access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request(priority=-1) ) yield access_lock_door2 lock2.doors_2[ lock2.node_3 ].users[0].id = self.id else: if ( lock2.doors_2[lock2.node_3].users != [] and lock2.doors_2[lock2.node_3] .users[0] .priority == -1 ): access_lock_door1 = lock2.doors_1[ lock2.node_1 ].request() yield access_lock_door1 access_lock_door2 = lock2.doors_2[ lock2.node_3 ].request(priority=-1) lock2.doors_2[lock2.node_3].release( lock2.doors_2[ lock2.node_3 ].users[0] ) yield access_lock_door2 lock2.doors_2[lock2.node_3].users[ 0 ].id = self.id elif ( lock2.doors_2[lock2.node_3].users != [] and lock2.doors_2[lock2.node_3] .users[0] .priority == 0 ): access_lock_door1 = lock2.doors_1[ lock2.node_1 ].request() yield access_lock_door1 access_lock_door2 = lock2.doors_2[ lock2.node_3 ].request(priority=-1) yield access_lock_door2 lock2.doors_2[lock2.node_3].users[ 0 ].id = self.id else: if ( lock.converting_while_in_line_up_area[ origin ].users != [] ): waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request( priority=-1 ) yield waiting_during_converting lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request() ) yield access_lock_door1 elif ( len( lock2.doors_1[ lock2.node_1 ].users ) == 0 or ( len( lock2.doors_1[ lock2.node_1 ].users ) != 0 and lock2.doors_1[ lock2.node_1 ] .users[0] .priority != -1 ) ) and self.route[ self.route.index(r) - 1 ] != lock2.water_level: access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request() ) waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request() yield waiting_during_converting yield from lock2.convert_chamber( self.env, self.route[ self.route.index(r) - 1 ], 0, ) lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) elif ( len( lock2.doors_1[ lock2.node_1 ].users ) != 0 and lock2.doors_1[lock2.node_1] .users[0] .priority == -1 ): access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request() ) yield access_lock_door1 else: access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request() ) if ( lock2.doors_2[ lock2.node_3 ].users != [] and lock2.doors_2[lock2.node_3] .users[0] .priority == -1 ): access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request(priority=-1) ) lock2.doors_2[ lock2.node_3 ].release( lock2.doors_2[ lock2.node_3 ].users[0] ) yield access_lock_door2 lock2.doors_2[ lock2.node_3 ].users[0].id = self.id else: access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request(priority=-1) ) yield access_lock_door2 lock2.doors_2[ lock2.node_3 ].users[0].id = self.id elif ( self.route[self.route.index(r) - 1] == lock2.node_3 ): if ( len(lock2.doors_1[lock2.node_1].users) != 0 ): if ( lock2.doors_1[lock2.node_1] .users[0] .priority == -1 ): if ( self.L > ( lock2.resource.users[ -1 ].lock_dist - 0.5 * lock2.resource.users[ -1 ].length ) or lock2.resource.users[ -1 ].converting == True ): access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request(priority=-1) ) yield access_lock_door1 lock2.doors_1[ lock2.node_1 ].release(access_lock_door1) wait_for_next_cycle = ( lock3.pass_line_up_area[ r2 ].request() ) yield wait_for_next_cycle lock3.pass_line_up_area[ r2 ].release(wait_for_next_cycle) if ( lock.converting_while_in_line_up_area[ origin ].users != [] ): waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request( priority=-1 ) yield waiting_during_converting lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) elif ( len( lock2.doors_2[ lock2.node_3 ].users ) == 0 or ( len( lock2.doors_2[ lock2.node_3 ].users ) != 0 and lock2.doors_2[ lock2.node_3 ] .users[0] .priority != -1 ) ) and self.route[ self.route.index(r) - 1 ] != lock2.water_level: waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request() yield waiting_during_converting yield from lock2.convert_chamber( self.env, self.route[ self.route.index(r) - 1 ], 0, ) lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) access_lock_door2 = lock2.doors_2[ lock2.node_3 ].request() yield access_lock_door2 if ( lock2.doors_1[ lock2.node_1 ].users != [] and lock2.doors_1[lock2.node_1] .users[0] .priority == -1 ): access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request(priority=-1) ) lock2.doors_1[ lock2.node_1 ].release( lock2.doors_1[ lock2.node_1 ].users[0] ) yield access_lock_door1 lock2.doors_1[ lock2.node_1 ].users[0].id = self.id else: access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request(priority=-1) ) yield access_lock_door1 lock2.doors_1[ lock2.node_1 ].users[0].id = self.id else: if ( lock3.converting_while_in_line_up_area[ r2 ].users != [] ): waiting_during_converting = lock3.converting_while_in_line_up_area[ r2 ].request() yield waiting_during_converting lock3.converting_while_in_line_up_area[ r2 ].release( waiting_during_converting ) access_lock_door2 = lock2.doors_2[ lock2.node_3 ].request() yield access_lock_door2 if ( lock2.doors_1[ lock2.node_1 ].users != [] and lock2.doors_1[lock2.node_1] .users[0] .priority == -1 ): access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request(priority=-1) ) lock2.doors_1[ lock2.node_1 ].release( lock2.doors_1[ lock2.node_1 ].users[0] ) yield access_lock_door1 lock2.doors_1[ lock2.node_1 ].users[0].id = self.id else: access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request(priority=-1) ) yield access_lock_door1 lock2.doors_1[ lock2.node_1 ].users[0].id = self.id else: if ( lock2.doors_1[lock2.node_1].users != [] and lock2.doors_1[lock2.node_1] .users[0] .priority == -1 ): access_lock_door2 = lock2.doors_2[ lock2.node_3 ].request() yield access_lock_door2 access_lock_door1 = lock2.doors_1[ lock2.node_1 ].request(priority=-1) lock2.doors_1[lock2.node_1].release( lock2.doors_1[ lock2.node_1 ].users[0] ) yield access_lock_door1 lock2.doors_1[lock2.node_1].users[ 0 ].id = self.id elif ( lock2.doors_1[lock2.node_1].users != [] and lock2.doors_1[lock2.node_1] .users[0] .priority == 0 ): access_lock_door2 = lock2.doors_2[ lock2.node_3 ].request() yield access_lock_door2 access_lock_door1 = lock2.doors_1[ lock2.node_1 ].request(priority=-1) yield access_lock_door1 lock2.doors_1[lock2.node_1].users[ 0 ].id = self.id else: if ( lock.converting_while_in_line_up_area[ origin ].users != [] ): waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request( priority=-1 ) yield waiting_during_converting lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request() ) yield access_lock_door2 elif ( len( lock2.doors_2[ lock2.node_3 ].users ) == 0 or ( len( lock2.doors_2[ lock2.node_3 ].users ) != 0 and lock2.doors_2[ lock2.node_3 ] .users[0] .priority != -1 ) ) and self.route[ self.route.index(r) - 1 ] != lock2.water_level: access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request() ) waiting_during_converting = lock.converting_while_in_line_up_area[ origin ].request() yield waiting_during_converting yield from lock2.convert_chamber( self.env, self.route[ self.route.index(r) - 1 ], 0, ) lock.converting_while_in_line_up_area[ origin ].release( waiting_during_converting ) elif ( len( lock2.doors_2[ lock2.node_3 ].users ) != 0 and lock2.doors_2[lock2.node_3] .users[0] .priority == -1 ): access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request() ) yield access_lock_door2 else: access_lock_door2 = ( lock2.doors_2[ lock2.node_3 ].request() ) if ( lock2.doors_1[ lock2.node_1 ].users != [] and lock2.doors_1[lock2.node_1] .users[0] .priority == -1 ): access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request(priority=-1) ) lock2.doors_1[ lock2.node_1 ].release( lock2.doors_1[ lock2.node_1 ].users[0] ) yield access_lock_door1 lock2.doors_1[ lock2.node_1 ].users[0].id = self.id else: access_lock_door1 = ( lock2.doors_1[ lock2.node_1 ].request(priority=-1) ) yield access_lock_door1 lock2.doors_1[ lock2.node_1 ].users[0].id = self.id access_lock_length = lock2.length.get(self.L) access_lock = lock2.resource.request() access_lock_pos_length = lock2.pos_length.get( self.L ) self.lock_dist = ( lock2.pos_length.level + 0.5 * self.L ) yield access_lock_pos_length lock2.resource.users[-1].id = self.id lock2.resource.users[-1].length = self.L lock2.resource.users[ -1 ].lock_dist = self.lock_dist lock2.resource.users[-1].converting = False if ( self.route[self.route.index(r) - 1] == lock2.node_1 ): lock2.resource.users[-1].dir = 1.0 else: lock2.resource.users[-1].dir = 2.0 if wait_for_lock_entry != self.env.now: waiting = self.env.now - wait_for_lock_entry self.log_entry( "Waiting in line-up area start", wait_for_lock_entry, 0, orig, ) self.log_entry( "Waiting in line-up area stop", self.env.now, waiting, orig, ) self.wgs84 = pyproj.Geod(ellps="WGS84") [ doors_origin_lat, doors_origin_lon, doors_destination_lat, doors_destination_lon, ] = [ self.env.FG.nodes[ self.route[self.route.index(r) - 1] ]["geometry"].x, self.env.FG.nodes[ self.route[self.route.index(r) - 1] ]["geometry"].y, self.env.FG.nodes[ self.route[self.route.index(r) + 1] ]["geometry"].x, self.env.FG.nodes[ self.route[self.route.index(r) + 1] ]["geometry"].y, ] fwd_azimuth, _, distance = self.wgs84.inv( doors_origin_lat, doors_origin_lon, doors_destination_lat, doors_destination_lon, ) [ self.lock_pos_lat, self.lock_pos_lon, _, ] = self.wgs84.fwd( self.env.FG.nodes[ self.route[self.route.index(r) - 1] ]["geometry"].x, self.env.FG.nodes[ self.route[self.route.index(r) - 1] ]["geometry"].y, fwd_azimuth, self.lock_dist, ) for r4 in reversed(self.route[: (loc - 1)]): if ( "Line-up area" in self.env.FG.nodes[r4].keys() ): locks = self.env.FG.nodes[r4][ "Line-up area" ] for lock4 in locks: if lock4.name == self.lock_name: lock4.lock_queue_length -= 1 break elif "Waiting area" in self.env.FG.nodes[r].keys(): for r2 in reversed(self.route[: (loc - 1)]): if "Lock" in self.env.FG.nodes[r2].keys(): locks = self.env.FG.nodes[r2]["Lock"] for lock2 in locks: if lock2.name == self.lock_name: if ( self.route[self.route.index(r2) + 1] == lock2.node_3 and len( lock2.doors_2[ lock2.node_3 ].users ) != 0 and lock2.doors_2[lock2.node_3] .users[0] .id == self.id ): lock2.doors_2[lock2.node_3].release( access_lock_door2 ) elif ( self.route[self.route.index(r2) + 1] == lock2.node_1 and len( lock2.doors_1[ lock2.node_1 ].users ) != 0 and lock2.doors_1[lock2.node_1] .users[0] .id == self.id ): lock2.doors_1[lock2.node_1].release( access_lock_door1 ) lock.pass_line_up_area[origin].release( departure_lock ) lock2.resource.release(access_lock) departure_lock_length = ( lock2.length.put(self.L) ) departure_lock_pos_length = ( lock2.pos_length.put(self.L) ) yield departure_lock_length yield departure_lock_pos_length break if "Line-up area" in self.env.FG.nodes[self.route[node[0] - 1]].keys(): locks = self.env.FG.nodes[self.route[node[0] - 1]]["Line-up area"] for lock in locks: if lock.name == self.lock_name: loc = self.route.index(origin) for r in self.route[loc:]: if "Lock" in self.env.FG.nodes[r].keys(): locks = self.env.FG.nodes[r]["Lock"] lock.line_up_area[self.route[node[0] - 1]].release( access_lineup_area ) departure_lineup_length = lock.length.put(self.L) yield departure_lineup_length if "Lock" in self.env.FG.nodes[origin].keys(): locks = self.env.FG.nodes[origin]["Lock"] for lock in locks: if lock.name == self.lock_name: if self.route[self.route.index(origin) - 1] == lock.node_1: lock.doors_1[lock.node_1].release(access_lock_door1) elif self.route[self.route.index(origin) - 1] == lock.node_3: lock.doors_2[lock.node_3].release(access_lock_door2) orig = shapely.geometry.Point( self.lock_pos_lat, self.lock_pos_lon ) loc = self.route.index(origin) for r2 in reversed(self.route[loc:]): if "Line-up area" in self.env.FG.nodes[r2].keys(): locks = self.env.FG.nodes[r2]["Line-up area"] for lock3 in locks: if lock3.name == self.lock_name: departure_lock = lock3.pass_line_up_area[ r2 ].request(priority=-1) break break for r in reversed(self.route[: (loc - 1)]): if "Line-up area" in self.env.FG.nodes[r].keys(): locks = self.env.FG.nodes[r]["Line-up area"] for lock2 in locks: if lock2.name == self.lock_name: for q2 in range(0, len(lock.resource.users)): if lock.resource.users[q2].id == self.id: break start_time_in_lock = self.env.now self.log_entry( "Passing lock start", self.env.now, 0, orig ) if ( len(lock2.line_up_area[r].users) != 0 and lock2.line_up_area[r].users[0].length < lock.length.level ): if ( self.route[self.route.index(origin) - 1] == lock.node_1 ): access_line_up_area = ( lock2.enter_line_up_area[ r ].request() ) yield access_line_up_area lock2.enter_line_up_area[r].release( access_line_up_area ) access_lock_door1 = lock.doors_1[ lock.node_1 ].request() yield access_lock_door1 lock.doors_1[lock.node_1].release( access_lock_door1 ) elif ( self.route[self.route.index(origin) - 1] == lock.node_3 ): access_line_up_area = ( lock2.enter_line_up_area[ r ].request() ) yield access_line_up_area lock2.enter_line_up_area[r].release( access_line_up_area ) access_lock_door2 = lock.doors_2[ lock.node_3 ].request() yield access_lock_door2 lock.doors_2[lock.node_3].release( access_lock_door2 ) if lock.resource.users[0].id == self.id: lock.resource.users[0].converting = True number_of_vessels = len(lock.resource.users) yield from lock.convert_chamber( self.env, destination, number_of_vessels ) else: for u in range(len(lock.resource.users)): if lock.resource.users[u].id == self.id: lock.resource.users[ u ].converting = True yield self.env.timeout( lock.doors_close + lock.operation_time(self.env) + lock.doors_open ) break yield departure_lock self.log_entry( "Passing lock stop", self.env.now, self.env.now - start_time_in_lock, orig, ) [self.lineup_pos_lat, self.lineup_pos_lon] = [ self.env.FG.nodes[self.route[self.route.index(r2)]][ "geometry" ].x, self.env.FG.nodes[self.route[self.route.index(r2)]][ "geometry" ].y, ] yield from self.pass_edge(origin, destination) self.v = speed else: # print('I am going to go to the next node {}'.format(destination)) yield from self.pass_edge(origin, destination) if node[0] + 2 == len(self.route): break # self.geometry = nx.get_node_attributes(self.env.FG, "geometry")[destination] logger.debug(" distance: " + "%4.2f" % self.distance + " m") if self.current_speed is not None: logger.debug(" sailing: " + "%4.2f" % self.current_speed + " m/s") logger.debug( " duration: " + "%4.2f" % ((self.distance / self.current_speed) / 3600) + " hrs" ) else: logger.debug(" current_speed: not set")
[docs] def pass_edge(self, origin, destination): edge = self.env.FG.edges[origin, destination] orig = nx.get_node_attributes(self.env.FG, "geometry")[origin] dest = nx.get_node_attributes(self.env.FG, "geometry")[destination] for edge_function in self.edge_functions: edge_function(orig, dest, edge) if "Lock" in self.env.FG.nodes[origin].keys(): orig = shapely.geometry.Point(self.lock_pos_lat, self.lock_pos_lon) if "Lock" in self.env.FG.nodes[destination].keys(): dest = shapely.geometry.Point(self.lock_pos_lat, self.lock_pos_lon) if "Line-up area" in self.env.FG.nodes[origin].keys(): orig = shapely.geometry.Point(self.lineup_pos_lat, self.lineup_pos_lon) if "Line-up area" in self.env.FG.nodes[destination].keys(): dest = shapely.geometry.Point(self.lineup_pos_lat, self.lineup_pos_lon) if "geometry" in edge: edge_route = np.array(edge["geometry"].coords) # check if edge is in the sailing direction, otherwise flip it distance_from_start = self.wgs84.inv( orig.x, orig.y, edge_route[0][0], edge_route[0][1], )[2] distance_from_stop = self.wgs84.inv( orig.x, orig.y, edge_route[-1][0], edge_route[-1][1], )[2] if distance_from_start > distance_from_stop: # when the distance from the starting point is greater than from the end point edge_route = np.flipud(np.array(edge["geometry"].coords)) for index, pt in enumerate(edge_route[:-1]): sub_orig = shapely.geometry.Point( edge_route[index][0], edge_route[index][1] ) sub_dest = shapely.geometry.Point( edge_route[index + 1][0], edge_route[index + 1][1] ) distance = self.wgs84.inv( shapely.geometry.shape(sub_orig).x, shapely.geometry.shape(sub_orig).y, shapely.geometry.shape(sub_dest).x, shapely.geometry.shape(sub_dest).y, )[2] self.distance += distance self.log_entry( "Sailing from node {} to node {} sub edge {} start".format( origin, destination, index ), self.env.now, 0, sub_orig, ) yield self.env.timeout(distance / self.current_speed) self.log_entry( "Sailing from node {} to node {} sub edge {} stop".format( origin, destination, index ), self.env.now, 0, sub_dest, ) self.geometry = dest # print(' My new origin is {}'.format(destination)) else: distance = self.wgs84.inv( shapely.geometry.shape(orig).x, shapely.geometry.shape(orig).y, shapely.geometry.shape(dest).x, shapely.geometry.shape(dest).y, )[2] self.distance += distance value = 0 # remember when we arrived at the edge arrival = self.env.now v = self.current_speed # This is the case if we are sailing on power if getattr(self, "P_tot_given", None) is not None: edge = self.env.FG.edges[origin, destination] depth = self.env.FG.get_edge_data(origin, destination)["Info"][ "GeneralDepth" ] # estimate 'grounding speed' as a useful upperbound ( upperbound, selected, results_df, ) = opentnsim.strategy.get_upperbound_for_power2v( self, width=150, depth=depth, margin=0 ) v = self.power2v(self, edge, upperbound) # use computed power value = self.P_given # determine time to pass edge timeout = distance / v # Wait for edge resources to become available if "Resources" in edge.keys(): with self.env.FG.edges[origin, destination][ "Resources" ].request() as request: yield request # we had to wait, log it if arrival != self.env.now: self.log_entry( "Waiting to pass edge {} - {} start".format( origin, destination ), arrival, value, orig, ) self.log_entry( "Waiting to pass edge {} - {} stop".format( origin, destination ), self.env.now, value, orig, ) # default velocity based on current speed. self.log_entry( "Sailing from node {} to node {} start".format(origin, destination), self.env.now, value, orig, ) yield self.env.timeout(timeout) self.log_entry( "Sailing from node {} to node {} stop".format(origin, destination), self.env.now, value, dest, ) self.geometry = dest
@property def current_speed(self): return self.v
[docs]class ContainerDependentMovable(Movable, HasContainer): """ContainerDependentMovable class Used for objects that move with a speed dependent on the container level compute_v: a function, given the fraction the container is filled (in [0,1]), returns the current speed""" def __init__(self, compute_v, *args, **kwargs): super().__init__(*args, **kwargs) """Initialization""" self.compute_v = compute_v self.wgs84 = pyproj.Geod(ellps="WGS84") @property def current_speed(self): return self.compute_v(self.container.level / self.container.capacity)
[docs]class ExtraMetadata: """store all leftover keyword arguments as metadata property (use as last mixin)""" def __init__(self, *args, **kwargs): super().__init__(*args) # store all other properties as metadata self.metadata = kwargs