-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtemplate.py
More file actions
273 lines (232 loc) · 13.1 KB
/
template.py
File metadata and controls
273 lines (232 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
__author__ = ["David Cardona-Vasquez, Beltran Castro-Gomez, Benjamin Stoeckl"]
__copyright__ = "Copyright 2024-2025, Graz University of Technology"
__credits__ = ["David Cardona-Vasquez"]
__license__ = "MIT"
__maintainer__ = "David Cardona-Vasquez"
__status__ = "Development"
import pyomo.environ as pyo
import pandas as pd
class model():
"""A template of the different energy system models we are going to use"""
def __init__(self, name:str, periods:int):
# create a model with the name and the number of periods
self.model = pyo.ConcreteModel(name="("+name+")")
self.periods = periods
self.model.PERIODS = periods
self.d_data: dict
def initialize_parameters(self):
# this initializes the parameters of the model
self.model.pT = pyo.Set(initialize=list(range(1, self.model.PERIODS + 1))) # set with periods
self.model.pG = pyo.Set(initialize=self.d_data['generators']) # set with generators
# this is similar to a subset in GAMS, one for renewables and one for thermals
self.model.pGR = pyo.Set(within=self.model.pG, initialize=self.d_data['renewables']) # set with renewable generators
self.model.pGT = pyo.Set(within=self.model.pG, initialize=self.d_data['thermals']) # set with thermal generators
# this is for the input data of the model. pCF is the capacity factor for each generator and period
# pRU and pRD are the ramp-up and ramp-down limits for each ramp-constrained generator
# pGMAX and pGMIN are the maximum and minimum generation for each generator
self.model.pCF = pyo.Param(self.model.pGR, self.model.pT, initialize=self.d_data['cf']) # capacity factor for each unit, time
self.model.pRU = pyo.Param(self.model.pGT, initialize=self.d_data['rmpup']) # set with ramp-up limits
self.model.pRD = pyo.Param(self.model.pGT, initialize=self.d_data['rmpdn']) # set with ramp-down limits
self.model.pGMAX = pyo.Param(self.model.pG, initialize=self.d_data['gmax']) # set with max generation for each un
self.model.pGMIN = pyo.Param(self.model.pG, initialize=self.d_data['gmin']) # set with min generation for each un
def initialize_variables(self):
# now we initialize the variables of the model; the only variable common to all models
# is the generation; every model defines the variables it needs and initializes them
self.model.vGen = pyo.Var(self.model.pG, self.model.pT, domain=pyo.Reals, initialize=0) # generation from each unit at each time
def initialize_costs(self, costs):
# this could be considered a parameter, but because they are so critical for the model, we define them
# a separate function to initialize them and structure the input data differently
self.model.pCG = pyo.Param(self.model.pG, initialize=costs['CG']) # generation units' variable cost
def initialize_constraints(self, ramping:bool):
# here we initialize the constraints of the model, only generation related constraints are included
# because the rest of the constraints are model specific
# this is the constraint that defines the maximum generation of each unit
def eMaxProd(mdl, g, p):
if g in mdl.pGR:
return 0 <= mdl.pGMAX[g] * mdl.pCF[g, p] - mdl.vGen[g, p]
else:
return 0 <= mdl.pGMAX[g] - mdl.vGen[g, p]
self.model.eMaxProd = pyo.Constraint(self.model.pG, self.model.pT, rule=eMaxProd)
# this is the constraint that defines the minimum generation of each unit
def eMinProd(mdl, g, p):
return 0 <= mdl.vGen[g, p] - mdl.pGMIN[g]
self.model.eMinProd = pyo.Constraint(self.model.pG, self.model.pT, rule=eMinProd)
# this is the constraint that defines the ramping limits of each unit
def eRmpUp(mdl, g, p):
if p == 1:
return pyo.Constraint.Skip
else:
return 0 <= mdl.pRU[g] - (mdl.vGen[g, p] - mdl.vGen[g, p-1])
# the constraint is only added if the model is considering ramps
if ramping:
self.model.eRmpUp = pyo.Constraint(self.model.pGT, self.model.pT, rule=eRmpUp)
# this is the constraint that defines the ramping limits of each unit
def eRmpDn(mdl, g, p):
if p == mdl.PERIODS:
return pyo.Constraint.Skip
else:
return 0 <= mdl.pRD[g] - (mdl.vGen[g, p] - mdl.vGen[g, p+1])
# the constraint is only added if the model is considering ramps
if ramping:
self.model.eRmpDn = pyo.Constraint(self.model.pGT, self.model.pT, rule=eRmpDn)
def fill_model_data(self, renewables, thermals, cf, demand, periods):
"""
This function fills the model with the data needed to run it. It is called after the model is created and
before the model is solved. Takes Dataframes and converts them dictionaries, which are better suited for
pyomo.
:param renewables: Dataframe with the renewable generators data
:param thermals: Dataframe with the thermal generators data
:param cf: Dataframe with the capacity factors for each generator and period
:param demand: Dataframe with the demand data, not used in this model
:param periods: Integer with the number of periods in the model
:return: No return value, at the end the model has all the data needed to run
"""
l_renewable_gen = renewables.loc[:, 'unit'].to_list()
l_thermal_gen = thermals.loc[:, 'unit'].to_list()
l_gen = l_renewable_gen.copy()
l_gen.extend(l_thermal_gen)
aux_cf_data = pd.DataFrame(data={'period': list(range(1, periods + 1))})
d_cf = {}
for i in cf:
aux_g = i['generator']
aux_cf_data[aux_g] = i['cf']['cf'].values
for i, row in aux_cf_data.iterrows():
for c in row.index[1:]:
d_cf[c, row.loc['period']] = row[c]
d_ru = {}
d_rd = {}
d_gmax = {}
d_gmin = {}
for i, row in thermals.iterrows():
d_ru[row['unit']] = row['rmp_up']
d_rd[row['unit']] = row['rmp_dn']
d_gmax[row['unit']] = row['max']
d_gmin[row['unit']] = row['min']
for i, row in renewables.iterrows():
d_gmax[row['unit']] = row['max']
d_gmin[row['unit']] = row['min']
d_data = {
'renewables': l_renewable_gen,
'thermals': l_thermal_gen,
'generators': l_gen,
'cf': d_cf,
'rmpup': d_ru,
'rmpdn': d_rd,
'gmax': d_gmax,
'gmin': d_gmin
}
self.d_data = d_data
def export_model_variables_old(template, model:pyo.ConcreteModel):
""""
This function exports the variables from a pyomo model to a dictionary of dataframes. Does not depend
on the model's structure, all the data is extracted from the model's components
"""
# a dictionary of dataframes to store all the variables data, includingg their indices and values.
# The keys are the variables' names
d_vars = {}
# get all the variables from the model
variables = model.component_objects(pyo.Var, active=True)
for v in variables:
# get the variable's representation inside the model
var_object = getattr(model, str(v))
# create a dictionary to store the variable's data: its index set and its value
aux_d = {}
aux_d['value'] = []
# get the indices of the variable
idx_elements = list(var_object.index_set())
# if it is a tuple, the variable has more than one index set
if isinstance(idx_elements[0], tuple):
# count the number of index sets
len_test_elem = len(idx_elements[0])
# initialize the dictionary all the index sets
for i in range(0, len_test_elem):
aux_d['index_set_' + str(i+1)] = []
# if it is not a tuple, the variable one index set and we can the values directly
if not (isinstance(idx_elements[0], tuple)):
aux_d['index_set_1'] = []
for index in idx_elements:
aux_d['index_set_1'].append(index)
aux_d['value'].append(pyo.value(var_object[index]))
# if it is not a tuple, we need to iterate over the index sets to get the values
else:
for index in idx_elements:
for i in range(0, len_test_elem):
aux_d['index_set_' + str(i+1)].append(index[i])
aux_d['value'].append(pyo.value(var_object[index]))
# create a dataframe with the variable's data
df_aux_var = pd.DataFrame(data=aux_d)
# store the dataframe in the dictionary with all the other variables
d_vars[str(v)] = df_aux_var
return d_vars
def export_model_variables(template, model:pyo.ConcreteModel):
""""
This function exports the variables from a pyomo model to a dictionary of dataframes. Does not depend
on the model's structure, all the data is extracted from the model's components
"""
# a dictionary of dataframes to store all the variables data, includingg their indices and values.
# The keys are the variables' names
d_vars = {}
# get all the variables from the model
variables = model.component_objects(pyo.Var, active=True)
for v in variables:
# get the variable's representation inside the model
var_object = getattr(model, str(v))
# create a dictionary to store the variable's data: its index set and its value
aux_d = {}
aux_d['value'] = []
# get the indices of the variable
idx_elements = list(var_object.index_set())
aux_data = v.get_values()
aux_data_df = pd.DataFrame({'indices': list(aux_data.keys()), 'value': list(aux_data.values())})
# if it is a tuple, the variable has more than one index set
if isinstance(idx_elements[0], tuple):
# count the number of index sets
len_test_elem = len(idx_elements[0])
aux_cols_idx = ['index_set_' + str(i+1) for i in range(0, len_test_elem)]
aux_data_df[aux_cols_idx] = pd.DataFrame(aux_data_df['indices'].tolist(), index=aux_data_df.index)
aux_data_df.drop(columns=['indices'], inplace=True)
# if it is not a tuple, the variable one index set and we can the values directly
else:
aux_d['index_set_1'] = []
aux_cols_idx = ['index_set_1']
aux_data_df[aux_cols_idx] = pd.DataFrame(aux_data_df['indices'].tolist(), index=aux_data_df.index)
aux_data_df.drop(columns=['indices'], inplace=True)
# store the dataframe in the dictionary with all the other variables
d_vars[str(v)] = aux_data_df
return d_vars
def export_model_expresions(template, model:pyo.ConcreteModel):
""""
This function exports the variables from a pyomo model to a dictionary of dataframes. Does not depend
on the model's structure, all the data is extracted from the model's components
"""
# a dictionary of dataframes to store all the variables data, includingg their indices and values.
# The keys are the variables' names
d_expr = {}
# get all the variables from the model
expressions = model.component_objects(pyo.Expression)
for expr in expressions:
# get the variable's representation inside the model
expr_object = getattr(model, str(expr))
# create a dictionary to store the variable's data: its index set and its value
aux_d = {}
aux_d['value'] = []
# get the indices of the variable
idx_elements = list(expr_object.index_set())
aux_data = {t: pyo.value(e) for t, e in expr.items()}
aux_data_df = pd.DataFrame({'indices': list(aux_data.keys()), 'value': list(aux_data.values())})
# if it is a tuple, the variable has more than one index set
if isinstance(idx_elements[0], tuple):
# count the number of index sets
len_test_elem = len(idx_elements[0])
aux_cols_idx = ['index_set_' + str(i+1) for i in range(0, len_test_elem)]
aux_data_df[aux_cols_idx] = pd.DataFrame(aux_data_df['indices'].tolist(), index=aux_data_df.index)
aux_data_df.drop(columns=['indices'], inplace=True)
# if it is not a tuple, the variable one index set and we can the values directly
else:
aux_d['index_set_1'] = []
aux_cols_idx = ['index_set_1']
aux_data_df[aux_cols_idx] = pd.DataFrame(aux_data_df['indices'].tolist(), index=aux_data_df.index)
aux_data_df.drop(columns=['indices'], inplace=True)
# store the dataframe in the dictionary with all the other variables
d_expr[str(expr)] = aux_data_df
return d_expr