Skip to content
Advertisement

How to include a variable in the summation bound of a objective function with pyomo?

I’m using pyomo to find an approach for solving an energy optimization problem. I’m trying to find the optimal time slot during a day to shift the start of a smart dishwasher to, so that the electricity cost is minimized.

I’m using an example from a paper but unfortunately I can’t embed pictures yet, so here are the links to the pictures describing the problem:

Diagramms for load and electricity costs

Problem description

I know how to use pyomo to solve a simple multi-period optimization problem. If bounds of the sum in the objective function are fix I can write “sum(… for t in m.T)“. See the following code example for a better understanding of what I mean:

from pyomo.environ import *

model = ConcreteModel()
model.T = RangeSet(5)
...

model.y = Var(model.T, domain=Binary)    
model.i_pos = Var(model.T, domain=NonNegativeReals)
model.i_neg = Var(model.T, domain=NegativeReals)
...

c = 30  # setup cost: fixe Kosten pro Zeitschritt wenn in diesem Zeitschritt produziert werden muss
h_pos = 0.7  # cost per unit of holding inventory
h_neg = 1.2  # shortage cost per unit
P = 5.0  # maximum production amount per time step
...

def obj_rule(m):
   return sum(c*m.y[t] + h_pos*m.i_pos[t] + h_neg*m.i_neg[t] for t in m.T)
model.obj = Objective(rule=obj_rule, sense=minimize)

Now my issue with the mentioned energy optimization problem is, that I don’t know how to include a variable in the bounds of a sum in pyomo. The case is that the variable s_n,i(see link: Problem description) describes the starting time slot of the dishwasher, but is also included in the sum of my objective function which minimizes the costs. So somehow I need to get my variable s_n,i into the “for t in m.T” part of my objective function.

I adapted the problem from the paper a bit:

  • 7 time steps
  • the dishwasher runs for 2 hours when it’s turned on
  • the dishwasher has a rated power of 1 kW

This is my current code:

from pyomo.environ import *

model = ConcreteModel()
# 7 time steps
model.T = RangeSet(7)


a = 1  # earliest starting time slot for the dishwasher
b = 7  # lastest ending time slot for the dishwasher
d = 2  # duration dishwasher in h
p = 1  # power dishwasher in kW

# electricity cost per time step in €/kWh
cost = {1: 0.4, 2: 0.3, 3: 0.3, 4: 0.15, 5: 0.25, 6: 0.30, 7: 0.35}

# definition of the variable s_n,i
model.start = Var(bounds=(a, b-d), domain=NonNegativeReals)

def obj_rule(m):
    return sum(cost[t]*p for t in m.T)
model.obj = Objective(rule=obj_rule, sense=minimize)

solver = SolverFactory('glpk')
solver.solve(model)

Thanks for your help!

Advertisement

Answer

Welcome to the site. Your question is a little more math programming rather than a coding issue, but let’s give it a try…

I hope you have some familiarity with integer programming, because that is where you need to go with this problem to fix the part that you are hung up on.

Think about changing your start variable. (Let’s just rename it Y for ease.) Let us decide that Y_j is a binary variable that represents the decision to start the machine in period j where j is a subset of the periods T that allow start. Now we have something to work with in your objective function….

In the objective then, we will want to look at the summation element and look to see if Y_j is 1 (selected) in the current or previous 2 time periods (assuming the machine runs for 3 time steps).

Alternatively, you could introduce yet another variable to indicate whether the machine is running in any particular period and set up constraints to force that variable based on Y_j.

You will need a constraint or two on Y_j depending on how you set the problem up, notably, of course, sum(Y_j) = 1

Comment back if yer stuck.

EDIT : Implementation below

there are a couple approaches to this. The one below uses 2 variables for start period and one for “running”. I think you could finesse it a bit with a more complicated objective function and just use the “start” variable, but this is along the lines of the comments you had.

# pick the cheapest 2-consecutive periods to run an appliance

import pyomo.environ as pyo

# Data
costs = {   1:10,
            2:5,
            3:7,
            4:15}
num_periods = 4
run_time = 2  # the number of periods the machine runs

m = pyo.ConcreteModel('appliance runner')

# SETS
m.T       = pyo.RangeSet(num_periods)
m.T_start = pyo.RangeSet(num_periods - (run_time - 1))  # legal starts {1,2,3}

# VARS
m.Y = pyo.Var(m.T_start, domain=pyo.Binary)  # the period to START
m.X = pyo.Var(m.T, domain=pyo.Binary)        # machine running



# CONSTRAINTS
# must start at least once
m.C1 = pyo.Constraint(expr=sum(m.Y[t] for t in m.T_start) >=1 )

# periods after the start, the machine must be on for run_time
# (this is the tricky one...  couple ways to do this...)

def running(self, t):
    return m.X[t] >= sum(m.Y[t2] for t2 in 
                        range(t, t-run_time, -1) 
                        if t2 in m.T_start)
m.C2 = pyo.Constraint(m.T, rule=running)
# m.pprint()  <-- use this to see what was created in this constraint

# OBJ
m.OBJ = pyo.Objective(expr=sum(m.X[t]*costs[t] for t in m.T))

solver = pyo.SolverFactory('glpk')
results = solver.solve(m)
print(results)
m.display()
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement