Skip to content

Commit fced19a

Browse files
committed
Transfer multiple updates
1 parent 0fb9f6d commit fced19a

File tree

9 files changed

+356
-139
lines changed

9 files changed

+356
-139
lines changed

src/pownet/core/builder.py

Lines changed: 35 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@
22
"""
33

44
from .input import SystemInput
5-
import logging
65

76
from gurobipy import GRB
87
import gurobipy as gp
98
import pandas as pd
109

1110
import pownet.modeling as modeling
12-
from pownet.modeling import PowerSystemModel
1311
from pownet.data_utils import get_unit_hour_from_varnam, get_edge_hour_from_varname
1412

15-
logger = logging.getLogger(__name__)
16-
1713

1814
class ModelBuilder:
1915
"""Build an instance of the Unit Commitment Problem.
@@ -35,7 +31,7 @@ class ModelBuilder:
3531
def __init__(self, inputs: SystemInput) -> None:
3632
self.inputs = inputs
3733
self.timesteps = range(1, self.inputs.sim_horizon + 1)
38-
self.model: gp.Model = None
34+
self.model: gp.Model = gp.Model(self.inputs.model_id)
3935

4036
# Variables
4137
# Thermal units
@@ -51,6 +47,7 @@ def __init__(self, inputs: SystemInput) -> None:
5147
# Energy storage
5248
self.pcharge = gp.tupledict()
5349
self.pdischarge = gp.tupledict()
50+
self.pdischarge_shortfall = gp.tupledict()
5451

5552
self.charge_state = gp.tupledict() # State of charge
5653
self.ucharge = gp.tupledict() # Charging indicator
@@ -92,6 +89,7 @@ def __init__(self, inputs: SystemInput) -> None:
9289

9390
self.load_shortfall_penalty_expr = gp.LinExpr()
9491
self.spin_shortfall_penalty_expr = gp.LinExpr()
92+
self.pdischarge_shortfall_penalty_expr = gp.LinExpr()
9593

9694
# Thermal unit constraints
9795
self.c_link_uvw_init = gp.tupledict()
@@ -161,6 +159,7 @@ def add_variables(self, step_k: int) -> None:
161159
-----------------
162160
- `pcharge`: Power charging an energy storage system. Unit: MW.
163161
- `pdischarge`: Power discharging an energy storage system. Unit: MW.
162+
- `pdischarge_shortfall`: Shortfall in discharging power. Unit: MW.
164163
- `charge_state`: State of charge of an energy storage system. Unit: MWh.
165164
- `ucharge`: Indicator that an ESS is charging. Unitless.
166165
- `udischarge`: Indicator that an ESS is discharging. Unitless.
@@ -205,28 +204,33 @@ def add_variables(self, step_k: int) -> None:
205204
(
206205
"phydro_curtail",
207206
self.inputs.hydro_units,
208-
self.inputs.hydro_contracted_capacity,
207+
self.inputs.hydro_max_capacity,
209208
),
210209
("psolar", self.inputs.solar_units, self.inputs.solar_contracted_capacity),
211210
(
212211
"psolar_curtail",
213212
self.inputs.solar_units,
214-
self.inputs.solar_contracted_capacity,
213+
self.inputs.solar_max_capacity,
215214
),
216215
("pwind", self.inputs.wind_units, self.inputs.wind_contracted_capacity),
217216
(
218217
"pwind_curtail",
219218
self.inputs.wind_units,
220-
self.inputs.wind_contracted_capacity,
219+
self.inputs.wind_max_capacity,
221220
),
222221
("pimp", self.inputs.import_units, self.inputs.import_contracted_capacity),
223222
(
224223
"pimp_curtail",
225224
self.inputs.import_units,
226-
self.inputs.import_contracted_capacity,
225+
self.inputs.import_max_capacity,
227226
),
228227
("pcharge", self.inputs.storage_units, self.inputs.ess_max_charge),
229228
("pdischarge", self.inputs.storage_units, self.inputs.ess_max_discharge),
229+
(
230+
"pdischarge_shortfall",
231+
self.inputs.storage_units,
232+
self.inputs.ess_max_discharge,
233+
),
230234
]
231235

232236
for varname, units, capacity_dict in var_with_u_tuples:
@@ -474,6 +478,12 @@ def set_objfunc(self, step_k: int) -> None:
474478
self.inputs.spin_shortfall_penalty_factor * gp.quicksum(self.spin_shortfall)
475479
)
476480

481+
# Penalize discharge shortfall
482+
self.pdischarge_shortfall_penalty_expr = (
483+
self.inputs.ess_discharge_shortfall_penalty_factor
484+
* gp.quicksum(self.pdischarge_shortfall)
485+
)
486+
477487
################################
478488
# Renewable energy, import sources, and energy storage
479489
################################
@@ -491,6 +501,7 @@ def set_objfunc(self, step_k: int) -> None:
491501
+ self.thermal_curtail_expr
492502
+ self.load_shortfall_penalty_expr
493503
+ self.spin_shortfall_penalty_expr
504+
+ self.pdischarge_shortfall_penalty_expr
494505
+ rnw_import_expr
495506
),
496507
sense=GRB.MINIMIZE,
@@ -503,7 +514,7 @@ def _add_hourly_hydropower_constraints(self, step_k: int) -> None:
503514
model=self.model,
504515
pdispatch=self.phydro,
505516
u=self.uhydro,
506-
type="hydro",
517+
unit_type="hydro",
507518
timesteps=self.timesteps,
508519
step_k=step_k,
509520
units=self.inputs.hydro_unit_node.keys(),
@@ -514,7 +525,7 @@ def _add_hourly_hydropower_constraints(self, step_k: int) -> None:
514525
pdispatch=self.phydro,
515526
pcurtail=self.phydro_curtail,
516527
pcharge=self.pcharge,
517-
type="hydro",
528+
unit_type="hydro",
518529
timesteps=self.timesteps,
519530
step_k=step_k,
520531
units=self.inputs.hydro_unit_node.keys(),
@@ -538,7 +549,7 @@ def _add_daily_hydropower_constraints(self, step_k: int) -> None:
538549
pdispatch=self.phydro,
539550
pcurtail=self.phydro_curtail,
540551
pcharge=self.pcharge,
541-
type="hydro",
552+
unit_type="hydro",
542553
sim_horizon=self.inputs.sim_horizon,
543554
step_k=step_k,
544555
units=self.inputs.daily_hydro_unit_node.keys(),
@@ -549,13 +560,15 @@ def _add_daily_hydropower_constraints(self, step_k: int) -> None:
549560
def _add_hydropower_constraints(self, step_k: int) -> None:
550561

551562
self._add_hourly_hydropower_constraints(step_k=step_k)
552-
self._add_daily_hydropower_constraints(step_k=step_k)
553563

554-
# Does not update every step_k for this constraint
564+
self._add_daily_hydropower_constraints(step_k=step_k)
565+
# With daily formulation, each hour is still limited by turbine capacity
566+
# Not updated every step_k
555567
self.c_link_daily_hydro_pu = modeling.add_c_link_unit_pu_constant(
556568
model=self.model,
557569
pdispatch=self.phydro,
558570
u=self.uhydro,
571+
unit_type="hydro",
559572
timesteps=self.timesteps,
560573
units=self.inputs.daily_hydro_unit_node.keys(),
561574
contracted_capacity=self.inputs.hydro_contracted_capacity,
@@ -593,15 +606,15 @@ def _add_unit_link_pu(self, step_k: int) -> None:
593606
"capacity_df": self.inputs.import_capacity,
594607
},
595608
}
596-
for type, params in unit_params.items():
609+
for unit_type, params in unit_params.items():
597610
setattr(
598611
self,
599-
f"c_link_{type}_pu",
612+
f"c_link_{unit_type}_pu",
600613
modeling.add_c_link_unit_pu(
601614
model=self.model,
602615
pdispatch=params["p"],
603616
u=params["u"],
604-
type=type,
617+
unit_type=unit_type,
605618
timesteps=self.timesteps,
606619
step_k=step_k,
607620
units=params["units"],
@@ -647,7 +660,7 @@ def _add_curtail_ess_constraints(self, step_k: int) -> None:
647660
pdispatch=params["pdispatch"],
648661
pcurtail=params["pcurtail"],
649662
pcharge=params["pcharge"],
650-
type=unit_type,
663+
unit_type=unit_type,
651664
timesteps=self.timesteps,
652665
step_k=step_k,
653666
units=params["units"],
@@ -1003,13 +1016,12 @@ def build(
10031016
self,
10041017
step_k: int,
10051018
init_conds: dict[str, dict],
1006-
) -> PowerSystemModel:
1019+
) -> modeling.PowerSystemModel:
10071020
"""Build the model for the unit commitment problem."""
1008-
self.model = gp.Model(self.inputs.model_id)
10091021
self.add_variables(step_k=step_k)
10101022
self.set_objfunc(step_k=step_k)
10111023
self.add_constraints(step_k=step_k, init_conds=init_conds)
1012-
return PowerSystemModel(self.model)
1024+
return modeling.PowerSystemModel(self.model)
10131025

10141026
def _update_variables(self, step_k: int) -> None:
10151027
"""
@@ -1100,6 +1112,7 @@ def _update_objfunc(self, step_k: int) -> None:
11001112
+ self.thermal_curtail_expr
11011113
+ self.load_shortfall_penalty_expr
11021114
+ self.spin_shortfall_penalty_expr
1115+
+ self.pdischarge_shortfall_penalty_expr
11031116
+ rnw_import_storage_expr
11041117
)
11051118

@@ -1343,64 +1356,4 @@ def update(
13431356
self._update_objfunc(step_k=step_k)
13441357
self._update_constraints(step_k=step_k, init_conds=init_conds)
13451358
self.model.update()
1346-
1347-
return PowerSystemModel(self.model)
1348-
1349-
def print_added_constraints(self):
1350-
added_constrs = []
1351-
not_added_constrs = []
1352-
1353-
constraints_list = [
1354-
"c_link_uvw_init",
1355-
"c_link_uvw",
1356-
"c_link_pthermal",
1357-
"c_link_pu_lower",
1358-
"c_link_pu_upper",
1359-
"c_thermal_curtail",
1360-
"c_min_down_init",
1361-
"c_min_up_init",
1362-
"c_min_down",
1363-
"c_min_up",
1364-
"c_peak_down_bound",
1365-
"c_peak_up_bound",
1366-
"c_ramp_down_init",
1367-
"c_ramp_up_init",
1368-
"c_ramp_down",
1369-
"c_ramp_up",
1370-
"c_ref_node",
1371-
"c_angle_diff",
1372-
"c_kirchhoff",
1373-
"c_flow_balance",
1374-
"c_link_spin",
1375-
"c_link_ppbar",
1376-
"c_reserve_req",
1377-
"c_hydro_curtail_ess",
1378-
"c_daily_hydro_curtail_ess",
1379-
"c_solar_curtail_ess",
1380-
"c_wind_curtail_ess",
1381-
"c_import_curtail_ess",
1382-
"c_link_hydro_pu",
1383-
"c_link_daily_hydro_pu",
1384-
"c_link_solar_pu",
1385-
"c_link_wind_pu",
1386-
"c_link_import_pu",
1387-
"c_hydro_limit_daily",
1388-
"c_link_ess_charge",
1389-
"c_link_discharge",
1390-
"c_link_ess_state",
1391-
"c_unit_ess_balance_init",
1392-
"c_unit_ess_balance",
1393-
]
1394-
1395-
for attr_name in constraints_list:
1396-
if getattr(self, attr_name):
1397-
added_constrs.append(attr_name)
1398-
else:
1399-
not_added_constrs.append(attr_name)
1400-
1401-
log_message = "\nAdded constraints:\n"
1402-
log_message += "\n".join(added_constrs)
1403-
log_message += "\n\nNot added constraints:\n"
1404-
log_message += "\n".join(not_added_constrs)
1405-
1406-
logger.warning(log_message)
1359+
return modeling.PowerSystemModel(self.model)

0 commit comments

Comments
 (0)