pythoncplexbatterypyomomixed-integer-programming

Pyomo: Minimize for Max Value in Vector


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


Solution

  • 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:

    1. if ismax[i] == 0 then P_GridMax[i] == 0.
    2. if 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.