Skip to content

Commit 92bf1e6

Browse files
committed
(fix): dispatch plot, allow for hydro_capacity_min to be empty (assign 0 if yes)
1 parent 021f0fd commit 92bf1e6

File tree

4 files changed

+242
-12
lines changed

4 files changed

+242
-12
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ user_inputs/*
119119
# Ignore subfolders in the analysis folder
120120
src/analysis/decom_files/*
121121
src/analysis/results/*
122-
scripts/*
123122

124123
# Pownet generated files
125124
/model_library/*/pownet_*.csv

scripts/run_hydrochipps.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import os
2+
from pownet.core import Simulator, DataProcessor, OutputProcessor
3+
4+
##### User inputs #####
5+
scen_path = ".\\model_library"
6+
results_path = ".\\results"
7+
to_process_inputs = True
8+
sim_horizon = 24*7
9+
model_name = "cumberland"
10+
steps_to_run = 3 # Default is None
11+
do_plot = True
12+
13+
14+
if to_process_inputs:
15+
data_processor = DataProcessor(
16+
input_folder=scen_path, model_name=model_name, year=2016, frequency=50
17+
)
18+
data_processor.execute_data_pipeline()
19+
20+
simulator = Simulator(
21+
input_folder=scen_path,
22+
model_name=model_name,
23+
model_year=2016,
24+
spin_reserve_factor=0,
25+
load_shortfall_penalty_factor=1000,
26+
load_curtail_penalty_factor=1,
27+
spin_shortfall_penalty_factor=1000,
28+
line_capacity_factor=1,
29+
line_loss_factor=0
30+
)
31+
32+
simulator.run(
33+
sim_horizon=sim_horizon,
34+
steps_to_run=steps_to_run,
35+
# solver='gurobi',
36+
)
37+
38+
# Write the simulation results
39+
output_folder = results_path + '\\' + model_name
40+
if not os.path.exists(output_folder):
41+
os.makedirs(output_folder)
42+
simulator.write_results(output_folder)
43+
44+
simulator.plot_fuelmix("area", output_folder)
45+
46+
output_processor = OutputProcessor()
47+
hourly_demand = output_processor.get_hourly_demand(simulator.inputs.demand)
48+
49+
## old code
50+
# #%%
51+
# import os
52+
# from datetime import datetime
53+
# from pownet.core import (
54+
# DataProcessor,
55+
# SystemInput,
56+
# ModelBuilder,
57+
# SystemRecord,
58+
# OutputProcessor,
59+
# Visualizer,
60+
# )
61+
# from pownet.data_utils import create_init_condition
62+
#
63+
# scen_path = ".\\model_library"
64+
# ##### User inputs #####
65+
# to_process_inputs = True
66+
# sim_horizon = 24*7
67+
# model_name = "cumberland"
68+
# steps_to_run = 3 # Default is None
69+
# do_plot = True
70+
# #######################
71+
#
72+
# if to_process_inputs:
73+
# data_processor = DataProcessor(
74+
# input_folder=scen_path, model_name=model_name, year=2016, frequency=50
75+
# )
76+
# data_processor.execute_data_pipeline()
77+
#
78+
# inputs = SystemInput(
79+
# input_folder=scen_path,
80+
# model_name=model_name,
81+
# year=2016,
82+
# sim_horizon=sim_horizon,
83+
# spin_reserve_factor=0,
84+
# load_shortfall_penalty_factor=1000,
85+
# load_curtail_penalty_factor=1,
86+
# spin_shortfall_penalty_factor=1000,
87+
# line_capacity_factor=1,
88+
# line_loss_factor=0
89+
# )
90+
# inputs.load_and_check_data()
91+
#
92+
#
93+
# model_builder = ModelBuilder(inputs)
94+
# record = SystemRecord(inputs)
95+
#
96+
# build_times = []
97+
# opt_times = []
98+
# objvals = []
99+
# init_conditions = create_init_condition(inputs.thermal_units) # init conditions should be empty dict if we dont have any thermal generators
100+
#
101+
# if steps_to_run is None:
102+
# steps_to_run = 10 # 365 - (sim_horizon // 24 - 1)
103+
#
104+
# start_day = 1
105+
# for step_k in range(start_day, steps_to_run):
106+
# start_time = datetime.now()
107+
# if step_k == start_day:
108+
# power_system_model = model_builder.build(
109+
# step_k=step_k,
110+
# init_conds=init_conditions,
111+
# )
112+
# else:
113+
# power_system_model = model_builder.update(
114+
# step_k=step_k,
115+
# init_conds=init_conditions,
116+
# )
117+
# build_times.append((datetime.now() - start_time).total_seconds())
118+
#
119+
# power_system_model.model.write(str(step_k) + '.lp')
120+
# power_system_model.optimize(mipgap=0.001)
121+
#
122+
# # Raise an error if the model is not feasible
123+
# if not power_system_model.check_feasible():
124+
# raise ValueError("Model is not feasible.")
125+
#
126+
# objvals.append(power_system_model.get_objval())
127+
# opt_times.append(power_system_model.get_runtime())
128+
#
129+
# record.keep(
130+
# runtime=power_system_model.get_runtime(),
131+
# objval=power_system_model.get_objval(),
132+
# solution=power_system_model.get_solution(),
133+
# lmp=power_system_model.solve_for_lmp(),
134+
# step_k=step_k,
135+
# )
136+
# init_conditions = record.get_init_conds()
137+
#
138+
# import os
139+
# output_folder = ".//outputs"
140+
# if not os.path.exists(output_folder):
141+
# os.makedirs(output_folder)
142+
# record.write_simulation_results(output_folder)
143+
#
144+
#
145+
#
146+
# # output_folder = ".//outputs"
147+
# # if not os.path.exists(output_folder):
148+
# # os.makedirs(output_folder)
149+
# # results.write_results(output_folder)
150+
#
151+
# import pandas as pd
152+
# # Process the results
153+
# output_processor = OutputProcessor()
154+
# output_processor.load(inputs=inputs)
155+
# # output_processor = OutputProcessor(inputs)
156+
# node_var_df = record.get_node_variables()
157+
# output_processor.get_hourly_generation(node_var_df)
158+
# # output_processor.load_from_dataframe(node_var_df)
159+
#
160+
# output_processor.get_thermal_unit_hourly_dispatch(node_var_df)
161+
# a = output_processor.get_unit_hourly_generation(node_var_df)
162+
# # Visualize the results
163+
# if do_plot:
164+
# visualizer = Visualizer(inputs.model_id)
165+
# if steps_to_run <= 7:
166+
# visualizer.plot_fuelmix_bar(
167+
# dispatch=output_processor.get_hourly_generation(node_var_df),
168+
# demand=output_processor.get_hourly_demand(inputs.demand),
169+
# )
170+
# visualizer.plot_lmp(record.lmp_df)
171+
# else:
172+
# visualizer = Visualizer(inputs.model_id)
173+
# visualizer.plot_fuelmix_area(
174+
# dispatch=output_processor.get_daily_dispatch(),
175+
# demand=output_processor.get_daily_demand(),
176+
# )
177+
#
178+
#
179+
# # Save other modeling statistics
180+
# import os
181+
# from pownet.folder_utils import get_output_dir
182+
# import pandas as pd
183+
#
184+
# folder_dir = get_output_dir()
185+
# prefix_name = "warmstart"
186+
# df = pd.DataFrame(
187+
# {
188+
# "build_time": build_times,
189+
# "opt_time": opt_times,
190+
# "objval": objvals,
191+
# }
192+
# )
193+
# df.to_csv(
194+
# os.path.join(
195+
# folder_dir,
196+
# f"branch{prefix_name}_{inputs.model_name}_{sim_horizon}_modelstats.csv",
197+
# ),
198+
# index=False,
199+
# )

src/pownet/core/visualizer.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import matplotlib as mpl
88
from matplotlib.colors import ListedColormap
99
from matplotlib.lines import Line2D
10+
from matplotlib.ticker import MaxNLocator
1011
import matplotlib.pyplot as plt
1112
import numpy as np
1213
import pandas as pd
@@ -55,12 +56,21 @@ def plot_fuelmix_bar(
5556
)
5657
ax.set_xlabel(dispatch.index.name)
5758

59+
# Use MaxNLocator to dynamically adjust the number of x-axis labels
60+
ax.xaxis.set_major_locator(MaxNLocator(integer=True, prune='both'))
61+
ticks = ax.get_xticks()
62+
ticks = ticks.astype(int)
63+
if total_timesteps - 1 not in ticks:
64+
ticks = list(ticks) + [total_timesteps - 1]
65+
ax.set_xticks(ticks)
66+
ax.set_xticklabels([dispatch.index[i] for i in ticks], rotation=0)
67+
5868
# Plot formatting
5969
legend = fig.legend(
6070
loc="outside lower center",
6171
ncols=5,
6272
fontsize="small",
63-
bbox_to_anchor=(0.5, -0.12),
73+
bbox_to_anchor=(0.5, -0.08),
6474
)
6575
ax.set_ylabel("Power (MW)")
6676
ax.set_ylim(top=(demand[:total_timesteps].max() * 1.30))
@@ -73,6 +83,7 @@ def plot_fuelmix_bar(
7383
bbox_inches="tight",
7484
dpi=350,
7585
)
86+
plt.tight_layout()
7687
plt.show()
7788

7889
def plot_fuelmix_area(
@@ -109,14 +120,24 @@ def plot_fuelmix_area(
109120
)
110121
ax.set_xlabel(dispatch.index.name)
111122

123+
# Use MaxNLocator to dynamically adjust the number of x-axis labels
124+
ax.xaxis.set_major_locator(MaxNLocator(integer=True, prune='both'))
125+
ticks = ax.get_xticks()
126+
ticks = ticks.astype(int)
127+
ticks = [tick for tick in ticks if tick < len(dispatch.index)]
128+
if total_timesteps - 1 not in ticks:
129+
ticks.append(total_timesteps - 1)
130+
ax.set_xticks(ticks)
131+
ax.set_xticklabels([dispatch.index[i] for i in ticks], rotation=0)
132+
112133
legend = fig.legend(
113134
loc="outside lower center",
114135
ncols=5,
115136
fontsize="small",
116-
bbox_to_anchor=(0.5, -0.12),
137+
bbox_to_anchor=(0.5, -0.08),
117138
)
118139
ax.set_ylabel("Power (MW)")
119-
ax.set_ylim(top=(demand[:total_timesteps].max() * 1.30).values[0])
140+
ax.set_ylim(top=(demand[:total_timesteps].max() * 1.30))
120141

121142
if output_folder is not None:
122143
figure_name = f"{self.model_id}_fuelmix.png"
@@ -126,6 +147,7 @@ def plot_fuelmix_area(
126147
bbox_inches="tight",
127148
dpi=350,
128149
)
150+
plt.tight_layout()
129151
plt.show()
130152

131153
def plot_thermal_units(

src/pownet/optim_model/constraints/nondispatch_constr.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,23 @@ def add_c_hydro_limit_weekly(
231231
name=cname,
232232
)
233233

234-
# Lower bound constraint
235-
constraints[cname_min] = model.addConstr(
236-
gp.quicksum(
237-
phydro[hydro_unit, t]
238-
for t in range(1 + (current_week - 1) * 168, current_week * 168 + 1)
234+
if hydro_capacity_min.empty:
235+
constraints[cname_min] = model.addConstr(
236+
gp.quicksum(
237+
phydro[hydro_unit, t]
238+
for t in range(1 + (current_week - 1) * 168, current_week * 168 + 1)
239+
)
240+
>= 0,
241+
name=cname_min,
242+
)
243+
else:
244+
# Lower bound constraint
245+
constraints[cname_min] = model.addConstr(
246+
gp.quicksum(
247+
phydro[hydro_unit, t]
248+
for t in range(1 + (current_week - 1) * 168, current_week * 168 + 1)
249+
)
250+
>= hydro_capacity_min.loc[week, hydro_unit],
251+
name=cname_min,
239252
)
240-
>= hydro_capacity_min.loc[week, hydro_unit],
241-
name=cname_min,
242-
)
243253
return constraints

0 commit comments

Comments
 (0)