0

So I am currently trying to optimize the costs for energy in a household. The optimization is based on a cost factor function which I am trying to minimize.

model = ConcreteModel()
model.t = RangeSet(0, 8759)

def costs(model, t):
    return sum(model.cost_factor[t] * model.elec_grid[t] for t in model.t)
model.costs = Objective(rule = costs, sense = minimize)

Due to pv overproduction being a thing I try to negate by using these functions:

model.elec_consumption = Param(model.t, initialize = df['Consumption'])
model.pv = Param(model.t, initialize = df['PV'])
model.excess_pv = Var(model.t, within = NonNegativeReals, initialize = 0)
model.demand = Var(model.t, initialize = 0, within = NonNegativeReals)

def pv_overproduction(model, t):
    return model.excess_pv[t] >= model.pv[t] - model.demand[t]
model.pv_overproduction = Constraint(model.t, rule = pv_overproduction)
def lastdeckung(model, t):
    return (model.pv[t] - model.excess_pv[t]) + model.elec_grid[t] == model.demand[t]
model.lastdeckung = Constraint(model.t, rule = lastdeckung)

The problem is when the cost factor is negative the optimizer puts model.excess_pv very high so he can crank up the model.elec_grid variable in an effort to minimize the cost factor. That is obviously not the intention but so far I wasnt able to find a better way to calculate the excess pv. An easy fix would technically be to just have a cost factor which is constantly positive but sadly thats not an option.

I'd appreciate if someone had an idea how to fix this.

The basics are that I want to maximize the usage of the pv electricity in order to reduce costs. At some points there is to mooch pv in the system so in order for that optimization to still work I need to get rid of the excess.

    return model.demand[t] == model.elec_consumption[t]
model.demand_rule = Constraint(model.t, rule = demand_rule)

This is the demand. Technically there are more functions but for the the problem solving that is irrelevant. The main problem is that this function doesnt work due to the cost factor being negative sometimes model.excess_pv[t] >= model.pv[t] - model.demand[t] Excess_pv aswell as model.demand are variables wheres model.pv is a parameter.

So as far as I got in my problemsearching I need to change my overproduction function in a way that it uses the value from pv - excess_pv if the value is > 0 and should the value be < = 0 its supposed to be zero.

saschav
  • 37
  • 6
  • I don't understand why pv overproduction is a constraint of the optimizer rather than just a cost factor. For clarity, could you write out the linear programming problem you're trying to solve? – Andrew Holmgren Jan 18 '22 at 22:42
  • Thats because I am not trying to get additional revenue by injecting the pv-overproduction into the electricity grid. The functions just exist in order to stop the optimization from crashing when too much pv energy is produced. – saschav Jan 19 '22 at 09:22
  • 1
    Could you please add a minimum reproducible example? I got an idea to solve, but need to check something first. The problem is that your overproduction is a `Var` and is upper-unbounded. You need to change that `Var` to be exactly to the subtraction of `pv[t]` and `demand[t]` when that substraction is NonNegative. For negative values, it is just zero. I think you can use the `pyomo.Expression.Piecewise` to achieve that. – pybegginer Jan 20 '22 at 00:18
  • I tried that approach before by just saying the variables is supposed to be within NonNegative but that just gabe me no primal feasable solution. Im gonna read up on piecewise functions in pyomo an try that. – saschav Jan 20 '22 at 14:56

2 Answers2

1

I think the easiest way to do this is to probably just penalize excess production to a greater extent than the maximally negative cost factor.

Why can't you...

excess_pentalty = max(-min(cost) + epsilon, 0)  # use maximin to prevent odd behavior if there is no negative cost, which might lead to a negative penalty...

# make obj from components, so we can inspect true cost (w/o penalty) later...
cost = sum(model.cost_factor[t] * model.elec_grid[t] for t in model.t)
overproduction_pentaly = sum(excess_penalty * model.excess_pv[t] for t in model.t)

model.obj = Objective(expr= cost + overproduction_penalty, sense = minimize)

and later if you want the cost independently, you can just check the value of cost, which is a legal pyomo expression.

value(cost)

I think you could also add the expression as a model component, if that is important...

model.cost = ...
model.overproduction_penalty = ...
AirSquid
  • 10,214
  • 2
  • 7
  • 31
0

So the idea of a piecewise function is definitely an option for the problem mentioned in this post. It is quite a fancy and complicated solution though. The idea of penalties is much easier and it also showed a few more flaws in my code. Due to negative cost factor the optimizer tries to maximize grid input which is not wrong but when some variables are not capped the optimizer uses electricity with no efficiency whatsoever. So easiest way as mentionted earlier is to just penalize the grid import from the beginning so there are no negative cost factor during the optimization.

saschav
  • 37
  • 6