simulation_handler.helpers
Helper functions for the simulation environment. This module contains various utility functions for logging, data cleaning, and random number generation used in the simulation.
1""" 2Helper functions for the simulation environment. 3This module contains various utility functions for logging, data cleaning, 4and random number generation used in the simulation. 5""" 6 7import os 8import shutil 9import re 10import random 11import os 12import glob 13 14import numpy as np 15from scipy.stats import truncnorm 16import pandas as pd 17 18import constants 19 20####################### 21# Logs 22####################### 23 24 25def log_line(run_id, filename, line): 26 """ 27 Logs a line to a specified file in the run's logs directory. 28 Args: 29 run_id (str): Unique identifier for the run to save the log. 30 filename (str): Name of the file to log the line. 31 line (str): The line to log. 32 Returns: 33 None: Appends the line to the specified log file. 34 """ 35 try: 36 with open(f'.{run_id}/logs/{filename}', 'a') as f: 37 f.write(line + '\n') 38 except FileNotFoundError: 39 with open(f'.{run_id}/logs/{filename}', 'w') as f: 40 f.write(line + '\n') 41 42 43def save_warning(run_id, message, print_val=True): 44 """ 45 Save a warning message to the logs directory for the current run. 46 If the logs directory does not exist, it will be created. 47 Args: 48 run_id (str): The identifier for the current run. 49 message (str): The warning message to be saved. 50 print_val (bool): Whether to print the message to the console. Default is True. 51 Returns: 52 None 53 """ 54 if not os.path.exists(f'.{run_id}/logs/warnings.txt'): 55 with open(f'.{run_id}/logs/warnings.txt', 'w') as f: 56 f.write('Warnings:\n') 57 with open(f'.{run_id}/logs/warnings.txt', 'a') as f: 58 f.write(f'{message}\n') 59 else: 60 with open(f'.{run_id}/logs/warnings.txt', 'a') as f: 61 f.write(f'{message}\n') 62 if print_val: 63 print(f"{message}") 64 65 66def clear_env(env, ship_proc, truck_proc, train_proc, data_taker_proc): 67 """ 68 Clear the environment and interrupt all processes. 69 Args: 70 env (Simpy.Environment): The simulation environment to be cleared. 71 ship_proc (Simpy.Process): The process for handling ships. 72 truck_proc (Simpy.Process): The process for handling trucks. 73 train_proc (Simpy.Process): The process for handling trains. 74 data_taker_proc: The process for taking data. 75 Returns: 76 None 77 """ 78 try: 79 ship_proc.interrupt() 80 except: 81 pass 82 try: 83 truck_proc.interrupt() 84 except: 85 pass 86 try: 87 train_proc.interrupt() 88 except: 89 pass 90 91 try: 92 data_taker_proc.interrupt() 93 except: 94 pass 95 96 env._queue.clear() 97 98 99def clear_logs(run_id): 100 """ 101 Clear the logs directory for the current run by removing all files and subdirectories. 102 Then create the necessary subdirectories again. 103 This function is useful for resetting the logs directory before starting a new simulation run. 104 Args: 105 run_id (str): The identifier for the current run. 106 Returns: 107 None 108 """ 109 110 if constants.DELETE_RESULTS_FOLDER == False: 111 shutil.rmtree(f'.{run_id}/logs', ignore_errors=True) 112 shutil.rmtree(f'.{run_id}/plots', ignore_errors=True) 113 # shutil.rmtree(f'.{run_id}/animations', ignore_errors=True) 114 shutil.rmtree(f'.{run_id}/bottlenecks', ignore_errors=True) 115 116 # os.makedirs(f'.{run_id}/animations') 117 118 os.makedirs(f'.{run_id}/logs') 119 os.makedirs(f'.{run_id}/logs/availability') 120 os.makedirs(f'.{run_id}/plots') 121 os.makedirs(f'.{run_id}/bottlenecks') 122 os.makedirs(f'.{run_id}/bottlenecks/chassisBays/') 123 os.makedirs(f'.{run_id}/plots/TerminalProcessCharts/') 124 os.makedirs(f'.{run_id}/plots/TerminalProcessDist/') 125 os.makedirs(f'.{run_id}/plots/DwellTimes/') 126 os.makedirs(f'.{run_id}/plots/TurnTimes/') 127 os.makedirs(f'.{run_id}/plots/DwellTimesDist/') 128 os.makedirs(f'.{run_id}/plots/TurnTimesDist/') 129 os.makedirs(f'.{run_id}/plots/scenarios') 130 os.makedirs(f'.{run_id}/plots/shipDistribution') 131 132 os.makedirs(f'.{run_id}/plots/truckDistribution') 133 os.makedirs(f'.{run_id}/plots/TruckDwellByCargo') 134 os.makedirs(f'.{run_id}/plots/TruckDwellByTerminal') 135 os.makedirs(f'.{run_id}/plots/TruckArrivalByCargo') 136 os.makedirs(f'.{run_id}/plots/TruckArrivalByTerminal') 137 138 # Create a force_action.txt file under logs 139 with open(f'.{run_id}/logs/force_action.txt', 'w') as f: 140 f.write('Force Actions') 141 f.write('\n') 142 143 144def clean_results_directory(): 145 """ 146 Remove existing result files and directories matching the pattern `.Results*`. 147 This function is useful for cleaning up the results directory before starting a new simulation run. 148 Args: 149 None 150 Returns: 151 None 152""" 153 for file_path in glob.glob(".Results*"): 154 try: 155 if os.path.isfile(file_path): 156 os.remove(file_path) 157 elif os.path.isdir(file_path): 158 for root, dirs, files in os.walk(file_path, topdown=False): 159 for name in files: 160 os.remove(os.path.join(root, name)) 161 for name in dirs: 162 os.rmdir(os.path.join(root, name)) 163 os.rmdir(file_path) 164 except OSError as e: 165 print(f"Error removing {file_path}: {e}") 166 167####################### 168# Data cleaning 169####################### 170 171 172def initialize_rng(seed): 173 """ 174 Initialize random state objects for repeatable randomness across runs. 175 Args: 176 seed (int): The seed value for the random number generator. 177 Returns: 178 None 179 """ 180 global rng_random, rng_numpy 181 rng_random = random.Random(seed) 182 rng_numpy = np.random.default_rng(seed) 183 184 185def clean_data(directory): 186 """ 187 Clean the terminal data by parsing ranges and converting them to tuples. 188 This function reads a CSV file containing terminal data, processes the range strings, 189 and converts them into tuples of integers or floats. 190 Args: 191 directory (str): The directory where the terminal data CSV file is located. 192 Returns: 193 pd.DataFrame: A DataFrame containing the cleaned terminal data with ranges parsed into tuples. 194 """ 195 196 def parse_range(range_str): 197 """ 198 Parse a range in the format '(a,b)' from a string into a tuple of integers or floats. 199 If the input is a single number, it will be returned as an integer. 200 Args: 201 range_str (str): The range string to be parsed. 202 Returns: 203 tuple or int: A tuple of integers if the input is a range, or an integer if it's a single number. 204 """ 205 # convert range string to a string 206 range_str = str(range_str) 207 try: 208 match = re.match(r"\(([\d.]+)\s*,\s*([\d.]+)\)", range_str) 209 start, end = int(match.group(1)), int(match.group(2)) 210 return (start, end) 211 except: 212 return int(range_str) 213 214 csv_data = pd.read_csv(f'{directory}/inputs/terminal_data.csv') 215 for column in csv_data.columns[3:]: 216 csv_data[column] = csv_data[column].apply( 217 lambda x: parse_range(x) if isinstance(x, str) else x) 218 return csv_data 219 220 221def create_terminal_data_cache(terminal_data, run_id, seed): 222 """ 223 Create a cache dictionary from terminal data for quick access to resource values. 224 This function initializes a random number generator with a given seed, 225 iterates through the terminal data, and populates a cache dictionary with keys 226 based on cargo type, terminal ID, and resource name. If the resource value is a range, 227 it stores a random integer from that range; otherwise, it stores the value directly. 228 Args: 229 terminal_data (pd.DataFrame): The DataFrame containing terminal data. 230 run_id (str): The identifier for the current run, used for logging. 231 seed (int): The seed value for the random number generator. 232 Returns: 233 dict: A dictionary where keys are tuples of (cargo_type, terminal_id, resource_name) 234 and values are the corresponding resource values or random integers from ranges. 235 """ 236 # Create the cache dictionary 237 initialize_rng(seed) 238 cache = {} 239 # Populate the cache 240 for index, row in terminal_data.iterrows(): 241 cargo_type = row['Cargo'] 242 terminal_id = row['Terminal'] 243 # Skip 'Cargo' and 'Terminal' columns 244 for resource_name in row.index[2:]: 245 value = row[resource_name] 246 key = (cargo_type, terminal_id, resource_name) 247 # If value is a tuple-like string, convert it to tuple and store a random range value; otherwise, store as int 248 if isinstance(value, tuple): 249 # Convert to tuple and store a random value from range 250 cache[key] = rng_random.randint(value[0], value[1]) 251 else: 252 if resource_name in ['train arrival rate', 'truck arrival rate']: 253 cache[key] = value 254 # Store integer or directly as it is 255 else: 256 cache[key] = int(value) 257 258 return cache 259 260 261def get_value_by_terminal(terminal_data_cache, cargo_type, terminal_id, resource_name): 262 """ 263 Retrieve a value from the terminal data cache based on cargo type, terminal ID, and resource name. 264 This function checks the cache for a specific key and returns the corresponding value. 265 Args: 266 terminal_data_cache (dict): The cache dictionary containing terminal data. 267 cargo_type (str): The type of cargo. 268 terminal_id (str): The ID of the terminal. 269 resource_name (str): The name of the resource. 270 Returns: 271 The value associated with the specified cargo type, terminal ID, and resource name, 272 or None if the key does not exist in the cache. 273 """ 274 key = (cargo_type, terminal_id, resource_name) 275 return terminal_data_cache.get(key) 276 277 278def create_terminal_tuple_cache(terminal_data, run_id, seed): 279 """ 280 Create a cache dictionary from terminal data for quick access to resource values as tuples. 281 This function initializes a random number generator with a given seed, 282 iterates through the terminal data, and populates a cache dictionary with keys 283 based on cargo type, terminal ID, and resource name. If the resource value is a range, 284 it stores the range as a tuple; otherwise, it stores the value as a tuple of itself. 285 Args: 286 terminal_data (pd.DataFrame): The DataFrame containing terminal data. 287 run_id (str): The identifier for the current run, used for logging. 288 seed (int): The seed value for the random number generator. 289 Returns: 290 dict: A dictionary where keys are tuples of (cargo_type, terminal_id, resource_name) 291 and values are tuples of the corresponding resource values or ranges. 292 """ 293 # Create the cache dictionary 294 initialize_rng(seed) 295 truck_load_cache = {} 296 297 # Populate the cache 298 for index, row in terminal_data.iterrows(): 299 cargo_type = row['Cargo'] 300 terminal_id = row['Terminal'] 301 # Skip 'Cargo' and 'Terminal' columns 302 for resource_name in row.index[2:]: 303 value = row[resource_name] 304 key = (cargo_type, terminal_id, resource_name) 305 # If value is a tuple-like string, convert it to tuple and store a random range value; otherwise, store as int 306 if isinstance(value, tuple): 307 # Convert to tuple and store a random value from range 308 truck_load_cache[key] = (value[0], value[1]) 309 else: 310 if resource_name in ['train arrival rate', 'truck arrival rate']: 311 truck_load_cache[key] = (value, value) 312 # Store integer or directly as it is 313 else: 314 truck_load_cache[key] = (int(value), int(value)) 315 316 return truck_load_cache 317 318 319def get_value_from_terminal_tuple(terminal_tuple_cache, cargo_type, terminal_id, resource_name): 320 """ 321 Retrieve a value from the terminal tuple cache based on cargo type, terminal ID, and resource name. 322 This function checks the cache for a specific key and returns a random integer from the range stored in the tuple. 323 Args: 324 terminal_tuple_cache (dict): The cache dictionary containing terminal data as tuples. 325 cargo_type (str): The type of cargo. 326 terminal_id (str): The ID of the terminal. 327 resource_name (str): The name of the resource. 328 Returns: 329 int: A random integer from the range stored in the tuple for the specified cargo type, terminal ID, and resource name. 330 If the key does not exist in the cache, it returns None. 331 """ 332 # initialize_rng(seed) 333 key = (cargo_type, terminal_id, resource_name) 334 tups = terminal_tuple_cache.get(key) 335 return rng_random.randint(tups[0], tups[1]) 336 337 338def get_values_by_terminal_random_sample(terminal_data, cargo_type, terminal_id, resource_name_input, num_samples, seed): 339 """ 340 Get a list of random samples from the terminal data based on cargo type and terminal ID. 341 This function retrieves a specific resource value from the terminal data for a given cargo type and terminal ID, 342 and generates a list of random samples from that value. If the value is a range, it samples from that range; 343 otherwise, it returns a list with the value repeated for the specified number of samples. 344 Args: 345 terminal_data (pd.DataFrame): The DataFrame containing terminal data. 346 cargo_type (str): The type of cargo. 347 terminal_id (str): The ID of the terminal. 348 resource_name_input (str): The name of the resource to sample from. 349 num_samples (int): The number of random samples to generate. 350 seed (int): The seed value for the random number generator. 351 Returns: 352 list: A list of random samples from the specified resource value. 353 If the resource value is a range, it returns random integers from that range; 354 otherwise, it returns a list with the value repeated for the specified number of samples. 355 """ 356 random.seed(seed) 357 358 row = terminal_data[(terminal_data['Terminal'] == terminal_id) & 359 (terminal_data['Cargo'] == cargo_type)].iloc[0] 360 361 value = row[resource_name_input] 362 363 try: 364 return [random.randint(int(value[0]), int(value[1])) for _ in range(num_samples)] 365 except: 366 return [value] * num_samples 367 368 369####################### 370# Helper functions 371####################### 372 373def is_daytime(time, start=6, end=18): 374 """ 375 Check if the given time is within the daytime range. 376 Args: 377 time (int or float): The time to check, in hours (0-23). 378 start (int): The start of the daytime range (default is 6). 379 end (int): The end of the daytime range (default is 18). 380 Returns: 381 bool: True if the time is within the daytime range, False otherwise. 382 """ 383 return start <= time % 24 <= end 384 385####################### 386# Random 387####################### 388 389 390def normal_random_with_sd(mu, sigma, seed, scale_factor=1): 391 """ 392 Generate a random value from a truncated normal distribution with specified mean and standard deviation. 393 The distribution is truncated to ensure the value is within a specified range based on the mean and standard deviation. 394 Args: 395 mu (float): The mean of the normal distribution. 396 sigma (float): The standard deviation of the normal distribution. 397 seed (int): Random seed for reproducibility. 398 scale_factor (float): Factor to scale the standard deviation for truncation (default is 1). 399 Returns: 400 float: A random value from the truncated normal distribution, ensuring it is within the range [a, b]. 401 """ 402 np.random.seed(seed) 403 a = max(0, mu - scale_factor * sigma) 404 b = max(0, mu + scale_factor * sigma) 405 if a <= 0: 406 a = 0 407 lower, upper = (a - mu) / sigma, (b - mu) / sigma 408 distribution = truncnorm(lower, upper, loc=mu, scale=sigma) 409 val = distribution.rvs() 410 if val < a: 411 return a 412 elif val > b: 413 return b 414 return distribution.rvs() 415 416 417def normal_random_with_limit(a, b, seed): 418 """ 419 Generate a random value from a truncated normal distribution between a and b. 420 Args: 421 a (float): Lower limit of the range. 422 b (float): Upper limit of the range. 423 seed (int): Random seed for reproducibility. 424 Returns: 425 float: A random value from the truncated normal distribution. 426 """ 427 np.random.seed(seed) 428 mean = (a + b) / 2 429 std_dev = (b - a) / 6 430 lower_bound = (a - mean) / std_dev 431 upper_bound = (b - mean) / std_dev 432 distribution = truncnorm(lower_bound, upper_bound, loc=mean, scale=std_dev) 433 return distribution.rvs()
26def log_line(run_id, filename, line): 27 """ 28 Logs a line to a specified file in the run's logs directory. 29 Args: 30 run_id (str): Unique identifier for the run to save the log. 31 filename (str): Name of the file to log the line. 32 line (str): The line to log. 33 Returns: 34 None: Appends the line to the specified log file. 35 """ 36 try: 37 with open(f'.{run_id}/logs/{filename}', 'a') as f: 38 f.write(line + '\n') 39 except FileNotFoundError: 40 with open(f'.{run_id}/logs/{filename}', 'w') as f: 41 f.write(line + '\n')
Logs a line to a specified file in the run's logs directory.
Arguments:
- run_id (str): Unique identifier for the run to save the log.
- filename (str): Name of the file to log the line.
- line (str): The line to log.
Returns:
None: Appends the line to the specified log file.
44def save_warning(run_id, message, print_val=True): 45 """ 46 Save a warning message to the logs directory for the current run. 47 If the logs directory does not exist, it will be created. 48 Args: 49 run_id (str): The identifier for the current run. 50 message (str): The warning message to be saved. 51 print_val (bool): Whether to print the message to the console. Default is True. 52 Returns: 53 None 54 """ 55 if not os.path.exists(f'.{run_id}/logs/warnings.txt'): 56 with open(f'.{run_id}/logs/warnings.txt', 'w') as f: 57 f.write('Warnings:\n') 58 with open(f'.{run_id}/logs/warnings.txt', 'a') as f: 59 f.write(f'{message}\n') 60 else: 61 with open(f'.{run_id}/logs/warnings.txt', 'a') as f: 62 f.write(f'{message}\n') 63 if print_val: 64 print(f"{message}")
Save a warning message to the logs directory for the current run. If the logs directory does not exist, it will be created.
Arguments:
- run_id (str): The identifier for the current run.
- message (str): The warning message to be saved.
- print_val (bool): Whether to print the message to the console. Default is True.
Returns:
None
67def clear_env(env, ship_proc, truck_proc, train_proc, data_taker_proc): 68 """ 69 Clear the environment and interrupt all processes. 70 Args: 71 env (Simpy.Environment): The simulation environment to be cleared. 72 ship_proc (Simpy.Process): The process for handling ships. 73 truck_proc (Simpy.Process): The process for handling trucks. 74 train_proc (Simpy.Process): The process for handling trains. 75 data_taker_proc: The process for taking data. 76 Returns: 77 None 78 """ 79 try: 80 ship_proc.interrupt() 81 except: 82 pass 83 try: 84 truck_proc.interrupt() 85 except: 86 pass 87 try: 88 train_proc.interrupt() 89 except: 90 pass 91 92 try: 93 data_taker_proc.interrupt() 94 except: 95 pass 96 97 env._queue.clear()
Clear the environment and interrupt all processes.
Arguments:
- env (Simpy.Environment): The simulation environment to be cleared.
- ship_proc (Simpy.Process): The process for handling ships.
- truck_proc (Simpy.Process): The process for handling trucks.
- train_proc (Simpy.Process): The process for handling trains.
- data_taker_proc: The process for taking data.
Returns:
None
100def clear_logs(run_id): 101 """ 102 Clear the logs directory for the current run by removing all files and subdirectories. 103 Then create the necessary subdirectories again. 104 This function is useful for resetting the logs directory before starting a new simulation run. 105 Args: 106 run_id (str): The identifier for the current run. 107 Returns: 108 None 109 """ 110 111 if constants.DELETE_RESULTS_FOLDER == False: 112 shutil.rmtree(f'.{run_id}/logs', ignore_errors=True) 113 shutil.rmtree(f'.{run_id}/plots', ignore_errors=True) 114 # shutil.rmtree(f'.{run_id}/animations', ignore_errors=True) 115 shutil.rmtree(f'.{run_id}/bottlenecks', ignore_errors=True) 116 117 # os.makedirs(f'.{run_id}/animations') 118 119 os.makedirs(f'.{run_id}/logs') 120 os.makedirs(f'.{run_id}/logs/availability') 121 os.makedirs(f'.{run_id}/plots') 122 os.makedirs(f'.{run_id}/bottlenecks') 123 os.makedirs(f'.{run_id}/bottlenecks/chassisBays/') 124 os.makedirs(f'.{run_id}/plots/TerminalProcessCharts/') 125 os.makedirs(f'.{run_id}/plots/TerminalProcessDist/') 126 os.makedirs(f'.{run_id}/plots/DwellTimes/') 127 os.makedirs(f'.{run_id}/plots/TurnTimes/') 128 os.makedirs(f'.{run_id}/plots/DwellTimesDist/') 129 os.makedirs(f'.{run_id}/plots/TurnTimesDist/') 130 os.makedirs(f'.{run_id}/plots/scenarios') 131 os.makedirs(f'.{run_id}/plots/shipDistribution') 132 133 os.makedirs(f'.{run_id}/plots/truckDistribution') 134 os.makedirs(f'.{run_id}/plots/TruckDwellByCargo') 135 os.makedirs(f'.{run_id}/plots/TruckDwellByTerminal') 136 os.makedirs(f'.{run_id}/plots/TruckArrivalByCargo') 137 os.makedirs(f'.{run_id}/plots/TruckArrivalByTerminal') 138 139 # Create a force_action.txt file under logs 140 with open(f'.{run_id}/logs/force_action.txt', 'w') as f: 141 f.write('Force Actions') 142 f.write('\n')
Clear the logs directory for the current run by removing all files and subdirectories. Then create the necessary subdirectories again. This function is useful for resetting the logs directory before starting a new simulation run.
Arguments:
- run_id (str): The identifier for the current run.
Returns:
None
145def clean_results_directory(): 146 """ 147 Remove existing result files and directories matching the pattern `.Results*`. 148 This function is useful for cleaning up the results directory before starting a new simulation run. 149 Args: 150 None 151 Returns: 152 None 153""" 154 for file_path in glob.glob(".Results*"): 155 try: 156 if os.path.isfile(file_path): 157 os.remove(file_path) 158 elif os.path.isdir(file_path): 159 for root, dirs, files in os.walk(file_path, topdown=False): 160 for name in files: 161 os.remove(os.path.join(root, name)) 162 for name in dirs: 163 os.rmdir(os.path.join(root, name)) 164 os.rmdir(file_path) 165 except OSError as e: 166 print(f"Error removing {file_path}: {e}")
Remove existing result files and directories matching the pattern .Results*.
This function is useful for cleaning up the results directory before starting a new simulation run.
Arguments:
- None
Returns:
None
173def initialize_rng(seed): 174 """ 175 Initialize random state objects for repeatable randomness across runs. 176 Args: 177 seed (int): The seed value for the random number generator. 178 Returns: 179 None 180 """ 181 global rng_random, rng_numpy 182 rng_random = random.Random(seed) 183 rng_numpy = np.random.default_rng(seed)
Initialize random state objects for repeatable randomness across runs.
Arguments:
- seed (int): The seed value for the random number generator.
Returns:
None
186def clean_data(directory): 187 """ 188 Clean the terminal data by parsing ranges and converting them to tuples. 189 This function reads a CSV file containing terminal data, processes the range strings, 190 and converts them into tuples of integers or floats. 191 Args: 192 directory (str): The directory where the terminal data CSV file is located. 193 Returns: 194 pd.DataFrame: A DataFrame containing the cleaned terminal data with ranges parsed into tuples. 195 """ 196 197 def parse_range(range_str): 198 """ 199 Parse a range in the format '(a,b)' from a string into a tuple of integers or floats. 200 If the input is a single number, it will be returned as an integer. 201 Args: 202 range_str (str): The range string to be parsed. 203 Returns: 204 tuple or int: A tuple of integers if the input is a range, or an integer if it's a single number. 205 """ 206 # convert range string to a string 207 range_str = str(range_str) 208 try: 209 match = re.match(r"\(([\d.]+)\s*,\s*([\d.]+)\)", range_str) 210 start, end = int(match.group(1)), int(match.group(2)) 211 return (start, end) 212 except: 213 return int(range_str) 214 215 csv_data = pd.read_csv(f'{directory}/inputs/terminal_data.csv') 216 for column in csv_data.columns[3:]: 217 csv_data[column] = csv_data[column].apply( 218 lambda x: parse_range(x) if isinstance(x, str) else x) 219 return csv_data
Clean the terminal data by parsing ranges and converting them to tuples. This function reads a CSV file containing terminal data, processes the range strings, and converts them into tuples of integers or floats.
Arguments:
- directory (str): The directory where the terminal data CSV file is located.
Returns:
pd.DataFrame: A DataFrame containing the cleaned terminal data with ranges parsed into tuples.
222def create_terminal_data_cache(terminal_data, run_id, seed): 223 """ 224 Create a cache dictionary from terminal data for quick access to resource values. 225 This function initializes a random number generator with a given seed, 226 iterates through the terminal data, and populates a cache dictionary with keys 227 based on cargo type, terminal ID, and resource name. If the resource value is a range, 228 it stores a random integer from that range; otherwise, it stores the value directly. 229 Args: 230 terminal_data (pd.DataFrame): The DataFrame containing terminal data. 231 run_id (str): The identifier for the current run, used for logging. 232 seed (int): The seed value for the random number generator. 233 Returns: 234 dict: A dictionary where keys are tuples of (cargo_type, terminal_id, resource_name) 235 and values are the corresponding resource values or random integers from ranges. 236 """ 237 # Create the cache dictionary 238 initialize_rng(seed) 239 cache = {} 240 # Populate the cache 241 for index, row in terminal_data.iterrows(): 242 cargo_type = row['Cargo'] 243 terminal_id = row['Terminal'] 244 # Skip 'Cargo' and 'Terminal' columns 245 for resource_name in row.index[2:]: 246 value = row[resource_name] 247 key = (cargo_type, terminal_id, resource_name) 248 # If value is a tuple-like string, convert it to tuple and store a random range value; otherwise, store as int 249 if isinstance(value, tuple): 250 # Convert to tuple and store a random value from range 251 cache[key] = rng_random.randint(value[0], value[1]) 252 else: 253 if resource_name in ['train arrival rate', 'truck arrival rate']: 254 cache[key] = value 255 # Store integer or directly as it is 256 else: 257 cache[key] = int(value) 258 259 return cache
Create a cache dictionary from terminal data for quick access to resource values. This function initializes a random number generator with a given seed, iterates through the terminal data, and populates a cache dictionary with keys based on cargo type, terminal ID, and resource name. If the resource value is a range, it stores a random integer from that range; otherwise, it stores the value directly.
Arguments:
- terminal_data (pd.DataFrame): The DataFrame containing terminal data.
- run_id (str): The identifier for the current run, used for logging.
- seed (int): The seed value for the random number generator.
Returns:
dict: A dictionary where keys are tuples of (cargo_type, terminal_id, resource_name) and values are the corresponding resource values or random integers from ranges.
262def get_value_by_terminal(terminal_data_cache, cargo_type, terminal_id, resource_name): 263 """ 264 Retrieve a value from the terminal data cache based on cargo type, terminal ID, and resource name. 265 This function checks the cache for a specific key and returns the corresponding value. 266 Args: 267 terminal_data_cache (dict): The cache dictionary containing terminal data. 268 cargo_type (str): The type of cargo. 269 terminal_id (str): The ID of the terminal. 270 resource_name (str): The name of the resource. 271 Returns: 272 The value associated with the specified cargo type, terminal ID, and resource name, 273 or None if the key does not exist in the cache. 274 """ 275 key = (cargo_type, terminal_id, resource_name) 276 return terminal_data_cache.get(key)
Retrieve a value from the terminal data cache based on cargo type, terminal ID, and resource name. This function checks the cache for a specific key and returns the corresponding value.
Arguments:
- terminal_data_cache (dict): The cache dictionary containing terminal data.
- cargo_type (str): The type of cargo.
- terminal_id (str): The ID of the terminal.
- resource_name (str): The name of the resource.
Returns:
The value associated with the specified cargo type, terminal ID, and resource name,
or None if the key does not exist in the cache.
279def create_terminal_tuple_cache(terminal_data, run_id, seed): 280 """ 281 Create a cache dictionary from terminal data for quick access to resource values as tuples. 282 This function initializes a random number generator with a given seed, 283 iterates through the terminal data, and populates a cache dictionary with keys 284 based on cargo type, terminal ID, and resource name. If the resource value is a range, 285 it stores the range as a tuple; otherwise, it stores the value as a tuple of itself. 286 Args: 287 terminal_data (pd.DataFrame): The DataFrame containing terminal data. 288 run_id (str): The identifier for the current run, used for logging. 289 seed (int): The seed value for the random number generator. 290 Returns: 291 dict: A dictionary where keys are tuples of (cargo_type, terminal_id, resource_name) 292 and values are tuples of the corresponding resource values or ranges. 293 """ 294 # Create the cache dictionary 295 initialize_rng(seed) 296 truck_load_cache = {} 297 298 # Populate the cache 299 for index, row in terminal_data.iterrows(): 300 cargo_type = row['Cargo'] 301 terminal_id = row['Terminal'] 302 # Skip 'Cargo' and 'Terminal' columns 303 for resource_name in row.index[2:]: 304 value = row[resource_name] 305 key = (cargo_type, terminal_id, resource_name) 306 # If value is a tuple-like string, convert it to tuple and store a random range value; otherwise, store as int 307 if isinstance(value, tuple): 308 # Convert to tuple and store a random value from range 309 truck_load_cache[key] = (value[0], value[1]) 310 else: 311 if resource_name in ['train arrival rate', 'truck arrival rate']: 312 truck_load_cache[key] = (value, value) 313 # Store integer or directly as it is 314 else: 315 truck_load_cache[key] = (int(value), int(value)) 316 317 return truck_load_cache
Create a cache dictionary from terminal data for quick access to resource values as tuples. This function initializes a random number generator with a given seed, iterates through the terminal data, and populates a cache dictionary with keys based on cargo type, terminal ID, and resource name. If the resource value is a range, it stores the range as a tuple; otherwise, it stores the value as a tuple of itself.
Arguments:
- terminal_data (pd.DataFrame): The DataFrame containing terminal data.
- run_id (str): The identifier for the current run, used for logging.
- seed (int): The seed value for the random number generator.
Returns:
dict: A dictionary where keys are tuples of (cargo_type, terminal_id, resource_name) and values are tuples of the corresponding resource values or ranges.
320def get_value_from_terminal_tuple(terminal_tuple_cache, cargo_type, terminal_id, resource_name): 321 """ 322 Retrieve a value from the terminal tuple cache based on cargo type, terminal ID, and resource name. 323 This function checks the cache for a specific key and returns a random integer from the range stored in the tuple. 324 Args: 325 terminal_tuple_cache (dict): The cache dictionary containing terminal data as tuples. 326 cargo_type (str): The type of cargo. 327 terminal_id (str): The ID of the terminal. 328 resource_name (str): The name of the resource. 329 Returns: 330 int: A random integer from the range stored in the tuple for the specified cargo type, terminal ID, and resource name. 331 If the key does not exist in the cache, it returns None. 332 """ 333 # initialize_rng(seed) 334 key = (cargo_type, terminal_id, resource_name) 335 tups = terminal_tuple_cache.get(key) 336 return rng_random.randint(tups[0], tups[1])
Retrieve a value from the terminal tuple cache based on cargo type, terminal ID, and resource name. This function checks the cache for a specific key and returns a random integer from the range stored in the tuple.
Arguments:
- terminal_tuple_cache (dict): The cache dictionary containing terminal data as tuples.
- cargo_type (str): The type of cargo.
- terminal_id (str): The ID of the terminal.
- resource_name (str): The name of the resource.
Returns:
int: A random integer from the range stored in the tuple for the specified cargo type, terminal ID, and resource name. If the key does not exist in the cache, it returns None.
339def get_values_by_terminal_random_sample(terminal_data, cargo_type, terminal_id, resource_name_input, num_samples, seed): 340 """ 341 Get a list of random samples from the terminal data based on cargo type and terminal ID. 342 This function retrieves a specific resource value from the terminal data for a given cargo type and terminal ID, 343 and generates a list of random samples from that value. If the value is a range, it samples from that range; 344 otherwise, it returns a list with the value repeated for the specified number of samples. 345 Args: 346 terminal_data (pd.DataFrame): The DataFrame containing terminal data. 347 cargo_type (str): The type of cargo. 348 terminal_id (str): The ID of the terminal. 349 resource_name_input (str): The name of the resource to sample from. 350 num_samples (int): The number of random samples to generate. 351 seed (int): The seed value for the random number generator. 352 Returns: 353 list: A list of random samples from the specified resource value. 354 If the resource value is a range, it returns random integers from that range; 355 otherwise, it returns a list with the value repeated for the specified number of samples. 356 """ 357 random.seed(seed) 358 359 row = terminal_data[(terminal_data['Terminal'] == terminal_id) & 360 (terminal_data['Cargo'] == cargo_type)].iloc[0] 361 362 value = row[resource_name_input] 363 364 try: 365 return [random.randint(int(value[0]), int(value[1])) for _ in range(num_samples)] 366 except: 367 return [value] * num_samples
Get a list of random samples from the terminal data based on cargo type and terminal ID. This function retrieves a specific resource value from the terminal data for a given cargo type and terminal ID, and generates a list of random samples from that value. If the value is a range, it samples from that range; otherwise, it returns a list with the value repeated for the specified number of samples.
Arguments:
- terminal_data (pd.DataFrame): The DataFrame containing terminal data.
- cargo_type (str): The type of cargo.
- terminal_id (str): The ID of the terminal.
- resource_name_input (str): The name of the resource to sample from.
- num_samples (int): The number of random samples to generate.
- seed (int): The seed value for the random number generator.
Returns:
list: A list of random samples from the specified resource value. If the resource value is a range, it returns random integers from that range; otherwise, it returns a list with the value repeated for the specified number of samples.
374def is_daytime(time, start=6, end=18): 375 """ 376 Check if the given time is within the daytime range. 377 Args: 378 time (int or float): The time to check, in hours (0-23). 379 start (int): The start of the daytime range (default is 6). 380 end (int): The end of the daytime range (default is 18). 381 Returns: 382 bool: True if the time is within the daytime range, False otherwise. 383 """ 384 return start <= time % 24 <= end
Check if the given time is within the daytime range.
Arguments:
- time (int or float): The time to check, in hours (0-23).
- start (int): The start of the daytime range (default is 6).
- end (int): The end of the daytime range (default is 18).
Returns:
bool: True if the time is within the daytime range, False otherwise.
391def normal_random_with_sd(mu, sigma, seed, scale_factor=1): 392 """ 393 Generate a random value from a truncated normal distribution with specified mean and standard deviation. 394 The distribution is truncated to ensure the value is within a specified range based on the mean and standard deviation. 395 Args: 396 mu (float): The mean of the normal distribution. 397 sigma (float): The standard deviation of the normal distribution. 398 seed (int): Random seed for reproducibility. 399 scale_factor (float): Factor to scale the standard deviation for truncation (default is 1). 400 Returns: 401 float: A random value from the truncated normal distribution, ensuring it is within the range [a, b]. 402 """ 403 np.random.seed(seed) 404 a = max(0, mu - scale_factor * sigma) 405 b = max(0, mu + scale_factor * sigma) 406 if a <= 0: 407 a = 0 408 lower, upper = (a - mu) / sigma, (b - mu) / sigma 409 distribution = truncnorm(lower, upper, loc=mu, scale=sigma) 410 val = distribution.rvs() 411 if val < a: 412 return a 413 elif val > b: 414 return b 415 return distribution.rvs()
Generate a random value from a truncated normal distribution with specified mean and standard deviation. The distribution is truncated to ensure the value is within a specified range based on the mean and standard deviation.
Arguments:
- mu (float): The mean of the normal distribution.
- sigma (float): The standard deviation of the normal distribution.
- seed (int): Random seed for reproducibility.
- scale_factor (float): Factor to scale the standard deviation for truncation (default is 1).
Returns:
float: A random value from the truncated normal distribution, ensuring it is within the range [a, b].
418def normal_random_with_limit(a, b, seed): 419 """ 420 Generate a random value from a truncated normal distribution between a and b. 421 Args: 422 a (float): Lower limit of the range. 423 b (float): Upper limit of the range. 424 seed (int): Random seed for reproducibility. 425 Returns: 426 float: A random value from the truncated normal distribution. 427 """ 428 np.random.seed(seed) 429 mean = (a + b) / 2 430 std_dev = (b - a) / 6 431 lower_bound = (a - mean) / std_dev 432 upper_bound = (b - mean) / std_dev 433 distribution = truncnorm(lower_bound, upper_bound, loc=mean, scale=std_dev) 434 return distribution.rvs()
Generate a random value from a truncated normal distribution between a and b.
Arguments:
- a (float): Lower limit of the range.
- b (float): Upper limit of the range.
- seed (int): Random seed for reproducibility.
Returns:
float: A random value from the truncated normal distribution.