simulation_classes.terminal_drybulk
Uses dry bulk terminal class objects to create dry bulk terminal processes. Vessel arrivals, berth and conveyor allocation, cargo unloading, cargo loading, vessel waiting, and vessel departure are simulated.
1""" 2Uses dry bulk terminal class objects to create dry bulk terminal processes. 3Vessel arrivals, berth and conveyor allocation, cargo unloading, cargo loading, vessel waiting, and vessel departure are simulated. 4""" 5from simulation_analysis.results import update_ship_logs 6from simulation_handler.helpers import get_value_by_terminal 7from simulation_handler.helpers import is_daytime 8import constants 9 10TIME_COMMON_CHANNEL = constants.TIME_COMMON_CHANNEL 11TIME_FOR_TUG_STEER = constants.TIME_FOR_TUG_STEER 12TIME_FOR_UTURN = constants.TIME_FOR_UTURN 13DRY_BULK_EFFICIENCY = constants.DRYBULK_TERMINAL_EFFICIENCY 14 15nan = float('nan') 16 17 18class DryBulkTerminal: 19 """ 20 Represents a dry bulk terminal and the process of handling arriving ships. 21 This class simulates the arrival, berthing, unloading, loading, waiting, and departure of dry bulk ships at the terminal. 22 It manages the allocation of berths and conveyors, handles cargo operations, and logs events during the simulation. 23 The process includes the following steps: 24 1. Ship arrival at the terminal. 25 2. Anchorage waiting period. 26 3. Berth allocation for the ship. 27 4. Channel process for entering the terminal. 28 5. Docking the ship to the berth. 29 6. Unloading dry bulk cargo from the ship. 30 7. Loading dry bulk cargo onto the ship. 31 8. Waiting at the terminal until the ship is ready to depart. 32 9. Detaching the ship from the berth and departing from the terminal. 33 10. Channel process for leaving the terminal. 34 The class also logs events and maintains ship logs throughout the process. 35 36 Args: 37 env (SimPy.Environment): The simulation environment. 38 chassis_bays_utilization (float): Utilization of chassis bays. 39 run_id (int): Unique identifier for the simulation run. 40 channel (Channel): The channel object for managing ship movements. 41 day_pilots (simpy.Container): Simpy container of available day pilots. 42 night_pilots (simpy.Container) : Simpy container of available night pilots. 43 tugboats (simpy.Container): Simpy container of available tugboats. 44 ship_info (dict): Information about the ships. 45 last_section (str): The last section of the channel. 46 selected_terminal (str): The terminal selected for the ship. 47 id (int): Unique identifier for the ship. 48 ship_type (str): Type of the ship (e.g., "DryBulk"). 49 draft (float): Draft of the ship. 50 width (float): Width of the ship. 51 unload_time (float): Time taken to unload cargo (in hours). 52 load_time (float): Time taken to load cargo (in hours). 53 unload_tons (float): Amount of cargo to unload (in tons). 54 load_tons (float): Amount of cargo to load (in tons). 55 events (list): List to store events during the simulation. 56 ship_logs (list): List to store logs of the ship's activities. 57 port_berths (simpy.Resource): Resource for managing port berths. 58 port_silos (simpy.Resource): Resource for managing port silos. 59 SHIPS_IN_CHANNEL (int): Number of ships allowed in the channel. 60 SHIPS_IN_ANCHORAGE (int): Number of ships allowed in the anchorage. 61 terminal_data (dict): Data related to the terminal. 62 """ 63 def __init__(self, env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal, id, ship_type, draft, width, unload_time, load_time, unload_tons, load_tons, events, ship_logs, port_berths, port_silos, SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE, terminal_data): 64 # Initialize the environment 65 self.env = env 66 self.run_id = run_id 67 self.SHIPS_IN_CHANNEL = SHIPS_IN_CHANNEL 68 self.SHIPS_IN_ANCHORAGE = SHIPS_IN_ANCHORAGE 69 self.terminal_data = terminal_data 70 71 # Initalise the channel 72 self.channel = channel 73 self.ship_info = ship_info 74 self.last_section = last_section 75 76 # day_pilots, night_pilots and tugboats 77 self.day_pilots, self.night_pilots = day_pilots, night_pilots 78 self.tugboats = tugboats 79 80 # Set vessel and terminal attributes 81 self.id = id 82 self.selected_terminal = selected_terminal 83 self.ship_type = ship_type 84 self.num_conveyors = get_value_by_terminal( 85 self.terminal_data, 'DryBulk', selected_terminal, 'transfer units per berth') 86 self.unload_tons = unload_tons 87 self.load_tons = load_tons 88 self.time_for_uturn_min, self.time_for_uturn_max = TIME_FOR_UTURN 89 90 self.draft = draft 91 self.width = width 92 self.unload_time = unload_time 93 self.load_time = load_time 94 95 # wait time proxy 96 self.cargo_wait_time = 0 97 98 # The allocation of conveyors and berth will be done during the process 99 self.current_berth = None 100 self.allocated_conveyors = [] 101 102 # Get the ship logs and events 103 self.events = events 104 self.ship_logs = ship_logs 105 106 # Allocate berth and conveyors 107 self.port_berths = port_berths 108 self.port_silos = port_silos 109 110 # Start the process 111 self.env.process(self.process()) 112 113 def process(self): 114 """ 115 The main process for the dry bulk terminal, simulating the arrival, berthing, unloading, loading, waiting, and departure of ships. 116 This method handles the entire lifecycle of a dry bulk ship at the terminal. 117 """ 118 119 self.ship_logs = update_ship_logs(self.ship_logs, "D", self.id, self.selected_terminal, ship_start_time=nan, time_to_get_berth=nan, time_for_restriction_in=nan, time_to_get_pilot_in=nan, 120 time_to_get_tugs_in=nan, time_to_common_channel_in=nan, time_to_travel_channel_in=nan, time_to_tug_steer_in=nan, unloading_time=nan, loading_time=nan, waiting_time=nan, 121 departure_time=nan, time_to_get_pilot_out=nan, time_to_get_tugs_out=nan, time_to_tug_steer_out=nan, time_for_uturn=nan, 122 time_to_travel_channel_out=nan, time_to_common_channel_out=nan, ship_end_time=nan) 123 124 # ship arrival 125 ship_start_time = self.env.now 126 update_ship_logs(self.ship_logs, "D", self.id, 127 self.selected_terminal, ship_start_time=ship_start_time) 128 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 129 "arrive", self.env.now, f"Ship {self.id} arrived at the port")) 130 131 # Anchorage waiting 132 yield self.env.timeout(constants.ANCHORAGE_WAITING_DRYBULK) 133 134 # Berth allocation 135 start_time = self.env.now 136 yield self.env.process(self.arrive_and_berth()) 137 time_to_get_berth = self.env.now - ship_start_time 138 update_ship_logs(self.ship_logs, "D", self.id, 139 self.selected_terminal, time_to_get_berth=time_to_get_berth) 140 141 # Channel process (in) 142 self.day = is_daytime(self.env.now, 7, 19) 143 yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "D", self.run_id)) 144 145 # Terminal process 146 self.events.append((f"Ship_{self.id}", "DryBulk", f"L.{self.selected_terminal}", "dock", self.env.now, 147 f"Ship {self.id} docked to berth {self.current_berth.id} with waiting time {'N/A'}")) 148 149 start_time = self.env.now 150 yield self.env.process(self.unload()) 151 unloading_time = self.env.now - start_time 152 update_ship_logs(self.ship_logs, "D", self.id, 153 self.selected_terminal, unloading_time=unloading_time) 154 155 start_time = self.env.now 156 yield self.env.process(self.load()) 157 loading_time = self.env.now - start_time 158 update_ship_logs(self.ship_logs, "D", self.id, 159 self.selected_terminal, loading_time=loading_time) 160 161 start_time = self.env.now 162 yield self.env.process(self.wait()) 163 waiting_time = self.env.now - start_time 164 update_ship_logs(self.ship_logs, "D", self.id, 165 self.selected_terminal, waiting_time=waiting_time) 166 167 start_time = self.env.now 168 yield self.env.process(self.detach_and_depart()) 169 departure_time = self.env.now - start_time 170 update_ship_logs(self.ship_logs, "D", self.id, 171 self.selected_terminal, departure_time=departure_time) 172 173 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", "undock", self.env.now, 174 f"Ship {self.id} undocked to berth {self.current_berth.id} with waiting time {'N/A'}")) 175 176 # Channel process (out) 177 self.day = is_daytime(self.env.now, 7, 19) 178 yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "D", self.run_id)) 179 180 ship_end_time = self.env.now 181 update_ship_logs(self.ship_logs, "D", self.id, 182 self.selected_terminal, ship_end_time=ship_end_time) 183 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", "depart", self.env.now, 184 f"Ship {self.id} departed from berth {self.current_berth.id} with waiting time {'N/A'}")) 185 186 def arrive_and_berth(self): 187 """ 188 Represents the process of a vessel arriving at the terminal and seizing a berth. 189 This method handles the allocation of a berth for the vessel and prepares it for unloading and loading operations. 190 """ 191 self.current_berth = yield self.port_berths.get() 192 193 def unload(self): 194 """ 195 Represents the unloading of dry bulk cargo from the vessel at the terminal. 196 This method allocates the required number of conveyors, unloads the cargo, and stores it in the port silos. 197 It also handles the waiting time until the unloading is completed, ensuring that it only proceeds during daytime hours. 198 The unloading process is divided into several steps: 199 1. Check if the terminal has the required import conveyors. 200 2. Allocate the conveyors for unloading. 201 3. Unload the dry bulk cargo from the vessel. 202 4. Wait until the unloading is completed, ensuring it only occurs during daytime hours. 203 5. Store the unloaded dry bulk cargo in the port silos. 204 """ 205 # Request for the required number of conveyors 206 import_terminal = get_value_by_terminal( 207 self.terminal_data, 'DryBulk', self.selected_terminal, 'import') 208 if import_terminal: 209 for _ in range(self.num_conveyors): 210 conveyor = yield self.current_berth.conveyors.get() 211 self.allocated_conveyors.append(conveyor) 212 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 213 "all_conveyors_attached", self.env.now, f"Ship {self.id} conveyors allocated")) 214 # Unload dry bulk cargo 215 total_unload_time = self.unload_tons * self.unload_time / self.num_conveyors 216 self.cargo_wait_time += total_unload_time 217 # keep waiting until day time 218 while total_unload_time > 0: 219 if not is_daytime(self.env.now, start=7, end=19): 220 yield self.env.timeout(1) 221 else: 222 yield self.env.timeout(1) 223 total_unload_time -= 1 224 # yield self.env.timeout(total_unload_time) 225 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 226 "all_bulk_unloaded", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 227 # Put dry bulk batches in silos 228 yield self.port_silos.put(self.unload_tons) 229 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 230 "all_bulk_stored", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 231 else: 232 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 233 "all_conveyors_attached", self.env.now, f"Ship {self.id} conveyors allocated")) 234 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 235 "all_bulk_unloaded", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 236 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 237 "all_bulk_stored", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 238 239 def load(self): 240 """ 241 Represents the loading of dry bulk cargo onto the vessel at the terminal. 242 This method allocates the required number of conveyors, loads the cargo onto the vessel, and updates the port silos. 243 It also handles the waiting time until the loading is completed, ensuring that it only proceeds during daytime hours. 244 The loading process is divided into several steps: 245 1. Check if the terminal has the required export conveyors. 246 2. Allocate the conveyors for loading. 247 3. Load the dry bulk cargo onto the vessel. 248 4. Wait until the loading is completed, ensuring it only occurs during daytime hours. 249 5. Update the port silos to reflect the loaded cargo. 250 """ 251 export_terminal = get_value_by_terminal( 252 self.terminal_data, 'DryBulk', self.selected_terminal, 'export') 253 if export_terminal: 254 # Represents dry bulk cargo being loaded onto the vessel at the input conveyor rate 255 total_load_time = self.load_tons * self.load_time / self.num_conveyors 256 # Load dry bulk cargo 257 yield self.port_silos.get(self.load_tons) 258 self.cargo_wait_time += total_load_time 259 # keep waiting until day time 260 while total_load_time > 0: 261 if not is_daytime(self.env.now, start=10, end=17): 262 yield self.env.timeout(1) 263 else: 264 yield self.env.timeout(1) 265 total_load_time -= 1 266 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 267 "all_bulk_loaded", self.env.now, "Ship {} loaded all dry bulk".format(self.id))) 268 else: 269 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 270 "all_bulk_loaded", self.env.now, "Ship {} loaded all dry bulk".format(self.id))) 271 272 def wait(self): 273 """ 274 Represents the waiting time for the vessel at the port after unloading and loading operations. 275 This method calculates the waiting time based on the dry bulk efficiency and the cargo wait time. 276 It ensures that the vessel waits at the port until it is ready to depart. 277 """ 278 port_waiting_time = (1/DRY_BULK_EFFICIENCY - 1) * self.cargo_wait_time 279 yield self.env.timeout(port_waiting_time) 280 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 281 "wait", self.env.now, "Ship {} waited at the port".format(self.id))) 282 283 def detach_and_depart(self): 284 """ 285 Represents the process of detaching the vessel from the berth and departing from the port. 286 This method handles the release of the allocated conveyors, updates the ship logs, and releases the berth. 287 It also logs the events related to the vessel's departure. 288 """ 289 290 # Release the conveyor belts 291 for conveyor in self.allocated_conveyors: 292 yield self.current_berth.conveyors.put(conveyor) 293 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", "all_conveyors_disconnected", 294 self.env.now, "Ship {} disconnected from conveyor {}".format(self.id, conveyor.id))) 295 296 # Depart from the berth 297 yield self.port_berths.put(self.current_berth)
19class DryBulkTerminal: 20 """ 21 Represents a dry bulk terminal and the process of handling arriving ships. 22 This class simulates the arrival, berthing, unloading, loading, waiting, and departure of dry bulk ships at the terminal. 23 It manages the allocation of berths and conveyors, handles cargo operations, and logs events during the simulation. 24 The process includes the following steps: 25 1. Ship arrival at the terminal. 26 2. Anchorage waiting period. 27 3. Berth allocation for the ship. 28 4. Channel process for entering the terminal. 29 5. Docking the ship to the berth. 30 6. Unloading dry bulk cargo from the ship. 31 7. Loading dry bulk cargo onto the ship. 32 8. Waiting at the terminal until the ship is ready to depart. 33 9. Detaching the ship from the berth and departing from the terminal. 34 10. Channel process for leaving the terminal. 35 The class also logs events and maintains ship logs throughout the process. 36 37 Args: 38 env (SimPy.Environment): The simulation environment. 39 chassis_bays_utilization (float): Utilization of chassis bays. 40 run_id (int): Unique identifier for the simulation run. 41 channel (Channel): The channel object for managing ship movements. 42 day_pilots (simpy.Container): Simpy container of available day pilots. 43 night_pilots (simpy.Container) : Simpy container of available night pilots. 44 tugboats (simpy.Container): Simpy container of available tugboats. 45 ship_info (dict): Information about the ships. 46 last_section (str): The last section of the channel. 47 selected_terminal (str): The terminal selected for the ship. 48 id (int): Unique identifier for the ship. 49 ship_type (str): Type of the ship (e.g., "DryBulk"). 50 draft (float): Draft of the ship. 51 width (float): Width of the ship. 52 unload_time (float): Time taken to unload cargo (in hours). 53 load_time (float): Time taken to load cargo (in hours). 54 unload_tons (float): Amount of cargo to unload (in tons). 55 load_tons (float): Amount of cargo to load (in tons). 56 events (list): List to store events during the simulation. 57 ship_logs (list): List to store logs of the ship's activities. 58 port_berths (simpy.Resource): Resource for managing port berths. 59 port_silos (simpy.Resource): Resource for managing port silos. 60 SHIPS_IN_CHANNEL (int): Number of ships allowed in the channel. 61 SHIPS_IN_ANCHORAGE (int): Number of ships allowed in the anchorage. 62 terminal_data (dict): Data related to the terminal. 63 """ 64 def __init__(self, env, chassis_bays_utilization, run_id, channel, day_pilots, night_pilots, tugboats, ship_info, last_section, selected_terminal, id, ship_type, draft, width, unload_time, load_time, unload_tons, load_tons, events, ship_logs, port_berths, port_silos, SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE, terminal_data): 65 # Initialize the environment 66 self.env = env 67 self.run_id = run_id 68 self.SHIPS_IN_CHANNEL = SHIPS_IN_CHANNEL 69 self.SHIPS_IN_ANCHORAGE = SHIPS_IN_ANCHORAGE 70 self.terminal_data = terminal_data 71 72 # Initalise the channel 73 self.channel = channel 74 self.ship_info = ship_info 75 self.last_section = last_section 76 77 # day_pilots, night_pilots and tugboats 78 self.day_pilots, self.night_pilots = day_pilots, night_pilots 79 self.tugboats = tugboats 80 81 # Set vessel and terminal attributes 82 self.id = id 83 self.selected_terminal = selected_terminal 84 self.ship_type = ship_type 85 self.num_conveyors = get_value_by_terminal( 86 self.terminal_data, 'DryBulk', selected_terminal, 'transfer units per berth') 87 self.unload_tons = unload_tons 88 self.load_tons = load_tons 89 self.time_for_uturn_min, self.time_for_uturn_max = TIME_FOR_UTURN 90 91 self.draft = draft 92 self.width = width 93 self.unload_time = unload_time 94 self.load_time = load_time 95 96 # wait time proxy 97 self.cargo_wait_time = 0 98 99 # The allocation of conveyors and berth will be done during the process 100 self.current_berth = None 101 self.allocated_conveyors = [] 102 103 # Get the ship logs and events 104 self.events = events 105 self.ship_logs = ship_logs 106 107 # Allocate berth and conveyors 108 self.port_berths = port_berths 109 self.port_silos = port_silos 110 111 # Start the process 112 self.env.process(self.process()) 113 114 def process(self): 115 """ 116 The main process for the dry bulk terminal, simulating the arrival, berthing, unloading, loading, waiting, and departure of ships. 117 This method handles the entire lifecycle of a dry bulk ship at the terminal. 118 """ 119 120 self.ship_logs = update_ship_logs(self.ship_logs, "D", self.id, self.selected_terminal, ship_start_time=nan, time_to_get_berth=nan, time_for_restriction_in=nan, time_to_get_pilot_in=nan, 121 time_to_get_tugs_in=nan, time_to_common_channel_in=nan, time_to_travel_channel_in=nan, time_to_tug_steer_in=nan, unloading_time=nan, loading_time=nan, waiting_time=nan, 122 departure_time=nan, time_to_get_pilot_out=nan, time_to_get_tugs_out=nan, time_to_tug_steer_out=nan, time_for_uturn=nan, 123 time_to_travel_channel_out=nan, time_to_common_channel_out=nan, ship_end_time=nan) 124 125 # ship arrival 126 ship_start_time = self.env.now 127 update_ship_logs(self.ship_logs, "D", self.id, 128 self.selected_terminal, ship_start_time=ship_start_time) 129 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 130 "arrive", self.env.now, f"Ship {self.id} arrived at the port")) 131 132 # Anchorage waiting 133 yield self.env.timeout(constants.ANCHORAGE_WAITING_DRYBULK) 134 135 # Berth allocation 136 start_time = self.env.now 137 yield self.env.process(self.arrive_and_berth()) 138 time_to_get_berth = self.env.now - ship_start_time 139 update_ship_logs(self.ship_logs, "D", self.id, 140 self.selected_terminal, time_to_get_berth=time_to_get_berth) 141 142 # Channel process (in) 143 self.day = is_daytime(self.env.now, 7, 19) 144 yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "D", self.run_id)) 145 146 # Terminal process 147 self.events.append((f"Ship_{self.id}", "DryBulk", f"L.{self.selected_terminal}", "dock", self.env.now, 148 f"Ship {self.id} docked to berth {self.current_berth.id} with waiting time {'N/A'}")) 149 150 start_time = self.env.now 151 yield self.env.process(self.unload()) 152 unloading_time = self.env.now - start_time 153 update_ship_logs(self.ship_logs, "D", self.id, 154 self.selected_terminal, unloading_time=unloading_time) 155 156 start_time = self.env.now 157 yield self.env.process(self.load()) 158 loading_time = self.env.now - start_time 159 update_ship_logs(self.ship_logs, "D", self.id, 160 self.selected_terminal, loading_time=loading_time) 161 162 start_time = self.env.now 163 yield self.env.process(self.wait()) 164 waiting_time = self.env.now - start_time 165 update_ship_logs(self.ship_logs, "D", self.id, 166 self.selected_terminal, waiting_time=waiting_time) 167 168 start_time = self.env.now 169 yield self.env.process(self.detach_and_depart()) 170 departure_time = self.env.now - start_time 171 update_ship_logs(self.ship_logs, "D", self.id, 172 self.selected_terminal, departure_time=departure_time) 173 174 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", "undock", self.env.now, 175 f"Ship {self.id} undocked to berth {self.current_berth.id} with waiting time {'N/A'}")) 176 177 # Channel process (out) 178 self.day = is_daytime(self.env.now, 7, 19) 179 yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "D", self.run_id)) 180 181 ship_end_time = self.env.now 182 update_ship_logs(self.ship_logs, "D", self.id, 183 self.selected_terminal, ship_end_time=ship_end_time) 184 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", "depart", self.env.now, 185 f"Ship {self.id} departed from berth {self.current_berth.id} with waiting time {'N/A'}")) 186 187 def arrive_and_berth(self): 188 """ 189 Represents the process of a vessel arriving at the terminal and seizing a berth. 190 This method handles the allocation of a berth for the vessel and prepares it for unloading and loading operations. 191 """ 192 self.current_berth = yield self.port_berths.get() 193 194 def unload(self): 195 """ 196 Represents the unloading of dry bulk cargo from the vessel at the terminal. 197 This method allocates the required number of conveyors, unloads the cargo, and stores it in the port silos. 198 It also handles the waiting time until the unloading is completed, ensuring that it only proceeds during daytime hours. 199 The unloading process is divided into several steps: 200 1. Check if the terminal has the required import conveyors. 201 2. Allocate the conveyors for unloading. 202 3. Unload the dry bulk cargo from the vessel. 203 4. Wait until the unloading is completed, ensuring it only occurs during daytime hours. 204 5. Store the unloaded dry bulk cargo in the port silos. 205 """ 206 # Request for the required number of conveyors 207 import_terminal = get_value_by_terminal( 208 self.terminal_data, 'DryBulk', self.selected_terminal, 'import') 209 if import_terminal: 210 for _ in range(self.num_conveyors): 211 conveyor = yield self.current_berth.conveyors.get() 212 self.allocated_conveyors.append(conveyor) 213 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 214 "all_conveyors_attached", self.env.now, f"Ship {self.id} conveyors allocated")) 215 # Unload dry bulk cargo 216 total_unload_time = self.unload_tons * self.unload_time / self.num_conveyors 217 self.cargo_wait_time += total_unload_time 218 # keep waiting until day time 219 while total_unload_time > 0: 220 if not is_daytime(self.env.now, start=7, end=19): 221 yield self.env.timeout(1) 222 else: 223 yield self.env.timeout(1) 224 total_unload_time -= 1 225 # yield self.env.timeout(total_unload_time) 226 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 227 "all_bulk_unloaded", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 228 # Put dry bulk batches in silos 229 yield self.port_silos.put(self.unload_tons) 230 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 231 "all_bulk_stored", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 232 else: 233 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 234 "all_conveyors_attached", self.env.now, f"Ship {self.id} conveyors allocated")) 235 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 236 "all_bulk_unloaded", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 237 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 238 "all_bulk_stored", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 239 240 def load(self): 241 """ 242 Represents the loading of dry bulk cargo onto the vessel at the terminal. 243 This method allocates the required number of conveyors, loads the cargo onto the vessel, and updates the port silos. 244 It also handles the waiting time until the loading is completed, ensuring that it only proceeds during daytime hours. 245 The loading process is divided into several steps: 246 1. Check if the terminal has the required export conveyors. 247 2. Allocate the conveyors for loading. 248 3. Load the dry bulk cargo onto the vessel. 249 4. Wait until the loading is completed, ensuring it only occurs during daytime hours. 250 5. Update the port silos to reflect the loaded cargo. 251 """ 252 export_terminal = get_value_by_terminal( 253 self.terminal_data, 'DryBulk', self.selected_terminal, 'export') 254 if export_terminal: 255 # Represents dry bulk cargo being loaded onto the vessel at the input conveyor rate 256 total_load_time = self.load_tons * self.load_time / self.num_conveyors 257 # Load dry bulk cargo 258 yield self.port_silos.get(self.load_tons) 259 self.cargo_wait_time += total_load_time 260 # keep waiting until day time 261 while total_load_time > 0: 262 if not is_daytime(self.env.now, start=10, end=17): 263 yield self.env.timeout(1) 264 else: 265 yield self.env.timeout(1) 266 total_load_time -= 1 267 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 268 "all_bulk_loaded", self.env.now, "Ship {} loaded all dry bulk".format(self.id))) 269 else: 270 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 271 "all_bulk_loaded", self.env.now, "Ship {} loaded all dry bulk".format(self.id))) 272 273 def wait(self): 274 """ 275 Represents the waiting time for the vessel at the port after unloading and loading operations. 276 This method calculates the waiting time based on the dry bulk efficiency and the cargo wait time. 277 It ensures that the vessel waits at the port until it is ready to depart. 278 """ 279 port_waiting_time = (1/DRY_BULK_EFFICIENCY - 1) * self.cargo_wait_time 280 yield self.env.timeout(port_waiting_time) 281 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 282 "wait", self.env.now, "Ship {} waited at the port".format(self.id))) 283 284 def detach_and_depart(self): 285 """ 286 Represents the process of detaching the vessel from the berth and departing from the port. 287 This method handles the release of the allocated conveyors, updates the ship logs, and releases the berth. 288 It also logs the events related to the vessel's departure. 289 """ 290 291 # Release the conveyor belts 292 for conveyor in self.allocated_conveyors: 293 yield self.current_berth.conveyors.put(conveyor) 294 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", "all_conveyors_disconnected", 295 self.env.now, "Ship {} disconnected from conveyor {}".format(self.id, conveyor.id))) 296 297 # Depart from the berth 298 yield self.port_berths.put(self.current_berth)
Represents a dry bulk terminal and the process of handling arriving ships. This class simulates the arrival, berthing, unloading, loading, waiting, and departure of dry bulk ships at the terminal. It manages the allocation of berths and conveyors, handles cargo operations, and logs events during the simulation. The process includes the following steps:
- Ship arrival at the terminal.
- Anchorage waiting period.
- Berth allocation for the ship.
- Channel process for entering the terminal.
- Docking the ship to the berth.
- Unloading dry bulk cargo from the ship.
- Loading dry bulk cargo onto the ship.
- Waiting at the terminal until the ship is ready to depart.
- Detaching the ship from the berth and departing from the terminal.
- Channel process for leaving the terminal. The class also logs events and maintains ship logs throughout the process.
Arguments:
- env (SimPy.Environment): The simulation environment.
- chassis_bays_utilization (float): Utilization of chassis bays.
- run_id (int): Unique identifier for the simulation run.
- channel (Channel): The channel object for managing ship movements.
- day_pilots (simpy.Container): Simpy container of available day pilots.
- night_pilots (simpy.Container) : Simpy container of available night pilots.
- tugboats (simpy.Container): Simpy container of available tugboats.
- ship_info (dict): Information about the ships.
- last_section (str): The last section of the channel.
- selected_terminal (str): The terminal selected for the ship.
- id (int): Unique identifier for the ship.
- ship_type (str): Type of the ship (e.g., "DryBulk").
- draft (float): Draft of the ship.
- width (float): Width of the ship.
- unload_time (float): Time taken to unload cargo (in hours).
- load_time (float): Time taken to load cargo (in hours).
- unload_tons (float): Amount of cargo to unload (in tons).
- load_tons (float): Amount of cargo to load (in tons).
- events (list): List to store events during the simulation.
- ship_logs (list): List to store logs of the ship's activities.
- port_berths (simpy.Resource): Resource for managing port berths.
- port_silos (simpy.Resource): Resource for managing port silos.
- SHIPS_IN_CHANNEL (int): Number of ships allowed in the channel.
- SHIPS_IN_ANCHORAGE (int): Number of ships allowed in the anchorage.
- terminal_data (dict): Data related to the terminal.
114 def process(self): 115 """ 116 The main process for the dry bulk terminal, simulating the arrival, berthing, unloading, loading, waiting, and departure of ships. 117 This method handles the entire lifecycle of a dry bulk ship at the terminal. 118 """ 119 120 self.ship_logs = update_ship_logs(self.ship_logs, "D", self.id, self.selected_terminal, ship_start_time=nan, time_to_get_berth=nan, time_for_restriction_in=nan, time_to_get_pilot_in=nan, 121 time_to_get_tugs_in=nan, time_to_common_channel_in=nan, time_to_travel_channel_in=nan, time_to_tug_steer_in=nan, unloading_time=nan, loading_time=nan, waiting_time=nan, 122 departure_time=nan, time_to_get_pilot_out=nan, time_to_get_tugs_out=nan, time_to_tug_steer_out=nan, time_for_uturn=nan, 123 time_to_travel_channel_out=nan, time_to_common_channel_out=nan, ship_end_time=nan) 124 125 # ship arrival 126 ship_start_time = self.env.now 127 update_ship_logs(self.ship_logs, "D", self.id, 128 self.selected_terminal, ship_start_time=ship_start_time) 129 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 130 "arrive", self.env.now, f"Ship {self.id} arrived at the port")) 131 132 # Anchorage waiting 133 yield self.env.timeout(constants.ANCHORAGE_WAITING_DRYBULK) 134 135 # Berth allocation 136 start_time = self.env.now 137 yield self.env.process(self.arrive_and_berth()) 138 time_to_get_berth = self.env.now - ship_start_time 139 update_ship_logs(self.ship_logs, "D", self.id, 140 self.selected_terminal, time_to_get_berth=time_to_get_berth) 141 142 # Channel process (in) 143 self.day = is_daytime(self.env.now, 7, 19) 144 yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "D", self.run_id)) 145 146 # Terminal process 147 self.events.append((f"Ship_{self.id}", "DryBulk", f"L.{self.selected_terminal}", "dock", self.env.now, 148 f"Ship {self.id} docked to berth {self.current_berth.id} with waiting time {'N/A'}")) 149 150 start_time = self.env.now 151 yield self.env.process(self.unload()) 152 unloading_time = self.env.now - start_time 153 update_ship_logs(self.ship_logs, "D", self.id, 154 self.selected_terminal, unloading_time=unloading_time) 155 156 start_time = self.env.now 157 yield self.env.process(self.load()) 158 loading_time = self.env.now - start_time 159 update_ship_logs(self.ship_logs, "D", self.id, 160 self.selected_terminal, loading_time=loading_time) 161 162 start_time = self.env.now 163 yield self.env.process(self.wait()) 164 waiting_time = self.env.now - start_time 165 update_ship_logs(self.ship_logs, "D", self.id, 166 self.selected_terminal, waiting_time=waiting_time) 167 168 start_time = self.env.now 169 yield self.env.process(self.detach_and_depart()) 170 departure_time = self.env.now - start_time 171 update_ship_logs(self.ship_logs, "D", self.id, 172 self.selected_terminal, departure_time=departure_time) 173 174 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", "undock", self.env.now, 175 f"Ship {self.id} undocked to berth {self.current_berth.id} with waiting time {'N/A'}")) 176 177 # Channel process (out) 178 self.day = is_daytime(self.env.now, 7, 19) 179 yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "D", self.run_id)) 180 181 ship_end_time = self.env.now 182 update_ship_logs(self.ship_logs, "D", self.id, 183 self.selected_terminal, ship_end_time=ship_end_time) 184 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", "depart", self.env.now, 185 f"Ship {self.id} departed from berth {self.current_berth.id} with waiting time {'N/A'}"))
The main process for the dry bulk terminal, simulating the arrival, berthing, unloading, loading, waiting, and departure of ships. This method handles the entire lifecycle of a dry bulk ship at the terminal.
187 def arrive_and_berth(self): 188 """ 189 Represents the process of a vessel arriving at the terminal and seizing a berth. 190 This method handles the allocation of a berth for the vessel and prepares it for unloading and loading operations. 191 """ 192 self.current_berth = yield self.port_berths.get()
Represents the process of a vessel arriving at the terminal and seizing a berth. This method handles the allocation of a berth for the vessel and prepares it for unloading and loading operations.
194 def unload(self): 195 """ 196 Represents the unloading of dry bulk cargo from the vessel at the terminal. 197 This method allocates the required number of conveyors, unloads the cargo, and stores it in the port silos. 198 It also handles the waiting time until the unloading is completed, ensuring that it only proceeds during daytime hours. 199 The unloading process is divided into several steps: 200 1. Check if the terminal has the required import conveyors. 201 2. Allocate the conveyors for unloading. 202 3. Unload the dry bulk cargo from the vessel. 203 4. Wait until the unloading is completed, ensuring it only occurs during daytime hours. 204 5. Store the unloaded dry bulk cargo in the port silos. 205 """ 206 # Request for the required number of conveyors 207 import_terminal = get_value_by_terminal( 208 self.terminal_data, 'DryBulk', self.selected_terminal, 'import') 209 if import_terminal: 210 for _ in range(self.num_conveyors): 211 conveyor = yield self.current_berth.conveyors.get() 212 self.allocated_conveyors.append(conveyor) 213 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 214 "all_conveyors_attached", self.env.now, f"Ship {self.id} conveyors allocated")) 215 # Unload dry bulk cargo 216 total_unload_time = self.unload_tons * self.unload_time / self.num_conveyors 217 self.cargo_wait_time += total_unload_time 218 # keep waiting until day time 219 while total_unload_time > 0: 220 if not is_daytime(self.env.now, start=7, end=19): 221 yield self.env.timeout(1) 222 else: 223 yield self.env.timeout(1) 224 total_unload_time -= 1 225 # yield self.env.timeout(total_unload_time) 226 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 227 "all_bulk_unloaded", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 228 # Put dry bulk batches in silos 229 yield self.port_silos.put(self.unload_tons) 230 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 231 "all_bulk_stored", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 232 else: 233 self.events.append((f"Ship_{self.id}", "DryBulk", f"D.{self.selected_terminal}", 234 "all_conveyors_attached", self.env.now, f"Ship {self.id} conveyors allocated")) 235 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 236 "all_bulk_unloaded", self.env.now, "Ship {} unloaded all dry bulk".format(self.id))) 237 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 238 "all_bulk_stored", self.env.now, "Ship {} unloaded all dry bulk".format(self.id)))
Represents the unloading of dry bulk cargo from the vessel at the terminal. This method allocates the required number of conveyors, unloads the cargo, and stores it in the port silos. It also handles the waiting time until the unloading is completed, ensuring that it only proceeds during daytime hours. The unloading process is divided into several steps:
- Check if the terminal has the required import conveyors.
- Allocate the conveyors for unloading.
- Unload the dry bulk cargo from the vessel.
- Wait until the unloading is completed, ensuring it only occurs during daytime hours.
- Store the unloaded dry bulk cargo in the port silos.
240 def load(self): 241 """ 242 Represents the loading of dry bulk cargo onto the vessel at the terminal. 243 This method allocates the required number of conveyors, loads the cargo onto the vessel, and updates the port silos. 244 It also handles the waiting time until the loading is completed, ensuring that it only proceeds during daytime hours. 245 The loading process is divided into several steps: 246 1. Check if the terminal has the required export conveyors. 247 2. Allocate the conveyors for loading. 248 3. Load the dry bulk cargo onto the vessel. 249 4. Wait until the loading is completed, ensuring it only occurs during daytime hours. 250 5. Update the port silos to reflect the loaded cargo. 251 """ 252 export_terminal = get_value_by_terminal( 253 self.terminal_data, 'DryBulk', self.selected_terminal, 'export') 254 if export_terminal: 255 # Represents dry bulk cargo being loaded onto the vessel at the input conveyor rate 256 total_load_time = self.load_tons * self.load_time / self.num_conveyors 257 # Load dry bulk cargo 258 yield self.port_silos.get(self.load_tons) 259 self.cargo_wait_time += total_load_time 260 # keep waiting until day time 261 while total_load_time > 0: 262 if not is_daytime(self.env.now, start=10, end=17): 263 yield self.env.timeout(1) 264 else: 265 yield self.env.timeout(1) 266 total_load_time -= 1 267 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 268 "all_bulk_loaded", self.env.now, "Ship {} loaded all dry bulk".format(self.id))) 269 else: 270 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 271 "all_bulk_loaded", self.env.now, "Ship {} loaded all dry bulk".format(self.id)))
Represents the loading of dry bulk cargo onto the vessel at the terminal. This method allocates the required number of conveyors, loads the cargo onto the vessel, and updates the port silos. It also handles the waiting time until the loading is completed, ensuring that it only proceeds during daytime hours. The loading process is divided into several steps:
- Check if the terminal has the required export conveyors.
- Allocate the conveyors for loading.
- Load the dry bulk cargo onto the vessel.
- Wait until the loading is completed, ensuring it only occurs during daytime hours.
- Update the port silos to reflect the loaded cargo.
273 def wait(self): 274 """ 275 Represents the waiting time for the vessel at the port after unloading and loading operations. 276 This method calculates the waiting time based on the dry bulk efficiency and the cargo wait time. 277 It ensures that the vessel waits at the port until it is ready to depart. 278 """ 279 port_waiting_time = (1/DRY_BULK_EFFICIENCY - 1) * self.cargo_wait_time 280 yield self.env.timeout(port_waiting_time) 281 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", 282 "wait", self.env.now, "Ship {} waited at the port".format(self.id)))
Represents the waiting time for the vessel at the port after unloading and loading operations. This method calculates the waiting time based on the dry bulk efficiency and the cargo wait time. It ensures that the vessel waits at the port until it is ready to depart.
284 def detach_and_depart(self): 285 """ 286 Represents the process of detaching the vessel from the berth and departing from the port. 287 This method handles the release of the allocated conveyors, updates the ship logs, and releases the berth. 288 It also logs the events related to the vessel's departure. 289 """ 290 291 # Release the conveyor belts 292 for conveyor in self.allocated_conveyors: 293 yield self.current_berth.conveyors.put(conveyor) 294 self.events.append(("Ship_{}".format(self.id), "DryBulk", f"D.{self.selected_terminal}", "all_conveyors_disconnected", 295 self.env.now, "Ship {} disconnected from conveyor {}".format(self.id, conveyor.id))) 296 297 # Depart from the berth 298 yield self.port_berths.put(self.current_berth)
Represents the process of detaching the vessel from the berth and departing from the port. This method handles the release of the allocated conveyors, updates the ship logs, and releases the berth. It also logs the events related to the vessel's departure.