Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
erikfilias committed Dec 10, 2024
2 parents 027efb6 + 9f960af commit 15fd32d
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 236 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
Change Log
=============

[4.18.1] - 2024-12-09
[4.18.1] - 2024-12-10
-----------------------
- [CHANGED] add margin reserve for heating (new CSV file, and modifications related to it in input data, model formulation and output results)
- [FIXED] some epsilon for heat were wrong
- [CHANGED] add reserve margin for heat demand (new CSV file, and modifications related to it in input data, model formulation and output results)
- [CHANGED] computing pDemandHeatPeak in InputData
- [CHANGED] refactoring the energy to heat conversion

Expand Down
8 changes: 4 additions & 4 deletions doc/rst/InputData.rst
Original file line number Diff line number Diff line change
Expand Up @@ -838,11 +838,11 @@ Heat adequacy reserve margin
The adequacy reserve margin for heating is the ratio between the available capacity and the maximum demand. It is modeled as the adequacy reserve margin for electricity, but considering the heat demand and the heat capacity of the units.
A description of the data included in the file ``oT_Data_ReserveMarginHeat.csv`` follows:

============== ============== ============= ========================================================== ====
============== ============== ============= =============================================================== ====
Identifiers Header Description
============================== ============= ========================================================== ====
Period Area ReserveMargin Minimum adequacy reserve margin for each period and area p.u.
============== ============== ============= ========================================================== ====
============== ============== ============= =============================================================== ====
Period Area ReserveMargin Minimum heat adequacy reserve margin for each period and area p.u.
============== ============== ============= =============================================================== ====

This parameter is only used for system heating generation expansion, not for the system operation. If no value is introduced for an area, the reserve margin is considered 0.

Expand Down
25 changes: 17 additions & 8 deletions doc/rst/MathematicalFormulation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,17 @@ They are written in **uppercase** letters.
:math:`UR^p_{\omega na}, DR^p_{\omega na}` Upward and downward operating reserves for each area GW
========================================== ================================================================== ====

================================== ================================================================ ====
**Adequacy system reserve margin**
----------------------------------------------------------------------------------------------------------
:math:`RM_{pa}` Minimum adequacy system reserve margin for each period and area p.u.
================================== ================================================================ ====
================================== ============================================================================ ====
**Adequacy electricity system reserve margin**
----------------------------------------------------------------------------------------------------------------------
:math:`RME_{pa}` Minimum adequacy electricity system reserve margin for each period and area p.u.
================================== ============================================================================ ====

================================== ============================================================================ ====
**Adequacy heat system reserve margin**
----------------------------------------------------------------------------------------------------------------------
:math:`RMH_{pa}` Minimum adequacy heat system reserve margin for each period and area p.u.
================================== ============================================================================ ====

================================== ================================================================ =====
**Maximum CO2 emission**
Expand Down Expand Up @@ -356,9 +362,13 @@ Heat production with fuel heater bounded by investment decision for candidate fu

:math:`\frac{gh^p_{\omega ng}}{\overline{GH}^p_{\omega ng}} \leq icg^p_g \quad \forall p \omega ng, g \in CB`

Adequacy system reserve margin [p.u.] «``eAdequacyReserveMargin``»
Adequacy electricity system reserve margin [p.u.] «``eAdequacyReserveMarginElec``»

:math:`\sum_{g \in a, EG} \overline{GP}_g A_g + \sum_{g \in a, CG} icg^p_g \overline{GP}_g A_g \geq PD_{pa} RM_{pa} \quad \forall pa`
:math:`\sum_{g \in a, EG} \overline{GP}_g A_g + \sum_{g \in a, CG} icg^p_g \overline{GP}_g A_g \geq PD_{pa} RME_{pa} \quad \forall pa`

Adequacy heat system reserve margin [p.u.] «``eAdequacyReserveMarginHeat``»

:math:`\sum_{g \in a, EB} \overline{GH}_g A_g + \sum_{g \in a, CB} icg^p_g \overline{GH}_g A_g \geq PD_{pa} RMH_{pa} \quad \forall pa`

Maximum CO2 emission [MtC02] «``eMaxSystemEmission``»

Expand Down Expand Up @@ -631,7 +641,6 @@ and disjunctive constraints in AC candidate parallel circuits are inversely prop

Given that there are disjunctive constraints, which are only correct with binary AC investment variables, this cycle-based formulation must be used only with binary AC investment decisions.


**Hydrogen network operation**

Balance of hydrogen generation by electrolyzers, hydrogen consumption from hydrogen heater using it, and demand at each node [tH2] «``eBalanceH2``»
Expand Down
16 changes: 8 additions & 8 deletions doc/rst/OutputResults.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1035,19 +1035,19 @@ The marginal costs (dual variables) are obtained after fixing the binary investm

File ``oT_Result_MarginalReserveMargin.csv``

============ ========== ========== ================================================================================
============ ========== ========== ===========================================================================
Identifier Header Description
======================== ========== ================================================================================
Period Scenario Area Marginal of the minimum adequacy reserve margin in the electricity system [€/MW]
============ ========== ========== ================================================================================
============ ========== ========== ===========================================================================
Period Scenario Area Marginal of the minimum adequacy electricity system reserve margin [€/MW]
============ ========== ========== ===========================================================================

File ``oT_Result_MarginalReserveMarginHeat.csv``

============ ========== ========== ============================================================================
============ ========== ========== ===========================================================================
Identifier Header Description
======================== ========== ============================================================================
Period Scenario Area Marginal of the minimum adequacy reserve margin in the heating system [€/MW]
============ ========== ========== ============================================================================
============ ========== ========== ===========================================================================
Period Scenario Area Marginal of the minimum adequacy heat system reserve margin [€/MW]
============ ========== ========== ===========================================================================

File ``oT_Result_MarginalEmission.csv``

Expand Down
6 changes: 3 additions & 3 deletions doc/rst/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
author = 'Andres Ramos'

# The short X.Y version
version = 'version 4.18.0'
version = 'version 4.18.1'
# The full version, including alpha/beta/rc tags
release = ''

Expand Down Expand Up @@ -84,13 +84,13 @@
#
# html_sidebars = {}
html_theme = 'alabaster'
html_title = 'version 4.18.0'
html_title = 'version 4.18.1'
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.18.0',
'description': 'version 4.18.1',
'body_max_width' : 'none',
'page_width': 'auto',
'font_family': 'Georgia'
Expand Down
2 changes: 1 addition & 1 deletion openTEPES/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
>>> import openTEPES as oT
>>> oT.routine("9n", "C:\\Users\\UserName\\Documents\\GitHub\\openTEPES", "glpk")
"""
__version__ = "4.18.0"
__version__ = "4.18.1"

from .openTEPES_Main import main
from .openTEPES import *
Expand Down
6 changes: 3 additions & 3 deletions openTEPES/openTEPES.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - December 04, 2024
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - December 09, 2024
"""

# import dill as pickle
Expand Down Expand Up @@ -39,8 +39,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.18.0 - December 04, 2024')
print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.0 - December 04, 2024', file=open(f'{_path}/openTEPES_version_{CaseName}.log','w'))
mTEPES = ConcreteModel('Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.1 - December 09, 2024')
print( 'Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.1 - December 09, 2024', file=open(f'{_path}/openTEPES_version_{CaseName}.log','w'))

pIndOutputResults = [j for i,j in idxDict.items() if i == pIndOutputResults][0]
pIndLogConsole = [j for i,j in idxDict.items() if i == pIndLogConsole ][0]
Expand Down
30 changes: 15 additions & 15 deletions openTEPES/openTEPES_InputData.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):

# show some statistics of the data
if pIndLogConsole == 1:
print('Reserve margin \n', dfReserveMargin.describe (), '\n')
print('Reserve margin electricity \n', dfReserveMargin.describe (), '\n')
print('Maximum CO2 emission \n', dfEmission.describe (), '\n')
print('Minimum RES energy \n', dfRESEnergy.describe (), '\n')
print('Electricity demand \n', dfDemand.describe (), '\n')
Expand Down Expand Up @@ -1056,7 +1056,7 @@ def Create_ESS_RES_Sets(mTEPES) -> None:
for p,ar in mTEPES.par:
# values < 1e-5 times the maximum demand for each area (an area is related to operating reserves procurement, i.e., country) are converted to 0
pDemandElecPeak[p,ar] = pDemandElec.loc[p,:,:][[nd for nd in d2a[ar]]].sum(axis=1).max()
pEpsilonElec = pDemandElecPeak[p,ar]*1e-5
pEpsilonElec = pDemandElecPeak[p,ar]*1e-5

# these parameters are in GW
pDemandElecPos [pDemandElecPos [[nd for nd in d2a[ar]]] < pEpsilonElec] = 0.0
Expand All @@ -1077,7 +1077,7 @@ def Create_ESS_RES_Sets(mTEPES) -> None:
pMaxStorage [pMaxStorage [[es for es in e2a[ar]]] < pEpsilonElec] = 0.0
pIniInventory [pIniInventory [[es for es in e2a[ar]]] < pEpsilonElec] = 0.0

# pInitialInventory.update(pd.Series([0.0 for es in e2a[ar] if pInitialInventory[es] < pEpsilon], index=[es for es in e2a[ar] if pInitialInventory[es] < pEpsilon], dtype='float64'))
# pInitialInventory.update(pd.Series([0.0 for es in e2a[ar] if pInitialInventory[es] < pEpsilonElec], index=[es for es in e2a[ar] if pInitialInventory[es] < pEpsilonElec], dtype='float64'))

# merging positive and negative values of the demand
pDemandElec = pDemandElecPos.where(pDemandElecNeg >= 0.0, pDemandElecNeg)
Expand Down Expand Up @@ -1113,8 +1113,8 @@ def Create_ESS_RES_Sets(mTEPES) -> None:
pDemandHeatPeak[p,ar] = pDemandHeat.loc[p,:,:][[nd for nd in d2a[ar]]].sum(axis=1).max()
pEpsilonHeat = pDemandHeatPeak[p,ar]*1e-5
pDemandHeat [pDemandHeat [[nd for nd in d2a[ar]]] < pEpsilonHeat] = 0.0
pHeatPipeNTCFrw.update(pd.Series([0.0 for ni,nf,cc in mTEPES.ha if pHeatPipeNTCFrw[ni,nf,cc] < pEpsilonElec], index=[(ni,nf,cc) for ni,nf,cc in mTEPES.ha if pHeatPipeNTCFrw[ni,nf,cc] < pEpsilonElec], dtype='float64'))
pHeatPipeNTCBck.update(pd.Series([0.0 for ni,nf,cc in mTEPES.ha if pHeatPipeNTCBck[ni,nf,cc] < pEpsilonElec], index=[(ni,nf,cc) for ni,nf,cc in mTEPES.ha if pHeatPipeNTCBck[ni,nf,cc] < pEpsilonElec], dtype='float64'))
pHeatPipeNTCFrw.update(pd.Series([0.0 for ni,nf,cc in mTEPES.ha if pHeatPipeNTCFrw[ni,nf,cc] < pEpsilonHeat], index=[(ni,nf,cc) for ni,nf,cc in mTEPES.ha if pHeatPipeNTCFrw[ni,nf,cc] < pEpsilonHeat], dtype='float64'))
pHeatPipeNTCBck.update(pd.Series([0.0 for ni,nf,cc in mTEPES.ha if pHeatPipeNTCBck[ni,nf,cc] < pEpsilonHeat], index=[(ni,nf,cc) for ni,nf,cc in mTEPES.ha if pHeatPipeNTCBck[ni,nf,cc] < pEpsilonHeat], dtype='float64'))

# drop generators not g or es or eh or ch
pMinPowerElec = pMinPowerElec.loc [:,mTEPES.g ]
Expand Down Expand Up @@ -1143,11 +1143,11 @@ def Create_ESS_RES_Sets(mTEPES) -> None:
# computation of the power to heat ratio of the CHP units
# heat ratio of boiler units is fixed to 1.0
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)
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)
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)
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))
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))
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))
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)
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)
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))
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))
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))

# drop values not par, p, or ps
pReserveMargin = pReserveMargin.loc [mTEPES.par]
Expand Down Expand Up @@ -1456,15 +1456,15 @@ def filter_rows(df, set):
pDemandHeat = filter_rows(pDemandHeat , mTEPES.psnnd)
pDemandHeatAbs = filter_rows(pDemandHeatAbs , mTEPES.psnnd)

mTEPES.pDemandHeatPeak = Param(mTEPES.par, initialize=pDemandHeatPeak.to_dict(), within=NonNegativeReals, doc='Peak heat demand' )
mTEPES.pDemandHeat = Param(mTEPES.psnnd, initialize=pDemandHeat.to_dict() , within=NonNegativeReals, doc='Heat demand per hour' )
mTEPES.pDemandHeatAbs = Param(mTEPES.psnnd, initialize=pDemandHeatAbs.to_dict(), within=NonNegativeReals, doc='Heat demand' )
mTEPES.pDemandHeatPeak = Param(mTEPES.par, initialize=pDemandHeatPeak.to_dict(), within=NonNegativeReals, doc='Peak heat demand' )
mTEPES.pDemandHeat = Param(mTEPES.psnnd, initialize=pDemandHeat.to_dict() , within=NonNegativeReals, doc='Heat demand per hour' )
mTEPES.pDemandHeatAbs = Param(mTEPES.psnnd, initialize=pDemandHeatAbs.to_dict(), within=NonNegativeReals, doc='Heat demand' )

mTEPES.pLoadLevelDuration = Param(mTEPES.psn, initialize=0 , within=NonNegativeIntegers, doc='Load level duration', mutable=True)
mTEPES.pLoadLevelDuration = Param(mTEPES.psn, initialize=0 , within=NonNegativeIntegers, doc='Load level duration', mutable=True)
for p,sc,n in mTEPES.psn:
mTEPES.pLoadLevelDuration[p,sc,n] = mTEPES.pLoadLevelWeight[p,sc,n]() * mTEPES.pDuration[p,sc,n]()

mTEPES.pPeriodProb = Param(mTEPES.ps, initialize=0.0 , within=NonNegativeReals, doc='Period probability', mutable=True)
mTEPES.pPeriodProb = Param(mTEPES.ps, initialize=0.0 , within=NonNegativeReals, doc='Period probability', mutable=True)
for p,sc in mTEPES.ps:
# periods and scenarios are going to be solved together with their weight and probability
mTEPES.pPeriodProb[p,sc] = mTEPES.pPeriodWeight[p] * mTEPES.pScenProb[p,sc]
Expand Down
4 changes: 2 additions & 2 deletions openTEPES/openTEPES_Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@
# For more information on this, and how to apply and follow the GNU AGPL, see
# <https://www.gnu.org/licenses/>.

# Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - December 04, 2024
# Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - December 09, 2024
# simplicity and transparency in power systems planning

# Developed by
Expand All @@ -685,7 +685,7 @@
# import pkg_resources
from .openTEPES import openTEPES_run

print('\033[1;32mOpen Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.0 - December 04, 2024\033[0m')
print('\033[1;32mOpen Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - Version 4.18.1 - December 09, 2024\033[0m')
print('\033[34m#### Academic research license - for non-commercial use only ####\033[0m \n')

parser = argparse.ArgumentParser(description='Introducing main parameters...')
Expand Down
Loading

0 comments on commit 15fd32d

Please sign in to comment.