openTEPES 4.18.8__py3-none-any.whl → 4.18.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openTEPES/__init__.py +1 -1
- openTEPES/openTEPES.py +3 -3
- openTEPES/openTEPES_InputData.py +4 -4
- openTEPES/openTEPES_Main.py +2 -2
- openTEPES/openTEPES_OutputResults.py +238 -78
- openTEPES/openTEPES_ProblemSolving.py +6 -6
- {opentepes-4.18.8.dist-info → opentepes-4.18.9.dist-info}/METADATA +16 -16
- {opentepes-4.18.8.dist-info → opentepes-4.18.9.dist-info}/RECORD +11 -11
- {opentepes-4.18.8.dist-info → opentepes-4.18.9.dist-info}/WHEEL +0 -0
- {opentepes-4.18.8.dist-info → opentepes-4.18.9.dist-info}/entry_points.txt +0 -0
- {opentepes-4.18.8.dist-info → opentepes-4.18.9.dist-info}/licenses/LICENSE +0 -0
openTEPES/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@ Open Generation, Storage, and Transmission Operation and Expansion Planning Mode
|
|
|
14
14
|
>>> import openTEPES as oT
|
|
15
15
|
>>> oT.routine("9n", "C:\\Users\\UserName\\Documents\\GitHub\\openTEPES", "glpk")
|
|
16
16
|
"""
|
|
17
|
-
__version__ = "4.18.
|
|
17
|
+
__version__ = "4.18.9"
|
|
18
18
|
|
|
19
19
|
from .openTEPES_Main import main
|
|
20
20
|
from .openTEPES import *
|
openTEPES/openTEPES.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 22, 2026
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
# import dill as pickle
|
|
@@ -38,8 +38,8 @@ def openTEPES_run(DirName, CaseName, SolverName, pIndOutputResults, pIndLogConso
|
|
|
38
38
|
idxDict['y' ] = 1
|
|
39
39
|
|
|
40
40
|
#%% model declaration
|
|
41
|
-
mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.
|
|
42
|
-
print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.
|
|
41
|
+
mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.9 - January 22, 2026')
|
|
42
|
+
print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.9 - January 22, 2026', file=open(f'{_path}/openTEPES_version_{CaseName}.log','w'))
|
|
43
43
|
|
|
44
44
|
pIndOutputResults = [j for i,j in idxDict.items() if i == pIndOutputResults][0]
|
|
45
45
|
pIndLogConsole = [j for i,j in idxDict.items() if i == pIndLogConsole ][0]
|
openTEPES/openTEPES_InputData.py
CHANGED
|
@@ -683,10 +683,10 @@ def DataConfiguration(mTEPES):
|
|
|
683
683
|
mTEPES.psnnd = Set(initialize = [(p,sc,n,nd) for p,sc,n,nd in mTEPES.psn*mTEPES.nd ])
|
|
684
684
|
mTEPES.psnar = Set(initialize = [(p,sc,n,ar) for p,sc,n,ar in mTEPES.psn*mTEPES.ar ])
|
|
685
685
|
|
|
686
|
-
mTEPES.psnla = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.la
|
|
687
|
-
mTEPES.psnle = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.le
|
|
688
|
-
mTEPES.psnll = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ll
|
|
689
|
-
mTEPES.psnls = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ls
|
|
686
|
+
mTEPES.psnla = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.la if (p,ni,nf,cc) in mTEPES.pla ])
|
|
687
|
+
mTEPES.psnle = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.le if (p,ni,nf,cc) in mTEPES.pla ])
|
|
688
|
+
mTEPES.psnll = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ll if (p,ni,nf,cc) in mTEPES.pll ])
|
|
689
|
+
mTEPES.psnls = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ls if (p,ni,nf,cc) in mTEPES.pla ])
|
|
690
690
|
|
|
691
691
|
mTEPES.psnehc = Set(initialize = [(p,sc,n,eh) for p,sc,n,eh in mTEPES.psneh if mTEPES.dPar['pRatedMaxCharge'][eh] > 0.0 ])
|
|
692
692
|
|
openTEPES/openTEPES_Main.py
CHANGED
|
@@ -660,7 +660,7 @@
|
|
|
660
660
|
# For more information on this, and how to apply and follow the GNU AGPL, see
|
|
661
661
|
# <https://www.gnu.org/licenses/>.
|
|
662
662
|
|
|
663
|
-
# Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January
|
|
663
|
+
# Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 22, 2026
|
|
664
664
|
# simplicity and transparency in power systems planning
|
|
665
665
|
|
|
666
666
|
# Developed by
|
|
@@ -693,7 +693,7 @@ GREEN = "\033[32m"
|
|
|
693
693
|
BLUE = "\033[34m"
|
|
694
694
|
RESET = "\033[0m"
|
|
695
695
|
|
|
696
|
-
print(GREEN + 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.
|
|
696
|
+
print(GREEN + 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.9 - January 22, 2026' + RESET)
|
|
697
697
|
print(BLUE + '#### Academic research license - for non-commercial use only ####' + RESET + '\n')
|
|
698
698
|
|
|
699
699
|
parser = argparse.ArgumentParser(description='Introducing main parameters...')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 22, 2026
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -14,6 +14,7 @@ import plotly.io as pio
|
|
|
14
14
|
import plotly.graph_objs as go
|
|
15
15
|
from collections import defaultdict
|
|
16
16
|
from colour import Color
|
|
17
|
+
import duckdb
|
|
17
18
|
|
|
18
19
|
# from line_profiler import profile
|
|
19
20
|
|
|
@@ -34,12 +35,12 @@ def PiePlots(period, scenario, df, Category, Value):
|
|
|
34
35
|
ComposedCategory = Category+':N'
|
|
35
36
|
ComposedValue = Value +':Q'
|
|
36
37
|
|
|
37
|
-
base = alt.Chart(OutputToPlot).encode(theta=alt.Theta(ComposedValue, type=
|
|
38
|
+
base = alt.Chart(OutputToPlot).encode(theta=alt.Theta(ComposedValue, type='quantitative', stack=True), color=alt.Color(ComposedCategory, scale=alt.Scale(scheme='category20c'), type='nominal', legend=alt.Legend(title=Category))).properties(width=800, height=800)
|
|
38
39
|
pie = base.mark_arc(outerRadius=240)
|
|
39
40
|
text = base.mark_text(radius=340, size=15).encode(text='Label:N')
|
|
40
41
|
chart = pie+text
|
|
41
|
-
# chart = chart.resolve_scale(theta=
|
|
42
|
-
chart = alt.layer(pie, text, data=OutputToPlot).resolve_scale(theta=
|
|
42
|
+
# chart = chart.resolve_scale(theta='independent')
|
|
43
|
+
chart = alt.layer(pie, text, data=OutputToPlot).resolve_scale(theta='independent')
|
|
43
44
|
|
|
44
45
|
return chart
|
|
45
46
|
|
|
@@ -103,6 +104,163 @@ def LinePlots(period, scenario, df, Category, X, Y, OperationType):
|
|
|
103
104
|
return plot
|
|
104
105
|
|
|
105
106
|
|
|
107
|
+
def _set_df(con, name: str, df: pd.DataFrame):
|
|
108
|
+
"""
|
|
109
|
+
Insert a pandas DataFrame into an existing DuckDB table.
|
|
110
|
+
|
|
111
|
+
Parameters
|
|
112
|
+
----------
|
|
113
|
+
con : duckdb.DuckDBPyConnection
|
|
114
|
+
Open DuckDB connection.
|
|
115
|
+
name : str
|
|
116
|
+
Target table name.
|
|
117
|
+
df : pandas.DataFrame
|
|
118
|
+
DataFrame to insert into the target table.
|
|
119
|
+
"""
|
|
120
|
+
con.cursor().execute(f"CREATE OR REPLACE TABLE '{name}' AS SELECT * FROM df;")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _write_var_to_db(con, var, name):
|
|
124
|
+
"""
|
|
125
|
+
Write a Pyomo Var's values and bounds to DuckDB.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
con : duckdb.DuckDBPyConnection
|
|
130
|
+
Open DuckDB connection.
|
|
131
|
+
var : pyomo.core.base.var.Var
|
|
132
|
+
Pyomo variable (indexed or scalar).
|
|
133
|
+
name : str
|
|
134
|
+
Variable/table name to use in the database.
|
|
135
|
+
"""
|
|
136
|
+
values = var.extract_values()
|
|
137
|
+
s = pd.Series(values, index=values.keys())
|
|
138
|
+
df = s.to_frame(name=name).reset_index()
|
|
139
|
+
df = pd.concat(
|
|
140
|
+
[
|
|
141
|
+
df,
|
|
142
|
+
pd.DataFrame([dict(upper_bound=var[index].ub, lower_bound=var[index].lb) for index in var]),
|
|
143
|
+
],
|
|
144
|
+
axis=1,
|
|
145
|
+
)
|
|
146
|
+
_set_df(con, f'v_{name}', df)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _write_param_to_db(con, param, name):
|
|
150
|
+
"""
|
|
151
|
+
Write a Pyomo Param's values to DuckDB.
|
|
152
|
+
|
|
153
|
+
Creates/inserts into a table named `{name}` with:
|
|
154
|
+
- the Param index (via reset_index)
|
|
155
|
+
- a column named `{name}` containing the Param value
|
|
156
|
+
|
|
157
|
+
Parameters
|
|
158
|
+
----------
|
|
159
|
+
con : duckdb.DuckDBPyConnection
|
|
160
|
+
Open DuckDB connection.
|
|
161
|
+
param : pyomo.core.base.param.Param
|
|
162
|
+
Pyomo parameter (indexed or scalar).
|
|
163
|
+
name : str
|
|
164
|
+
Parameter/table name to use in the database.
|
|
165
|
+
"""
|
|
166
|
+
values = param.extract_values()
|
|
167
|
+
s = pd.Series(values, index=values.keys())
|
|
168
|
+
df = s.to_frame(name=name).reset_index()
|
|
169
|
+
_set_df(con, f'p_{name}', df)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _write_set_to_db(con, set_, name):
|
|
173
|
+
"""
|
|
174
|
+
Write a Pyomo Set's elements to DuckDB.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
con : duckdb.DuckDBPyConnection
|
|
179
|
+
Open DuckDB connection.
|
|
180
|
+
set_ : pyomo.core.base.set.Set
|
|
181
|
+
Pyomo set.
|
|
182
|
+
name : str
|
|
183
|
+
Base name used to build the table name `s_{name}`.
|
|
184
|
+
"""
|
|
185
|
+
s = pd.Series(set_.sorted_data())
|
|
186
|
+
df = s.to_frame(name=name).reset_index()
|
|
187
|
+
_set_df(con, f's_{name}', df)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _write_constraint_to_db(con, constraint, name, model):
|
|
191
|
+
"""
|
|
192
|
+
Write an indexed Pyomo Constraint's duals and bounds to DuckDB.
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
con : duckdb.DuckDBPyConnection
|
|
197
|
+
Open DuckDB connection.
|
|
198
|
+
constraint : pyomo.core.base.constraint.Constraint
|
|
199
|
+
Pyomo constraint component (must be indexed; scalar constraints are skipped).
|
|
200
|
+
name : str
|
|
201
|
+
Constraint name used to build the table name `c_{name}`.
|
|
202
|
+
model : pyomo.core.base.PyomoModel.ConcreteModel
|
|
203
|
+
Model providing `pDuals` (mapping from constraint+index string to dual value).
|
|
204
|
+
|
|
205
|
+
Notes
|
|
206
|
+
-----
|
|
207
|
+
Non-indexed constraints are ignored
|
|
208
|
+
"""
|
|
209
|
+
if not constraint.is_indexed():
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
records = []
|
|
213
|
+
for index in constraint:
|
|
214
|
+
key = str(constraint.name) + str(index)
|
|
215
|
+
records.append(
|
|
216
|
+
dict(
|
|
217
|
+
name=str(name),
|
|
218
|
+
index=index,
|
|
219
|
+
dual=model.pDuals.get(key),
|
|
220
|
+
lower_bound=constraint[index].lb,
|
|
221
|
+
upper_bound=constraint[index].ub,
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
df = pd.DataFrame(records)
|
|
226
|
+
_set_df(con, f'c_{name}', df.reset_index())
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def write_model_to_db(model, filename):
|
|
230
|
+
"""
|
|
231
|
+
Export Pyomo model components to a DuckDB database file.
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
model : pyomo.core.base.PyomoModel.ConcreteModel
|
|
236
|
+
filename : str
|
|
237
|
+
"""
|
|
238
|
+
with duckdb.connect(filename) as con:
|
|
239
|
+
model_vars = model.component_map(ctype=pyo.Var)
|
|
240
|
+
for k in model_vars.keys():
|
|
241
|
+
var = model_vars[k]
|
|
242
|
+
name = var.getname()
|
|
243
|
+
_write_var_to_db(con, var, name)
|
|
244
|
+
|
|
245
|
+
model_params = model.component_map(ctype=pyo.Param)
|
|
246
|
+
for k in model_params.keys():
|
|
247
|
+
param = model_params[k]
|
|
248
|
+
name = param.getname()
|
|
249
|
+
_write_param_to_db(con, param, name)
|
|
250
|
+
|
|
251
|
+
model_sets = model.component_map(ctype=pyo.Set)
|
|
252
|
+
for k in model_sets.keys():
|
|
253
|
+
s = model_sets[k]
|
|
254
|
+
name = s.getname()
|
|
255
|
+
_write_set_to_db(con, s, name)
|
|
256
|
+
|
|
257
|
+
model_constraints = model.component_map(ctype=pyo.Constraint)
|
|
258
|
+
for k in model_constraints.keys():
|
|
259
|
+
constraint = model_constraints[k]
|
|
260
|
+
name = k
|
|
261
|
+
_write_constraint_to_db(con, constraint, name, model)
|
|
262
|
+
|
|
263
|
+
|
|
106
264
|
# write parameters, variables, and duals
|
|
107
265
|
# @profile
|
|
108
266
|
def OutputResultsParVarCon(DirName, CaseName, OptModel, mTEPES):
|
|
@@ -123,40 +281,42 @@ def OutputResultsParVarCon(DirName, CaseName, OptModel, mTEPES):
|
|
|
123
281
|
os.makedirs(dump_folder)
|
|
124
282
|
DateName = str(datetime.datetime.now().strftime('%Y%m%d'))
|
|
125
283
|
|
|
284
|
+
write_model_to_db(OptModel, f'{_path}/CaseDumpFolder_{CaseName}_{DateName}/oT_Case_{CaseName}.duckdb')
|
|
285
|
+
|
|
126
286
|
# Extract and write parameters from the case
|
|
127
|
-
with open(f'{_path}/CaseDumpFolder_{CaseName}_{DateName}/oT_Case_{CaseName}_Parameters.csv', 'w', newline='') as csvfile:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# Extract and write variables
|
|
142
|
-
with open(f'{_path}/CaseDumpFolder_{CaseName}_{DateName}/oT_Case_{CaseName}_Variables.csv', 'w', newline='') as csvfile:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# Extract and write dual variables
|
|
151
|
-
with open(f'{_path}/CaseDumpFolder_{CaseName}_{DateName}/oT_Case_{CaseName}_Constraints.csv', 'w', newline='') as csvfile:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
287
|
+
# with open(f'{_path}/CaseDumpFolder_{CaseName}_{DateName}/oT_Case_{CaseName}_Parameters.csv', 'w', newline='') as csvfile:
|
|
288
|
+
# writer = csv.writer(csvfile)
|
|
289
|
+
# writer.writerow(['Name', 'Index', 'Value'])
|
|
290
|
+
# for par in OptModel.component_objects(pyo.Param):
|
|
291
|
+
# par_object = getattr(OptModel, str(par))
|
|
292
|
+
# if par_object.is_indexed():
|
|
293
|
+
# for index in par_object:
|
|
294
|
+
# if (isinstance(index, tuple) and par_object.mutable == False) or par_object.mutable == False:
|
|
295
|
+
# writer.writerow([str(par), index, par_object[index]])
|
|
296
|
+
# else:
|
|
297
|
+
# writer.writerow([str(par), index, par_object[index].value])
|
|
298
|
+
# else:
|
|
299
|
+
# writer.writerow ([str(par), 'NA', par_object.value])
|
|
300
|
+
#
|
|
301
|
+
# # Extract and write variables
|
|
302
|
+
# with open(f'{_path}/CaseDumpFolder_{CaseName}_{DateName}/oT_Case_{CaseName}_Variables.csv', 'w', newline='') as csvfile:
|
|
303
|
+
# writer = csv.writer(csvfile)
|
|
304
|
+
# writer.writerow(['Name', 'Index', 'Value', 'Lower Bound', 'Upper Bound'])
|
|
305
|
+
# for var in OptModel.component_objects(pyo.Var, active=True):
|
|
306
|
+
# var_object = getattr(OptModel, str(var))
|
|
307
|
+
# for index in var_object:
|
|
308
|
+
# writer.writerow([str(var), index, var_object[index].value, str(var_object[index].lb), str(var_object[index].ub)])
|
|
309
|
+
#
|
|
310
|
+
# # Extract and write dual variables
|
|
311
|
+
# with open(f'{_path}/CaseDumpFolder_{CaseName}_{DateName}/oT_Case_{CaseName}_Constraints.csv', 'w', newline='') as csvfile:
|
|
312
|
+
# writer = csv.writer(csvfile)
|
|
313
|
+
# writer.writerow(['Name', 'Index', 'Value', 'Lower Bound', 'Upper Bound'])
|
|
314
|
+
# for con in OptModel.component_objects(pyo.Constraint, active=True):
|
|
315
|
+
# con_object = getattr(OptModel, str(con))
|
|
316
|
+
# if con.is_indexed():
|
|
317
|
+
# for index in con_object:
|
|
318
|
+
# writer.writerow([str(con), index, mTEPES.pDuals[str(con_object.name)+str(index)], str(con_object[index].lb), str(con_object[index].ub)])
|
|
319
|
+
# # writer.writerow([str(con), index, OptModel.dual[con_object[index]], str(con_object[index].lb), str(con_object[index].ub)])
|
|
160
320
|
|
|
161
321
|
# NameList = ['Parameters', 'Variables', 'Constraints']
|
|
162
322
|
#
|
|
@@ -267,9 +427,9 @@ def InvestmentResults(DirName, CaseName, OptModel, mTEPES, pIndTechnologyOutput,
|
|
|
267
427
|
|
|
268
428
|
MarketResultsInv = pd.concat([MarketResultsInv, OutputResults], axis=1)
|
|
269
429
|
|
|
270
|
-
GenTechInvestCost = pd.Series(data=[sum(mTEPES.pDiscountedWeight[p] * mTEPES.pGenInvestCost[eb] * OptModel.vGenerationInvest[p,eb]() for eb in mTEPES.eb if eb in g2t[gt] ) for p,gt in mTEPES.p*mTEPES.gt], index=mTEPES.p*mTEPES.gt)
|
|
430
|
+
GenTechInvestCost = pd.Series(data=[sum(mTEPES.pDiscountedWeight[p] * mTEPES.pGenInvestCost[eb] * OptModel.vGenerationInvest[p,eb]() for eb in mTEPES.eb if eb in g2t[gt] and (p,eb) in mTEPES.peb ) for p,gt in mTEPES.p*mTEPES.gt], index=mTEPES.p*mTEPES.gt)
|
|
271
431
|
GenTechInvestCost *= 1e3
|
|
272
|
-
GenTechInjection = pd.Series(data=[sum(mTEPES.pDiscountedWeight[p] * mTEPES.pLoadLevelDuration[p,sc,n]() * OptModel.vTotalOutput[p,sc,n,eb]() for sc,n,eb in mTEPES.sc*mTEPES.n*mTEPES.eb if eb in g2t[gt] and (p,sc,n) in mTEPES.psn) for p,gt in mTEPES.p*mTEPES.gt], index=mTEPES.p*mTEPES.gt)
|
|
432
|
+
GenTechInjection = pd.Series(data=[sum(mTEPES.pDiscountedWeight[p] * mTEPES.pLoadLevelDuration[p,sc,n]() * OptModel.vTotalOutput[p,sc,n,eb]() for sc,n,eb in mTEPES.sc*mTEPES.n*mTEPES.eb if eb in g2t[gt] and (p,eb) in mTEPES.peb and (p,sc,n) in mTEPES.psn) for p,gt in mTEPES.p*mTEPES.gt], index=mTEPES.p*mTEPES.gt)
|
|
273
433
|
GenTechInjection.name = 'Generation'
|
|
274
434
|
MarketResultsInv = pd.concat([MarketResultsInv, GenTechInjection], axis=1)
|
|
275
435
|
LCOE = GenTechInvestCost.div(GenTechInjection)
|
|
@@ -2227,24 +2387,24 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2227
2387
|
OutputResults.loc[(OutputResults['Period'] == p) & (OutputResults['Scenario'] == sc), 'MEUR/year'] = OutputResults.loc[(OutputResults['Period'] == p) & (OutputResults['Scenario'] == sc), 'MEUR'] / mTEPES.pDiscountedWeight[p] / mTEPES.pScenProb[p,sc]()
|
|
2228
2388
|
OutputResults.to_csv(f'{_path}/oT_Result_CostSummary_{CaseName}_{ar}.csv', sep=',', index=False)
|
|
2229
2389
|
|
|
2230
|
-
sPSSTNG = [(p,sc,st,n, g) for p,sc,st,n, g in mTEPES.s2n*mTEPES.g if (p,sc,n) in mTEPES.psn]
|
|
2390
|
+
sPSSTNG = [(p,sc,st,n, g) for p,sc,st,n, g in mTEPES.s2n*mTEPES.g if (p,sc,n) in mTEPES.psn and (p,g) in mTEPES.pg]
|
|
2231
2391
|
sPSSTNND = [(p,sc,st,n,nd ) for p,sc,st,n,nd in mTEPES.s2n*mTEPES.nd if sum(1 for g in g2n[nd]) + sum(1 for nf,cc in lout[nd]) + sum(1 for ni,cc in lin[nd]) and (p,sc,n) in mTEPES.psn]
|
|
2232
2392
|
sPSSTNNDG = [(p,sc,st,n,nd,g) for p,sc,st,n,nd,g in sPSSTNND*mTEPES.g if (nd,g) in mTEPES.n2g and (p,g) in mTEPES.pg]
|
|
2233
|
-
OutputResults = pd.Series(data=[ mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vTotalOutput [p,sc,n,g]() for p,sc,st,n,nd,g in sPSSTNNDG], index=pd.Index(sPSSTNNDG))
|
|
2234
|
-
MeanOutput = pd.Series(data=[ OptModel.vTotalOutput [p,sc,n,g]() for p,sc,st,n, g in sPSSTNG
|
|
2393
|
+
OutputResults = pd.Series(data=[ mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vTotalOutput [p,sc,n,g]() for p,sc,st,n,nd,g in sPSSTNNDG if (p,g) in mTEPES.pg], index=pd.Index(sPSSTNNDG))
|
|
2394
|
+
MeanOutput = pd.Series(data=[ OptModel.vTotalOutput [p,sc,n,g]() for p,sc,st,n, g in sPSSTNG if (p,g) in mTEPES.pg], index=pd.Index(sPSSTNG )).groupby(level=4).mean()
|
|
2235
2395
|
MeanOutput *= 1e-3
|
|
2236
2396
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueEnergyGeneration_{CaseName}.csv', sep=',')
|
|
2237
2397
|
OutputResults = pd.Series(data=[ mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vTotalOutput [p,sc,n,g]()/MeanOutput[g] for p,sc,st,n,nd,g in sPSSTNNDG], index=pd.Index(sPSSTNNDG))
|
|
2238
2398
|
OutputResults.to_frame(name='EUR/MWh').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='EUR/MWh').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_GenerationCapturedSRMC_{CaseName}.csv', sep=',')
|
|
2239
2399
|
|
|
2240
2400
|
if mTEPES.eh:
|
|
2241
|
-
sPSSTNES = [(p,sc,st,n, eh) for p,sc,st,n, eh in mTEPES.s2n*mTEPES.eh if (p,sc,n) in mTEPES.psn]
|
|
2242
|
-
sPSSTNNDEH = [(p,sc,st,n,nd,eh) for p,sc,st,n,nd,eh in sPSSTNND*mTEPES.eh
|
|
2243
|
-
OutputResults = pd.Series(data=[-mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vESSTotalCharge[p,sc,n,eh]() for p,sc,st,n,nd,eh in sPSSTNNDEH], index=pd.Index(sPSSTNNDEH))
|
|
2244
|
-
MeanOutput = pd.Series(data=[ OptModel.vESSTotalCharge[p,sc,n,eh]() for p,sc,st,n, eh in sPSSTNES
|
|
2401
|
+
sPSSTNES = [(p,sc,st,n, eh) for p,sc,st,n, eh in mTEPES.s2n*mTEPES.eh if (p,sc,n) in mTEPES.psn and (p,eh) in mTEPES.peh]
|
|
2402
|
+
sPSSTNNDEH = [(p,sc,st,n,nd,eh) for p,sc,st,n,nd,eh in sPSSTNND*mTEPES.eh if (nd,eh) in mTEPES.n2g and (p,eh) in mTEPES.peh]
|
|
2403
|
+
OutputResults = pd.Series(data=[-mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vESSTotalCharge[p,sc,n,eh]() for p,sc,st,n,nd,eh in sPSSTNNDEH if (p,eh) in mTEPES.peh], index=pd.Index(sPSSTNNDEH))
|
|
2404
|
+
MeanOutput = pd.Series(data=[ OptModel.vESSTotalCharge[p,sc,n,eh]() for p,sc,st,n, eh in sPSSTNES if (p,eh) in mTEPES.peh], index=pd.Index(sPSSTNES )).groupby(level=4).mean()
|
|
2245
2405
|
MeanOutput *= 1e-3
|
|
2246
2406
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueEnergyConsumption_{CaseName}.csv', sep=',')
|
|
2247
|
-
OutputResults = pd.Series(data=[-mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vESSTotalCharge[p,sc,n,eh]()/MeanOutput[eh] for p,sc,st,n,nd,eh in sPSSTNNDEH], index=pd.Index(sPSSTNNDEH))
|
|
2407
|
+
OutputResults = pd.Series(data=[-mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vESSTotalCharge[p,sc,n,eh]()/MeanOutput[eh] for p,sc,st,n,nd,eh in sPSSTNNDEH if (p,eh) in mTEPES.peh], index=pd.Index(sPSSTNNDEH))
|
|
2248
2408
|
OutputResults.to_frame(name='EUR/MWh').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='EUR/MWh').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_ConsumptionCapturedSRMC_{CaseName}.csv', sep=',')
|
|
2249
2409
|
|
|
2250
2410
|
if mTEPES.gc:
|
|
@@ -2253,16 +2413,16 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2253
2413
|
sPSSTNNDGC1 = [(p,sc,st,n,nd,gc) for p,sc,st,n,nd,gc in mTEPES.s2n*mTEPES.n2g if gc in mTEPES.gc if (p,gc) in mTEPES.pgc and (p,sc,n) in mTEPES.psn]
|
|
2254
2414
|
OutputToGenRev = pd.Series(data=[mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vTotalOutput [p,sc,n,gc]() for p,sc,st,n,nd,gc in sPSSTNNDGC1], index=pd.Index(sPSSTNNDGC1))
|
|
2255
2415
|
GenRev.append(OutputToGenRev)
|
|
2256
|
-
if len([(p,sc,n,nd,gc)
|
|
2257
|
-
sPSSTNNDGC2 = [(p,sc,st,n,nd,gc) for p,sc,st,n,nd,gc in sPSSTNNDGC1
|
|
2416
|
+
if len([(p,sc,n,nd,gc) for p,sc, n,nd,gc in mTEPES.psn*mTEPES.n2g if gc in mTEPES.gc for ot in mTEPES.ot if (p,sc,n) in mTEPES.psn and (p,gc) in mTEPES.pgc and gc in o2e[ot]]):
|
|
2417
|
+
sPSSTNNDGC2 = [(p,sc,st,n,nd,gc) for p,sc,st,n,nd,gc in sPSSTNNDGC1 for ot in mTEPES.ot if (p,sc,n) in mTEPES.psn and (p,gc) in mTEPES.pgc and gc in o2e[ot]]
|
|
2258
2418
|
OutputChargeRevESS = pd.Series(data=[mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vESSTotalCharge[p,sc,n,gc]() for p,sc,st,n,nd,gc in sPSSTNNDGC2], index=pd.Index(sPSSTNNDGC2))
|
|
2259
2419
|
ChargeRev.append(OutputChargeRevESS)
|
|
2260
|
-
if len([(p,sc,n,nd,gc)
|
|
2261
|
-
sPSSTNNDGC3 = [(p,sc,st,n,nd,gc) for p,sc,st,n,nd,gc in sPSSTNNDGC1
|
|
2420
|
+
if len([(p,sc,n,nd,gc) for p,sc, n,nd,gc in mTEPES.psn*mTEPES.n2g if gc in mTEPES.gc for rt in mTEPES.rt if (p,sc,n) in mTEPES.psn and (p,gc) in mTEPES.pgc and gc in r2r[rt]]):
|
|
2421
|
+
sPSSTNNDGC3 = [(p,sc,st,n,nd,gc) for p,sc,st,n,nd,gc in sPSSTNNDGC1 for rt in mTEPES.rt if (p,sc,n) in mTEPES.psn and (p,gc) in mTEPES.pgc and gc in r2r[rt]]
|
|
2262
2422
|
OutputChargeRevRES = pd.Series(data=[mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]() * 0.0 for p,sc,st,n,nd,gc in sPSSTNNDGC3], index=pd.Index(sPSSTNNDGC3))
|
|
2263
2423
|
ChargeRev.append(OutputChargeRevRES)
|
|
2264
|
-
if len([(p,sc,n,nd,gc)
|
|
2265
|
-
sPSSTNNDGC4 = [(p,sc,st,n,nd,gc) for p,sc,st,n,nd,gc in sPSSTNNDGC1
|
|
2424
|
+
if len([(p,sc,n,nd,gc) for p,sc, n,nd,gc in mTEPES.psn*mTEPES.n2g if gc in mTEPES.gc for ot in mTEPES.ot if (p,sc,n) in mTEPES.psn and (p,gc) in mTEPES.pgc and gc in o2e[ot]]):
|
|
2425
|
+
sPSSTNNDGC4 = [(p,sc,st,n,nd,gc) for p,sc,st,n,nd,gc in sPSSTNNDGC1 for ot in mTEPES.ot if (p,sc,n) in mTEPES.psn and (p,gc) in mTEPES.pgc and gc in o2e[ot]]
|
|
2266
2426
|
OutputChargeRevThr = pd.Series(data=[mTEPES.pDuals["".join([f"eBalanceElec_{p}_{sc}_{st}('{n}', '{nd}')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]() * 0.0 for p,sc,st,n,nd,gc in sPSSTNNDGC4], index=pd.Index(sPSSTNNDGC4))
|
|
2267
2427
|
ChargeRev.append(OutputChargeRevThr)
|
|
2268
2428
|
if len(GenRev):
|
|
@@ -2284,7 +2444,7 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2284
2444
|
|
|
2285
2445
|
if sum(mTEPES.pReserveMargin[:,:]):
|
|
2286
2446
|
if mTEPES.gc:
|
|
2287
|
-
sPSSTARGC = [(p,sc,st,ar,gc) for p,sc,st,ar,gc in mTEPES.ps*mTEPES.st*mTEPES.ar*mTEPES.gc if gc in g2a[ar] and mTEPES.pReserveMargin[p,ar] and st == mTEPES.Last_st and sum(1 for gc in mTEPES.gc if gc in g2a[ar]) and sum(mTEPES.pRatedMaxPowerElec[g] * mTEPES.pAvailability[g]() / (1.0-mTEPES.pEFOR[g]) for g in mTEPES.g if g in g2a[ar] and g not in (mTEPES.gc or mTEPES.gd)) <= mTEPES.pDemandElecPeak[p,ar] * mTEPES.pReserveMargin[p,ar]]
|
|
2447
|
+
sPSSTARGC = [(p,sc,st,ar,gc) for p,sc,st,ar,gc in mTEPES.ps*mTEPES.st*mTEPES.ar*mTEPES.gc if gc in g2a[ar] and (p,gc) in mTEPES.pgc and mTEPES.pReserveMargin[p,ar] and st == mTEPES.Last_st and sum(1 for gc in mTEPES.gc if gc in g2a[ar]) and sum(mTEPES.pRatedMaxPowerElec[g] * mTEPES.pAvailability[g]() / (1.0-mTEPES.pEFOR[g]) for g in mTEPES.g if g in g2a[ar] and g not in (mTEPES.gc or mTEPES.gd)) <= mTEPES.pDemandElecPeak[p,ar] * mTEPES.pReserveMargin[p,ar]]
|
|
2288
2448
|
OutputToResRev = pd.Series(data=[mTEPES.pDuals[''.join([f'eAdequacyReserveMarginElec_{p}_{sc}_{st}{ar}'])]*mTEPES.pRatedMaxPowerElec[gc]*mTEPES.pAvailability[gc]() for p,sc,st,ar,gc in sPSSTARGC], index=pd.Index(sPSSTARGC))
|
|
2289
2449
|
ResRev = pd.Series(data=[0.0 for gc in mTEPES.gc], index=mTEPES.gc, dtype='float64')
|
|
2290
2450
|
if sPSSTARGC:
|
|
@@ -2296,18 +2456,18 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2296
2456
|
ResRev = pd.Series(data=[0.0 for gc in mTEPES.gc], index=mTEPES.gc, dtype='float64')
|
|
2297
2457
|
|
|
2298
2458
|
if sum(mTEPES.pOperReserveUp[:,:,:,:]) and sum(1 for ar,nr in mTEPES.ar*mTEPES.nr if nr in g2a[ar] and (mTEPES.pIndOperReserveGen[nr] == 0 or mTEPES.pIndOperReserveCon[nr] == 0)) + sum(1 for ar,es in mTEPES.ar*mTEPES.es if es in g2a[ar] and (mTEPES.pIndOperReserveGen[nr] == 0 or mTEPES.pIndOperReserveCon[nr] == 0)):
|
|
2299
|
-
if len([(p,sc,n,ar,nr)
|
|
2300
|
-
sPSSTNARNR = [(p,sc,st,n,ar,nr) for p,sc,st,n,ar,nr in mTEPES.s2n*mTEPES.ar*mTEPES.nr if nr in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,nr) in mTEPES.psnnr]
|
|
2459
|
+
if len([(p,sc,n,ar,nr) for p,sc, n,ar,nr in mTEPES.psn*mTEPES.ar*mTEPES.nr if nr in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]):
|
|
2460
|
+
sPSSTNARNR = [(p,sc,st,n,ar,nr) for p,sc,st,n,ar,nr in mTEPES.s2n*mTEPES.ar*mTEPES.nr if nr in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]
|
|
2301
2461
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eOperReserveUp_{p}_{sc}_{st}('{n}', '{ar}')"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vReserveUp [p,sc,n,nr]() for p,sc,st,n,ar,nr in sPSSTNARNR], index=pd.Index(sPSSTNARNR))
|
|
2302
2462
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueOperatingReserveUp_{CaseName}.csv', sep=',')
|
|
2303
2463
|
|
|
2304
|
-
if len([(p,sc,n,ar,eh)
|
|
2305
|
-
sPSSTNARES = [(p,sc,st,n,ar,eh) for p,sc,st,n,ar,eh in mTEPES.s2n*mTEPES.ar*mTEPES.eh if eh in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,eh) in mTEPES.psnehc]
|
|
2464
|
+
if len([(p,sc,n,ar,eh) for p,sc, n,ar,eh in mTEPES.psn*mTEPES.ar*mTEPES.eh if eh in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]):
|
|
2465
|
+
sPSSTNARES = [(p,sc,st,n,ar,eh) for p,sc,st,n,ar,eh in mTEPES.s2n*mTEPES.ar*mTEPES.eh if eh in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]
|
|
2306
2466
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eOperReserveUp_{p}_{sc}_{st}('{n}', '{ar}')"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vESSReserveUp[p,sc,n,eh]() for p,sc,st,n,ar,eh in sPSSTNARES], index=pd.Index(sPSSTNARES))
|
|
2307
2467
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueOperatingReserveUpESS_{CaseName}.csv', sep=',')
|
|
2308
2468
|
|
|
2309
|
-
if len([(p,sc,n,ar,ec)
|
|
2310
|
-
sPSSTNAREC = [(p,sc,st,n,ar,ec) for p,sc,st,n,ar,ec in mTEPES.s2n*mTEPES.ar*mTEPES.ec if ec in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,ec) in mTEPES.psnec]
|
|
2469
|
+
if len([(p,sc,n,ar,ec) for p,sc, n,ar,ec in mTEPES.psn*mTEPES.ar*mTEPES.gc if ec in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]):
|
|
2470
|
+
sPSSTNAREC = [(p,sc,st,n,ar,ec) for p,sc,st,n,ar,ec in mTEPES.s2n*mTEPES.ar*mTEPES.ec if ec in g2a[ar] and mTEPES.pOperReserveUp[p,sc,n,ar] and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]
|
|
2311
2471
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eOperReserveUp_{p}_{sc}_{st}('{n}', '{ar}')"])]/mTEPES.pPeriodProb[p,sc]()*(OptModel.vReserveUp[p,sc,n,ec]()+OptModel.vESSReserveUp[p,sc,n,ec]()) for p,sc,st,n,ar,ec in sPSSTNAREC], index=pd.Index(sPSSTNAREC), dtype='float64')
|
|
2312
2472
|
if len(OutputResults):
|
|
2313
2473
|
OutputToUpRev = OutputResults.to_frame('MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).sum(axis=0)
|
|
@@ -2323,18 +2483,18 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2323
2483
|
UpRev = pd.Series(data=[0.0 for gc in mTEPES.gc], index=mTEPES.gc, dtype='float64')
|
|
2324
2484
|
|
|
2325
2485
|
if sum(mTEPES.pOperReserveDw[:,:,:,:]) and sum(1 for ar,nr in mTEPES.ar*mTEPES.nr if nr in g2a[ar] and (mTEPES.pIndOperReserveGen[nr] == 0 or mTEPES.pIndOperReserveGen[nr] == 0 )) + sum(1 for ar,es in mTEPES.ar*mTEPES.es if es in g2a[ar] and (mTEPES.pIndOperReserveGen[es] == 0 or mTEPES.pIndOperReserveCon[es] == 0 )):
|
|
2326
|
-
if len([(p,sc,n,ar,nr)
|
|
2327
|
-
sPSSTNARNR = [(p,sc,st,n,ar,nr) for p,sc,st,n,ar,nr in mTEPES.s2n*mTEPES.ar*mTEPES.nr if nr in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,nr) in mTEPES.psnnr]
|
|
2486
|
+
if len([(p,sc,n,ar,nr) for p,sc, n,ar,nr in mTEPES.psn*mTEPES.ar*mTEPES.nr if nr in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]):
|
|
2487
|
+
sPSSTNARNR = [(p,sc,st,n,ar,nr) for p,sc,st,n,ar,nr in mTEPES.s2n*mTEPES.ar*mTEPES.nr if nr in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]
|
|
2328
2488
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eOperReserveDw_{p}_{sc}_{st}('{n}', '{ar}')"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vReserveDown [p,sc,n,nr]() for p,sc,st,n,ar,nr in sPSSTNARNR], index=pd.Index(sPSSTNARNR))
|
|
2329
2489
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueOperatingReserveDw_{CaseName}.csv', sep=',')
|
|
2330
2490
|
|
|
2331
|
-
if len([(p,sc,n,ar,eh)
|
|
2332
|
-
sPSSTNARES = [(p,sc,st,n,ar,eh) for p,sc,st,n,ar,eh in mTEPES.s2n*mTEPES.ar*mTEPES.eh if eh in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,eh) in mTEPES.psnehc]
|
|
2491
|
+
if len([(p,sc,n,ar,eh) for p,sc, n,ar,eh in mTEPES.psn*mTEPES.ar*mTEPES.eh if eh in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]):
|
|
2492
|
+
sPSSTNARES = [(p,sc,st,n,ar,eh) for p,sc,st,n,ar,eh in mTEPES.s2n*mTEPES.ar*mTEPES.eh if eh in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]
|
|
2333
2493
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eOperReserveDw_{p}_{sc}_{st}('{n}', '{ar}')"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vESSReserveDown[p,sc,n,eh]() for p,sc,st,n,ar,eh in sPSSTNARES], index=pd.Index(sPSSTNARES))
|
|
2334
2494
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueOperatingReserveDwESS_{CaseName}.csv', sep=',')
|
|
2335
2495
|
|
|
2336
|
-
if len([(p,sc,n,ar,ec)
|
|
2337
|
-
sPSSTNAREC = [(p,sc,st,n,ar,ec) for p,sc,st,n,ar,ec in mTEPES.s2n*mTEPES.ar*mTEPES.ec if ec in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,ec) in mTEPES.psnec]
|
|
2496
|
+
if len([(p,sc,n,ar,ec) for p,sc, n,ar,ec in mTEPES.psn*mTEPES.ar*mTEPES.ec if ec in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]):
|
|
2497
|
+
sPSSTNAREC = [(p,sc,st,n,ar,ec) for p,sc,st,n,ar,ec in mTEPES.s2n*mTEPES.ar*mTEPES.ec if ec in g2a[ar] and mTEPES.pOperReserveDw[p,sc,n,ar] and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]
|
|
2338
2498
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eOperReserveDw_{p}_{sc}_{st}('{n}', '{ar}')"])]/mTEPES.pPeriodProb[p,sc]()*(OptModel.vReserveDown[p,sc,n,ec]()+OptModel.vESSReserveDown[p,sc,n,ec]()) for p,sc,st,n,ar,ec in sPSSTNAREC], index=pd.Index(sPSSTNAREC), dtype='float64')
|
|
2339
2499
|
if len(OutputResults):
|
|
2340
2500
|
OutputToDwRev = OutputResults.to_frame('MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_5', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).sum(axis=0)
|
|
@@ -2349,18 +2509,18 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2349
2509
|
DwRev = pd.Series(data=[0.0 for gc in mTEPES.gc], index=mTEPES.gc, dtype='float64')
|
|
2350
2510
|
|
|
2351
2511
|
if mTEPES.pIndRampReserves == 1 and sum(mTEPES.pRampReserveUp[:,:,:,:]):
|
|
2352
|
-
if len([(p,sc,n,nr)
|
|
2353
|
-
sPSSTNNR = [(p,sc,st,n,nr) for p,sc,st,n,nr in mTEPES.s2n*mTEPES.nr if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,nr) in mTEPES.psnnr]
|
|
2512
|
+
if len([(p,sc,n,nr) for p,sc, n,nr in mTEPES.psn*mTEPES.nr if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]):
|
|
2513
|
+
sPSSTNNR = [(p,sc,st,n,nr) for p,sc,st,n,nr in mTEPES.s2n*mTEPES.nr if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]
|
|
2354
2514
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eSystemRampUp_{p}_{sc}_{st}{n}"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vRampReserveUp[p,sc,n,nr]() for p,sc,st,n,nr in sPSSTNNR], index=pd.Index(sPSSTNNR))
|
|
2355
2515
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_4', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueRampReserveUp_{CaseName}.csv', sep=',')
|
|
2356
2516
|
|
|
2357
|
-
# if len([(p,sc,n,eh)
|
|
2358
|
-
# sPSSTNES = [(p,sc,st,n,eh) for p,sc,st,n,eh in mTEPES.s2n*mTEPES.eh if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,eh) in mTEPES.psnehc]
|
|
2517
|
+
# if len([(p,sc,n,eh) for p,sc, n,eh in mTEPES.psn*mTEPES.eh if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]):
|
|
2518
|
+
# sPSSTNES = [(p,sc,st,n,eh) for p,sc,st,n,eh in mTEPES.s2n*mTEPES.eh if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]
|
|
2359
2519
|
# OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eSystemRampUp_{p}_{sc}_{st}{n}"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vRampReserveUp[p,sc,n,eh]() for p,sc,st,n,eh in sPSSTNES], index=pd.Index(sPSSTNES))
|
|
2360
2520
|
# OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_4', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueRampReserveUpESS_{CaseName}.csv', sep=',')
|
|
2361
2521
|
|
|
2362
|
-
if len([(p,sc,n,ec)
|
|
2363
|
-
sPSSTNEC = [(p,sc,st,n,ec) for p,sc,st,n,ec in mTEPES.s2n*mTEPES.ec if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,ec) in mTEPES.psnec]
|
|
2522
|
+
if len([(p,sc,n,ec) for p,sc, n,ec in mTEPES.psn*mTEPES.gc if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]):
|
|
2523
|
+
sPSSTNEC = [(p,sc,st,n,ec) for p,sc,st,n,ec in mTEPES.s2n*mTEPES.ec if sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]
|
|
2364
2524
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eSystemRampUp_{p}_{sc}_{st}{n}"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vRampReserveUp[p,sc,n,ec]() for p,sc,st,n,ec in sPSSTNEC], index=pd.Index(sPSSTNEC), dtype='float64')
|
|
2365
2525
|
if len(OutputResults):
|
|
2366
2526
|
OutputToUpRev = OutputResults.to_frame('MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_4', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).sum(axis=0)
|
|
@@ -2376,18 +2536,18 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2376
2536
|
RampUpRev = pd.Series(data=[0.0 for gc in mTEPES.gc], index=mTEPES.gc, dtype='float64')
|
|
2377
2537
|
|
|
2378
2538
|
if mTEPES.pIndRampReserves == 1 and sum(mTEPES.pRampReserveDw[:,:,:,:]):
|
|
2379
|
-
if len([(p,sc,n,nr)
|
|
2380
|
-
sPSSTNNR = [(p,sc,st,n,nr) for p,sc,st,n,nr in mTEPES.s2n*mTEPES.nr if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,nr) in mTEPES.psnnr]
|
|
2539
|
+
if len([(p,sc,n,nr) for p,sc, n,nr in mTEPES.psn*mTEPES.nr if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]):
|
|
2540
|
+
sPSSTNNR = [(p,sc,st,n,nr) for p,sc,st,n,nr in mTEPES.s2n*mTEPES.nr if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,nr) in mTEPES.psnnr and (p,nr) in mTEPES.pnr]
|
|
2381
2541
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eSystemRampDw_{p}_{sc}_{st}{n}"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vRampReserveDw[p,sc,n,nr]() for p,sc,st,n,nr in sPSSTNNR], index=pd.Index(sPSSTNNR))
|
|
2382
2542
|
OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_4', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueRampReserveDw_{CaseName}.csv', sep=',')
|
|
2383
2543
|
|
|
2384
|
-
# if len([(p,sc,n,eh)
|
|
2385
|
-
# sPSSTNES = [(p,sc,st,n,eh) for p,sc,st,n,eh in mTEPES.s2n*mTEPES.eh if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,eh) in mTEPES.psnehc]
|
|
2544
|
+
# if len([(p,sc,n,eh) for p,sc, n,eh in mTEPES.psn*mTEPES.eh if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]):
|
|
2545
|
+
# sPSSTNES = [(p,sc,st,n,eh) for p,sc,st,n,eh in mTEPES.s2n*mTEPES.eh if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,eh) in mTEPES.psnehc and (p,eh) in mTEPES.peh]
|
|
2386
2546
|
# OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eSystemRampDw_{p}_{sc}_{st}{n}"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vRampReserveDw[p,sc,n,eh]() for p,sc,st,n,eh in sPSSTNES], index=pd.Index(sPSSTNES))
|
|
2387
2547
|
# OutputResults.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_4', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_RevenueRampReserveDwESS_{CaseName}.csv', sep=',')
|
|
2388
2548
|
|
|
2389
|
-
if len([(p,sc,n,ec)
|
|
2390
|
-
sPSSTNEC = [(p,sc,st,n,ec) for p,sc,st,n,ec in mTEPES.s2n*mTEPES.ec if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,ec) in mTEPES.psnec]
|
|
2549
|
+
if len([(p,sc,n,ec) for p,sc, n,ec in mTEPES.psn*mTEPES.gc if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]):
|
|
2550
|
+
sPSSTNEC = [(p,sc,st,n,ec) for p,sc,st,n,ec in mTEPES.s2n*mTEPES.ec if sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) and (p,sc,n,ec) in mTEPES.psnec and (p,ec) in mTEPES.pec]
|
|
2391
2551
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join([f"eSystemRampDw_{p}_{sc}_{st}{n}"])]/mTEPES.pPeriodProb[p,sc]()*OptModel.vRampReserveUp[p,sc,n,ec]() for p,sc,st,n,ec in sPSSTNEC], index=pd.Index(sPSSTNEC), dtype='float64')
|
|
2392
2552
|
if len(OutputResults):
|
|
2393
2553
|
OutputToDwRev = OutputResults.to_frame('MEUR').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_4', values='MEUR').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).sum(axis=0)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 21, 2026
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -50,8 +50,8 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
50
50
|
# Solver.options['RINSHeur' ] = 100
|
|
51
51
|
# Solver.options['EpGap' ] = 0.01
|
|
52
52
|
Solver.options['Threads' ] = int((psutil.cpu_count(logical=True) + psutil.cpu_count(logical=False))/2)
|
|
53
|
-
Solver.options['TimeLimit' ] =
|
|
54
|
-
# Solver.options['ItLim' ] =
|
|
53
|
+
Solver.options['TimeLimit' ] = 72000
|
|
54
|
+
# Solver.options['ItLim' ] = 72000000
|
|
55
55
|
if SolverName == 'appsi_highs':
|
|
56
56
|
FileName = f'{_path}/openTEPES_highs_{CaseName}_{p}_{sc}_{st}.log'
|
|
57
57
|
if os.path.exists(FileName):
|
|
@@ -63,15 +63,15 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
63
63
|
Solver.options['mip_rel_gap' ] = 0.01
|
|
64
64
|
Solver.options['parallel' ] = 'on'
|
|
65
65
|
Solver.options['threads' ] = int((psutil.cpu_count(logical=True) + psutil.cpu_count(logical=False))/2)
|
|
66
|
-
Solver.options['time_limit' ] =
|
|
67
|
-
Solver.options['simplex_iteration_limit'] =
|
|
66
|
+
Solver.options['time_limit' ] = 72000
|
|
67
|
+
Solver.options['simplex_iteration_limit'] = 72000000
|
|
68
68
|
if SolverName == 'gams':
|
|
69
69
|
FileName = f'{_path}/openTEPES_gams_{CaseName}_{p}_{sc}_{st}.log'
|
|
70
70
|
solver_options = {
|
|
71
71
|
'file COPT / cplex.opt / ; put COPT putclose "LPMethod 4" / "EpGap 0.01" / ; GAMS_MODEL.OptFile = 1 ; '
|
|
72
72
|
'option SysOut = off ;',
|
|
73
73
|
'option LP = cplex ; option MIP = cplex ;',
|
|
74
|
-
'option ResLim =
|
|
74
|
+
'option ResLim = 72000 ; option IterLim = 72000000 ;',
|
|
75
75
|
'option Threads = '+str(int((psutil.cpu_count(logical=True) + psutil.cpu_count(logical=False))/2))+' ;'
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openTEPES
|
|
3
|
-
Version: 4.18.
|
|
3
|
+
Version: 4.18.9
|
|
4
4
|
Summary: Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES)
|
|
5
5
|
Home-page: https://opentepes.readthedocs.io/en/latest/index.html
|
|
6
6
|
Author: IIT-EnergySystemModels
|
|
@@ -11,19 +11,19 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
11
11
|
Classifier: Intended Audience :: Science/Research
|
|
12
12
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
License-File: LICENSE
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist: matplotlib>=3.3.4
|
|
18
|
-
Requires-Dist: numpy>=1.20.1
|
|
19
|
-
Requires-Dist: pandas>=1.2.3
|
|
20
|
-
Requires-Dist: plotly>=5.7.0
|
|
16
|
+
Requires-Dist: altair>=5.5.0
|
|
21
17
|
Requires-Dist: colour>=0.1.5
|
|
22
|
-
Requires-Dist: altair>=5.0.0
|
|
23
|
-
Requires-Dist: psutil>=5.8.0
|
|
24
|
-
Requires-Dist: jsonschema>=4.16.0
|
|
25
|
-
Requires-Dist: networkx>=3.3
|
|
26
18
|
Requires-Dist: dill>=0.3.8
|
|
19
|
+
Requires-Dist: duckdb>=1.3.2
|
|
20
|
+
Requires-Dist: jsonschema>=4.26.0
|
|
21
|
+
Requires-Dist: matplotlib>=3.10.0
|
|
22
|
+
Requires-Dist: networkx>=3.6.1
|
|
23
|
+
Requires-Dist: pandas>=2.2.2
|
|
24
|
+
Requires-Dist: plotly>=5.24.1
|
|
25
|
+
Requires-Dist: psutil>=5.9.5
|
|
26
|
+
Requires-Dist: pyomo>=6.9.5
|
|
27
27
|
|
|
28
28
|
.. image:: https://raw.githubusercontent.com/IIT-EnergySystemModels/openTEPES/refs/heads/master/doc/img/openTEPES.png
|
|
29
29
|
:target: https://opentepes.readthedocs.io/en/latest/index.html
|
|
@@ -123,7 +123,7 @@ It determines automatically optimal expansion plans that satisfy simultaneously
|
|
|
123
123
|
|
|
124
124
|
It represents hierarchically the different time scopes to make decisions in an electric system:
|
|
125
125
|
|
|
126
|
-
- Load level: one hour, e.g., 01-01 00:00:00+01:00 to 12-30 23:00:00+01:00, or quarter of an hour
|
|
126
|
+
- Load level: one hour, e.g., 01-01 00:00:00+01:00 to 12-30 23:00:00+01:00, or **quarter of an hour**, e.g., 01-01 00:00:00+01:00 to 30-12 23:45:00+01:00
|
|
127
127
|
|
|
128
128
|
The time division allows a user-defined flexible representation of the periods for evaluating the system operation. Moreover, it can be run with chronological periods of several consecutive hours (bi-hourly, tri-hourly resolution in case of hourly definition or half an hour in case of quarter of an hour definition) to decrease the computational burden without losing accuracy. The model can be run with a single period (year) or with several periods (years) to allow the analysis of the system's evolution.
|
|
129
129
|
The time definition also allows for specifying disconnected representative periods (e.g., days, weeks) to evaluate the system operation.
|
|
@@ -131,7 +131,7 @@ It determines automatically optimal expansion plans that satisfy simultaneously
|
|
|
131
131
|
The period (year) must be represented by 8736 hours because several model concepts representing the system operation are based on weeks (168 hours) or months (made of 4 weeks, 672 hours).
|
|
132
132
|
|
|
133
133
|
- **Stochastic**: several stochastic parameters that can influence the optimal generation, storage, and transmission expansion decisions are considered. The model considers stochastic
|
|
134
|
-
medium-term yearly uncertainties (scenarios) related to the system operation. These operation scenarios are associated with renewable energy sources, energy inflows and outflows, natural water inflows, operating reserves, inertia, and electricity, hydrogen, and heat demand.
|
|
134
|
+
medium-term yearly uncertainties (scenarios) related to the system operation. These operation scenarios are associated with renewable energy sources, energy inflows and outflows, natural water inflows, operating reserves, ramp reserves, inertia, and electricity, hydrogen, and heat demand.
|
|
135
135
|
|
|
136
136
|
The objective function incorporates the two main quantifiable costs: **generation, storage, and transmission investment cost (CAPEX)** and **expected variable operation costs (including generation, consumption, emission, and reliability costs) (system OPEX)**.
|
|
137
137
|
|
|
@@ -150,10 +150,10 @@ Besides, it includes a representation of **Power to Heat (P2H)** by setting the
|
|
|
150
150
|
The main results of the model can be structured into these topics:
|
|
151
151
|
|
|
152
152
|
- **Investment**: (generation, storage, hydro reservoirs, electric lines, hydrogen pipelines, and heat pipes) investment decisions and cost
|
|
153
|
-
- **Operation**: unit commitment, startup, and shutdown of non-renewable units, unit output and aggregation by technologies (thermal, storage hydro, pumped-hydro storage, RES), RES curtailment, electric line, hydrogen pipeline, and heat pipe flows, line ohmic losses, node voltage angles, upward and downward operating reserves, ESS inventory levels, hydro reservoir volumes, power, hydrogen, and heat not served
|
|
153
|
+
- **Operation**: unit commitment, startup, and shutdown of non-renewable units, unit output and aggregation by technologies (thermal, storage hydro, pumped-hydro storage, RES), RES curtailment, electric line, hydrogen pipeline, and heat pipe flows, line ohmic losses, node voltage angles, upward and downward operating reserves, upward and downward ramp reserves, ESS inventory levels, hydro reservoir volumes, power, hydrogen, and heat not served
|
|
154
154
|
- **Emissions**: CO2 emissions by unit
|
|
155
155
|
- **Marginal**: Locational Short-Run Marginal Costs (LSRMC), stored energy value, water volume value
|
|
156
|
-
- **Economic**: operation, emission, and reliability costs and revenues from operation and
|
|
156
|
+
- **Economic**: operation, emission, and reliability costs and revenues from operation, operating reserves, and ramp reserves
|
|
157
157
|
- **Flexibility**: flexibility provided by demand, by the different generation and consumption technologies, and by power not served
|
|
158
158
|
|
|
159
159
|
Results are shown in csv files and graphical plots.
|
|
@@ -164,7 +164,7 @@ Installation
|
|
|
164
164
|
############
|
|
165
165
|
`Installation guide <https://pascua.iit.comillas.edu/aramos/openTEPES_installation.pdf>`_.
|
|
166
166
|
|
|
167
|
-
There are 2 ways to get all the required packages under Windows. We recommend using the Python distribution Miniconda. If you don't want to use it or already have an existing Python
|
|
167
|
+
There are 2 ways to get all the required packages under Windows. We recommend using the Python distribution Miniconda. If you don't want to use it or already have an existing Python installation, you can also download the required packages by yourself.
|
|
168
168
|
|
|
169
169
|
Miniconda (recommended)
|
|
170
170
|
=======================
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
openTEPES/__init__.py,sha256=
|
|
1
|
+
openTEPES/__init__.py,sha256=FozTs8PMPcaz1sPtbHKQYWkBTIQar2I6GWwQGq69oXI,832
|
|
2
2
|
openTEPES/openTEPES.mapbox_token,sha256=xsXNkwGp2vzXqQy2zVkyLhhNcNWniK2BMeOFpc5SZHI,93
|
|
3
|
-
openTEPES/openTEPES.py,sha256=
|
|
4
|
-
openTEPES/openTEPES_InputData.py,sha256=
|
|
5
|
-
openTEPES/openTEPES_Main.py,sha256=
|
|
3
|
+
openTEPES/openTEPES.py,sha256=CnikcchmCP49DjB81RuAtxz8TEK8MSKMhwV1nEeTbMI,27461
|
|
4
|
+
openTEPES/openTEPES_InputData.py,sha256=ue_B_yaANq5k4w0vo9CPkESWpXfb5BZjF1xFqRpw3to,245619
|
|
5
|
+
openTEPES/openTEPES_Main.py,sha256=glvDODa5hfmSLTXIKamQmt3rlkTFN6d2CMFNMXXuCs0,40782
|
|
6
6
|
openTEPES/openTEPES_ModelFormulation.py,sha256=g9nRAbJpZ8r1_aIP-EH6t7mJhoWDB8uj8Q8zZjVnwjM,141751
|
|
7
|
-
openTEPES/openTEPES_OutputResults.py,sha256=
|
|
8
|
-
openTEPES/openTEPES_ProblemSolving.py,sha256=
|
|
7
|
+
openTEPES/openTEPES_OutputResults.py,sha256=rIcdKqhVyzaizcZiPtN0uGcst1-SHuFF9gs1Fj-zJjM,242215
|
|
8
|
+
openTEPES/openTEPES_ProblemSolving.py,sha256=gORJdW46pSzphOfjIT-vSJ2kgkAEuPWh0exqm7ozx0Q,16766
|
|
9
9
|
openTEPES/openTEPES_gitinfo.py,sha256=6fA1fa-JcyusSc_HcjPiCgnV9zn-fZwdG-kK0a5Fxc8,2004
|
|
10
10
|
openTEPES/.idea/.name,sha256=jiwfcnJ20wztcvpny4SHcqmAIWK-w5tCqN9TWf0GOkw,11
|
|
11
11
|
openTEPES/.idea/misc.xml,sha256=m4-3O284ZBS8WZSQD0QWwk8YjuQYz92w6kr7NRFNips,298
|
|
@@ -384,8 +384,8 @@ openTEPES/sSEP/oT_Dict_Storage_sSEP.csv,sha256=H2rJXZvoMuT-25sI2GpG8IuiNKD-dxuty
|
|
|
384
384
|
openTEPES/sSEP/oT_Dict_Technology_sSEP.csv,sha256=MCTpplzz7_eVPKQfOw35c86ib6CTtW6UK6JrbCJ8wls,170
|
|
385
385
|
openTEPES/sSEP/oT_Dict_ZoneToArea_sSEP.csv,sha256=AUDCs5Bg6sw9f2pVjGP1o4IJjXFF_VrokOGf_V3QsEI,24
|
|
386
386
|
openTEPES/sSEP/oT_Dict_Zone_sSEP.csv,sha256=TBud-fvbFbiAsuutxTYe8wWlv_x1P8oyWXILMpYiXJc,13
|
|
387
|
-
opentepes-4.18.
|
|
388
|
-
opentepes-4.18.
|
|
389
|
-
opentepes-4.18.
|
|
390
|
-
opentepes-4.18.
|
|
391
|
-
opentepes-4.18.
|
|
387
|
+
opentepes-4.18.9.dist-info/entry_points.txt,sha256=gNNPrDaTsRuRJXI1FLNgqMX1CiJ45bEp1dEDH7ZB8Oc,49
|
|
388
|
+
opentepes-4.18.9.dist-info/licenses/LICENSE,sha256=4O7bphXVzRuYavtsWzpLGuM3E-fp3HTRna7F4yIfnS4,35184
|
|
389
|
+
opentepes-4.18.9.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
390
|
+
opentepes-4.18.9.dist-info/METADATA,sha256=7bDfXPGg6f_5jya58I6bg6evEtMQx8TDoxr-OaSrbTs,19420
|
|
391
|
+
opentepes-4.18.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|