naohiro701 commited on
Commit
18b7c9d
·
verified ·
1 Parent(s): 04ffbd0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -149
app.py CHANGED
@@ -2,21 +2,20 @@ import streamlit as st
2
  import requests
3
  import pandas as pd
4
  import pulp
 
5
  import plotly.express as px
6
- import plotly.graph_objects as go
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):
12
  url = f"https://energy-sustainability.jp/_ajax/renewable_energy/get/?code={city_code}"
13
  response = requests.get(url)
14
  if response.status_code != 200:
15
- return None, "Failed to retrieve data."
16
 
17
  data = response.json()
18
  if not data:
19
- return None, "No data found."
20
 
21
  base_times = data[next(iter(data))]['x']
22
  result_df = pd.DataFrame({"Time": base_times})
@@ -28,14 +27,12 @@ def get_renewable_energy_data(city_code):
28
 
29
  return result_df, None
30
 
31
- # Optimize energy system with MGA
32
- def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, nuclear_cost, coal_cost,
33
- battery_cost, yearly_demand, solar_range, wind_range, river_range, offshore_wind_range,
34
- nuclear_range, coal_range, thresholds, selected_tech):
35
  data, error = get_renewable_energy_data(city_code)
36
  if error:
37
  st.error(error)
38
- return None
39
 
40
  for col in data.columns[1:]:
41
  data[col] = pd.to_numeric(data[col], errors='coerce')
@@ -46,40 +43,18 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
46
  onshore_wind_cf = data['onshore_wind hourly capacity factor']
47
  offshore_wind_cf = data['offshore_wind hourly capacity factor']
48
  river_cf = data['river hourly capacity factor']
49
- nuclear_cf = 1.0 # Assume nuclear operates at full capacity
50
- coal_cf = 0.8 # Assume coal operates at 80% capacity
51
  demand_cf = data['demand hourly capacity factor']
52
 
53
  regions = ['region1']
54
- technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river', 'nuclear', 'coal']
55
  capacity_factor = {
56
  'solar': solar_cf,
57
  'onshore_wind': onshore_wind_cf,
58
  'offshore_wind': offshore_wind_cf,
59
- 'river': river_cf,
60
- 'nuclear': [nuclear_cf] * len(time_steps),
61
- 'coal': [coal_cf] * len(time_steps)
62
- }
63
-
64
- renewable_capacity_cost = {
65
- 'solar': solar_cost,
66
- 'onshore_wind': onshore_wind_cost,
67
- 'offshore_wind': offshore_wind_cost,
68
- 'river': river_cost,
69
- 'nuclear': nuclear_cost,
70
- 'coal': coal_cost
71
- }
72
-
73
- # CO2 emissions per MWh
74
- co2_emissions = {
75
- 'solar': 0,
76
- 'onshore_wind': 0,
77
- 'offshore_wind': 0,
78
- 'river': 0,
79
- 'nuclear': 0,
80
- 'coal': 820 # in kg CO2 per MWh
81
  }
82
 
 
83
  battery_cost_per_mwh = battery_cost
84
  battery_efficiency = 0.9
85
 
@@ -96,142 +71,265 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
96
  battery_discharge = pulp.LpVariable.dicts("battery_discharge", time_steps, lowBound=0, cat='Continuous')
97
  SOC = pulp.LpVariable.dicts("SOC", time_steps, lowBound=0, cat='Continuous')
98
 
99
- # Add a variable for CO2 emissions
100
- total_co2_emissions = pulp.LpVariable("total_co2_emissions", lowBound=0, cat='Continuous')
101
-
102
- model = pulp.LpProblem("EnergySystemOptimizationWithBatteryAndCO2", pulp.LpMinimize)
103
 
104
- # Objective: minimize total cost and CO2 emissions
105
  model += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
106
  for r in regions for g in technologies]) + \
107
- battery_capacity * battery_cost_per_mwh + \
108
- 0.01 * total_co2_emissions, "TotalCostAndCO2"
109
 
110
- # Constraints: meet demand, manage battery SOC, calculate CO2 emissions
111
  for r in regions:
112
  for t in time_steps:
113
  model += pulp.lpSum([renewable_capacity[(r, g)] * capacity_factor[g][t]
114
  for g in technologies]) + battery_discharge[t] == demand[t] + battery_charge[t] + curtailment[(r, t)], f"DemandConstraint_{r}_{t}"
 
115
  if t == 0:
116
  model += SOC[t] == battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"
117
  else:
118
  model += SOC[t] == SOC[t - 1] + battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"
 
119
  model += SOC[t] <= battery_capacity, f"SOCUpperBound_{t}"
120
 
121
- # Add CO2 emissions constraint
122
- model += total_co2_emissions == pulp.lpSum([renewable_capacity[(r, 'coal')] * coal_cf * co2_emissions['coal'] for r in regions]), "CO2Emissions"
123
-
124
- # Capacity range constraints
125
- model += renewable_capacity[('region1', 'solar')] >= solar_range[0]
126
- model += renewable_capacity[('region1', 'solar')] <= solar_range[1]
127
- model += renewable_capacity[('region1', 'onshore_wind')] >= wind_range[0]
128
- model += renewable_capacity[('region1', 'onshore_wind')] <= wind_range[1]
129
- model += renewable_capacity[('region1', 'offshore_wind')] >= offshore_wind_range[0]
130
- model += renewable_capacity[('region1', 'offshore_wind')] <= offshore_wind_range[1]
131
- model += renewable_capacity[('region1', 'river')] >= river_range[0]
132
- model += renewable_capacity[('region1', 'river')] <= river_range[1]
133
- model += renewable_capacity[('region1', 'nuclear')] >= nuclear_range[0]
134
- model += renewable_capacity[('region1', 'nuclear')] <= nuclear_range[1]
135
- model += renewable_capacity[('region1', 'coal')] >= coal_range[0]
136
- model += renewable_capacity[('region1', 'coal')] <= coal_range[1]
137
-
138
- # Solve the initial model to find the optimal solution
139
  model.solve()
140
- optimal_cost = pulp.value(model.objective)
141
-
142
- # MGA: Generate alternative solutions
143
- alternative_solutions = []
144
- for threshold in thresholds:
145
- relaxed_cost = optimal_cost * (1 + threshold)
146
- for tech in selected_tech:
147
- alt_model = pulp.LpProblem(f"AlternativeModel_{tech}_{threshold}", pulp.LpMinimize)
148
- alt_model += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
149
- for r in regions for g in technologies]) + battery_capacity * battery_cost_per_mwh + 0.01 * total_co2_emissions <= relaxed_cost
150
-
151
- # Avoid overlapping constraint names by appending unique suffixes
152
- for i, (name, constraint) in enumerate(model.constraints.items()):
153
- alt_model += constraint.copy(), f"{name}_{tech}_{threshold}_{i}"
154
-
155
- # Modify the objective to minimize or maximize capacity of selected technology
156
- alt_model += renewable_capacity[('region1', tech)], f"Minimize_{tech}_Capacity"
157
- alt_model.solve()
158
-
159
- if pulp.LpStatus[alt_model.status] == 'Optimal':
160
- alternative_solutions.append({
161
- 'threshold': threshold,
162
- 'technology': tech,
163
- 'solution': {g: renewable_capacity[('region1', g)].varValue for g in technologies},
164
- 'battery_capacity': battery_capacity.varValue,
165
- 'total_cost': pulp.value(alt_model.objective),
166
- 'total_co2_emissions': total_co2_emissions.varValue
167
- })
168
-
169
- return alternative_solutions, data, renewable_capacity, battery_discharge, battery_charge, demand, curtailment
170
 
171
- # Streamlit UI setup
172
- st.set_page_config(page_title='Energy System Optimization with Nuclear, Coal, and CO2', layout='wide')
173
- st.title('Energy System Optimization with MGA: Including Nuclear, Coal, and CO2 Considerations')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
- technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river', 'nuclear', 'coal']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
  with st.sidebar:
178
  st.header('Input Parameters')
179
- city_code = st.text_input("City Code", "999999")
180
- solar_cost = st.number_input("Solar Cost (¥/MW)", value=80.0)
181
- onshore_wind_cost = st.number_input("Onshore Wind Cost (¥/MW)", value=120.0)
182
- offshore_wind_cost = st.number_input("Offshore Wind Cost (¥/MW)", value=180.0)
183
- river_cost = st.number_input("River Cost (¥/MW)", value=1000.0)
184
- nuclear_cost = st.number_input("Nuclear Cost (¥/MW)", value=500.0)
185
- coal_cost = st.number_input("Coal Cost (¥/MW)", value=300.0)
186
- battery_cost = st.number_input("Battery Cost (¥/MWh)", value=80.0)
187
- yearly_demand = st.number_input("Yearly Demand (TWh/year)", value=15.0)
188
  solar_range = st.slider("Solar Capacity Range (MW)", 0, 10000, (0, 10000))
189
  wind_range = st.slider("Onshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
190
  offshore_wind_range = st.slider("Offshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
191
  river_range = st.slider("River Capacity Range (MW)", 0, 10000, (0, 10000))
192
- nuclear_range = st.slider("Nuclear Capacity Range (MW)", 0, 10000, (0, 10000))
193
- coal_range = st.slider("Coal Capacity Range (MW)", 0, 10000, (0, 10000))
194
- thresholds = st.multiselect("Thresholds (%)", [0, 5, 10], default=[0, 5])
195
- selected_technologies = st.multiselect("Technologies", technologies, default=technologies)
196
-
197
- if st.button("Run Optimization"):
198
- solutions, data, renewable_capacity, battery_discharge, battery_charge, demand, curtailment = optimize_energy_system(
199
- city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost,
200
- nuclear_cost, coal_cost, battery_cost, yearly_demand, solar_range, wind_range,
201
- river_range, offshore_wind_range, nuclear_range, coal_range,
202
- [t / 100 for t in thresholds], selected_technologies)
203
- if solutions:
204
- st.write("Optimization Results", solutions)
205
-
206
- supply_solar = [renewable_capacity[("region1", "solar")].varValue * cf for cf in data['solar hourly capacity factor']]
207
- supply_onshore_wind = [renewable_capacity[("region1", "onshore_wind")].varValue * cf for cf in data['onshore_wind hourly capacity factor']]
208
- supply_offshore_wind = [renewable_capacity[("region1", "offshore_wind")].varValue * cf for cf in data['offshore_wind hourly capacity factor']]
209
- supply_river = [renewable_capacity[("region1", "river")].varValue * cf for cf in data['river hourly capacity factor']]
210
- battery_discharge_values = [battery_discharge[t].varValue for t in range(len(data['Time']))]
211
- battery_charge_values = [battery_charge[t].varValue for t in range(len(data['Time']))]
212
- curtailment_values = [curtailment[("region1", t)].varValue for t in range(len(data['Time']))]
213
-
214
- fig_energy = go.Figure()
215
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_solar, mode='lines', stackgroup='one', name='Solar', line=dict(color='#FFD700', width=0)))
216
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_onshore_wind, mode='lines', stackgroup='one', name='Onshore Wind', line=dict(color='#1F78B4', width=0)))
217
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_offshore_wind, mode='lines', stackgroup='one', name='Offshore Wind', line=dict(color='#66C2A5', width=0)))
218
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_river, mode='lines', stackgroup='one', name='Run of River', line=dict(color='#FF7F00', width=0)))
219
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=battery_discharge_values, mode='lines', stackgroup='one', name='Battery Discharge', fill='tonexty', line=dict(color='#6A3D9A', width=0)))
220
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=battery_charge_values, mode='lines', stackgroup='two', name='Battery Charge', fill='tonexty', line=dict(color='#6A3D9A', width=0)))
221
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=-demand, mode='lines', stackgroup='two', name='Demand', line=dict(color='black', width=0)))
222
- fig_energy.add_trace(go.Scatter(x=data['Time'], y=curtailment_values, mode='lines', stackgroup='two', name='Curtailment', line=dict(color='#aaaaaa', width=0)))
223
-
224
- fig_energy.update_layout(
225
- title_text='Power Supply and Demand',
226
- title_x=0.5,
227
- yaxis_title='Power dispatch (MW)',
228
- legend_title='Source',
229
- font=dict(size=12),
230
- margin=dict(l=40, r=40, t=40, b=40),
231
- hovermode='x unified',
232
- plot_bgcolor='white',
233
- xaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray'),
234
- yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')
235
- )
236
 
237
- st.plotly_chart(fig_energy)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
 
9
+ # Function to fetch renewable energy data
10
  def get_renewable_energy_data(city_code):
11
  url = f"https://energy-sustainability.jp/_ajax/renewable_energy/get/?code={city_code}"
12
  response = requests.get(url)
13
  if response.status_code != 200:
14
+ return None, "データの取得に失敗しました。"
15
 
16
  data = response.json()
17
  if not data:
18
+ return None, "データが見つかりませんでした。"
19
 
20
  base_times = data[next(iter(data))]['x']
21
  result_df = pd.DataFrame({"Time": base_times})
 
27
 
28
  return result_df, None
29
 
30
+ # Function to optimize the energy system and create visualizations
31
+ 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):
 
 
32
  data, error = get_renewable_energy_data(city_code)
33
  if error:
34
  st.error(error)
35
+ return None, None, None, None, None, None
36
 
37
  for col in data.columns[1:]:
38
  data[col] = pd.to_numeric(data[col], errors='coerce')
 
43
  onshore_wind_cf = data['onshore_wind hourly capacity factor']
44
  offshore_wind_cf = data['offshore_wind hourly capacity factor']
45
  river_cf = data['river hourly capacity factor']
 
 
46
  demand_cf = data['demand hourly capacity factor']
47
 
48
  regions = ['region1']
49
+ technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']
50
  capacity_factor = {
51
  'solar': solar_cf,
52
  'onshore_wind': onshore_wind_cf,
53
  'offshore_wind': offshore_wind_cf,
54
+ 'river': river_cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
+ renewable_capacity_cost = {'solar': solar_cost, 'onshore_wind': onshore_wind_cost, 'offshore_wind': offshore_wind_cost, 'river': river_cost}
58
  battery_cost_per_mwh = battery_cost
59
  battery_efficiency = 0.9
60
 
 
71
  battery_discharge = pulp.LpVariable.dicts("battery_discharge", time_steps, lowBound=0, cat='Continuous')
72
  SOC = pulp.LpVariable.dicts("SOC", time_steps, lowBound=0, cat='Continuous')
73
 
74
+ model = pulp.LpProblem("EnergySystemOptimizationWithBattery", pulp.LpMinimize)
 
 
 
75
 
 
76
  model += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
77
  for r in regions for g in technologies]) + \
78
+ battery_capacity * battery_cost_per_mwh, "TotalCost"
 
79
 
 
80
  for r in regions:
81
  for t in time_steps:
82
  model += pulp.lpSum([renewable_capacity[(r, g)] * capacity_factor[g][t]
83
  for g in technologies]) + battery_discharge[t] == demand[t] + battery_charge[t] + curtailment[(r, t)], f"DemandConstraint_{r}_{t}"
84
+
85
  if t == 0:
86
  model += SOC[t] == battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"
87
  else:
88
  model += SOC[t] == SOC[t - 1] + battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"
89
+
90
  model += SOC[t] <= battery_capacity, f"SOCUpperBound_{t}"
91
 
92
+ model += renewable_capacity[('region1', 'solar')] >= solar_range[0], "SolarMinConstraint"
93
+ model += renewable_capacity[('region1', 'solar')] <= solar_range[1], "SolarMaxConstraint"
94
+ model += renewable_capacity[('region1', 'onshore_wind')] >= wind_range[0], "WindMinConstraint"
95
+ model += renewable_capacity[('region1', 'onshore_wind')] <= wind_range[1], "WindMaxConstraint"
96
+ model += renewable_capacity[('region1', 'offshore_wind')] >= offshore_wind_range[0], "OffshoreWindMinConstraint"
97
+ model += renewable_capacity[('region1', 'offshore_wind')] <= offshore_wind_range[1], "OffshoreWindMaxConstraint"
98
+ model += renewable_capacity[('region1', 'river')] >= river_range[0], "RiverMinConstraint"
99
+ model += renewable_capacity[('region1', 'river')] <= river_range[1], "RiverMaxConstraint"
100
+
 
 
 
 
 
 
 
 
 
101
  model.solve()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
+ supply_solar = solar_cf * renewable_capacity[('region1', 'solar')].varValue
104
+ supply_onshore_wind = onshore_wind_cf * renewable_capacity[('region1', 'onshore_wind')].varValue
105
+ supply_offshore_wind = offshore_wind_cf * renewable_capacity[('region1', 'offshore_wind')].varValue
106
+ supply_river = river_cf * renewable_capacity[('region1', 'river')].varValue
107
+
108
+ battery_discharge_values = [battery_discharge[t].varValue for t in time_steps]
109
+ battery_charge_values = [-battery_charge[t].varValue for t in time_steps]
110
+ SOC_values = [SOC[t].varValue for t in time_steps]
111
+ curtailment_values = [-curtailment[(r, t)].varValue for r in regions for t in time_steps]
112
+
113
+ max_SOC = max(SOC_values)
114
+ SOC_normalized = [(soc / max_SOC) * 100 for soc in SOC_values] if max_SOC > 0 else [0] * len(SOC_values)
115
+
116
+ fig_energy = go.Figure()
117
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_solar, mode='lines', stackgroup='one', name='Solar', line=dict(color='#FFD700', width=0)))
118
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_onshore_wind, mode='lines', stackgroup='one', name='Onshore Wind', line=dict(color='#1F78B4', width=0)))
119
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_offshore_wind, mode='lines', stackgroup='one', name='Offshore Wind', line=dict(color='#66C2A5', width=0)))
120
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_river, mode='lines', stackgroup='one', name='Run of River', line=dict(color='#FF7F00', width=0)))
121
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=battery_discharge_values, mode='lines', stackgroup='one', name='Battery Discharge', fill='tonexty', line=dict(color='#6A3D9A', width=0)))
122
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=battery_charge_values, mode='lines', stackgroup='two', name='Battery Charge', fill='tonexty', line=dict(color='#6A3D9A', width=0)))
123
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=-demand, mode='lines', stackgroup='two', name='Demand', line=dict(color='black', width=0)))
124
+ fig_energy.add_trace(go.Scatter(x=data['Time'], y=curtailment_values, mode='lines', stackgroup='two', name='Curtailment', line=dict(color='#aaaaaa', width=0)))
125
+
126
+ fig_energy.update_layout(
127
+ title_text='Power Supply and Demand',
128
+ title_x=0.5,
129
+ yaxis_title='Power dispatch (MW)',
130
+ legend_title='Source',
131
+ font=dict(size=12),
132
+ margin=dict(l=40, r=40, t=40, b=40),
133
+ hovermode='x unified',
134
+ plot_bgcolor='white',
135
+ xaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray'),
136
+ yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')
137
+ )
138
+
139
+ # Heatmap generation for each renewable energy source
140
+ heatmaps = []
141
+ for energy_source in ['solar', 'onshore_wind', 'offshore_wind', 'river']:
142
+ df_heatmap = data[['Time', f'{energy_source} hourly capacity factor']].copy()
143
+ df_heatmap['Time'] = pd.to_datetime(df_heatmap['Time'], errors='coerce')
144
+ df_heatmap['day_of_year'] = df_heatmap['Time'].dt.dayofyear
145
+ df_heatmap['hour_of_day'] = df_heatmap['Time'].dt.hour
146
+
147
+ pivot_df = df_heatmap.pivot_table(
148
+ index='hour_of_day',
149
+ columns='day_of_year',
150
+ values=f'{energy_source} hourly capacity factor',
151
+ aggfunc='mean'
152
+ )
153
+
154
+ fig_heatmap = px.imshow(
155
+ pivot_df.values,
156
+ labels=dict(x="Day of Year", y="Hour of Day", color=f"{energy_source.replace('_', ' ').title()} Capacity Factor"),
157
+ x=pivot_df.columns,
158
+ y=pivot_df.index,
159
+ aspect="auto",
160
+ color_continuous_scale='Plasma'
161
+ )
162
 
163
+ fig_heatmap.update_layout(
164
+ title=f'{energy_source.replace("_", " ").title()} Hourly Capacity Factor (24 Hours x 365 Days)',
165
+ xaxis_title='Day of Year',
166
+ yaxis_title='Hour of Day',
167
+ font=dict(size=12),
168
+ plot_bgcolor='white',
169
+ margin=dict(l=40, r=40, t=40, b=40),
170
+ )
171
+ heatmaps.append(fig_heatmap)
172
+
173
+ # Create capacity range visualization for each technology
174
+ fig_capacity_ranges = go.Figure()
175
+ technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']
176
+ capacity_ranges = [solar_range, wind_range, offshore_wind_range, river_range]
177
+ optimized_capacities = [
178
+ renewable_capacity[('region1', 'solar')].varValue,
179
+ renewable_capacity[('region1', 'onshore_wind')].varValue,
180
+ renewable_capacity[('region1', 'offshore_wind')].varValue,
181
+ renewable_capacity[('region1', 'river')].varValue
182
+ ]
183
+
184
+ for tech, cap_range, optimized_cap in zip(technologies, capacity_ranges, optimized_capacities):
185
+ fig_capacity_ranges.add_trace(go.Scatter(
186
+ x=[tech, tech],
187
+ y=cap_range,
188
+ mode='lines',
189
+ name=f'{tech} capacity range',
190
+ line=dict(color='blue', width=4)
191
+ ))
192
+ fig_capacity_ranges.add_trace(go.Scatter(
193
+ x=[tech],
194
+ y=[optimized_cap],
195
+ mode='markers',
196
+ name=f'{tech} optimized capacity',
197
+ marker=dict(color='red', symbol='x', size=10)
198
+ ))
199
+
200
+ fig_capacity_ranges.update_layout(
201
+ title_text='Optimized Capacity vs. Capacity Ranges',
202
+ title_x=0.5,
203
+ yaxis_title='Capacity (MW)',
204
+ xaxis_title='Technology',
205
+ font=dict(size=12),
206
+ margin=dict(l=40, r=40, t=40, b=40),
207
+ hovermode='x unified',
208
+ plot_bgcolor='white',
209
+ xaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray'),
210
+ yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')
211
+ )
212
+
213
+ return fig_energy, heatmaps, curtailment_values, SOC_normalized, fig_capacity_ranges, renewable_capacity
214
+
215
+ # 資源コストの感度解析を行う関数
216
+ def analyze_cost_sensitivity(renewable_capacity_cost, technologies, renewable_capacity):
217
+ # コストの変動範囲(0.5倍から1.5倍)
218
+ cost_multipliers = np.linspace(0.5, 1.5, 11)
219
+
220
+ # 結果を格納する辞書
221
+ sensitivity_results = {}
222
+
223
+ for tech in technologies:
224
+ # 各技術ごとのコスト変動に対する総コストの変化を計算
225
+ original_cost = renewable_capacity_cost[tech]
226
+ total_costs = []
227
+
228
+ for multiplier in cost_multipliers:
229
+ # コストを変更
230
+ modified_cost = original_cost * multiplier
231
+ # 総コスト = 変更後のコスト * 設備容量
232
+ total_cost = modified_cost * renewable_capacity[('region1', tech)].varValue
233
+ total_costs.append(total_cost)
234
+
235
+ # 技術ごとに結果を保存
236
+ sensitivity_results[tech] = total_costs
237
+
238
+ # 可視化
239
+ fig = go.Figure()
240
+
241
+ for tech, total_costs in sensitivity_results.items():
242
+ fig.add_trace(go.Scatter(
243
+ x=cost_multipliers,
244
+ y=total_costs,
245
+ mode='lines+markers',
246
+ name=f'{tech} Cost Sensitivity'
247
+ ))
248
+
249
+ # グラフのレイアウト
250
+ fig.update_layout(
251
+ title='Cost Sensitivity Analysis: Impact of Cost Changes on Total System Cost',
252
+ xaxis_title='Cost Multiplier (0.5x to 1.5x)',
253
+ yaxis_title='Total System Cost (¥)',
254
+ hovermode='x unified',
255
+ plot_bgcolor='white',
256
+ xaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray'),
257
+ yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')
258
+ )
259
+
260
+ return fig
261
+
262
+ # Streamlit UI setup
263
+ st.set_page_config(page_title='Renewable Energy System Optimization', layout='wide')
264
+ st.title('Renewable Energy System Optimization')
265
+
266
+ st.markdown("""
267
+ ### Model Overview
268
+ This application is designed to optimize renewable energy systems for a specific region. The model allows the user to set the costs for different renewable energy technologies and battery storage, as well as minimum and maximum capacity limits for each technology. The optimization uses linear programming to minimize the total cost while ensuring demand is met, incorporating energy storage to help manage intermittency.
269
+ The renewable technologies considered are:
270
+ - Solar PV
271
+ - Onshore Wind
272
+ - Offshore Wind
273
+ - Run of River (Hydro)
274
+ The optimization problem aims to balance supply and demand at minimal cost, while also providing flexibility in the form of battery energy storage. Curtailment and battery state of charge are also considered in the model.
275
+ """)
276
 
277
  with st.sidebar:
278
  st.header('Input Parameters')
279
+ city_code = st.text_input("Enter City Code", value=999999)
280
+ solar_cost = st.number_input("Solar Capacity Cost (¥/MW)", value=80.0)
281
+ onshore_wind_cost = st.number_input("Onshore Wind Capacity Cost (¥/MW)", value=120.0)
282
+ offshore_wind_cost = st.number_input("Offshore Wind Capacity Cost (¥/MW)", value=180.0)
283
+ river_cost = st.number_input("River Capacity Cost (¥/MW)", value=1000.0)
284
+ battery_cost = st.number_input("Battery Cost (¥/MWh)", value=80.0)
285
+ yearly_demand = st.number_input("Yearly Power Demand (TWh/year)", value=15.0)
 
 
286
  solar_range = st.slider("Solar Capacity Range (MW)", 0, 10000, (0, 10000))
287
  wind_range = st.slider("Onshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
288
  offshore_wind_range = st.slider("Offshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))
289
  river_range = st.slider("River Capacity Range (MW)", 0, 10000, (0, 10000))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
+ calculated_optimal_energy_mix = False
292
+
293
+ if st.button('Calculate Optimal Energy Mix'):
294
+ fig_energy, heatmaps, curtailment_values, soc_per_hour, fig_capacity_ranges, renewable_capacity = optimize_energy_system(
295
+ 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
296
+ )
297
+
298
+ if fig_energy:
299
+ st.plotly_chart(fig_energy, use_container_width=True, height=800)
300
+
301
+ # Additional visualizations
302
+ st.markdown("### Hourly Capacity Factor Heatmaps")
303
+ for fig_heatmap in heatmaps:
304
+ st.plotly_chart(fig_heatmap, use_container_width=True, height=800)
305
+
306
+ st.markdown("### Additional Analysis")
307
+ st.markdown("The following plots provide additional insights into the renewable energy mix, curtailment, and electricity price variations.")
308
+
309
+ # Plot curtailment over time
310
+ curtailment_df = pd.DataFrame({"Time": fig_energy.data[0].x, "Curtailment (MW)": curtailment_values})
311
+ fig_curtailment = px.line(curtailment_df, x='Time', y='Curtailment (MW)', title='Curtailment Over Time', template='plotly_white')
312
+ st.plotly_chart(fig_curtailment, use_container_width=True, height=800)
313
+
314
+ # Plot electricity price variation over time
315
+ soc_df = pd.DataFrame({"Time": fig_energy.data[0].x, "State of charge [%]": soc_per_hour})
316
+ fig_battery_operation = px.line(soc_df, x='Time', y='State of charge [%]', title='State of charge in battery', template='plotly_white')
317
+ st.plotly_chart(fig_battery_operation, use_container_width=True, height=800)
318
+
319
+ # Plot optimized capacity vs. capacity ranges
320
+ st.plotly_chart(fig_capacity_ranges, use_container_width=True, height=800)
321
+
322
+ calculated_optimal_energy_mix = True
323
+
324
+ # Streamlit UIに感度解析ボタンを追加
325
+ if st.button('Analyze Cost Sensitivity'):
326
+ if calculated_optimal_energy_mix:
327
+ fig_sensitivity = analyze_cost_sensitivity({
328
+ 'solar': solar_cost,
329
+ 'onshore_wind': onshore_wind_cost,
330
+ 'offshore_wind': offshore_wind_cost,
331
+ 'river': river_cost
332
+ }, ['solar', 'onshore_wind', 'offshore_wind', 'river'], renewable_capacity)
333
+ st.plotly_chart(fig_sensitivity, use_container_width=True, height=800)
334
+ else:
335
+ st.error("Please calculate the optimal energy mix first before running the cost sensitivity analysis.")