naohiro701 commited on
Commit
90f4000
·
verified ·
1 Parent(s): 450b5a3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -115
app.py CHANGED
@@ -2,8 +2,10 @@ import streamlit as st
2
  import requests
3
  import pandas as pd
4
  import pulp
 
5
  import plotly.express as px
6
  import numpy as np
 
7
 
8
  # Renewable energy data fetch function
9
  def get_renewable_energy_data(city_code):
@@ -26,114 +28,137 @@ def get_renewable_energy_data(city_code):
26
 
27
  return result_df, None
28
 
29
- # Generate scenarios for robust optimization
30
- def generate_scenarios(data, num_scenarios, demand_variation, supply_variation):
31
- scenarios = []
32
- for i in range(num_scenarios):
33
- scenario = data.copy()
34
- # Vary demand
35
- demand_factor = 1 + np.random.uniform(-demand_variation, demand_variation)
36
- scenario['demand hourly capacity factor'] *= demand_factor
37
- # Vary supply capacity factors
38
- for tech in ['solar', 'onshore_wind', 'offshore_wind', 'river']:
39
- supply_factor = 1 + np.random.uniform(-supply_variation, supply_variation)
40
- scenario[f'{tech} hourly capacity factor'] *= supply_factor
41
- scenario[f'{tech} hourly capacity factor'] = scenario[f'{tech} hourly capacity factor'].clip(upper=1.0)
42
- scenarios.append(scenario)
43
- return scenarios
44
-
45
- # Optimize energy system for multiple scenarios
46
- def optimize_energy_system_robust(scenarios, technologies, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand, solar_range, wind_range, river_range, offshore_wind_range):
47
  regions = ['region1']
 
 
 
 
 
 
 
 
48
  renewable_capacity_cost = {'solar': solar_cost, 'onshore_wind': onshore_wind_cost, 'offshore_wind': offshore_wind_cost, 'river': river_cost}
49
  battery_cost_per_mwh = battery_cost
50
  battery_efficiency = 0.9
51
 
52
- # Create the model
53
- model = pulp.LpProblem("RobustEnergySystemOptimization", pulp.LpMinimize)
54
 
55
- # Variables
56
  renewable_capacity = pulp.LpVariable.dicts("renewable_capacity",
57
  [(r, g) for r in regions for g in technologies],
58
  lowBound=0, cat='Continuous')
 
 
 
59
  battery_capacity = pulp.LpVariable("battery_capacity", lowBound=0, cat='Continuous')
 
 
 
60
 
61
- # Objective: minimize total cost
62
- model += pulp.lpSum([renewable_capacity[('region1', g)] * renewable_capacity_cost[g] for g in technologies]) + \
63
- battery_capacity * battery_cost_per_mwh, "TotalCost"
64
-
65
- # Constraints for each scenario
66
- for idx, data in enumerate(scenarios):
67
- time_steps = range(len(data['Time']))
68
- capacity_factor = {
69
- 'solar': data['solar hourly capacity factor'],
70
- 'onshore_wind': data['onshore_wind hourly capacity factor'],
71
- 'offshore_wind': data['offshore_wind hourly capacity factor'],
72
- 'river': data['river hourly capacity factor']
73
- }
74
- demand_cf = data['demand hourly capacity factor']
75
- demand = demand_cf * yearly_demand * 1e6 / demand_cf.sum() # MW
76
-
77
- # Scenario-specific variables
78
- renewable_generation = pulp.LpVariable.dicts(f"renewable_generation_s{idx}",
79
- [(t, g) for t in time_steps for g in technologies],
80
- lowBound=0, cat='Continuous')
81
- battery_charge = pulp.LpVariable.dicts(f"battery_charge_s{idx}",
82
- time_steps, lowBound=0, cat='Continuous')
83
- battery_discharge = pulp.LpVariable.dicts(f"battery_discharge_s{idx}",
84
- time_steps, lowBound=0, cat='Continuous')
85
- battery_storage = pulp.LpVariable.dicts(f"battery_storage_s{idx}",
86
- time_steps, lowBound=0, cat='Continuous')
87
-
88
- # Renewable generation constraints
89
- for t in time_steps:
90
- for g in technologies:
91
- model += renewable_generation[(t, g)] <= renewable_capacity[('region1', g)] * capacity_factor[g].iloc[t], f"GenCap_s{idx}_{t}_{g}"
92
 
93
- # Energy balance constraints
94
- for t in time_steps:
95
- total_generation = pulp.lpSum([renewable_generation[(t, g)] for g in technologies])
96
- model += total_generation + battery_discharge[t] == demand.iloc[t] + battery_charge[t], f"EnergyBalance_s{idx}_{t}"
97
 
98
- # Battery storage constraints
 
99
  for t in time_steps:
 
 
100
  if t == 0:
101
- model += battery_storage[t] == battery_capacity * 0.5 + battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"StorageBalance_s{idx}_{t}"
102
  else:
103
- model += battery_storage[t] == battery_storage[t - 1] + battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"StorageBalance_s{idx}_{t}"
104
- model += battery_storage[t] <= battery_capacity, f"StorageCapacity_s{idx}_{t}"
105
- model += battery_storage[t] >= 0, f"StorageNonNegative_s{idx}_{t}"
106
 
107
- # Renewable capacity constraints (within specified ranges)
108
- model += renewable_capacity[('region1', 'solar')] >= solar_range[0], "SolarCapMin"
109
- model += renewable_capacity[('region1', 'solar')] <= solar_range[1], "SolarCapMax"
 
 
 
 
 
 
110
 
111
- model += renewable_capacity[('region1', 'onshore_wind')] >= wind_range[0], "WindCapMin"
112
- model += renewable_capacity[('region1', 'onshore_wind')] <= wind_range[1], "WindCapMax"
113
-
114
- model += renewable_capacity[('region1', 'offshore_wind')] >= offshore_wind_range[0], "OffshoreWindCapMin"
115
- model += renewable_capacity[('region1', 'offshore_wind')] <= offshore_wind_range[1], "OffshoreWindCapMax"
116
-
117
- model += renewable_capacity[('region1', 'river')] >= river_range[0], "RiverCapMin"
118
- model += renewable_capacity[('region1', 'river')] <= river_range[1], "RiverCapMax"
119
-
120
- # Solve the model
121
  model.solve()
 
122
 
123
- if pulp.LpStatus[model.status] == 'Optimal':
124
- solution = {
125
- 'renewable_capacity': {g: renewable_capacity[('region1', g)].varValue for g in technologies},
126
- 'battery_capacity': battery_capacity.varValue,
127
- 'total_cost': pulp.value(model.objective)
128
- }
129
- return solution
130
- else:
131
- st.error("Optimization did not find an optimal solution.")
132
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  # Streamlit UI setup
135
- st.set_page_config(page_title='Robust Energy System Optimization', layout='wide')
136
- st.title('Robust Energy System Optimization Analysis')
137
 
138
  # Sidebar Inputs
139
  with st.sidebar:
@@ -149,36 +174,143 @@ with st.sidebar:
149
  wind_range = st.slider("Onshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
150
  offshore_wind_range = st.slider("Offshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
151
  river_range = st.slider("River Capacity Range (MW)", 0, 10000, (0, 10000))
152
- num_scenarios = st.number_input("Number of Scenarios", min_value=1, max_value=10, value=3)
153
- demand_variation = st.number_input("Demand Variation (%)", min_value=0.0, max_value=100.0, value=10.0) / 100
154
- supply_variation = st.number_input("Supply Variation (%)", min_value=0.0, max_value=100.0, value=10.0) / 100
 
 
 
 
 
155
 
156
- if st.button("Run Robust Optimization"):
157
- # Fetch data
158
- data, error = get_renewable_energy_data(city_code)
159
- if error:
160
- st.error(error)
161
- st.stop()
 
 
 
 
 
 
 
 
162
 
163
- # Generate scenarios
164
- scenarios = generate_scenarios(data, num_scenarios, demand_variation, supply_variation)
 
 
165
 
166
- # Define technologies
167
- technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
- # Run robust optimization
170
- solution = optimize_energy_system_robust(scenarios, technologies, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand, solar_range, wind_range, river_range, offshore_wind_range)
171
-
172
- if solution:
173
- st.header("Optimization Results")
174
- st.write(f"Total Cost: {solution['total_cost']}")
175
- st.write(f"Battery Capacity (MWh): {solution['battery_capacity']}")
176
- capacity_data = pd.DataFrame({
177
- 'Technology': list(solution['renewable_capacity'].keys()),
178
- 'Capacity (MW)': list(solution['renewable_capacity'].values())
179
- })
180
- st.write(capacity_data)
181
-
182
- # Visualization
183
- fig = px.bar(capacity_data, x='Technology', y='Capacity (MW)', title='Optimal Renewable Capacities')
184
- st.plotly_chart(fig, use_container_width=True)
 
2
  import requests
3
  import pandas as pd
4
  import pulp
5
+ import plotly.graph_objs as go
6
  import plotly.express as px
7
  import numpy as np
8
+ import matplotlib.pyplot as plt
9
 
10
  # Renewable energy data fetch function
11
  def get_renewable_energy_data(city_code):
 
28
 
29
  return result_df, None
30
 
31
+ # Optimize energy system and use MGA
32
+ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand, solar_range, wind_range, river_range, offshore_wind_range, thresholds, selected_tech):
33
+ data, error = get_renewable_energy_data(city_code)
34
+ if error:
35
+ st.error(error)
36
+ return None, None, None, None, None, None, None
37
+
38
+ for col in data.columns[1:]:
39
+ data[col] = pd.to_numeric(data[col], errors='coerce')
40
+ data = data.fillna(0)
41
+
42
+ time_steps = range(len(data['Time']))
43
+ solar_cf = data['solar hourly capacity factor']
44
+ onshore_wind_cf = data['onshore_wind hourly capacity factor']
45
+ offshore_wind_cf = data['offshore_wind hourly capacity factor']
46
+ river_cf = data['river hourly capacity factor']
47
+ demand_cf = data['demand hourly capacity factor']
48
+
49
  regions = ['region1']
50
+ technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']
51
+ capacity_factor = {
52
+ 'solar': solar_cf,
53
+ 'onshore_wind': onshore_wind_cf,
54
+ 'offshore_wind': offshore_wind_cf,
55
+ 'river': river_cf
56
+ }
57
+
58
  renewable_capacity_cost = {'solar': solar_cost, 'onshore_wind': onshore_wind_cost, 'offshore_wind': offshore_wind_cost, 'river': river_cost}
59
  battery_cost_per_mwh = battery_cost
60
  battery_efficiency = 0.9
61
 
62
+ demand = demand_cf * yearly_demand / 100 * 1000 * 1000
 
63
 
 
64
  renewable_capacity = pulp.LpVariable.dicts("renewable_capacity",
65
  [(r, g) for r in regions for g in technologies],
66
  lowBound=0, cat='Continuous')
67
+ curtailment = pulp.LpVariable.dicts("curtailment",
68
+ [(r, t) for r in regions for t in time_steps],
69
+ lowBound=0, cat='Continuous')
70
  battery_capacity = pulp.LpVariable("battery_capacity", lowBound=0, cat='Continuous')
71
+ battery_charge = pulp.LpVariable.dicts("battery_charge", time_steps, lowBound=0, cat='Continuous')
72
+ battery_discharge = pulp.LpVariable.dicts("battery_discharge", time_steps, lowBound=0, cat='Continuous')
73
+ SOC = pulp.LpVariable.dicts("SOC", time_steps, lowBound=0, cat='Continuous')
74
 
75
+ model = pulp.LpProblem("EnergySystemOptimizationWithBattery", pulp.LpMinimize)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ # Objective: minimize total cost (renewable capacities and battery)
78
+ model += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
79
+ for r in regions for g in technologies]) + \
80
+ battery_capacity * battery_cost_per_mwh, "TotalCost"
81
 
82
+ # Constraints: meet demand, manage battery SOC
83
+ for r in regions:
84
  for t in time_steps:
85
+ model += pulp.lpSum([renewable_capacity[(r, g)] * capacity_factor[g][t]
86
+ for g in technologies]) + battery_discharge[t] == demand[t] + battery_charge[t] + curtailment[(r, t)], f"DemandConstraint_{r}_{t}"
87
  if t == 0:
88
+ model += SOC[t] == battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"
89
  else:
90
+ model += SOC[t] == SOC[t - 1] + battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"
91
+ model += SOC[t] <= battery_capacity, f"SOCUpperBound_{t}"
 
92
 
93
+ # Capacity range constraints
94
+ model += renewable_capacity[('region1', 'solar')] >= solar_range[0]
95
+ model += renewable_capacity[('region1', 'solar')] <= solar_range[1]
96
+ model += renewable_capacity[('region1', 'onshore_wind')] >= wind_range[0]
97
+ model += renewable_capacity[('region1', 'onshore_wind')] <= wind_range[1]
98
+ model += renewable_capacity[('region1', 'offshore_wind')] >= offshore_wind_range[0]
99
+ model += renewable_capacity[('region1', 'offshore_wind')] <= offshore_wind_range[1]
100
+ model += renewable_capacity[('region1', 'river')] >= river_range[0]
101
+ model += renewable_capacity[('region1', 'river')] <= river_range[1]
102
 
103
+ # Solve the initial model to find the optimal solution
 
 
 
 
 
 
 
 
 
104
  model.solve()
105
+ optimal_cost = pulp.value(model.objective)
106
 
107
+ # MGA: Generate alternative solutions for selected technologies only
108
+ mga_models = []
109
+ alternative_solutions = []
110
+ for threshold in thresholds:
111
+ relaxed_cost = optimal_cost * (1 + threshold)
112
+ for tech in selected_tech: # 選択された技術のみ実行
113
+ # Minimize capacity of each technology
114
+ alt_model_min = pulp.LpProblem(f"AlternativeModel_Min_{tech}_{threshold}", pulp.LpMinimize)
115
+ alt_model_min += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
116
+ for r in regions for g in technologies]) + battery_capacity * battery_cost_per_mwh <= relaxed_cost
117
+
118
+ # Copy original constraints with unique names
119
+ for name, constraint in model.constraints.items():
120
+ alt_model_min += constraint.copy(), f"{name}_min_{tech}_{threshold}"
121
+
122
+ # Minimize the capacity of the selected technology
123
+ alt_model_min += renewable_capacity[('region1', tech)], f"Minimize_{tech}_Capacity"
124
+ alt_model_min.solve()
125
+ if pulp.LpStatus[alt_model_min.status] == 'Optimal':
126
+ alternative_solutions.append({
127
+ 'threshold': threshold,
128
+ 'type': 'min',
129
+ 'technology': tech,
130
+ 'solution': {g: renewable_capacity[('region1', g)].varValue for g in technologies},
131
+ 'battery_capacity': battery_capacity.varValue,
132
+ 'total_cost': pulp.value(alt_model_min.objective)
133
+ })
134
+
135
+ # Maximize capacity of each technology
136
+ alt_model_max = pulp.LpProblem(f"AlternativeModel_Max_{tech}_{threshold}", pulp.LpMinimize)
137
+ alt_model_max += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
138
+ for r in regions for g in technologies]) + battery_capacity * battery_cost_per_mwh <= relaxed_cost
139
+
140
+ # Copy original constraints with unique names
141
+ for name, constraint in model.constraints.items():
142
+ alt_model_max += constraint.copy(), f"{name}_max_{tech}_{threshold}"
143
+
144
+ # Maximize the capacity of the selected technology
145
+ alt_model_max += -renewable_capacity[('region1', tech)], f"Maximize_{tech}_Capacity"
146
+ alt_model_max.solve()
147
+ if pulp.LpStatus[alt_model_max.status] == 'Optimal':
148
+ alternative_solutions.append({
149
+ 'threshold': threshold,
150
+ 'type': 'max',
151
+ 'technology': tech,
152
+ 'solution': {g: renewable_capacity[('region1', g)].varValue for g in technologies},
153
+ 'battery_capacity': battery_capacity.varValue,
154
+ 'total_cost': pulp.value(alt_model_max.objective)
155
+ })
156
+
157
+ return alternative_solutions
158
 
159
  # Streamlit UI setup
160
+ st.set_page_config(page_title='Renewable Energy System Optimization with MGA', layout='wide')
161
+ st.title('Modeling to Generate Alternatives (MGA) in Renewable Energy System Optimization')
162
 
163
  # Sidebar Inputs
164
  with st.sidebar:
 
174
  wind_range = st.slider("Onshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
175
  offshore_wind_range = st.slider("Offshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
176
  river_range = st.slider("River Capacity Range (MW)", 0, 10000, (0, 10000))
177
+ # 0.1の刻みで0から1までの値を生成し、小数点以下3桁に丸める
178
+ thresholds = st.multiselect(
179
+ "Select MGA Cost Deviation Thresholds (%)",
180
+ list(np.arange(0, 11, 0.5)),
181
+ default=[0, 5, 10]
182
+ )
183
+ # 技術の選択オプション
184
+ selected_technologies = st.multiselect("Select Technologies to Optimize", ['solar', 'onshore_wind', 'offshore_wind', 'river'], default=['solar', 'onshore_wind', 'offshore_wind', 'river'])
185
 
186
+ if st.button("Run MGA Optimization"):
187
+ # 実行して alternative_solutions を取得
188
+ alternative_solutions = optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand, solar_range, wind_range, river_range, offshore_wind_range, [t / 100 for t in thresholds], selected_technologies)
189
+
190
+ if alternative_solutions:
191
+ # コスト積み上げ用データの収集
192
+ cost_data = []
193
+ for sol in alternative_solutions:
194
+ cost_data.append({
195
+ 'threshold': sol['threshold'] * 100,
196
+ 'type': sol['type'],
197
+ 'technology': sol['technology'],
198
+ 'total_cost': sol['total_cost']
199
+ })
200
 
201
+ # コスト積み上げグラフのプロット
202
+ cost_df = pd.DataFrame(cost_data)
203
+ fig_cost = px.bar(cost_df, x='threshold', y='total_cost', color='technology', title="Cost Breakdown by Technology and Threshold")
204
+ fig_cost.update_layout(xaxis_title='Threshold (%)', yaxis_title='Total Cost (¥)')
205
 
206
+ # Streamlitでコスト積み上げグラフを表示
207
+ st.plotly_chart(fig_cost, use_container_width=True)
208
+
209
+ # 各技術ごとに異なる色を指定
210
+ colors = {
211
+ 'solar': 'gold',
212
+ 'onshore_wind': 'skyblue',
213
+ 'offshore_wind': 'lightgreen',
214
+ 'river': 'salmon'
215
+ }
216
+
217
+ # 各技術の容量範囲を個別のグラフで表示
218
+ epsilon_values = sorted(list(set(sol['threshold'] * 100 for sol in alternative_solutions)))
219
+
220
+ for tech in selected_technologies:
221
+ storage_min = []
222
+ storage_max = []
223
+ for epsilon in epsilon_values:
224
+ capacities = [sol['solution'][tech] for sol in alternative_solutions if sol['technology'] == tech and sol['threshold'] * 100 == epsilon]
225
+ storage_min.append(min(capacities))
226
+ storage_max.append(max(capacities))
227
+
228
+ # 各技術ごとにグラフ作成
229
+ fig, ax = plt.subplots()
230
+ ax.fill_between(epsilon_values, storage_min, storage_max, color=colors[tech], alpha=0.3, label=f"{tech} range")
231
+ ax.plot(epsilon_values, storage_min, marker='o', color=colors[tech], linestyle='-', linewidth=1.5, label=f"{tech} Min")
232
+ ax.plot(epsilon_values, storage_max, marker='o', color=colors[tech], linestyle='-', linewidth=1.5, label=f"{tech} Max")
233
+
234
+ # ラベルとタイトル
235
+ ax.set_xlabel(r'$\epsilon$ [%]')
236
+ ax.set_ylabel(f'{tech.capitalize()} Capacity [GW]')
237
+ ax.set_title(f'Capacity Range for {tech.capitalize()}')
238
+ ax.legend()
239
+ ax.grid(True, linestyle='--', alpha=0.7)
240
+
241
+ # Streamlitに各技術のプロットを表示
242
+ st.pyplot(fig)
243
+
244
+
245
+ st.markdown("""
246
+ This application uses **Modeling to Generate Alternatives (MGA)** to explore near-optimal solutions in a renewable energy system model. MGA helps to identify alternative configurations that are close to the optimal solution but vary in their specific technological composition, providing flexibility for policy makers and stakeholders who might prioritize factors beyond cost minimization, such as social acceptance or regional preferences.
247
+ """)
248
+
249
+ st.write("## Objective Function and Cost Minimization")
250
+ st.markdown("""
251
+ In our renewable energy model, the **objective function** is to minimize the total annual cost of the system, which includes the costs of installing renewable generation capacities (such as solar, wind, and hydroelectric) and storage (batteries). The objective function is defined as:
252
+ """)
253
+ st.latex(r"""
254
+ \text{Minimize } \quad \sum_{r, g} \text{Cost}_{g} \times \text{Capacity}_{r, g} + \text{Battery Cost} \times \text{Battery Capacity}
255
+ """)
256
+ st.markdown("""
257
+ where:
258
+ - \( r \) represents the region (in this case, a single region),
259
+ - \( g \) represents the generation technology (solar, onshore wind, offshore wind, river),
260
+ - \( \text{Cost}_{g} \) is the per-MW cost of technology \( g \),
261
+ - \( \text{Capacity}_{r, g} \) is the installed capacity of technology \( g \) in region \( r \),
262
+ - \( \text{Battery Cost} \) represents the cost per MWh of battery storage,
263
+ - \( \text{Battery Capacity} \) is the total installed battery capacity.
264
+ """)
265
+
266
+ st.markdown("""
267
+ ## What is MGA and Why is it Important?
268
+ Typically, optimization models produce a **single optimal solution** that minimizes the cost under a given set of constraints. However, in many real-world applications, there are **multiple near-optimal solutions** that achieve similar costs but vary in other characteristics. This diversity is valuable because:
269
+ - **Flexibility**: Different solutions might be preferable depending on policy objectives, geographic constraints, or social preferences.
270
+ - **Robustness**: Exploring near-optimal solutions reveals which elements (e.g., specific technologies or infrastructure investments) are consistently essential, regardless of slight variations in cost.
271
+ MGA addresses this need by generating **alternative solutions** that are close to the optimal cost but differ in technological composition.
272
+ """)
273
+
274
+ st.write("## How MGA Works: Adding a Cost Constraint")
275
+ st.markdown("""
276
+ To generate alternatives, MGA introduces a **cost tolerance** parameter \\( \\epsilon \\), which represents the acceptable increase in total cost relative to the optimal solution. The cost constraint for alternative solutions is expressed as:
277
+ """)
278
+ st.latex(r"""
279
+ \text{Total Cost} \leq (1 + \epsilon) \times \text{Optimal Cost}
280
+ """)
281
+ st.markdown("""
282
+ where:
283
+ - \\( \\epsilon \\) is the cost deviation percentage (e.g., if \\( \\epsilon = 0.05 \\), then the solution can be up to 5% more expensive than the optimal cost),
284
+ - \\( \\text{Optimal Cost} \\) is the minimum cost obtained from the initial optimization.
285
+ This constraint allows for flexibility in cost, enabling the exploration of solutions that are **near-optimal** but differ in terms of installed capacities for each technology.
286
+ """)
287
+
288
+ st.markdown("""
289
+ ### MGA Process in This Application
290
+ 1. **Initial Optimization**: First, we solve for the optimal solution to obtain the minimal total cost, referred to as \\( \\text{Optimal Cost} \\).
291
+ 2. **Setting the Cost Threshold**: We introduce a range of \\( \\epsilon \\) values (0%, 5%, 10%, etc.) to explore how alternative solutions differ as we allow for higher costs.
292
+ 3. **Minimizing and Maximizing Capacities**: For each selected technology (e.g., solar, wind, hydro), we attempt to:
293
+ - **Minimize the installed capacity** within the allowed cost threshold, identifying configurations with the lowest feasible capacity for that technology.
294
+ - **Maximize the installed capacity** under the same conditions, exploring configurations with higher reliance on that technology.
295
+ These steps generate a set of **alternative solutions** that are close in cost but vary significantly in their reliance on each technology, revealing **flexibility** and **trade-offs** in the renewable energy system configuration.
296
+ """)
297
+
298
+ st.write("## Interpreting the Cost Threshold (\\( \\epsilon \\))")
299
+ st.markdown("""
300
+ The cost threshold parameter \\( \\epsilon \\) is crucial in MGA, as it determines the range within which we consider solutions to be "near-optimal." For example:
301
+ - **\\( \\epsilon = 0 \\%**: Only the exact optimal solution is considered.
302
+ - **\\( \\epsilon = 5 \\%**: Solutions within 5% of the optimal cost are considered acceptable, allowing for slightly more flexibility in technology choice.
303
+ - **\\( \\epsilon = 10 \\%**: Solutions within 10% of the optimal cost are allowed, providing even greater flexibility.
304
+ By exploring a range of \\( \\epsilon \\) values, we can see how the system configuration changes as we relax the cost constraint, offering a broader view of feasible solutions.
305
+ """)
306
 
307
+ st.markdown("""
308
+ ## Visualization of Results
309
+ - **Cost Breakdown**: The total cost of each solution, broken down by technology, helps us see the contribution of each technology to the total cost.
310
+ - **Capacity Ranges**: For each technology, we plot the minimum and maximum capacities across different \\( \\epsilon \\) values, showing the flexibility in system design as cost thresholds change.
311
+ This visualization provides insights into:
312
+ - Which technologies are essential (appear consistently in solutions across all \\( \\epsilon \\) values),
313
+ - Which technologies offer flexibility (capacities vary widely as \\( \\epsilon \\) increases),
314
+ - The cost impact of relying more or less on specific technologies.
315
+ Through MGA, we can make more **informed decisions** about the renewable energy mix and identify robust, flexible strategies that align with broader goals beyond cost minimization.
316
+ """)