simulation_handler.generators
Generates resources and processes needed to run the simulation using the object classes defined in truck.py, Channel.py, and Port.py and the DES processes contained in ContainerTerminal.py, LiquidTerminal.py, and DryBulkTerminal.py.
1""" 2Generates resources and processes needed to run the simulation using the object classes defined in truck.py, Channel.py, and Port.py 3and the DES processes contained in ContainerTerminal.py, LiquidTerminal.py, and DryBulkTerminal.py. 4""" 5 6import os 7import random 8 9import numpy as np 10import pandas as pd 11from tqdm import tqdm 12 13import constants 14from simulation_classes.terminal_container import ContainerTerminal 15from simulation_classes.terminal_liquid import LiquidTerminal 16from simulation_classes.terminal_drybulk import DryBulkTerminal 17from simulation_classes.truck import Truck 18from simulation_classes.train import Train 19from simulation_classes.pipeline import Pipeline 20from simulation_classes.port import Container, get_value_by_terminal, get_value_from_terminal_tuple 21from simulation_classes.channel import Channel 22from simulation_analysis.results import plot_queues, track_utilization, save_track_list, get_utilization 23 24SIMULATION_TIME = constants.SIMULATION_TIME 25PIPELINE_RATE = constants.PIPELINE_RATE 26CHANNEL_SAFETWOWAY = constants.CHANNEL_SAFETWOWAY 27 28 29def initialize_rng(seed): 30 """ 31 Initialize random state objects for repeatable randomness across runs. 32 This function sets the global random number generator states for both the built-in `random` module and NumPy's random module. 33 This is useful for ensuring that the simulation can be reproduced with the same random events. 34 This function should be called at the start of the simulation to ensure that all random processes are initialized with the same seed. 35 Args: 36 seed (int): The seed value for the random number generator. 37 Returns: 38 None 39 """ 40 global rng_random, rng_numpy 41 rng_random = random.Random(seed) 42 rng_numpy = np.random.default_rng(seed) 43 44 45def update_availability(run_id, env, it, port_berths_container_terminals, port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data): 46 """ 47 This function updates the availability of berths, yards, tanks, and silos at the port terminals. 48 It calculates the number of available and used resources for each terminal type (Container, Liquid, DryBulk) and appends this data to the respective DataFrames. 49 This function also tracks the queue lengths at each terminal type and updates the availability DataFrames with the current time and resource availability. 50 51 Args: 52 run_id (str): The unique identifier for the simulation run. 53 env (simpy.Environment): The simulation environment. 54 it (int): The current iteration of the simulation. 55 port_berths_container_terminals (list): List of container terminal berths. 56 port_yard_container_terminals (list): List of container terminal yards. 57 port_berths_liquid_terminals (list): List of liquid terminal berths. 58 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 59 port_berths_drybulk_terminals (list): List of dry bulk terminal berths. 60 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 61 availability_df_container (pd.DataFrame): DataFrame to store container terminal availability data. 62 availability_df_liquid (pd.DataFrame): DataFrame to store liquid terminal availability data. 63 availability_df_drybulk (pd.DataFrame): DataFrame to store dry bulk terminal availability data. 64 ship_data (dict): Dictionary containing ship data for the simulation. 65 terminal_data (dict): Dictionary containing terminal data for the simulation. 66 Returns: 67 availability_df_container (pd.DataFrame): Updated DataFrame with container terminal availability data. 68 availability_df_liquid (pd.DataFrame): Updated DataFrame with liquid terminal availability data. 69 availability_df_drybulk (pd.DataFrame): Updated DataFrame with dry bulk terminal availability data. 70 queue_lengths_in_ctr (list): List of queue lengths in container terminals. 71 queue_lengths_in_liq (list): List of queue lengths in liquid terminals. 72 queue_lengths_in_drybulk (list): List of queue lengths in dry bulk terminals. 73 """ 74 # Initialize lists to store vessel availability data for each terminal 75 berth_availability_data_container = [] 76 yard_availability_data = [] 77 berth_availability_data_liquid = [] 78 tank_availability_data = [] 79 berth_availability_data_drybulk = [] 80 silo_availability_data = [] 81 queue_lengths_in_ctr = [] 82 queue_lengths_in_liq = [] 83 queue_lengths_in_drybulk = [] 84 85 # Container Terminals 86 for i, (port_berths, port_yard) in enumerate(zip(port_berths_container_terminals, port_yard_container_terminals)): 87 # Determine the vessel queue length and number of available berths for a container terminal 88 queue = len(port_berths.get_queue) 89 queue_lengths_in_ctr.append(queue) 90 available_port_berths = len(port_berths.items) 91 used_port_berths = get_value_by_terminal( 92 terminal_data, "Container", i+1, "Berths") - available_port_berths 93 berth_availability_data_container.extend( 94 [available_port_berths, used_port_berths, queue]) 95 96 # Determine the number of containers stored on the dock (yard) and the remaining available storage space 97 used_yard = len(port_yard.items) 98 available_yard = get_value_by_terminal( 99 terminal_data, "Container", i+1, "storage volume") - used_yard 100 yard_availability_data.extend( 101 [available_yard, used_yard]) 102 103 # Liquid Terminals 104 for i, port_berths in enumerate(port_berths_liquid_terminals): 105 # Determine the vessel queue length and number of available berths for a liquid bulk terminal 106 queue = len(port_berths.get_queue) 107 queue_lengths_in_liq.append(queue) 108 available_port_berths = len(port_berths.items) 109 used_port_berths = get_value_by_terminal( 110 terminal_data, "Liquid", i+1, "Berths") - available_port_berths 111 berth_availability_data_liquid.extend( 112 [available_port_berths, used_port_berths, queue]) 113 114 for i, port_tanks in enumerate(port_tanks_liquid_terminals): 115 # Determine the amount of storage space used and available in the liquid bulk tanks 116 used_tank = port_tanks.level 117 available_tank = get_value_by_terminal( 118 terminal_data, "Liquid", i+1, "storage volume") - used_tank 119 tank_availability_data.extend([available_tank, used_tank]) 120 121 # Dry Bulk Terminals 122 for i, port_berths in enumerate(port_berths_drybulk_terminals): 123 # Determine the vessel queue length and number of available berths for a dry bulk terminal 124 queue = len(port_berths.get_queue) 125 queue_lengths_in_drybulk.append(queue) 126 available_port_berths = len(port_berths.items) 127 used_port_berths = get_value_by_terminal( 128 terminal_data, "DryBulk", i+1, "Berths") - available_port_berths 129 berth_availability_data_drybulk.extend( 130 [available_port_berths, used_port_berths, queue]) 131 132 for i, port_silos in enumerate(port_silos_drybulk_terminals): 133 # Determine the amount of storage space used and available in the dry bulk silos 134 used_silo = port_silos.level 135 available_silo = get_value_by_terminal( 136 terminal_data, "DryBulk", i+1, "storage volume") - used_silo 137 silo_availability_data.extend([available_silo, used_silo]) 138 139 # Update DataFrames for each terminal type to track storage availability over the span of the simulation 140 availability_container = [ 141 env.now] + berth_availability_data_container + yard_availability_data 142 columns_container = ["Time"] + [f"Terminal_{i+1}_{col}" for i in range(len(port_berths_container_terminals)) for col in ["Available_Berth_Ctr", "Used_Berth_Ctr", "Berth_Queue_Ctr"]] + [ 143 f"Terminal_{i+1}_{col}" for i in range(len(port_yard_container_terminals)) for col in ["Available_Yard", "Used_Yard"]] 144 row_container = pd.DataFrame( 145 [availability_container], columns=columns_container) 146 availability_df_container = pd.concat( 147 [availability_df_container, row_container], ignore_index=True) 148 149 availability_liquid = [env.now] + \ 150 berth_availability_data_liquid + tank_availability_data 151 columns_liquid = ["Time"] + [f"Terminal_{i+1}_{col}" for i in range(len(port_berths_liquid_terminals)) for col in ["Available_Berth_liq", "Used_Berth_liq", "Berth_Queue_liq"]] + [ 152 f"Terminal_{i+1}_{col}" for i in range(len(port_tanks_liquid_terminals)) for col in ["Available_Tank", "Used_Tank"]] 153 row_liquid = pd.DataFrame([availability_liquid], columns=columns_liquid) 154 availability_df_liquid = pd.concat( 155 [availability_df_liquid, row_liquid], ignore_index=True) 156 157 availability_drybulk = [env.now] + \ 158 berth_availability_data_drybulk + silo_availability_data 159 columns_drybulk = ["Time"] + [f"Terminal_{i+1}_{col}" for i in range(len(port_berths_drybulk_terminals)) for col in ["Available_Berth_db", "Used_Berth_db", "Berth_Queue_db"]] + [ 160 f"Terminal_{i+1}_{col}" for i in range(len(port_silos_drybulk_terminals)) for col in ["Available_Silo", "Used_Silo"]] 161 row_drybulk = pd.DataFrame([availability_drybulk], columns=columns_drybulk) 162 availability_df_drybulk = pd.concat( 163 [availability_df_drybulk, row_drybulk], ignore_index=True) 164 165 track_list = get_utilization( 166 availability_df_container, availability_df_liquid, availability_df_drybulk, run_id) 167 168 # innitialise empty dataframes 169 if it == 0: 170 availability_df_liquid = pd.DataFrame() 171 availability_df_drybulk = pd.DataFrame() 172 if it > 2: # save from second iterarion 173 save_track_list(run_id, env.now, track_list) 174 if it == len(ship_data): 175 # Write the data to an Excel file, with different sheets for Container, Liquid Bulk, and Dry Bulk Terminals. 176 file_exists = os.path.isfile("./logs/availability.xlsx") 177 if file_exists: 178 mode = 'a' 179 if_sheet_exists = 'replace' 180 else: 181 mode = 'w' 182 if_sheet_exists = None # Not used in write mode 183 184 with pd.ExcelWriter(f".{run_id}/logs/availability.xlsx", engine="openpyxl", mode=mode, if_sheet_exists=if_sheet_exists) as writer: 185 availability_df_container.to_excel( 186 writer, sheet_name="Container_Terminals", index=False) 187 availability_df_liquid.to_excel( 188 writer, sheet_name="Liquid_Terminals", index=False) 189 availability_df_drybulk.to_excel( 190 writer, sheet_name="Dry_Bulk_Terminals", index=False) 191 192 return availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk 193 194 195def create_containers(ship_id, ship_info): 196 """ 197 This function creates a list of Container objects to be loaded and unloaded from a ship. 198 It generates the number of containers based on the ship's information, specifically the width and the number of containers to load and unload. 199 Args: 200 ship_id (str): The unique identifier for the ship. 201 ship_info (dict): A dictionary containing information about the ship, including its width and the number of containers to load and unload. 202 Returns: 203 containers_to_unload (list): A list of Container objects representing the containers to be unloaded from the ship. 204 containers_to_load (list): A list of Container objects representing the containers to be loaded onto the ship. 205 """ 206 containers_to_unload = [Container(id=f"{ship_id}_unload_{j}", width=ship_info['width']) for j in range( 207 int(ship_info['num_container_or_liq_tons_or_dry_tons_to_unload']))] 208 containers_to_load = [Container(id=f"{ship_id}_load_{j}", width=ship_info['width'])for j in range( 209 int(ship_info['num_container_or_liq_tons_or_dry_tons_to_load']))] 210 return containers_to_unload, containers_to_load 211 212 213def tons_to_unload_load(ship_id, ship_info): 214 """ 215 This function retrieves the number of tons to unload and load for a ship based on its information. 216 Args: 217 ship_id (str): The unique identifier for the ship. 218 ship_info (dict): A dictionary containing information about the ship, including the number of tons to unload and load. 219 Returns: 220 unload_tons (int): The number of tons to unload from the ship. 221 load_tons (int): The number of tons to load onto the ship. 222 """ 223 unload_tons = ship_info['num_container_or_liq_tons_or_dry_tons_to_unload'] 224 load_tons = ship_info['num_container_or_liq_tons_or_dry_tons_to_load'] 225 return unload_tons, load_tons 226 227 228 229def ship_generator(run_id, env, chassis_bays_utilization, port_berths_container_terminals, port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, 230 port_berths_drybulk_terminals, port_silos_drybulk_terminals, channel, day_pilots, night_pilots, tugboats, events, ship_logs, channel_events, channel_logs, SHIPS_IN_ANCHORAGE, SHIPS_IN_CHANNEL, ship_data, terminal_data, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink): 231 """ 232 This function generates ships and processes them in the simulation environment. 233 It iterates through the ship data, simulating the arrival of each ship at the port and processing it based on its type (Container, Liquid, DryBulk). 234 The ContainerTerminal, LiquidTerminal, and DryBulkTerminal classes simulates the vessel's operations at the port. 235 It updates the availability of resources at the port terminals and tracks the number of ships in the channel and anchorage. 236 Args: 237 run_id (str): The unique identifier for the simulation run. 238 env (simpy.Environment): The simulation environment. 239 chassis_bays_utilization (dict): A dictionary to track chassis bays utilization. 240 port_berths_container_terminals (list): List of container terminal berths. 241 port_yard_container_terminals (list): List of container terminal yards. 242 port_berths_liquid_terminals (list): List of liquid terminal berths. 243 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 244 port_berths_drybulk_terminals (list): List of dry bulk terminal berths. 245 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 246 channel (Channel): The channel object for managing ship movements. 247 day_pilots (int): Number of day pilots available. 248 night_pilots (int): Number of night pilots available. 249 tugboats (int): Number of tugboats available. 250 events (list): List to store events during the simulation. 251 ship_logs (list): List to store ship logs during the simulation. 252 channel_events (list): List to store channel events during the simulation. 253 channel_logs (list): List to store channel logs during the simulation. 254 SHIPS_IN_ANCHORAGE (list): List to track ships in anchorage by type. 255 SHIPS_IN_CHANNEL (list): List to track ships in the channel. 256 ship_data (dict): Dictionary containing ship data for the simulation. 257 terminal_data (dict): Dictionary containing terminal data for the simulation. 258 liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections. 259 liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections. 260 Yields: 261 simpy.Timeout: A timeout event to simulate the arrival of each ship at the port. 262 Returns: 263 None 264 """ 265 i = 0 266 terminal_data = terminal_data 267 268 availability_df_container = pd.DataFrame() 269 availability_df_liquid = pd.DataFrame() 270 availability_df_drybulk = pd.DataFrame() 271 272 queue_ctr_its = [] 273 queue_liq_its = [] 274 queue_drybulk_its = [] 275 276 SHIPS_IN_CHANNEL_TRACK = [] 277 SHIPS_IN_ANCHORAGE_TRACK = [] 278 CONTAINER_VES_IN_ANCHORAGE = [] 279 LIQUID_VES_IN_ANCHORAGE = [] 280 DRYBULK_VES_IN_ANCHORAGE = [] 281 282 it = 0 283 284 for ship_id, ship_info in tqdm(ship_data.items(), desc=f"Simulation Progress {run_id}"): 285 it += 1 286 yield env.timeout(ship_info['arrival'] - env.now) 287 if ship_data[ship_id]['ship_type'] == 'Container': 288 SHIPS_IN_ANCHORAGE[0] += 1 289 elif ship_data[ship_id]['ship_type'] == 'Liquid': 290 SHIPS_IN_ANCHORAGE[1] += 1 291 elif ship_data[ship_id]['ship_type'] == 'DryBulk': 292 SHIPS_IN_ANCHORAGE[2] += 1 293 294 selected_port = ship_info["terminal"] - 1 295 last_section = ship_info["last_section"] 296 297 # Container ship processes 298 if ship_info['ship_type'] == 'Container': 299 port_berths = port_berths_container_terminals[selected_port] 300 port_yard = port_yard_container_terminals[selected_port] 301 selected_terminal = selected_port + 1 302 availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk = update_availability(run_id, env, it, port_berths_container_terminals, 303 port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data) 304 queue_ctr_its.append(queue_lengths_in_ctr) 305 containers_to_unload, containers_to_load = create_containers( 306 ship_id, ship_info) 307 transfer_rate = get_value_by_terminal( 308 terminal_data, "Container", terminal_id=ship_info["terminal"], resource_name="transfer rate per unit") 309 transfer_time = 1 / transfer_rate 310 ContainerTerminal(env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal=selected_terminal, id=ship_id, ship_type=ship_info['ship_type'], draft=ship_info['draft'], 311 width=ship_info['width'], unload_time_per_container=transfer_time, load_time_per_container=transfer_time, containers_to_unload=containers_to_unload, containers_to_load=containers_to_load, events=events, ship_logs=ship_logs, port_berths=port_berths, port_yard=port_yard, SHIPS_IN_CHANNEL=SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE=SHIPS_IN_ANCHORAGE, terminal_data=terminal_data) 312 313 # Liquid and Dry Bulk ship processes 314 elif ship_info['ship_type'] == 'Liquid': 315 port_berths = port_berths_liquid_terminals[selected_port] 316 port_tanks = port_tanks_liquid_terminals[selected_port] 317 selected_terminal = selected_port + 1 318 availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk = update_availability(run_id, env, it, port_berths_container_terminals, 319 port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data) 320 queue_liq_its.append(queue_lengths_in_liq) 321 unload_tons, load_tons = tons_to_unload_load(ship_id, ship_info) 322 transfer_rate = get_value_by_terminal( 323 terminal_data, "Liquid", terminal_id=ship_info["terminal"], resource_name="transfer rate per unit") 324 transfer_time = 1 / transfer_rate 325 LiquidTerminal(env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal, id=ship_id, ship_type=ship_info['ship_type'], draft=ship_info['draft'], 326 width=ship_info['width'], unload_time=transfer_time, load_time=transfer_time, unload_tons=unload_tons, load_tons=load_tons, events=events, ship_logs=ship_logs, port_berths=port_berths, port_tanks=port_tanks, SHIPS_IN_CHANNEL=SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE=SHIPS_IN_ANCHORAGE, terminal_data=terminal_data, liq_terminals_with_pipeline_source=liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink=liq_terminals_with_pipeline_sink) 327 328 # Dry Bulk ship processes 329 elif ship_info['ship_type'] == 'DryBulk': 330 port_berths = port_berths_drybulk_terminals[selected_port] 331 port_silos = port_silos_drybulk_terminals[selected_port] 332 selected_terminal = selected_port + 1 333 availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk = update_availability(run_id, env, it, port_berths_container_terminals, 334 port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data) 335 queue_drybulk_its.append(queue_lengths_in_drybulk) 336 unload_tons, load_tons = tons_to_unload_load(ship_id, ship_info) 337 transfer_rate = get_value_by_terminal( 338 terminal_data, "DryBulk", terminal_id=ship_info["terminal"], resource_name="transfer rate per unit") 339 transfer_time = 1 / transfer_rate 340 DryBulkTerminal(env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal=selected_terminal, id=ship_id, ship_type=ship_info['ship_type'], draft=ship_info 341 ['draft'], width=ship_info['width'], unload_time=transfer_time, load_time=transfer_time, unload_tons=unload_tons, load_tons=load_tons, events=events, ship_logs=ship_logs, port_berths=port_berths, port_silos=port_silos, SHIPS_IN_CHANNEL=SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE=SHIPS_IN_ANCHORAGE, terminal_data=terminal_data) 342 343 i += 1 # Move to the next vessel in the input file 344 SHIPS_IN_CHANNEL_TRACK.append((env.now, SHIPS_IN_CHANNEL[0])) 345 SHIPS_IN_ANCHORAGE_TRACK.append( 346 (env.now, SHIPS_IN_ANCHORAGE[0] + SHIPS_IN_ANCHORAGE[1] + SHIPS_IN_ANCHORAGE[2])) 347 CONTAINER_VES_IN_ANCHORAGE.append((env.now, SHIPS_IN_ANCHORAGE[0])) 348 LIQUID_VES_IN_ANCHORAGE.append((env.now, SHIPS_IN_ANCHORAGE[1])) 349 DRYBULK_VES_IN_ANCHORAGE.append((env.now, SHIPS_IN_ANCHORAGE[2])) 350 351 352 353def truck_generator(run_id, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, env, terminal_tuple_cache, port_tanks_liquid_terminals, port_yard_container_terminals, port_silos_drybulk_terminals, port_loading_bays_liquid_terminals, port_drybulk_bays_drybulk_terminals, port_chassis_container_terminals, truck_gates_ctr, truck_gates_liquid, truck_gates_dk, events, seed, terminal_data): 354 """ 355 This function generates trucks and processes them in the simulation environment. 356 It iterates through the truck data, simulating the arrival of each truck at the port and processing it based on its type (Container, Liquid, DryBulk). 357 It creates Truck objects and initializes them with the appropriate parameters based on the truck type and terminal information. (This simulates each truck's processing at the port.) 358 Args: 359 run_id (str): The unique identifier for the simulation run. 360 liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections. 361 liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections. 362 chassis_bays_utilization (dict): A dictionary to track chassis bays utilization. 363 env (simpy.Environment): The simulation environment. 364 terminal_tuple_cache (dict): A cache of terminal tuples for quick access to terminal data. 365 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 366 port_yard_container_terminals (list): List of container terminal yards. 367 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 368 port_loading_bays_liquid_terminals (list): List of liquid terminal loading bays. 369 port_drybulk_bays_drybulk_terminals (list): List of dry bulk terminal loading bays. 370 port_chassis_container_terminals (list): List of container terminal chassis bays. 371 truck_gates_ctr (list): List of container terminal truck gates. 372 truck_gates_liquid (list): List of liquid terminal truck gates. 373 truck_gates_dk (list): List of dry bulk terminal truck gates. 374 events (list): List to store events during the simulation. 375 seed (int): The seed value for the random number generator. 376 terminal_data (dict): Dictionary containing terminal data for the simulation. 377 Yields: 378 simpy.Timeout: A timeout event to simulate the arrival of each truck at the port. 379 Returns: 380 None 381 """ 382 383 initialize_rng(seed) 384 385 truck_data = pd.read_pickle(f".{run_id}/logs/truck_data.pkl") 386 truck_data = truck_data.to_dict(orient="index") 387 388 for truck_id, truck_info in truck_data.items(): 389 yield env.timeout(truck_info['arrival'] - env.now) 390 if truck_info['truck_type'] == 'Container': 391 terminal_type = "Container" 392 terminal_id = truck_info['terminal_id'] 393 port_tanks = None 394 port_yard = port_yard_container_terminals[terminal_id] 395 port_silos = None 396 loading_bays = None 397 drybulk_bays = None 398 truck_chassis = port_chassis_container_terminals[terminal_id] 399 container_load_amount = get_value_from_terminal_tuple( 400 terminal_tuple_cache, "Container", terminal_id=terminal_id+1, resource_name="truck payload size") 401 container_unload_amount = get_value_from_terminal_tuple( 402 terminal_tuple_cache, "Container", terminal_id=terminal_id+1, resource_name="truck payload size") 403 container_amount = (container_load_amount, container_unload_amount) 404 Truck(env, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, truck_id, run_id, terminal_type, terminal_id+1, container_amount, 405 None, None, loading_bays, port_tanks, truck_chassis, port_yard, port_silos, drybulk_bays, events, seed=rng_random.randint(1, 100000000), terminal_data=terminal_data) 406 elif truck_info['truck_type'] == 'Liquid': 407 terminal_type = "Liquid" 408 terminal_id = truck_info['terminal_id'] 409 port_tanks = port_tanks_liquid_terminals[terminal_id] 410 port_yard = None 411 port_silos = None 412 loading_bays = port_loading_bays_liquid_terminals[terminal_id] 413 drybulk_bays = None 414 truck_chassis = None 415 liquid_amount = get_value_from_terminal_tuple( 416 terminal_tuple_cache, "Liquid", terminal_id=terminal_id+1, resource_name="truck payload size") 417 Truck(env, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, truck_id, run_id, terminal_type, terminal_id+1, None, liquid_amount, 418 None, loading_bays, port_tanks, truck_chassis, port_yard, port_silos, drybulk_bays, events, seed=rng_random.randint(1, 100000000), terminal_data=terminal_data) 419 elif truck_info['truck_type'] == 'DryBulk': 420 terminal_type = "DryBulk" 421 terminal_id = truck_info['terminal_id'] 422 port_tanks = None 423 port_yard = None 424 port_silos = port_silos_drybulk_terminals[terminal_id] 425 loading_bays = None 426 drybulk_bays = port_drybulk_bays_drybulk_terminals[terminal_id] 427 truck_chassis = None 428 drybulk_amount = get_value_from_terminal_tuple( 429 terminal_tuple_cache, "DryBulk", terminal_id=terminal_id+1, resource_name="truck payload size") 430 Truck(env, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, truck_id, run_id, terminal_type, terminal_id+1, None, None, 431 drybulk_amount, loading_bays, port_tanks, truck_chassis, port_yard, port_silos, drybulk_bays, events, seed=rng_random.randint(1, 100000000), terminal_data=terminal_data) 432 433 434def train_generator(run_id, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, env, terminal_tuple_cache, train_loading_racks_ctr, train_loading_racks_liquid, train_loading_racks_dk, train_events, port_tanks_liquid_terminals, port_yard_container_terminals, port_silos_drybulk_terminals, seed): 435 """ 436 This function generates trains and processes them in the simulation environment. 437 It iterates through the train data, simulating the arrival of each train at the port and processing it based on its cargo type (Container, Liquid, DryBulk). 438 It creates Train objects and initializes them with the appropriate parameters based on the train type and terminal information. (This simulates each train's processing at the port.) 439 Args: 440 run_id (str): The unique identifier for the simulation run. 441 liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections. 442 liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections. 443 env (simpy.Environment): The simulation environment. 444 terminal_tuple_cache (dict): A cache of terminal tuples for quick access to terminal data. 445 train_loading_racks_ctr (dict): Dictionary of loading racks for container trains. 446 train_loading_racks_liquid (dict): Dictionary of loading racks for liquid trains. 447 train_loading_racks_dk (dict): Dictionary of loading racks for dry bulk trains. 448 train_events (list): List to store train events during the simulation. 449 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 450 port_yard_container_terminals (list): List of container terminal yards. 451 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 452 seed (int): The seed value for the random number generator. 453 Yields: 454 simpy.Timeout: A timeout event to simulate the arrival of each train at the port. 455 Returns: 456 None 457 """ 458 initialize_rng(seed) 459 460 train_df = pd.read_csv(f".{run_id}/logs/train_data.csv") 461 train_data = train_df.to_dict(orient="index") 462 463 for train_id, train_info in train_data.items(): 464 yield env.timeout(train_info['arrival at'] - env.now) 465 466 cargo_type = train_info['cargo type'] 467 terminal_id = train_info['terminal_id'] 468 terminal = train_info['terminal'] 469 470 if cargo_type == 'Container': 471 racks = train_loading_racks_ctr[terminal_id] 472 cargo_yard = port_yard_container_terminals[terminal_id] 473 elif cargo_type == 'Liquid': 474 racks = train_loading_racks_liquid[terminal_id] 475 cargo_yard = port_tanks_liquid_terminals[terminal_id] 476 477 if cargo_yard.level >= 0.8 * cargo_yard.capacity: 478 if terminal in liq_terminals_with_pipeline_sink: 479 Pipeline(run_id, env, cargo_yard, mode='sink', 480 rate=constants.PIPELINE_RATE) 481 with open(f'.{run_id}/logs/force_action.txt', 'a') as f: 482 f.write( 483 f"Pipeline from train sink activated {env.now}") 484 f.write('\n') 485 486 if cargo_yard.level <= 0.2 * cargo_yard.capacity: 487 if terminal in liq_terminals_with_pipeline_source: 488 Pipeline(run_id, env, cargo_yard, mode='source', 489 rate=constants.PIPELINE_RATE) 490 with open(f'.{run_id}/logs/force_action.txt', 'a') as f: 491 f.write( 492 f"Pipeline from train source activated {env.now}") 493 f.write('\n') 494 495 elif cargo_type == 'DryBulk': 496 racks = train_loading_racks_dk[terminal_id] 497 cargo_yard = port_silos_drybulk_terminals[terminal_id] 498 499 car_amount = train_info['car amount'] 500 cargo_transfer_rate = train_info['cargo transfer rate'] 501 502 transfer_amount = train_info['total transfer cargo'] 503 import_bool = train_info['import'] 504 export_bool = train_info['export'] 505 506 Train(env, train_id, terminal_id, car_amount, cargo_transfer_rate, racks, 507 cargo_yard, train_events, transfer_amount, import_bool, export_bool, cargo_type) 508 509 510def data_logger(run_id, env, pilots_tugs_data, day_pilots, night_pilots, tugboats): 511 """ 512 This function logs the availability of pilots and tugboats at the port over time. 513 It creates a DataFrame to store the time, number of day pilots, night pilots, and tugboats available at each time step. 514 Args: 515 run_id (str): The unique identifier for the simulation run. 516 env (simpy.Environment): The simulation environment. 517 pilots_tugs_data (pd.DataFrame): DataFrame to store pilots and tugboats data. 518 day_pilots (simpy.Resource): Resource representing day pilots. 519 night_pilots (simpy.Resource): Resource representing night pilots. 520 tugboats (simpy.Resource): Resource representing tugboats. 521 Yields: 522 simpy.Timeout: A timeout event to log the data at each time step. 523 Returns: 524 None 525 """ 526 527 while env.now < constants.SIMULATION_TIME-1: 528 529 new_row = pd.DataFrame([{ 530 'Time': env.now, 531 'Day Pilots': day_pilots.level, 532 'Night Pilots': night_pilots.level, 533 'Tugboats': tugboats.level 534 }]) 535 536 pilots_tugs_data = pd.concat( 537 [pilots_tugs_data, new_row], ignore_index=True) 538 yield env.timeout(1) 539 540 pilots_tugs_data.to_csv( 541 f'.{run_id}/logs/pilots_tugs_data.csv', index=False)
30def initialize_rng(seed): 31 """ 32 Initialize random state objects for repeatable randomness across runs. 33 This function sets the global random number generator states for both the built-in `random` module and NumPy's random module. 34 This is useful for ensuring that the simulation can be reproduced with the same random events. 35 This function should be called at the start of the simulation to ensure that all random processes are initialized with the same seed. 36 Args: 37 seed (int): The seed value for the random number generator. 38 Returns: 39 None 40 """ 41 global rng_random, rng_numpy 42 rng_random = random.Random(seed) 43 rng_numpy = np.random.default_rng(seed)
Initialize random state objects for repeatable randomness across runs.
This function sets the global random number generator states for both the built-in random module and NumPy's random module.
This is useful for ensuring that the simulation can be reproduced with the same random events.
This function should be called at the start of the simulation to ensure that all random processes are initialized with the same seed.
Arguments:
- seed (int): The seed value for the random number generator.
Returns:
None
46def update_availability(run_id, env, it, port_berths_container_terminals, port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data): 47 """ 48 This function updates the availability of berths, yards, tanks, and silos at the port terminals. 49 It calculates the number of available and used resources for each terminal type (Container, Liquid, DryBulk) and appends this data to the respective DataFrames. 50 This function also tracks the queue lengths at each terminal type and updates the availability DataFrames with the current time and resource availability. 51 52 Args: 53 run_id (str): The unique identifier for the simulation run. 54 env (simpy.Environment): The simulation environment. 55 it (int): The current iteration of the simulation. 56 port_berths_container_terminals (list): List of container terminal berths. 57 port_yard_container_terminals (list): List of container terminal yards. 58 port_berths_liquid_terminals (list): List of liquid terminal berths. 59 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 60 port_berths_drybulk_terminals (list): List of dry bulk terminal berths. 61 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 62 availability_df_container (pd.DataFrame): DataFrame to store container terminal availability data. 63 availability_df_liquid (pd.DataFrame): DataFrame to store liquid terminal availability data. 64 availability_df_drybulk (pd.DataFrame): DataFrame to store dry bulk terminal availability data. 65 ship_data (dict): Dictionary containing ship data for the simulation. 66 terminal_data (dict): Dictionary containing terminal data for the simulation. 67 Returns: 68 availability_df_container (pd.DataFrame): Updated DataFrame with container terminal availability data. 69 availability_df_liquid (pd.DataFrame): Updated DataFrame with liquid terminal availability data. 70 availability_df_drybulk (pd.DataFrame): Updated DataFrame with dry bulk terminal availability data. 71 queue_lengths_in_ctr (list): List of queue lengths in container terminals. 72 queue_lengths_in_liq (list): List of queue lengths in liquid terminals. 73 queue_lengths_in_drybulk (list): List of queue lengths in dry bulk terminals. 74 """ 75 # Initialize lists to store vessel availability data for each terminal 76 berth_availability_data_container = [] 77 yard_availability_data = [] 78 berth_availability_data_liquid = [] 79 tank_availability_data = [] 80 berth_availability_data_drybulk = [] 81 silo_availability_data = [] 82 queue_lengths_in_ctr = [] 83 queue_lengths_in_liq = [] 84 queue_lengths_in_drybulk = [] 85 86 # Container Terminals 87 for i, (port_berths, port_yard) in enumerate(zip(port_berths_container_terminals, port_yard_container_terminals)): 88 # Determine the vessel queue length and number of available berths for a container terminal 89 queue = len(port_berths.get_queue) 90 queue_lengths_in_ctr.append(queue) 91 available_port_berths = len(port_berths.items) 92 used_port_berths = get_value_by_terminal( 93 terminal_data, "Container", i+1, "Berths") - available_port_berths 94 berth_availability_data_container.extend( 95 [available_port_berths, used_port_berths, queue]) 96 97 # Determine the number of containers stored on the dock (yard) and the remaining available storage space 98 used_yard = len(port_yard.items) 99 available_yard = get_value_by_terminal( 100 terminal_data, "Container", i+1, "storage volume") - used_yard 101 yard_availability_data.extend( 102 [available_yard, used_yard]) 103 104 # Liquid Terminals 105 for i, port_berths in enumerate(port_berths_liquid_terminals): 106 # Determine the vessel queue length and number of available berths for a liquid bulk terminal 107 queue = len(port_berths.get_queue) 108 queue_lengths_in_liq.append(queue) 109 available_port_berths = len(port_berths.items) 110 used_port_berths = get_value_by_terminal( 111 terminal_data, "Liquid", i+1, "Berths") - available_port_berths 112 berth_availability_data_liquid.extend( 113 [available_port_berths, used_port_berths, queue]) 114 115 for i, port_tanks in enumerate(port_tanks_liquid_terminals): 116 # Determine the amount of storage space used and available in the liquid bulk tanks 117 used_tank = port_tanks.level 118 available_tank = get_value_by_terminal( 119 terminal_data, "Liquid", i+1, "storage volume") - used_tank 120 tank_availability_data.extend([available_tank, used_tank]) 121 122 # Dry Bulk Terminals 123 for i, port_berths in enumerate(port_berths_drybulk_terminals): 124 # Determine the vessel queue length and number of available berths for a dry bulk terminal 125 queue = len(port_berths.get_queue) 126 queue_lengths_in_drybulk.append(queue) 127 available_port_berths = len(port_berths.items) 128 used_port_berths = get_value_by_terminal( 129 terminal_data, "DryBulk", i+1, "Berths") - available_port_berths 130 berth_availability_data_drybulk.extend( 131 [available_port_berths, used_port_berths, queue]) 132 133 for i, port_silos in enumerate(port_silos_drybulk_terminals): 134 # Determine the amount of storage space used and available in the dry bulk silos 135 used_silo = port_silos.level 136 available_silo = get_value_by_terminal( 137 terminal_data, "DryBulk", i+1, "storage volume") - used_silo 138 silo_availability_data.extend([available_silo, used_silo]) 139 140 # Update DataFrames for each terminal type to track storage availability over the span of the simulation 141 availability_container = [ 142 env.now] + berth_availability_data_container + yard_availability_data 143 columns_container = ["Time"] + [f"Terminal_{i+1}_{col}" for i in range(len(port_berths_container_terminals)) for col in ["Available_Berth_Ctr", "Used_Berth_Ctr", "Berth_Queue_Ctr"]] + [ 144 f"Terminal_{i+1}_{col}" for i in range(len(port_yard_container_terminals)) for col in ["Available_Yard", "Used_Yard"]] 145 row_container = pd.DataFrame( 146 [availability_container], columns=columns_container) 147 availability_df_container = pd.concat( 148 [availability_df_container, row_container], ignore_index=True) 149 150 availability_liquid = [env.now] + \ 151 berth_availability_data_liquid + tank_availability_data 152 columns_liquid = ["Time"] + [f"Terminal_{i+1}_{col}" for i in range(len(port_berths_liquid_terminals)) for col in ["Available_Berth_liq", "Used_Berth_liq", "Berth_Queue_liq"]] + [ 153 f"Terminal_{i+1}_{col}" for i in range(len(port_tanks_liquid_terminals)) for col in ["Available_Tank", "Used_Tank"]] 154 row_liquid = pd.DataFrame([availability_liquid], columns=columns_liquid) 155 availability_df_liquid = pd.concat( 156 [availability_df_liquid, row_liquid], ignore_index=True) 157 158 availability_drybulk = [env.now] + \ 159 berth_availability_data_drybulk + silo_availability_data 160 columns_drybulk = ["Time"] + [f"Terminal_{i+1}_{col}" for i in range(len(port_berths_drybulk_terminals)) for col in ["Available_Berth_db", "Used_Berth_db", "Berth_Queue_db"]] + [ 161 f"Terminal_{i+1}_{col}" for i in range(len(port_silos_drybulk_terminals)) for col in ["Available_Silo", "Used_Silo"]] 162 row_drybulk = pd.DataFrame([availability_drybulk], columns=columns_drybulk) 163 availability_df_drybulk = pd.concat( 164 [availability_df_drybulk, row_drybulk], ignore_index=True) 165 166 track_list = get_utilization( 167 availability_df_container, availability_df_liquid, availability_df_drybulk, run_id) 168 169 # innitialise empty dataframes 170 if it == 0: 171 availability_df_liquid = pd.DataFrame() 172 availability_df_drybulk = pd.DataFrame() 173 if it > 2: # save from second iterarion 174 save_track_list(run_id, env.now, track_list) 175 if it == len(ship_data): 176 # Write the data to an Excel file, with different sheets for Container, Liquid Bulk, and Dry Bulk Terminals. 177 file_exists = os.path.isfile("./logs/availability.xlsx") 178 if file_exists: 179 mode = 'a' 180 if_sheet_exists = 'replace' 181 else: 182 mode = 'w' 183 if_sheet_exists = None # Not used in write mode 184 185 with pd.ExcelWriter(f".{run_id}/logs/availability.xlsx", engine="openpyxl", mode=mode, if_sheet_exists=if_sheet_exists) as writer: 186 availability_df_container.to_excel( 187 writer, sheet_name="Container_Terminals", index=False) 188 availability_df_liquid.to_excel( 189 writer, sheet_name="Liquid_Terminals", index=False) 190 availability_df_drybulk.to_excel( 191 writer, sheet_name="Dry_Bulk_Terminals", index=False) 192 193 return availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk
This function updates the availability of berths, yards, tanks, and silos at the port terminals. It calculates the number of available and used resources for each terminal type (Container, Liquid, DryBulk) and appends this data to the respective DataFrames. This function also tracks the queue lengths at each terminal type and updates the availability DataFrames with the current time and resource availability.
Arguments:
- run_id (str): The unique identifier for the simulation run.
- env (simpy.Environment): The simulation environment.
- it (int): The current iteration of the simulation.
- port_berths_container_terminals (list): List of container terminal berths.
- port_yard_container_terminals (list): List of container terminal yards.
- port_berths_liquid_terminals (list): List of liquid terminal berths.
- port_tanks_liquid_terminals (list): List of liquid terminal tanks.
- port_berths_drybulk_terminals (list): List of dry bulk terminal berths.
- port_silos_drybulk_terminals (list): List of dry bulk terminal silos.
- availability_df_container (pd.DataFrame): DataFrame to store container terminal availability data.
- availability_df_liquid (pd.DataFrame): DataFrame to store liquid terminal availability data.
- availability_df_drybulk (pd.DataFrame): DataFrame to store dry bulk terminal availability data.
- ship_data (dict): Dictionary containing ship data for the simulation.
- terminal_data (dict): Dictionary containing terminal data for the simulation.
Returns:
availability_df_container (pd.DataFrame): Updated DataFrame with container terminal availability data. availability_df_liquid (pd.DataFrame): Updated DataFrame with liquid terminal availability data. availability_df_drybulk (pd.DataFrame): Updated DataFrame with dry bulk terminal availability data. queue_lengths_in_ctr (list): List of queue lengths in container terminals. queue_lengths_in_liq (list): List of queue lengths in liquid terminals. queue_lengths_in_drybulk (list): List of queue lengths in dry bulk terminals.
196def create_containers(ship_id, ship_info): 197 """ 198 This function creates a list of Container objects to be loaded and unloaded from a ship. 199 It generates the number of containers based on the ship's information, specifically the width and the number of containers to load and unload. 200 Args: 201 ship_id (str): The unique identifier for the ship. 202 ship_info (dict): A dictionary containing information about the ship, including its width and the number of containers to load and unload. 203 Returns: 204 containers_to_unload (list): A list of Container objects representing the containers to be unloaded from the ship. 205 containers_to_load (list): A list of Container objects representing the containers to be loaded onto the ship. 206 """ 207 containers_to_unload = [Container(id=f"{ship_id}_unload_{j}", width=ship_info['width']) for j in range( 208 int(ship_info['num_container_or_liq_tons_or_dry_tons_to_unload']))] 209 containers_to_load = [Container(id=f"{ship_id}_load_{j}", width=ship_info['width'])for j in range( 210 int(ship_info['num_container_or_liq_tons_or_dry_tons_to_load']))] 211 return containers_to_unload, containers_to_load
This function creates a list of Container objects to be loaded and unloaded from a ship. It generates the number of containers based on the ship's information, specifically the width and the number of containers to load and unload.
Arguments:
- ship_id (str): The unique identifier for the ship.
- ship_info (dict): A dictionary containing information about the ship, including its width and the number of containers to load and unload.
Returns:
containers_to_unload (list): A list of Container objects representing the containers to be unloaded from the ship. containers_to_load (list): A list of Container objects representing the containers to be loaded onto the ship.
214def tons_to_unload_load(ship_id, ship_info): 215 """ 216 This function retrieves the number of tons to unload and load for a ship based on its information. 217 Args: 218 ship_id (str): The unique identifier for the ship. 219 ship_info (dict): A dictionary containing information about the ship, including the number of tons to unload and load. 220 Returns: 221 unload_tons (int): The number of tons to unload from the ship. 222 load_tons (int): The number of tons to load onto the ship. 223 """ 224 unload_tons = ship_info['num_container_or_liq_tons_or_dry_tons_to_unload'] 225 load_tons = ship_info['num_container_or_liq_tons_or_dry_tons_to_load'] 226 return unload_tons, load_tons
This function retrieves the number of tons to unload and load for a ship based on its information.
Arguments:
- ship_id (str): The unique identifier for the ship.
- ship_info (dict): A dictionary containing information about the ship, including the number of tons to unload and load.
Returns:
unload_tons (int): The number of tons to unload from the ship. load_tons (int): The number of tons to load onto the ship.
230def ship_generator(run_id, env, chassis_bays_utilization, port_berths_container_terminals, port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, 231 port_berths_drybulk_terminals, port_silos_drybulk_terminals, channel, day_pilots, night_pilots, tugboats, events, ship_logs, channel_events, channel_logs, SHIPS_IN_ANCHORAGE, SHIPS_IN_CHANNEL, ship_data, terminal_data, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink): 232 """ 233 This function generates ships and processes them in the simulation environment. 234 It iterates through the ship data, simulating the arrival of each ship at the port and processing it based on its type (Container, Liquid, DryBulk). 235 The ContainerTerminal, LiquidTerminal, and DryBulkTerminal classes simulates the vessel's operations at the port. 236 It updates the availability of resources at the port terminals and tracks the number of ships in the channel and anchorage. 237 Args: 238 run_id (str): The unique identifier for the simulation run. 239 env (simpy.Environment): The simulation environment. 240 chassis_bays_utilization (dict): A dictionary to track chassis bays utilization. 241 port_berths_container_terminals (list): List of container terminal berths. 242 port_yard_container_terminals (list): List of container terminal yards. 243 port_berths_liquid_terminals (list): List of liquid terminal berths. 244 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 245 port_berths_drybulk_terminals (list): List of dry bulk terminal berths. 246 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 247 channel (Channel): The channel object for managing ship movements. 248 day_pilots (int): Number of day pilots available. 249 night_pilots (int): Number of night pilots available. 250 tugboats (int): Number of tugboats available. 251 events (list): List to store events during the simulation. 252 ship_logs (list): List to store ship logs during the simulation. 253 channel_events (list): List to store channel events during the simulation. 254 channel_logs (list): List to store channel logs during the simulation. 255 SHIPS_IN_ANCHORAGE (list): List to track ships in anchorage by type. 256 SHIPS_IN_CHANNEL (list): List to track ships in the channel. 257 ship_data (dict): Dictionary containing ship data for the simulation. 258 terminal_data (dict): Dictionary containing terminal data for the simulation. 259 liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections. 260 liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections. 261 Yields: 262 simpy.Timeout: A timeout event to simulate the arrival of each ship at the port. 263 Returns: 264 None 265 """ 266 i = 0 267 terminal_data = terminal_data 268 269 availability_df_container = pd.DataFrame() 270 availability_df_liquid = pd.DataFrame() 271 availability_df_drybulk = pd.DataFrame() 272 273 queue_ctr_its = [] 274 queue_liq_its = [] 275 queue_drybulk_its = [] 276 277 SHIPS_IN_CHANNEL_TRACK = [] 278 SHIPS_IN_ANCHORAGE_TRACK = [] 279 CONTAINER_VES_IN_ANCHORAGE = [] 280 LIQUID_VES_IN_ANCHORAGE = [] 281 DRYBULK_VES_IN_ANCHORAGE = [] 282 283 it = 0 284 285 for ship_id, ship_info in tqdm(ship_data.items(), desc=f"Simulation Progress {run_id}"): 286 it += 1 287 yield env.timeout(ship_info['arrival'] - env.now) 288 if ship_data[ship_id]['ship_type'] == 'Container': 289 SHIPS_IN_ANCHORAGE[0] += 1 290 elif ship_data[ship_id]['ship_type'] == 'Liquid': 291 SHIPS_IN_ANCHORAGE[1] += 1 292 elif ship_data[ship_id]['ship_type'] == 'DryBulk': 293 SHIPS_IN_ANCHORAGE[2] += 1 294 295 selected_port = ship_info["terminal"] - 1 296 last_section = ship_info["last_section"] 297 298 # Container ship processes 299 if ship_info['ship_type'] == 'Container': 300 port_berths = port_berths_container_terminals[selected_port] 301 port_yard = port_yard_container_terminals[selected_port] 302 selected_terminal = selected_port + 1 303 availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk = update_availability(run_id, env, it, port_berths_container_terminals, 304 port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data) 305 queue_ctr_its.append(queue_lengths_in_ctr) 306 containers_to_unload, containers_to_load = create_containers( 307 ship_id, ship_info) 308 transfer_rate = get_value_by_terminal( 309 terminal_data, "Container", terminal_id=ship_info["terminal"], resource_name="transfer rate per unit") 310 transfer_time = 1 / transfer_rate 311 ContainerTerminal(env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal=selected_terminal, id=ship_id, ship_type=ship_info['ship_type'], draft=ship_info['draft'], 312 width=ship_info['width'], unload_time_per_container=transfer_time, load_time_per_container=transfer_time, containers_to_unload=containers_to_unload, containers_to_load=containers_to_load, events=events, ship_logs=ship_logs, port_berths=port_berths, port_yard=port_yard, SHIPS_IN_CHANNEL=SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE=SHIPS_IN_ANCHORAGE, terminal_data=terminal_data) 313 314 # Liquid and Dry Bulk ship processes 315 elif ship_info['ship_type'] == 'Liquid': 316 port_berths = port_berths_liquid_terminals[selected_port] 317 port_tanks = port_tanks_liquid_terminals[selected_port] 318 selected_terminal = selected_port + 1 319 availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk = update_availability(run_id, env, it, port_berths_container_terminals, 320 port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data) 321 queue_liq_its.append(queue_lengths_in_liq) 322 unload_tons, load_tons = tons_to_unload_load(ship_id, ship_info) 323 transfer_rate = get_value_by_terminal( 324 terminal_data, "Liquid", terminal_id=ship_info["terminal"], resource_name="transfer rate per unit") 325 transfer_time = 1 / transfer_rate 326 LiquidTerminal(env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal, id=ship_id, ship_type=ship_info['ship_type'], draft=ship_info['draft'], 327 width=ship_info['width'], unload_time=transfer_time, load_time=transfer_time, unload_tons=unload_tons, load_tons=load_tons, events=events, ship_logs=ship_logs, port_berths=port_berths, port_tanks=port_tanks, SHIPS_IN_CHANNEL=SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE=SHIPS_IN_ANCHORAGE, terminal_data=terminal_data, liq_terminals_with_pipeline_source=liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink=liq_terminals_with_pipeline_sink) 328 329 # Dry Bulk ship processes 330 elif ship_info['ship_type'] == 'DryBulk': 331 port_berths = port_berths_drybulk_terminals[selected_port] 332 port_silos = port_silos_drybulk_terminals[selected_port] 333 selected_terminal = selected_port + 1 334 availability_df_container, availability_df_liquid, availability_df_drybulk, queue_lengths_in_ctr, queue_lengths_in_liq, queue_lengths_in_drybulk = update_availability(run_id, env, it, port_berths_container_terminals, 335 port_yard_container_terminals, port_berths_liquid_terminals, port_tanks_liquid_terminals, port_berths_drybulk_terminals, port_silos_drybulk_terminals, availability_df_container, availability_df_liquid, availability_df_drybulk, ship_data, terminal_data) 336 queue_drybulk_its.append(queue_lengths_in_drybulk) 337 unload_tons, load_tons = tons_to_unload_load(ship_id, ship_info) 338 transfer_rate = get_value_by_terminal( 339 terminal_data, "DryBulk", terminal_id=ship_info["terminal"], resource_name="transfer rate per unit") 340 transfer_time = 1 / transfer_rate 341 DryBulkTerminal(env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal=selected_terminal, id=ship_id, ship_type=ship_info['ship_type'], draft=ship_info 342 ['draft'], width=ship_info['width'], unload_time=transfer_time, load_time=transfer_time, unload_tons=unload_tons, load_tons=load_tons, events=events, ship_logs=ship_logs, port_berths=port_berths, port_silos=port_silos, SHIPS_IN_CHANNEL=SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE=SHIPS_IN_ANCHORAGE, terminal_data=terminal_data) 343 344 i += 1 # Move to the next vessel in the input file 345 SHIPS_IN_CHANNEL_TRACK.append((env.now, SHIPS_IN_CHANNEL[0])) 346 SHIPS_IN_ANCHORAGE_TRACK.append( 347 (env.now, SHIPS_IN_ANCHORAGE[0] + SHIPS_IN_ANCHORAGE[1] + SHIPS_IN_ANCHORAGE[2])) 348 CONTAINER_VES_IN_ANCHORAGE.append((env.now, SHIPS_IN_ANCHORAGE[0])) 349 LIQUID_VES_IN_ANCHORAGE.append((env.now, SHIPS_IN_ANCHORAGE[1])) 350 DRYBULK_VES_IN_ANCHORAGE.append((env.now, SHIPS_IN_ANCHORAGE[2]))
This function generates ships and processes them in the simulation environment. It iterates through the ship data, simulating the arrival of each ship at the port and processing it based on its type (Container, Liquid, DryBulk). The ContainerTerminal, LiquidTerminal, and DryBulkTerminal classes simulates the vessel's operations at the port. It updates the availability of resources at the port terminals and tracks the number of ships in the channel and anchorage.
Arguments:
- run_id (str): The unique identifier for the simulation run.
- env (simpy.Environment): The simulation environment.
- chassis_bays_utilization (dict): A dictionary to track chassis bays utilization.
- port_berths_container_terminals (list): List of container terminal berths.
- port_yard_container_terminals (list): List of container terminal yards.
- port_berths_liquid_terminals (list): List of liquid terminal berths.
- port_tanks_liquid_terminals (list): List of liquid terminal tanks.
- port_berths_drybulk_terminals (list): List of dry bulk terminal berths.
- port_silos_drybulk_terminals (list): List of dry bulk terminal silos.
- channel (Channel): The channel object for managing ship movements.
- day_pilots (int): Number of day pilots available.
- night_pilots (int): Number of night pilots available.
- tugboats (int): Number of tugboats available.
- events (list): List to store events during the simulation.
- ship_logs (list): List to store ship logs during the simulation.
- channel_events (list): List to store channel events during the simulation.
- channel_logs (list): List to store channel logs during the simulation.
- SHIPS_IN_ANCHORAGE (list): List to track ships in anchorage by type.
- SHIPS_IN_CHANNEL (list): List to track ships in the channel.
- ship_data (dict): Dictionary containing ship data for the simulation.
- terminal_data (dict): Dictionary containing terminal data for the simulation.
- liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections.
- liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections.
Yields:
simpy.Timeout: A timeout event to simulate the arrival of each ship at the port.
Returns:
None
354def truck_generator(run_id, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, env, terminal_tuple_cache, port_tanks_liquid_terminals, port_yard_container_terminals, port_silos_drybulk_terminals, port_loading_bays_liquid_terminals, port_drybulk_bays_drybulk_terminals, port_chassis_container_terminals, truck_gates_ctr, truck_gates_liquid, truck_gates_dk, events, seed, terminal_data): 355 """ 356 This function generates trucks and processes them in the simulation environment. 357 It iterates through the truck data, simulating the arrival of each truck at the port and processing it based on its type (Container, Liquid, DryBulk). 358 It creates Truck objects and initializes them with the appropriate parameters based on the truck type and terminal information. (This simulates each truck's processing at the port.) 359 Args: 360 run_id (str): The unique identifier for the simulation run. 361 liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections. 362 liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections. 363 chassis_bays_utilization (dict): A dictionary to track chassis bays utilization. 364 env (simpy.Environment): The simulation environment. 365 terminal_tuple_cache (dict): A cache of terminal tuples for quick access to terminal data. 366 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 367 port_yard_container_terminals (list): List of container terminal yards. 368 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 369 port_loading_bays_liquid_terminals (list): List of liquid terminal loading bays. 370 port_drybulk_bays_drybulk_terminals (list): List of dry bulk terminal loading bays. 371 port_chassis_container_terminals (list): List of container terminal chassis bays. 372 truck_gates_ctr (list): List of container terminal truck gates. 373 truck_gates_liquid (list): List of liquid terminal truck gates. 374 truck_gates_dk (list): List of dry bulk terminal truck gates. 375 events (list): List to store events during the simulation. 376 seed (int): The seed value for the random number generator. 377 terminal_data (dict): Dictionary containing terminal data for the simulation. 378 Yields: 379 simpy.Timeout: A timeout event to simulate the arrival of each truck at the port. 380 Returns: 381 None 382 """ 383 384 initialize_rng(seed) 385 386 truck_data = pd.read_pickle(f".{run_id}/logs/truck_data.pkl") 387 truck_data = truck_data.to_dict(orient="index") 388 389 for truck_id, truck_info in truck_data.items(): 390 yield env.timeout(truck_info['arrival'] - env.now) 391 if truck_info['truck_type'] == 'Container': 392 terminal_type = "Container" 393 terminal_id = truck_info['terminal_id'] 394 port_tanks = None 395 port_yard = port_yard_container_terminals[terminal_id] 396 port_silos = None 397 loading_bays = None 398 drybulk_bays = None 399 truck_chassis = port_chassis_container_terminals[terminal_id] 400 container_load_amount = get_value_from_terminal_tuple( 401 terminal_tuple_cache, "Container", terminal_id=terminal_id+1, resource_name="truck payload size") 402 container_unload_amount = get_value_from_terminal_tuple( 403 terminal_tuple_cache, "Container", terminal_id=terminal_id+1, resource_name="truck payload size") 404 container_amount = (container_load_amount, container_unload_amount) 405 Truck(env, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, truck_id, run_id, terminal_type, terminal_id+1, container_amount, 406 None, None, loading_bays, port_tanks, truck_chassis, port_yard, port_silos, drybulk_bays, events, seed=rng_random.randint(1, 100000000), terminal_data=terminal_data) 407 elif truck_info['truck_type'] == 'Liquid': 408 terminal_type = "Liquid" 409 terminal_id = truck_info['terminal_id'] 410 port_tanks = port_tanks_liquid_terminals[terminal_id] 411 port_yard = None 412 port_silos = None 413 loading_bays = port_loading_bays_liquid_terminals[terminal_id] 414 drybulk_bays = None 415 truck_chassis = None 416 liquid_amount = get_value_from_terminal_tuple( 417 terminal_tuple_cache, "Liquid", terminal_id=terminal_id+1, resource_name="truck payload size") 418 Truck(env, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, truck_id, run_id, terminal_type, terminal_id+1, None, liquid_amount, 419 None, loading_bays, port_tanks, truck_chassis, port_yard, port_silos, drybulk_bays, events, seed=rng_random.randint(1, 100000000), terminal_data=terminal_data) 420 elif truck_info['truck_type'] == 'DryBulk': 421 terminal_type = "DryBulk" 422 terminal_id = truck_info['terminal_id'] 423 port_tanks = None 424 port_yard = None 425 port_silos = port_silos_drybulk_terminals[terminal_id] 426 loading_bays = None 427 drybulk_bays = port_drybulk_bays_drybulk_terminals[terminal_id] 428 truck_chassis = None 429 drybulk_amount = get_value_from_terminal_tuple( 430 terminal_tuple_cache, "DryBulk", terminal_id=terminal_id+1, resource_name="truck payload size") 431 Truck(env, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, chassis_bays_utilization, truck_id, run_id, terminal_type, terminal_id+1, None, None, 432 drybulk_amount, loading_bays, port_tanks, truck_chassis, port_yard, port_silos, drybulk_bays, events, seed=rng_random.randint(1, 100000000), terminal_data=terminal_data)
This function generates trucks and processes them in the simulation environment. It iterates through the truck data, simulating the arrival of each truck at the port and processing it based on its type (Container, Liquid, DryBulk). It creates Truck objects and initializes them with the appropriate parameters based on the truck type and terminal information. (This simulates each truck's processing at the port.)
Arguments:
- run_id (str): The unique identifier for the simulation run.
- liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections.
- liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections.
- chassis_bays_utilization (dict): A dictionary to track chassis bays utilization.
- env (simpy.Environment): The simulation environment.
- terminal_tuple_cache (dict): A cache of terminal tuples for quick access to terminal data.
- port_tanks_liquid_terminals (list): List of liquid terminal tanks.
- port_yard_container_terminals (list): List of container terminal yards.
- port_silos_drybulk_terminals (list): List of dry bulk terminal silos.
- port_loading_bays_liquid_terminals (list): List of liquid terminal loading bays.
- port_drybulk_bays_drybulk_terminals (list): List of dry bulk terminal loading bays.
- port_chassis_container_terminals (list): List of container terminal chassis bays.
- truck_gates_ctr (list): List of container terminal truck gates.
- truck_gates_liquid (list): List of liquid terminal truck gates.
- truck_gates_dk (list): List of dry bulk terminal truck gates.
- events (list): List to store events during the simulation.
- seed (int): The seed value for the random number generator.
- terminal_data (dict): Dictionary containing terminal data for the simulation.
Yields:
simpy.Timeout: A timeout event to simulate the arrival of each truck at the port.
Returns:
None
435def train_generator(run_id, liq_terminals_with_pipeline_source, liq_terminals_with_pipeline_sink, env, terminal_tuple_cache, train_loading_racks_ctr, train_loading_racks_liquid, train_loading_racks_dk, train_events, port_tanks_liquid_terminals, port_yard_container_terminals, port_silos_drybulk_terminals, seed): 436 """ 437 This function generates trains and processes them in the simulation environment. 438 It iterates through the train data, simulating the arrival of each train at the port and processing it based on its cargo type (Container, Liquid, DryBulk). 439 It creates Train objects and initializes them with the appropriate parameters based on the train type and terminal information. (This simulates each train's processing at the port.) 440 Args: 441 run_id (str): The unique identifier for the simulation run. 442 liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections. 443 liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections. 444 env (simpy.Environment): The simulation environment. 445 terminal_tuple_cache (dict): A cache of terminal tuples for quick access to terminal data. 446 train_loading_racks_ctr (dict): Dictionary of loading racks for container trains. 447 train_loading_racks_liquid (dict): Dictionary of loading racks for liquid trains. 448 train_loading_racks_dk (dict): Dictionary of loading racks for dry bulk trains. 449 train_events (list): List to store train events during the simulation. 450 port_tanks_liquid_terminals (list): List of liquid terminal tanks. 451 port_yard_container_terminals (list): List of container terminal yards. 452 port_silos_drybulk_terminals (list): List of dry bulk terminal silos. 453 seed (int): The seed value for the random number generator. 454 Yields: 455 simpy.Timeout: A timeout event to simulate the arrival of each train at the port. 456 Returns: 457 None 458 """ 459 initialize_rng(seed) 460 461 train_df = pd.read_csv(f".{run_id}/logs/train_data.csv") 462 train_data = train_df.to_dict(orient="index") 463 464 for train_id, train_info in train_data.items(): 465 yield env.timeout(train_info['arrival at'] - env.now) 466 467 cargo_type = train_info['cargo type'] 468 terminal_id = train_info['terminal_id'] 469 terminal = train_info['terminal'] 470 471 if cargo_type == 'Container': 472 racks = train_loading_racks_ctr[terminal_id] 473 cargo_yard = port_yard_container_terminals[terminal_id] 474 elif cargo_type == 'Liquid': 475 racks = train_loading_racks_liquid[terminal_id] 476 cargo_yard = port_tanks_liquid_terminals[terminal_id] 477 478 if cargo_yard.level >= 0.8 * cargo_yard.capacity: 479 if terminal in liq_terminals_with_pipeline_sink: 480 Pipeline(run_id, env, cargo_yard, mode='sink', 481 rate=constants.PIPELINE_RATE) 482 with open(f'.{run_id}/logs/force_action.txt', 'a') as f: 483 f.write( 484 f"Pipeline from train sink activated {env.now}") 485 f.write('\n') 486 487 if cargo_yard.level <= 0.2 * cargo_yard.capacity: 488 if terminal in liq_terminals_with_pipeline_source: 489 Pipeline(run_id, env, cargo_yard, mode='source', 490 rate=constants.PIPELINE_RATE) 491 with open(f'.{run_id}/logs/force_action.txt', 'a') as f: 492 f.write( 493 f"Pipeline from train source activated {env.now}") 494 f.write('\n') 495 496 elif cargo_type == 'DryBulk': 497 racks = train_loading_racks_dk[terminal_id] 498 cargo_yard = port_silos_drybulk_terminals[terminal_id] 499 500 car_amount = train_info['car amount'] 501 cargo_transfer_rate = train_info['cargo transfer rate'] 502 503 transfer_amount = train_info['total transfer cargo'] 504 import_bool = train_info['import'] 505 export_bool = train_info['export'] 506 507 Train(env, train_id, terminal_id, car_amount, cargo_transfer_rate, racks, 508 cargo_yard, train_events, transfer_amount, import_bool, export_bool, cargo_type)
This function generates trains and processes them in the simulation environment. It iterates through the train data, simulating the arrival of each train at the port and processing it based on its cargo type (Container, Liquid, DryBulk). It creates Train objects and initializes them with the appropriate parameters based on the train type and terminal information. (This simulates each train's processing at the port.)
Arguments:
- run_id (str): The unique identifier for the simulation run.
- liq_terminals_with_pipeline_source (list): List of liquid terminals with pipeline source connections.
- liq_terminals_with_pipeline_sink (list): List of liquid terminals with pipeline sink connections.
- env (simpy.Environment): The simulation environment.
- terminal_tuple_cache (dict): A cache of terminal tuples for quick access to terminal data.
- train_loading_racks_ctr (dict): Dictionary of loading racks for container trains.
- train_loading_racks_liquid (dict): Dictionary of loading racks for liquid trains.
- train_loading_racks_dk (dict): Dictionary of loading racks for dry bulk trains.
- train_events (list): List to store train events during the simulation.
- port_tanks_liquid_terminals (list): List of liquid terminal tanks.
- port_yard_container_terminals (list): List of container terminal yards.
- port_silos_drybulk_terminals (list): List of dry bulk terminal silos.
- seed (int): The seed value for the random number generator.
Yields:
simpy.Timeout: A timeout event to simulate the arrival of each train at the port.
Returns:
None
511def data_logger(run_id, env, pilots_tugs_data, day_pilots, night_pilots, tugboats): 512 """ 513 This function logs the availability of pilots and tugboats at the port over time. 514 It creates a DataFrame to store the time, number of day pilots, night pilots, and tugboats available at each time step. 515 Args: 516 run_id (str): The unique identifier for the simulation run. 517 env (simpy.Environment): The simulation environment. 518 pilots_tugs_data (pd.DataFrame): DataFrame to store pilots and tugboats data. 519 day_pilots (simpy.Resource): Resource representing day pilots. 520 night_pilots (simpy.Resource): Resource representing night pilots. 521 tugboats (simpy.Resource): Resource representing tugboats. 522 Yields: 523 simpy.Timeout: A timeout event to log the data at each time step. 524 Returns: 525 None 526 """ 527 528 while env.now < constants.SIMULATION_TIME-1: 529 530 new_row = pd.DataFrame([{ 531 'Time': env.now, 532 'Day Pilots': day_pilots.level, 533 'Night Pilots': night_pilots.level, 534 'Tugboats': tugboats.level 535 }]) 536 537 pilots_tugs_data = pd.concat( 538 [pilots_tugs_data, new_row], ignore_index=True) 539 yield env.timeout(1) 540 541 pilots_tugs_data.to_csv( 542 f'.{run_id}/logs/pilots_tugs_data.csv', index=False)
This function logs the availability of pilots and tugboats at the port over time. It creates a DataFrame to store the time, number of day pilots, night pilots, and tugboats available at each time step.
Arguments:
- run_id (str): The unique identifier for the simulation run.
- env (simpy.Environment): The simulation environment.
- pilots_tugs_data (pd.DataFrame): DataFrame to store pilots and tugboats data.
- day_pilots (simpy.Resource): Resource representing day pilots.
- night_pilots (simpy.Resource): Resource representing night pilots.
- tugboats (simpy.Resource): Resource representing tugboats.
Yields:
simpy.Timeout: A timeout event to log the data at each time step.
Returns:
None