@@ -234,6 +234,11 @@ def solve_release_from_dispatch(
234234 6. Definition of spill_bar
235235 spill_bar = INFLOW_t + storage_t-1 - STORAGE_MAX - release_t
236236
237+ 7. Spill cannot be positive when storage is less than max_storage
238+ spill <= 0 when storage_t < max_storage
239+ spill >= 0 when storage_t > max_storage
240+
241+
237242 Note that the objective function is not linear because of the absolute value.
238243 To make it linear, we introduce a new variable mismatch_t and rewrite
239244 the objective function as:
@@ -265,10 +270,15 @@ def solve_release_from_dispatch(
265270
266271 # Unbounded hydropower
267272 unb_hourly_hydropower = model .addVar (lb = 0.0 , name = "unb_hourly_hydropower" )
273+
268274 # Bounded hydropower
269275 hourly_hydropower = model .addVar (lb = 0.0 , name = "hourly_hydropower" )
270276 daily_hydropower = model .addVar (lb = 0.0 , name = "daily_hydropower" )
271277
278+ # Binary variable for spill condition
279+ delta = model .addVar (vtype = gp .GRB .BINARY , name = "delta" )
280+ big_m = storage_max * 2
281+
272282 # Set objective
273283 model .setObjective (mismatch , gp .GRB .MINIMIZE )
274284
@@ -335,6 +345,17 @@ def solve_release_from_dispatch(
335345 # (6) Define spill
336346 model .addConstr (spill == gp .max_ (0 , spill_bar ), name = "c_spill" )
337347
348+ # (7) Model the conditional spill constraint using binary variable and big-M
349+ model .addConstr (spill <= big_m * delta , name = "c_spill_conditional_upper" )
350+ model .addConstr (
351+ storage <= storage_max - 1e-6 + big_m * (1 - delta ),
352+ name = "c_storage_less_than_max" ,
353+ ) # 1e-6 is a small tolerance (epsilon)
354+ model .addConstr (
355+ storage >= storage_max + 1e-6 - big_m * delta ,
356+ name = "c_storage_greater_than_equal_to_max" ,
357+ ) # 1e-6 is a small tolerance (epsilon)
358+
338359 ############################
339360 # Solve the optimization problem
340361 ############################
0 commit comments