Under certain PPAs, there are penalties for selling power to the market when the PPA capacity is not fully met. I have developed a constraint (with the kind help of
Konstantinos) which prevents market sales while the PPA capacity is not met.
This works when I run the following:
m = network.optimize.create_model()
m = constrain_merchant_sell_below_PPA(m)
network.optimize.solve_model(solver_name="highs", solver_options={'log_to_console': False})
However, it takes too long to run this at full resolution. It is much faster if I split it up using a rolling horizon optimisation. Unfortunately, the solution is not correct when I do this.
The constraint is implemented for all timeblocks except when the RE_to_PPA_p = 100.This is how I run the model with rolling horizons.
def custom_constraints(network):
#add custom constraints
m = network.optimize.create_model()
m = constrain_merchant_sell_below_PPA(m)
network.optimize.optimize_with_rolling_horizon(snapshots=network.snapshots, horizon=672, overlap=2, extra_functionality=custom_constraints(network), solver_name='highs', solver_options={'log_to_console': False})
I have also manually run a rolling horizon optimisation, but the outcome is the same as the use of optimize_with_rolling_horizon (works except for when RE_to_PPA = 100).
Does anyone have any thoughts on what could be causing the difference between the rolling horizon optimisation and the solve_model run?This is the constraint I have implemented:
def constrain_merchant_sell_below_PPA(m)
# Define the PPA offtake capacity
LOAD_PPA_MW = 200
# Define the variable time series of power to the PPA
RE_to_PPA_p = m.variables["Link-p"].sel(Link=RE_to_PPA)
# define the variable of the time series of the link to sell power to merchant sales
RE_to_MerchantSell_p = m.variables["Link-p"].sel(Link=RE_to_MerchantSell)
# Merchant_sell is the generators which buys power (flexible negative generator).
# It has Commitable=TRUE, so we can get its status
sel_dict = {'Generator-com':'Merchant_sell'}
Merchant_sell_status = m.variables['Generator-status'].sel(sel_dict)
# We want to prevent the merchant_sell generator from ever generating if the power to the
#PPA is < 200MW
lhs = Merchant_sell_status * LOAD_PPA_MW
rhs = RE_to_PPA_p
m.add_constraints(lhs <= rhs, name=f'Merchant_sell_below_ppa_{auction}')
return(m)
Sketch of the network is attached.