We run opt.solve(model) with a gamsSolverFactory and see a list index out of range error. This is due to pyomo splitting lines in the GAMS.py solver file and expecting 3 elements separated by spaces, but only two exist.
from pyomo.environ import *
import pandas as pd
import matplotlib.pyplot as plt
# BASIC "TWO-FACTOR" PARAMETERS
tfl_params = {
'b':{
'Nuclear':.061,
'Coal':.355,
'Gas T':.275,
'Gas CC':.031,
'Bioenergy (Total)':.008,
'Geothermal':-.607,
'Hydro':-.527,
'Wind Onshore':.039,
'Wind Offshore':-.273,
'Solar PV (Utility)':.414,
'Solar CSP':-0.017
},
'rho':{
'Nuclear':.435,
'Coal':.844,
'Gas T':.734,
'Gas CC':.561,
'Bioenergy (Total)':.571,
'Geothermal':.859,
'Hydro':1.181,
'Wind Onshore':.773,
'Wind Offshore':.121,
'Solar PV (Utility)':.676,
'Solar CSP':-0.346
},
'alpha':{
'Nuclear':0.115,
'Coal':2.536,
'Gas T':.103,
'Gas CC':.237,
'Bioenergy (Total)':.056,
'Geothermal':1.760,
'Hydro':0.240,
'Wind Onshore':.704,
'Wind Offshore':.376,
'Solar PV (Utility)':.428,
'Solar CSP':.188
},
'nbr_init':{
'Nuclear':770,
'Coal':2161,
'Gas T':3941,
'Gas CC':3039,
'Bioenergy (Total)':1436,
'Geothermal':403,
'Hydro':1275,
'Wind Onshore':404073,
'Wind Offshore':7191,
'Solar PV (Utility)':11381,
'Solar CSP':70
},
'unt_sizes':{
('Nuclear','s'): 0.6,
('Nuclear','m'): 1.1,
('Nuclear','l'): 1.5,
('Coal','s'): 0.45,
('Coal','m'): 0.9,
('Coal','l'): 1.2,
('Gas T','s'): 0.2,
('Gas T','m'): 0.5,
('Gas T','l'): 1.0,
('Gas CC','s'): 0.45,
('Gas CC','m'): 0.9,
('Gas CC','l'): 1.2,
('Bioenergy (Total)','s'): 0.045,
('Bioenergy (Total)','m'): 0.1,
('Bioenergy (Total)','l'): 0.5,
('Geothermal','s'): 0.05,
('Geothermal','m'): 0.1,
('Geothermal','l'): 0.5,
('Hydro','s'): 0.45,
('Hydro','m'): 0.9,
('Hydro','l'): 1.2,
('Wind Onshore','s'): 0.003,
('Wind Onshore','m'): 0.005,
('Wind Onshore','l'): 0.010,
('Wind Offshore','s'): 0.007,
('Wind Offshore','m'): 0.015,
('Wind Offshore','l'): 0.025,
('Solar CSP','s'): 0.05,
('Solar CSP','m'): 0.1,
('Solar CSP','l'): 0.5,
('Solar PV (Utility)','s'): 0.06,
('Solar PV (Utility)','m'): 0.2,
('Solar PV (Utility)','l'): 1.0,
},
'size_ref':{##
'Nuclear':1.1,
'Coal':0.590,
'Gas T':0.154,
'Gas CC':0.467,
'Bioenergy (Total)':.049,
'Geothermal':0.067,
'Hydro':.455,
'Wind Onshore':0.003,
'Wind Offshore':0.007,
'Solar PV (Utility)':0.060,
'Solar CSP':0.084
},
}
# READ DATA FROM EXCEL
sheet = ['Capacity',
]
df_newcapacity = pd.read_excel('./data.xlsx',
sheet_name=sheet[0], header=[0],
usecols='A:N', engine='openpyxl').fillna(0)
df_newcapacity = df_newcapacity.set_index('Technology')
df = df_newcapacity.copy()
df_bin = df.applymap(lambda x: 1 if x > 0 else 0)
# LINEARIZED LEARNING AND SCALE-UP FACTOR
## EoS factor calculation
eos_factor = {}
for t in list(df.index):
for s in ['s','m','l']:
eos_factor.update({(t,s): (tfl_params.get('unt_sizes').get((t,s))
/tfl_params.get('size_ref').get(t))**(tfl_params.get('rho').get(t))})
## Linearized learning calculation
xnbr_values = [*range(0,31)]
xnbr_segment = [*range(1,31)]
xnbr_unit = {}
yinv_cost = {}
minv_cost = {}
cinv_cost = {}
yu_avg = {}
mu_avg = {}
cu_avg = {}
## Xnbr and Yinv values
for t in list(df.index):
for x in xnbr_values:
xnbr_unit.update({(t,x): 2**(x)})
yinv_cost.update({(t,x): (((tfl_params.get('nbr_init').get(t)
+(2**(x)))/tfl_params.get('nbr_init').get(t))**(-tfl_params.get('b').get(t)))
})
yu_avg.update({(t,x): tfl_params.get('size_ref').get(t) * (((tfl_params.get('nbr_init').get(t)
+(2**(x)))/tfl_params.get('nbr_init').get(t))**(tfl_params.get('alpha').get(t)))
})
## Linearized parameters
for t in list(df.index):
for x in xnbr_segment:
# values calculation
minv_cost_value = ((yinv_cost.get((t,x)) - yinv_cost.get((t,x-1)))
/(xnbr_unit.get((t,x)) - xnbr_unit.get((t,x-1))))
cinv_cost_value = minv_cost_value * xnbr_unit.get((t,x-1)) + yinv_cost.get((t,x-1))
mu_avg_value = ((yu_avg.get((t,x)) - yu_avg.get((t,x-1)))
/(xnbr_unit.get((t,x)) - xnbr_unit.get((t,x-1))))
cu_avg_value = mu_avg_value * xnbr_unit.get((t,x-1)) + yu_avg.get((t,x-1))
# updating the dictionary
minv_cost.update({(t,x): minv_cost_value})
mu_avg.update({(t,x): mu_avg_value})
cinv_cost.update({(t,x): cinv_cost_value})
cu_avg.update({(t,x): cu_avg_value})
# Sizes portfolio optimization
## Declare model's name
model = ConcreteModel('Optimal separated effects')
## Define sets ##
model.t = Set(initialize=list(df.index), doc='Technology')
model.s = Set(initialize=['s','m','l'], doc='Unit sizes')
model.y = Set(initialize=list(df.columns), doc='Periods')
model.yy = Set(initialize=list(df.columns), doc='Periods')
model.x = Set(initialize=xnbr_segment, doc='Linearized-x segment')
#map(str, list(df.columns))
## Define data and parameters ##
model.nbr_unit_ref = Param(model.t, initialize=tfl_params.get('nbr_init'), doc='Units # set as reference')
model.unt_size_ref = Param(model.t, initialize=tfl_params.get('size_ref'), doc='Units size set as reference')
model.unt_sizes = Param(model.t, model.s, initialize=tfl_params.get('unt_sizes'), doc='Units size data')
model.learning_par = Param(model.t, initialize=tfl_params.get('b'), doc='Units # data')
model.eos_par = Param(model.t, initialize=tfl_params.get('rho'), doc='Units # data')
model.eos_f = Param(model.t, model.s, initialize=eos_factor, doc='Units # data')
model.scaleup_par = Param(model.t, initialize=tfl_params.get('alpha'), doc='Units # data')
model.new_capacity = Param(model.t, model.y, initialize=df.stack().to_dict(), doc='Units # data')
model.bin_new_cap = Param(model.t, model.y, initialize=df_bin.stack().to_dict(), doc='Units # data')
model.minv = Param(model.t, model.x, initialize=minv_cost, doc='Units # data')
model.mu = Param(model.t, model.x, initialize=mu_avg, doc='Units # data')
model.cinv = Param(model.t, model.x, initialize=cinv_cost, doc='Units # data')
model.cu = Param(model.t, model.x, initialize=cu_avg, doc='Units # data')
## Define variables ##
model.CAPEX_TEC_IDX = Var(model.t, model.y, bounds=(0.0,None), doc='Investment cost')
model.CAPEX_TEC_IDXr = Var(model.t, model.y, bounds=(0.0,None), doc='Investment cost index')
model.NBR_UNIT = Var(model.t, model.s, model.y, bounds=(0.0,None), doc='Number of units')
### FROM HERE
## Define constraints ##
def capacitybalance_rule(model, t, y):
return model.new_capacity[t,y] <= sum(model.NBR_UNIT[t,s,y] * model.unt_sizes[t,s] for s in model.s)
model.capacitybalance = Constraint(model.t, model.y, rule=capacitybalance_rule, doc='Absolute x calculation')
def costlearn_rule(model, t, y, x):
return model.CAPEX_TEC_IDXr[t,y] >= ((sum(model.NBR_UNIT[t,s,yy] for s in model.s for yy in model.yy if yy <= y)
* model.minv[t,x]) + model.cinv[t,x])
model.costlearn = Constraint(model.t, model.y, model.x, rule=costlearn_rule, doc='Cost reduction from the learning effect')
def costhold_rule(model, t, y):
if y == model.y.first():
return model.CAPEX_TEC_IDX[t,y] == (model.bin_new_cap[t,y] * model.CAPEX_TEC_IDX[t,y]
+ (1-model.bin_new_cap[t,y]) * 1)
else:
return model.CAPEX_TEC_IDX[t,y] == (model.bin_new_cap[t,y] * model.CAPEX_TEC_IDX[t,y]
+ (1-model.bin_new_cap[t,y]) * model.CAPEX_TEC_IDX[t,model.y.prev(y)])
model.costhold = Constraint(model.t, model.y, rule=costhold_rule, doc='Cost hold at previous period level if no new capacity')
def costscale_rule(model, t, y):
return (model.CAPEX_TEC_IDX[t,y] * model.new_capacity[t,y]) >= (sum(model.NBR_UNIT[t,s,y] * model.eos_f[t,s] for s in model.s)
* model.CAPEX_TEC_IDXr[t,y]
* model.unt_size_ref[t])
model.costscale = Constraint(model.t, model.y, rule=costscale_rule, doc='cost weight average calculation')
## Define objective functions ##
def objective_rule(model):
return sum(model.CAPEX_TEC_IDX[t,y] * model.new_capacity[t,y] for t in model.t for y in model.y)
model.objective = Objective(rule=objective_rule, sense=minimize, doc='Objective function in squared absolute percentage term')
# return sum(model.CAPEX_TEC_IDX[t,y] * model.new_capacity[t,y] for t in model.t for y in model.y)
if __name__ == '__main__':
# This emulates what the pyomo command-line tools does
from pyomo.opt import SolverFactory
import pyomo.environ
opt = SolverFactory("gams")
opt.options['solver'] = 'minos'
results = opt.solve(model)
model.display()
obj_value = model.objective(value)
print('The objective value is', obj_value)
df_capex_tec_idx=[]
fig, ax = plt.subplots(figsize=(10,7), sharex=True)
for t in list(df.index):
capex_tec_idx = []
for y in list(df.columns):
capex_tec_idx.append(model.CAPEX_TEC_IDX[t,y](value))
df_capex_tec_idx.append(capex_tec_idx)
plot_1 = ax.plot(list(df.columns), capex_tec_idx,
ls='-', mfc='white', label=t, zorder=2)
plt.show()
Error Message
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Input In [3], in <cell line: 66>()
70 opt = SolverFactory("gams")
71 opt.options['solver'] = 'minos'
---> 72 results = opt.solve(model)
73 model.display()
75 obj_value = model.objective(value)
File ~\Anaconda3\lib\site-packages\pyomo\solvers\plugins\solvers\GAMS.py:853, in GAMSShell.solve(self, *args, **kwds)
850 model_soln, stat_vars = self._parse_gdx_results(
851 results_filename, statresults_filename)
852 else:
--> 853 model_soln, stat_vars = self._parse_dat_results(
854 results_filename, statresults_filename)
855 finally:
856 if not keepfiles:
File ~\Anaconda3\lib\site-packages\pyomo\solvers\plugins\solvers\GAMS.py:1236, in GAMSShell._parse_dat_results(self, results_filename, statresults_filename)
1233 for line in results_text.splitlines()[1:]:
1234 # line = line.replace('GAMS_OBJECTIVE', 'GAMS_OBJECTIVE ')
1235 items = line.split()
-> 1236 model_soln[items[0]] = (items[1], items[2])
1238 return model_soln, stat_vars
IndexError: list index out of range
Information on your system
Pyomo version: 6.4.2
Python version: 3.9.12
Operating system: windows
How Pyomo was installed (PyPI, conda, source): conda
Solver (if applicable): GAMS using minos or ipopt (observed in both)
Additional information
We were able to address this by patching GAMS.py line 1234 with
line = line.replace("GAMS_OBJECTIVE", "GAMS_OBJECTIVE ")
effectively forcing the 3 elements. Of course a complete hack, not a fix at all - just to show that a solution is actually found.
Summary
We run
opt.solve(model)
with agams
SolverFactory
and see a list index out of range error. This is due to pyomo splitting lines in theGAMS.py
solver file and expecting 3 elements separated by spaces, but only two exist.Steps to reproduce the issue
Using
data.xlsx
Error Message
Information on your system
Pyomo version: 6.4.2 Python version: 3.9.12 Operating system: windows How Pyomo was installed (PyPI, conda, source): conda Solver (if applicable): GAMS using minos or ipopt (observed in both)
Additional information
We were able to address this by patching
GAMS.py
line 1234 witheffectively forcing the 3 elements. Of course a complete hack, not a fix at all - just to show that a solution is actually found.