diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b5977a0c..fee1ecbb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,10 @@ Change Log ============= +[4.14.5] - 2023-10-20 +---------------------- +- [FIXED] if there are system emission constraints no stage run can be done + [4.14.4] - 2023-10-15 ---------------------- - [FIXED] check that the duration of all the stages is equal diff --git a/doc/rst/conf.py b/doc/rst/conf.py index 0cefdb97..d835b2aa 100644 --- a/doc/rst/conf.py +++ b/doc/rst/conf.py @@ -12,7 +12,7 @@ author = 'Andres Ramos' # The short X.Y version -version = 'version 4.14.4' +version = 'version 4.14.5' # The full version, including alpha/beta/rc tags release = '' @@ -84,13 +84,13 @@ # # html_sidebars = {} html_theme = 'alabaster' -html_title = 'version 4.14.4' +html_title = 'version 4.14.5' html_logo = '../img/openTEPES.png' html_last_updated_fmt = '' html_show_sphinx = False html_theme_options = { 'analytics_id': 'UA-515200-2', # Provided by Google in your dashboard - 'description': 'version 4.14.4', + 'description': 'version 4.14.5', 'page_width': 'auto', 'font_family': 'Georgia' } diff --git a/openTEPES/__init__.py b/openTEPES/__init__.py index dcd4d543..350f25c6 100644 --- a/openTEPES/__init__.py +++ b/openTEPES/__init__.py @@ -14,7 +14,7 @@ >>> import openTEPES as oT >>> oT.routine("9n", "C:\\Users\\UserName\\Documents\\GitHub\\openTEPES", "glpk") """ -__version__ = "4.14.4" +__version__ = "4.14.5" from .openTEPES_Main import main from .openTEPES import * diff --git a/openTEPES/openTEPES.py b/openTEPES/openTEPES.py index 806e8d11..5fdd8453 100644 --- a/openTEPES/openTEPES.py +++ b/openTEPES/openTEPES.py @@ -1,10 +1,11 @@ """ -Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 15, 2023 +Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 20, 2023 """ -import time +import math import os import setuptools +import time import pyomo.environ as pyo from pyomo.environ import ConcreteModel, Set, Param, Reals @@ -37,8 +38,8 @@ def openTEPES_run(DirName, CaseName, SolverName, pIndOutputResults, pIndLogConso idxDict['y' ] = 1 #%% model declaration - mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.14.4 - October 15, 2023') - print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.14.4 - October 15, 2023', file=open(_path+'/openTEPES_version_'+CaseName+'.log','a')) + mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.14.5 - October 20, 2023') + print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.14.5 - October 20, 2023', file=open(_path+'/openTEPES_version_'+CaseName+'.log','a')) pIndOutputResults = [j for i,j in idxDict.items() if i == pIndOutputResults][0] pIndLogConsole = [j for i,j in idxDict.items() if i == pIndLogConsole ][0] @@ -82,7 +83,7 @@ def openTEPES_run(DirName, CaseName, SolverName, pIndOutputResults, pIndLogConso NetworkSwitchingModelFormulation (mTEPES, mTEPES, pIndLogConsole, p, sc, st) NetworkOperationModelFormulation (mTEPES, mTEPES, pIndLogConsole, p, sc, st) - if (len(mTEPES.gc) == 0 or (len(mTEPES.gc) > 0 and mTEPES.pIndBinGenInvest() == 2)) and (len(mTEPES.gd) == 0 or (len(mTEPES.gd) > 0 and mTEPES.pIndBinGenRetire() == 2)) and (len(mTEPES.lc) == 0 or (len(mTEPES.lc) > 0 and mTEPES.pIndBinNetInvest() == 2)): + if (len(mTEPES.gc) == 0 or (len(mTEPES.gc) > 0 and mTEPES.pIndBinGenInvest() == 2)) and (len(mTEPES.gd) == 0 or (len(mTEPES.gd) > 0 and mTEPES.pIndBinGenRetire() == 2)) and (len(mTEPES.lc) == 0 or (len(mTEPES.lc) > 0 and mTEPES.pIndBinNetInvest() == 2)) and (min([mTEPES.pEmission[p,ar] for ar in mTEPES.ar]) == math.inf or sum(mTEPES.pEmissionRate[nr] for nr in mTEPES.nr) == 0): mTEPES.pPeriodProb[p,sc] = mTEPES.pPeriodWeight[p] = mTEPES.pScenProb[p,sc] = 1.0 if pIndLogConsole == 1: @@ -100,7 +101,7 @@ def openTEPES_run(DirName, CaseName, SolverName, pIndOutputResults, pIndLogConso if c.name.find(str(p)) != -1 and c.name.find(str(sc)) != -1: c.deactivate() else: - if mTEPES.p.ord(p)*mTEPES.sc.ord(sc) == len(mTEPES.sc): + if mTEPES.p.ord(p)*mTEPES.sc.ord(sc) == len(mTEPES.ps) and mTEPES.st.last() == mTEPES.stt.last(): if pIndLogConsole == 1: StartTime = time.time() diff --git a/openTEPES/openTEPES_ModelFormulation.py b/openTEPES/openTEPES_ModelFormulation.py index 80ca32bf..1b323c8e 100644 --- a/openTEPES/openTEPES_ModelFormulation.py +++ b/openTEPES/openTEPES_ModelFormulation.py @@ -1,5 +1,5 @@ """ -Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 09, 2023 +Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - October 18, 2023 """ import time @@ -140,14 +140,14 @@ def eTotalECost(OptModel,n): def eTotalECostArea(OptModel,n,ar): if (st,n) in mTEPES.s2n and sum(mTEPES.pEmissionCost[nr] for nr in mTEPES.nr if (ar,nr) in mTEPES.a2g): - return OptModel.vTotalECostArea[p,sc,n,ar] == sum(mTEPES.pLoadLevelDuration[n] * mTEPES.pEmissionCost[nr] * OptModel.vTotalOutput [p,sc,n,nr] for nr in mTEPES.nr if (ar,nr) in mTEPES.a2g) + return OptModel.vTotalECostArea[p,sc,n,ar] == sum(mTEPES.pLoadLevelDuration[n] * mTEPES.pEmissionCost[nr] * OptModel.vTotalOutput[p,sc,n,nr] for nr in mTEPES.nr if (ar,nr) in mTEPES.a2g) else: return Constraint.Skip setattr(OptModel, 'eTotalECostArea_'+str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.n, mTEPES.ar, rule=eTotalECostArea, doc='area emission cost [MEUR]')) def eTotalRCost(OptModel,n): if (st,n) in mTEPES.s2n: - return OptModel.vTotalRCost[p,sc,n] == sum(mTEPES.pLoadLevelDuration[n] * mTEPES.pENSCost * OptModel.vENS [p,sc,n,nd] for nd in mTEPES.nd) + sum(mTEPES.pHNSCost * OptModel.vHNS[p,sc,n,nd] for nd in mTEPES.nd if sum(1 for el in e2n[nd]) + sum(1 for lout in lout[nd]) + sum(1 for ni,cc in lin[nd])) + return OptModel.vTotalRCost[p,sc,n] == sum(mTEPES.pLoadLevelDuration[n] * mTEPES.pENSCost * OptModel.vENS[p,sc,n,nd] for nd in mTEPES.nd) + sum(mTEPES.pHNSCost * OptModel.vHNS[p,sc,n,nd] for nd in mTEPES.nd if sum(1 for el in e2n[nd]) + sum(1 for lout in lout[nd]) + sum(1 for ni,cc in lin[nd])) else: return Constraint.Skip setattr(OptModel, 'eTotalRCost_'+str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.n, rule=eTotalRCost, doc='system reliability cost [MEUR]')) @@ -235,8 +235,8 @@ def eAdequacyReserveMargin(OptModel,p,ar): print('eAdequacyReserveMargin... ', len(getattr(OptModel, 'eAdequacyReserveMargin_'+str(p)+'_'+str(sc)+'_'+str(st))), ' rows') def eMaxSystemEmission(OptModel,p,ar): - if mTEPES.pEmission[p,ar] < math.inf and sum(mTEPES.pEmissionRate[nr] for nr in mTEPES.nr if (ar,nr) in mTEPES.a2g): - return sum(OptModel.vTotalECostArea[p,sc,n,ar]/mTEPES.pCO2Cost for n in mTEPES.n) <= mTEPES.pEmission[p,ar] + if mTEPES.pEmission[p,ar] < math.inf and sum(mTEPES.pEmissionCost[nr] for nr in mTEPES.nr if (ar,nr) in mTEPES.a2g): + return sum(OptModel.vTotalECostArea[p,sc,n,ar]/mTEPES.pCO2Cost for n in mTEPES.nn if (st,n) in mTEPES.s2n) <= mTEPES.pEmission[p,ar] else: return Constraint.Skip setattr(OptModel, 'eMaxSystemEmission_'+str(p)+'_'+str(sc)+'_'+str(st), Constraint(mTEPES.p, mTEPES.ar, rule=eMaxSystemEmission, doc='maximum CO2 emission [tCO2]')) diff --git a/openTEPES/openTEPES_OutputResults.py b/openTEPES/openTEPES_OutputResults.py index 9c136c00..d6371525 100644 --- a/openTEPES/openTEPES_OutputResults.py +++ b/openTEPES/openTEPES_OutputResults.py @@ -1353,20 +1353,20 @@ def CostSummaryResults(DirName, CaseName, OptModel, mTEPES): _path = os.path.join(DirName, CaseName) StartTime = time.time() - SysCost = pd.Series(data=[ OptModel.vTotalSCost() ], index=[' '] ).to_frame(name='Total System Cost').stack() - GenInvCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pGenInvestCost[gc ] * OptModel.vGenerationInvest[p,gc ]() for gc in mTEPES.gc ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Investment Cost').stack() - GenRetCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pGenRetireCost[gd ] * OptModel.vGenerationRetire[p,gd ]() for gd in mTEPES.gd ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Retirement Cost').stack() + SysCost = pd.Series(data=[ OptModel.vTotalSCost() ], index=[' '] ).to_frame(name='Total System Cost').stack() + GenInvCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pGenInvestCost[gc ] * OptModel.vGenerationInvest[p,gc ]() for gc in mTEPES.gc ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Investment Cost').stack() + GenRetCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pGenRetireCost[gd ] * OptModel.vGenerationRetire[p,gd ]() for gd in mTEPES.gd ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Retirement Cost').stack() if mTEPES.pIndHydroTopology == 1: - RsrInvCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pRsrInvestCost[rc ] * OptModel.vReservoirInvest [p,rc ]() for rc in mTEPES.rn ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reservoir Investment Cost').stack() + RsrInvCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pRsrInvestCost[rc ] * OptModel.vReservoirInvest [p,rc ]() for rc in mTEPES.rn ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reservoir Investment Cost').stack() else: - RsrInvCost = pd.Series(data=[0.0 ], index=mTEPES.p).to_frame(name='Reservoir Investment Cost').stack() - NetInvCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pNetFixedCost [lc ] * OptModel.vNetworkInvest [p,lc ]() for lc in mTEPES.lc ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Network Investment Cost').stack() - GenCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalGCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Operation Cost').stack() - ConCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalCCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Consumption Operation Cost').stack() - EmiCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalECost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Emission Cost').stack() - RelCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalRCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reliability Cost').stack() - # DemPayment = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * mTEPES.pLoadLevelDuration[n]() * mTEPES.pDemand[p,sc,n,nd] * OptModel.LSRMC [p,sc,n,nd] for sc,n,nd in mTEPES.sc*mTEPES.n*mTEPES.nd if mTEPES.pScenProb[p,sc]())/1e3 for p in mTEPES.p], index=mTEPES.p).to_frame(name='Demand Payment' ).stack() - 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) + RsrInvCost = pd.Series(data=[0.0 ], index=mTEPES.p).to_frame(name='Reservoir Investment Cost').stack() + NetInvCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pNetFixedCost [lc ] * OptModel.vNetworkInvest [p,lc ]() for lc in mTEPES.lc ) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Network Investment Cost').stack() + GenCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalGCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Generation Operation Cost').stack() + ConCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalCCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Consumption Operation Cost').stack() + EmiCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalECost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Emission Cost').stack() + RelCost = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * OptModel.vTotalRCost [p,sc,n]() for sc,n in mTEPES.sc*mTEPES.n if mTEPES.pScenProb[p,sc]()) for p in mTEPES.p], index=mTEPES.p).to_frame(name='Reliability Cost').stack() + # DemPayment = pd.Series(data=[mTEPES.pDiscountFactor[p] * sum(mTEPES.pScenProb [p,sc]() * mTEPES.pLoadLevelDuration[n]() * mTEPES.pDemand[p,sc,n,nd] * OptModel.LSRMC [p,sc,n,nd] for sc,n,nd in mTEPES.sc*mTEPES.n*mTEPES.nd if mTEPES.pScenProb[p,sc]())/1e3 for p in mTEPES.p], index=mTEPES.p).to_frame(name='Demand Payment' ).stack() + 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) WritingResultsTime = time.time() - StartTime print('Writing cost summary results ... ', round(WritingResultsTime), 's')