Skip to content

Commit 8cbbb9a

Browse files
committed
feat(hydro): lower bound for power output of hydro units
1 parent 91394f4 commit 8cbbb9a

File tree

3 files changed

+76
-12
lines changed

3 files changed

+76
-12
lines changed

src/pownet/builder/hydro.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def __init__(self, model: gp.Model, inputs: SystemInput):
5858

5959
# Constraints
6060
self.c_hourly_hydro_ub = gp.tupledict()
61+
self.c_hourly_hydro_lb = gp.tupledict()
6162
self.c_hydro_limit_daily = gp.tupledict()
6263
self.c_hydro_limit_weekly = gp.tupledict()
6364

@@ -83,7 +84,11 @@ def add_variables(self, step_k: int) -> None:
8384
self.daily_phydro = self.model.addVars(
8485
self.inputs.daily_hydro_unit_node.keys(),
8586
self.timesteps,
86-
lb=0,
87+
lb={
88+
(unit, t): self.inputs.hydro_contracted_capacity_min[unit]
89+
for unit in self.inputs.daily_hydro_unit_node.keys()
90+
for t in self.timesteps
91+
},
8792
ub={
8893
(unit, t): self.inputs.hydro_contracted_capacity[unit]
8994
for unit in self.inputs.daily_hydro_unit_node.keys()
@@ -96,7 +101,11 @@ def add_variables(self, step_k: int) -> None:
96101
self.weekly_phydro = self.model.addVars(
97102
self.inputs.weekly_hydro_unit_node.keys(),
98103
self.timesteps,
99-
lb=0,
104+
lb={
105+
(unit, t): self.inputs.hydro_contracted_capacity_min[unit]
106+
for unit in self.inputs.weekly_hydro_unit_node.keys()
107+
for t in self.timesteps
108+
},
100109
ub={
101110
(unit, t): self.inputs.hydro_contracted_capacity[unit]
102111
for unit in self.inputs.weekly_hydro_unit_node.keys()
@@ -158,6 +167,15 @@ def add_constraints(self, step_k: int, init_conds: dict, **kwargs) -> None:
158167
contracted_capacity_dict=self.inputs.hydro_contracted_capacity,
159168
)
160169

170+
self.c_hourly_hydro_lb = nondispatch_constr.add_c_hourly_unit_lb(
171+
model=self.model,
172+
pdispatch=self.hourly_phydro,
173+
unit_type="hydro",
174+
timesteps=self.timesteps,
175+
units=self.inputs.hydro_unit_node.keys(),
176+
contracted_capacity_min_dict=self.inputs.hydro_contracted_capacity_min,
177+
)
178+
161179
# Daily upper bound
162180
self.c_hydro_limit_daily = nondispatch_constr.add_c_hydro_limit_daily(
163181
model=self.model,

src/pownet/input.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def __init__(
160160

161161
# Hydropower (hourly and daily timeseries)
162162
self.hydro_contracted_capacity: dict[str, float] = {}
163+
self.hydro_contracted_capacity_min: dict[str, float] = {}
163164
self.hydro_capacity: pd.DataFrame = pd.DataFrame()
164165
self.hydro_min_capacity: pd.DataFrame = pd.DataFrame()
165166
self.hydro_max_capacity: dict[str, float] = {}
@@ -611,11 +612,13 @@ def _load_contracted_capacity(self) -> None:
611612
if nondispatch_df.empty:
612613
return
613614
# Replace -1 in the contracted_capacity column to GRB.INFINITY
614-
nondispatch_df["contracted_capacity"] = (
615-
nondispatch_df["contracted_capacity"]
616-
.astype(float)
617-
.replace(-1, GRB.INFINITY)
618-
)
615+
for col in ["contracted_capacity", "contracted_capacity_min"]:
616+
nondispatch_df[col] = (
617+
nondispatch_df[col]
618+
.astype(float)
619+
.replace(-1, GRB.INFINITY)
620+
.fillna(0)
621+
)
619622

620623
unit_types = {
621624
"hydro": self.hydro_unit_node,
@@ -646,6 +649,20 @@ def _load_contracted_capacity(self) -> None:
646649
.to_dict()
647650
)
648651

652+
# Add daily hydro units to the contracted capacity under "hydro"
653+
self.hydro_contracted_capacity_min.update(
654+
nondispatch_df.loc[nondispatch_df["name"].isin(self.daily_hydro_unit_node)]
655+
.set_index("name")["contracted_capacity_min"]
656+
.to_dict()
657+
)
658+
659+
# Add daily hydro units to the contracted capacity under "hydro"
660+
self.hydro_contracted_capacity_min.update(
661+
nondispatch_df.loc[nondispatch_df["name"].isin(self.weekly_hydro_unit_node)]
662+
.set_index("name")["contracted_capacity_min"]
663+
.to_dict()
664+
)
665+
649666
def load_data(self):
650667
"""Load the input data for the power system model.
651668
Timeseries are loaded as dataframes with the index starting at 1.

src/pownet/optim_model/constraints/nondispatch_constr.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,38 @@ def add_c_hourly_unit_ub(
3636
name=f"hourly_{unit_type}_ub",
3737
)
3838

39+
def add_c_hourly_unit_lb(
40+
model: gp.Model,
41+
pdispatch: gp.tupledict,
42+
unit_type: str,
43+
timesteps: range,
44+
units: list,
45+
contracted_capacity_min_dict: dict[str, float],
46+
) -> gp.tupledict:
47+
"""
48+
Add constraints to define the hourly availability of non-dispatchable units. This is limited by the
49+
contracted_capacity
50+
51+
Args:
52+
model (gp.Model): The optimization model.
53+
pdispatch (gp.tupledict): The dispatch variable.
54+
unit_type (str): The type of unit ("wind", "solar", "hydro", "import").
55+
timesteps (range): The range of timesteps.
56+
units (list): The list of units.
57+
contracted_capacity_min_dict (dict[str, float]): The contracted minimum capacity of the unit.
58+
59+
Returns:
60+
gp.tupledict: The constraints linking the dispatch variable and the unit status variable.
61+
"""
62+
return model.addConstrs(
63+
(
64+
pdispatch[unit, t] >= contracted_capacity_min_dict[unit]
65+
for unit in units
66+
for t in timesteps
67+
),
68+
name=f"hourly_{unit_type}_lb",
69+
)
70+
3971

4072
def add_c_link_unit_pu(
4173
model: gp.Model,
@@ -45,6 +77,7 @@ def add_c_link_unit_pu(
4577
timesteps: range,
4678
units: list,
4779
contracted_capacity_dict: dict[str, float],
80+
contracted_capacity_min_dict: dict[str, float],
4881
) -> gp.tupledict:
4982
"""
5083
Add constraints to link the dispatch variable and the unit status variable.
@@ -183,7 +216,6 @@ def add_c_hydro_limit_weekly(
183216
sim_horizon: int,
184217
hydro_units: list,
185218
hydro_capacity: pd.DataFrame,
186-
# hydro_capacity_min: pd.DataFrame,
187219
) -> gp.tupledict:
188220
"""
189221
Defines the weekly limit (lower and upper bounds) of hydro generation.
@@ -218,7 +250,6 @@ def add_c_hydro_limit_weekly(
218250
for week in range(step_k, step_k + max_week):
219251
for hydro_unit in hydro_units:
220252
cname = f"hydro_limit_weekly_ub[{hydro_unit},{week}]"
221-
cname_min = f"hydro_limit_weekly_lb[{hydro_unit},{week}]"
222253
current_week = week - step_k + 1
223254

224255
# Upper bound constraint
@@ -240,12 +271,11 @@ def add_c_hydro_limit_weekly_lb(
240271
step_k: int,
241272
sim_horizon: int,
242273
hydro_units: list,
243-
# hydro_capacity: pd.DataFrame,
244274
hydro_capacity_min: pd.DataFrame,
245275
) -> gp.tupledict:
246276
"""
247277
Defines the weekly limit (lower and upper bounds) of hydro generation.
248-
Assumes that a certain amount of water is available for hydropower generation each day.
278+
Assumes that a certain amount of water is available for hydropower generation each week.
249279
250280
Args:
251281
model (gp.Model): The optimization model
@@ -275,7 +305,6 @@ def add_c_hydro_limit_weekly_lb(
275305
max_week = sim_horizon // 168
276306
for week in range(step_k, step_k + max_week):
277307
for hydro_unit in hydro_units:
278-
# cname = f"hydro_limit_weekly_ub[{hydro_unit},{week}]"
279308
cname_min = f"hydro_limit_weekly_lb[{hydro_unit},{week}]"
280309
current_week = week - step_k + 1
281310

0 commit comments

Comments
 (0)