simulation_classes.train

This module defines the Train class, which simulates the loading and unloading of cargo at a terminal. The train processes cargo in batches, either importing or exporting based on the flags provided.

  1"""
  2This module defines the Train class, which simulates the loading and unloading of cargo at a terminal.
  3The train processes cargo in batches, either importing or exporting based on the flags provided.
  4"""
  5
  6from simulation_classes.port import Container
  7
  8class Train(object):
  9    """Train class to simulate the loading and unloading of cargo at a terminal.
 10        Args:
 11            env (simpy.Environment): Simulation environment.
 12            train_id (str): Unique identifier for the train.
 13            terminal_id (str): Unique identifier for the terminal.
 14            car_amount (int): Number of cars in the train.
 15            cargo_transfer_rate (float): Rate at which cargo is transferred (e.g., pounds per hour).
 16            racks (simpy.Resource): Resource representing the unloading racks.
 17            cargo_yard (simpy.Container): Container representing the cargo yard.
 18            train_events (dict): Dictionary to log events related to the train.
 19            transfer_amount (float): Total amount of cargo to be transferred per car.
 20            import_bool (bool): Flag indicating if the train is importing cargo.
 21            export_bool (bool): Flag indicating if the train is exporting cargo.
 22            cargo_type (str): Type of cargo being handled ("Container" or "Bulk").
 23        """
 24    def __init__(self, env, train_id, terminal_id, car_amount, cargo_transfer_rate, racks, cargo_yard, train_events, transfer_amount, import_bool, export_bool, cargo_type):
 25        self.env = env
 26        self.train_id = train_id
 27        self.terminal_id = terminal_id
 28        self.car_amount = car_amount  
 29        self.cargo_transfer_rate = cargo_transfer_rate 
 30        self.racks = racks  # simpy.Resource object for unloading
 31        self.cargo_yard = cargo_yard
 32        self.train_events = train_events
 33        self.transfer_amount_per_car = transfer_amount / car_amount
 34        self.import_bool = import_bool
 35        self.export_bool = export_bool
 36        self.cargo_type = cargo_type
 37        self.env.process(self.process())
 38
 39    def process(self):
 40        """
 41        Process to handle the train's loading and unloading operations.
 42        This method initializes the train's event log, waits for the train to arrive,
 43        and then either imports or exports cargo based on the flags provided.
 44        It also logs the time taken for each operation and the start/end times of each car.
 45        """
 46        # Initialize train event log
 47        self.train_events[self.train_id] = {}
 48        self.train_events[self.train_id]["Batch size"] = self.racks.capacity
 49        self.train_events[self.train_id]["Cargo type"] = self.cargo_type
 50        self.train_events[self.train_id]["Cargo transfer rate"] = self.cargo_transfer_rate
 51        self.train_events[self.train_id]["Transfer amount per car"] = self.transfer_amount_per_car
 52        self.train_events[self.train_id]["Import/Export"] = "Import" if self.import_bool else "Export"
 53        self.train_events[self.train_id]["Train ID"] = self.train_id
 54        self.train_events[self.train_id]["Terminal ID"] = self.terminal_id
 55        self.train_events[self.train_id]["Car amount"] = self.car_amount
 56        self.train_events[self.train_id]["Car start/end times"] = []
 57        self.train_events[self.train_id]["Time to get racks"] = []
 58        self.train_events[self.train_id]["Train Arrival"] = self.env.now    
 59
 60        if self.import_bool:  # Import Process
 61            yield self.env.process(self.import_cargo(self.car_amount))
 62        if self.export_bool:  # Export Process
 63            yield self.env.process(self.export_cargo(self.car_amount))
 64
 65        self.train_events[self.train_id]["Train Departure"] = self.env.now
 66        self.train_events[self.train_id]["Train Duration"] = self.train_events[self.train_id]["Train Departure"] - self.train_events[self.train_id]["Train Arrival"]
 67
 68    def import_cargo(self, empty_cars):
 69        """
 70        Import cargo into the train's cars.
 71        This method processes the loading of cargo into the train's cars in batches.
 72        Args:
 73            empty_cars (int): Number of empty cars to be loaded with cargo.
 74        Yields:
 75            simpy.Timeout: Waits for all cars in the batch to be loaded before proceeding.
 76        Returns:
 77            None
 78        """
 79        cars_remaining = empty_cars
 80        while cars_remaining > 0:
 81            batch_size = min(cars_remaining, self.racks.capacity)
 82            tasks = [self.env.process(self.load_car()) for _ in range(batch_size)]
 83            yield self.env.all_of(tasks)  # Wait for all cars in the batch to be loaded
 84            cars_remaining -= batch_size
 85
 86    def export_cargo(self, full_cars):
 87        """
 88        Export cargo from the train's cars.
 89        This method processes the unloading of cargo from the train's cars in batches.
 90        Args:
 91            full_cars (int): Number of full cars to be unloaded.
 92        Yields:
 93            simpy.Timeout: Waits for all cars in the batch to be unloaded before proceeding.
 94        Returns:
 95            None
 96        """
 97        cars_remaining = full_cars
 98        while cars_remaining > 0:
 99            batch_size = min(cars_remaining, self.racks.capacity)
100            tasks = [self.env.process(self.unload_car()) for _ in range(batch_size)]
101            yield self.env.all_of(tasks)  # Wait for all cars in the batch to be unloaded
102            cars_remaining -= batch_size                                                                                              
103
104    def load_car(self):
105        """
106        Load cargo into a single car of the train.
107        This method handles the loading of cargo into a car, including waiting for the racks
108        and calculating the time taken for loading.
109        Yields:
110            simpy.Timeout: Waits for the loading process to complete.
111        Returns:
112            None
113        """
114        car_start_time = self.env.now
115        with self.racks.request() as req:
116            yield req
117            self.train_events[self.train_id]["Time to get racks"].append(self.env.now - car_start_time)
118            load_time = self.transfer_amount_per_car / self.cargo_transfer_rate # ex: (50 lbs / car) / (20 lbs/hr) = 2.5 hr / car
119            yield self.env.timeout(load_time)
120            if self.cargo_type == "Container":
121                num_containers =  int(self.transfer_amount_per_car)
122                for i in range(num_containers):
123                    yield self.cargo_yard.get()
124            else:
125                yield self.cargo_yard.get(self.transfer_amount_per_car)
126        car_end_time = self.env.now
127        self.train_events[self.train_id]["Car start/end times"].append((car_start_time, car_end_time))
128            
129
130    def unload_car(self):
131        """
132        Unload cargo from a single car of the train.
133        This method handles the unloading of cargo from a car, including waiting for the racks
134        and calculating the time taken for unloading.
135        Yields:
136            simpy.Timeout: Waits for the unloading process to complete.
137        Returns:
138            None
139        """
140        car_start_time = self.env.now
141        with self.racks.request() as req:
142            yield req
143            self.train_events[self.train_id]["Time to get racks"].append(self.env.now - car_start_time)
144            unload_time = self.transfer_amount_per_car / self.cargo_transfer_rate # ex: (50 lbs / car) / (20 lbs/hr) = 2.5 hr / car
145            yield self.env.timeout(unload_time)
146            if self.cargo_type == "Container":
147                num_containers =  int(self.transfer_amount_per_car)
148                for i in range(num_containers):
149                    ctr = Container(id=f"{self.train_id}_unload_{i}", width=100000)
150                    yield self.cargo_yard.put(ctr)
151            else:
152                self.cargo_yard.put(self.transfer_amount_per_car)
153        car_end_time = self.env.now
154        self.train_events[self.train_id]["Car start/end times"].append((car_start_time, car_end_time))
155
156                                  
157            
158            
159            
160             
161             
162               
163            
164             
165             
166            
167            
168                                                                                                                               
class Train:
  9class Train(object):
 10    """Train class to simulate the loading and unloading of cargo at a terminal.
 11        Args:
 12            env (simpy.Environment): Simulation environment.
 13            train_id (str): Unique identifier for the train.
 14            terminal_id (str): Unique identifier for the terminal.
 15            car_amount (int): Number of cars in the train.
 16            cargo_transfer_rate (float): Rate at which cargo is transferred (e.g., pounds per hour).
 17            racks (simpy.Resource): Resource representing the unloading racks.
 18            cargo_yard (simpy.Container): Container representing the cargo yard.
 19            train_events (dict): Dictionary to log events related to the train.
 20            transfer_amount (float): Total amount of cargo to be transferred per car.
 21            import_bool (bool): Flag indicating if the train is importing cargo.
 22            export_bool (bool): Flag indicating if the train is exporting cargo.
 23            cargo_type (str): Type of cargo being handled ("Container" or "Bulk").
 24        """
 25    def __init__(self, env, train_id, terminal_id, car_amount, cargo_transfer_rate, racks, cargo_yard, train_events, transfer_amount, import_bool, export_bool, cargo_type):
 26        self.env = env
 27        self.train_id = train_id
 28        self.terminal_id = terminal_id
 29        self.car_amount = car_amount  
 30        self.cargo_transfer_rate = cargo_transfer_rate 
 31        self.racks = racks  # simpy.Resource object for unloading
 32        self.cargo_yard = cargo_yard
 33        self.train_events = train_events
 34        self.transfer_amount_per_car = transfer_amount / car_amount
 35        self.import_bool = import_bool
 36        self.export_bool = export_bool
 37        self.cargo_type = cargo_type
 38        self.env.process(self.process())
 39
 40    def process(self):
 41        """
 42        Process to handle the train's loading and unloading operations.
 43        This method initializes the train's event log, waits for the train to arrive,
 44        and then either imports or exports cargo based on the flags provided.
 45        It also logs the time taken for each operation and the start/end times of each car.
 46        """
 47        # Initialize train event log
 48        self.train_events[self.train_id] = {}
 49        self.train_events[self.train_id]["Batch size"] = self.racks.capacity
 50        self.train_events[self.train_id]["Cargo type"] = self.cargo_type
 51        self.train_events[self.train_id]["Cargo transfer rate"] = self.cargo_transfer_rate
 52        self.train_events[self.train_id]["Transfer amount per car"] = self.transfer_amount_per_car
 53        self.train_events[self.train_id]["Import/Export"] = "Import" if self.import_bool else "Export"
 54        self.train_events[self.train_id]["Train ID"] = self.train_id
 55        self.train_events[self.train_id]["Terminal ID"] = self.terminal_id
 56        self.train_events[self.train_id]["Car amount"] = self.car_amount
 57        self.train_events[self.train_id]["Car start/end times"] = []
 58        self.train_events[self.train_id]["Time to get racks"] = []
 59        self.train_events[self.train_id]["Train Arrival"] = self.env.now    
 60
 61        if self.import_bool:  # Import Process
 62            yield self.env.process(self.import_cargo(self.car_amount))
 63        if self.export_bool:  # Export Process
 64            yield self.env.process(self.export_cargo(self.car_amount))
 65
 66        self.train_events[self.train_id]["Train Departure"] = self.env.now
 67        self.train_events[self.train_id]["Train Duration"] = self.train_events[self.train_id]["Train Departure"] - self.train_events[self.train_id]["Train Arrival"]
 68
 69    def import_cargo(self, empty_cars):
 70        """
 71        Import cargo into the train's cars.
 72        This method processes the loading of cargo into the train's cars in batches.
 73        Args:
 74            empty_cars (int): Number of empty cars to be loaded with cargo.
 75        Yields:
 76            simpy.Timeout: Waits for all cars in the batch to be loaded before proceeding.
 77        Returns:
 78            None
 79        """
 80        cars_remaining = empty_cars
 81        while cars_remaining > 0:
 82            batch_size = min(cars_remaining, self.racks.capacity)
 83            tasks = [self.env.process(self.load_car()) for _ in range(batch_size)]
 84            yield self.env.all_of(tasks)  # Wait for all cars in the batch to be loaded
 85            cars_remaining -= batch_size
 86
 87    def export_cargo(self, full_cars):
 88        """
 89        Export cargo from the train's cars.
 90        This method processes the unloading of cargo from the train's cars in batches.
 91        Args:
 92            full_cars (int): Number of full cars to be unloaded.
 93        Yields:
 94            simpy.Timeout: Waits for all cars in the batch to be unloaded before proceeding.
 95        Returns:
 96            None
 97        """
 98        cars_remaining = full_cars
 99        while cars_remaining > 0:
100            batch_size = min(cars_remaining, self.racks.capacity)
101            tasks = [self.env.process(self.unload_car()) for _ in range(batch_size)]
102            yield self.env.all_of(tasks)  # Wait for all cars in the batch to be unloaded
103            cars_remaining -= batch_size                                                                                              
104
105    def load_car(self):
106        """
107        Load cargo into a single car of the train.
108        This method handles the loading of cargo into a car, including waiting for the racks
109        and calculating the time taken for loading.
110        Yields:
111            simpy.Timeout: Waits for the loading process to complete.
112        Returns:
113            None
114        """
115        car_start_time = self.env.now
116        with self.racks.request() as req:
117            yield req
118            self.train_events[self.train_id]["Time to get racks"].append(self.env.now - car_start_time)
119            load_time = self.transfer_amount_per_car / self.cargo_transfer_rate # ex: (50 lbs / car) / (20 lbs/hr) = 2.5 hr / car
120            yield self.env.timeout(load_time)
121            if self.cargo_type == "Container":
122                num_containers =  int(self.transfer_amount_per_car)
123                for i in range(num_containers):
124                    yield self.cargo_yard.get()
125            else:
126                yield self.cargo_yard.get(self.transfer_amount_per_car)
127        car_end_time = self.env.now
128        self.train_events[self.train_id]["Car start/end times"].append((car_start_time, car_end_time))
129            
130
131    def unload_car(self):
132        """
133        Unload cargo from a single car of the train.
134        This method handles the unloading of cargo from a car, including waiting for the racks
135        and calculating the time taken for unloading.
136        Yields:
137            simpy.Timeout: Waits for the unloading process to complete.
138        Returns:
139            None
140        """
141        car_start_time = self.env.now
142        with self.racks.request() as req:
143            yield req
144            self.train_events[self.train_id]["Time to get racks"].append(self.env.now - car_start_time)
145            unload_time = self.transfer_amount_per_car / self.cargo_transfer_rate # ex: (50 lbs / car) / (20 lbs/hr) = 2.5 hr / car
146            yield self.env.timeout(unload_time)
147            if self.cargo_type == "Container":
148                num_containers =  int(self.transfer_amount_per_car)
149                for i in range(num_containers):
150                    ctr = Container(id=f"{self.train_id}_unload_{i}", width=100000)
151                    yield self.cargo_yard.put(ctr)
152            else:
153                self.cargo_yard.put(self.transfer_amount_per_car)
154        car_end_time = self.env.now
155        self.train_events[self.train_id]["Car start/end times"].append((car_start_time, car_end_time))

Train class to simulate the loading and unloading of cargo at a terminal.

Arguments:
  • env (simpy.Environment): Simulation environment.
  • train_id (str): Unique identifier for the train.
  • terminal_id (str): Unique identifier for the terminal.
  • car_amount (int): Number of cars in the train.
  • cargo_transfer_rate (float): Rate at which cargo is transferred (e.g., pounds per hour).
  • racks (simpy.Resource): Resource representing the unloading racks.
  • cargo_yard (simpy.Container): Container representing the cargo yard.
  • train_events (dict): Dictionary to log events related to the train.
  • transfer_amount (float): Total amount of cargo to be transferred per car.
  • import_bool (bool): Flag indicating if the train is importing cargo.
  • export_bool (bool): Flag indicating if the train is exporting cargo.
  • cargo_type (str): Type of cargo being handled ("Container" or "Bulk").
def process(self):
40    def process(self):
41        """
42        Process to handle the train's loading and unloading operations.
43        This method initializes the train's event log, waits for the train to arrive,
44        and then either imports or exports cargo based on the flags provided.
45        It also logs the time taken for each operation and the start/end times of each car.
46        """
47        # Initialize train event log
48        self.train_events[self.train_id] = {}
49        self.train_events[self.train_id]["Batch size"] = self.racks.capacity
50        self.train_events[self.train_id]["Cargo type"] = self.cargo_type
51        self.train_events[self.train_id]["Cargo transfer rate"] = self.cargo_transfer_rate
52        self.train_events[self.train_id]["Transfer amount per car"] = self.transfer_amount_per_car
53        self.train_events[self.train_id]["Import/Export"] = "Import" if self.import_bool else "Export"
54        self.train_events[self.train_id]["Train ID"] = self.train_id
55        self.train_events[self.train_id]["Terminal ID"] = self.terminal_id
56        self.train_events[self.train_id]["Car amount"] = self.car_amount
57        self.train_events[self.train_id]["Car start/end times"] = []
58        self.train_events[self.train_id]["Time to get racks"] = []
59        self.train_events[self.train_id]["Train Arrival"] = self.env.now    
60
61        if self.import_bool:  # Import Process
62            yield self.env.process(self.import_cargo(self.car_amount))
63        if self.export_bool:  # Export Process
64            yield self.env.process(self.export_cargo(self.car_amount))
65
66        self.train_events[self.train_id]["Train Departure"] = self.env.now
67        self.train_events[self.train_id]["Train Duration"] = self.train_events[self.train_id]["Train Departure"] - self.train_events[self.train_id]["Train Arrival"]

Process to handle the train's loading and unloading operations. This method initializes the train's event log, waits for the train to arrive, and then either imports or exports cargo based on the flags provided. It also logs the time taken for each operation and the start/end times of each car.

def import_cargo(self, empty_cars):
69    def import_cargo(self, empty_cars):
70        """
71        Import cargo into the train's cars.
72        This method processes the loading of cargo into the train's cars in batches.
73        Args:
74            empty_cars (int): Number of empty cars to be loaded with cargo.
75        Yields:
76            simpy.Timeout: Waits for all cars in the batch to be loaded before proceeding.
77        Returns:
78            None
79        """
80        cars_remaining = empty_cars
81        while cars_remaining > 0:
82            batch_size = min(cars_remaining, self.racks.capacity)
83            tasks = [self.env.process(self.load_car()) for _ in range(batch_size)]
84            yield self.env.all_of(tasks)  # Wait for all cars in the batch to be loaded
85            cars_remaining -= batch_size

Import cargo into the train's cars. This method processes the loading of cargo into the train's cars in batches.

Arguments:
  • empty_cars (int): Number of empty cars to be loaded with cargo.
Yields:

simpy.Timeout: Waits for all cars in the batch to be loaded before proceeding.

Returns:

None

def export_cargo(self, full_cars):
 87    def export_cargo(self, full_cars):
 88        """
 89        Export cargo from the train's cars.
 90        This method processes the unloading of cargo from the train's cars in batches.
 91        Args:
 92            full_cars (int): Number of full cars to be unloaded.
 93        Yields:
 94            simpy.Timeout: Waits for all cars in the batch to be unloaded before proceeding.
 95        Returns:
 96            None
 97        """
 98        cars_remaining = full_cars
 99        while cars_remaining > 0:
100            batch_size = min(cars_remaining, self.racks.capacity)
101            tasks = [self.env.process(self.unload_car()) for _ in range(batch_size)]
102            yield self.env.all_of(tasks)  # Wait for all cars in the batch to be unloaded
103            cars_remaining -= batch_size                                                                                              

Export cargo from the train's cars. This method processes the unloading of cargo from the train's cars in batches.

Arguments:
  • full_cars (int): Number of full cars to be unloaded.
Yields:

simpy.Timeout: Waits for all cars in the batch to be unloaded before proceeding.

Returns:

None

def load_car(self):
105    def load_car(self):
106        """
107        Load cargo into a single car of the train.
108        This method handles the loading of cargo into a car, including waiting for the racks
109        and calculating the time taken for loading.
110        Yields:
111            simpy.Timeout: Waits for the loading process to complete.
112        Returns:
113            None
114        """
115        car_start_time = self.env.now
116        with self.racks.request() as req:
117            yield req
118            self.train_events[self.train_id]["Time to get racks"].append(self.env.now - car_start_time)
119            load_time = self.transfer_amount_per_car / self.cargo_transfer_rate # ex: (50 lbs / car) / (20 lbs/hr) = 2.5 hr / car
120            yield self.env.timeout(load_time)
121            if self.cargo_type == "Container":
122                num_containers =  int(self.transfer_amount_per_car)
123                for i in range(num_containers):
124                    yield self.cargo_yard.get()
125            else:
126                yield self.cargo_yard.get(self.transfer_amount_per_car)
127        car_end_time = self.env.now
128        self.train_events[self.train_id]["Car start/end times"].append((car_start_time, car_end_time))

Load cargo into a single car of the train. This method handles the loading of cargo into a car, including waiting for the racks and calculating the time taken for loading.

Yields:

simpy.Timeout: Waits for the loading process to complete.

Returns:

None

def unload_car(self):
131    def unload_car(self):
132        """
133        Unload cargo from a single car of the train.
134        This method handles the unloading of cargo from a car, including waiting for the racks
135        and calculating the time taken for unloading.
136        Yields:
137            simpy.Timeout: Waits for the unloading process to complete.
138        Returns:
139            None
140        """
141        car_start_time = self.env.now
142        with self.racks.request() as req:
143            yield req
144            self.train_events[self.train_id]["Time to get racks"].append(self.env.now - car_start_time)
145            unload_time = self.transfer_amount_per_car / self.cargo_transfer_rate # ex: (50 lbs / car) / (20 lbs/hr) = 2.5 hr / car
146            yield self.env.timeout(unload_time)
147            if self.cargo_type == "Container":
148                num_containers =  int(self.transfer_amount_per_car)
149                for i in range(num_containers):
150                    ctr = Container(id=f"{self.train_id}_unload_{i}", width=100000)
151                    yield self.cargo_yard.put(ctr)
152            else:
153                self.cargo_yard.put(self.transfer_amount_per_car)
154        car_end_time = self.env.now
155        self.train_events[self.train_id]["Car start/end times"].append((car_start_time, car_end_time))

Unload cargo from a single car of the train. This method handles the unloading of cargo from a car, including waiting for the racks and calculating the time taken for unloading.

Yields:

simpy.Timeout: Waits for the unloading process to complete.

Returns:

None