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 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.7"
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) - September 20, 2024
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.7 - September 20, 2024')
43
- print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.17.7 - September 20, 2024', file=open(_path+'/openTEPES_version_'+CaseName+'.log','w'))
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]
@@ -1,5 +1,5 @@
1
1
  """
2
- Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - September 20, 2024
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 = pLineNTCBck.where (pLineNTCBck > 0.0, pLineNTCFrw)
507
+ pLineNTCBck = pLineNTCBck.where (pLineNTCBck > 0.0, pLineNTCFrw)
503
508
  # replace pLineNTCFrw = 0.0 by pLineNTCBck
504
- pLineNTCFrw = pLineNTCFrw.where (pLineNTCFrw > 0.0, pLineNTCBck)
509
+ pLineNTCFrw = pLineNTCFrw.where (pLineNTCFrw > 0.0, pLineNTCBck)
505
510
  # replace pGenUpInvest = 0.0 by 1.0
506
- pGenUpInvest = pGenUpInvest.where(pGenUpInvest > 0.0, 1.0 )
511
+ pGenUpInvest = pGenUpInvest.where (pGenUpInvest > 0.0, 1.0 )
507
512
  # replace pGenUpRetire = 0.0 by 1.0
508
- pGenUpRetire = pGenUpRetire.where(pGenUpRetire > 0.0, 1.0 )
513
+ pGenUpRetire = pGenUpRetire.where (pGenUpRetire > 0.0, 1.0 )
509
514
  # replace pNetUpInvest = 0.0 by 1.0
510
- pNetUpInvest = pNetUpInvest.where(pNetUpInvest > 0.0, 1.0 )
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 [[nr for nr in n2a[ar]]] < pEpsilon] = 0.0
1100
- pMaxCharge2ndBlock[pMaxCharge2ndBlock[[eh for eh in g2a[ar]]] < pEpsilon] = 0.0
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
- pMaxPower2ndBlock = pMaxPower2ndBlock.where (pMaxPower2ndBlock > 0.0, 0.0)
1137
- pMaxCharge2ndBlock = pMaxCharge2ndBlock.where(pMaxCharge2ndBlock > 0.0, 0.0)
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] for ch in mTEPES.ch] for p,sc,n in mTEPES.psn], index=pd.MultiIndex.from_tuples(mTEPES.psn), columns=mTEPES.ch)
1143
- 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)
1144
- 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))
1145
- 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))
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 y going to be used to order the generating units
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 a number
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 ]() < pEpsilon:
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
 
@@ -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) - September 20, 2024
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.7 - September 20, 2024\033[0m')
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) - September 20, 2024
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
- # the small tolerance pEpsilon=5e-2 is added to detect if the generator is ramping up/down
1060
- pEpsilon = 5e-2
1061
- def eRampUpState(OptModel,n,nr):
1062
- if (p,nr) in mTEPES.pnr:
1063
- if mTEPES.pStableTime[nr] and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and mTEPES.pDuration[p,sc,n]():
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 [nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
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] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp [nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,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] - pEpsilon * OptModel.vRampDwState[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] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,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
- return Constraint.Skip
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 mTEPES.pStableTime[nr] and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and mTEPES.pDuration[p,sc,n]():
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 [nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
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] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw [nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,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] - pEpsilon * OptModel.vRampUpState[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] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,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
- return Constraint.Skip
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
- MinStableTimeLoadLevels = []
1132
- if sum(mTEPES.pStableTime[nr] for nr in mTEPES.nr if (p,nr) in mTEPES.pnr):
1133
- for n in mTEPES.n:
1134
- for nr in mTEPES.nr:
1135
- if (mTEPES.pStableTime[nr] and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and mTEPES.n.ord(n) >= mTEPES.pStableTime[nr] + 2):
1136
- for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)-mTEPES.pStableTime[nr]-1:mTEPES.n.ord(n)-1]:
1137
- MinStableTimeLoadLevels.append((n,n2,nr))
1138
-
1139
- def eMinStableTime(OptModel,n,n2,nr):
1140
- if (p,nr) in mTEPES.pnr:
1141
- return OptModel.vRampUpState[p,sc,n,nr] <= 1 - OptModel.vRampDwState[p,sc,n2,nr]
1142
- else:
1143
- return Constraint.Skip
1144
- setattr(OptModel, 'eMinStableTime_'+str(p)+'_'+str(sc)+'_'+str(st), Constraint(MinStableTimeLoadLevels, rule=eMinStableTime, doc='minimum stable time [p.u.]'))
1145
-
1146
- if pIndLogConsole == 1:
1147
- print('eMinStableTime ... ', len(getattr(OptModel, 'eMinStableTime_'+str(p)+'_'+str(sc)+'_'+str(st))), ' rows')
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) - September 12, 2024
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]()) for p,sc,n,g in sPSNG], index=pd.Index(sPSNG))
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]() for p,sc,n,re in mTEPES.psnre], index=pd.Index(mTEPES.psnre))
487
- OutputToFile2 = pd.Series(data=[(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))
488
- for p,sc,n,re in mTEPES.psnre:
489
- if re in mTEPES.gc:
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]()) for p,sc,n,re in mTEPES.psnre], index=pd.Index(mTEPES.psnre))
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]() for p,sc,n,re in mTEPES.psnre], index=pd.Index(mTEPES.psnre))
516
- for p,sc,n,re in mTEPES.psnre:
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 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 ))
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] = OptModel.vTotalOutput[p,sc,n,ch] /mTEPES.pPower2HeatRatio[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] = OptModel.vESSTotalCharge[p,sc,n,hp] /mTEPES.pProductionFunctionHeat[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,chp) for p,sc,n,chp in mTEPES.psnchp if OptModel.vTotalOutputHeat[p,sc,n,chp].ub - OptModel.vTotalOutputHeat[p,sc,n,chp]() > pEpsilon]
629
- OutputToFile = pd.Series(data=[(OptModel.vTotalOutputHeat[p,sc,n,chp].ub - OptModel.vTotalOutputHeat[p,sc,n,chp]()) for p,sc,n,chp in sPSNG], index=pd.Index(sPSNG))
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]() for p,sc,n,es in mTEPES.psnes], index=pd.Index(mTEPES.psnes))
783
- OutputToFile2 = pd.Series(data=[(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))
784
- for p,sc,n,ec in mTEPES.psnec:
785
- OutputToFile1[p,sc,n,ec] *= OptModel.vGenerationInvest[p,ec]()
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] for p,gc in mTEPES.pgc if 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] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Technology' )
1363
- OutputToFile02 = pd.Series(data=[n2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Node' )
1364
- OutputToFile03 = pd.Series(data=[z2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Zone' )
1365
- OutputToFile04 = pd.Series(data=[a2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Area' )
1366
- OutputToFile05 = pd.Series(data=[r2g[0][g] for p,sc,n,g in mTEPES.psng], index=pd.Index(mTEPES.psng)).to_frame(name='Region' )
1367
- 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]' )
1368
- 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}' )
1369
- 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}' )
1370
- 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}' )
1371
- 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]' )
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 [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]' )
1374
- 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]' )
1375
- 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]' )
1376
- 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]' )
1377
- 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]' )
1378
- 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]' )
1379
- 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]' )
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 [p,sc,n,g]()*mTEPES.pLoadLevelDuration[p,sc,n]()*mTEPES.pEmissionVarCost[p,sc,n,g]/mTEPES.pCO2Cost() if g not in mTEPES.bo
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 for p,sc,n,nd in mTEPES.psnnd], index=pd.Index(mTEPES.psnnd))
1769
- OutputToFile2 = pd.Series(data=[ mTEPES.pDemandElec [p,sc,n,nd] for p,sc,n,nd in mTEPES.psnnd], index=pd.Index(mTEPES.psnnd))
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+'/oT_Result_NetworkNetDemand_'+CaseName+'.csv', sep=',')
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 = pd.Series(data=[ OptModel.vTotalSCost() ], index=[''] ).to_frame(name='Total System Cost').stack()
1806
- 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()
1807
- 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()
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 ] * 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()
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 for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reservoir Investment Cost').stack()
1812
- 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()
1813
- 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()
1814
- 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()
1815
- 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()
1816
- 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()
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
- 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=',')
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.es) and sum(mTEPES.pIndOperReserve[es] for es in mTEPES.es if mTEPES.pIndOperReserve[es] == 0):
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
- 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]() +
1957
- 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))
1958
- 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=',')
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) and sum(mTEPES.pIndOperReserve[eh] for eh in mTEPES.eh if mTEPES.pIndOperReserve[eh] == 0):
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
- OutputResults5 = Transformation1(OutputResults5, 'Consumption Operating Reserve Cost')
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], axis=0)
2021
- OutputResults.rename_axis(['Period', 'Scenario', 'Cost'], axis=0).to_csv(_path+'/oT_Result_CostSummary_'+ar+'_'+CaseName+'.csv', sep=',')
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) - August 01, 2024
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
1
+ Metadata-Version: 2.3
2
2
  Name: openTEPES
3
- Version: 4.17.7
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://energia.gob.es/_layouts/15/HttpHandlerParticipacionPublicaAnexos.ashx?k=64347>`_ in June 2023.
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.8 | 3.9 **recommended**, 2.7 is supported as well) installation, you can also download the required packages by yourself.
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.0 <https://ergo-code.github.io/HiGHS/dev/interfaces/python/#python-getting-started>`_ is our recommendation if you want a free and open-source solver.
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=938AmMocR2j63IXr6PG95ry5yb9aIe3vQE-PvgoNxTk,832
1
+ openTEPES/__init__.py,sha256=v3FaNgxRmDOkSqAYJY3r1KzqhSzemsiNXPlfkdxhTLw,832
2
2
  openTEPES/openTEPES.mapbox_token,sha256=xsXNkwGp2vzXqQy2zVkyLhhNcNWniK2BMeOFpc5SZHI,93
3
- openTEPES/openTEPES.py,sha256=mvGSFlwWdJqhw7OVuYWR9vAwkPjxO9Xs6UnKMC13wGs,20802
4
- openTEPES/openTEPES_InputData.py,sha256=F4bJrJJgNeM7lCuwpAigEP9vhyelUYTl9I_PBF1xHVc,207974
5
- openTEPES/openTEPES_Main.py,sha256=UomHmuMZt0IrshRxaLIiTiQTCwr-tnwC3L9r82MSFXY,39840
6
- openTEPES/openTEPES_ModelFormulation.py,sha256=QUHA39lNxb6Wfrg23lJ84dcEIrDLRwuErKMiHOM6q98,112645
7
- openTEPES/openTEPES_OutputResults.py,sha256=W_oEYQb0L2EYFhbVlMldzXP214mJe6lggzAahrJnYtA,203163
8
- openTEPES/openTEPES_ProblemSolving.py,sha256=wsFwr3gyH9vdNHstV7RwB-NRZrtftTBfXzuFf7zEg90,14722
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.7.dist-info/entry_points.txt,sha256=gNNPrDaTsRuRJXI1FLNgqMX1CiJ45bEp1dEDH7ZB8Oc,49
339
- opentepes-4.17.7.dist-info/LICENSE,sha256=4O7bphXVzRuYavtsWzpLGuM3E-fp3HTRna7F4yIfnS4,35184
340
- opentepes-4.17.7.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
341
- opentepes-4.17.7.dist-info/METADATA,sha256=n9mjU9Av6PJ7Isq3VCcxz22D_uTYxPn1F2YIvN9-VQA,17808
342
- opentepes-4.17.7.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.9.0
2
+ Generator: flit 3.10.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any