As part of an optimization problem I'm trying to loop inside a dictionary with multiple keys (look UNIT_TASKS). Particularly I have problem looping through indices to define the Objective and Constraint since Python returns "Key Error: ('Reactor_2', 'Reaction_3','Juan') ". A fragment of the script is the following:
import numpy as np
from pyomo.environ import *
from pyomo.gdp import
STATES = {
'Feed_A' : {'capacity': 500, 'initial': 500, 'price': 0},
'Feed_B' : {'capacity': 500, 'initial': 500, 'price': 0},
'Feed_C' : {'capacity': 500, 'initial': 500, 'price': 0},
'Hot_A' : {'capacity': 100, 'initial': 0, 'price': -1},
'Int_AB' : {'capacity': 200, 'initial': 0, 'price': -1},
'Int_BC' : {'capacity': 150, 'initial': 0, 'price': -1},
'Impure_E' : {'capacity': 100, 'initial': 0, 'price': -1},
'Product_1': {'capacity': 500, 'initial': 0, 'price': 10},
'Product_2': {'capacity': 500, 'initial': 0, 'price': 10},
}
UNIT_TASKS = {
('Heater', 'Heating', 'Pablo') : {'Bmin': 0, 'Bmax': 100},
('Reactor_1', 'Reaction_1', 'Juan'): {'Bmin': 0, 'Bmax': 80},
('Reactor_1', 'Reaction_2', 'Pedro'): {'Bmin': 0, 'Bmax': 80},
('Reactor_1', 'Reaction_3', 'Pablo'): {'Bmin': 0, 'Bmax': 80},
('Reactor_2', 'Reaction_1', 'Pablo'): {'Bmin': 0, 'Bmax': 80},
('Reactor_2', 'Reaction_2', 'Juan'): {'Bmin': 0, 'Bmax': 80},
('Reactor_2', 'Reaction_3', 'Pablo'): {'Bmin': 0, 'Bmax': 80},
('Still', 'Separation', 'Pablo'): {'Bmin': 0, 'Bmax': 200},
('Heater', 'Heating', 'Juan') : {'Bmin': 0, 'Bmax': 300},
}
TASKS = set([i for (j,i,k) in UNIT_TASKS])
UNITS = set([j for (j,i,k) in UNIT_TASKS])
NAMES = set([k for (j,i,k) in UNIT_TASKS])
TIME = range(0,11)
TIME = np.array(TIME)
model = ConcreteModel()
model.W = Var(TASKS, UNITS, TIME, domain = Boolean)
model.B = Var(TASKS, UNITS, TIME, domain = NonNegativeReals)
model.S = Var(STATES.keys(), TIME, domain = NonNegativeReals)
model.Q = Var(UNITS, TIME, domain = NonNegativeReals)
Bmax = {(i,j,k): UNIT_TASKS[(j,i,k)]['Bmax'] for (j,i,k) in UNIT_TASKS}
UNITS_DIC = {j: set() for j in UNITS}
for (j,i,k) in UNIT_TASKS:
UNITS_DIC[j].add(i)
UNITS_DIC_N = {j: set() for j in UNITS}
for (j,i,k) in UNIT_TASKS:
UNITS_DIC_N[j].add(k)
NAMES_DIC = {k: set() for k in NAMES}
for (j,i,k) in UNIT_TASKS:
NAMES_DIC[k].add(i)
TASKS_DIC = {i: set() for i in TASKS}
for (j,i,k) in UNIT_TASKS:
TASKS_DIC[i].add(k)
TASKS_DIC_U = {i: set() for i in TASKS}
for (j,i,k) in UNIT_TASKS:
TASKS_DIC_U[i].add(j)
model.Cost = Var(domain=NonNegativeReals)
model.costc = Constraint(expr = model.Cost == sum([UNIT_TASKS[(j,i,k)]['Bmin']*model.W[i,j,t]
+ UNIT_TASKS[(j,i,k)]['Bmax']*model.B[i,j,t] for i in TASKS for j in TASKS_DIC_U[i] for k in UNITS_DIC_N[j] for t in TIME]))
model.cons = ConstraintList()
for t in TIME:
for j in UNITS:
for i in UNITS_DIC[j]:
for k in TASKS_DIC[i]:
model.cons.add(model.B[i,j,t] <= model.W[i,j,t]*Bmax[i,j,k])
A couple things to clean up... :)
First, when you are making the constraint that you are getting the key error for, it is because you are using the multiple 'for' expressions there which is going to create the cross product of all of the items... try it out on the side.
You have a sparse matrix of values, so you should initialize a set with just those values and use it. I also like using Pyomo's Set (note the capital S) when making models, although you can use either.
also, your indices are tough to follow. If you use (i,j,k) use them in that order (standard) not (j,i,k) (confusing). I like to use more intuitive names when you have them...it increases readability.
also note in your code above you have the indices flipped around in this line:
Bmax = {(i,j,k): UNIT_TASKS[(j,i,k)]['Bmax'] for (j,i,k) in UNIT_TASKS}
Here is a suggested cleanup. Note you will get some "warnings" with this because we are using parts of your dictionary keys to make sets, and there is some redundancy.
model = ConcreteModel()
model.TASKS = Set(initialize = (i for (j,i,k) in UNIT_TASKS)) # note capital "S" Set for pyomo set
model.UNITS = Set(initialize = (j for (j,i,k) in UNIT_TASKS))
model.NAMES = Set(initialize = (k for (j,i,k) in UNIT_TASKS))
model.UTN = Set(within=model.UNITS * model.TASKS * model.NAMES,
initialize=UNIT_TASKS.keys()) # this is now a Set of the keys (U, T, N)
model.TIMES = Set(initialize=range(0,11))
#TIME = np.array(TIME)
model.W = Var(model.TASKS, model.UNITS, model.TIMES, domain = Boolean)
model.B = Var(model.TASKS, model.UNITS, model.TIMES, domain = NonNegativeReals)
model.S = Var(STATES.keys(), model.TIMES, domain = NonNegativeReals)
model.Q = Var(model.UNITS, model.TIMES, domain = NonNegativeReals)
Bmax = {key: UNIT_TASKS[key]['Bmax'] for key in model.UTN} # NOTE: YOU HAD FLIPPED (i,j,k) -> (j, i, k)
# none of these "helper" dictionaries should be required!! You should be able to delete these lines
# UNITS_DIC = {j: set() for j in UNITS} # UNIT: {TASKS}
# for (j,i,k) in UNIT_TASKS:
# UNITS_DIC[j].add(i)
# UNITS_DIC_N = {j: set() for j in UNITS} # UNIT: {NAMES}
# for (j,i,k) in UNIT_TASKS:
# UNITS_DIC_N[j].add(k)
# NAMES_DIC = {k: set() for k in NAMES} # NAME: {TASKS}
# for (j,i,k) in UNIT_TASKS:
# NAMES_DIC[k].add(i)
# TASKS_DIC = {i: set() for i in TASKS} # TASK: {NAMES}
# for (j,i,k) in UNIT_TASKS:
# TASKS_DIC[i].add(k)
# TASKS_DIC_U = {i: set() for i in TASKS} # TASK: {UNITS}
# for (j,i,k) in UNIT_TASKS:
# TASKS_DIC_U[i].add(j)
# for k in UNIT_TASKS:
# print (k, UNIT_TASKS[k])
model.Cost = Var(domain=NonNegativeReals)
model.costc = Constraint(expr = model.Cost == sum( UNIT_TASKS[(u,t,n)]['Bmin']*model.W[t,u,time]
+ UNIT_TASKS[(u,t,n)]['Bmax']*model.B[t,u,time]
for u,t,n in model.UTN
for time in model.TIMES))