openTEPES 4.18.9__py3-none-any.whl → 4.18.11__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.
@@ -1,2 +1,2 @@
1
1
  ENSCost,HNSCost,HTNSCost,CO2Cost,UpReserveActivation,DwReserveActivation,MinRatioDwUp,MaxRatioDwUp,SBase,ReferenceNode,TimeStep,EconomicBaseYear,AnnualDiscountRate
2
- 10000,10000,10000,25,0.25,0.3,0,1,100,Node_4,1,2020,0
2
+ 10000,10000,10000,25,0.25,0.3,0,1,100,Node_4,2,2020,0
openTEPES/__init__.py CHANGED
@@ -14,7 +14,7 @@ Open Generation, Storage, and Transmission Operation and Expansion Planning Mode
14
14
  >>> import openTEPES as oT
15
15
  >>> oT.routine("9n", "C:\\Users\\UserName\\Documents\\GitHub\\openTEPES", "glpk")
16
16
  """
17
- __version__ = "4.18.9"
17
+ __version__ = "4.18.11"
18
18
 
19
19
  from .openTEPES_Main import main
20
20
  from .openTEPES import *
openTEPES/openTEPES.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 22, 2026
2
+ Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 28, 2026
3
3
  """
4
4
 
5
5
  # import dill as pickle
@@ -38,8 +38,8 @@ def openTEPES_run(DirName, CaseName, SolverName, pIndOutputResults, pIndLogConso
38
38
  idxDict['y' ] = 1
39
39
 
40
40
  #%% model declaration
41
- mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.9 - January 22, 2026')
42
- print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.9 - January 22, 2026', file=open(f'{_path}/openTEPES_version_{CaseName}.log','w'))
41
+ mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.11 - January 28, 2026')
42
+ print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.11 - January 28, 2026', file=open(f'{_path}/openTEPES_version_{CaseName}.log','w'))
43
43
 
44
44
  pIndOutputResults = [j for i,j in idxDict.items() if i == pIndOutputResults][0]
45
45
  pIndLogConsole = [j for i,j in idxDict.items() if i == pIndLogConsole ][0]
@@ -1,5 +1,5 @@
1
1
  """
2
- Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 19, 2026
2
+ Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 28, 2026
3
3
  """
4
4
 
5
5
  import time
@@ -515,7 +515,7 @@ def DataConfiguration(mTEPES):
515
515
 
516
516
  StartTime = time.time()
517
517
  #%% Getting the branches from the electric network data
518
- sBr = [(ni,nf) for (ni,nf,cc) in mTEPES.dFrame['dfNetwork'].index]
518
+ sBr = [(ni,nf) for ni,nf,cc in mTEPES.dFrame['dfNetwork'].index]
519
519
  # Dropping duplicate keys
520
520
  sBrList = [(ni,nf) for n,(ni,nf) in enumerate(sBr) if (ni,nf) not in sBr[:n]]
521
521
 
@@ -527,7 +527,7 @@ def DataConfiguration(mTEPES):
527
527
  mTEPES.n = Set(doc='load levels' , initialize=[nn for nn in mTEPES.nn if sum(mTEPES.dPar['pDuration'] [p,sc,nn] for p,sc in mTEPES.ps) > 0])
528
528
  mTEPES.n2 = Set(doc='load levels' , initialize=[nn for nn in mTEPES.nn if sum(mTEPES.dPar['pDuration'] [p,sc,nn] for p,sc in mTEPES.ps) > 0])
529
529
  mTEPES.g = Set(doc='generating units' , initialize=[gg for gg in mTEPES.gg if (mTEPES.dPar['pRatedMaxPowerElec'] [gg] > 0.0 or mTEPES.dPar['pRatedMaxCharge'][gg] > 0.0 or mTEPES.dPar['pRatedMaxPowerHeat'] [gg] > 0.0) and mTEPES.dPar['pElecGenPeriodIni'][gg] <= mTEPES.p.last() and mTEPES.dPar['pElecGenPeriodFin'][gg] >= mTEPES.p.first() and mTEPES.dPar['pGenToNode'].reset_index().set_index(['Generator']).isin(mTEPES.nd)['Node'][gg]]) # excludes generators with empty node
530
- mTEPES.t = Set(doc='thermal units' , initialize=[g for g in mTEPES.g if mTEPES.dPar['pRatedLinearOperCost'][g ] > 0.0])
530
+ mTEPES.tr = Set(doc='thermal units' , initialize=[g for g in mTEPES.g if mTEPES.dPar['pRatedLinearOperCost'][g ] > 0.0])
531
531
  mTEPES.re = Set(doc='RES units' , initialize=[g for g in mTEPES.g if mTEPES.dPar['pRatedLinearOperCost'][g ] == 0.0 and mTEPES.dPar['pRatedMaxStorage'][g] == 0.0 and mTEPES.dPar['pProductionFunctionH2'][g ] == 0.0 and mTEPES.dPar['pProductionFunctionHeat'][g ] == 0.0 and mTEPES.dPar['pProductionFunctionHydro'][g ] == 0.0])
532
532
  mTEPES.es = Set(doc='ESS units' , initialize=[g for g in mTEPES.g if (mTEPES.dPar['pRatedMaxCharge'][g ] > 0.0 or mTEPES.dPar['pRatedMaxStorage'][g] > 0.0 or mTEPES.dPar['pProductionFunctionH2'][g ] > 0.0 or mTEPES.dPar['pProductionFunctionHeat'][g ] > 0.0) and mTEPES.dPar['pProductionFunctionHydro'][g ] == 0.0])
533
533
  mTEPES.h = Set(doc='hydro units' , initialize=[g for g in mTEPES.g if mTEPES.dPar['pProductionFunctionH2'][g ] == 0.0 and mTEPES.dPar['pProductionFunctionHeat'][g ] == 0.0 and mTEPES.dPar['pProductionFunctionHydro'][g ] > 0.0])
@@ -613,7 +613,7 @@ def DataConfiguration(mTEPES):
613
613
  #%% inverse index load level to stage
614
614
  mTEPES.dPar['pStageToLevel'] = mTEPES.dPar['pLevelToStage'].reset_index().set_index(['Period','Scenario','Stage'])['LoadLevel']
615
615
  #Filter only valid indices
616
- mTEPES.dPar['pStageToLevel'] = mTEPES.dPar['pStageToLevel'].loc[mTEPES.dPar['pStageToLevel'].index.isin([(p,s,st) for (p,s) in mTEPES.ps for st in mTEPES.st]) & mTEPES.dPar['pStageToLevel'].isin(mTEPES.n)]
616
+ mTEPES.dPar['pStageToLevel'] = mTEPES.dPar['pStageToLevel'].loc[mTEPES.dPar['pStageToLevel'].index.isin([(p,s,st) for p,s in mTEPES.ps for st in mTEPES.st]) & mTEPES.dPar['pStageToLevel'].isin(mTEPES.n)]
617
617
  #Reorder the elements
618
618
  mTEPES.dPar['pStageToLevel'] = [(p,sc,st,n) for (p,sc,st),n in mTEPES.dPar['pStageToLevel'].items()]
619
619
  mTEPES.s2n = Set(initialize=mTEPES.dPar['pStageToLevel'], doc='Load level to stage')
@@ -642,6 +642,7 @@ def DataConfiguration(mTEPES):
642
642
  None: Sets are added directly to the mTEPES object.
643
643
  '''
644
644
  mTEPES.pg = Set(initialize = [(p, g ) for p, g in mTEPES.p *mTEPES.g if mTEPES.dPar['pElecGenPeriodIni'][g ] <= p and mTEPES.dPar['pElecGenPeriodFin'][g ] >= p])
645
+ mTEPES.ptr = Set(initialize = [(p, tr ) for p, tr in mTEPES.p *mTEPES.tr if (p,tr) in mTEPES.pg])
645
646
  mTEPES.pgc = Set(initialize = [(p, gc ) for p, gc in mTEPES.p *mTEPES.gc if (p,gc) in mTEPES.pg])
646
647
  mTEPES.pnr = Set(initialize = [(p, nr ) for p, nr in mTEPES.p *mTEPES.nr if (p,nr) in mTEPES.pg])
647
648
  mTEPES.pch = Set(initialize = [(p, ch ) for p, ch in mTEPES.p *mTEPES.ch if (p,ch) in mTEPES.pg])
@@ -669,6 +670,7 @@ def DataConfiguration(mTEPES):
669
670
  mTEPES.pseh = Set(initialize = [(p,sc, eh) for p,sc, eh in mTEPES.ps *mTEPES.eh if (p,eh) in mTEPES.peh ])
670
671
  mTEPES.psn = Set(initialize = [(p,sc,n ) for p,sc,n in mTEPES.ps *mTEPES.n if mTEPES.dPar['pDuration'][p,sc,n]])
671
672
  mTEPES.psng = Set(initialize = [(p,sc,n,g ) for p,sc,n,g in mTEPES.psn*mTEPES.g if (p,g ) in mTEPES.pg ])
673
+ mTEPES.psntr = Set(initialize = [(p,sc,n,tr) for p,sc,n,tr in mTEPES.psn*mTEPES.tr if (p,tr) in mTEPES.ptr ])
672
674
  mTEPES.psngc = Set(initialize = [(p,sc,n,gc) for p,sc,n,gc in mTEPES.psn*mTEPES.gc if (p,gc) in mTEPES.pgc ])
673
675
  mTEPES.psngb = Set(initialize = [(p,sc,n,gb) for p,sc,n,gb in mTEPES.psn*mTEPES.gb if (p,gb) in mTEPES.pgc ])
674
676
  mTEPES.psnre = Set(initialize = [(p,sc,n,re) for p,sc,n,re in mTEPES.psn*mTEPES.re if (p,re) in mTEPES.pre ])
@@ -677,6 +679,7 @@ def DataConfiguration(mTEPES):
677
679
  mTEPES.psnchp = Set(initialize = [(p,sc,n,chp) for p,sc,n,chp in mTEPES.psn*mTEPES.chp if (p,chp) in mTEPES.pchp])
678
680
  mTEPES.psnbo = Set(initialize = [(p,sc,n,bo) for p,sc,n,bo in mTEPES.psn*mTEPES.bo if (p,bo) in mTEPES.pbo ])
679
681
  mTEPES.psnhp = Set(initialize = [(p,sc,n,hp) for p,sc,n,hp in mTEPES.psn*mTEPES.hp if (p,hp) in mTEPES.php ])
682
+ mTEPES.psneb = Set(initialize = [(p,sc,n,eb) for p,sc,n,eb in mTEPES.psn*mTEPES.eb if (p,eb) in mTEPES.peb ])
680
683
  mTEPES.psnes = Set(initialize = [(p,sc,n,es) for p,sc,n,es in mTEPES.psn*mTEPES.es if (p,es) in mTEPES.pes ])
681
684
  mTEPES.psneh = Set(initialize = [(p,sc,n,eh) for p,sc,n,eh in mTEPES.psn*mTEPES.eh if (p,eh) in mTEPES.peh ])
682
685
  mTEPES.psnec = Set(initialize = [(p,sc,n,ec) for p,sc,n,ec in mTEPES.psn*mTEPES.ec if (p,ec) in mTEPES.pec ])
@@ -722,7 +725,8 @@ def DataConfiguration(mTEPES):
722
725
  mTEPES.psnland = Set(initialize = [(p,sc,n,ni,nf,cc,nd) for p,sc,n,ni,nf,cc,nd in mTEPES.psnla*mTEPES.nd if (ni,nf,cc,nd) in mTEPES.dPar['pVariablePTDF'].columns])
723
726
 
724
727
  # assigning a node to an area
725
- mTEPES.ndar = Set(initialize = [(nd,ar) for (nd,zn,ar) in mTEPES.ndzn*mTEPES.ar if (zn,ar) in mTEPES.znar])
728
+ mTEPES.ndar = Set(initialize = [(nd,ar) for nd,zn,ar in mTEPES.ndzn*mTEPES.ar if (zn,ar) in mTEPES.znar])
729
+ mTEPES.arnd = Set(initialize = [(ar,nd) for nd, ar in mTEPES.ndar])
726
730
 
727
731
  # assigning a line to an area. Both nodes are in the same area. Cross-area lines are not included
728
732
  mTEPES.laar = Set(initialize = [(ni,nf,cc,ar) for ni,nf,cc,ar in mTEPES.la*mTEPES.ar if (ni,ar) in mTEPES.ndar and (nf,ar) in mTEPES.ndar])
@@ -819,9 +823,9 @@ def DataConfiguration(mTEPES):
819
823
 
820
824
  mTEPES.n2g = Set(initialize=mTEPES.dPar['pNodeToGen'].index, doc='node to generator')
821
825
 
822
- mTEPES.z2g = Set(doc='zone to generator', initialize=[(zn,g) for (nd,g,zn ) in mTEPES.n2g*mTEPES.zn if (nd,zn) in mTEPES.ndzn ])
823
- mTEPES.a2g = Set(doc='area to generator', initialize=[(ar,g) for (nd,g,zn,ar ) in mTEPES.n2g*mTEPES.znar if (nd,zn) in mTEPES.ndzn ])
824
- mTEPES.r2g = Set(doc='region to generator', initialize=[(rg,g) for (nd,g,zn,ar,rg) in mTEPES.n2g*mTEPES.znar*mTEPES.rg if (nd,zn) in mTEPES.ndzn and [ar,rg] in mTEPES.arrg])
826
+ mTEPES.z2g = Set(doc='zone to generator', initialize=[(zn,g) for nd,g,zn in mTEPES.n2g*mTEPES.zn if (nd,zn) in mTEPES.ndzn ])
827
+ mTEPES.a2g = Set(doc='area to generator', initialize=[(ar,g) for nd,g,zn,ar in mTEPES.n2g*mTEPES.znar if (nd,zn) in mTEPES.ndzn ])
828
+ mTEPES.r2g = Set(doc='region to generator', initialize=[(rg,g) for nd,g,zn,ar,rg in mTEPES.n2g*mTEPES.znar*mTEPES.rg if (nd,zn) in mTEPES.ndzn and [ar,rg] in mTEPES.arrg])
825
829
 
826
830
  # mTEPES.z2g = Set(initialize = [(zn,g) for zn,g in mTEPES.zn*mTEPES.g if (zn,g) in pZone2Gen])
827
831
 
@@ -2082,7 +2086,6 @@ def SettingUpVariables(OptModel, mTEPES):
2082
2086
  '''
2083
2087
  nFixedVariables = 0
2084
2088
 
2085
- # fix the must-run existing units and their output
2086
2089
  # must-run units must produce at least their minimum output
2087
2090
  [OptModel.vTotalOutput[p,sc,n,g].setlb(mTEPES.pMinPowerElec[p,sc,n,g]) for p,sc,n,g in mTEPES.psng if mTEPES.pMustRun[g] == 1 and g not in mTEPES.gc]
2088
2091
 
@@ -2119,8 +2122,12 @@ def SettingUpVariables(OptModel, mTEPES):
2119
2122
  OptModel.vReserveUp [p,sc,n,nr].fix(0.0)
2120
2123
  OptModel.vReserveDown [p,sc,n,nr].fix(0.0)
2121
2124
  nFixedVariables += 3
2125
+ # fix the must-run existing units and their output
2126
+ if mTEPES.pMustRun[nr] == 1 and mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0 and nr not in mTEPES.gc:
2127
+ OptModel.vTotalOutput[p,sc,n,nr].fix(mTEPES.pMinPowerElec[p,sc,n,nr])
2128
+ nFixedVariables += 1
2122
2129
  if mTEPES.pIndRampReserves == 1:
2123
- if mTEPES.pMaxPower2ndBlock[p, sc, n, nr] == 0.0 or mTEPES.pRampUp[nr] == 0.0:
2130
+ if mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0 or mTEPES.pRampUp[nr] == 0.0:
2124
2131
  OptModel.vRampReserveUp[p,sc,n,nr].fix(0.0)
2125
2132
  OptModel.vRampReserveDw[p,sc,n,nr].fix(0.0)
2126
2133
  nFixedVariables += 2
@@ -2197,7 +2204,7 @@ def SettingUpVariables(OptModel, mTEPES):
2197
2204
  # activate only period, scenario, and load levels to formulate
2198
2205
  mTEPES.del_component(mTEPES.st)
2199
2206
  mTEPES.del_component(mTEPES.n )
2200
- mTEPES.st = Set(doc='stages', initialize=[stt for stt in mTEPES.stt if st == stt and mTEPES.pStageWeight[stt] and sum(1 for (p,sc,st,nn) in mTEPES.s2n)])
2207
+ mTEPES.st = Set(doc='stages', initialize=[stt for stt in mTEPES.stt if st == stt and mTEPES.pStageWeight[stt] and sum(1 for p,sc,st,nn in mTEPES.s2n)])
2201
2208
  mTEPES.n = Set(doc='load levels', initialize=[nn for nn in mTEPES.nn if (p,sc,st,nn) in mTEPES.s2n ])
2202
2209
 
2203
2210
  if mTEPES.n:
@@ -660,7 +660,7 @@
660
660
  # For more information on this, and how to apply and follow the GNU AGPL, see
661
661
  # <https://www.gnu.org/licenses/>.
662
662
 
663
- # Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 22, 2026
663
+ # Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 25, 2026
664
664
  # simplicity and transparency in power systems planning
665
665
 
666
666
  # Developed by
@@ -693,7 +693,7 @@ GREEN = "\033[32m"
693
693
  BLUE = "\033[34m"
694
694
  RESET = "\033[0m"
695
695
 
696
- print(GREEN + 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.9 - January 22, 2026' + RESET)
696
+ print(GREEN + 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.11 - January 28, 2026' + RESET)
697
697
  print(BLUE + '#### Academic research license - for non-commercial use only ####' + RESET + '\n')
698
698
 
699
699
  parser = argparse.ArgumentParser(description='Introducing main parameters...')