openTEPES 4.17.7__py3-none-any.whl → 4.17.8__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 +45 -29
- openTEPES/openTEPES_Main.py +2 -2
- openTEPES/openTEPES_ModelFormulation.py +69 -40
- openTEPES/openTEPES_OutputResults.py +82 -83
- openTEPES/openTEPES_ProblemSolving.py +8 -8
- {opentepes-4.17.7.dist-info → opentepes-4.17.8.dist-info}/METADATA +5 -6
- {opentepes-4.17.7.dist-info → opentepes-4.17.8.dist-info}/RECORD +12 -12
- {opentepes-4.17.7.dist-info → opentepes-4.17.8.dist-info}/WHEEL +1 -1
- {opentepes-4.17.7.dist-info → opentepes-4.17.8.dist-info}/LICENSE +0 -0
- {opentepes-4.17.7.dist-info → opentepes-4.17.8.dist-info}/entry_points.txt +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.17.
|
|
17
|
+
__version__ = "4.17.8"
|
|
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) -
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 30, 2024
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
# import dill as pickle
|
|
@@ -39,8 +39,8 @@ def openTEPES_run(DirName, CaseName, SolverName, pIndOutputResults, pIndLogConso
|
|
|
39
39
|
idxDict['y' ] = 1
|
|
40
40
|
|
|
41
41
|
#%% model declaration
|
|
42
|
-
mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.17.
|
|
43
|
-
print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.17.
|
|
42
|
+
mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.17.8 - October 30, 2024')
|
|
43
|
+
print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.17.8 - October 30, 2024', file=open(_path+'/openTEPES_version_'+CaseName+'.log','w'))
|
|
44
44
|
|
|
45
45
|
pIndOutputResults = [j for i,j in idxDict.items() if i == pIndOutputResults][0]
|
|
46
46
|
pIndLogConsole = [j for i,j in idxDict.items() if i == pIndLogConsole ][0]
|
openTEPES/openTEPES_InputData.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) -
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 30, 2024
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import datetime
|
|
@@ -498,16 +498,21 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
498
498
|
pNetLoInvest = dfNetwork ['InvestmentLo' ] # Lower bound of the investment decision [p.u.]
|
|
499
499
|
pNetUpInvest = dfNetwork ['InvestmentUp' ] # Upper bound of the investment decision [p.u.]
|
|
500
500
|
|
|
501
|
+
# replace PeriodFin = 0.0 by year 3000
|
|
502
|
+
pElecGenPeriodFin = pElecGenPeriodFin.where(pElecGenPeriodFin != 0.0, 3000 )
|
|
503
|
+
if pIndHydroTopology == 1:
|
|
504
|
+
pRsrPeriodFin = pRsrPeriodFin.where (pRsrPeriodFin != 0.0, 3000 )
|
|
505
|
+
pElecNetPeriodFin = pElecNetPeriodFin.where(pElecNetPeriodFin != 0.0, 3000 )
|
|
501
506
|
# replace pLineNTCBck = 0.0 by pLineNTCFrw
|
|
502
|
-
pLineNTCBck
|
|
507
|
+
pLineNTCBck = pLineNTCBck.where (pLineNTCBck > 0.0, pLineNTCFrw)
|
|
503
508
|
# replace pLineNTCFrw = 0.0 by pLineNTCBck
|
|
504
|
-
pLineNTCFrw
|
|
509
|
+
pLineNTCFrw = pLineNTCFrw.where (pLineNTCFrw > 0.0, pLineNTCBck)
|
|
505
510
|
# replace pGenUpInvest = 0.0 by 1.0
|
|
506
|
-
pGenUpInvest
|
|
511
|
+
pGenUpInvest = pGenUpInvest.where (pGenUpInvest > 0.0, 1.0 )
|
|
507
512
|
# replace pGenUpRetire = 0.0 by 1.0
|
|
508
|
-
pGenUpRetire
|
|
513
|
+
pGenUpRetire = pGenUpRetire.where (pGenUpRetire > 0.0, 1.0 )
|
|
509
514
|
# replace pNetUpInvest = 0.0 by 1.0
|
|
510
|
-
pNetUpInvest
|
|
515
|
+
pNetUpInvest = pNetUpInvest.where (pNetUpInvest > 0.0, 1.0 )
|
|
511
516
|
|
|
512
517
|
# minimum up- and downtime converted to an integer number of time steps
|
|
513
518
|
pSwitchOnTime = round(pSwitchOnTime /pTimeStep).astype('int')
|
|
@@ -524,6 +529,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
524
529
|
pH2PipeLoInvest = dfNetworkHydrogen['InvestmentLo' ] # Lower bound of the investment decision [p.u.]
|
|
525
530
|
pH2PipeUpInvest = dfNetworkHydrogen['InvestmentUp' ] # Upper bound of the investment decision [p.u.]
|
|
526
531
|
|
|
532
|
+
pH2PipePeriodFin = pH2PipePeriodFin.where(pH2PipePeriodFin != 0.0, 3000 )
|
|
527
533
|
# replace pH2PipeNTCBck = 0.0 by pH2PipeNTCFrw
|
|
528
534
|
pH2PipeNTCBck = pH2PipeNTCBck.where (pH2PipeNTCBck > 0.0, pH2PipeNTCFrw)
|
|
529
535
|
# replace pH2PipeNTCFrw = 0.0 by pH2PipeNTCBck
|
|
@@ -542,6 +548,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
542
548
|
pHeatPipeLoInvest = dfNetworkHeat['InvestmentLo' ] # Lower bound of the investment decision [p.u.]
|
|
543
549
|
pHeatPipeUpInvest = dfNetworkHeat['InvestmentUp' ] # Upper bound of the investment decision [p.u.]
|
|
544
550
|
|
|
551
|
+
pHeatPipePeriodFin = pHeatPipePeriodFin.where(pHeatPipePeriodFin != 0.0, 3000 )
|
|
545
552
|
# replace pHeatPipeNTCBck = 0.0 by pHeatPipeNTCFrw
|
|
546
553
|
pHeatPipeNTCBck = pHeatPipeNTCBck.where (pHeatPipeNTCBck > 0.0, pHeatPipeNTCFrw)
|
|
547
554
|
# replace pHeatPipeNTCFrw = 0.0 by pHeatPipeNTCBck
|
|
@@ -581,6 +588,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
581
588
|
mTEPES.bc = Set(doc='candidate boiler units' , initialize=[bo for bo in mTEPES.bo if pGenInvestCost [bo] > 0.0])
|
|
582
589
|
mTEPES.br = Set(doc='all input electric branches', initialize=sBrList )
|
|
583
590
|
mTEPES.ln = Set(doc='all input electric lines' , initialize=dfNetwork.index)
|
|
591
|
+
if len(mTEPES.ln) != len(dfNetwork.index):
|
|
592
|
+
raise ValueError('### Some electric lines are invalid ', len(mTEPES.ln), len(dfNetwork.index))
|
|
584
593
|
mTEPES.la = Set(doc='all real electric lines' , initialize=[ln for ln in mTEPES.ln if pLineX [ln] != 0.0 and pLineNTCFrw[ln] > 0.0 and pLineNTCBck[ln] > 0.0 and pElecNetPeriodIni[ln] <= mTEPES.p.last() and pElecNetPeriodFin[ln] >= mTEPES.p.first()])
|
|
585
594
|
mTEPES.ls = Set(doc='all real switch electric lines' , initialize=[la for la in mTEPES.la if pIndBinLineSwitch [la] ])
|
|
586
595
|
mTEPES.lc = Set(doc='candidate electric lines' , initialize=[la for la in mTEPES.la if pNetFixedCost [la] > 0.0])
|
|
@@ -598,6 +607,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
598
607
|
mTEPES.rn = Set(doc='candidate reservoirs' , initialize=[] )
|
|
599
608
|
if pIndHydrogen == 1:
|
|
600
609
|
mTEPES.pn = Set(doc='all input hydrogen pipes' , initialize=dfNetworkHydrogen.index )
|
|
610
|
+
if len(mTEPES.pn) != len(dfNetworkHydrogen.index):
|
|
611
|
+
raise ValueError('### Some hydrogen pipes are invalid ', len(mTEPES.pn), len(dfNetworkHydrogen.index))
|
|
601
612
|
mTEPES.pa = Set(doc='all real hydrogen pipes' , initialize=[pn for pn in mTEPES.pn if pH2PipeNTCFrw [pn] > 0.0 and pH2PipeNTCBck[pn] > 0.0 and pH2PipePeriodIni[pn] <= mTEPES.p.last() and pH2PipePeriodFin[pn] >= mTEPES.p.first()])
|
|
602
613
|
mTEPES.pc = Set(doc='candidate hydrogen pipes' , initialize=[pa for pa in mTEPES.pa if pH2PipeFixedCost [pa] > 0.0])
|
|
603
614
|
# existing hydrogen pipelines (pe)
|
|
@@ -609,6 +620,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
609
620
|
|
|
610
621
|
if pIndHeat == 1:
|
|
611
622
|
mTEPES.hn = Set(doc='all input heat pipes' , initialize=dfNetworkHeat.index)
|
|
623
|
+
if len(mTEPES.hn) != len(dfNetworkHeat.index):
|
|
624
|
+
raise ValueError('### Some heat pipes are invalid ', len(mTEPES.hn), len(dfNetworkHeat.index))
|
|
612
625
|
mTEPES.ha = Set(doc='all real heat pipes' , initialize=[hn for hn in mTEPES.hn if pHeatPipeNTCFrw [hn] > 0.0 and pHeatPipeNTCBck[hn] > 0.0 and pHeatPipePeriodIni[hn] <= mTEPES.p.last() and pHeatPipePeriodFin[hn] >= mTEPES.p.first()])
|
|
613
626
|
mTEPES.hc = Set(doc='candidate heat pipes' , initialize=[ha for ha in mTEPES.ha if pHeatPipeFixedCost [ha] > 0.0])
|
|
614
627
|
# existing heat pipes (he)
|
|
@@ -1096,8 +1109,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1096
1109
|
pMaxCapacity = pMaxPowerElec.where(pMaxPowerElec > pMaxCharge, pMaxCharge)
|
|
1097
1110
|
|
|
1098
1111
|
if len(g2a[ar]):
|
|
1099
|
-
pMaxPower2ndBlock [pMaxPower2ndBlock [[
|
|
1100
|
-
pMaxCharge2ndBlock[pMaxCharge2ndBlock[[
|
|
1112
|
+
pMaxPower2ndBlock [pMaxPower2ndBlock [[g for g in g2a[ar]]] < pEpsilon] = 0.0
|
|
1113
|
+
pMaxCharge2ndBlock[pMaxCharge2ndBlock[[g for g in g2a[ar]]] < pEpsilon] = 0.0
|
|
1101
1114
|
|
|
1102
1115
|
pLineNTCFrw.update(pd.Series([0.0 for ni,nf,cc in mTEPES.la if pLineNTCFrw[ni,nf,cc] < pEpsilon], index=[(ni,nf,cc) for ni,nf,cc in mTEPES.la if pLineNTCFrw[ni,nf,cc] < pEpsilon], dtype='float64'))
|
|
1103
1116
|
pLineNTCBck.update(pd.Series([0.0 for ni,nf,cc in mTEPES.la if pLineNTCBck[ni,nf,cc] < pEpsilon], index=[(ni,nf,cc) for ni,nf,cc in mTEPES.la if pLineNTCBck[ni,nf,cc] < pEpsilon], dtype='float64'))
|
|
@@ -1133,16 +1146,18 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1133
1146
|
pEmissionVarCost = pEmissionVarCost.loc [:,mTEPES.g ]
|
|
1134
1147
|
|
|
1135
1148
|
# replace < 0.0 by 0.0
|
|
1136
|
-
|
|
1137
|
-
|
|
1149
|
+
pEpsilon = 1e-6
|
|
1150
|
+
pMaxPower2ndBlock = pMaxPower2ndBlock.where (pMaxPower2ndBlock > pEpsilon, 0.0)
|
|
1151
|
+
pMaxCharge2ndBlock = pMaxCharge2ndBlock.where(pMaxCharge2ndBlock > pEpsilon, 0.0)
|
|
1138
1152
|
|
|
1139
1153
|
# computation of the power to heat ratio of the CHP units
|
|
1140
1154
|
# heat ratio of boiler units is fixed to 1.0
|
|
1141
1155
|
pPower2HeatRatio = pd.Series([1.0 if ch in mTEPES.bo else (pRatedMaxPowerElec[ch]-pRatedMinPowerElec[ch])/(pRatedMaxPowerHeat[ch]-pRatedMinPowerHeat[ch]) for ch in mTEPES.ch], index=mTEPES.ch)
|
|
1142
|
-
pMinPowerHeat = pd.DataFrame([[pMinPowerElec [ch][p,sc,n]/pPower2HeatRatio[ch]
|
|
1143
|
-
pMaxPowerHeat = pd.DataFrame([[pMaxPowerElec [ch][p,sc,n]/pPower2HeatRatio[ch]
|
|
1144
|
-
pMinPowerHeat.update(pd.DataFrame([[pRatedMinPowerHeat[bo]
|
|
1145
|
-
pMaxPowerHeat.update(pd.DataFrame([[pRatedMaxPowerHeat[bo]
|
|
1156
|
+
pMinPowerHeat = pd.DataFrame([[pMinPowerElec [ch][p,sc,n]/pPower2HeatRatio[ch] for ch in mTEPES.ch] for p,sc,n in mTEPES.psn], index=pd.MultiIndex.from_tuples(mTEPES.psn), columns=mTEPES.ch)
|
|
1157
|
+
pMaxPowerHeat = pd.DataFrame([[pMaxPowerElec [ch][p,sc,n]/pPower2HeatRatio[ch] for ch in mTEPES.ch] for p,sc,n in mTEPES.psn], index=pd.MultiIndex.from_tuples(mTEPES.psn), columns=mTEPES.ch)
|
|
1158
|
+
pMinPowerHeat.update(pd.DataFrame([[pRatedMinPowerHeat[bo] for bo in mTEPES.bo] for p,sc,n in mTEPES.psn], index=pd.MultiIndex.from_tuples(mTEPES.psn), columns=mTEPES.bo))
|
|
1159
|
+
pMaxPowerHeat.update(pd.DataFrame([[pRatedMaxPowerHeat[bo] for bo in mTEPES.bo] for p,sc,n in mTEPES.psn], index=pd.MultiIndex.from_tuples(mTEPES.psn), columns=mTEPES.bo))
|
|
1160
|
+
pMaxPowerHeat.update(pd.DataFrame([[pMaxCharge[hp][p,sc,n]/pProductionFunctionHeat[hp] for hp in mTEPES.hp] for p,sc,n in mTEPES.psn], index=pd.MultiIndex.from_tuples(mTEPES.psn), columns=mTEPES.hp))
|
|
1146
1161
|
|
|
1147
1162
|
# drop values not par, p, or ps
|
|
1148
1163
|
pReserveMargin = pReserveMargin.loc [mTEPES.par]
|
|
@@ -1235,8 +1250,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1235
1250
|
pStartUpCost.update (pd.Series([0.0 for nr in mTEPES.nr if pStartUpCost [nr] < pEpsilon], index=[nr for nr in mTEPES.nr if pStartUpCost [nr] < pEpsilon]))
|
|
1236
1251
|
pShutDownCost.update (pd.Series([0.0 for nr in mTEPES.nr if pShutDownCost [nr] < pEpsilon], index=[nr for nr in mTEPES.nr if pShutDownCost [nr] < pEpsilon]))
|
|
1237
1252
|
|
|
1238
|
-
# this rated linear variable cost
|
|
1239
|
-
# we include a small term to avoid stochastic behavior due to equal values
|
|
1253
|
+
# this rated linear variable cost is going to be used to order the generating units
|
|
1254
|
+
# we include a small term to avoid a stochastic behavior due to equal values
|
|
1240
1255
|
for g in mTEPES.g:
|
|
1241
1256
|
pRatedLinearVarCost[g] += 1e-3*pEpsilon*mTEPES.g.ord(g)
|
|
1242
1257
|
|
|
@@ -1739,11 +1754,11 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1739
1754
|
[OptModel.vIniInventory [p,sc,n,ec].setlb(mTEPES.pMinStorage [p,sc,n,ec] ) for p,sc,n,ec in mTEPES.psnec]
|
|
1740
1755
|
[OptModel.vIniInventory [p,sc,n,ec].setub(mTEPES.pMaxStorage [p,sc,n,ec] ) for p,sc,n,ec in mTEPES.psnec]
|
|
1741
1756
|
|
|
1742
|
-
[OptModel.vESSTotalCharge[p,sc,n,eh].setub(mTEPES.pMaxCharge [p,sc,n,eh]) for p,sc,n,eh in mTEPES.psneh]
|
|
1743
|
-
[OptModel.vCharge2ndBlock[p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh]) for p,sc,n,eh in mTEPES.psneh]
|
|
1744
|
-
[OptModel.vESSReserveUp [p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh]) for p,sc,n,eh in mTEPES.psneh]
|
|
1745
|
-
[OptModel.vESSReserveDown[p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh]) for p,sc,n,eh in mTEPES.psneh]
|
|
1746
|
-
[OptModel.vENS [p,sc,n,nd].setub(mTEPES.pDemandElecAbs [p,sc,n,nd]) for p,sc,n,nd in mTEPES.psnnd]
|
|
1757
|
+
[OptModel.vESSTotalCharge[p,sc,n,eh].setub(mTEPES.pMaxCharge [p,sc,n,eh] ) for p,sc,n,eh in mTEPES.psneh]
|
|
1758
|
+
[OptModel.vCharge2ndBlock[p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh] ) for p,sc,n,eh in mTEPES.psneh]
|
|
1759
|
+
[OptModel.vESSReserveUp [p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh] ) for p,sc,n,eh in mTEPES.psneh]
|
|
1760
|
+
[OptModel.vESSReserveDown[p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh] ) for p,sc,n,eh in mTEPES.psneh]
|
|
1761
|
+
[OptModel.vENS [p,sc,n,nd].setub(mTEPES.pDemandElecAbs [p,sc,n,nd] ) for p,sc,n,nd in mTEPES.psnnd]
|
|
1747
1762
|
|
|
1748
1763
|
if mTEPES.pIndHydroTopology == 1:
|
|
1749
1764
|
[OptModel.vHydroInflows [p,sc,n,rc].setub(mTEPES.pHydroInflows[p,sc,n,rc]()) for p,sc,n,rc in mTEPES.psnrc]
|
|
@@ -2137,6 +2152,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2137
2152
|
if mTEPES.pDemandHeat[p,sc,n,nd] == 0.0:
|
|
2138
2153
|
OptModel.vHeatNS [p,sc,n,nd].fix (0.0)
|
|
2139
2154
|
nFixedVariables += 1
|
|
2155
|
+
|
|
2140
2156
|
def AvoidForbiddenInstallationsAndRetirements(mTEPES, OptModel) -> int:
|
|
2141
2157
|
'''
|
|
2142
2158
|
Fix installations/retirements forbidden by period.
|
|
@@ -2281,7 +2297,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2281
2297
|
[OptModel.vFlowHeat[p,sc,n,ni,nf,cc].fix(0.0) for p,sc,n,ni,nf,cc in mTEPES.psnha if (ni,nf,cc) not in mTEPES.hc and mTEPES.pHeatPipePeriodIni[ni,nf,cc] > p]
|
|
2282
2298
|
nFixedVariables += sum( 4 for p,sc,n,ni,nf,cc in mTEPES.psnha if (ni,nf,cc) not in mTEPES.hc and mTEPES.pHeatPipePeriodIni[ni,nf,cc] > p)
|
|
2283
2299
|
|
|
2284
|
-
# tolerance to consider 0
|
|
2300
|
+
# tolerance to consider 0 an investment decision
|
|
2285
2301
|
pEpsilon = 1e-4
|
|
2286
2302
|
def SetToZero(mTEPES, OptModel, pEpsilon) -> None:
|
|
2287
2303
|
'''
|
|
@@ -2311,7 +2327,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2311
2327
|
[OptModel.vGenerationInvest[p,eb ].setlb(mTEPES.pGenLoInvest[eb ]()) for p,eb in mTEPES.peb]
|
|
2312
2328
|
[OptModel.vGenerationInvest[p,eb ].setub(mTEPES.pGenUpInvest[eb ]()) for p,eb in mTEPES.peb]
|
|
2313
2329
|
for p,gd in mTEPES.pgd:
|
|
2314
|
-
if mTEPES.pGenLoRetire[ gd ]() <
|
|
2330
|
+
if mTEPES.pGenLoRetire[ gd ]() < pEpsilon:
|
|
2315
2331
|
mTEPES.pGenLoRetire[ gd ] = 0
|
|
2316
2332
|
if mTEPES.pGenUpRetire[ gd ]() < pEpsilon:
|
|
2317
2333
|
mTEPES.pGenUpRetire[ gd ] = 0
|
|
@@ -2405,9 +2421,9 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2405
2421
|
for es in mTEPES.es:
|
|
2406
2422
|
# detecting infeasibility: total min ESS output greater than total inflows, total max ESS charge lower than total outflows
|
|
2407
2423
|
if sum(mTEPES.pMinPowerElec[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyInflows [p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) > 0.0:
|
|
2408
|
-
raise ValueError('### Total minimum output greater than total inflows for ESS unit ', es)
|
|
2424
|
+
raise ValueError('### Total minimum output greater than total inflows for ESS unit ', es, sum(mTEPES.pMinPowerElec[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyInflows [p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes))
|
|
2409
2425
|
if sum(mTEPES.pMaxCharge [p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyOutflows[p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) < 0.0:
|
|
2410
|
-
raise ValueError('### Total maximum charge lower than total outflows for ESS unit ', es)
|
|
2426
|
+
raise ValueError('### Total maximum charge lower than total outflows for ESS unit ', es, sum(mTEPES.pMaxCharge [p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyOutflows[p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes))
|
|
2411
2427
|
|
|
2412
2428
|
# detect inventory infeasibility
|
|
2413
2429
|
for p,sc,n,es in mTEPES.ps*mTEPES.nesc:
|
|
@@ -2415,22 +2431,22 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2415
2431
|
if mTEPES.pMaxCapacity[p,sc,n,es]:
|
|
2416
2432
|
if mTEPES.n.ord(n) == mTEPES.pStorageTimeStep[es]:
|
|
2417
2433
|
if mTEPES.pIniInventory[p,sc,n,es]() + sum(mTEPES.pDuration[p,sc,n2]()*(mTEPES.pEnergyInflows[p,sc,n2,es]() - mTEPES.pMinPowerElec[p,sc,n2,es] + mTEPES.pEfficiency[es]*mTEPES.pMaxCharge[p,sc,n2,es]) for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)-mTEPES.pStorageTimeStep[es]:mTEPES.n.ord(n)]) < mTEPES.pMinStorage[p,sc,n,es]:
|
|
2418
|
-
raise ValueError('### Inventory equation violation ', p, sc, n, es)
|
|
2434
|
+
raise ValueError('### Inventory equation violation ', p, sc, n, es, mTEPES.pIniInventory[p,sc,n,es]() + sum(mTEPES.pDuration[p,sc,n2]()*(mTEPES.pEnergyInflows[p,sc,n2,es]() - mTEPES.pMinPowerElec[p,sc,n2,es] + mTEPES.pEfficiency[es]*mTEPES.pMaxCharge[p,sc,n2,es]) for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)-mTEPES.pStorageTimeStep[es]:mTEPES.n.ord(n)]), mTEPES.pMinStorage[p,sc,n,es])
|
|
2419
2435
|
elif mTEPES.n.ord(n) > mTEPES.pStorageTimeStep[es]:
|
|
2420
2436
|
if mTEPES.pMaxStorage[p,sc,mTEPES.n.prev(n,mTEPES.pStorageTimeStep[es]),es] + sum(mTEPES.pDuration[p,sc,n2]()*(mTEPES.pEnergyInflows[p,sc,n2,es]() - mTEPES.pMinPowerElec[p,sc,n2,es] + mTEPES.pEfficiency[es]*mTEPES.pMaxCharge[p,sc,n2,es]) for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)-mTEPES.pStorageTimeStep[es]:mTEPES.n.ord(n)]) < mTEPES.pMinStorage[p,sc,n,es]:
|
|
2421
|
-
raise ValueError('### Inventory equation violation ', p, sc, n, es)
|
|
2437
|
+
raise ValueError('### Inventory equation violation ', p, sc, n, es, mTEPES.pMaxStorage[p,sc,mTEPES.n.prev(n,mTEPES.pStorageTimeStep[es]),es] + sum(mTEPES.pDuration[p,sc,n2]()*(mTEPES.pEnergyInflows[p,sc,n2,es]() - mTEPES.pMinPowerElec[p,sc,n2,es] + mTEPES.pEfficiency[es]*mTEPES.pMaxCharge[p,sc,n2,es]) for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)-mTEPES.pStorageTimeStep[es]:mTEPES.n.ord(n)]), mTEPES.pMinStorage[p,sc,n,es])
|
|
2422
2438
|
|
|
2423
2439
|
# detect minimum energy infeasibility
|
|
2424
2440
|
for p,sc,n,g in mTEPES.ps*mTEPES.ngen:
|
|
2425
2441
|
if (p,g) in mTEPES.pg:
|
|
2426
2442
|
if (p,sc,g) in mTEPES.gm:
|
|
2427
2443
|
if sum((mTEPES.pMaxPowerElec[p,sc,n2,g] - mTEPES.pMinEnergy[p,sc,n2,g])*mTEPES.pDuration[p,sc,n2]() for n2 in list(mTEPES.n2)[mTEPES.n.ord(n) - mTEPES.pEnergyTimeStep[g]:mTEPES.n.ord(n)]) < 0.0:
|
|
2428
|
-
raise ValueError('### Minimum energy violation ', p, sc, n, g)
|
|
2444
|
+
raise ValueError('### Minimum energy violation ', p, sc, n, g, sum((mTEPES.pMaxPowerElec[p,sc,n2,g] - mTEPES.pMinEnergy[p,sc,n2,g])*mTEPES.pDuration[p,sc,n2]() for n2 in list(mTEPES.n2)[mTEPES.n.ord(n) - mTEPES.pEnergyTimeStep[g]:mTEPES.n.ord(n)]))
|
|
2429
2445
|
|
|
2430
2446
|
# detecting reserve margin infeasibility
|
|
2431
2447
|
for p,ar in mTEPES.p*mTEPES.ar:
|
|
2432
2448
|
if sum(mTEPES.pRatedMaxPowerElec[g] * mTEPES.pAvailability[g]() / (1.0-mTEPES.pEFOR[g]) for g in mTEPES.g if (p,g) in mTEPES.pg and (ar,g) in mTEPES.a2g) < mTEPES.pDemandElecPeak[p,ar] * mTEPES.pReserveMargin[p,ar]:
|
|
2433
|
-
raise ValueError('### Reserve margin infeasibility ', p, ar)
|
|
2449
|
+
raise ValueError('### Reserve margin infeasibility ', p, ar, sum(mTEPES.pRatedMaxPowerElec[g] * mTEPES.pAvailability[g]() / (1.0-mTEPES.pEFOR[g]) for g in mTEPES.g if (p,g) in mTEPES.pg and (ar,g) in mTEPES.a2g), mTEPES.pDemandElecPeak[p,ar] * mTEPES.pReserveMargin[p,ar])
|
|
2434
2450
|
|
|
2435
2451
|
DetectInfeasibilities(mTEPES)
|
|
2436
2452
|
|
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) -
|
|
663
|
+
# Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 30, 2024
|
|
664
664
|
# simplicity and transparency in power systems planning
|
|
665
665
|
|
|
666
666
|
# Developed by
|
|
@@ -685,7 +685,7 @@ import time
|
|
|
685
685
|
# import pkg_resources
|
|
686
686
|
from .openTEPES import openTEPES_run
|
|
687
687
|
|
|
688
|
-
print('\033[1;32mOpen Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.17.
|
|
688
|
+
print('\033[1;32mOpen Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.17.8 - October 30, 2024\033[0m')
|
|
689
689
|
print('\033[34m#### Academic research license - for non-commercial use only ####\033[0m \n')
|
|
690
690
|
|
|
691
691
|
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) -
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 28, 2024
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -1056,51 +1056,75 @@ def GenerationOperationModelFormulationRampMinTime(OptModel, mTEPES, pIndLogCons
|
|
|
1056
1056
|
if pIndLogConsole == 1:
|
|
1057
1057
|
print('eRampDwChr ... ', len(getattr(OptModel, 'eRampDwChr_'+str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
|
|
1058
1058
|
|
|
1059
|
-
#
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1059
|
+
pIndSimplexFormulation = True # Parameter to choose if minimum stable time should be physically accurate or computationally efficient. True for efficiency, False for accuracy
|
|
1060
|
+
pIndStableTimeDeadBand = True # Parameter to choose if ramps below a certain threshold set by pEpsilon should not be restricted. True for having dead band, False for restricting all ramps
|
|
1061
|
+
|
|
1062
|
+
if pIndStableTimeDeadBand:
|
|
1063
|
+
pEpsilon = 1e-2
|
|
1064
|
+
else:
|
|
1065
|
+
pEpsilon = 1e-4
|
|
1066
|
+
|
|
1067
|
+
def eRampUpState(OptModel, n, nr):
|
|
1068
|
+
if mTEPES.pStableTime[nr] and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and (p,nr) in mTEPES.pnr and mTEPES.pDuration[p,sc,n]():
|
|
1069
|
+
if pIndStableTimeDeadBand:
|
|
1064
1070
|
if mTEPES.pRampUp[nr]:
|
|
1065
1071
|
if n == mTEPES.n.first():
|
|
1066
|
-
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr],0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp
|
|
1072
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1067
1073
|
else:
|
|
1068
|
-
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr]
|
|
1074
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1069
1075
|
else:
|
|
1070
1076
|
if n == mTEPES.n.first():
|
|
1071
|
-
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr],0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr]
|
|
1077
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1072
1078
|
else:
|
|
1073
|
-
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr]
|
|
1079
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1074
1080
|
else:
|
|
1075
|
-
|
|
1081
|
+
if mTEPES.pRampUp[nr]:
|
|
1082
|
+
if n == mTEPES.n.first():
|
|
1083
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr]
|
|
1084
|
+
else:
|
|
1085
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr]
|
|
1086
|
+
else:
|
|
1087
|
+
if n == mTEPES.n.first():
|
|
1088
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr]
|
|
1089
|
+
else:
|
|
1090
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampUpState[p,sc,n,nr] / pEpsilon - OptModel.vRampDwState[p,sc,n,nr]
|
|
1076
1091
|
else:
|
|
1077
1092
|
return Constraint.Skip
|
|
1078
|
-
setattr(OptModel, 'eRampUpState_'+str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.n, mTEPES.nr, rule=eRampUpState, doc='ramp up state [p.u.]'))
|
|
1093
|
+
setattr(OptModel, 'eRampUpState_' +str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.n, mTEPES.nr, rule=eRampUpState, doc='ramp up state [p.u.]'))
|
|
1079
1094
|
|
|
1080
1095
|
if pIndLogConsole == 1:
|
|
1081
|
-
print('eRampUpState ... ', len(getattr(OptModel, 'eRampUpState_'+str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
|
|
1096
|
+
print('eRampUpState ... ', len(getattr(OptModel, 'eRampUpState_' +str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
|
|
1082
1097
|
|
|
1083
|
-
def eRampDwState(OptModel,n,nr):
|
|
1084
|
-
if (p,nr) in mTEPES.pnr:
|
|
1085
|
-
if
|
|
1098
|
+
def eRampDwState(OptModel, n, nr):
|
|
1099
|
+
if mTEPES.pStableTime[nr] and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and (p,nr) in mTEPES.pnr and mTEPES.pDuration[p,sc,n]():
|
|
1100
|
+
if pIndStableTimeDeadBand:
|
|
1086
1101
|
if mTEPES.pRampDw[nr]:
|
|
1087
1102
|
if n == mTEPES.n.first():
|
|
1088
|
-
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr],0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw
|
|
1103
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1089
1104
|
else:
|
|
1090
|
-
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr]
|
|
1105
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1091
1106
|
else:
|
|
1092
1107
|
if n == mTEPES.n.first():
|
|
1093
|
-
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr],0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr]
|
|
1108
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1094
1109
|
else:
|
|
1095
|
-
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr]
|
|
1110
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr] + OptModel.vStableState[p,sc,n,nr]
|
|
1096
1111
|
else:
|
|
1097
|
-
|
|
1112
|
+
if mTEPES.pRampDw[nr]:
|
|
1113
|
+
if n == mTEPES.n.first():
|
|
1114
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr]
|
|
1115
|
+
else:
|
|
1116
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr]
|
|
1117
|
+
else:
|
|
1118
|
+
if n == mTEPES.n.first():
|
|
1119
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr]
|
|
1120
|
+
else:
|
|
1121
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] / pEpsilon <= OptModel.vRampDwState[p,sc,n,nr] / pEpsilon - OptModel.vRampUpState[p,sc,n,nr]
|
|
1098
1122
|
else:
|
|
1099
1123
|
return Constraint.Skip
|
|
1100
|
-
setattr(OptModel, 'eRampDwState_'+str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.n, mTEPES.nr, rule=eRampDwState, doc='maximum ramp down [p.u.]'))
|
|
1124
|
+
setattr(OptModel, 'eRampDwState_' +str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.n, mTEPES.nr, rule=eRampDwState, doc='maximum ramp down [p.u.]'))
|
|
1101
1125
|
|
|
1102
1126
|
if pIndLogConsole == 1:
|
|
1103
|
-
print('eRampDwState ... ', len(getattr(OptModel, 'eRampDwState_'+str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
|
|
1127
|
+
print('eRampDwState ... ', len(getattr(OptModel, 'eRampDwState_' +str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
|
|
1104
1128
|
|
|
1105
1129
|
def eMinUpTime(OptModel,n,t):
|
|
1106
1130
|
if (p,t) in mTEPES.pg:
|
|
@@ -1128,23 +1152,28 @@ def GenerationOperationModelFormulationRampMinTime(OptModel, mTEPES, pIndLogCons
|
|
|
1128
1152
|
if pIndLogConsole == 1:
|
|
1129
1153
|
print('eMinDownTime ... ', len(getattr(OptModel, 'eMinDownTime_'+str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
|
|
1130
1154
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
if (
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1155
|
+
if pIndSimplexFormulation:
|
|
1156
|
+
def eMinStableTime(OptModel, n, nr):
|
|
1157
|
+
if (mTEPES.pStableTime[nr] and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and mTEPES.n.ord(n) >= mTEPES.pStableTime[nr] + 2):
|
|
1158
|
+
return OptModel.vRampUpState[p,sc,n,nr] + sum(OptModel.vRampDwState[p,sc,n2,nr] for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)-mTEPES.pStableTime[nr]-1:mTEPES.n.ord(n)-1]) <= 1
|
|
1159
|
+
else:
|
|
1160
|
+
return Constraint.Skip
|
|
1161
|
+
setattr(OptModel, 'eMinStableTime_' +str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.n, mTEPES.nr, rule=eMinStableTime, doc='minimum stable time [p.u.]'))
|
|
1162
|
+
else:
|
|
1163
|
+
MinStableTimeLoadLevels = []
|
|
1164
|
+
if sum(mTEPES.pStableTime[nr] for nr in mTEPES.nr):
|
|
1165
|
+
for n in mTEPES.n:
|
|
1166
|
+
for nr in mTEPES.nr:
|
|
1167
|
+
if (mTEPES.pStableTime[nr] and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and mTEPES.n.ord(n) >= mTEPES.pStableTime[nr] + 2):
|
|
1168
|
+
for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)-mTEPES.pStableTime[nr]-1:mTEPES.n.ord(n)-1]:
|
|
1169
|
+
MinStableTimeLoadLevels.append((n, n2, nr))
|
|
1170
|
+
|
|
1171
|
+
def eMinStableTime(OptModel, n, n2, nr):
|
|
1172
|
+
return OptModel.vRampUpState[p,sc,n,nr] + OptModel.vRampDwState[p,sc,n2,nr] <= 1
|
|
1173
|
+
setattr(OptModel, 'eMinStableTime_' +str(p)+'_'+str(sc)+'_'+str(st), Constraint(MinStableTimeLoadLevels, rule=eMinStableTime, doc='minimum stable time [p.u.]'))
|
|
1174
|
+
|
|
1175
|
+
if pIndLogConsole == 1:
|
|
1176
|
+
print('eMinStableTime ... ', len(getattr(OptModel, 'eMinStableTime_' +str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
|
|
1148
1177
|
|
|
1149
1178
|
GeneratingTime = time.time() - StartTime
|
|
1150
1179
|
if pIndLogConsole == 1:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) -
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 22, 2024
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -455,11 +455,9 @@ def GenerationOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnolo
|
|
|
455
455
|
pEpsilon = 1e-6
|
|
456
456
|
|
|
457
457
|
sPSNG = [(p,sc,n,g) for p,sc,n,g in mTEPES.psng if OptModel.vTotalOutput[p,sc,n,g].ub - OptModel.vTotalOutput[p,sc,n,g]() > pEpsilon]
|
|
458
|
-
OutputToFile = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,g].ub - OptModel.vTotalOutput[p,sc,n,g]())
|
|
458
|
+
OutputToFile = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,g].ub*OptModel.vGenerationInvest[p,g]() - OptModel.vTotalOutput[p,sc,n,g]()) if g in mTEPES.gc else
|
|
459
|
+
(OptModel.vTotalOutput[p,sc,n,g].ub - OptModel.vTotalOutput[p,sc,n,g]()) for p,sc,n,g in sPSNG], index=pd.Index(sPSNG))
|
|
459
460
|
OutputToFile *= 1e3
|
|
460
|
-
for p,sc,n,g in sPSNG:
|
|
461
|
-
if g in mTEPES.gc:
|
|
462
|
-
OutputToFile[p,sc,n,g] *= OptModel.vGenerationInvest[p,g]()
|
|
463
461
|
OutputToFile.to_frame(name='MW').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MW').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationSurplus_'+CaseName+'.csv', sep=',')
|
|
464
462
|
|
|
465
463
|
OutputResults = []
|
|
@@ -483,12 +481,10 @@ def GenerationOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnolo
|
|
|
483
481
|
OutputResults.to_frame(name='MW/h').reset_index().pivot_table(index=['level_0','level_1','level_3'], columns='level_4', values='MW/h', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationRampDwSurplus_'+CaseName+'.csv', sep=',')
|
|
484
482
|
|
|
485
483
|
if len(mTEPES.re) and len(mTEPES.rt):
|
|
486
|
-
OutputToFile1 = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,re].ub - OptModel.vTotalOutput[p,sc,n,re]())*mTEPES.pLoadLevelDuration[p,sc,n]()
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
OutputToFile1[p,sc,n,re] *= OptModel.vGenerationInvest[p,re]()
|
|
491
|
-
OutputToFile2[p,sc,n,re] *= OptModel.vGenerationInvest[p,re]()
|
|
484
|
+
OutputToFile1 = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,re].ub*OptModel.vGenerationInvest[p,re]() - OptModel.vTotalOutput[p,sc,n,re]())*mTEPES.pLoadLevelDuration[p,sc,n]() if re in mTEPES.gc else
|
|
485
|
+
(OptModel.vTotalOutput[p,sc,n,re].ub - OptModel.vTotalOutput[p,sc,n,re]())*mTEPES.pLoadLevelDuration[p,sc,n]() for p,sc,n,re in mTEPES.psnre], index=pd.Index(mTEPES.psnre))
|
|
486
|
+
OutputToFile2 = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,re].ub*OptModel.vGenerationInvest[p,re]() )*mTEPES.pLoadLevelDuration[p,sc,n]() if re in mTEPES.gc else
|
|
487
|
+
(OptModel.vTotalOutput[p,sc,n,re].ub )*mTEPES.pLoadLevelDuration[p,sc,n]() for p,sc,n,re in mTEPES.psnre], index=pd.Index(mTEPES.psnre))
|
|
492
488
|
OutputToFile1 = OutputToFile1.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_3'], values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'Generating unit'], axis=0).rename_axis([None], axis=1)
|
|
493
489
|
OutputToFile2 = OutputToFile2.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_3'], values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'Generating unit'], axis=0).rename_axis([None], axis=1)
|
|
494
490
|
if pIndTechnologyOutput == 0 or pIndTechnologyOutput == 2:
|
|
@@ -504,18 +500,14 @@ def GenerationOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnolo
|
|
|
504
500
|
OutputToFile = OutputToFile.fillna(0.0)
|
|
505
501
|
OutputToFile.to_frame(name='%').rename_axis(['Period', 'Scenario', 'Technology'], axis=0).to_csv(_path+'/oT_Result_TechnologyCurtailmentEnergyRelative_'+CaseName+'.csv', index=True, sep=',')
|
|
506
502
|
|
|
507
|
-
OutputToFile = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,re].ub - OptModel.vTotalOutput[p,sc,n,re]())
|
|
503
|
+
OutputToFile = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,re].ub*OptModel.vGenerationInvest[p,re]() - OptModel.vTotalOutput[p,sc,n,re]()) if re in mTEPES.gc else
|
|
504
|
+
(OptModel.vTotalOutput[p,sc,n,re].ub - OptModel.vTotalOutput[p,sc,n,re]()) for p,sc,n,re in mTEPES.psnre], index=pd.Index(mTEPES.psnre))
|
|
508
505
|
OutputToFile *= 1e3
|
|
509
|
-
for p,sc,n,re in mTEPES.psnre:
|
|
510
|
-
if re in mTEPES.gc:
|
|
511
|
-
OutputToFile[p,sc,n,re] *= OptModel.vGenerationInvest[p,re]()
|
|
512
506
|
if pIndTechnologyOutput == 0 or pIndTechnologyOutput == 2:
|
|
513
507
|
OutputToFile.to_frame(name='MW' ).reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MW' , aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationCurtailment_'+CaseName+'.csv', sep=',')
|
|
514
508
|
|
|
515
|
-
OutputToFile = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,re].ub - OptModel.vTotalOutput[p,sc,n,re]())*mTEPES.pLoadLevelDuration[p,sc,n]()
|
|
516
|
-
|
|
517
|
-
if re in mTEPES.gc:
|
|
518
|
-
OutputToFile[p,sc,n,re] *= OptModel.vGenerationInvest[p,re]()
|
|
509
|
+
OutputToFile = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,re].ub*OptModel.vGenerationInvest[p,re]() - OptModel.vTotalOutput[p,sc,n,re]())*mTEPES.pLoadLevelDuration[p,sc,n]() if re in mTEPES.gc else
|
|
510
|
+
(OptModel.vTotalOutput[p,sc,n,re].ub - OptModel.vTotalOutput[p,sc,n,re]())*mTEPES.pLoadLevelDuration[p,sc,n]() for p,sc,n,re in mTEPES.psnre], index=pd.Index(mTEPES.psnre))
|
|
519
511
|
if pIndTechnologyOutput == 0 or pIndTechnologyOutput == 2:
|
|
520
512
|
OutputToFile.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationCurtailmentEnergy_'+CaseName+'.csv', sep=',')
|
|
521
513
|
|
|
@@ -533,7 +525,8 @@ def GenerationOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnolo
|
|
|
533
525
|
OutputToFile.to_frame(name='GWh').reset_index().pivot_table( index=['level_0','level_1','level_2'], columns='level_3', values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationEnergy_'+CaseName+'.csv', sep=',')
|
|
534
526
|
|
|
535
527
|
if len(mTEPES.nr) + len(mTEPES.bo):
|
|
536
|
-
OutputToFile = pd.Series(data=[OptModel.vTotalOutput[p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]()*mTEPES.pEmissionRate[g]/1e3 if g not in mTEPES.bo else
|
|
528
|
+
OutputToFile = pd.Series(data=[OptModel.vTotalOutput [p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]()*mTEPES.pEmissionRate[g]/1e3 if g not in mTEPES.bo else
|
|
529
|
+
OptModel.vTotalOutputHeat[p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]()*mTEPES.pEmissionRate[g]/1e3 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng ))
|
|
537
530
|
if pIndTechnologyOutput == 0 or pIndTechnologyOutput == 2:
|
|
538
531
|
OutputToFile.to_frame(name='MtCO2').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MtCO2', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationEmission_'+CaseName+'.csv', sep=',')
|
|
539
532
|
|
|
@@ -611,12 +604,10 @@ def GenerationOperationHeatResults(DirName, CaseName, OptModel, mTEPES, pIndTech
|
|
|
611
604
|
|
|
612
605
|
for p,sc,n,ch in mTEPES.psnch:
|
|
613
606
|
if ch not in mTEPES.bo:
|
|
614
|
-
OptModel.vTotalOutputHeat[p,sc,n,ch]
|
|
615
|
-
OptModel.vTotalOutputHeat[p,sc,n,ch].ub = OptModel.vTotalOutput[p,sc,n,ch].ub/mTEPES.pPower2HeatRatio[ch]
|
|
607
|
+
OptModel.vTotalOutputHeat[p,sc,n,ch] = OptModel.vTotalOutput[p,sc,n,ch]/mTEPES.pPower2HeatRatio[ch]
|
|
616
608
|
|
|
617
609
|
for p,sc,n,hp in mTEPES.psnhp:
|
|
618
|
-
OptModel.vTotalOutputHeat[p,sc,n,hp]
|
|
619
|
-
OptModel.vTotalOutputHeat[p,sc,n,hp].ub = OptModel.vESSTotalCharge[p,sc,n,hp].ub/mTEPES.pProductionFunctionHeat[hp]
|
|
610
|
+
OptModel.vTotalOutputHeat[p,sc,n,hp] = OptModel.vESSTotalCharge[p,sc,n,hp]/mTEPES.pProductionFunctionHeat[hp]
|
|
620
611
|
|
|
621
612
|
OutputToFile = pd.Series(data=[OptModel.vTotalOutputHeat[p,sc,n,chp]() for p,sc,n,chp in mTEPES.psnchp], index=pd.Index(mTEPES.psnchp))
|
|
622
613
|
OutputToFile *= 1e3
|
|
@@ -625,12 +616,10 @@ def GenerationOperationHeatResults(DirName, CaseName, OptModel, mTEPES, pIndTech
|
|
|
625
616
|
# tolerance to consider 0 a number
|
|
626
617
|
pEpsilon = 1e-6
|
|
627
618
|
|
|
628
|
-
sPSNG = [(p,sc,n,
|
|
629
|
-
OutputToFile = pd.Series(data=[(OptModel.vTotalOutputHeat[p,sc,n,
|
|
619
|
+
sPSNG = [(p,sc,n,ch) for p,sc,n,ch in mTEPES.psnch if OptModel.vTotalOutputHeat[p,sc,n,ch].ub - OptModel.vTotalOutputHeat[p,sc,n,ch]() > pEpsilon]
|
|
620
|
+
OutputToFile = pd.Series(data=[(OptModel.vTotalOutputHeat[p,sc,n,ch].ub*OptModel.vGenerationInvest[p,ch]() - OptModel.vTotalOutputHeat[p,sc,n,ch]()) if ch in mTEPES.gc or ch in mTEPES.bc else
|
|
621
|
+
(OptModel.vTotalOutputHeat[p,sc,n,ch].ub - OptModel.vTotalOutputHeat[p,sc,n,ch]()) for p,sc,n,ch in sPSNG], index=pd.Index(sPSNG))
|
|
630
622
|
OutputToFile *= 1e3
|
|
631
|
-
for p,sc,n,chp in sPSNG:
|
|
632
|
-
if chp in mTEPES.gc or chp in mTEPES.bc:
|
|
633
|
-
OutputToFile[p,sc,n,chp] *= OptModel.vGenerationInvest[p,chp]()
|
|
634
623
|
OutputToFile.to_frame(name='MW').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MW').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationSurplusHeat_'+CaseName+'.csv', sep=',')
|
|
635
624
|
|
|
636
625
|
if pIndTechnologyOutput == 0 or pIndTechnologyOutput == 2:
|
|
@@ -722,7 +711,7 @@ def ESSOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnologyOutpu
|
|
|
722
711
|
OutputToFile.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_TechnologyOutflowsEnergy_'+CaseName+'.csv', sep=',')
|
|
723
712
|
|
|
724
713
|
OutputToFile = pd.Series(data=[OptModel.vESSTotalCharge [p,sc,n,eh]()*mTEPES.pLoadLevelDuration[p,sc,n]() for p,sc,n,eh in mTEPES.psneh], index=pd.Index(mTEPES.psneh))
|
|
725
|
-
OutputToFile *= -1
|
|
714
|
+
OutputToFile *= -1.0
|
|
726
715
|
if pIndTechnologyOutput == 0 or pIndTechnologyOutput == 2:
|
|
727
716
|
OutputToFile.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_ConsumptionEnergy_' +CaseName+'.csv', sep=',')
|
|
728
717
|
|
|
@@ -779,11 +768,10 @@ def ESSOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnologyOutpu
|
|
|
779
768
|
OutputToFile = pd.Series(data=[OutputToFile[p,sc,n,es] for p,sc,n,es,ot in sPSNES*mTEPES.ot if es in o2e[ot]], index=pd.Index(sPSNESOT))
|
|
780
769
|
OutputToFile.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_TechnologySpillage_'+CaseName+'.csv', sep=',')
|
|
781
770
|
|
|
782
|
-
OutputToFile1 = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,es].ub - OptModel.vTotalOutput[p,sc,n,es]())*mTEPES.pLoadLevelDuration[p,sc,n]()
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
OutputToFile2[p,sc,n,ec] *= OptModel.vGenerationInvest[p,ec]()
|
|
771
|
+
OutputToFile1 = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,es].ub*OptModel.vGenerationInvest[p,es]() - OptModel.vTotalOutput[p,sc,n,es]())*mTEPES.pLoadLevelDuration[p,sc,n]() if es in mTEPES.ec else
|
|
772
|
+
(OptModel.vTotalOutput[p,sc,n,es].ub - OptModel.vTotalOutput[p,sc,n,es]())*mTEPES.pLoadLevelDuration[p,sc,n]() for p,sc,n,es in mTEPES.psnes], index=pd.Index(mTEPES.psnes))
|
|
773
|
+
OutputToFile2 = pd.Series(data=[(OptModel.vTotalOutput[p,sc,n,es].ub*OptModel.vGenerationInvest[p,es]() )*mTEPES.pLoadLevelDuration[p,sc,n]() if es in mTEPES.ec else
|
|
774
|
+
(OptModel.vTotalOutput[p,sc,n,es].ub )*mTEPES.pLoadLevelDuration[p,sc,n]() for p,sc,n,es in mTEPES.psnes], index=pd.Index(mTEPES.psnes))
|
|
787
775
|
OutputToFile1 = OutputToFile1.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_3'], values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'Generating unit'], axis=0).rename_axis([None], axis=1)
|
|
788
776
|
OutputToFile2 = OutputToFile2.to_frame(name='GWh').reset_index().pivot_table(index=['level_0','level_1','level_3'], values='GWh', aggfunc='sum').rename_axis(['Period', 'Scenario', 'Generating unit'], axis=0).rename_axis([None], axis=1)
|
|
789
777
|
|
|
@@ -1283,7 +1271,7 @@ def OperationSummaryResults(DirName, CaseName, OptModel, mTEPES):
|
|
|
1283
1271
|
RsrInvestmentCost = 0.0
|
|
1284
1272
|
NetInvestmentCost = sum(mTEPES.pDiscountedWeight[p] * mTEPES.pNetFixedCost [ni,nf,cc] * OptModel.vNetworkInvest [p,ni,nf,cc]() for p,ni,nf,cc in mTEPES.plc)
|
|
1285
1273
|
# Ratio Generation Investment cost/ Generation Installed Capacity [MEUR-MW]
|
|
1286
|
-
GenInvCostCapacity = sum(mTEPES.pGenInvestCost[gc] * OptModel.vGenerationInvest[p,gc]()/mTEPES.pRatedMaxPowerElec[gc]
|
|
1274
|
+
GenInvCostCapacity = sum(mTEPES.pGenInvestCost[gc] * OptModel.vGenerationInvest[p,gc]()/mTEPES.pRatedMaxPowerElec[gc] for p,gc in mTEPES.pgc if mTEPES.pRatedMaxPowerElec[gc])
|
|
1287
1275
|
# Ratio Additional Transmission Capacity-Length [MW-km]
|
|
1288
1276
|
NetCapacityLength = sum(mTEPES.pLineNTCMax[ni,nf,cc]*OptModel.vNetworkInvest[p,ni,nf,cc]()/mTEPES.pLineLength[ni,nf,cc]() for p,ni,nf,cc in mTEPES.plc)
|
|
1289
1277
|
# Ratio Network Investment Cost/Variable RES Injection [EUR/MWh]
|
|
@@ -1359,27 +1347,27 @@ def OperationSummaryResults(DirName, CaseName, OptModel, mTEPES):
|
|
|
1359
1347
|
a2g = pd.DataFrame(mTEPES.a2g).set_index(1)
|
|
1360
1348
|
r2g = pd.DataFrame(mTEPES.r2g).set_index(1)
|
|
1361
1349
|
t2g = pd.DataFrame(mTEPES.t2g).set_index(1)
|
|
1362
|
-
OutputToFile01 = pd.Series(data=[t2g[0][g]
|
|
1363
|
-
OutputToFile02 = pd.Series(data=[n2g[0][g]
|
|
1364
|
-
OutputToFile03 = pd.Series(data=[z2g[0][g]
|
|
1365
|
-
OutputToFile04 = pd.Series(data=[a2g[0][g]
|
|
1366
|
-
OutputToFile05 = pd.Series(data=[r2g[0][g]
|
|
1367
|
-
OutputToFile06 = pd.Series(data=[mTEPES.pLoadLevelDuration[p,sc,n]()
|
|
1368
|
-
OutputToFile07 = pd.Series(data=[OptModel.vCommitment
|
|
1369
|
-
OutputToFile08 = pd.Series(data=[OptModel.vStartUp
|
|
1370
|
-
OutputToFile09 = pd.Series(data=[OptModel.vShutDown
|
|
1371
|
-
OutputToFile10 = pd.Series(data=[OptModel.vTotalOutput
|
|
1372
|
-
OutputToFile11 = pd.Series(data=[OptModel.vESSTotalCharge[p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]() if g in mTEPES.es else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='EnergyConsumption [GWh]')
|
|
1373
|
-
OutputToFile12 = pd.Series(data=[OptModel.vESSSpillage
|
|
1374
|
-
OutputToFile13 = pd.Series(data=[OptModel.vTotalOutput
|
|
1375
|
-
OutputToFile14 = pd.Series(data=[OptModel.vTotalOutput
|
|
1376
|
-
OutputToFile15 = pd.Series(data=[OptModel.vTotalOutput
|
|
1377
|
-
OutputToFile16 = pd.Series(data=[OptModel.vReserveUp
|
|
1378
|
-
OutputToFile17 = pd.Series(data=[OptModel.vReserveDown
|
|
1379
|
-
OutputToFile18 = pd.Series(data=[OptModel.vESSReserveUp
|
|
1380
|
-
OutputToFile19 = pd.Series(data=[OptModel.vESSReserveDown[p,sc,n,g]() if g in mTEPES.es else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='ESSReserveDown [MW]' )
|
|
1381
|
-
OutputToFile20 = pd.Series(data=[OptModel.vTotalOutput
|
|
1382
|
-
else OptModel.vTotalOutputHeat[p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]()*mTEPES.pEmissionVarCost[p,sc,n,g]/mTEPES.pCO2Cost() for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Emissions [MtCO2]' )
|
|
1350
|
+
OutputToFile01 = pd.Series(data=[t2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Technology' )
|
|
1351
|
+
OutputToFile02 = pd.Series(data=[n2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Node' )
|
|
1352
|
+
OutputToFile03 = pd.Series(data=[z2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Zone' )
|
|
1353
|
+
OutputToFile04 = pd.Series(data=[a2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Area' )
|
|
1354
|
+
OutputToFile05 = pd.Series(data=[r2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Region' )
|
|
1355
|
+
OutputToFile06 = pd.Series(data=[mTEPES.pLoadLevelDuration[p,sc,n ]() for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='LoadLevelDuration [h]' )
|
|
1356
|
+
OutputToFile07 = pd.Series(data=[OptModel.vCommitment [p,sc,n,g]() if g in mTEPES.nr else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Commitment {0,1}' )
|
|
1357
|
+
OutputToFile08 = pd.Series(data=[OptModel.vStartUp [p,sc,n,g]() if g in mTEPES.nr else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='StartUp {0,1}' )
|
|
1358
|
+
OutputToFile09 = pd.Series(data=[OptModel.vShutDown [p,sc,n,g]() if g in mTEPES.nr else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='ShutDown {0,1}' )
|
|
1359
|
+
OutputToFile10 = pd.Series(data=[OptModel.vTotalOutput [p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]() for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='EnergyProduction [GWh]' )
|
|
1360
|
+
OutputToFile11 = pd.Series(data=[OptModel.vESSTotalCharge [p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]() if g in mTEPES.es else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='EnergyConsumption [GWh]')
|
|
1361
|
+
OutputToFile12 = pd.Series(data=[OptModel.vESSSpillage [p,sc,n,g]() if g in mTEPES.es else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='ESSSpillage [GWh]' )
|
|
1362
|
+
OutputToFile13 = pd.Series(data=[OptModel.vTotalOutput [p,sc,n,g].ub for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='MaxPower [MW]' )
|
|
1363
|
+
OutputToFile14 = pd.Series(data=[OptModel.vTotalOutput [p,sc,n,g].lb for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='MinPower [MW]' )
|
|
1364
|
+
OutputToFile15 = pd.Series(data=[OptModel.vTotalOutput [p,sc,n,g].ub - OptModel.vTotalOutput[p,sc,n,g]() if g in mTEPES.re else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Curtailment [MW]' )
|
|
1365
|
+
OutputToFile16 = pd.Series(data=[OptModel.vReserveUp [p,sc,n,g]() if g in mTEPES.nr else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='ReserveUp [MW]' )
|
|
1366
|
+
OutputToFile17 = pd.Series(data=[OptModel.vReserveDown [p,sc,n,g]() if g in mTEPES.nr else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='ReserveDown [MW]' )
|
|
1367
|
+
OutputToFile18 = pd.Series(data=[OptModel.vESSReserveUp [p,sc,n,g]() if g in mTEPES.es else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='ESSReserveUp [MW]' )
|
|
1368
|
+
OutputToFile19 = pd.Series(data=[OptModel.vESSReserveDown [p,sc,n,g]() if g in mTEPES.es else 0.0 for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='ESSReserveDown [MW]' )
|
|
1369
|
+
OutputToFile20 = pd.Series(data=[OptModel.vTotalOutput [p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]()*mTEPES.pEmissionVarCost[p,sc,n,g]/mTEPES.pCO2Cost() if g not in mTEPES.bo
|
|
1370
|
+
else OptModel.vTotalOutputHeat [p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]()*mTEPES.pEmissionVarCost[p,sc,n,g]/mTEPES.pCO2Cost() for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Emissions [MtCO2]' )
|
|
1383
1371
|
|
|
1384
1372
|
OutputToFile13 *= 1e3
|
|
1385
1373
|
OutputToFile14 *= 1e3
|
|
@@ -1763,14 +1751,14 @@ def ReliabilityResults(DirName, CaseName, OptModel, mTEPES):
|
|
|
1763
1751
|
|
|
1764
1752
|
# Determination of the net demand
|
|
1765
1753
|
if len(mTEPES.re):
|
|
1766
|
-
OutputToFile1 = pd.Series(data=[sum(OptModel.vTotalOutput[p,sc,n,re]() for rt in mTEPES.rt for re in r2r[rt] if (p,re) in mTEPES.pre) for p,sc,n,nd in mTEPES.psnnd], index=pd.Index(mTEPES.psnnd))
|
|
1754
|
+
OutputToFile1 = pd.Series(data=[sum(OptModel.vTotalOutput[p,sc,n,re]() for rt in mTEPES.rt for re in r2r[rt] if (nd,re) in mTEPES.n2g and (p,re) in mTEPES.pre) for p,sc,n,nd in mTEPES.psnnd], index=pd.Index(mTEPES.psnnd))
|
|
1767
1755
|
else:
|
|
1768
|
-
OutputToFile1 = pd.Series(data=[0.0
|
|
1769
|
-
OutputToFile2
|
|
1756
|
+
OutputToFile1 = pd.Series(data=[0.0 for p,sc,n,nd in mTEPES.psnnd], index=pd.Index(mTEPES.psnnd))
|
|
1757
|
+
OutputToFile2 = pd.Series(data=[ mTEPES.pDemandElec [p,sc,n,nd] for p,sc,n,nd in mTEPES.psnnd], index=pd.Index(mTEPES.psnnd))
|
|
1770
1758
|
OutputToFile = OutputToFile2 - OutputToFile1
|
|
1771
1759
|
OutputToFile *= 1e3
|
|
1772
1760
|
OutputToFile = OutputToFile.to_frame(name='MW' )
|
|
1773
|
-
OutputToFile.reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MW', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/
|
|
1761
|
+
OutputToFile.reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MW', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_NetDemandNetwork_'+CaseName+'.csv', sep=',')
|
|
1774
1762
|
OutputToFile.reset_index().pivot_table(index=['level_0','level_1','level_2'], values='MW', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_NetDemand_' +CaseName+'.csv', sep=',')
|
|
1775
1763
|
|
|
1776
1764
|
# Determination of the index: Reserve Margin
|
|
@@ -1802,20 +1790,25 @@ def CostSummaryResults(DirName, CaseName, OptModel, mTEPES):
|
|
|
1802
1790
|
_path = os.path.join(DirName, CaseName)
|
|
1803
1791
|
StartTime = time.time()
|
|
1804
1792
|
|
|
1805
|
-
SysCost
|
|
1806
|
-
GenInvCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pGenInvestCost[gc ]
|
|
1807
|
-
GenRetCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pGenRetireCost[gd ]
|
|
1793
|
+
# SysCost = pd.Series(data=[ OptModel.vTotalSCost() ], index=[''] ).to_frame(name='Total System Cost').stack()
|
|
1794
|
+
GenInvCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pGenInvestCost[gc ] * OptModel.vGenerationInvest[p,gc ]() for gc in mTEPES.gc if (p,gc) in mTEPES.pgc) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Investment Cost').stack()
|
|
1795
|
+
GenRetCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pGenRetireCost[gd ] * OptModel.vGenerationRetire[p,gd ]() for gd in mTEPES.gd if (p,gd) in mTEPES.pgd) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Retirement Cost').stack()
|
|
1808
1796
|
if mTEPES.pIndHydroTopology == 1:
|
|
1809
|
-
RsrInvCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pRsrInvestCost[rc ]
|
|
1797
|
+
RsrInvCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pRsrInvestCost[rc ] * OptModel.vReservoirInvest [p,rc ]() for rc in mTEPES.rn if (p,rc) in mTEPES.prc) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reservoir Investment Cost' ).stack()
|
|
1810
1798
|
else:
|
|
1811
|
-
RsrInvCost = pd.Series(data=[0.0
|
|
1812
|
-
NetInvCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pNetFixedCost [lc ]
|
|
1813
|
-
GenCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]()
|
|
1814
|
-
ConCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]()
|
|
1815
|
-
EmiCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]()
|
|
1816
|
-
RelCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]()
|
|
1799
|
+
RsrInvCost = pd.Series(data=[0.0 for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reservoir Investment Cost' ).stack()
|
|
1800
|
+
NetInvCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pNetFixedCost [lc ] * OptModel.vNetworkInvest [p,lc ]() for lc in mTEPES.lc if (p,lc) in mTEPES.plc) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Network Investment Cost' ).stack()
|
|
1801
|
+
GenCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalGCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if (p,sc) in mTEPES.ps ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Operation Cost' ).stack()
|
|
1802
|
+
ConCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalCCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if (p,sc) in mTEPES.ps ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Consumption Operation Cost').stack()
|
|
1803
|
+
EmiCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalECost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if (p,sc) in mTEPES.ps ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Emission Cost' ).stack()
|
|
1804
|
+
RelCost = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalRCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if (p,sc) in mTEPES.ps ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reliability Cost' ).stack()
|
|
1805
|
+
CostSummary = pd.concat([GenInvCost, GenRetCost, RsrInvCost, NetInvCost, GenCost, ConCost, EmiCost, RelCost]).reset_index().rename(columns={'level_0': 'Period', 'level_1': 'Cost', 0: 'MEUR'})
|
|
1806
|
+
CostSummary['MEUR/year'] = CostSummary['MEUR']
|
|
1807
|
+
for p in mTEPES.p:
|
|
1808
|
+
CostSummary.loc[CostSummary['Period'] == p, 'MEUR/year'] = CostSummary.loc[CostSummary['Period'] == p, 'MEUR'] / mTEPES.pDiscountedWeight[p]
|
|
1809
|
+
CostSummary.to_csv(_path+'/oT_Result_CostSummary_'+CaseName+'.csv', sep=',', index=False)
|
|
1810
|
+
|
|
1817
1811
|
# DemPayment = pd.Series(data=[mTEPES.pDiscountedWeight[p] * sum(mTEPES.pScenProb [p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pDemandElec[p,sc,n,nd] * OptModel.LSRMC [p,sc,n,nd] for sc,n,nd in mTEPES.sc*mTEPES.n*mTEPES.nd if (p,sc) in mTEPES.ps )/1e3 for p in mTEPES.p], index=mTEPES.p).to_frame(name='Demand Payment' ).stack()
|
|
1818
|
-
CostSummary = pd.concat([SysCost, GenInvCost, GenRetCost, RsrInvCost, NetInvCost, GenCost, ConCost, EmiCost, RelCost]).reset_index().rename(columns={'level_0': 'Period', 'level_1': 'Costs', 0: 'MEUR'}).to_csv(_path+'/oT_Result_CostSummary_'+CaseName+'.csv', sep=',', index=False)
|
|
1819
1812
|
|
|
1820
1813
|
WritingResultsTime = time.time() - StartTime
|
|
1821
1814
|
print('Writing cost summary results ... ', round(WritingResultsTime), 's')
|
|
@@ -1939,7 +1932,8 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
1939
1932
|
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pConstantVarCost[p,sc,n,nr] * OptModel.vCommitment [p,sc,n,nr]() +
|
|
1940
1933
|
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pStartUpCost [ nr] * OptModel.vStartUp [p,sc,n,nr]() +
|
|
1941
1934
|
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pShutDownCost [ nr] * OptModel.vShutDown [p,sc,n,nr]()) for p,sc,n,nr in mTEPES.psnnr if (p,nr) in mTEPES.pnr], index=pd.Index(mTEPES.psnnr))
|
|
1942
|
-
|
|
1935
|
+
if len(mTEPES.psnnr):
|
|
1936
|
+
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationCostOperation_' +CaseName+'.csv', sep=',')
|
|
1943
1937
|
|
|
1944
1938
|
if sum(mTEPES.pOperReserveUp[:,:,:,:]) + sum(mTEPES.pOperReserveDw[:,:,:,:]):
|
|
1945
1939
|
OutputToFile = pd.Series(data=[(mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight[p,sc,n]() * mTEPES.pOperReserveCost[nr] * OptModel.vReserveUp [p,sc,n,nr]() +
|
|
@@ -1950,12 +1944,13 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
1950
1944
|
OutputToFile = pd.Series(data=[mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pLinearOMCost [re] * OptModel.vTotalOutput [p,sc,n,re]() for p,sc,n,re in mTEPES.psnre if (p,re) in mTEPES.pre], index=pd.Index(mTEPES.psnre))
|
|
1951
1945
|
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationCostOandM_' +CaseName+'.csv', sep=',')
|
|
1952
1946
|
|
|
1953
|
-
if len(mTEPES.
|
|
1947
|
+
if len(mTEPES.eh):
|
|
1954
1948
|
OutputToFile = pd.Series(data=[ mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pLinearVarCost[p,sc,n,eh] * OptModel.vESSTotalCharge[p,sc,n,eh]() for p,sc,n,eh in mTEPES.psneh if (p,eh) in mTEPES.peh], index=pd.Index(mTEPES.psneh))
|
|
1955
1949
|
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_ConsumptionCostOperation_' +CaseName+'.csv', sep=',')
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1950
|
+
if sum(mTEPES.pIndOperReserve[eh] for eh in mTEPES.eh if mTEPES.pIndOperReserve[eh] == 0):
|
|
1951
|
+
OutputToFile = pd.Series(data=[(mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight[p,sc,n]() * mTEPES.pOperReserveCost[eh] * OptModel.vESSReserveUp [p,sc,n,eh]() +
|
|
1952
|
+
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight[p,sc,n]() * mTEPES.pOperReserveCost[eh] * OptModel.vESSReserveDown[p,sc,n,eh]()) for p,sc,n,eh in mTEPES.psneh if (p,eh) in mTEPES.peh], index=pd.Index(mTEPES.psneh))
|
|
1953
|
+
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_ConsumptionCostOperatingReserve_'+CaseName+'.csv', sep=',')
|
|
1959
1954
|
|
|
1960
1955
|
OutputToFile = pd.Series(data=[mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pEmissionVarCost[p,sc,n,g] * OptModel.vTotalOutput[p,sc,n,g]() for p,sc,n,g in mTEPES.psng if (p,g) in mTEPES.pg], index=pd.Index(mTEPES.psng))
|
|
1961
1956
|
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(_path+'/oT_Result_GenerationCostEmission_' +CaseName+'.csv', sep=',')
|
|
@@ -2003,22 +1998,26 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2003
1998
|
OutputResults3 = pd.Series(data=[mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pLinearOMCost [re] * OptModel.vTotalOutput [p,sc,n,re]() for p,sc,n,re in sPSNRE if (p,re) in mTEPES.pre], index=pd.Index(sPSNRE))
|
|
2004
1999
|
OutputResults3 = Transformation1(OutputResults3, 'Generation O&M Cost')
|
|
2005
2000
|
|
|
2006
|
-
if len(mTEPES.eh)
|
|
2001
|
+
if len(mTEPES.eh):
|
|
2007
2002
|
sPSNES = [(p,sc,n,eh) for p,sc,n,eh in mTEPES.psneh if eh in g2a[ar]]
|
|
2008
2003
|
if len(sPSNES):
|
|
2009
2004
|
OutputResults4 = pd.Series(data=[ mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pLinearVarCost [p,sc,n,eh] * OptModel.vESSTotalCharge[p,sc,n,eh]() for p,sc,n,eh in sPSNES if (p,eh) in mTEPES.peh], index=pd.Index(sPSNES))
|
|
2010
|
-
OutputResults5 = pd.Series(data=[(mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pOperReserveCost[ eh] * OptModel.vESSReserveUp [p,sc,n,eh]() +
|
|
2011
|
-
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pOperReserveCost[ eh] * OptModel.vESSReserveDown[p,sc,n,eh]()) for p,sc,n,eh in sPSNES if (p,eh) in mTEPES.peh], index=pd.Index(sPSNES))
|
|
2012
2005
|
OutputResults4 = Transformation1(OutputResults4, 'Consumption Operation Cost')
|
|
2013
|
-
|
|
2006
|
+
if sum(mTEPES.pIndOperReserve[eh] for eh in mTEPES.eh if mTEPES.pIndOperReserve[eh] == 0):
|
|
2007
|
+
OutputResults5 = pd.Series(data=[(mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pOperReserveCost[ eh] * OptModel.vESSReserveUp [p,sc,n,eh]() +
|
|
2008
|
+
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pOperReserveCost[ eh] * OptModel.vESSReserveDown[p,sc,n,eh]()) for p,sc,n,eh in sPSNES if (p,eh) in mTEPES.peh], index=pd.Index(sPSNES))
|
|
2009
|
+
OutputResults5 = Transformation1(OutputResults5, 'Consumption Operating Reserve Cost')
|
|
2014
2010
|
|
|
2015
2011
|
sPSNND = [(p,sc,n,nd) for p,sc,n,nd in mTEPES.psnnd if (nd,ar) in mTEPES.ndar]
|
|
2016
2012
|
if len(sPSNND):
|
|
2017
2013
|
OutputResults7 = pd.Series(data=[mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pENSCost() * OptModel.vENS [p,sc,n,nd]() for p,sc,n,nd in sPSNND], index=pd.Index(sPSNND))
|
|
2018
2014
|
OutputResults7 = Transformation1(OutputResults7, 'Reliability Cost')
|
|
2019
2015
|
|
|
2020
|
-
OutputResults = pd.concat([OutputResults1, OutputResults2, OutputResults3, OutputResults4, OutputResults5, OutputResults6, OutputResults7],
|
|
2021
|
-
OutputResults
|
|
2016
|
+
OutputResults = pd.concat([OutputResults1, OutputResults2, OutputResults3, OutputResults4, OutputResults5, OutputResults6, OutputResults7]).reset_index().rename(columns={'level_0': 'Period', 'level_1': 'Scenario', 'level_2': 'Cost', 0: 'MEUR'})
|
|
2017
|
+
OutputResults['MEUR/year'] = OutputResults['MEUR']
|
|
2018
|
+
for p,sc in mTEPES.psc:
|
|
2019
|
+
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]()
|
|
2020
|
+
OutputResults.to_csv(_path+'/oT_Result_CostSummary_'+ar+'_'+CaseName+'.csv', sep=',', index=False)
|
|
2022
2021
|
|
|
2023
2022
|
sPSSTNNDG = [(p,sc,st,n,nd,g) for p,sc,st,n,nd,g in mTEPES.s2n*mTEPES.n2g if (p,g) in mTEPES.pg and (p,sc,n) in mTEPES.psn]
|
|
2024
2023
|
OutputResults = pd.Series(data=[mTEPES.pDuals["".join(["eBalanceElec_", str(p), "_", str(sc), "_", str(st), "('", str(n), "', '", str(nd), "')"])]/mTEPES.pPeriodProb[p,sc]()/mTEPES.pLoadLevelDuration[p,sc,n]()*OptModel.vTotalOutput [p,sc,n,g]()*np.sign(getattr(OptModel, 'eBalanceElec_'+str(p)+'_'+str(sc)+'_'+str(st))[n,nd].ub+1e-10) for p,sc,st,n,nd,g in sPSSTNNDG], index=pd.Index(sPSSTNNDG))
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) -
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 23, 2024
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -20,10 +20,10 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
20
20
|
#%% solving the problem
|
|
21
21
|
Solver = SolverFactory(SolverName) # select solver
|
|
22
22
|
if SolverName == 'gurobi':
|
|
23
|
-
FileName = _path+'/openTEPES_gurobi_'+CaseName+'.log'
|
|
23
|
+
FileName = _path+'/openTEPES_gurobi_'+CaseName+'_'+str(p)+'_'+str(sc)+'_'+str(st)+'.log'
|
|
24
24
|
if os.path.exists(FileName):
|
|
25
25
|
os.remove(FileName)
|
|
26
|
-
Solver.options['LogFile' ] = _path+'/openTEPES_gurobi_'+CaseName+'.log'
|
|
26
|
+
Solver.options['LogFile' ] = _path+'/openTEPES_gurobi_'+CaseName+'_'+str(p)+'_'+str(sc)+'_'+str(st)+'.log'
|
|
27
27
|
# Solver.options['SolutionTarget'] = 1 # optimal solution with or without basic solutions
|
|
28
28
|
Solver.options['Method' ] = 2 # barrier method
|
|
29
29
|
Solver.options['Crossover' ] = -1
|
|
@@ -38,10 +38,10 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
38
38
|
Solver.options['TimeLimit' ] = 36000
|
|
39
39
|
Solver.options['IterationLimit' ] = 36000000
|
|
40
40
|
if SolverName == 'cplex':
|
|
41
|
-
FileName = _path+'/openTEPES_cplex_'+CaseName+'.log'
|
|
41
|
+
FileName = _path+'/openTEPES_cplex_'+CaseName+'_'+str(p)+'_'+str(sc)+'_'+str(st)+'.log'
|
|
42
42
|
if os.path.exists(FileName):
|
|
43
43
|
os.remove(FileName)
|
|
44
|
-
# Solver.options['LogFile' ] = _path+'/openTEPES_cplex_'+CaseName+'.log'
|
|
44
|
+
# Solver.options['LogFile' ] = _path+'/openTEPES_cplex_'+CaseName+'_'+str(p)+'_'+str(sc)+'_'+str(st)+'.log'
|
|
45
45
|
Solver.options['LPMethod' ] = 4 # barrier method
|
|
46
46
|
# Solver.options['BarCrossAlg' ] = 0
|
|
47
47
|
# Solver.options['NumericalEmphasis'] = 1
|
|
@@ -52,10 +52,10 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
52
52
|
Solver.options['TimeLimit' ] = 36000
|
|
53
53
|
# Solver.options['ItLim' ] = 36000000
|
|
54
54
|
if SolverName == 'appsi_highs':
|
|
55
|
-
FileName = _path+'/openTEPES_highs_'+CaseName+'.log'
|
|
55
|
+
FileName = _path+'/openTEPES_highs_'+CaseName+'_'+str(p)+'_'+str(sc)+'_'+str(st)+'.log'
|
|
56
56
|
if os.path.exists(FileName):
|
|
57
57
|
os.remove(FileName)
|
|
58
|
-
Solver.options['log_file' ] = _path+'/openTEPES_highs_'+CaseName+'.log'
|
|
58
|
+
Solver.options['log_file' ] = _path+'/openTEPES_highs_'+CaseName+'_'+str(p)+'_'+str(sc)+'_'+str(st)+'.log'
|
|
59
59
|
Solver.options['log_to_console' ] = 'true'
|
|
60
60
|
Solver.options['solver' ] = 'simplex'
|
|
61
61
|
Solver.options['run_crossover' ] = 'off'
|
|
@@ -88,7 +88,7 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
88
88
|
print('Termination condition: ', SolverResults.solver.termination_condition)
|
|
89
89
|
if SolverResults.solver.termination_condition == TerminationCondition.infeasible or SolverResults.solver.termination_condition == TerminationCondition.maxTimeLimit or SolverResults.solver.termination_condition == TerminationCondition.infeasible.maxIterations:
|
|
90
90
|
log_infeasible_constraints(OptModel, log_expression=True, log_variables=True)
|
|
91
|
-
logging.basicConfig(filename=_path+'/openTEPES_infeasibilities_'+CaseName+'.log', level=logging.INFO)
|
|
91
|
+
logging.basicConfig(filename=_path+'/openTEPES_infeasibilities_'+CaseName+'_'+str(p)+'_'+str(sc)+'_'+str(st)+'.log', level=logging.INFO)
|
|
92
92
|
raise ValueError('Problem infeasible')
|
|
93
93
|
SolverResults.write() # summary of the solver results
|
|
94
94
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: openTEPES
|
|
3
|
-
Version: 4.17.
|
|
3
|
+
Version: 4.17.8
|
|
4
4
|
Summary: Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES)
|
|
5
5
|
Home-page: https://pascua.iit.comillas.edu/aramos/openTEPES/index.html
|
|
6
6
|
Author: IIT-EnergySystemModels
|
|
@@ -68,7 +68,7 @@ defined as a set of **generation, storage, and (electricity, hydrogen, and heat)
|
|
|
68
68
|
|
|
69
69
|
It is integrated in the `open energy system modelling platform <https://openenergymodels.net/>`_ helping modelling Europe's energy system.
|
|
70
70
|
|
|
71
|
-
It has been used by the **Ministry for the Ecological Transition and the Demographic Challenge (MITECO)** to analyze the electricity sector in the latest Spanish `National Energy and Climate Plan (NECP) 2023-2030 <https://
|
|
71
|
+
It has been used by the **Ministry for the Ecological Transition and the Demographic Challenge (MITECO)** to analyze the electricity sector in the latest Spanish `National Energy and Climate Plan (NECP) Update 2023-2030 <https://www.miteco.gob.es/content/dam/miteco/es/energia/files-1/pniec-2023-2030/PNIEC_2024_240924.pdf>`_ in September 2024.
|
|
72
72
|
|
|
73
73
|
Reference
|
|
74
74
|
############
|
|
@@ -132,8 +132,7 @@ Installation
|
|
|
132
132
|
############
|
|
133
133
|
`Installation guide <https://pascua.iit.comillas.edu/aramos/openTEPES_installation.pdf>`_.
|
|
134
134
|
|
|
135
|
-
There are 2 ways to get all 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 (version 3.
|
|
136
|
-
|
|
135
|
+
There are 2 ways to get all 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 (version 3.11) installation, you can also download the required packages by yourself.
|
|
137
136
|
|
|
138
137
|
Miniconda (recommended)
|
|
139
138
|
=======================
|
|
@@ -144,7 +143,7 @@ Miniconda (recommended)
|
|
|
144
143
|
2. **Packages and Solver**:
|
|
145
144
|
|
|
146
145
|
1. Launch a new Anaconda prompt (or terminal in any IDE)
|
|
147
|
-
2. The `HiGHS 1.7.
|
|
146
|
+
2. The `HiGHS 1.7.2 <https://ergo-code.github.io/HiGHS/dev/interfaces/python/#python-getting-started>`_ is our recommendation if you want a free and open-source solver.
|
|
148
147
|
3. Install openTEPES via pip by ``pip install openTEPES``
|
|
149
148
|
|
|
150
149
|
Continue at `Get Started <#get-started>`_ and see the `Tips <#tips>`_.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
openTEPES/__init__.py,sha256=
|
|
1
|
+
openTEPES/__init__.py,sha256=v3FaNgxRmDOkSqAYJY3r1KzqhSzemsiNXPlfkdxhTLw,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=
|
|
6
|
-
openTEPES/openTEPES_ModelFormulation.py,sha256=
|
|
7
|
-
openTEPES/openTEPES_OutputResults.py,sha256=
|
|
8
|
-
openTEPES/openTEPES_ProblemSolving.py,sha256=
|
|
3
|
+
openTEPES/openTEPES.py,sha256=87aSr5RF0Xd7y7ywZ0ZoWZwQDG3nkFF3tAfg2hCwuNE,20796
|
|
4
|
+
openTEPES/openTEPES_InputData.py,sha256=o5TK_ZUelz29Ur3zgVg8Aa6MMX5KRablVIGU71fO0Os,210776
|
|
5
|
+
openTEPES/openTEPES_Main.py,sha256=2SDIKLuefs2HyMrwYu3FUpomo266Tib62oXipetcU7s,39836
|
|
6
|
+
openTEPES/openTEPES_ModelFormulation.py,sha256=irAQUBmhTfJUJQPLgAlV86fbjtovqllZyRSpKjnlnxI,116871
|
|
7
|
+
openTEPES/openTEPES_OutputResults.py,sha256=BlifXH0YW74dlwlRLZm91UfAEIBZldL0Hx1N33puW84,203935
|
|
8
|
+
openTEPES/openTEPES_ProblemSolving.py,sha256=vjkC7Xwhx7BQjvWoFufSt8Wb4WjQfveYmWlK2Rt1778,14968
|
|
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
|
|
@@ -335,8 +335,8 @@ openTEPES/sSEP/oT_Dict_Storage_sSEP.csv,sha256=H2rJXZvoMuT-25sI2GpG8IuiNKD-dxuty
|
|
|
335
335
|
openTEPES/sSEP/oT_Dict_Technology_sSEP.csv,sha256=MCTpplzz7_eVPKQfOw35c86ib6CTtW6UK6JrbCJ8wls,170
|
|
336
336
|
openTEPES/sSEP/oT_Dict_ZoneToArea_sSEP.csv,sha256=AUDCs5Bg6sw9f2pVjGP1o4IJjXFF_VrokOGf_V3QsEI,24
|
|
337
337
|
openTEPES/sSEP/oT_Dict_Zone_sSEP.csv,sha256=TBud-fvbFbiAsuutxTYe8wWlv_x1P8oyWXILMpYiXJc,13
|
|
338
|
-
opentepes-4.17.
|
|
339
|
-
opentepes-4.17.
|
|
340
|
-
opentepes-4.17.
|
|
341
|
-
opentepes-4.17.
|
|
342
|
-
opentepes-4.17.
|
|
338
|
+
opentepes-4.17.8.dist-info/entry_points.txt,sha256=gNNPrDaTsRuRJXI1FLNgqMX1CiJ45bEp1dEDH7ZB8Oc,49
|
|
339
|
+
opentepes-4.17.8.dist-info/LICENSE,sha256=4O7bphXVzRuYavtsWzpLGuM3E-fp3HTRna7F4yIfnS4,35184
|
|
340
|
+
opentepes-4.17.8.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
341
|
+
opentepes-4.17.8.dist-info/METADATA,sha256=ht4PLI5v4aEGsBP_zNL1NniPkH_d_aPcskrkfVi3RCQ,17788
|
|
342
|
+
opentepes-4.17.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|