Enegy Storage Arbitrage - Penalty function or Linearization
回答済みDear all, following is my problem:
I am implementing a energy storage arbitrage optimization for the running of a battery. I am specifically interested in the charge and discharge profile that maximizes Net Present Value.
I have first coded an optimization using two variable lists : one for all the charging actions and the other for all the discharging actions. Using those two lists, an efficiency for the discharge and a rule that accounts for the maximum and minimum capacity of the battery as well as the maximums on the energy charging and discharge rates (ie, the power), a profit optimisation works just fine.
However, this is not what I am interested in, since the battery has a certain calendar lifetime (= a cap on the operational lifetime in years, whether one charges and discharges a lot or not) and a maximum cycles life (= a cap on the operational lifetime in the number of cycles, which is equivalent to the sum of all the discharging and charging events, ie, our parameters)
I have tried implementing an NPV optimisation as follows, included are the variables:
model = pe.ConcreteModel()
model.IDX = pe.RangeSet(17544)
model.price_values = pe.Param(model.IDX, initialize=lambda model, i: values_list[i-1])
model.discharge = pe.Var(model.IDX, domain=pe.NonNegativeReals)
model.charge = pe.Var(model.IDX,domain=pe.NonNegativeReals)
model.e = pe.Var(model.IDX,bounds =((100-DOD)*0.01*Emax, Emax))
def ObjectiveNPVn2(model,P,E,CAPEX_kW,CAPEX_kWh,OPEX_selector,OPEX_param,max_cyles,cal_life,discount_rate):
SOC_list = np.array([model.e[i].value for i in model.IDX])
SOC_list = SOC_list #!! readd 1/2 for bi yearly below
eq_cycles = sum(1/2*(1/2*Emax) * (model.discharge[i] + model.charge[i]) for i in model.IDX)
#Give_equivalent_cycles(SOC_list) * (100/2*Emax)
years = max_cycles/eq_cycles
NPV_factor = (((1/1+discount_rate)**(years+1)*np.power((1/1+discount_rate),np.power(1/Emax,2))-1)/((1/(1+discount_rate)) - 1))
profit_expr_biyearly = sum(model.price_values[i] * (model.discharge[i]*RTE - model.charge[i]) for i in model.IDX)
OPEX = Give_percent_CAPEX_OPEX(CAPEX_kW,CAPEX_kWh, OPEX_param,P,E)
NPV_expr = NPV_factor * (profit_expr_biyearly/2 - OPEX) - (CAPEX_kW*P + CAPEX_kWh*E)*1000
return NPV_expr
model.obj = pe.Objective(expr = ObjectiveNPVn2( model, Pmax, Emax,capexkw,capexkwh,Opex_selector, opex_param, max_cycles, cal_life,0.08), sense=pe.maximize)
model.power_discharge_constraint = pe.Constraint(model.IDX, rule=lambda model, i: model.discharge[i] <= Pmax)
model.power_charge_constraint = pe.Constraint(model.IDX, rule=lambda model, i: model.charge[i] <= Pmax)
def ebalance_rule(model, i):
if i == 1:
return model.e[i] == (Emax+(100-DOD)*0.01*Emax)/2
else:
return model.e[i] == model.e[i-1] + model.charge[i] - model.discharge[i]
model.energy_storage_capacity_constraint = pe.Constraint(model.IDX, rule = ebalance_rule)
opt = pe.SolverFactory('gurobi')
-
Hi Viktor,
You could use general constraints to model your nonlinear function. However, given the complexity of your function, I would recommend trying a manual piecewise-linear approximation. You can use the addGenConstrPWL() method for this. An example of how to compute a piecewise-linear approximation is given in the gc_pwl_func.py example.
Best regards,
Jaromił0
サインインしてコメントを残してください。
コメント
1件のコメント