1import os
2import pandas as pd
3
4# set directory
5directory = os.getcwd()
6
7# set simulation IDs and run characteristics
8WARMUP_ITERS = 1000 # Number of ships to ignore while calculating means, recommended: first 1,000
9NUM_MONTHS = 6 # Number of months to run the simulation
10SIMULATION_TIME = int(NUM_MONTHS*30*24) # Total time to run the simulation for, in hours
11
12LOG_NUM = 10
13NUM_RUNS = 4 # How many unique replications of different seeds to conduct for a given run
14NUM_CORES = 4 # How many CPU cores to run individual seeds on simultaneously (set depending on local hardware capabilities)
15START_SEED = 100 # Initial random seed for the simulation
16
17# Cleans output result folders
18DELETE_EVERYTHING = False # Cleans all existing result folders, including collated results (SAVE ALL DESIRED DATA PRIOR TO RUNNING AS TRUE)
19DELETE_RESULTS_FOLDER = True # Cleans the results folder only
20
21SCENARIO_NAME = "Base" # Set to "Base" for base conditions, or set to "BottleneckDetection" for bottleneck analysis, or "BreakpointAnalysis" for breakpoint analysis
22
23# Set "what-if" scenarios. Set all as "False" for base conditions
24MODEL_HURRICANE = False
25SEVERE_HURRICANE = False # If True, models a more severe hurricane with longer port closure and more resource damage
26TROPICAL_STORM = False # If True, models a tropical storm with shorter port closure and less resource damage
27MODEL_FOG = True
28EXPANDED_CHANNEL = False
29
30if MODEL_HURRICANE == False:
31 if SEVERE_HURRICANE == True or TROPICAL_STORM == True:
32 raise ValueError("If MODEL_HURRICANE is False, both SEVERE_HURRICANE and TROPICAL_STORM must be False; update the config file.")
33if MODEL_HURRICANE == True:
34 if SEVERE_HURRICANE == False and TROPICAL_STORM == False:
35 raise ValueError("If MODEL_HURRICANE is True, either SEVERE_HURRICANE or TROPICAL_STORM must be True; update the config file.")
36
37# Set truck and pipeline overrides to ensure terminal storage capacities remain
38CTR_TRUCK_OVERRIDE = False # Set False if running bottleneck analysis; True for stable yard queue
39LIQ_PIPELINE_OVERRIDE = False # Set False if running bottleneck analysis; True for stable yard queue
40
41########################################################################################
42# The following variables have been calibrated for a simulation of the a random port #
43# Change with ACTUAL port data. #
44########################################################################################
45
46if MODEL_FOG == True:
47 INBOUND_CLOSED = [(1500, 1520), (1664, 1695), (1932, 1972)]
48 OUTBOUND_CLOSED = [(1500, 1515), (1673, 1695), (2002, 2023)]
49 FOG_CLOSURES = [] # BOTH INBOUND AND OUTBOUND CLOSURES
50else:
51 INBOUND_CLOSED = []
52 OUTBOUND_CLOSED = []
53 FOG_CLOSURES = []
54
55if MODEL_HURRICANE == True:
56 HURRICANE_START = int(30*1.5*24) # in hours from start of simulation 1.5 months
57 if SEVERE_HURRICANE == False and TROPICAL_STORM == False:
58 raise ValueError("If MODEL_HURRICANE is True, either SEVERE_HURRICANE or TROPICAL_STORM must be True; update the config file.")
59else:
60 HURRICANE_START = None
61
62# set as 1.0 for most cases, values change directly if running breakpoint analysis
63ARRIVAL_INCREASE_FACTOR_CTR = 1.0
64ARRIVAL_INCREASE_FACTOR_LIQ = 1.0
65ARRIVAL_INCREASE_FACTOR_DRYBULK = 1.0
66
67ARRIVAL_INCREASE_FACTOR = ARRIVAL_INCREASE_FACTOR_CTR
68
69if ARRIVAL_INCREASE_FACTOR != 1.0:
70 print(f"\n\nWARNING!: Arrival increase factor set to {ARRIVAL_INCREASE_FACTOR} for all cargo types.")
71
72ARRIVAL_INCREASE_FACTOR = round(ARRIVAL_INCREASE_FACTOR, 2)
73
74# percent of total vessel tonnage accounted for as vessel weight
75NON_CARGO_DEAD_WEIGHT_PERCENT_CTR = 0.2
76NON_CARGO_DEAD_WEIGHT_PERCENT_DK = 0.1
77NON_CARGO_DEAD_WEIGHT_PERCENT_LIQ = 0.1
78
79# anchorage waiting times calibrated to each cargo type
80# These are unexplained wait times (additional waiting times account for factors not modelled
81# in simulation explicitely. First run with 0 values then caliberate based on observed
82# data and simulation outputs.
83ANCHORAGE_WAITING_CONTAINER = 0
84ANCHORAGE_WAITING_LIQUID = 0
85ANCHORAGE_WAITING_DRYBULK = 0
86
87# terminal efficiency times, used to extend the vessel time in port for each cargo type
88# These are efficiency factors for each terminal. Should be between 0 and 1
89# A Value of one means terminal is fully efficient and no delays.
90# First run with 0 values then caliberate based on observed
91# data and simulation outputs.
92CTR_TERMINAL_EFFICIENCY = 1.0
93LIQ_TERMINAL_EFFICIENCY = 1.0
94DRYBULK_TERMINAL_EFFICIENCY = 1.0
95
96# removes combined beam and draft restrictions if set to "True"
97CHANNEL_SAFETWOWAY = False
98
99# set number of pilots and tugs
100# Dummy values, set as actual port values
101NUM_PILOTS_DAY = (50, 60)
102NUM_PILOTS_NIGHT = (20, 30)
103NUM_TUGBOATS = (50, 100)
104
105# set channel entrance and navigation times
106TIME_COMMON_CHANNEL = (2, 3) # in hours
107TIME_FOR_TUG_STEER = (0.2, 0.4) # in hours
108TIME_FOR_UTURN = (0.2, 0.4) # in hours
109
110# set min and max range for arriving truck wait times, in hours
111TRUCK_WAITING_TIME = (1/60, 5/60)
112
113# interarrival times derived from AIS data.
114# Look for Bathgate et al. (2026) for procedure to obtain these values.
115# Currently values set as dummy values. (All values in hours)
116mean_interarrival_time_container = 10 * 1/ARRIVAL_INCREASE_FACTOR_CTR
117mean_interarrival_time_gencargo = 5 * 1/ARRIVAL_INCREASE_FACTOR_DRYBULK
118mean_interarrival_time_tanker = 1 * 1/ARRIVAL_INCREASE_FACTOR_LIQ
119mean_interarrival_time_total = mean_interarrival_time_container + mean_interarrival_time_gencargo + mean_interarrival_time_tanker
120base_arrival_rate = 1/10 + 1/5 + 1/1
121
122# set truncation values for
123max_interaarival_ctr = 100000000000000000
124max_interaarival_liq = 100000000000000000
125max_interaarival_drybulk = 100000000000000000
126
127min_interarrival_liquid = 0.0001
128
129# Set terminals with no truck or rail connections.
130# Note: These overrides terminal data; Example shown below:
131# Example: TERMINALS_WITH_NO_TRUCKS = {"Liquid": [1]}
132TERMINALS_WITH_NO_TRUCKS = {"Liquid": [], "DryBulk": [], "Liquid": []}
133TERMINALS_WITH_NO_TRAINS = {"Container": [], "Liquid": [], "DryBulk": []}
134
135# Rate for landside pipelines connection (these act as sorce or sink)
136# Note this is different from pipelines that connects to vessels.
137PIPELINE_RATE = 100 # cbm/timestep
138
139# Model the channel
140NUM_CHANNEL_SECTIONS = 10
141
142# Model portion after which wider ships are not allowed.
143# Helps in modelling channels that become narrower.
144# certain channel segments might only accessible to smaller vessels, list the start terminal for these conditions here
145# Set as large number if not applicable
146NO_LARGE_SHIP_BEYOND_THIS_TERMINAL_CTR = 2
147NO_LARGE_SHIP_BEYOND_THIS_TERMINAL_LIQ = 10
148NO_LARGE_SHIP_BEYOND_THIS_TERMINAL_DRYBULK = 10
149
150# Set what beam determines when wider ships are not allowed
151MAX_BEAM_SMALL_SHIP = 106
152
153# common liquid bulk cargo density conversion factors for more accurate cargo payload estimation
154LIQUID_CONVERSION_FACTORS = [
155 1.0, # Water (1 ton = 1 CBM)
156 1.18, # Crude oil (1 ton = 1.18 CBM)
157 1.16, # Diesel (1 ton = 1.16 CBM)
158 1.38, # Gasoline (1 ton = 1.38 CBM)
159 0.92, # Liquid ammonia (1 ton = 0.92 CBM)
160 0.78, # Molasses (1 ton = 0.78 CBM)
161 0.87, # Vegetable oil (1 ton = 0.87 CBM)
162 1.11, # Ethanol (1 ton = 1.11 CBM)
163 1.03, # Jet fuel (1 ton = 1.03 CBM)
164 1.03, # Kerosene (1 ton = 1.03 CBM)
165 1.22, # LPG (1 ton = 1.22 CBM)
166 1.25 # LNG (1 ton = 1.25 CBM)
167]
168
169CONTAINER_CONVERSION_FACTORS = [
170 1/24, # 20' container can carry 24 tons
171 1/33 # 40' container can carry 33 tons
172]
173
174# import terminal data from CSV in "inputs" folder
175df = pd.read_csv('./inputs/terminal_data.csv')
176
177# set daylight restriction hours
178# daylight hours are 7 AM till 7 PM (19:00)
179# hours for daylight restriction due to expanded channel are 4 AM till 9 PM (21:00)
180if EXPANDED_CHANNEL == True:
181 START_DAYLIGHT_RESTRICTION_HR = 4
182 STOP_DAYLIGHT_RESTRICTION_HR = 21
183else:
184 START_DAYLIGHT_RESTRICTION_HR = 7
185 STOP_DAYLIGHT_RESTRICTION_HR = 19
186
187# set channel dimensions (current or expanded)
188if EXPANDED_CHANNEL == True:
189 channel_dimensions = pd.read_csv(
190 './inputs/channel_dimensions_expanded.csv')
191else:
192 channel_dimensions = pd.read_csv('./inputs/channel_dimensions.csv')
193
194# read channel data from CSV in "inputs" folder
195dict_from_csv = channel_dimensions.to_dict(orient="index")
196CHANNEL_SECTION_DIMENSIONS = {list(value.values())[0]: tuple(
197 list(value.values())[1:]) for key, value in dict_from_csv.items()}
198LAST_SECTION_DICT = {cargo: dict(
199 zip(group['Terminal'], group['Segment'])) for cargo, group in df.groupby('Cargo')}
200BERTHS_CTR_TERMINAL = df[df['Cargo'] == 'Container']['Berths'].tolist()
201BERTHS_LIQ_TERMINAL = df[df['Cargo'] == 'Liquid']['Berths'].tolist()
202BERTH_DRYBULK_TERMINAL = df[df['Cargo'] == 'DryBulk']['Berths'].tolist()
203
204# conversion factors
205TIME_FACTOR = 60 # 1 hour = 60 minutes (Channel timestep is in minutes)
206
207# in hours, minimum spacing between entering inbound vessels (5/60 is 5 minutes)
208CHANNEL_ENTRY_SPACING = 5/60