I am optimizing the behavior of battery storage combined with solar PV to generate the highest possible revenue stream. I now want to add one more revenue stream: Peak Shaving (or Demand Charge Reduction)
My approach is as follows:
Now, does anybody see a solution for how to formulate such a constraint (P_GridMax)? I already updated my objective function and defined P_Grid. Any other approach would also be welcome.
This is the relevant part of my model, with P_xxx = power flow vectors, C_xxx = price vectors, ...
m.P_Grid = Var(m.i_TIME, within = NonNegativeReals)
m.P_GridMax = Var(m.i_TIME, within = NonNegativeReals)
# Minimize electricity bill
def Total_cost(m):
return ... + sum(m.P_GridMax[i] * m.C_PowerCosts[i] for i in m.i_TIME) - ...
m.Cost = Objective(rule=Total_cost)
## Peak Shaving constraints
def Grid_Def(m,i):
return m.P_Grid[i] = m.P_GridLoad[i] + m.P_GridBatt[i]
m.Bound_Grid = Constraint(m.i_TIME,rule=Grid_Def)
def Peak_Rule(m,i):
????
????
????
????
m.Bound_Peak = Constraint(m.i_TIME,rule=Peak_Rule)
Thank you very much in advance! Please be aware that I have very little experience with python/pyomo coding, I would really appreciate you giving extensive explanations :)
Best, Mathias
Here is one way to do this:
introduce a binary helper variable ismax[i] for i in i_TIME
. This variable is 1 if the maximum is obtained in period i
and 0 otherwise. Then obviously you have a constraint sum(ismax[i] for i in i_TIME) == 1
: the maximum must be attained in exactly one period.
Now you need two additional constraints:
ismax[i] == 0
then P_GridMax[i] == 0
.ismax[i] == 1
then for all j in i_TIME
we must have P_GridMax[i] >= P_GridMax[j]
.The best way to formulate this would be to use indicator constraints but I don't know Pyomo so I don't know whether it supports that (I suppose it does but I don't know how to write them). So I'll give instead a big-M formulation.
For this formulation you need to define a constant M
so that P_Grid[i]
can not exceed that value for any i
. With that the first constraint becomes
P_GridMax[i] <= M * ismax[i]
That constraint forces P_GridMax[i]
to 0 unless ismax[i] == 1
. For ismax[i] == 1
it is redundant.
The second constraint would be for all j in i_TIME
P_GridMax[i] + M * (1 - ismax[i]) >= P_Grid[j]
If ismax[i] == 0
then the left-hand side of this constraint is at least M
, so by the definition of M
it will be satisfied no matter what the value of P_GridMax[i]
is (the first constraint forces P_Grid[i] == 0
in that case). For ismax[i] == 1
the left-hand side of the constraint becomes just P_GridMax[i]
, exactly what we want.