Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -11,29 +11,18 @@ import numpy as np
|
|
| 11 |
|
| 12 |
# Function to fetch renewable energy data
|
| 13 |
def get_renewable_energy_data(city_code):
|
| 14 |
-
"""
|
| 15 |
-
Fetch renewable energy data from an external source based on a city code.
|
| 16 |
-
|
| 17 |
-
Args:
|
| 18 |
-
- city_code (str): The code representing the city for which the renewable energy data is to be fetched.
|
| 19 |
-
Returns:
|
| 20 |
-
- result_df (DataFrame): A DataFrame containing the energy data with time and capacity factors for each energy type.
|
| 21 |
-
- error (str): Error message if the data fetching fails.
|
| 22 |
-
"""
|
| 23 |
url = f"https://energy-sustainability.jp/_ajax/renewable_energy/get/?code={city_code}"
|
| 24 |
response = requests.get(url)
|
| 25 |
if response.status_code != 200:
|
| 26 |
-
return None, "データの取得に失敗しました。"
|
| 27 |
|
| 28 |
data = response.json()
|
| 29 |
if not data:
|
| 30 |
-
return None, "データが見つかりませんでした。"
|
| 31 |
|
| 32 |
-
# Extract base times from the first energy type.
|
| 33 |
base_times = data[next(iter(data))]['x']
|
| 34 |
result_df = pd.DataFrame({"Time": base_times})
|
| 35 |
|
| 36 |
-
# Loop through each energy type and append hourly capacity factor data.
|
| 37 |
for energy_type, energy_data in data.items():
|
| 38 |
if 'x' in energy_data and 'y' in energy_data:
|
| 39 |
values = energy_data['y']
|
|
@@ -43,48 +32,21 @@ def get_renewable_energy_data(city_code):
|
|
| 43 |
|
| 44 |
# Function to filter numeric columns
|
| 45 |
def filter_numeric_columns(df):
|
| 46 |
-
"""
|
| 47 |
-
Filters only numeric columns from the provided DataFrame.
|
| 48 |
-
|
| 49 |
-
Args:
|
| 50 |
-
- df (DataFrame): The DataFrame from which to filter numeric columns.
|
| 51 |
-
|
| 52 |
-
Returns:
|
| 53 |
-
- DataFrame: A DataFrame containing only numeric columns.
|
| 54 |
-
"""
|
| 55 |
numeric_df = df.select_dtypes(include=['number'])
|
| 56 |
return numeric_df
|
| 57 |
|
| 58 |
# Function to optimize the energy system
|
| 59 |
def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand):
|
| 60 |
-
"""
|
| 61 |
-
Optimize the energy system using linear programming to minimize cost while meeting demand with renewable sources and batteries.
|
| 62 |
-
|
| 63 |
-
Args:
|
| 64 |
-
- city_code (str): The code of the city for which energy data is to be optimized.
|
| 65 |
-
- solar_cost (float): The cost of solar capacity per MW.
|
| 66 |
-
- onshore_wind_cost (float): The cost of onshore wind capacity per MW.
|
| 67 |
-
- offshore_wind_cost (float): The cost of offshore wind capacity per MW.
|
| 68 |
-
- river_cost (float): The cost of river (hydro) capacity per MW.
|
| 69 |
-
- battery_cost (float): The cost of battery capacity per MWh.
|
| 70 |
-
- yearly_demand (float): The total yearly demand in TWh/year.
|
| 71 |
-
Returns:
|
| 72 |
-
- fig (Figure): A plotly figure showing the optimized energy dispatch and state of charge (SOC) over time.
|
| 73 |
-
- curtailment_values (list): List of curtailment values for visualization.
|
| 74 |
-
- price_per_hour (list): List of estimated electricity price per hour for visualization.
|
| 75 |
-
"""
|
| 76 |
data, error = get_renewable_energy_data(city_code)
|
| 77 |
if error:
|
| 78 |
st.error(error)
|
| 79 |
return None, None, None
|
| 80 |
|
| 81 |
-
# Convert capacity factor columns to numeric values, handling any errors and filling missing data.
|
| 82 |
for col in data.columns[1:]:
|
| 83 |
data[col] = pd.to_numeric(data[col], errors='coerce')
|
| 84 |
|
| 85 |
-
data = data.fillna(0)
|
| 86 |
|
| 87 |
-
# Extract necessary data for optimization.
|
| 88 |
time_steps = range(len(data['Time']))
|
| 89 |
solar_cf = data['solar hourly capacity factor']
|
| 90 |
onshore_wind_cf = data['onshore_wind hourly capacity factor']
|
|
@@ -92,7 +54,6 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
|
|
| 92 |
river_cf = data['river hourly capacity factor']
|
| 93 |
demand_cf = data['demand hourly capacity factor']
|
| 94 |
|
| 95 |
-
# Define regions and technologies.
|
| 96 |
regions = ['region1']
|
| 97 |
technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']
|
| 98 |
capacity_factor = {
|
|
@@ -102,15 +63,12 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
|
|
| 102 |
'river': river_cf
|
| 103 |
}
|
| 104 |
|
| 105 |
-
# Cost definitions for renewable capacity and battery.
|
| 106 |
renewable_capacity_cost = {'solar': solar_cost, 'onshore_wind': onshore_wind_cost, 'offshore_wind': offshore_wind_cost, 'river': river_cost}
|
| 107 |
battery_cost_per_mwh = battery_cost
|
| 108 |
-
battery_efficiency = 0.9
|
| 109 |
|
| 110 |
-
# Scale demand from capacity factor and yearly demand in TWh/year to hourly demand in MW.
|
| 111 |
demand = demand_cf * yearly_demand / 100 * 1000 * 1000
|
| 112 |
|
| 113 |
-
# Define LP variables for renewable capacities, curtailment, battery capacity, charge, discharge, and state of charge (SOC).
|
| 114 |
renewable_capacity = pulp.LpVariable.dicts("renewable_capacity",
|
| 115 |
[(r, g) for r in regions for g in technologies],
|
| 116 |
lowBound=0, cat='Continuous')
|
|
@@ -122,14 +80,12 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
|
|
| 122 |
battery_discharge = pulp.LpVariable.dicts("battery_discharge", time_steps, lowBound=0, cat='Continuous')
|
| 123 |
SOC = pulp.LpVariable.dicts("SOC", time_steps, lowBound=0, cat='Continuous')
|
| 124 |
|
| 125 |
-
# Define the optimization problem: minimize the total system cost.
|
| 126 |
model = pulp.LpProblem("EnergySystemOptimizationWithBattery", pulp.LpMinimize)
|
| 127 |
|
| 128 |
model += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
|
| 129 |
for r in regions for g in technologies]) + \
|
| 130 |
battery_capacity * battery_cost_per_mwh, "TotalCost"
|
| 131 |
|
| 132 |
-
# Constraints to meet demand, manage curtailment, and update state of charge for the battery.
|
| 133 |
for r in regions:
|
| 134 |
for t in time_steps:
|
| 135 |
model += pulp.lpSum([renewable_capacity[(r, g)] * capacity_factor[g][t]
|
|
@@ -142,10 +98,8 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
|
|
| 142 |
|
| 143 |
model += SOC[t] <= battery_capacity, f"SOCUpperBound_{t}"
|
| 144 |
|
| 145 |
-
# Solve the optimization model.
|
| 146 |
model.solve()
|
| 147 |
|
| 148 |
-
# Extract optimized supply and battery values.
|
| 149 |
supply_solar = solar_cf * renewable_capacity[('region1', 'solar')].varValue
|
| 150 |
supply_onshore_wind = onshore_wind_cf * renewable_capacity[('region1', 'onshore_wind')].varValue
|
| 151 |
supply_offshore_wind = offshore_wind_cf * renewable_capacity[('region1', 'offshore_wind')].varValue
|
|
@@ -156,25 +110,21 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
|
|
| 156 |
SOC_values = [SOC[t].varValue for t in time_steps]
|
| 157 |
curtailment_values = [curtailment[(r, t)].varValue for r in regions for t in time_steps]
|
| 158 |
|
| 159 |
-
|
| 160 |
-
price_per_hour = [100 + 0.05 * demand[t] for t in time_steps] # Base price + factor for demand.
|
| 161 |
|
| 162 |
-
# Normalize state of charge (SOC) to a percentage.
|
| 163 |
max_SOC = max(SOC_values)
|
| 164 |
SOC_normalized = [(soc / max_SOC) * 100 for soc in SOC_values] if max_SOC > 0 else [0] * len(SOC_values)
|
| 165 |
|
| 166 |
-
# Plot energy dispatch.
|
| 167 |
fig_energy = go.Figure()
|
| 168 |
-
fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_solar, mode='lines', stackgroup='one', name='Solar', line=dict(color='#FFD700', width=0)))
|
| 169 |
-
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)))
|
| 170 |
-
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)))
|
| 171 |
-
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)))
|
| 172 |
-
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)))
|
| 173 |
-
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)))
|
| 174 |
-
fig_energy.add_trace(go.Scatter(x=data['Time'], y=-demand, mode='lines', stackgroup='two', name='Demand', line=dict(color='black', width=0)))
|
| 175 |
-
fig_energy.add_trace(go.Scatter(x=data['Time'], y=curtailment_values, mode='lines', stackgroup='two', name='Curtailment', line=dict(color='#aaaaaa', width=0)))
|
| 176 |
|
| 177 |
-
# Layout settings for energy dispatch plot.
|
| 178 |
fig_energy.update_layout(
|
| 179 |
title_text='Power Supply and Demand',
|
| 180 |
title_x=0.5,
|
|
@@ -188,14 +138,11 @@ def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wi
|
|
| 188 |
yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')
|
| 189 |
)
|
| 190 |
|
| 191 |
-
# Return the figures and other outputs.
|
| 192 |
return fig_energy, curtailment_values, price_per_hour
|
| 193 |
|
| 194 |
-
# Streamlit UI for the application
|
| 195 |
st.set_page_config(page_title='Renewable Energy System Optimization', layout='wide')
|
| 196 |
st.title('Renewable Energy System Optimization')
|
| 197 |
|
| 198 |
-
# Explanation of the project
|
| 199 |
st.markdown("""
|
| 200 |
### Overview
|
| 201 |
This application is designed to help researchers and policymakers explore and optimize renewable energy systems for a specified region. By inputting cost parameters for different renewable energy sources and energy storage systems, the application determines the optimal mix of resources to meet energy demand while minimizing cost.
|
|
@@ -203,7 +150,6 @@ The optimization problem is solved using linear programming, ensuring a balance
|
|
| 203 |
The visualizations provided help to better understand how different energy sources contribute to the overall power supply, how energy storage systems are utilized, and the impact of cost variations on energy prices.
|
| 204 |
""")
|
| 205 |
|
| 206 |
-
# Input fields for the user
|
| 207 |
with st.sidebar:
|
| 208 |
st.header('Input Parameters')
|
| 209 |
city_code = st.text_input("Enter City Code", value="")
|
|
@@ -214,17 +160,14 @@ with st.sidebar:
|
|
| 214 |
battery_cost = st.number_input("Battery Cost (¥/MWh)", value=80.0, help="Estimated average cost of battery storage per MWh")
|
| 215 |
yearly_demand = st.number_input("Yearly Power Demand (TWh/year)", value=15.0, help="Total yearly power demand in TWh")
|
| 216 |
|
| 217 |
-
# Button to trigger optimization
|
| 218 |
if st.button('Calculate Optimal Energy Mix'):
|
| 219 |
fig, curtailment_values, price_per_hour = optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand)
|
| 220 |
if fig:
|
| 221 |
st.plotly_chart(fig, use_container_width=True, height=800)
|
| 222 |
|
| 223 |
-
# Additional analysis and visualizations
|
| 224 |
st.markdown("### Additional Analysis")
|
| 225 |
st.markdown("The following plots provide additional insights into the renewable energy mix, curtailment, and electricity price variations.")
|
| 226 |
|
| 227 |
-
# Data for additional visualizations
|
| 228 |
renewable_data = {
|
| 229 |
'Solar': solar_cost,
|
| 230 |
'Onshore Wind': onshore_wind_cost,
|
|
@@ -233,21 +176,17 @@ if st.button('Calculate Optimal Energy Mix'):
|
|
| 233 |
}
|
| 234 |
cost_df = pd.DataFrame(list(renewable_data.items()), columns=['Technology', 'Cost (¥/MW)'])
|
| 235 |
|
| 236 |
-
# Plot cost comparison
|
| 237 |
fig_cost = px.bar(cost_df, x='Technology', y='Cost (¥/MW)', color='Technology', title='Cost Comparison of Different Renewable Technologies', template='plotly_white')
|
| 238 |
st.plotly_chart(fig_cost, use_container_width=True, height=800)
|
| 239 |
|
| 240 |
-
# Plot curtailment over time
|
| 241 |
curtailment_df = pd.DataFrame({"Time": fig.data[0].x, "Curtailment (MW)": curtailment_values})
|
| 242 |
fig_curtailment = px.line(curtailment_df, x='Time', y='Curtailment (MW)', title='Curtailment Over Time', template='plotly_white')
|
| 243 |
st.plotly_chart(fig_curtailment, use_container_width=True, height=800)
|
| 244 |
|
| 245 |
-
# Plot electricity price over time
|
| 246 |
price_df = pd.DataFrame({"Time": fig.data[0].x, "Electricity Price (¥/MWh)": price_per_hour})
|
| 247 |
fig_price = px.line(price_df, x='Time', y='Electricity Price (¥/MWh)', title='Electricity Price Variation Over Time', template='plotly_white')
|
| 248 |
st.plotly_chart(fig_price, use_container_width=True, height=800)
|
| 249 |
|
| 250 |
-
# Correlation analysis of renewable capacity factors
|
| 251 |
if city_code:
|
| 252 |
st.markdown("### Correlation Between Renewable Energy Sources")
|
| 253 |
energy_data_df, error = get_renewable_energy_data(city_code)
|
|
@@ -256,7 +195,6 @@ if st.button('Calculate Optimal Energy Mix'):
|
|
| 256 |
else:
|
| 257 |
numeric_energy_data_df = filter_numeric_columns(energy_data_df)
|
| 258 |
|
| 259 |
-
# Handling missing values by filling them with 0.
|
| 260 |
numeric_energy_data_df = numeric_energy_data_df.fillna(0)
|
| 261 |
|
| 262 |
correlation_matrix = numeric_energy_data_df.corr()
|
|
@@ -265,13 +203,13 @@ if st.button('Calculate Optimal Energy Mix'):
|
|
| 265 |
fig_corr = px.imshow(correlation_matrix, title='Correlation Matrix of Renewable Capacity Factors', labels={'color': 'Correlation'}, template='plotly_white')
|
| 266 |
st.plotly_chart(fig_corr, use_container_width=True, height=800)
|
| 267 |
|
| 268 |
-
# Plot heatmaps for renewable energy and demand characteristics
|
| 269 |
st.markdown("### Heatmap of Renewable Energy and Demand Characteristics")
|
| 270 |
hourly_data = numeric_energy_data_df.copy()
|
| 271 |
-
hourly_data['
|
| 272 |
-
hourly_data['
|
|
|
|
| 273 |
|
| 274 |
-
for column in hourly_data.columns[2:]:
|
| 275 |
pivot_df = hourly_data.pivot_table(index='Hour', columns='Date', values=column, aggfunc=np.mean)
|
| 276 |
fig_heatmap = px.imshow(pivot_df, title=f"Heatmap of {column}", labels={'color': column}, template='plotly_white', aspect='auto')
|
| 277 |
st.plotly_chart(fig_heatmap, use_container_width=True, height=800)
|
|
|
|
| 11 |
|
| 12 |
# Function to fetch renewable energy data
|
| 13 |
def get_renewable_energy_data(city_code):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
url = f"https://energy-sustainability.jp/_ajax/renewable_energy/get/?code={city_code}"
|
| 15 |
response = requests.get(url)
|
| 16 |
if response.status_code != 200:
|
| 17 |
+
return None, "データの取得に失敗しました。"
|
| 18 |
|
| 19 |
data = response.json()
|
| 20 |
if not data:
|
| 21 |
+
return None, "データが見つかりませんでした。"
|
| 22 |
|
|
|
|
| 23 |
base_times = data[next(iter(data))]['x']
|
| 24 |
result_df = pd.DataFrame({"Time": base_times})
|
| 25 |
|
|
|
|
| 26 |
for energy_type, energy_data in data.items():
|
| 27 |
if 'x' in energy_data and 'y' in energy_data:
|
| 28 |
values = energy_data['y']
|
|
|
|
| 32 |
|
| 33 |
# Function to filter numeric columns
|
| 34 |
def filter_numeric_columns(df):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
numeric_df = df.select_dtypes(include=['number'])
|
| 36 |
return numeric_df
|
| 37 |
|
| 38 |
# Function to optimize the energy system
|
| 39 |
def optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
data, error = get_renewable_energy_data(city_code)
|
| 41 |
if error:
|
| 42 |
st.error(error)
|
| 43 |
return None, None, None
|
| 44 |
|
|
|
|
| 45 |
for col in data.columns[1:]:
|
| 46 |
data[col] = pd.to_numeric(data[col], errors='coerce')
|
| 47 |
|
| 48 |
+
data = data.fillna(0)
|
| 49 |
|
|
|
|
| 50 |
time_steps = range(len(data['Time']))
|
| 51 |
solar_cf = data['solar hourly capacity factor']
|
| 52 |
onshore_wind_cf = data['onshore_wind hourly capacity factor']
|
|
|
|
| 54 |
river_cf = data['river hourly capacity factor']
|
| 55 |
demand_cf = data['demand hourly capacity factor']
|
| 56 |
|
|
|
|
| 57 |
regions = ['region1']
|
| 58 |
technologies = ['solar', 'onshore_wind', 'offshore_wind', 'river']
|
| 59 |
capacity_factor = {
|
|
|
|
| 63 |
'river': river_cf
|
| 64 |
}
|
| 65 |
|
|
|
|
| 66 |
renewable_capacity_cost = {'solar': solar_cost, 'onshore_wind': onshore_wind_cost, 'offshore_wind': offshore_wind_cost, 'river': river_cost}
|
| 67 |
battery_cost_per_mwh = battery_cost
|
| 68 |
+
battery_efficiency = 0.9
|
| 69 |
|
|
|
|
| 70 |
demand = demand_cf * yearly_demand / 100 * 1000 * 1000
|
| 71 |
|
|
|
|
| 72 |
renewable_capacity = pulp.LpVariable.dicts("renewable_capacity",
|
| 73 |
[(r, g) for r in regions for g in technologies],
|
| 74 |
lowBound=0, cat='Continuous')
|
|
|
|
| 80 |
battery_discharge = pulp.LpVariable.dicts("battery_discharge", time_steps, lowBound=0, cat='Continuous')
|
| 81 |
SOC = pulp.LpVariable.dicts("SOC", time_steps, lowBound=0, cat='Continuous')
|
| 82 |
|
|
|
|
| 83 |
model = pulp.LpProblem("EnergySystemOptimizationWithBattery", pulp.LpMinimize)
|
| 84 |
|
| 85 |
model += pulp.lpSum([renewable_capacity[(r, g)] * renewable_capacity_cost[g]
|
| 86 |
for r in regions for g in technologies]) + \
|
| 87 |
battery_capacity * battery_cost_per_mwh, "TotalCost"
|
| 88 |
|
|
|
|
| 89 |
for r in regions:
|
| 90 |
for t in time_steps:
|
| 91 |
model += pulp.lpSum([renewable_capacity[(r, g)] * capacity_factor[g][t]
|
|
|
|
| 98 |
|
| 99 |
model += SOC[t] <= battery_capacity, f"SOCUpperBound_{t}"
|
| 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
|
|
|
|
| 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 |
+
price_per_hour = [100 + 0.05 * demand[t] for t in time_steps]
|
|
|
|
| 114 |
|
|
|
|
| 115 |
max_SOC = max(SOC_values)
|
| 116 |
SOC_normalized = [(soc / max_SOC) * 100 for soc in SOC_values] if max_SOC > 0 else [0] * len(SOC_values)
|
| 117 |
|
|
|
|
| 118 |
fig_energy = go.Figure()
|
| 119 |
+
fig_energy.add_trace(go.Scatter(x=data['Time'], y=supply_solar, mode='lines', stackgroup='one', name='Solar', line=dict(color='#FFD700', width=0)))
|
| 120 |
+
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)))
|
| 121 |
+
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)))
|
| 122 |
+
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)))
|
| 123 |
+
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)))
|
| 124 |
+
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)))
|
| 125 |
+
fig_energy.add_trace(go.Scatter(x=data['Time'], y=-demand, mode='lines', stackgroup='two', name='Demand', line=dict(color='black', width=0)))
|
| 126 |
+
fig_energy.add_trace(go.Scatter(x=data['Time'], y=curtailment_values, mode='lines', stackgroup='two', name='Curtailment', line=dict(color='#aaaaaa', width=0)))
|
| 127 |
|
|
|
|
| 128 |
fig_energy.update_layout(
|
| 129 |
title_text='Power Supply and Demand',
|
| 130 |
title_x=0.5,
|
|
|
|
| 138 |
yaxis=dict(showgrid=True, gridwidth=0.5, gridcolor='lightgray')
|
| 139 |
)
|
| 140 |
|
|
|
|
| 141 |
return fig_energy, curtailment_values, price_per_hour
|
| 142 |
|
|
|
|
| 143 |
st.set_page_config(page_title='Renewable Energy System Optimization', layout='wide')
|
| 144 |
st.title('Renewable Energy System Optimization')
|
| 145 |
|
|
|
|
| 146 |
st.markdown("""
|
| 147 |
### Overview
|
| 148 |
This application is designed to help researchers and policymakers explore and optimize renewable energy systems for a specified region. By inputting cost parameters for different renewable energy sources and energy storage systems, the application determines the optimal mix of resources to meet energy demand while minimizing cost.
|
|
|
|
| 150 |
The visualizations provided help to better understand how different energy sources contribute to the overall power supply, how energy storage systems are utilized, and the impact of cost variations on energy prices.
|
| 151 |
""")
|
| 152 |
|
|
|
|
| 153 |
with st.sidebar:
|
| 154 |
st.header('Input Parameters')
|
| 155 |
city_code = st.text_input("Enter City Code", value="")
|
|
|
|
| 160 |
battery_cost = st.number_input("Battery Cost (¥/MWh)", value=80.0, help="Estimated average cost of battery storage per MWh")
|
| 161 |
yearly_demand = st.number_input("Yearly Power Demand (TWh/year)", value=15.0, help="Total yearly power demand in TWh")
|
| 162 |
|
|
|
|
| 163 |
if st.button('Calculate Optimal Energy Mix'):
|
| 164 |
fig, curtailment_values, price_per_hour = optimize_energy_system(city_code, solar_cost, onshore_wind_cost, offshore_wind_cost, river_cost, battery_cost, yearly_demand)
|
| 165 |
if fig:
|
| 166 |
st.plotly_chart(fig, use_container_width=True, height=800)
|
| 167 |
|
|
|
|
| 168 |
st.markdown("### Additional Analysis")
|
| 169 |
st.markdown("The following plots provide additional insights into the renewable energy mix, curtailment, and electricity price variations.")
|
| 170 |
|
|
|
|
| 171 |
renewable_data = {
|
| 172 |
'Solar': solar_cost,
|
| 173 |
'Onshore Wind': onshore_wind_cost,
|
|
|
|
| 176 |
}
|
| 177 |
cost_df = pd.DataFrame(list(renewable_data.items()), columns=['Technology', 'Cost (¥/MW)'])
|
| 178 |
|
|
|
|
| 179 |
fig_cost = px.bar(cost_df, x='Technology', y='Cost (¥/MW)', color='Technology', title='Cost Comparison of Different Renewable Technologies', template='plotly_white')
|
| 180 |
st.plotly_chart(fig_cost, use_container_width=True, height=800)
|
| 181 |
|
|
|
|
| 182 |
curtailment_df = pd.DataFrame({"Time": fig.data[0].x, "Curtailment (MW)": curtailment_values})
|
| 183 |
fig_curtailment = px.line(curtailment_df, x='Time', y='Curtailment (MW)', title='Curtailment Over Time', template='plotly_white')
|
| 184 |
st.plotly_chart(fig_curtailment, use_container_width=True, height=800)
|
| 185 |
|
|
|
|
| 186 |
price_df = pd.DataFrame({"Time": fig.data[0].x, "Electricity Price (¥/MWh)": price_per_hour})
|
| 187 |
fig_price = px.line(price_df, x='Time', y='Electricity Price (¥/MWh)', title='Electricity Price Variation Over Time', template='plotly_white')
|
| 188 |
st.plotly_chart(fig_price, use_container_width=True, height=800)
|
| 189 |
|
|
|
|
| 190 |
if city_code:
|
| 191 |
st.markdown("### Correlation Between Renewable Energy Sources")
|
| 192 |
energy_data_df, error = get_renewable_energy_data(city_code)
|
|
|
|
| 195 |
else:
|
| 196 |
numeric_energy_data_df = filter_numeric_columns(energy_data_df)
|
| 197 |
|
|
|
|
| 198 |
numeric_energy_data_df = numeric_energy_data_df.fillna(0)
|
| 199 |
|
| 200 |
correlation_matrix = numeric_energy_data_df.corr()
|
|
|
|
| 203 |
fig_corr = px.imshow(correlation_matrix, title='Correlation Matrix of Renewable Capacity Factors', labels={'color': 'Correlation'}, template='plotly_white')
|
| 204 |
st.plotly_chart(fig_corr, use_container_width=True, height=800)
|
| 205 |
|
|
|
|
| 206 |
st.markdown("### Heatmap of Renewable Energy and Demand Characteristics")
|
| 207 |
hourly_data = numeric_energy_data_df.copy()
|
| 208 |
+
hourly_data['Time'] = pd.to_datetime(data['Time'], errors='coerce')
|
| 209 |
+
hourly_data['Date'] = hourly_data['Time'].dt.date
|
| 210 |
+
hourly_data['Hour'] = hourly_data['Time'].dt.hour
|
| 211 |
|
| 212 |
+
for column in hourly_data.columns[2:]:
|
| 213 |
pivot_df = hourly_data.pivot_table(index='Hour', columns='Date', values=column, aggfunc=np.mean)
|
| 214 |
fig_heatmap = px.imshow(pivot_df, title=f"Heatmap of {column}", labels={'color': column}, template='plotly_white', aspect='auto')
|
| 215 |
st.plotly_chart(fig_heatmap, use_container_width=True, height=800)
|