simulation_classes.terminal_container

Uses container terminal class objects to create container terminal processes. Vessel arrivals, berth and crane allocation, container unloading, container loading, vessel waiting, and vessel departure are simulated.

  1"""
  2Uses container terminal class objects to create container terminal processes.
  3Vessel arrivals, berth and crane allocation, container unloading, container loading, vessel waiting, and vessel departure are simulated.
  4"""
  5import constants
  6from simulation_analysis.results import update_ship_logs
  7from simulation_handler.helpers import get_value_by_terminal, is_daytime
  8
  9TIME_COMMON_CHANNEL = constants.TIME_COMMON_CHANNEL
 10TIME_FOR_TUG_STEER = constants.TIME_FOR_TUG_STEER
 11TIME_FOR_UTURN = constants.TIME_FOR_UTURN
 12CONTAINER_TERMINAL_EFFICIENCY = constants.CTR_TERMINAL_EFFICIENCY
 13
 14nan = float('nan')
 15
 16
 17class ContainerTerminal:
 18    """
 19    Class to simulate the container terminal and the processes of arriving ships.
 20    This class handles the docking, unloading, loading, waiting, and departure of container ships.
 21    It also manages the allocation of berths and cranes, and the interaction with the channel for ship movements.   
 22    The processes include the following steps:
 23    1. Ship arrival at the port.
 24    2. Anchorage waiting.
 25    3. Berth allocation.
 26    4. Channel process (inbound).
 27    5. Docking the ship to a berth.
 28    6. Unloading containers from the ship.
 29    7. Loading containers onto the ship.
 30    8. Waiting at the port after unloading and loading operations.
 31    9. Detaching from the berth and departing from the port.
 32    10. Channel process (outbound).
 33    11. Ship departure from the port.
 34    The class also logs events and ship activities 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 for day pilots available
 42        night_pilots (simpy.Container) : Simpy container for night pilots available
 43        tugboats (simpy.Container): Simpy container for tugboats available
 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., Container, Liquid, DryBulk)
 49        draft (float): Draft of the ship
 50        width (float): Width of the ship
 51        unload_time_per_container (float): Time taken to unload a container
 52        load_time_per_container (float): Time taken to load a container
 53        containers_to_unload (list): List of containers to be unloaded
 54        containers_to_load (list): List of containers to be loaded
 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 representing the berths at the port
 58        port_yard (simpy.Store): Store representing the port's yard for containers
 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 about the terminals, including transfer units per berth and import/export capabilities
 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_per_container, load_time_per_container, containers_to_unload, containers_to_load, events, ship_logs, port_berths, port_yard, SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE, terminal_data):
 64        # Initialize the environment
 65        self.env = env
 66        self.SHIPS_IN_CHANNEL = SHIPS_IN_CHANNEL
 67        self.SHIPS_IN_ANCHORAGE = SHIPS_IN_ANCHORAGE
 68        self.run_id = run_id
 69        self.terminal_data = terminal_data
 70        self.chassis_bays_utilization = chassis_bays_utilization
 71
 72        # Initialize 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        # Attributes of ship
 82        self.id = id
 83        self.selected_terminal = selected_terminal
 84        self.ship_type = ship_type
 85        self.num_cranes = get_value_by_terminal(
 86            self.terminal_data, 'Container', selected_terminal, 'transfer units per berth')
 87        self.containers_to_unload = containers_to_unload
 88        self.containers_to_load = containers_to_load
 89        self.draft = draft
 90        self.width = width
 91        self.day = None
 92
 93        # Attributes of container terminal
 94        self.unload_time = unload_time_per_container
 95        self.load_time = load_time_per_container
 96        self.time_for_uturn_min, self.time_for_uturn_max = TIME_FOR_UTURN
 97
 98        # proxy for wait time
 99        self.cargo_wait_time = 0
100
101        # The allocation of cranes and berth will be done during the process
102        self.current_berth = None
103        self.allocated_cranes = []
104
105        # Get the ship logs and events
106        self.events = events
107        self.ship_logs = ship_logs
108
109        # Allocate berth and cranes (note that cranes are part of berths)
110        self.port_berths = port_berths
111        self.port_yard = port_yard
112
113        # Start the process
114        self.env.process(self.process())
115
116    def process(self):
117        """
118        Process for the container terminal, simulating the arrival, unloading, loading, waiting, and departure of ships.
119        """
120
121        self.ship_logs = update_ship_logs(self.ship_logs, "C", 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,
122                                          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,
123                                          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,
124                                          time_to_travel_channel_out=nan, time_to_common_channel_out=nan, ship_end_time=nan)
125
126        # ship arrival
127        ship_start_time = self.env.now
128        update_ship_logs(self.ship_logs, "C", self.id,
129                         self.selected_terminal, ship_start_time=ship_start_time)
130        self.events.append((f"Ship_{self.id}", "Container", f"L.{self.selected_terminal}",
131                           "arrive", self.env.now, f"Ship {self.id} arrived at the port"))
132
133        # Anchorage waiting
134        yield self.env.timeout(constants.ANCHORAGE_WAITING_CONTAINER)
135
136        # Berth allocation
137        start_time = self.env.now
138        yield self.env.process(self.dock())
139        time_to_get_berth = self.env.now - ship_start_time
140        update_ship_logs(self.ship_logs, "C", self.id,
141                         self.selected_terminal, time_to_get_berth=time_to_get_berth)
142
143        # check day or night
144        self.day = is_daytime(self.env.now, 7, 19)
145
146        # Channel process (in)
147        yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "C", self.run_id))
148
149        # Terminal process
150        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "dock", self.env.now,
151                           f"Ship {self.id} docked to berth {self.current_berth.id} with waiting time {'N/A'}"))
152
153        start_time = self.env.now
154        yield self.env.process(self.unload())
155        unloading_time = self.env.now - start_time
156        update_ship_logs(self.ship_logs, "C", self.id,
157                         self.selected_terminal, unloading_time=unloading_time)
158
159        start_time = self.env.now
160        yield self.env.process(self.load())
161        loading_time = self.env.now - start_time
162        update_ship_logs(self.ship_logs, "C", self.id,
163                         self.selected_terminal, loading_time=loading_time)
164
165        start_time = self.env.now
166        yield self.env.process(self.wait())
167        waiting_time = self.env.now - start_time
168        update_ship_logs(self.ship_logs, "C", self.id,
169                         self.selected_terminal, waiting_time=waiting_time)
170
171        start_time = self.env.now
172        yield self.env.process(self.detach_and_depart())
173        departure_time = self.env.now - start_time
174        update_ship_logs(self.ship_logs, "C", self.id,
175                         self.selected_terminal, departure_time=departure_time)
176
177        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "undock", self.env.now,
178                           f"Ship {self.id} undocked to berth {self.current_berth.id} with waiting time {'N/A'}"))
179
180        # Channel process (out)
181        self.day = is_daytime(self.env.now, 7, 19)
182        yield self.env.process(self.channel.channel_process(self.ship_info, self.day,  self.last_section, self.id, self.selected_terminal, "C", self.run_id))
183
184        ship_end_time = self.env.now
185        update_ship_logs(self.ship_logs, "C", self.id,
186                         self.selected_terminal, ship_end_time=ship_end_time)
187        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "depart", self.env.now,
188                           f"Ship {self.id} departed from berth {self.current_berth.id} with waiting time {'N/A'}"))
189
190    def dock(self):
191        """
192        Represents the process of docking the ship to a berth and allocating cranes.
193        This includes waiting for a berth to become available and then getting the cranes allocated.
194        """
195        self.current_berth = yield self.port_berths.get()
196
197    def unload(self):
198        """
199        Represents the unloading of containers from the vessel at the input crane movement rate.
200        This includes requesting cranes, unloading containers, and storing them in the port yard.
201        """
202        import_terminal = get_value_by_terminal(
203            self.terminal_data, 'Container', self.selected_terminal, 'import')
204        if import_terminal:
205            # Request for the required number of cranes
206            for _ in range(self.num_cranes):
207                crane = yield self.current_berth.cranes.get()
208                self.allocated_cranes.append(crane)
209            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
210                               "all_cranes_attached", self.env.now, f"Ship {self.id} cranes allocated"))
211            # Unload containers
212            for container in self.containers_to_unload:
213                unload_time_per_container = self.unload_time
214                self.cargo_wait_time += unload_time_per_container / self.num_cranes
215                yield self.env.timeout(unload_time_per_container / self.num_cranes)
216            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
217                               "all_container_unloaded", self.env.now, f"Ship {self.id} unloaded all containers"))
218            # Put containers in yard
219            for container in self.containers_to_unload:
220                yield self.port_yard.put(container)
221            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
222                               "all_container_stored", self.env.now, f"Ship {self.id} unloaded all containers"))
223        else:
224            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
225                               "all_cranes_attached", self.env.now, f"Ship {self.id} cranes allocated"))
226            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
227                               "all_container_unloaded", self.env.now, f"Ship {self.id} unloaded all containers"))
228            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
229                               "all_container_stored", self.env.now, f"Ship {self.id} unloaded all containers"))
230
231    def load(self):
232        """
233        Represents the loading of containers onto the vessel at the input crane movement rate.
234        This includes checking if the terminal can export containers, loading them onto the vessel, and removing them from the yard.
235        """
236        export_terminal = get_value_by_terminal(
237            self.terminal_data, 'Container', self.selected_terminal, 'export')
238        # Load containers
239        if export_terminal:
240            for container in self.containers_to_load:
241                # Remove the container from the yard
242                yield self.port_yard.get()
243                # Load the containers onto the vessel
244                load_time_per_container = self.load_time
245                self.cargo_wait_time += load_time_per_container / self.num_cranes
246                yield self.env.timeout(load_time_per_container / self.num_cranes)
247            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
248                               "all_container_loaded", self.env.now, f"Ship {self.id} loaded all containers"))
249        else:
250            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
251                               "all_container_loaded", self.env.now, f"Ship {self.id} loaded all containers"))
252
253    def wait(self):
254        """
255        Represents the waiting time at the port after unloading and loading operations.
256        This includes calculating the waiting time based on the terminal efficiency and cargo wait time.
257        """
258        # Calculate the waiting time based on the terminal efficiency and cargo wait time
259        port_waiting_time = (1 / CONTAINER_TERMINAL_EFFICIENCY) * \
260            self.cargo_wait_time - self.cargo_wait_time
261        yield self.env.timeout(port_waiting_time)
262
263    def detach_and_depart(self):
264        """
265        Represents the process of detaching from the berth and departing from the port.
266        This includes returning the cranes to the berth and releasing the berth for other ships.
267        """
268        # Return the cranes to the berth
269        for crane in self.allocated_cranes:
270            yield self.current_berth.cranes.put(crane)
271        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
272                           "all_cranes_returned", self.env.now, f"Ship {self.id} returned all cranes"))
273
274        # Depart from the berth
275        yield self.port_berths.put(self.current_berth)
class ContainerTerminal:
 18class ContainerTerminal:
 19    """
 20    Class to simulate the container terminal and the processes of arriving ships.
 21    This class handles the docking, unloading, loading, waiting, and departure of container ships.
 22    It also manages the allocation of berths and cranes, and the interaction with the channel for ship movements.   
 23    The processes include the following steps:
 24    1. Ship arrival at the port.
 25    2. Anchorage waiting.
 26    3. Berth allocation.
 27    4. Channel process (inbound).
 28    5. Docking the ship to a berth.
 29    6. Unloading containers from the ship.
 30    7. Loading containers onto the ship.
 31    8. Waiting at the port after unloading and loading operations.
 32    9. Detaching from the berth and departing from the port.
 33    10. Channel process (outbound).
 34    11. Ship departure from the port.
 35    The class also logs events and ship activities 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 for day pilots available
 43        night_pilots (simpy.Container) : Simpy container for night pilots available
 44        tugboats (simpy.Container): Simpy container for tugboats available
 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., Container, Liquid, DryBulk)
 50        draft (float): Draft of the ship
 51        width (float): Width of the ship
 52        unload_time_per_container (float): Time taken to unload a container
 53        load_time_per_container (float): Time taken to load a container
 54        containers_to_unload (list): List of containers to be unloaded
 55        containers_to_load (list): List of containers to be loaded
 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 representing the berths at the port
 59        port_yard (simpy.Store): Store representing the port's yard for containers
 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 about the terminals, including transfer units per berth and import/export capabilities
 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_per_container, load_time_per_container, containers_to_unload, containers_to_load, events, ship_logs, port_berths, port_yard, SHIPS_IN_CHANNEL, SHIPS_IN_ANCHORAGE, terminal_data):
 65        # Initialize the environment
 66        self.env = env
 67        self.SHIPS_IN_CHANNEL = SHIPS_IN_CHANNEL
 68        self.SHIPS_IN_ANCHORAGE = SHIPS_IN_ANCHORAGE
 69        self.run_id = run_id
 70        self.terminal_data = terminal_data
 71        self.chassis_bays_utilization = chassis_bays_utilization
 72
 73        # Initialize the channel
 74        self.channel = channel
 75        self.ship_info = ship_info
 76        self.last_section = last_section
 77
 78        # day_pilots, night_pilots and tugboats
 79        self.day_pilots, self.night_pilots = day_pilots, night_pilots
 80        self.tugboats = tugboats
 81
 82        # Attributes of ship
 83        self.id = id
 84        self.selected_terminal = selected_terminal
 85        self.ship_type = ship_type
 86        self.num_cranes = get_value_by_terminal(
 87            self.terminal_data, 'Container', selected_terminal, 'transfer units per berth')
 88        self.containers_to_unload = containers_to_unload
 89        self.containers_to_load = containers_to_load
 90        self.draft = draft
 91        self.width = width
 92        self.day = None
 93
 94        # Attributes of container terminal
 95        self.unload_time = unload_time_per_container
 96        self.load_time = load_time_per_container
 97        self.time_for_uturn_min, self.time_for_uturn_max = TIME_FOR_UTURN
 98
 99        # proxy for wait time
100        self.cargo_wait_time = 0
101
102        # The allocation of cranes and berth will be done during the process
103        self.current_berth = None
104        self.allocated_cranes = []
105
106        # Get the ship logs and events
107        self.events = events
108        self.ship_logs = ship_logs
109
110        # Allocate berth and cranes (note that cranes are part of berths)
111        self.port_berths = port_berths
112        self.port_yard = port_yard
113
114        # Start the process
115        self.env.process(self.process())
116
117    def process(self):
118        """
119        Process for the container terminal, simulating the arrival, unloading, loading, waiting, and departure of ships.
120        """
121
122        self.ship_logs = update_ship_logs(self.ship_logs, "C", 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,
123                                          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,
124                                          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,
125                                          time_to_travel_channel_out=nan, time_to_common_channel_out=nan, ship_end_time=nan)
126
127        # ship arrival
128        ship_start_time = self.env.now
129        update_ship_logs(self.ship_logs, "C", self.id,
130                         self.selected_terminal, ship_start_time=ship_start_time)
131        self.events.append((f"Ship_{self.id}", "Container", f"L.{self.selected_terminal}",
132                           "arrive", self.env.now, f"Ship {self.id} arrived at the port"))
133
134        # Anchorage waiting
135        yield self.env.timeout(constants.ANCHORAGE_WAITING_CONTAINER)
136
137        # Berth allocation
138        start_time = self.env.now
139        yield self.env.process(self.dock())
140        time_to_get_berth = self.env.now - ship_start_time
141        update_ship_logs(self.ship_logs, "C", self.id,
142                         self.selected_terminal, time_to_get_berth=time_to_get_berth)
143
144        # check day or night
145        self.day = is_daytime(self.env.now, 7, 19)
146
147        # Channel process (in)
148        yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "C", self.run_id))
149
150        # Terminal process
151        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "dock", self.env.now,
152                           f"Ship {self.id} docked to berth {self.current_berth.id} with waiting time {'N/A'}"))
153
154        start_time = self.env.now
155        yield self.env.process(self.unload())
156        unloading_time = self.env.now - start_time
157        update_ship_logs(self.ship_logs, "C", self.id,
158                         self.selected_terminal, unloading_time=unloading_time)
159
160        start_time = self.env.now
161        yield self.env.process(self.load())
162        loading_time = self.env.now - start_time
163        update_ship_logs(self.ship_logs, "C", self.id,
164                         self.selected_terminal, loading_time=loading_time)
165
166        start_time = self.env.now
167        yield self.env.process(self.wait())
168        waiting_time = self.env.now - start_time
169        update_ship_logs(self.ship_logs, "C", self.id,
170                         self.selected_terminal, waiting_time=waiting_time)
171
172        start_time = self.env.now
173        yield self.env.process(self.detach_and_depart())
174        departure_time = self.env.now - start_time
175        update_ship_logs(self.ship_logs, "C", self.id,
176                         self.selected_terminal, departure_time=departure_time)
177
178        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "undock", self.env.now,
179                           f"Ship {self.id} undocked to berth {self.current_berth.id} with waiting time {'N/A'}"))
180
181        # Channel process (out)
182        self.day = is_daytime(self.env.now, 7, 19)
183        yield self.env.process(self.channel.channel_process(self.ship_info, self.day,  self.last_section, self.id, self.selected_terminal, "C", self.run_id))
184
185        ship_end_time = self.env.now
186        update_ship_logs(self.ship_logs, "C", self.id,
187                         self.selected_terminal, ship_end_time=ship_end_time)
188        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "depart", self.env.now,
189                           f"Ship {self.id} departed from berth {self.current_berth.id} with waiting time {'N/A'}"))
190
191    def dock(self):
192        """
193        Represents the process of docking the ship to a berth and allocating cranes.
194        This includes waiting for a berth to become available and then getting the cranes allocated.
195        """
196        self.current_berth = yield self.port_berths.get()
197
198    def unload(self):
199        """
200        Represents the unloading of containers from the vessel at the input crane movement rate.
201        This includes requesting cranes, unloading containers, and storing them in the port yard.
202        """
203        import_terminal = get_value_by_terminal(
204            self.terminal_data, 'Container', self.selected_terminal, 'import')
205        if import_terminal:
206            # Request for the required number of cranes
207            for _ in range(self.num_cranes):
208                crane = yield self.current_berth.cranes.get()
209                self.allocated_cranes.append(crane)
210            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
211                               "all_cranes_attached", self.env.now, f"Ship {self.id} cranes allocated"))
212            # Unload containers
213            for container in self.containers_to_unload:
214                unload_time_per_container = self.unload_time
215                self.cargo_wait_time += unload_time_per_container / self.num_cranes
216                yield self.env.timeout(unload_time_per_container / self.num_cranes)
217            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
218                               "all_container_unloaded", self.env.now, f"Ship {self.id} unloaded all containers"))
219            # Put containers in yard
220            for container in self.containers_to_unload:
221                yield self.port_yard.put(container)
222            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
223                               "all_container_stored", self.env.now, f"Ship {self.id} unloaded all containers"))
224        else:
225            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
226                               "all_cranes_attached", self.env.now, f"Ship {self.id} cranes allocated"))
227            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
228                               "all_container_unloaded", self.env.now, f"Ship {self.id} unloaded all containers"))
229            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
230                               "all_container_stored", self.env.now, f"Ship {self.id} unloaded all containers"))
231
232    def load(self):
233        """
234        Represents the loading of containers onto the vessel at the input crane movement rate.
235        This includes checking if the terminal can export containers, loading them onto the vessel, and removing them from the yard.
236        """
237        export_terminal = get_value_by_terminal(
238            self.terminal_data, 'Container', self.selected_terminal, 'export')
239        # Load containers
240        if export_terminal:
241            for container in self.containers_to_load:
242                # Remove the container from the yard
243                yield self.port_yard.get()
244                # Load the containers onto the vessel
245                load_time_per_container = self.load_time
246                self.cargo_wait_time += load_time_per_container / self.num_cranes
247                yield self.env.timeout(load_time_per_container / self.num_cranes)
248            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
249                               "all_container_loaded", self.env.now, f"Ship {self.id} loaded all containers"))
250        else:
251            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
252                               "all_container_loaded", self.env.now, f"Ship {self.id} loaded all containers"))
253
254    def wait(self):
255        """
256        Represents the waiting time at the port after unloading and loading operations.
257        This includes calculating the waiting time based on the terminal efficiency and cargo wait time.
258        """
259        # Calculate the waiting time based on the terminal efficiency and cargo wait time
260        port_waiting_time = (1 / CONTAINER_TERMINAL_EFFICIENCY) * \
261            self.cargo_wait_time - self.cargo_wait_time
262        yield self.env.timeout(port_waiting_time)
263
264    def detach_and_depart(self):
265        """
266        Represents the process of detaching from the berth and departing from the port.
267        This includes returning the cranes to the berth and releasing the berth for other ships.
268        """
269        # Return the cranes to the berth
270        for crane in self.allocated_cranes:
271            yield self.current_berth.cranes.put(crane)
272        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
273                           "all_cranes_returned", self.env.now, f"Ship {self.id} returned all cranes"))
274
275        # Depart from the berth
276        yield self.port_berths.put(self.current_berth)

Class to simulate the container terminal and the processes of arriving ships. This class handles the docking, unloading, loading, waiting, and departure of container ships. It also manages the allocation of berths and cranes, and the interaction with the channel for ship movements.
The processes include the following steps:

  1. Ship arrival at the port.
  2. Anchorage waiting.
  3. Berth allocation.
  4. Channel process (inbound).
  5. Docking the ship to a berth.
  6. Unloading containers from the ship.
  7. Loading containers onto the ship.
  8. Waiting at the port after unloading and loading operations.
  9. Detaching from the berth and departing from the port.
  10. Channel process (outbound).
  11. Ship departure from the port. The class also logs events and ship activities 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 for day pilots available
  • night_pilots (simpy.Container) : Simpy container for night pilots available
  • tugboats (simpy.Container): Simpy container for tugboats available
  • 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., Container, Liquid, DryBulk)
  • draft (float): Draft of the ship
  • width (float): Width of the ship
  • unload_time_per_container (float): Time taken to unload a container
  • load_time_per_container (float): Time taken to load a container
  • containers_to_unload (list): List of containers to be unloaded
  • containers_to_load (list): List of containers to be loaded
  • 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 representing the berths at the port
  • port_yard (simpy.Store): Store representing the port's yard for containers
  • 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 about the terminals, including transfer units per berth and import/export capabilities
def process(self):
117    def process(self):
118        """
119        Process for the container terminal, simulating the arrival, unloading, loading, waiting, and departure of ships.
120        """
121
122        self.ship_logs = update_ship_logs(self.ship_logs, "C", 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,
123                                          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,
124                                          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,
125                                          time_to_travel_channel_out=nan, time_to_common_channel_out=nan, ship_end_time=nan)
126
127        # ship arrival
128        ship_start_time = self.env.now
129        update_ship_logs(self.ship_logs, "C", self.id,
130                         self.selected_terminal, ship_start_time=ship_start_time)
131        self.events.append((f"Ship_{self.id}", "Container", f"L.{self.selected_terminal}",
132                           "arrive", self.env.now, f"Ship {self.id} arrived at the port"))
133
134        # Anchorage waiting
135        yield self.env.timeout(constants.ANCHORAGE_WAITING_CONTAINER)
136
137        # Berth allocation
138        start_time = self.env.now
139        yield self.env.process(self.dock())
140        time_to_get_berth = self.env.now - ship_start_time
141        update_ship_logs(self.ship_logs, "C", self.id,
142                         self.selected_terminal, time_to_get_berth=time_to_get_berth)
143
144        # check day or night
145        self.day = is_daytime(self.env.now, 7, 19)
146
147        # Channel process (in)
148        yield self.env.process(self.channel.channel_process(self.ship_info, self.day, self.last_section, self.id, self.selected_terminal, "C", self.run_id))
149
150        # Terminal process
151        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "dock", self.env.now,
152                           f"Ship {self.id} docked to berth {self.current_berth.id} with waiting time {'N/A'}"))
153
154        start_time = self.env.now
155        yield self.env.process(self.unload())
156        unloading_time = self.env.now - start_time
157        update_ship_logs(self.ship_logs, "C", self.id,
158                         self.selected_terminal, unloading_time=unloading_time)
159
160        start_time = self.env.now
161        yield self.env.process(self.load())
162        loading_time = self.env.now - start_time
163        update_ship_logs(self.ship_logs, "C", self.id,
164                         self.selected_terminal, loading_time=loading_time)
165
166        start_time = self.env.now
167        yield self.env.process(self.wait())
168        waiting_time = self.env.now - start_time
169        update_ship_logs(self.ship_logs, "C", self.id,
170                         self.selected_terminal, waiting_time=waiting_time)
171
172        start_time = self.env.now
173        yield self.env.process(self.detach_and_depart())
174        departure_time = self.env.now - start_time
175        update_ship_logs(self.ship_logs, "C", self.id,
176                         self.selected_terminal, departure_time=departure_time)
177
178        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "undock", self.env.now,
179                           f"Ship {self.id} undocked to berth {self.current_berth.id} with waiting time {'N/A'}"))
180
181        # Channel process (out)
182        self.day = is_daytime(self.env.now, 7, 19)
183        yield self.env.process(self.channel.channel_process(self.ship_info, self.day,  self.last_section, self.id, self.selected_terminal, "C", self.run_id))
184
185        ship_end_time = self.env.now
186        update_ship_logs(self.ship_logs, "C", self.id,
187                         self.selected_terminal, ship_end_time=ship_end_time)
188        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}", "depart", self.env.now,
189                           f"Ship {self.id} departed from berth {self.current_berth.id} with waiting time {'N/A'}"))

Process for the container terminal, simulating the arrival, unloading, loading, waiting, and departure of ships.

def dock(self):
191    def dock(self):
192        """
193        Represents the process of docking the ship to a berth and allocating cranes.
194        This includes waiting for a berth to become available and then getting the cranes allocated.
195        """
196        self.current_berth = yield self.port_berths.get()

Represents the process of docking the ship to a berth and allocating cranes. This includes waiting for a berth to become available and then getting the cranes allocated.

def unload(self):
198    def unload(self):
199        """
200        Represents the unloading of containers from the vessel at the input crane movement rate.
201        This includes requesting cranes, unloading containers, and storing them in the port yard.
202        """
203        import_terminal = get_value_by_terminal(
204            self.terminal_data, 'Container', self.selected_terminal, 'import')
205        if import_terminal:
206            # Request for the required number of cranes
207            for _ in range(self.num_cranes):
208                crane = yield self.current_berth.cranes.get()
209                self.allocated_cranes.append(crane)
210            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
211                               "all_cranes_attached", self.env.now, f"Ship {self.id} cranes allocated"))
212            # Unload containers
213            for container in self.containers_to_unload:
214                unload_time_per_container = self.unload_time
215                self.cargo_wait_time += unload_time_per_container / self.num_cranes
216                yield self.env.timeout(unload_time_per_container / self.num_cranes)
217            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
218                               "all_container_unloaded", self.env.now, f"Ship {self.id} unloaded all containers"))
219            # Put containers in yard
220            for container in self.containers_to_unload:
221                yield self.port_yard.put(container)
222            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
223                               "all_container_stored", self.env.now, f"Ship {self.id} unloaded all containers"))
224        else:
225            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
226                               "all_cranes_attached", self.env.now, f"Ship {self.id} cranes allocated"))
227            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
228                               "all_container_unloaded", self.env.now, f"Ship {self.id} unloaded all containers"))
229            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
230                               "all_container_stored", self.env.now, f"Ship {self.id} unloaded all containers"))

Represents the unloading of containers from the vessel at the input crane movement rate. This includes requesting cranes, unloading containers, and storing them in the port yard.

def load(self):
232    def load(self):
233        """
234        Represents the loading of containers onto the vessel at the input crane movement rate.
235        This includes checking if the terminal can export containers, loading them onto the vessel, and removing them from the yard.
236        """
237        export_terminal = get_value_by_terminal(
238            self.terminal_data, 'Container', self.selected_terminal, 'export')
239        # Load containers
240        if export_terminal:
241            for container in self.containers_to_load:
242                # Remove the container from the yard
243                yield self.port_yard.get()
244                # Load the containers onto the vessel
245                load_time_per_container = self.load_time
246                self.cargo_wait_time += load_time_per_container / self.num_cranes
247                yield self.env.timeout(load_time_per_container / self.num_cranes)
248            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
249                               "all_container_loaded", self.env.now, f"Ship {self.id} loaded all containers"))
250        else:
251            self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
252                               "all_container_loaded", self.env.now, f"Ship {self.id} loaded all containers"))

Represents the loading of containers onto the vessel at the input crane movement rate. This includes checking if the terminal can export containers, loading them onto the vessel, and removing them from the yard.

def wait(self):
254    def wait(self):
255        """
256        Represents the waiting time at the port after unloading and loading operations.
257        This includes calculating the waiting time based on the terminal efficiency and cargo wait time.
258        """
259        # Calculate the waiting time based on the terminal efficiency and cargo wait time
260        port_waiting_time = (1 / CONTAINER_TERMINAL_EFFICIENCY) * \
261            self.cargo_wait_time - self.cargo_wait_time
262        yield self.env.timeout(port_waiting_time)

Represents the waiting time at the port after unloading and loading operations. This includes calculating the waiting time based on the terminal efficiency and cargo wait time.

def detach_and_depart(self):
264    def detach_and_depart(self):
265        """
266        Represents the process of detaching from the berth and departing from the port.
267        This includes returning the cranes to the berth and releasing the berth for other ships.
268        """
269        # Return the cranes to the berth
270        for crane in self.allocated_cranes:
271            yield self.current_berth.cranes.put(crane)
272        self.events.append((f"Ship_{self.id}", "Container", f"C.{self.selected_terminal}",
273                           "all_cranes_returned", self.env.now, f"Ship {self.id} returned all cranes"))
274
275        # Depart from the berth
276        yield self.port_berths.put(self.current_berth)

Represents the process of detaching from the berth and departing from the port. This includes returning the cranes to the berth and releasing the berth for other ships.