naohiro701/ToyModel-EnergySystemOptimizations
0
1import streamlit as st2import requests3import pandas as pd4import pulp5import plotly.graph_objs as go6import plotly.express as px7import numpy as np8import json9 10# Function to fetch renewable energy data11def get_json():12 """13 open data.json14 """15 with open('data.json') as f:16 data = json.load(f)17 if not data:18 return None, "No data found."19 20 base_times = data[next(iter(data))]['x']21 result_df = pd.DataFrame({"Time": base_times})22 23 for energy_type, energy_data in data.items():24 if 'x' in energy_data and 'y' in energy_data:25 values = energy_data['y']26 result_df[f"{energy_type} hourly capacity factor"] = values27 28 return result_df29 30# Function to optimize the energy system and create visualizations31def optimize_energy_system(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 = get_json()33 34 for col in data.columns[1:]:35 data[col] = pd.to_numeric(data[col], errors='coerce')36 data = data.fillna(0)37 38 time_steps = range(len(data['Time']))39 solar_cf = data['solar hourly capacity factor']40 onshore_wind_cf = data['onshore_wind hourly capacity factor']41 offshore_wind_cf = data['offshore_wind hourly capacity factor']42 river_cf = data['river hourly capacity factor']43 demand_cf = data['demand hourly capacity factor']44 45 regions = ['region1']46 technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']47 capacity_factor = {48 'solar': solar_cf,49 'onshore_wind': onshore_wind_cf,50 'offshore_wind': offshore_wind_cf,51 'river': river_cf52 }53 54 renewable_capacity_cost = {'solar': solar_cost, 'onshore_wind': onshore_wind_cost, 'offshore_wind': offshore_wind_cost, 'river': river_cost}55 battery_cost_per_mwh = battery_cost56 battery_efficiency = 0.957 58 demand = demand_cf * yearly_demand / 100 * 1000 * 100059 60 renewable_capacity = pulp.LpVariable.dicts("renewable_capacity",61 [(r, g) for r in regions for g in technologies],62 lowBound=0, cat='Continuous')63 curtailment = pulp.LpVariable.dicts("curtailment",64 [(r, t) for r in regions for t in time_steps],65 lowBound=0, cat='Continuous')66 battery_capacity = pulp.LpVariable("battery_capacity", lowBound=0, cat='Continuous')67 battery_charge = pulp.LpVariable.dicts("battery_charge", time_steps, lowBound=0, cat='Continuous')68 battery_discharge = pulp.LpVariable.dicts("battery_discharge", time_steps, lowBound=0, cat='Continuous')69 SOC = pulp.LpVariable.dicts("SOC", time_steps, lowBound=0, cat='Continuous')70 71 model = pulp.LpProblem("EnergySystemOptimizationWithBattery", pulp.LpMinimize)72 73 model += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]74 for r in regions for g in technologies]) + \75 battery_capacity * battery_cost_per_mwh, "TotalCost"76 77 for r in regions:78 for t in time_steps:79 model += pulp.lpSum([renewable_capacity[(r, g)] * capacity_factor[g][t]80 for g in technologies]) + battery_discharge[t] == demand[t] + battery_charge[t] + curtailment[(r, t)], f"DemandConstraint_{r}_{t}"81 82 if t == 0:83 model += SOC[t] == battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"84 else:85 model += SOC[t] == SOC[t - 1] + battery_charge[t] * battery_efficiency - battery_discharge[t] * (1 / battery_efficiency), f"SOCUpdate_{t}"86 87 model += SOC[t] <= battery_capacity, f"SOCUpperBound_{t}"88 89 model += renewable_capacity[('region1', 'solar')] >= solar_range[0], "SolarMinConstraint"90 model += renewable_capacity[('region1', 'solar')] <= solar_range[1], "SolarMaxConstraint"91 model += renewable_capacity[('region1', 'onshore_wind')] >= wind_range[0], "WindMinConstraint"92 model += renewable_capacity[('region1', 'onshore_wind')] <= wind_range[1], "WindMaxConstraint"93 model += renewable_capacity[('region1', 'offshore_wind')] >= offshore_wind_range[0], "OffshoreWindMinConstraint"94 model += renewable_capacity[('region1', 'offshore_wind')] <= offshore_wind_range[1], "OffshoreWindMaxConstraint"95 model += renewable_capacity[('region1', 'river')] >= river_range[0], "RiverMinConstraint"96 model += renewable_capacity[('region1', 'river')] <= river_range[1], "RiverMaxConstraint"97 98 model.solve()99 100 supply_solar = solar_cf * renewable_capacity[('region1', 'solar')].varValue101 supply_onshore_wind = onshore_wind_cf * renewable_capacity[('region1', 'onshore_wind')].varValue102 supply_offshore_wind = offshore_wind_cf * renewable_capacity[('region1', 'offshore_wind')].varValue103 supply_river = river_cf * renewable_capacity[('region1', 'river')].varValue104 105 battery_discharge_values = [battery_discharge[t].varValue for t in time_steps]106 battery_charge_values = [-battery_charge[t].varValue for t in time_steps]107 SOC_values = [SOC[t].varValue for t in time_steps]108 curtailment_values = [-curtailment[(r, t)].varValue for r in regions for t in time_steps]109 110 max_SOC = max(SOC_values)111 SOC_normalized = [(soc / max_SOC) * 100 for soc in SOC_values] if max_SOC > 0 else [0] * len(SOC_values)112 113 fig_energy = go.Figure()114 fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_solar, mode='lines', stackgroup='one', name='Solar', line=dict(color='#FFD700', width=0)))115 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)))116 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)))117 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)))118 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)))119 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)))120 fig_energy.add_trace(go.Scatter(x=data['Time'], y=-demand, mode='lines', stackgroup='two', name='Demand', line=dict(color='black', width=0)))121 fig_energy.add_trace(go.Scatter(x=data['Time'], y=curtailment_values, mode='lines', stackgroup='two', name='Curtailment', line=dict(color='#aaaaaa', width=0)))122 123 fig_energy.update_layout(124 title_text='Power Supply and Demand',125 title_x=0.5,126 yaxis_title='Power dispatch (MW)',127 legend_title='Source',128 font=dict(size=12),129 margin=dict(l=40, r=40, t=40, b=40),130 hovermode='x unified',131 plot_bgcolor='white',132 xaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray'),133 yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')134 )135 136 # Heatmap generation for each renewable energy source137 heatmaps = []138 for energy_source in ['solar', 'onshore_wind', 'offshore_wind', 'river']:139 df_heatmap = data[['Time', f'{energy_source} hourly capacity factor']].copy()140 df_heatmap['Time'] = pd.to_datetime(df_heatmap['Time'], errors='coerce')141 df_heatmap['day_of_year'] = df_heatmap['Time'].dt.dayofyear142 df_heatmap['hour_of_day'] = df_heatmap['Time'].dt.hour143 144 pivot_df = df_heatmap.pivot_table(145 index='hour_of_day',146 columns='day_of_year',147 values=f'{energy_source} hourly capacity factor',148 aggfunc='mean'149 )150 151 fig_heatmap = px.imshow(152 pivot_df.values,153 labels=dict(x="Day of Year", y="Hour of Day", color=f"{energy_source.replace('_', ' ').title()} Capacity Factor"),154 x=pivot_df.columns,155 y=pivot_df.index,156 aspect="auto",157 color_continuous_scale='Plasma'158 )159 160 fig_heatmap.update_layout(161 title=f'{energy_source.replace("_", " ").title()} Hourly Capacity Factor (24 Hours x 365 Days)',162 xaxis_title='Day of Year',163 yaxis_title='Hour of Day',164 font=dict(size=12),165 plot_bgcolor='white',166 margin=dict(l=40, r=40, t=40, b=40),167 )168 heatmaps.append(fig_heatmap)169 170 # Create capacity range visualization for each technology171 fig_capacity_ranges = go.Figure()172 technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']173 capacity_ranges = [solar_range, wind_range, offshore_wind_range, river_range]174 optimized_capacities = [175 renewable_capacity[('region1', 'solar')].varValue,176 renewable_capacity[('region1', 'onshore_wind')].varValue,177 renewable_capacity[('region1', 'offshore_wind')].varValue,178 renewable_capacity[('region1', 'river')].varValue179 ]180 181 for tech, cap_range, optimized_cap in zip(technologies, capacity_ranges, optimized_capacities):182 fig_capacity_ranges.add_trace(go.Scatter(183 x=[tech, tech],184 y=cap_range,185 mode='lines',186 name=f'{tech} capacity range',187 line=dict(color='blue', width=4)188 ))189 fig_capacity_ranges.add_trace(go.Scatter(190 x=[tech],191 y=[optimized_cap],192 mode='markers',193 name=f'{tech} optimized capacity',194 marker=dict(color='red', symbol='x', size=10)195 ))196 197 fig_capacity_ranges.update_layout(198 title_text='Optimized Capacity vs. Capacity Ranges',199 title_x=0.5,200 yaxis_title='Capacity (MW)',201 xaxis_title='Technology',202 font=dict(size=12),203 margin=dict(l=40, r=40, t=40, b=40),204 hovermode='x unified',205 plot_bgcolor='white',206 xaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray'),207 yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')208 )209 210 return fig_energy, heatmaps, curtailment_values, SOC_normalized, fig_capacity_ranges, renewable_capacity211 212# 資源コストの感度解析を行う関数213def analyze_cost_sensitivity(renewable_capacity_cost, technologies, renewable_capacity):214 # コストの変動範囲(0.5倍から1.5倍)215 cost_multipliers = np.linspace(0.5, 1.5, 11)216 217 # 結果を格納する辞書218 sensitivity_results = {}219 220 for tech in technologies:221 # 各技術ごとのコスト変動に対する総コストの変化を計算222 original_cost = renewable_capacity_cost[tech]223 total_costs = []224 225 for multiplier in cost_multipliers:226 # コストを変更227 modified_cost = original_cost * multiplier228 # 総コスト = 変更後のコスト * 設備容量229 total_cost = modified_cost * renewable_capacity[('region1', tech)].varValue230 total_costs.append(total_cost)231 232 # 技術ごとに結果を保存233 sensitivity_results[tech] = total_costs234 235 # 可視化236 fig = go.Figure()237 238 for tech, total_costs in sensitivity_results.items():239 fig.add_trace(go.Scatter(240 x=cost_multipliers,241 y=total_costs,242 mode='lines+markers',243 name=f'{tech} Cost Sensitivity'244 ))245 246 # グラフのレイアウト247 fig.update_layout(248 title='Cost Sensitivity Analysis: Impact of Cost Changes on Total System Cost',249 xaxis_title='Cost Multiplier (0.5x to 1.5x)',250 yaxis_title='Total System Cost (¥)',251 hovermode='x unified',252 plot_bgcolor='white',253 xaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray'),254 yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')255 )256 257 return fig258 259# Streamlit UI setup260st.set_page_config(page_title='Renewable Energy System Optimization', layout='wide')261st.title('Renewable Energy System Optimization')262 263st.markdown("""264### Model Overview265This 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.266The renewable technologies considered are:267- Solar PV268- Onshore Wind269- Offshore Wind270- Run of River (Hydro)271The 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.272""")273 274with st.sidebar:275 st.header('Input Parameters')276 solar_cost = st.number_input("Solar Capacity Cost (¥/MW)", value=80.0)277 onshore_wind_cost = st.number_input("Onshore Wind Capacity Cost (¥/MW)", value=120.0)278 offshore_wind_cost = st.number_input("Offshore Wind Capacity Cost (¥/MW)", value=180.0)279 river_cost = st.number_input("River Capacity Cost (¥/MW)", value=1000.0)280 battery_cost = st.number_input("Battery Cost (¥/MWh)", value=80.0)281 yearly_demand = st.number_input("Yearly Power Demand (TWh/year)", value=15.0)282 solar_range = st.slider("Solar Capacity Range (MW)", 0, 10000, (0, 10000))283 wind_range = st.slider("Onshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))284 offshore_wind_range = st.slider("Offshore Wind Capacity Range (MW)", 0, 10000, (0, 10000))285 river_range = st.slider("River Capacity Range (MW)", 0, 10000, (0, 10000))286 287calculated_optimal_energy_mix = False288 289if st.button('Calculate Optimal Energy Mix'):290 fig_energy, heatmaps, curtailment_values, soc_per_hour, fig_capacity_ranges, renewable_capacity = optimize_energy_system(291 solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand, solar_range, wind_range, river_range, offshore_wind_range292 )293 294 if fig_energy:295 st.plotly_chart(fig_energy, use_container_width=True, height=800)296 297 # Additional visualizations298 st.markdown("### Hourly Capacity Factor Heatmaps")299 for fig_heatmap in heatmaps:300 st.plotly_chart(fig_heatmap, use_container_width=True, height=800)301 302 st.markdown("### Additional Analysis")303 st.markdown("The following plots provide additional insights into the renewable energy mix, curtailment, and electricity price variations.")304 305 # Plot curtailment over time306 curtailment_df = pd.DataFrame({"Time": fig_energy.data[0].x, "Curtailment (MW)": curtailment_values})307 fig_curtailment = px.line(curtailment_df, x='Time', y='Curtailment (MW)', title='Curtailment Over Time', template='plotly_white')308 st.plotly_chart(fig_curtailment, use_container_width=True, height=800)309 310 # Plot electricity price variation over time311 soc_df = pd.DataFrame({"Time": fig_energy.data[0].x, "State of charge [%]": soc_per_hour})312 fig_battery_operation = px.line(soc_df, x='Time', y='State of charge [%]', title='State of charge in battery', template='plotly_white')313 st.plotly_chart(fig_battery_operation, use_container_width=True, height=800)314 315 # Plot optimized capacity vs. capacity ranges316 st.plotly_chart(fig_capacity_ranges, use_container_width=True, height=800)317 318 calculated_optimal_energy_mix = True319 320# Streamlit UIに感度解析ボタンを追加321if st.button('Analyze Cost Sensitivity'):322 if calculated_optimal_energy_mix:323 fig_sensitivity = analyze_cost_sensitivity({324 'solar': solar_cost,325 'onshore_wind': onshore_wind_cost,326 'offshore_wind': offshore_wind_cost,327 'river': river_cost328 }, ['solar', 'onshore_wind', 'offshore_wind', 'river'], renewable_capacity)329 st.plotly_chart(fig_sensitivity, use_container_width=True, height=800)330 else:331 st.error("Please calculate the optimal energy mix first before running the cost sensitivity analysis.")