openTEPES 4.18.3__py3-none-any.whl → 4.18.5__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/9n_PTDF/oT_Data_Demand_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_Duration_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_Emission_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_EnergyInflows_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_EnergyOutflows_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_Generation_9n_PTDF.csv +17 -0
- openTEPES/9n_PTDF/oT_Data_Inertia_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_Network_9n_PTDF.csv +14 -0
- openTEPES/9n_PTDF/oT_Data_NodeLocation_9n_PTDF.csv +10 -0
- openTEPES/9n_PTDF/oT_Data_OperatingReserveDown_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_OperatingReserveUp_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_Option_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_Parameter_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_Period_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_RESEnergy_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_ReserveMargin_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_Scenario_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_Stage_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Data_VariableEmissionCost_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableFuelCost_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMaxConsumption_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMaxEnergy_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMaxGeneration_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMaxStorage_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMinConsumption_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMinEnergy_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMinGeneration_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariableMinStorage_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Data_VariablePTDF_9n_PTDF.csv +8740 -0
- openTEPES/9n_PTDF/oT_Data_VariableTTCBck_9n_PTDF.csv +8739 -0
- openTEPES/9n_PTDF/oT_Data_VariableTTCFrw_9n_PTDF.csv +8739 -0
- openTEPES/9n_PTDF/oT_Dict_AreaToRegion_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Dict_Area_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Dict_Circuit_9n_PTDF.csv +3 -0
- openTEPES/9n_PTDF/oT_Dict_Generation_9n_PTDF.csv +17 -0
- openTEPES/9n_PTDF/oT_Dict_Line_9n_PTDF.csv +3 -0
- openTEPES/9n_PTDF/oT_Dict_LoadLevel_9n_PTDF.csv +8737 -0
- openTEPES/9n_PTDF/oT_Dict_NodeToZone_9n_PTDF.csv +10 -0
- openTEPES/9n_PTDF/oT_Dict_Node_9n_PTDF.csv +10 -0
- openTEPES/9n_PTDF/oT_Dict_Period_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Dict_Region_9n_PTDF.csv +31 -0
- openTEPES/9n_PTDF/oT_Dict_Scenario_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Dict_Stage_9n_PTDF.csv +2 -0
- openTEPES/9n_PTDF/oT_Dict_Storage_9n_PTDF.csv +3 -0
- openTEPES/9n_PTDF/oT_Dict_Technology_9n_PTDF.csv +7 -0
- openTEPES/9n_PTDF/oT_Dict_ZoneToArea_9n_PTDF.csv +10 -0
- openTEPES/9n_PTDF/oT_Dict_Zone_9n_PTDF.csv +10 -0
- openTEPES/RTS-GMLC_6y/oT_Dict_AreaToRegion_RTS-GMLC_6y.csv +4 -4
- openTEPES/RTS-GMLC_6y/oT_Dict_Area_RTS-GMLC_6y.csv +4 -4
- openTEPES/RTS-GMLC_6y/oT_Dict_Circuit_RTS-GMLC_6y.csv +5 -5
- openTEPES/RTS-GMLC_6y/oT_Dict_Line_RTS-GMLC_6y.csv +3 -3
- openTEPES/RTS-GMLC_6y/oT_Dict_NodeToZone_RTS-GMLC_6y.csv +74 -74
- openTEPES/RTS-GMLC_6y/oT_Dict_Region_RTS-GMLC_6y.csv +2 -2
- openTEPES/RTS-GMLC_6y/oT_Dict_Scenario_RTS-GMLC_6y.csv +2 -2
- openTEPES/RTS-GMLC_6y/oT_Dict_Storage_RTS-GMLC_6y.csv +3 -3
- openTEPES/RTS-GMLC_6y/oT_Dict_Technology_RTS-GMLC_6y.csv +10 -10
- openTEPES/RTS-GMLC_6y/oT_Dict_ZoneToArea_RTS-GMLC_6y.csv +22 -22
- openTEPES/RTS-GMLC_6y/oT_Dict_Zone_RTS-GMLC_6y.csv +22 -22
- openTEPES/__init__.py +1 -1
- openTEPES/openTEPES.py +137 -65
- openTEPES/openTEPES_InputData.py +419 -234
- openTEPES/openTEPES_Main.py +2 -2
- openTEPES/openTEPES_ModelFormulation.py +469 -180
- openTEPES/openTEPES_OutputResults.py +305 -223
- openTEPES/openTEPES_ProblemSolving.py +68 -56
- {opentepes-4.18.3.dist-info → openTEPES-4.18.5.dist-info}/METADATA +17 -18
- {opentepes-4.18.3.dist-info → openTEPES-4.18.5.dist-info}/RECORD +70 -23
- {opentepes-4.18.3.dist-info → openTEPES-4.18.5.dist-info}/WHEEL +1 -1
- {opentepes-4.18.3.dist-info → openTEPES-4.18.5.dist-info}/LICENSE +0 -0
- {opentepes-4.18.3.dist-info → openTEPES-4.18.5.dist-info}/entry_points.txt +0 -0
openTEPES/openTEPES_InputData.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) -
|
|
2
|
+
Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - June 20, 2025
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import datetime
|
|
@@ -10,7 +10,6 @@ import pandas as pd
|
|
|
10
10
|
from collections import defaultdict
|
|
11
11
|
from pyomo.environ import DataPortal, Set, Param, Var, Binary, NonNegativeReals, NonNegativeIntegers, PositiveReals, PositiveIntegers, Reals, UnitInterval, Any
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
15
14
|
print('Input data ****')
|
|
16
15
|
|
|
@@ -47,6 +46,20 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
47
46
|
dfNodeLocation = pd.read_csv(f'{_path}/oT_Data_NodeLocation_' f'{CaseName}.csv', header=0, index_col=[0 ])
|
|
48
47
|
dfNetwork = pd.read_csv(f'{_path}/oT_Data_Network_' f'{CaseName}.csv', header=0, index_col=[0,1,2])
|
|
49
48
|
|
|
49
|
+
try:
|
|
50
|
+
dfVariableTTCFrw = pd.read_csv(f'{_path}/oT_Data_VariableTTCFrw_' f'{CaseName}.csv', header=[0,1,2 ], index_col=[0,1,2])
|
|
51
|
+
dfVariableTTCBck = pd.read_csv(f'{_path}/oT_Data_VariableTTCBck_' f'{CaseName}.csv', header=[0,1,2 ], index_col=[0,1,2])
|
|
52
|
+
pIndVarTTC = 1
|
|
53
|
+
except:
|
|
54
|
+
pIndVarTTC = 0
|
|
55
|
+
print('**** No variable transmission line TTCs')
|
|
56
|
+
try:
|
|
57
|
+
dfVariablePTDF = pd.read_csv(f'{_path}/oT_Data_VariablePTDF_' f'{CaseName}.csv', header=[0,1,2,3], index_col=[0,1,2])
|
|
58
|
+
pIndPTDF = 1
|
|
59
|
+
except:
|
|
60
|
+
pIndPTDF = 0
|
|
61
|
+
print('**** No flow-based market coupling method')
|
|
62
|
+
|
|
50
63
|
try:
|
|
51
64
|
dfReservoir = pd.read_csv(f'{_path}/oT_Data_Reservoir_' f'{CaseName}.csv', header=0, index_col=[0 ])
|
|
52
65
|
dfVariableMinVolume = pd.read_csv(f'{_path}/oT_Data_VariableMinVolume_' f'{CaseName}.csv', header=0, index_col=[0,1,2])
|
|
@@ -76,34 +89,41 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
76
89
|
print('**** No heat energy carrier')
|
|
77
90
|
|
|
78
91
|
# substitute NaN by 0
|
|
79
|
-
dfOption.fillna
|
|
80
|
-
dfParameter.fillna
|
|
81
|
-
dfPeriod.fillna
|
|
82
|
-
dfScenario.fillna
|
|
83
|
-
dfStage.fillna
|
|
84
|
-
dfDuration.fillna
|
|
85
|
-
dfReserveMargin.fillna
|
|
86
|
-
dfEmission.fillna
|
|
87
|
-
dfRESEnergy.fillna
|
|
88
|
-
dfDemand.fillna
|
|
89
|
-
dfInertia.fillna
|
|
90
|
-
dfUpOperatingReserve.fillna
|
|
91
|
-
dfDwOperatingReserve.fillna
|
|
92
|
-
dfGeneration.fillna
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
92
|
+
dfOption.fillna (0 , inplace=True)
|
|
93
|
+
dfParameter.fillna (0.0, inplace=True)
|
|
94
|
+
dfPeriod.fillna (0.0, inplace=True)
|
|
95
|
+
dfScenario.fillna (0.0, inplace=True)
|
|
96
|
+
dfStage.fillna (0.0, inplace=True)
|
|
97
|
+
dfDuration.fillna (0 , inplace=True)
|
|
98
|
+
dfReserveMargin.fillna (0.0, inplace=True)
|
|
99
|
+
dfEmission.fillna (math.inf , inplace=True)
|
|
100
|
+
dfRESEnergy.fillna (0.0, inplace=True)
|
|
101
|
+
dfDemand.fillna (0.0, inplace=True)
|
|
102
|
+
dfInertia.fillna (0.0, inplace=True)
|
|
103
|
+
dfUpOperatingReserve.fillna (0.0, inplace=True)
|
|
104
|
+
dfDwOperatingReserve.fillna (0.0, inplace=True)
|
|
105
|
+
dfGeneration.fillna ({"Efficiency":1.0}, inplace=True)
|
|
106
|
+
dfGeneration.fillna (0.0, inplace=True)
|
|
107
|
+
dfVariableMinPower.fillna (0.0, inplace=True)
|
|
108
|
+
dfVariableMaxPower.fillna (0.0, inplace=True)
|
|
109
|
+
dfVariableMinCharge.fillna (0.0, inplace=True)
|
|
110
|
+
dfVariableMaxCharge.fillna (0.0, inplace=True)
|
|
111
|
+
dfVariableMinStorage.fillna (0.0, inplace=True)
|
|
112
|
+
dfVariableMaxStorage.fillna (0.0, inplace=True)
|
|
113
|
+
dfVariableMinEnergy.fillna (0.0, inplace=True)
|
|
114
|
+
dfVariableMaxEnergy.fillna (0.0, inplace=True)
|
|
115
|
+
dfVariableFuelCost.fillna (0.0, inplace=True)
|
|
116
|
+
dfVariableEmissionCost.fillna (0.0, inplace=True)
|
|
117
|
+
dfEnergyInflows.fillna (0.0, inplace=True)
|
|
118
|
+
dfEnergyOutflows.fillna (0.0, inplace=True)
|
|
119
|
+
dfNodeLocation.fillna (0.0, inplace=True)
|
|
120
|
+
dfNetwork.fillna (0.0, inplace=True)
|
|
121
|
+
|
|
122
|
+
if pIndVarTTC == 1:
|
|
123
|
+
dfVariableTTCFrw.fillna (0.0, inplace=True)
|
|
124
|
+
dfVariableTTCBck.fillna (0.0, inplace=True)
|
|
125
|
+
if pIndPTDF == 1:
|
|
126
|
+
dfVariablePTDF.fillna (0.0, inplace=True)
|
|
107
127
|
|
|
108
128
|
if pIndHydroTopology == 1:
|
|
109
129
|
dfReservoir.fillna (0.0, inplace=True)
|
|
@@ -140,6 +160,11 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
140
160
|
dfEnergyInflows = dfEnergyInflows.where (dfEnergyInflows > 0.0, 0.0)
|
|
141
161
|
dfEnergyOutflows = dfEnergyOutflows.where (dfEnergyOutflows > 0.0, 0.0)
|
|
142
162
|
|
|
163
|
+
if (dfGeneration["Efficiency"] == 0).any():
|
|
164
|
+
print("WARNING: Efficiency values of 0.0 are not valid. They have been changed to 1.0.")
|
|
165
|
+
print("If you want to disable charging, set 'MaximumCharge' to 0.0 or leave it empty.")
|
|
166
|
+
dfGeneration["Efficiency"] = dfGeneration["Efficiency"].where(dfGeneration["Efficiency"] != 0.0, 1.0)
|
|
167
|
+
|
|
143
168
|
if pIndHydroTopology == 1:
|
|
144
169
|
dfVariableMinVolume = dfVariableMinVolume.where (dfVariableMinVolume > 0.0, 0.0)
|
|
145
170
|
dfVariableMaxVolume = dfVariableMaxVolume.where (dfVariableMaxVolume > 0.0, 0.0)
|
|
@@ -170,6 +195,12 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
170
195
|
print('Energy outflows \n', dfEnergyOutflows.describe (), '\n')
|
|
171
196
|
print('Electric network \n', dfNetwork.describe (), '\n')
|
|
172
197
|
|
|
198
|
+
if pIndVarTTC == 1:
|
|
199
|
+
print('Variable TTC forward \n', dfVariableTTCFrw.describe (), '\n')
|
|
200
|
+
print('Variable TTC backward \n', dfVariableTTCBck.describe (), '\n')
|
|
201
|
+
if pIndPTDF == 1:
|
|
202
|
+
print('Variable PTDF \n', dfVariablePTDF.describe (), '\n')
|
|
203
|
+
|
|
173
204
|
if pIndHydroTopology == 1:
|
|
174
205
|
print('Reservoir \n', dfReservoir.describe (), '\n')
|
|
175
206
|
print('Variable minimum reservoir volume \n', dfVariableMinVolume.describe (), '\n')
|
|
@@ -268,40 +299,40 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
268
299
|
pass
|
|
269
300
|
|
|
270
301
|
#%% parameters
|
|
271
|
-
pIndBinGenInvest
|
|
272
|
-
pIndBinGenRetire
|
|
273
|
-
pIndBinRsrInvest
|
|
274
|
-
pIndBinNetElecInvest
|
|
275
|
-
pIndBinNetH2Invest
|
|
276
|
-
pIndBinNetHeatInvest
|
|
277
|
-
pIndBinGenOperat
|
|
278
|
-
pIndBinSingleNode
|
|
279
|
-
pIndBinGenRamps
|
|
280
|
-
pIndBinGenMinTime
|
|
281
|
-
pIndBinLineCommit
|
|
282
|
-
pIndBinNetLosses
|
|
283
|
-
pENSCost
|
|
284
|
-
pH2NSCost
|
|
285
|
-
pHeatNSCost
|
|
286
|
-
pCO2Cost
|
|
287
|
-
pEconomicBaseYear
|
|
288
|
-
pAnnualDiscRate
|
|
289
|
-
pUpReserveActivation
|
|
290
|
-
pDwReserveActivation
|
|
291
|
-
pMinRatioDwUp
|
|
292
|
-
pMaxRatioDwUp
|
|
293
|
-
pSBase
|
|
294
|
-
pReferenceNode
|
|
295
|
-
pTimeStep
|
|
296
|
-
|
|
297
|
-
pPeriodWeight
|
|
298
|
-
pScenProb
|
|
299
|
-
pStageWeight
|
|
300
|
-
pDuration
|
|
301
|
-
pLevelToStage
|
|
302
|
-
pReserveMargin
|
|
303
|
-
pEmission
|
|
304
|
-
pRESEnergy
|
|
302
|
+
pIndBinGenInvest = dfOption ['IndBinGenInvest' ].iloc[0].astype('int') # Indicator of binary generation expansion decisions, 0 continuous - 1 binary - 2 no investment variables
|
|
303
|
+
pIndBinGenRetire = dfOption ['IndBinGenRetirement'].iloc[0].astype('int') # Indicator of binary generation retirement decisions,0 continuous - 1 binary - 2 no retirement variables
|
|
304
|
+
pIndBinRsrInvest = dfOption ['IndBinRsrInvest' ].iloc[0].astype('int') # Indicator of binary reservoir expansion decisions, 0 continuous - 1 binary - 2 no investment variables
|
|
305
|
+
pIndBinNetElecInvest = dfOption ['IndBinNetInvest' ].iloc[0].astype('int') # Indicator of binary electric network expansion decisions, 0 continuous - 1 binary - 2 no investment variables
|
|
306
|
+
pIndBinNetH2Invest = dfOption ['IndBinNetH2Invest' ].iloc[0].astype('int') # Indicator of binary hydrogen pipeline expansion decisions, 0 continuous - 1 binary - 2 no investment variables
|
|
307
|
+
pIndBinNetHeatInvest = dfOption ['IndBinNetHeatInvest'].iloc[0].astype('int') # Indicator of binary heat pipe expansion decisions, 0 continuous - 1 binary - 2 no investment variables
|
|
308
|
+
pIndBinGenOperat = dfOption ['IndBinGenOperat' ].iloc[0].astype('int') # Indicator of binary generation operation decisions, 0 continuous - 1 binary
|
|
309
|
+
pIndBinSingleNode = dfOption ['IndBinSingleNode' ].iloc[0].astype('int') # Indicator of single node although with electric network, 0 electric network - 1 single node
|
|
310
|
+
pIndBinGenRamps = dfOption ['IndBinGenRamps' ].iloc[0].astype('int') # Indicator of ramp constraints, 0 no ramps - 1 ramp constraints
|
|
311
|
+
pIndBinGenMinTime = dfOption ['IndBinGenMinTime' ].iloc[0].astype('int') # Indicator of minimum up/downtime constraints, 0 no min time - 1 min time constraints
|
|
312
|
+
pIndBinLineCommit = dfOption ['IndBinLineCommit' ].iloc[0].astype('int') # Indicator of binary electric network switching decisions, 0 continuous - 1 binary
|
|
313
|
+
pIndBinNetLosses = dfOption ['IndBinNetLosses' ].iloc[0].astype('int') # Indicator of electric network losses, 0 lossless - 1 ohmic losses
|
|
314
|
+
pENSCost = dfParameter['ENSCost' ].iloc[0] * 1e-3 # cost of energy not served [MEUR/GWh]
|
|
315
|
+
pH2NSCost = dfParameter['HNSCost' ].iloc[0] * 1e-3 # cost of hydrogen not served [MEUR/tH2]
|
|
316
|
+
pHeatNSCost = dfParameter['HTNSCost' ].iloc[0] * 1e-3 # cost of heat not served [MEUR/GWh]
|
|
317
|
+
pCO2Cost = dfParameter['CO2Cost' ].iloc[0] # cost of CO2 emission [EUR/tCO2]
|
|
318
|
+
pEconomicBaseYear = dfParameter['EconomicBaseYear' ].iloc[0] # economic base year [year]
|
|
319
|
+
pAnnualDiscRate = dfParameter['AnnualDiscountRate' ].iloc[0] # annual discount rate [p.u.]
|
|
320
|
+
pUpReserveActivation = dfParameter['UpReserveActivation'].iloc[0] # upward reserve activation [p.u.]
|
|
321
|
+
pDwReserveActivation = dfParameter['DwReserveActivation'].iloc[0] # downward reserve activation [p.u.]
|
|
322
|
+
pMinRatioDwUp = dfParameter['MinRatioDwUp' ].iloc[0] # minimum ratio down up operating reserves [p.u.]
|
|
323
|
+
pMaxRatioDwUp = dfParameter['MaxRatioDwUp' ].iloc[0] # maximum ratio down up operating reserves [p.u.]
|
|
324
|
+
pSBase = dfParameter['SBase' ].iloc[0] * 1e-3 # base power [GW]
|
|
325
|
+
pReferenceNode = dfParameter['ReferenceNode' ].iloc[0] # reference node
|
|
326
|
+
pTimeStep = dfParameter['TimeStep' ].iloc[0].astype('int') # duration of the unit time step [h]
|
|
327
|
+
|
|
328
|
+
pPeriodWeight = dfPeriod ['Weight' ].astype('int') # weights of periods [p.u.]
|
|
329
|
+
pScenProb = dfScenario ['Probability' ].astype('float64') # probabilities of scenarios [p.u.]
|
|
330
|
+
pStageWeight = dfStage ['Weight' ].astype('float64') # weights of stages
|
|
331
|
+
pDuration = dfDuration ['Duration' ] * pTimeStep # duration of load levels [h]
|
|
332
|
+
pLevelToStage = dfDuration ['Stage' ] # load levels assignment to stages
|
|
333
|
+
pReserveMargin = dfReserveMargin['ReserveMargin' ] # minimum adequacy reserve margin [p.u.]
|
|
334
|
+
pEmission = dfEmission ['CO2Emission' ] # maximum CO2 emission [MtCO2]
|
|
335
|
+
pRESEnergy = dfRESEnergy ['RESEnergy' ] # minimum RES energy [GWh]
|
|
305
336
|
pDemandElec = dfDemand.reindex (columns=mTEPES.nd, fill_value=0.0) * 1e-3 # electric demand [GW]
|
|
306
337
|
pSystemInertia = dfInertia.reindex (columns=mTEPES.ar, fill_value=0.0) # inertia [s]
|
|
307
338
|
pOperReserveUp = dfUpOperatingReserve.reindex (columns=mTEPES.ar, fill_value=0.0) * 1e-3 # upward operating reserve [GW]
|
|
@@ -320,20 +351,24 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
320
351
|
pEnergyInflows = dfEnergyInflows.reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic energy inflows [GW]
|
|
321
352
|
pEnergyOutflows = dfEnergyOutflows.reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic energy outflows [GW]
|
|
322
353
|
|
|
354
|
+
if pIndVarTTC == 1:
|
|
355
|
+
pVariableTTCFrw = dfVariableTTCFrw * 1e-3 # variable TTC forward [GW]
|
|
356
|
+
pVariableTTCBck = dfVariableTTCBck * 1e-3 # variable TTC backward [GW]
|
|
357
|
+
if pIndPTDF == 1:
|
|
358
|
+
pVariablePTDF = dfVariablePTDF # variable PTDF [p.u.]
|
|
359
|
+
|
|
323
360
|
if pIndHydroTopology == 1:
|
|
324
361
|
pVariableMinVolume = dfVariableMinVolume.reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic variable minimum reservoir volume [hm3]
|
|
325
362
|
pVariableMaxVolume = dfVariableMaxVolume.reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic variable maximum reservoir volume [hm3]
|
|
326
363
|
pHydroInflows = dfHydroInflows.reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic hydro inflows [m3/s]
|
|
327
364
|
pHydroOutflows = dfHydroOutflows.reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic hydro outflows [m3/s]
|
|
328
365
|
|
|
329
|
-
|
|
330
|
-
|
|
331
366
|
if pIndHydrogen == 1:
|
|
332
|
-
pDemandH2 = dfDemandHydrogen [mTEPES.nd]
|
|
367
|
+
pDemandH2 = dfDemandHydrogen [mTEPES.nd] # hydrogen demand [tH2/h]
|
|
333
368
|
|
|
334
369
|
if pIndHeat == 1:
|
|
335
|
-
pReserveMarginHeat = dfReserveMarginHeat ['ReserveMargin']
|
|
336
|
-
pDemandHeat = dfDemandHeat [mTEPES.nd] * 1e-3
|
|
370
|
+
pReserveMarginHeat = dfReserveMarginHeat ['ReserveMargin'] # minimum adequacy reserve margin [p.u.]
|
|
371
|
+
pDemandHeat = dfDemandHeat [mTEPES.nd] * 1e-3 # heat demand [GW]
|
|
337
372
|
|
|
338
373
|
if pTimeStep > 1:
|
|
339
374
|
|
|
@@ -344,6 +379,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
344
379
|
pDataFrame = pDataFrame.rolling(pTimeStep).mean()
|
|
345
380
|
pDataFrame.fillna(0.0, inplace=True)
|
|
346
381
|
return pDataFrame
|
|
382
|
+
|
|
347
383
|
# Apply the ProcessParameter function to each DataFrame
|
|
348
384
|
pDemandElec = ProcessParameter(pDemandElec, pTimeStep)
|
|
349
385
|
pSystemInertia = ProcessParameter(pSystemInertia, pTimeStep)
|
|
@@ -362,6 +398,12 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
362
398
|
pEnergyInflows = ProcessParameter(pEnergyInflows, pTimeStep)
|
|
363
399
|
pEnergyOutflows = ProcessParameter(pEnergyOutflows, pTimeStep)
|
|
364
400
|
|
|
401
|
+
if pIndVarTTC == 1:
|
|
402
|
+
pVariableTTCFrw = ProcessParameter(pVariableTTCFrw, pTimeStep)
|
|
403
|
+
pVariableTTCBck = ProcessParameter(pVariableTTCBck, pTimeStep)
|
|
404
|
+
if pIndPTDF == 1:
|
|
405
|
+
pVariablePTDF = ProcessParameter(pVariablePTDF, pTimeStep)
|
|
406
|
+
|
|
365
407
|
if pIndHydroTopology == 1:
|
|
366
408
|
pVariableMinVolume = ProcessParameter(pVariableMinVolume, pTimeStep)
|
|
367
409
|
pVariableMaxVolume = ProcessParameter(pVariableMaxVolume, pTimeStep)
|
|
@@ -374,7 +416,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
374
416
|
if pIndHeat == 1:
|
|
375
417
|
pDemandHeat = ProcessParameter(pDemandHeat, pTimeStep)
|
|
376
418
|
|
|
377
|
-
# assign duration 0 to load levels not being considered
|
|
419
|
+
# assign duration 0 to load levels not being considered; active load levels are at the end of every pTimeStep
|
|
378
420
|
for n in range(pTimeStep-2,-1,-1):
|
|
379
421
|
pDuration.iloc[[range(n,len(mTEPES.pp)*len(mTEPES.scc)*len(mTEPES.nn),pTimeStep)]] = 0
|
|
380
422
|
|
|
@@ -548,7 +590,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
548
590
|
|
|
549
591
|
#%% defining subsets: active load levels (n,n2), thermal units (t), RES units (r), ESS units (es), candidate gen units (gc), candidate ESS units (ec), all the electric lines (la), candidate electric lines (lc), candidate DC electric lines (cd), existing DC electric lines (cd), electric lines with losses (ll), reference node (rf), and reactive generating units (gq)
|
|
550
592
|
mTEPES.p = Set(doc='periods' , initialize=[pp for pp in mTEPES.pp if pPeriodWeight [pp] > 0.0 and sum(pDuration[pp,sc,n] for sc,n in mTEPES.scc*mTEPES.nn)])
|
|
551
|
-
mTEPES.sc = Set(doc='scenarios' , initialize=[scc for scc in mTEPES.scc
|
|
593
|
+
mTEPES.sc = Set(doc='scenarios' , initialize=[scc for scc in mTEPES.scc ])
|
|
552
594
|
mTEPES.ps = Set(doc='periods/scenarios' , initialize=[(p,sc) for p,sc in mTEPES.p*mTEPES.sc if pScenProb [p,sc] > 0.0 and sum(pDuration[p,sc,n ] for n in mTEPES.nn)])
|
|
553
595
|
mTEPES.st = Set(doc='stages' , initialize=[stt for stt in mTEPES.stt if pStageWeight [stt] > 0.0])
|
|
554
596
|
mTEPES.n = Set(doc='load levels' , initialize=[nn for nn in mTEPES.nn if sum(pDuration [p,sc,nn] for p,sc in mTEPES.ps) > 0])
|
|
@@ -612,6 +654,16 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
612
654
|
mTEPES.ha = Set(doc='all real heat pipes' , initialize=[])
|
|
613
655
|
mTEPES.hc = Set(doc='candidate heat pipes' , initialize=[])
|
|
614
656
|
|
|
657
|
+
pIndBinLinePTDF = pd.Series(index=mTEPES.la, data=0.0) # indicate if the line has a PTDF or not
|
|
658
|
+
if pIndVarTTC == 1:
|
|
659
|
+
pVariableTTCFrw = pVariableTTCFrw.reindex(columns=mTEPES.la, fill_value=0.0) # variable TTC forward direction
|
|
660
|
+
pVariableTTCBck = pVariableTTCBck.reindex(columns=mTEPES.la, fill_value=0.0) # variable TTC backward direction
|
|
661
|
+
if pIndPTDF == 1:
|
|
662
|
+
# get the level_3, level_4, and level_5 from multiindex of pVariablePTDF
|
|
663
|
+
PTDF_columns = pVariablePTDF.columns
|
|
664
|
+
PTDF_lines = PTDF_columns.droplevel([3]).drop_duplicates()
|
|
665
|
+
pIndBinLinePTDF.loc[:] = pIndBinLinePTDF.index.isin(PTDF_lines).astype(float)
|
|
666
|
+
|
|
615
667
|
# non-RES units, they can be committed and also contribute to the operating reserves
|
|
616
668
|
mTEPES.nr = mTEPES.g - mTEPES.re
|
|
617
669
|
# machines able to provide reactive power
|
|
@@ -630,10 +682,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
630
682
|
#%% inverse index load level to stage
|
|
631
683
|
pStageToLevel = pLevelToStage.reset_index().set_index(['Period','Scenario','Stage'])['LoadLevel']
|
|
632
684
|
#Filter only valid indices
|
|
633
|
-
pStageToLevel = pStageToLevel.loc[
|
|
634
|
-
pStageToLevel.index.isin([(p, s, st) for (p, s) in mTEPES.ps for st in mTEPES.st]) &
|
|
635
|
-
pStageToLevel.isin(mTEPES.n)
|
|
636
|
-
]
|
|
685
|
+
pStageToLevel = pStageToLevel.loc[pStageToLevel.index.isin([(p,s,st) for (p,s) in mTEPES.ps for st in mTEPES.st]) & pStageToLevel.isin(mTEPES.n)]
|
|
637
686
|
#Reorder the elements
|
|
638
687
|
pStageToLevel = [(p,sc,st,n) for (p,sc,st),n in pStageToLevel.items()]
|
|
639
688
|
mTEPES.s2n = Set(initialize=pStageToLevel, doc='Load level to stage')
|
|
@@ -653,7 +702,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
653
702
|
'''
|
|
654
703
|
Create mTEPES instrumental sets.
|
|
655
704
|
|
|
656
|
-
This function takes
|
|
705
|
+
This function takes an mTEPES instance and adds instrumental sets which will be used later.
|
|
657
706
|
|
|
658
707
|
Parameters:
|
|
659
708
|
mTEPES: The instance of mTEPES.
|
|
@@ -704,10 +753,12 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
704
753
|
mTEPES.psnnd = Set(initialize = [(p,sc,n,nd) for p,sc,n,nd in mTEPES.psn*mTEPES.nd ])
|
|
705
754
|
mTEPES.psnar = Set(initialize = [(p,sc,n,ar) for p,sc,n,ar in mTEPES.psn*mTEPES.ar ])
|
|
706
755
|
|
|
707
|
-
mTEPES.psnla = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.la
|
|
708
|
-
mTEPES.psnle = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.le
|
|
709
|
-
mTEPES.psnll = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ll
|
|
710
|
-
mTEPES.psnls = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ls
|
|
756
|
+
mTEPES.psnla = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.la if (p,ni,nf,cc) in mTEPES.pla ])
|
|
757
|
+
mTEPES.psnle = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.le if (p,ni,nf,cc) in mTEPES.pla ])
|
|
758
|
+
mTEPES.psnll = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ll if (p,ni,nf,cc) in mTEPES.pll ])
|
|
759
|
+
mTEPES.psnls = Set(initialize = [(p,sc,n,ni,nf,cc) for p,sc,n,ni,nf,cc in mTEPES.psn*mTEPES.ls if (p,ni,nf,cc) in mTEPES.pla ])
|
|
760
|
+
|
|
761
|
+
mTEPES.psnehc = Set(initialize = [(p,sc,n,eh) for p,sc,n,eh in mTEPES.psneh if pRatedMaxCharge[eh] > 0.0 ])
|
|
711
762
|
|
|
712
763
|
if pIndHydroTopology == 1:
|
|
713
764
|
mTEPES.prs = Set(initialize = [(p, rs) for p, rs in mTEPES.p *mTEPES.rs if pRsrPeriodIni[rs] <= p and pRsrPeriodFin[rs] >= p])
|
|
@@ -737,10 +788,13 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
737
788
|
else:
|
|
738
789
|
mTEPES.phc = Set(initialize = [])
|
|
739
790
|
|
|
791
|
+
if pIndPTDF == 1:
|
|
792
|
+
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 pVariablePTDF.columns])
|
|
793
|
+
|
|
740
794
|
# assigning a node to an area
|
|
741
795
|
mTEPES.ndar = Set(initialize = [(nd,ar) for (nd,zn,ar) in mTEPES.ndzn*mTEPES.ar if (zn,ar) in mTEPES.znar])
|
|
742
796
|
|
|
743
|
-
# assigning a line to an area. Both nodes are in the same area. Cross-area lines not included
|
|
797
|
+
# assigning a line to an area. Both nodes are in the same area. Cross-area lines are not included
|
|
744
798
|
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])
|
|
745
799
|
|
|
746
800
|
|
|
@@ -767,10 +821,31 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
767
821
|
pIndBinStorInvest = pIndBinStorInvest.map (idxDict)
|
|
768
822
|
pIndBinLineInvest = pIndBinLineInvest.map (idxDict)
|
|
769
823
|
pIndBinLineSwitch = pIndBinLineSwitch.map (idxDict)
|
|
770
|
-
pIndOperReserve = pIndOperReserve.map (idxDict)
|
|
824
|
+
# pIndOperReserve = pIndOperReserve.map (idxDict)
|
|
771
825
|
pIndOutflowIncomp = pIndOutflowIncomp.map (idxDict)
|
|
772
826
|
pMustRun = pMustRun.map (idxDict)
|
|
773
827
|
|
|
828
|
+
# Operating reserves can be provided while generating or while consuming
|
|
829
|
+
# So there is need for two options to decide if the unit is able to provide them
|
|
830
|
+
# Due to backwards compatibility reasons instead of adding a new column to Data_Generation NoOperatingReserve column now accepts two inputs
|
|
831
|
+
# They are separated by "|", if only one input is detected, both parameters are set to whatever the input is
|
|
832
|
+
|
|
833
|
+
def split_and_map(val):
|
|
834
|
+
# Handle new format with double input. Detect if there is a | character
|
|
835
|
+
if isinstance(val, str) and '|' in val:
|
|
836
|
+
gen, cons = val.split('|', 1)
|
|
837
|
+
return pd.Series([idxDict.get(gen.strip(), 0), idxDict.get(cons.strip(), 0)])
|
|
838
|
+
else:
|
|
839
|
+
# If no | character is found, both options are set to the inputted value
|
|
840
|
+
mapped = idxDict.get(val, 0)
|
|
841
|
+
return pd.Series([mapped, mapped])
|
|
842
|
+
|
|
843
|
+
# Split the columns in pIndOperReserve and group them in Generation and Consumption tuples
|
|
844
|
+
pIndOperReserveGen, pIndOperReserveCon = zip(*pIndOperReserve.map(split_and_map))
|
|
845
|
+
|
|
846
|
+
pIndOperReserveGen = pd.Series(pIndOperReserveGen, index=pIndOperReserve.index)
|
|
847
|
+
pIndOperReserveCon = pd.Series(pIndOperReserveCon, index=pIndOperReserve.index)
|
|
848
|
+
|
|
774
849
|
if pIndHydroTopology == 1:
|
|
775
850
|
pIndBinRsrvInvest = pIndBinRsrvInvest.map (idxDict)
|
|
776
851
|
|
|
@@ -849,11 +924,42 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
849
924
|
|
|
850
925
|
Create_ESS_RES_Sets(mTEPES)
|
|
851
926
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
927
|
+
# Create mutually exclusive groups
|
|
928
|
+
# Store in a group-generator dictionary all the relevant data
|
|
929
|
+
group_dict = {}
|
|
930
|
+
for generator, groups in pGenToExclusiveGen.items():
|
|
931
|
+
if groups != 0.0:
|
|
932
|
+
for group in str(groups).split('|'):
|
|
933
|
+
group_dict.setdefault(group, []).append(generator)
|
|
934
|
+
|
|
935
|
+
# These sets store all groups and the generators in them
|
|
936
|
+
mTEPES.ExclusiveGroups = Set(initialize=list(group_dict.keys()))
|
|
937
|
+
mTEPES.GeneratorsInExclusiveGroup = Set(mTEPES.ExclusiveGroups, initialize=group_dict)
|
|
938
|
+
|
|
939
|
+
# Create filtered dictionaries for Yearly and Hourly groups
|
|
940
|
+
group_dict_yearly = {}
|
|
941
|
+
group_dict_hourly = {}
|
|
942
|
+
|
|
943
|
+
for group, generators in group_dict.items():
|
|
944
|
+
if all(gen in mTEPES.gc for gen in generators):
|
|
945
|
+
group_dict_yearly[group] = generators
|
|
946
|
+
else:
|
|
947
|
+
group_dict_hourly[group] = generators
|
|
948
|
+
|
|
949
|
+
# The exclusive groups sets have all groups which are mutually exclusive in that time scope
|
|
950
|
+
# Generators in group sets are sets with the corresponding generators to a given group
|
|
951
|
+
mTEPES.ExclusiveGroupsYearly = Set(initialize=list(group_dict_yearly.keys()))
|
|
952
|
+
mTEPES.GeneratorsInYearlyGroup = Set(mTEPES.ExclusiveGroupsYearly, initialize=group_dict_yearly)
|
|
855
953
|
|
|
856
|
-
mTEPES.
|
|
954
|
+
mTEPES.ExclusiveGroupsHourly = Set(initialize=list(group_dict_hourly.keys()))
|
|
955
|
+
mTEPES.GeneratorsInHourlyGroup = Set(mTEPES.ExclusiveGroupsHourly, initialize=group_dict_hourly)
|
|
956
|
+
|
|
957
|
+
# All exclusive generators (sorting to ensure deterministic behavior)
|
|
958
|
+
mTEPES.ExclusiveGenerators = Set(initialize=sorted(sum(group_dict.values(), [])))
|
|
959
|
+
# All yearly exclusive generators (sorting to ensure deterministic behavior)
|
|
960
|
+
mTEPES.ExclusiveGeneratorsYearly = Set(initialize=sorted(sum(group_dict_yearly.values(), [])))
|
|
961
|
+
# All hourly exclusive generators (sorting to ensure deterministic behavior)
|
|
962
|
+
mTEPES.ExclusiveGeneratorsHourly = Set(initialize=sorted(sum(group_dict_hourly.values(), [])))
|
|
857
963
|
|
|
858
964
|
# minimum and maximum variable power, charge, and storage capacity
|
|
859
965
|
pMinPowerElec = pVariableMinPowerElec.replace(0.0, pRatedMinPowerElec)
|
|
@@ -1034,6 +1140,11 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1034
1140
|
pDemandHeat = pDemandHeat.loc [mTEPES.psn ]
|
|
1035
1141
|
pDemandHeatAbs = pDemandHeat.where(pDemandHeat > 0.0, 0.0)
|
|
1036
1142
|
|
|
1143
|
+
if pIndPTDF == 1:
|
|
1144
|
+
pVariableTTCFrw = pVariableTTCFrw.loc [mTEPES.psn]
|
|
1145
|
+
pVariableTTCBck = pVariableTTCBck.loc [mTEPES.psn]
|
|
1146
|
+
pVariablePTDF = pVariablePTDF.loc [mTEPES.psn]
|
|
1147
|
+
|
|
1037
1148
|
# separate positive and negative demands to avoid converting negative values to 0
|
|
1038
1149
|
pDemandElecPos = pDemandElec.where(pDemandElec >= 0.0, 0.0)
|
|
1039
1150
|
pDemandElecNeg = pDemandElec.where(pDemandElec < 0.0, 0.0)
|
|
@@ -1047,6 +1158,11 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1047
1158
|
for ar,es in mTEPES.ar*mTEPES.es:
|
|
1048
1159
|
if (ar,es) in mTEPES.a2g:
|
|
1049
1160
|
e2a[ar].append(es)
|
|
1161
|
+
r2a = defaultdict(list)
|
|
1162
|
+
for ar,rs in mTEPES.ar*mTEPES.rs:
|
|
1163
|
+
for h in mTEPES.h:
|
|
1164
|
+
if (ar,h) in mTEPES.a2g and sum(1 for h in mTEPES.h if (rs,h) in mTEPES.r2h or (h,rs) in mTEPES.h2r or (rs,h) in mTEPES.r2p or (h,rs) in mTEPES.p2r) and rs not in r2a[ar]:
|
|
1165
|
+
r2a[ar].append(rs)
|
|
1050
1166
|
n2a = defaultdict(list)
|
|
1051
1167
|
for ar,nr in mTEPES.ar*mTEPES.nr:
|
|
1052
1168
|
if (ar,nr) in mTEPES.a2g:
|
|
@@ -1072,7 +1188,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1072
1188
|
pOperReserveUp [pOperReserveUp [[ ar ]] < pEpsilonElec] = 0.0
|
|
1073
1189
|
pOperReserveDw [pOperReserveDw [[ ar ]] < pEpsilonElec] = 0.0
|
|
1074
1190
|
|
|
1075
|
-
if
|
|
1191
|
+
if g2a[ar]:
|
|
1076
1192
|
pMinPowerElec [pMinPowerElec [[g for g in g2a[ar]]] < pEpsilonElec] = 0.0
|
|
1077
1193
|
pMaxPowerElec [pMaxPowerElec [[g for g in g2a[ar]]] < pEpsilonElec] = 0.0
|
|
1078
1194
|
pMinCharge [pMinCharge [[es for es in e2a[ar]]] < pEpsilonElec] = 0.0
|
|
@@ -1084,6 +1200,12 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1084
1200
|
pMaxStorage [pMaxStorage [[es for es in e2a[ar]]] < pEpsilonElec] = 0.0
|
|
1085
1201
|
pIniInventory [pIniInventory [[es for es in e2a[ar]]] < pEpsilonElec] = 0.0
|
|
1086
1202
|
|
|
1203
|
+
if pIndHydroTopology == 1:
|
|
1204
|
+
pMinVolume [pMinVolume [[rs for rs in r2a[ar]]] < pEpsilonElec] = 0.0
|
|
1205
|
+
pMaxVolume [pMaxVolume [[rs for rs in r2a[ar]]] < pEpsilonElec] = 0.0
|
|
1206
|
+
pHydroInflows [pHydroInflows [[rs for rs in r2a[ar]]] < pEpsilonElec] = 0.0
|
|
1207
|
+
pHydroOutflows[pHydroOutflows[[rs for rs in r2a[ar]]] < pEpsilonElec] = 0.0
|
|
1208
|
+
|
|
1087
1209
|
# 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'))
|
|
1088
1210
|
|
|
1089
1211
|
# merging positive and negative values of the demand
|
|
@@ -1103,25 +1225,25 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1103
1225
|
|
|
1104
1226
|
pMaxCapacity = pMaxPowerElec.where(pMaxPowerElec > pMaxCharge, pMaxCharge)
|
|
1105
1227
|
|
|
1106
|
-
if
|
|
1228
|
+
if g2a[ar]:
|
|
1107
1229
|
pMaxPower2ndBlock [pMaxPower2ndBlock [[g for g in g2a[ar]]] < pEpsilonElec] = 0.0
|
|
1108
1230
|
pMaxCharge2ndBlock[pMaxCharge2ndBlock[[g for g in g2a[ar]]] < pEpsilonElec] = 0.0
|
|
1109
1231
|
|
|
1110
|
-
pLineNTCFrw.update(pd.Series([0.0 for
|
|
1111
|
-
pLineNTCBck.update(pd.Series([0.0 for
|
|
1232
|
+
pLineNTCFrw.update(pd.Series([0.0 for la in mTEPES.la if pLineNTCFrw[la] < pEpsilonElec], index=[la for la in mTEPES.la if pLineNTCFrw[la] < pEpsilonElec], dtype='float64'))
|
|
1233
|
+
pLineNTCBck.update(pd.Series([0.0 for la in mTEPES.la if pLineNTCBck[la] < pEpsilonElec], index=[la for la in mTEPES.la if pLineNTCBck[la] < pEpsilonElec], dtype='float64'))
|
|
1112
1234
|
pLineNTCMax = pLineNTCFrw.where(pLineNTCFrw > pLineNTCBck, pLineNTCBck)
|
|
1113
1235
|
|
|
1114
1236
|
if pIndHydrogen == 1:
|
|
1115
1237
|
pDemandH2[pDemandH2[[nd for nd in d2a[ar]]] < pEpsilonElec] = 0.0
|
|
1116
|
-
pH2PipeNTCFrw.update(pd.Series([0.0 for
|
|
1117
|
-
pH2PipeNTCBck.update(pd.Series([0.0 for
|
|
1238
|
+
pH2PipeNTCFrw.update(pd.Series([0.0 for pa in mTEPES.pa if pH2PipeNTCFrw[pa] < pEpsilonElec], index=[pa for pa in mTEPES.pa if pH2PipeNTCFrw[pa] < pEpsilonElec], dtype='float64'))
|
|
1239
|
+
pH2PipeNTCBck.update(pd.Series([0.0 for pa in mTEPES.pa if pH2PipeNTCBck[pa] < pEpsilonElec], index=[pa for pa in mTEPES.pa if pH2PipeNTCBck[pa] < pEpsilonElec], dtype='float64'))
|
|
1118
1240
|
|
|
1119
1241
|
if pIndHeat == 1:
|
|
1120
1242
|
pDemandHeatPeak[p,ar] = pDemandHeat.loc[p,:,:][[nd for nd in d2a[ar]]].sum(axis=1).max()
|
|
1121
1243
|
pEpsilonHeat = pDemandHeatPeak[p,ar]*1e-5
|
|
1122
1244
|
pDemandHeat [pDemandHeat [[nd for nd in d2a[ar]]] < pEpsilonHeat] = 0.0
|
|
1123
|
-
pHeatPipeNTCFrw.update(pd.Series([0.0 for
|
|
1124
|
-
pHeatPipeNTCBck.update(pd.Series([0.0 for
|
|
1245
|
+
pHeatPipeNTCFrw.update(pd.Series([0.0 for ha in mTEPES.ha if pHeatPipeNTCFrw[ha] < pEpsilonHeat], index=[ha for ha in mTEPES.ha if pHeatPipeNTCFrw[ha] < pEpsilonHeat], dtype='float64'))
|
|
1246
|
+
pHeatPipeNTCBck.update(pd.Series([0.0 for ha in mTEPES.ha if pHeatPipeNTCBck[ha] < pEpsilonHeat], index=[ha for ha in mTEPES.ha if pHeatPipeNTCBck[ha] < pEpsilonHeat], dtype='float64'))
|
|
1125
1247
|
|
|
1126
1248
|
# drop generators not g or es or eh or ch
|
|
1127
1249
|
pMinPowerElec = pMinPowerElec.loc [:,mTEPES.g ]
|
|
@@ -1147,7 +1269,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1147
1269
|
pMaxPower2ndBlock = pMaxPower2ndBlock.where (pMaxPower2ndBlock > pEpsilon, 0.0)
|
|
1148
1270
|
pMaxCharge2ndBlock = pMaxCharge2ndBlock.where(pMaxCharge2ndBlock > pEpsilon, 0.0)
|
|
1149
1271
|
|
|
1150
|
-
# computation of the power
|
|
1272
|
+
# computation of the power-to-heat ratio of the CHP units
|
|
1151
1273
|
# heat ratio of boiler units is fixed to 1.0
|
|
1152
1274
|
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)
|
|
1153
1275
|
pMinPowerHeat = pd.DataFrame([[pMinPowerElec [ch][p,sc,n]/pPower2HeatRatio[ch] for ch in mTEPES.ch] for p,sc,n in mTEPES.psn], index=mTEPES.psn, columns=mTEPES.ch)
|
|
@@ -1206,6 +1328,17 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1206
1328
|
pNetUpInvest = pNetUpInvest.loc [mTEPES.lc]
|
|
1207
1329
|
pLineLossFactor = pLineLossFactor.loc [mTEPES.ll]
|
|
1208
1330
|
|
|
1331
|
+
pMaxNTCFrw = pd.DataFrame([[pLineNTCFrw[la] for la in mTEPES.la] for p,sc,n in mTEPES.psn], index=mTEPES.psn, columns=mTEPES.la)
|
|
1332
|
+
pMaxNTCBck = pd.DataFrame([[pLineNTCBck[la] for la in mTEPES.la] for p,sc,n in mTEPES.psn], index=mTEPES.psn, columns=mTEPES.la)
|
|
1333
|
+
if pIndVarTTC == 1:
|
|
1334
|
+
pMaxNTCFrw = pVariableTTCFrw.replace(0.0, pLineNTCFrw / dfNetwork['SecurityFactor']) * dfNetwork['SecurityFactor'].loc[dfNetwork.index.isin(set(mTEPES.la))]
|
|
1335
|
+
pMaxNTCBck = pVariableTTCBck.replace(0.0, pLineNTCBck / dfNetwork['SecurityFactor']) * dfNetwork['SecurityFactor'].loc[dfNetwork.index.isin(set(mTEPES.la))]
|
|
1336
|
+
pMaxNTCMax = pMaxNTCFrw.where(pMaxNTCFrw > pMaxNTCBck, pMaxNTCBck)
|
|
1337
|
+
|
|
1338
|
+
pMaxNTCBck = pMaxNTCBck.loc [:,mTEPES.la]
|
|
1339
|
+
pMaxNTCFrw = pMaxNTCFrw.loc [:,mTEPES.la]
|
|
1340
|
+
pMaxNTCMax = pMaxNTCFrw.loc [:,mTEPES.la]
|
|
1341
|
+
|
|
1209
1342
|
if pIndHydroTopology == 1:
|
|
1210
1343
|
# drop generators not h
|
|
1211
1344
|
pProductionFunctionHydro = pProductionFunctionHydro.loc[mTEPES.h ]
|
|
@@ -1310,6 +1443,16 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1310
1443
|
pOperReserveUp = filter_rows(pOperReserveUp , mTEPES.psnar)
|
|
1311
1444
|
pOperReserveDw = filter_rows(pOperReserveDw , mTEPES.psnar)
|
|
1312
1445
|
|
|
1446
|
+
pMaxNTCBck = filter_rows(pMaxNTCBck , mTEPES.psnla)
|
|
1447
|
+
pMaxNTCFrw = filter_rows(pMaxNTCFrw , mTEPES.psnla)
|
|
1448
|
+
pMaxNTCMax = filter_rows(pMaxNTCMax , mTEPES.psnla)
|
|
1449
|
+
|
|
1450
|
+
if pIndPTDF == 1:
|
|
1451
|
+
pPTDF = pVariablePTDF.stack(level=list(range(pVariablePTDF.columns.nlevels)), future_stack=True)
|
|
1452
|
+
pPTDF.index.set_names(['Period', 'Scenario', 'LoadLevel', 'InitialNode', 'FinalNode', 'Circuit', 'Node'], inplace=True)
|
|
1453
|
+
# filter rows to keep the same as mTEPES.psnland
|
|
1454
|
+
pPTDF = pPTDF[pPTDF.index.isin(mTEPES.psnland)]
|
|
1455
|
+
|
|
1313
1456
|
# %% parameters
|
|
1314
1457
|
mTEPES.pIndBinGenInvest = Param(initialize=pIndBinGenInvest , within=NonNegativeIntegers, doc='Indicator of binary generation investment decisions', mutable=True)
|
|
1315
1458
|
mTEPES.pIndBinGenRetire = Param(initialize=pIndBinGenRetire , within=NonNegativeIntegers, doc='Indicator of binary generation retirement decisions', mutable=True)
|
|
@@ -1326,19 +1469,20 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1326
1469
|
mTEPES.pIndHydroTopology = Param(initialize=pIndHydroTopology , within=Binary, doc='Indicator of reservoir and hydropower topology' )
|
|
1327
1470
|
mTEPES.pIndHydrogen = Param(initialize=pIndHydrogen , within=Binary, doc='Indicator of hydrogen demand and pipeline network' )
|
|
1328
1471
|
mTEPES.pIndHeat = Param(initialize=pIndHeat , within=Binary, doc='Indicator of heat demand and pipe network' )
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
mTEPES.
|
|
1332
|
-
mTEPES.
|
|
1333
|
-
mTEPES.
|
|
1334
|
-
mTEPES.
|
|
1335
|
-
mTEPES.
|
|
1336
|
-
mTEPES.
|
|
1337
|
-
mTEPES.
|
|
1338
|
-
mTEPES.
|
|
1339
|
-
mTEPES.
|
|
1340
|
-
mTEPES.
|
|
1341
|
-
mTEPES.
|
|
1472
|
+
mTEPES.pIndPTDF = Param(initialize=pIndPTDF , within=Binary, doc='Indicator of using or not the Flow-based method' )
|
|
1473
|
+
|
|
1474
|
+
mTEPES.pENSCost = Param(initialize=pENSCost , within=NonNegativeReals, doc='ENS cost' )
|
|
1475
|
+
mTEPES.pH2NSCost = Param(initialize=pH2NSCost , within=NonNegativeReals, doc='HNS cost' )
|
|
1476
|
+
mTEPES.pHeatNSCost = Param(initialize=pHeatNSCost , within=NonNegativeReals, doc='HTNS cost' )
|
|
1477
|
+
mTEPES.pCO2Cost = Param(initialize=pCO2Cost , within=NonNegativeReals, doc='CO2 emission cost' )
|
|
1478
|
+
mTEPES.pAnnualDiscRate = Param(initialize=pAnnualDiscRate , within=UnitInterval, doc='Annual discount rate' )
|
|
1479
|
+
mTEPES.pUpReserveActivation = Param(initialize=pUpReserveActivation, within=UnitInterval, doc='Proportion of upward reserve activation' )
|
|
1480
|
+
mTEPES.pDwReserveActivation = Param(initialize=pDwReserveActivation, within=UnitInterval, doc='Proportion of downward reserve activation' )
|
|
1481
|
+
mTEPES.pMinRatioDwUp = Param(initialize=pMinRatioDwUp , within=UnitInterval, doc='Minimum ratio downward to upward operating reserves')
|
|
1482
|
+
mTEPES.pMaxRatioDwUp = Param(initialize=pMaxRatioDwUp , within=UnitInterval, doc='Maximum ratio downward to upward operating reserves')
|
|
1483
|
+
mTEPES.pSBase = Param(initialize=pSBase , within=PositiveReals, doc='Base power' )
|
|
1484
|
+
mTEPES.pTimeStep = Param(initialize=pTimeStep , within=PositiveIntegers, doc='Unitary time step' )
|
|
1485
|
+
mTEPES.pEconomicBaseYear = Param(initialize=pEconomicBaseYear , within=PositiveIntegers, doc='Base year' )
|
|
1342
1486
|
|
|
1343
1487
|
mTEPES.pReserveMargin = Param(mTEPES.par, initialize=pReserveMargin.to_dict() , within=NonNegativeReals, doc='Adequacy reserve margin' )
|
|
1344
1488
|
mTEPES.pEmission = Param(mTEPES.par, initialize=pEmission.to_dict() , within=NonNegativeReals, doc='Maximum CO2 emission' )
|
|
@@ -1399,7 +1543,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1399
1543
|
mTEPES.pIndBinUnitRetire = Param(mTEPES.gd, initialize=pIndBinUnitRetire.to_dict() , within=Binary , doc='Binary retirement decision' )
|
|
1400
1544
|
mTEPES.pIndBinUnitCommit = Param(mTEPES.nr, initialize=pIndBinUnitCommit.to_dict() , within=Binary , doc='Binary commitment decision' )
|
|
1401
1545
|
mTEPES.pIndBinStorInvest = Param(mTEPES.ec, initialize=pIndBinStorInvest.to_dict() , within=Binary , doc='Storage linked to generation investment' )
|
|
1402
|
-
mTEPES.
|
|
1546
|
+
mTEPES.pIndOperReserveGen = Param(mTEPES.gg, initialize=pIndOperReserveGen.to_dict() , within=Binary , doc='Indicator of operating reserve when generating power')
|
|
1547
|
+
mTEPES.pIndOperReserveCon = Param(mTEPES.gg, initialize=pIndOperReserveCon.to_dict() , within=Binary , doc='Indicator of operating reserve when consuming power' )
|
|
1403
1548
|
mTEPES.pIndOutflowIncomp = Param(mTEPES.gg, initialize=pIndOutflowIncomp.to_dict() , within=Binary , doc='Indicator of outflow incompatibility with charging' )
|
|
1404
1549
|
mTEPES.pEfficiency = Param(mTEPES.eh, initialize=pEfficiency.to_dict() , within=UnitInterval , doc='Round-trip efficiency' )
|
|
1405
1550
|
mTEPES.pStorageTimeStep = Param(mTEPES.es, initialize=pStorageTimeStep.to_dict() , within=PositiveIntegers, doc='ESS Storage cycle' )
|
|
@@ -1427,8 +1572,10 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1427
1572
|
mTEPES.pProductionFunctionHeat = Param(mTEPES.hp, initialize=pProductionFunctionHeat.to_dict() , within=NonNegativeReals, doc='Production function of an CHP plant' )
|
|
1428
1573
|
mTEPES.pProductionFunctionH2ToHeat = Param(mTEPES.hh, initialize=pProductionFunctionH2ToHeat.to_dict(), within=NonNegativeReals, doc='Production function of an boiler using H2')
|
|
1429
1574
|
|
|
1430
|
-
if
|
|
1575
|
+
if pIndPTDF == 1:
|
|
1576
|
+
mTEPES.pPTDF = Param(mTEPES.psnland, initialize=pPTDF.to_dict() , within=Reals , doc='Power transfer distribution factor' )
|
|
1431
1577
|
|
|
1578
|
+
if pIndHydroTopology == 1:
|
|
1432
1579
|
pHydroInflows = filter_rows(pHydroInflows , mTEPES.psnrs)
|
|
1433
1580
|
pHydroOutflows = filter_rows(pHydroOutflows, mTEPES.psnrs)
|
|
1434
1581
|
pMaxOutflows = filter_rows(pMaxOutflows , mTEPES.psnrs)
|
|
@@ -1502,6 +1649,10 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
|
|
|
1502
1649
|
mTEPES.pAngMax = Param(mTEPES.la, initialize=pAngMax.to_dict() , within= Reals, doc='Maximum phase angle difference', mutable=True)
|
|
1503
1650
|
mTEPES.pNetLoInvest = Param(mTEPES.lc, initialize=pNetLoInvest.to_dict() , within=NonNegativeReals, doc='Lower bound of the electric line investment decision', mutable=True)
|
|
1504
1651
|
mTEPES.pNetUpInvest = Param(mTEPES.lc, initialize=pNetUpInvest.to_dict() , within=NonNegativeReals, doc='Upper bound of the electric line investment decision', mutable=True)
|
|
1652
|
+
mTEPES.pIndBinLinePTDF = Param(mTEPES.la, initialize=pIndBinLinePTDF.to_dict() , within=Binary , doc='Binary indicator of line with' )
|
|
1653
|
+
mTEPES.pMaxNTCFrw = Param(mTEPES.psnla, initialize=pMaxNTCFrw.to_dict() , within= Reals, doc='Maximum NTC forward capacity' )
|
|
1654
|
+
mTEPES.pMaxNTCBck = Param(mTEPES.psnla, initialize=pMaxNTCBck.to_dict() , within= Reals, doc='Maximum NTC backward capacity' )
|
|
1655
|
+
mTEPES.pMaxNTCMax = Param(mTEPES.psnla, initialize=pMaxNTCMax.to_dict() , within= Reals, doc='Maximum NTC capacity' )
|
|
1505
1656
|
|
|
1506
1657
|
if pIndHydrogen == 1:
|
|
1507
1658
|
mTEPES.pH2PipeLength = Param(mTEPES.pn, initialize=pH2PipeLength.to_dict() , within=NonNegativeReals, doc='Hydrogen pipeline length', mutable=True)
|
|
@@ -1599,7 +1750,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1599
1750
|
'''
|
|
1600
1751
|
Create all mTEPES variables.
|
|
1601
1752
|
|
|
1602
|
-
This function takes
|
|
1753
|
+
This function takes an mTEPES instance with all parameters and sets created and adds variables to it.
|
|
1603
1754
|
|
|
1604
1755
|
Parameters:
|
|
1605
1756
|
mTEPES: The instance of mTEPES.
|
|
@@ -1608,126 +1759,137 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1608
1759
|
None: Variables are added directly to the mTEPES object.
|
|
1609
1760
|
'''
|
|
1610
1761
|
#%% variables
|
|
1611
|
-
OptModel.vTotalSCost = Var( within=NonNegativeReals,
|
|
1612
|
-
OptModel.vTotalICost = Var( within=NonNegativeReals,
|
|
1613
|
-
OptModel.vTotalFCost = Var(mTEPES.p, within=NonNegativeReals,
|
|
1614
|
-
OptModel.vTotalGCost = Var(mTEPES.psn, within=NonNegativeReals,
|
|
1615
|
-
OptModel.vTotalCCost = Var(mTEPES.psn, within=NonNegativeReals,
|
|
1616
|
-
OptModel.vTotalECost = Var(mTEPES.psn, within=NonNegativeReals,
|
|
1617
|
-
OptModel.vTotalRCost = Var(mTEPES.psn, within=NonNegativeReals,
|
|
1618
|
-
OptModel.vTotalNCost = Var(mTEPES.psn, within=NonNegativeReals,
|
|
1619
|
-
OptModel.vTotalEmissionArea = Var(mTEPES.psnar, within=NonNegativeReals,
|
|
1620
|
-
OptModel.vTotalECostArea = Var(mTEPES.psnar, within=NonNegativeReals,
|
|
1621
|
-
OptModel.vTotalRESEnergyArea = Var(mTEPES.psnar, within=NonNegativeReals,
|
|
1622
|
-
|
|
1623
|
-
OptModel.vTotalOutput = Var(mTEPES.psng , within=NonNegativeReals,
|
|
1624
|
-
OptModel.vOutput2ndBlock = Var(mTEPES.psnnr, within=NonNegativeReals,
|
|
1625
|
-
OptModel.vReserveUp = Var(mTEPES.psnnr, within=NonNegativeReals,
|
|
1626
|
-
OptModel.vReserveDown = Var(mTEPES.psnnr, within=NonNegativeReals,
|
|
1627
|
-
OptModel.vEnergyInflows = Var(mTEPES.psnec, within=NonNegativeReals,
|
|
1628
|
-
OptModel.vEnergyOutflows = Var(mTEPES.psnes, within=NonNegativeReals,
|
|
1629
|
-
OptModel.vESSInventory = Var(mTEPES.psnes, within=NonNegativeReals,
|
|
1630
|
-
OptModel.vESSSpillage = Var(mTEPES.psnes, within=NonNegativeReals,
|
|
1631
|
-
OptModel.vIniInventory = Var(mTEPES.psnec, within=NonNegativeReals,
|
|
1632
|
-
|
|
1633
|
-
OptModel.vESSTotalCharge = Var(mTEPES.psneh, within=NonNegativeReals,
|
|
1634
|
-
OptModel.vCharge2ndBlock = Var(mTEPES.psneh, within=NonNegativeReals,
|
|
1635
|
-
OptModel.vESSReserveUp = Var(mTEPES.psneh, within=NonNegativeReals,
|
|
1636
|
-
OptModel.vESSReserveDown = Var(mTEPES.psneh, within=NonNegativeReals,
|
|
1637
|
-
OptModel.vENS = Var(mTEPES.psnnd, within=NonNegativeReals,
|
|
1762
|
+
OptModel.vTotalSCost = Var( within=NonNegativeReals, doc='total system cost [MEUR]')
|
|
1763
|
+
OptModel.vTotalICost = Var( within=NonNegativeReals, doc='total system investment cost [MEUR]')
|
|
1764
|
+
OptModel.vTotalFCost = Var(mTEPES.p, within=NonNegativeReals, doc='total system fixed cost [MEUR]')
|
|
1765
|
+
OptModel.vTotalGCost = Var(mTEPES.psn, within=NonNegativeReals, doc='total variable generation operation cost [MEUR]')
|
|
1766
|
+
OptModel.vTotalCCost = Var(mTEPES.psn, within=NonNegativeReals, doc='total variable consumption operation cost [MEUR]')
|
|
1767
|
+
OptModel.vTotalECost = Var(mTEPES.psn, within=NonNegativeReals, doc='total system emission cost [MEUR]')
|
|
1768
|
+
OptModel.vTotalRCost = Var(mTEPES.psn, within=NonNegativeReals, doc='total system reliability cost [MEUR]')
|
|
1769
|
+
OptModel.vTotalNCost = Var(mTEPES.psn, within=NonNegativeReals, doc='total network loss penalty operation cost [MEUR]')
|
|
1770
|
+
OptModel.vTotalEmissionArea = Var(mTEPES.psnar, within=NonNegativeReals, doc='total area emission [MtCO2]')
|
|
1771
|
+
OptModel.vTotalECostArea = Var(mTEPES.psnar, within=NonNegativeReals, doc='total area emission cost [MEUR]')
|
|
1772
|
+
OptModel.vTotalRESEnergyArea = Var(mTEPES.psnar, within=NonNegativeReals, doc=' RES energy [GWh]')
|
|
1773
|
+
|
|
1774
|
+
OptModel.vTotalOutput = Var(mTEPES.psng , within=NonNegativeReals, doc='total output of the unit [GW]')
|
|
1775
|
+
OptModel.vOutput2ndBlock = Var(mTEPES.psnnr, within=NonNegativeReals, doc='second block of the unit [GW]')
|
|
1776
|
+
OptModel.vReserveUp = Var(mTEPES.psnnr, within=NonNegativeReals, doc='upward operating reserve [GW]')
|
|
1777
|
+
OptModel.vReserveDown = Var(mTEPES.psnnr, within=NonNegativeReals, doc='downward operating reserve [GW]')
|
|
1778
|
+
OptModel.vEnergyInflows = Var(mTEPES.psnec, within=NonNegativeReals, doc='unscheduled inflows of candidate ESS units [GW]')
|
|
1779
|
+
OptModel.vEnergyOutflows = Var(mTEPES.psnes, within=NonNegativeReals, doc='scheduled outflows of all ESS units [GW]')
|
|
1780
|
+
OptModel.vESSInventory = Var(mTEPES.psnes, within=NonNegativeReals, doc='ESS inventory [GWh]')
|
|
1781
|
+
OptModel.vESSSpillage = Var(mTEPES.psnes, within=NonNegativeReals, doc='ESS spillage [GWh]')
|
|
1782
|
+
OptModel.vIniInventory = Var(mTEPES.psnec, within=NonNegativeReals, doc='initial inventory for ESS candidate [GWh]')
|
|
1783
|
+
|
|
1784
|
+
OptModel.vESSTotalCharge = Var(mTEPES.psneh, within=NonNegativeReals, doc='ESS total charge power [GW]')
|
|
1785
|
+
OptModel.vCharge2ndBlock = Var(mTEPES.psneh, within=NonNegativeReals, doc='ESS charge power [GW]')
|
|
1786
|
+
OptModel.vESSReserveUp = Var(mTEPES.psneh, within=NonNegativeReals, doc='ESS upward operating reserve [GW]')
|
|
1787
|
+
OptModel.vESSReserveDown = Var(mTEPES.psneh, within=NonNegativeReals, doc='ESS downward operating reserve [GW]')
|
|
1788
|
+
OptModel.vENS = Var(mTEPES.psnnd, within=NonNegativeReals, doc='energy not served in node [GW]')
|
|
1638
1789
|
|
|
1639
1790
|
if mTEPES.pIndHydroTopology == 1:
|
|
1640
|
-
OptModel.vHydroInflows = Var(mTEPES.psnrc, within=NonNegativeReals,
|
|
1641
|
-
OptModel.vHydroOutflows = Var(mTEPES.psnrs, within=NonNegativeReals,
|
|
1642
|
-
OptModel.vReservoirVolume = Var(mTEPES.psnrs, within=NonNegativeReals,
|
|
1643
|
-
OptModel.vReservoirSpillage = Var(mTEPES.psnrs, within=NonNegativeReals,
|
|
1791
|
+
OptModel.vHydroInflows = Var(mTEPES.psnrc, within=NonNegativeReals, doc='unscheduled inflows of candidate hydro units [m3/s]')
|
|
1792
|
+
OptModel.vHydroOutflows = Var(mTEPES.psnrs, within=NonNegativeReals, doc='scheduled outflows of all hydro units [m3/s]')
|
|
1793
|
+
OptModel.vReservoirVolume = Var(mTEPES.psnrs, within=NonNegativeReals, doc='Reservoir volume [hm3]')
|
|
1794
|
+
OptModel.vReservoirSpillage = Var(mTEPES.psnrs, within=NonNegativeReals, doc='Reservoir spillage [hm3]')
|
|
1644
1795
|
|
|
1645
1796
|
if mTEPES.pIndHeat == 1:
|
|
1646
|
-
OptModel.vTotalOutputHeat = Var(mTEPES.psng , within=NonNegativeReals,
|
|
1797
|
+
OptModel.vTotalOutputHeat = Var(mTEPES.psng , within=NonNegativeReals, doc='total heat output of the boiler unit [GW]')
|
|
1647
1798
|
[OptModel.vTotalOutputHeat[p,sc,n,ch].setub(mTEPES.pMaxPowerHeat[p,sc,n,ch]) for p,sc,n,ch in mTEPES.psnch]
|
|
1648
1799
|
[OptModel.vTotalOutputHeat[p,sc,n,ch].setlb(mTEPES.pMinPowerHeat[p,sc,n,ch]) for p,sc,n,ch in mTEPES.psnch]
|
|
1649
1800
|
# only boilers are forced to produce at their minimum heat power. CHPs are not forced to produce at their minimum heat power, they are committed or not to produce electricity
|
|
1650
1801
|
|
|
1651
1802
|
if mTEPES.pIndBinGenInvest() != 1:
|
|
1652
|
-
OptModel.vGenerationInvest = Var(mTEPES.peb, within=UnitInterval,
|
|
1653
|
-
OptModel.vGenerationInvPer = Var(mTEPES.peb, within=UnitInterval,
|
|
1803
|
+
OptModel.vGenerationInvest = Var(mTEPES.peb, within=UnitInterval, doc='generation investment decision exists in a year [0,1]')
|
|
1804
|
+
OptModel.vGenerationInvPer = Var(mTEPES.peb, within=UnitInterval, doc='generation investment decision done in a year [0,1]')
|
|
1654
1805
|
else:
|
|
1655
|
-
OptModel.vGenerationInvest = Var(mTEPES.peb, within=Binary,
|
|
1656
|
-
OptModel.vGenerationInvPer = Var(mTEPES.peb, within=Binary,
|
|
1806
|
+
OptModel.vGenerationInvest = Var(mTEPES.peb, within=Binary, doc='generation investment decision exists in a year {0,1}')
|
|
1807
|
+
OptModel.vGenerationInvPer = Var(mTEPES.peb, within=Binary, doc='generation investment decision done in a year {0,1}')
|
|
1657
1808
|
|
|
1658
1809
|
if mTEPES.pIndBinGenRetire() != 1:
|
|
1659
|
-
OptModel.vGenerationRetire = Var(mTEPES.pgd, within=UnitInterval,
|
|
1660
|
-
OptModel.vGenerationRetPer = Var(mTEPES.pgd, within=UnitInterval,
|
|
1810
|
+
OptModel.vGenerationRetire = Var(mTEPES.pgd, within=UnitInterval, doc='generation retirement decision exists in a year [0,1]')
|
|
1811
|
+
OptModel.vGenerationRetPer = Var(mTEPES.pgd, within=UnitInterval, doc='generation retirement decision exists in a year [0,1]')
|
|
1661
1812
|
else:
|
|
1662
|
-
OptModel.vGenerationRetire = Var(mTEPES.pgd, within=Binary,
|
|
1663
|
-
OptModel.vGenerationRetPer = Var(mTEPES.pgd, within=Binary,
|
|
1813
|
+
OptModel.vGenerationRetire = Var(mTEPES.pgd, within=Binary, doc='generation retirement decision exists in a year {0,1}')
|
|
1814
|
+
OptModel.vGenerationRetPer = Var(mTEPES.pgd, within=Binary, doc='generation retirement decision exists in a year {0,1}')
|
|
1664
1815
|
|
|
1665
1816
|
if mTEPES.pIndBinNetElecInvest() != 1:
|
|
1666
|
-
OptModel.vNetworkInvest = Var(mTEPES.plc, within=UnitInterval,
|
|
1667
|
-
OptModel.vNetworkInvPer = Var(mTEPES.plc, within=UnitInterval,
|
|
1817
|
+
OptModel.vNetworkInvest = Var(mTEPES.plc, within=UnitInterval, doc='electric network investment decision exists in a year [0,1]')
|
|
1818
|
+
OptModel.vNetworkInvPer = Var(mTEPES.plc, within=UnitInterval, doc='electric network investment decision exists in a year [0,1]')
|
|
1668
1819
|
else:
|
|
1669
|
-
OptModel.vNetworkInvest = Var(mTEPES.plc, within=Binary,
|
|
1670
|
-
OptModel.vNetworkInvPer = Var(mTEPES.plc, within=Binary,
|
|
1820
|
+
OptModel.vNetworkInvest = Var(mTEPES.plc, within=Binary, doc='electric network investment decision exists in a year {0,1}')
|
|
1821
|
+
OptModel.vNetworkInvPer = Var(mTEPES.plc, within=Binary, doc='electric network investment decision exists in a year {0,1}')
|
|
1671
1822
|
|
|
1672
1823
|
if mTEPES.pIndHydroTopology == 1:
|
|
1673
1824
|
if mTEPES.pIndBinRsrInvest() != 1:
|
|
1674
|
-
OptModel.vReservoirInvest = Var(mTEPES.prc, within=UnitInterval,
|
|
1675
|
-
OptModel.vReservoirInvPer = Var(mTEPES.prc, within=UnitInterval,
|
|
1825
|
+
OptModel.vReservoirInvest = Var(mTEPES.prc, within=UnitInterval, doc='reservoir investment decision exists in a year [0,1]')
|
|
1826
|
+
OptModel.vReservoirInvPer = Var(mTEPES.prc, within=UnitInterval, doc='reservoir investment decision exists in a year [0,1]')
|
|
1676
1827
|
else:
|
|
1677
|
-
OptModel.vReservoirInvest = Var(mTEPES.prc, within=Binary,
|
|
1678
|
-
OptModel.vReservoirInvPer = Var(mTEPES.prc, within=Binary,
|
|
1828
|
+
OptModel.vReservoirInvest = Var(mTEPES.prc, within=Binary, doc='reservoir investment decision exists in a year {0,1}')
|
|
1829
|
+
OptModel.vReservoirInvPer = Var(mTEPES.prc, within=Binary, doc='reservoir investment decision exists in a year {0,1}')
|
|
1679
1830
|
|
|
1680
1831
|
if mTEPES.pIndHydrogen == 1:
|
|
1681
1832
|
if mTEPES.pIndBinNetH2Invest() != 1:
|
|
1682
|
-
OptModel.vH2PipeInvest = Var(mTEPES.ppc, within=UnitInterval,
|
|
1683
|
-
OptModel.vH2PipeInvPer = Var(mTEPES.ppc, within=UnitInterval,
|
|
1833
|
+
OptModel.vH2PipeInvest = Var(mTEPES.ppc, within=UnitInterval, doc='hydrogen network investment decision exists in a year [0,1]')
|
|
1834
|
+
OptModel.vH2PipeInvPer = Var(mTEPES.ppc, within=UnitInterval, doc='hydrogen network investment decision exists in a year [0,1]')
|
|
1684
1835
|
else:
|
|
1685
|
-
OptModel.vH2PipeInvest = Var(mTEPES.ppc, within=Binary,
|
|
1686
|
-
OptModel.vH2PipeInvPer = Var(mTEPES.ppc, within=Binary,
|
|
1836
|
+
OptModel.vH2PipeInvest = Var(mTEPES.ppc, within=Binary, doc='hydrogen network investment decision exists in a year {0,1}')
|
|
1837
|
+
OptModel.vH2PipeInvPer = Var(mTEPES.ppc, within=Binary, doc='hydrogen network investment decision exists in a year {0,1}')
|
|
1687
1838
|
|
|
1688
1839
|
if mTEPES.pIndHeat == 1:
|
|
1689
1840
|
if mTEPES.pIndBinGenInvest() != 1:
|
|
1690
|
-
OptModel.vGenerationInvestHeat = Var(mTEPES.pbc, within=UnitInterval,
|
|
1691
|
-
OptModel.vGenerationInvPerHeat = Var(mTEPES.pbc, within=UnitInterval,
|
|
1841
|
+
OptModel.vGenerationInvestHeat = Var(mTEPES.pbc, within=UnitInterval, doc='generation investment decision exists in a year [0,1]')
|
|
1842
|
+
OptModel.vGenerationInvPerHeat = Var(mTEPES.pbc, within=UnitInterval, doc='generation investment decision done in a year [0,1]')
|
|
1692
1843
|
else:
|
|
1693
|
-
OptModel.vGenerationInvestHeat = Var(mTEPES.pbc, within=Binary,
|
|
1694
|
-
OptModel.vGenerationInvPerHeat = Var(mTEPES.pbc, within=Binary,
|
|
1844
|
+
OptModel.vGenerationInvestHeat = Var(mTEPES.pbc, within=Binary, doc='generation investment decision exists in a year {0,1}')
|
|
1845
|
+
OptModel.vGenerationInvPerHeat = Var(mTEPES.pbc, within=Binary, doc='generation investment decision done in a year {0,1}')
|
|
1695
1846
|
if mTEPES.pIndBinNetHeatInvest() != 1:
|
|
1696
|
-
OptModel.vHeatPipeInvest = Var(mTEPES.phc, within=UnitInterval,
|
|
1697
|
-
OptModel.vHeatPipeInvPer = Var(mTEPES.phc, within=UnitInterval,
|
|
1847
|
+
OptModel.vHeatPipeInvest = Var(mTEPES.phc, within=UnitInterval, doc='heat network investment decision exists in a year [0,1]' )
|
|
1848
|
+
OptModel.vHeatPipeInvPer = Var(mTEPES.phc, within=UnitInterval, doc='heat network investment decision exists in a year [0,1]' )
|
|
1698
1849
|
else:
|
|
1699
|
-
OptModel.vHeatPipeInvest = Var(mTEPES.phc, within=Binary,
|
|
1700
|
-
OptModel.vHeatPipeInvPer = Var(mTEPES.phc, within=Binary,
|
|
1850
|
+
OptModel.vHeatPipeInvest = Var(mTEPES.phc, within=Binary, doc='heat network investment decision exists in a year {0,1}' )
|
|
1851
|
+
OptModel.vHeatPipeInvPer = Var(mTEPES.phc, within=Binary, doc='heat network investment decision exists in a year {0,1}' )
|
|
1701
1852
|
|
|
1702
1853
|
if mTEPES.pIndBinGenOperat() == 0:
|
|
1703
1854
|
OptModel.vCommitment = Var(mTEPES.psnnr, within=UnitInterval, initialize=0.0, doc='commitment of the unit [0,1]')
|
|
1704
1855
|
OptModel.vStartUp = Var(mTEPES.psnnr, within=UnitInterval, initialize=0.0, doc='startup of the unit [0,1]')
|
|
1705
1856
|
OptModel.vShutDown = Var(mTEPES.psnnr, within=UnitInterval, initialize=0.0, doc='shutdown of the unit [0,1]')
|
|
1706
|
-
OptModel.vMaxCommitment = Var(mTEPES.psnr , within=UnitInterval, initialize=0.0, doc='maximum commitment of the unit [0,1]')
|
|
1707
1857
|
OptModel.vStableState = Var(mTEPES.psnnr, within=UnitInterval, initialize=0.0, doc='stable state of the unit [0,1]')
|
|
1708
1858
|
OptModel.vRampUpState = Var(mTEPES.psnnr, within=UnitInterval, initialize=0.0, doc='ramp up state of the unit [0,1]')
|
|
1709
1859
|
OptModel.vRampDwState = Var(mTEPES.psnnr, within=UnitInterval, initialize=0.0, doc='ramp down state of the unit [0,1]')
|
|
1860
|
+
|
|
1861
|
+
OptModel.vMaxCommitmentYearly = Var(mTEPES.psnr ,mTEPES.ExclusiveGroupsYearly, within=UnitInterval, initialize=0.0, doc='maximum commitment of the unit yearly [0,1]')
|
|
1862
|
+
OptModel.vMaxCommitmentHourly = Var(mTEPES.psnnr,mTEPES.ExclusiveGroupsHourly, within=UnitInterval, initialize=0.0, doc='maximum commitment of the unit hourly [0,1]')
|
|
1863
|
+
|
|
1864
|
+
if mTEPES.pIndHydroTopology == 1:
|
|
1865
|
+
OptModel.vCommitmentCons = Var(mTEPES.psnh, within=UnitInterval, doc='consumption commitment of the unit [0,1]')
|
|
1866
|
+
|
|
1710
1867
|
else:
|
|
1711
1868
|
OptModel.vCommitment = Var(mTEPES.psnnr, within=Binary, initialize=0 , doc='commitment of the unit {0,1}')
|
|
1712
1869
|
OptModel.vStartUp = Var(mTEPES.psnnr, within=Binary, initialize=0 , doc='startup of the unit {0,1}')
|
|
1713
1870
|
OptModel.vShutDown = Var(mTEPES.psnnr, within=Binary, initialize=0 , doc='shutdown of the unit {0,1}')
|
|
1714
|
-
OptModel.vMaxCommitment = Var(mTEPES.psnr , within=Binary, initialize=0 , doc='maximum commitment of the unit {0,1}')
|
|
1715
1871
|
OptModel.vStableState = Var(mTEPES.psnnr, within=Binary, initialize=0 , doc='stable state of the unit {0,1}')
|
|
1716
1872
|
OptModel.vRampUpState = Var(mTEPES.psnnr, within=Binary, initialize=0 , doc='ramp up state of the unit {0,1}')
|
|
1717
1873
|
OptModel.vRampDwState = Var(mTEPES.psnnr, within=Binary, initialize=0 , doc='ramp down state of the unit {0,1}')
|
|
1718
1874
|
|
|
1875
|
+
OptModel.vMaxCommitmentYearly = Var(mTEPES.psnr ,mTEPES.ExclusiveGroupsYearly, within=Binary, initialize=0.0, doc='maximum commitment of the unit yearly [0,1]')
|
|
1876
|
+
OptModel.vMaxCommitmentHourly = Var(mTEPES.psnnr,mTEPES.ExclusiveGroupsHourly, within=Binary, initialize=0.0, doc='maximum commitment of the unit hourly [0,1]')
|
|
1877
|
+
if mTEPES.pIndHydroTopology == 1:
|
|
1878
|
+
OptModel.vCommitmentCons = Var(mTEPES.psnh, within=Binary, doc='consumption commitment of the unit {0,1}')
|
|
1879
|
+
|
|
1880
|
+
|
|
1719
1881
|
if mTEPES.pIndBinLineCommit() == 0:
|
|
1720
|
-
OptModel.vLineCommit = Var(mTEPES.psnla, within=UnitInterval,
|
|
1882
|
+
OptModel.vLineCommit = Var(mTEPES.psnla, within=UnitInterval, doc='line switching of the electric line [0,1]')
|
|
1721
1883
|
else:
|
|
1722
|
-
OptModel.vLineCommit = Var(mTEPES.psnla, within=Binary,
|
|
1884
|
+
OptModel.vLineCommit = Var(mTEPES.psnla, within=Binary, doc='line switching of the electric line {0,1}')
|
|
1723
1885
|
|
|
1724
1886
|
if sum(mTEPES.pIndBinLineSwitch[:,:,:]):
|
|
1725
1887
|
if mTEPES.pIndBinSingleNode() == 0 and mTEPES.pIndBinLineCommit() == 0:
|
|
1726
|
-
OptModel.vLineOnState = Var(mTEPES.psnla, within=UnitInterval,
|
|
1727
|
-
OptModel.vLineOffState = Var(mTEPES.psnla, within=UnitInterval,
|
|
1888
|
+
OptModel.vLineOnState = Var(mTEPES.psnla, within=UnitInterval, doc='switching on state of the electric line [0,1]')
|
|
1889
|
+
OptModel.vLineOffState = Var(mTEPES.psnla, within=UnitInterval, doc='switching off state of the electric line [0,1]')
|
|
1728
1890
|
else:
|
|
1729
|
-
OptModel.vLineOnState = Var(mTEPES.psnla, within=Binary,
|
|
1730
|
-
OptModel.vLineOffState = Var(mTEPES.psnla, within=Binary,
|
|
1891
|
+
OptModel.vLineOnState = Var(mTEPES.psnla, within=Binary, doc='switching on state of the electric line {0,1}')
|
|
1892
|
+
OptModel.vLineOffState = Var(mTEPES.psnla, within=Binary, doc='switching off state of the electric line {0,1}')
|
|
1731
1893
|
|
|
1732
1894
|
CreateVariables(mTEPES, mTEPES)
|
|
1733
1895
|
# assign lower and upper bounds to variables
|
|
@@ -1735,7 +1897,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1735
1897
|
'''
|
|
1736
1898
|
Set upper/lower bounds.
|
|
1737
1899
|
|
|
1738
|
-
This function takes
|
|
1900
|
+
This function takes an mTEPES instance and adds lower/upper bounds to variables which are limited by a parameter.
|
|
1739
1901
|
|
|
1740
1902
|
Parameters:
|
|
1741
1903
|
mTEPES: The instance of mTEPES
|
|
@@ -1773,16 +1935,16 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1773
1935
|
nFixedVariables = 0
|
|
1774
1936
|
def RelaxBinaryInvestmentConditions(mTEPES, OptModel) -> int:
|
|
1775
1937
|
'''
|
|
1776
|
-
Relax binary
|
|
1938
|
+
Relax binary investment variables.
|
|
1777
1939
|
|
|
1778
|
-
This function takes
|
|
1940
|
+
This function takes an mTEPES instance, relaxes binary investment conditions and calculates the number of variables fixed in the process.
|
|
1779
1941
|
|
|
1780
1942
|
Parameters:
|
|
1781
1943
|
mTEPES: The instance of mTEPES.
|
|
1782
1944
|
OptModel:
|
|
1783
1945
|
|
|
1784
1946
|
Returns:
|
|
1785
|
-
int: The
|
|
1947
|
+
int: The number of fixed variables.
|
|
1786
1948
|
'''
|
|
1787
1949
|
|
|
1788
1950
|
nFixedBinaries = 0
|
|
@@ -1844,9 +2006,20 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1844
2006
|
OptModel.vStartUp [p,sc,n,nr].domain = UnitInterval
|
|
1845
2007
|
OptModel.vShutDown [p,sc,n,nr].domain = UnitInterval
|
|
1846
2008
|
|
|
1847
|
-
for p,sc,
|
|
1848
|
-
if mTEPES.pIndBinUnitCommit[nr] == 0:
|
|
1849
|
-
OptModel.
|
|
2009
|
+
for p,sc,nr, group in mTEPES.psnr * mTEPES.ExclusiveGroups:
|
|
2010
|
+
if mTEPES.pIndBinUnitCommit[nr] == 0 and nr in mTEPES.ExclusiveGeneratorsYearly:
|
|
2011
|
+
OptModel.vMaxCommitmentYearly[p,sc, nr,group].domain = UnitInterval
|
|
2012
|
+
for p,sc,n,nr,group in mTEPES.psnnr * mTEPES.ExclusiveGroups:
|
|
2013
|
+
if mTEPES.pIndBinUnitCommit[nr] == 0 and nr in mTEPES.ExclusiveGeneratorsHourly:
|
|
2014
|
+
OptModel.vMaxCommitmentHourly[p,sc,n,nr,group].domain = UnitInterval
|
|
2015
|
+
if mTEPES.pIndHydroTopology == 1:
|
|
2016
|
+
for p,sc,n,h in mTEPES.psnh:
|
|
2017
|
+
if mTEPES.pIndBinUnitCommit[h] == 0:
|
|
2018
|
+
OptModel.vCommitmentCons[p,sc,n,h].domain = UnitInterval
|
|
2019
|
+
if mTEPES.pMaxCharge[p,sc,n,h] == 0:
|
|
2020
|
+
OptModel.vCommitmentCons[p,sc,n,h].fix(0)
|
|
2021
|
+
nFixedBinaries += 1
|
|
2022
|
+
|
|
1850
2023
|
return nFixedBinaries
|
|
1851
2024
|
|
|
1852
2025
|
#Call the relaxing variables function and add its output to nFixedVariables
|
|
@@ -1856,7 +2029,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1856
2029
|
def CreateFlowVariables(mTEPES,OptModel) -> int:
|
|
1857
2030
|
#TODO use a more descriptive name for nFixedVariables
|
|
1858
2031
|
'''
|
|
1859
|
-
Create electricity, hydrogen and heat
|
|
2032
|
+
Create electricity, hydrogen and heat-flow-related variables.
|
|
1860
2033
|
|
|
1861
2034
|
This function takes a mTEPES instance and adds the variables necessary to model power, hydrogen and heat flows in a network.
|
|
1862
2035
|
|
|
@@ -1865,7 +2038,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1865
2038
|
OptModel:
|
|
1866
2039
|
|
|
1867
2040
|
Returns:
|
|
1868
|
-
int: The
|
|
2041
|
+
int: The number of line commitment variables fixed
|
|
1869
2042
|
'''
|
|
1870
2043
|
nFixedVariables = 0
|
|
1871
2044
|
# existing lines are always committed if no switching decision is modeled
|
|
@@ -1882,12 +2055,15 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1882
2055
|
OptModel.vFlowElec = Var(mTEPES.psnla, within=Reals, doc='electric flow [GW]')
|
|
1883
2056
|
OptModel.vTheta = Var(mTEPES.psnnd, within=Reals, doc='voltage angle [rad]')
|
|
1884
2057
|
|
|
2058
|
+
if mTEPES.pIndPTDF == 1:
|
|
2059
|
+
OptModel.vNetPosition = Var(mTEPES.psnnd, within=Reals, doc='net position in node [GW]')
|
|
2060
|
+
|
|
1885
2061
|
[OptModel.vLineLosses[p,sc,n,ni,nf,cc].setub(0.5*mTEPES.pLineLossFactor[ni,nf,cc]*mTEPES.pLineNTCMax[ni,nf,cc]) for p,sc,n,ni,nf,cc in mTEPES.psnll]
|
|
1886
2062
|
if mTEPES.pIndBinSingleNode() == 0:
|
|
1887
|
-
[OptModel.vFlowElec[p,sc,n,ni,nf,cc].setlb(-mTEPES.
|
|
1888
|
-
[OptModel.vFlowElec[p,sc,n,ni,nf,cc].setub( mTEPES.
|
|
1889
|
-
[OptModel.vTheta [p,sc,n,nd ].setlb(-mTEPES.pMaxTheta
|
|
1890
|
-
[OptModel.vTheta [p,sc,n,nd ].setub( mTEPES.pMaxTheta
|
|
2063
|
+
[OptModel.vFlowElec[p,sc,n,ni,nf,cc].setlb(-mTEPES.pMaxNTCBck[p,sc,n,ni,nf,cc] ) for p,sc,n,ni,nf,cc in mTEPES.psnla]
|
|
2064
|
+
[OptModel.vFlowElec[p,sc,n,ni,nf,cc].setub( mTEPES.pMaxNTCFrw[p,sc,n,ni,nf,cc] ) for p,sc,n,ni,nf,cc in mTEPES.psnla]
|
|
2065
|
+
[OptModel.vTheta [p,sc,n,nd ].setlb(-mTEPES.pMaxTheta [p,sc,n,nd ]() ) for p,sc,n,nd in mTEPES.psnnd]
|
|
2066
|
+
[OptModel.vTheta [p,sc,n,nd ].setub( mTEPES.pMaxTheta [p,sc,n,nd ]() ) for p,sc,n,nd in mTEPES.psnnd]
|
|
1891
2067
|
|
|
1892
2068
|
if mTEPES.pIndHydrogen == 1:
|
|
1893
2069
|
OptModel.vFlowH2 = Var(mTEPES.psnpa, within=Reals, doc='pipeline flow [tH2]')
|
|
@@ -1911,14 +2087,14 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1911
2087
|
'''
|
|
1912
2088
|
Fix commitment variables.
|
|
1913
2089
|
|
|
1914
|
-
This function takes
|
|
2090
|
+
This function takes an mTEPES instance and fixes commitment-related variables for must run units, ESS units and units with no minimum power.
|
|
1915
2091
|
|
|
1916
2092
|
Parameters:
|
|
1917
2093
|
mTEPES: The instance of mTEPES.
|
|
1918
2094
|
OptModel:
|
|
1919
2095
|
|
|
1920
2096
|
Returns:
|
|
1921
|
-
int: The
|
|
2097
|
+
int: The number of commitment variables fixed.
|
|
1922
2098
|
'''
|
|
1923
2099
|
nFixedVariables = 0
|
|
1924
2100
|
|
|
@@ -1930,32 +2106,31 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1930
2106
|
nFixedVariables += sum( 1 for p,sc,n,g in mTEPES.psng if mTEPES.pMaxPowerElec[p,sc,n,g] == 0.0)
|
|
1931
2107
|
|
|
1932
2108
|
for p,sc,n,nr in mTEPES.psnnr:
|
|
1933
|
-
# must run units or units with no minimum power or ESS existing units are always committed and must produce at least their minimum output
|
|
2109
|
+
# must run units or units with no minimum power, or ESS existing units are always committed and must produce at least their minimum output
|
|
1934
2110
|
# not applicable to mutually exclusive units
|
|
1935
|
-
if len(mTEPES.
|
|
1936
|
-
|
|
1937
|
-
nFixedVariables += 1/len(mTEPES.n)
|
|
1938
|
-
if (mTEPES.pMustRun[nr] == 1 or (mTEPES.pMinPowerElec[p,sc,n,nr] == 0.0 and mTEPES.pRatedConstantVarCost[nr] == 0.0) or nr in mTEPES.es) and nr not in mTEPES.ec:
|
|
2111
|
+
if len(mTEPES.ExclusiveGroups) == 0:
|
|
2112
|
+
if (mTEPES.pMustRun[nr] == 1 or (mTEPES.pMinPowerElec[p,sc,n,nr] == 0.0 and mTEPES.pRatedConstantVarCost[nr] == 0.0) or nr in mTEPES.es ) and nr not in mTEPES.ec and nr not in mTEPES.h:
|
|
1939
2113
|
OptModel.vCommitment [p,sc,n,nr].fix(1)
|
|
1940
2114
|
OptModel.vStartUp [p,sc,n,nr].fix(0)
|
|
1941
2115
|
OptModel.vShutDown [p,sc,n,nr].fix(0)
|
|
1942
2116
|
nFixedVariables += 3
|
|
1943
|
-
|
|
1944
|
-
|
|
2117
|
+
# If there are mutually exclusive groups do not fix variables from ESS in mutually exclusive groups
|
|
2118
|
+
elif len(mTEPES.ExclusiveGroups) > 0 and nr not in mTEPES.ExclusiveGenerators:
|
|
2119
|
+
if (mTEPES.pMustRun[nr] == 1 or (mTEPES.pMinPowerElec[p,sc,n,nr] == 0.0 and mTEPES.pRatedConstantVarCost[nr] == 0.0) or nr in mTEPES.es) and nr not in mTEPES.ec and nr not in mTEPES.h:
|
|
1945
2120
|
OptModel.vCommitment [p,sc,n,nr].fix(1)
|
|
1946
2121
|
OptModel.vStartUp [p,sc,n,nr].fix(0)
|
|
1947
2122
|
OptModel.vShutDown [p,sc,n,nr].fix(0)
|
|
1948
|
-
|
|
1949
|
-
|
|
2123
|
+
nFixedVariables += 3
|
|
2124
|
+
|
|
1950
2125
|
# if min and max power coincide there are neither second block, nor operating reserve
|
|
1951
|
-
if mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0:
|
|
1952
|
-
OptModel.vOutput2ndBlock[p,sc,n,nr].fix(0.0)
|
|
1953
|
-
OptModel.vReserveUp
|
|
1954
|
-
OptModel.vReserveDown
|
|
2126
|
+
if mTEPES.pMaxPower2ndBlock [p,sc,n,nr] == 0.0:
|
|
2127
|
+
OptModel.vOutput2ndBlock [p,sc,n,nr].fix(0.0)
|
|
2128
|
+
OptModel.vReserveUp [p,sc,n,nr].fix(0.0)
|
|
2129
|
+
OptModel.vReserveDown [p,sc,n,nr].fix(0.0)
|
|
1955
2130
|
nFixedVariables += 3
|
|
1956
|
-
if mTEPES.
|
|
1957
|
-
OptModel.vReserveUp
|
|
1958
|
-
OptModel.vReserveDown
|
|
2131
|
+
if mTEPES.pIndOperReserveGen[ nr] == 1:
|
|
2132
|
+
OptModel.vReserveUp [p,sc,n,nr].fix(0.0)
|
|
2133
|
+
OptModel.vReserveDown [p,sc,n,nr].fix(0.0)
|
|
1959
2134
|
nFixedVariables += 2
|
|
1960
2135
|
|
|
1961
2136
|
# total energy inflows per storage
|
|
@@ -1981,7 +2156,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1981
2156
|
if mTEPES.pMaxCharge2ndBlock[p,sc,n,es] == 0.0:
|
|
1982
2157
|
OptModel.vCharge2ndBlock [p,sc,n,es].fix(0.0)
|
|
1983
2158
|
nFixedVariables += 1
|
|
1984
|
-
if mTEPES.pMaxCharge2ndBlock[p,sc,n,es] == 0.0 or mTEPES.
|
|
2159
|
+
if mTEPES.pMaxCharge2ndBlock[p,sc,n,es] == 0.0 or mTEPES.pIndOperReserveCon[es] == 1:
|
|
1985
2160
|
OptModel.vESSReserveUp [p,sc,n,es].fix(0.0)
|
|
1986
2161
|
OptModel.vESSReserveDown [p,sc,n,es].fix(0.0)
|
|
1987
2162
|
nFixedVariables += 2
|
|
@@ -1994,11 +2169,12 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
1994
2169
|
# ESS with no charge capacity or not storage capacity can't charge
|
|
1995
2170
|
if mTEPES.pMaxCharge [p,sc,n,h ] == 0.0:
|
|
1996
2171
|
OptModel.vESSTotalCharge [p,sc,n,h ].fix(0.0)
|
|
1997
|
-
|
|
2172
|
+
OptModel.vCommitmentCons [p,sc,n,h ].fix(0.0)
|
|
2173
|
+
nFixedVariables += 2
|
|
1998
2174
|
if mTEPES.pMaxCharge2ndBlock[p,sc,n,h ] == 0.0:
|
|
1999
2175
|
OptModel.vCharge2ndBlock [p,sc,n,h ].fix(0.0)
|
|
2000
2176
|
nFixedVariables += 1
|
|
2001
|
-
if mTEPES.pMaxCharge2ndBlock[p,sc,n,h ] == 0.0 or mTEPES.
|
|
2177
|
+
if mTEPES.pMaxCharge2ndBlock[p,sc,n,h ] == 0.0 or mTEPES.pIndOperReserveCon[h ] == 1:
|
|
2002
2178
|
OptModel.vESSReserveUp [p,sc,n,h ].fix(0.0)
|
|
2003
2179
|
OptModel.vESSReserveDown [p,sc,n,h ].fix(0.0)
|
|
2004
2180
|
nFixedVariables += 2
|
|
@@ -2006,7 +2182,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2006
2182
|
nFixedGeneratorCommits = FixGeneratorsCommitment(mTEPES,mTEPES)
|
|
2007
2183
|
nFixedVariables += nFixedGeneratorCommits
|
|
2008
2184
|
# thermal, ESS, and RES units ordered by increasing variable operation cost, excluding reactive generating units
|
|
2009
|
-
if
|
|
2185
|
+
if mTEPES.tq:
|
|
2010
2186
|
mTEPES.go = Set(initialize=[g for g in sorted(mTEPES.pRatedLinearVarCost, key=mTEPES.pRatedLinearVarCost.__getitem__) if g not in mTEPES.sq])
|
|
2011
2187
|
else:
|
|
2012
2188
|
if mTEPES.pIndHydroTopology == 1:
|
|
@@ -2026,29 +2202,29 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2026
2202
|
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)])
|
|
2027
2203
|
mTEPES.n = Set(doc='load levels', initialize=[nn for nn in mTEPES.nn if (p,sc,st,nn) in mTEPES.s2n ])
|
|
2028
2204
|
|
|
2029
|
-
if
|
|
2205
|
+
if mTEPES.n:
|
|
2030
2206
|
# determine the first load level of each stage
|
|
2031
2207
|
n1 = (p,sc,mTEPES.n.first())
|
|
2032
2208
|
# commit the units of each area and their output at the first load level of each stage
|
|
2033
2209
|
for ar in mTEPES.ar:
|
|
2034
2210
|
pSystemOutput = 0.0
|
|
2035
2211
|
for nr in mTEPES.nr:
|
|
2036
|
-
if nr in g2a[ar] and pSystemOutput < sum(mTEPES.pDemandElec[n1,nd] for nd in mTEPES.nd if (nd,ar) in mTEPES.ndar) and mTEPES.pMustRun[nr] == 1:
|
|
2037
|
-
mTEPES.pInitialOutput[n1,nr] = mTEPES.pMaxPowerElec[n1,nr]
|
|
2212
|
+
if nr in g2a[ar] and (p,nr) in mTEPES.pnr and pSystemOutput < sum(mTEPES.pDemandElec[n1,nd] for nd in mTEPES.nd if (nd,ar) in mTEPES.ndar) and mTEPES.pMustRun[nr] == 1:
|
|
2213
|
+
mTEPES.pInitialOutput[n1,nr] = mTEPES.pMaxPowerElec [n1,nr]
|
|
2038
2214
|
mTEPES.pInitialUC [n1,nr] = 1
|
|
2039
2215
|
pSystemOutput += mTEPES.pInitialOutput[n1,nr]()
|
|
2040
2216
|
|
|
2041
|
-
# determine the
|
|
2217
|
+
# determine the initially committed units and their output at the first load level of each period, scenario, and stage
|
|
2042
2218
|
for go in mTEPES.go:
|
|
2043
|
-
if go in g2a[ar] and pSystemOutput < sum(mTEPES.pDemandElec[n1,nd] for nd in mTEPES.nd if (nd,ar) in mTEPES.ndar) and mTEPES.pMustRun[go] == 0:
|
|
2219
|
+
if go in g2a[ar] and (p,go) in mTEPES.pg and pSystemOutput < sum(mTEPES.pDemandElec[n1,nd] for nd in mTEPES.nd if (nd,ar) in mTEPES.ndar) and mTEPES.pMustRun[go] == 0:
|
|
2044
2220
|
if go in mTEPES.re:
|
|
2045
|
-
mTEPES.pInitialOutput[n1,go] = mTEPES.pMaxPowerElec[n1,go]
|
|
2221
|
+
mTEPES.pInitialOutput[n1,go] = mTEPES.pMaxPowerElec [n1,go]
|
|
2046
2222
|
else:
|
|
2047
|
-
mTEPES.pInitialOutput[n1,go] = mTEPES.pMinPowerElec[n1,go]
|
|
2223
|
+
mTEPES.pInitialOutput[n1,go] = mTEPES.pMinPowerElec [n1,go]
|
|
2048
2224
|
mTEPES.pInitialUC[n1,go] = 1
|
|
2049
2225
|
pSystemOutput += mTEPES.pInitialOutput[n1,go]()
|
|
2050
2226
|
|
|
2051
|
-
# determine the
|
|
2227
|
+
# determine the initially committed lines
|
|
2052
2228
|
for la in mTEPES.la:
|
|
2053
2229
|
if la in mTEPES.lc:
|
|
2054
2230
|
mTEPES.pInitialSwitch[n1,la] = 0
|
|
@@ -2057,7 +2233,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2057
2233
|
|
|
2058
2234
|
# fixing the ESS inventory at the last load level of the stage for every period and scenario if between storage limits
|
|
2059
2235
|
for es in mTEPES.es:
|
|
2060
|
-
if es not in mTEPES.ec:
|
|
2236
|
+
if es not in mTEPES.ec and (p,es) in mTEPES.pes:
|
|
2061
2237
|
OptModel.vESSInventory[p,sc,mTEPES.n.last(),es].fix(mTEPES.pIniInventory[p,sc,mTEPES.n.last(),es])
|
|
2062
2238
|
|
|
2063
2239
|
if mTEPES.pIndHydroTopology == 1:
|
|
@@ -2112,7 +2288,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2112
2288
|
OptModel.vEnergyInflows [p,sc,n,ec].fix (0.0)
|
|
2113
2289
|
nFixedVariables += 1
|
|
2114
2290
|
|
|
2115
|
-
# if no operating reserve is required no variables are needed
|
|
2291
|
+
# if no operating reserve is required, no variables are needed
|
|
2116
2292
|
for p,sc,n,ar,nr in mTEPES.psnar*mTEPES.nr:
|
|
2117
2293
|
if (ar,nr) in mTEPES.a2g and (p,nr) in mTEPES.pnr:
|
|
2118
2294
|
if mTEPES.pOperReserveUp [p,sc,n,ar] == 0.0:
|
|
@@ -2130,7 +2306,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2130
2306
|
OptModel.vESSReserveDown[p,sc,n,es].fix(0.0)
|
|
2131
2307
|
nFixedVariables += 1
|
|
2132
2308
|
|
|
2133
|
-
# if there are no energy outflows no variable is needed
|
|
2309
|
+
# if there are no energy outflows, no variable is needed
|
|
2134
2310
|
for es in mTEPES.es:
|
|
2135
2311
|
if sum(mTEPES.pEnergyOutflows[p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) == 0.0:
|
|
2136
2312
|
for p,sc,n in mTEPES.psn:
|
|
@@ -2138,6 +2314,15 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2138
2314
|
OptModel.vEnergyOutflows[p,sc,n,es].fix(0.0)
|
|
2139
2315
|
nFixedVariables += 1
|
|
2140
2316
|
|
|
2317
|
+
if mTEPES.pIndHydroTopology == 1:
|
|
2318
|
+
# if there are no hydro outflows, no variable is needed
|
|
2319
|
+
for rs in mTEPES.rs:
|
|
2320
|
+
if sum(mTEPES.pHydroOutflows[p,sc,n,rs]() for p,sc,n in mTEPES.psn if (p,rs) in mTEPES.prs) == 0.0:
|
|
2321
|
+
for p,sc,n in mTEPES.psn:
|
|
2322
|
+
if (p,rs) in mTEPES.prs:
|
|
2323
|
+
OptModel.vHydroOutflows[p,sc,n,rs].fix(0.0)
|
|
2324
|
+
nFixedVariables += 1
|
|
2325
|
+
|
|
2141
2326
|
# fixing the voltage angle of the reference node for each scenario, period, and load level
|
|
2142
2327
|
if mTEPES.pIndBinSingleNode() == 0:
|
|
2143
2328
|
for p,sc,n in mTEPES.psn:
|
|
@@ -2168,14 +2353,14 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2168
2353
|
'''
|
|
2169
2354
|
Fix installations/retirements forbidden by period.
|
|
2170
2355
|
|
|
2171
|
-
This function takes
|
|
2356
|
+
This function takes an mTEPES instance and fixes all installation and retirement variables to 0 if they are not allowed in the corresponding period.
|
|
2172
2357
|
|
|
2173
2358
|
Parameters:
|
|
2174
2359
|
mTEPES: The instance of mTEPES.
|
|
2175
2360
|
OptModel:
|
|
2176
2361
|
|
|
2177
2362
|
Returns:
|
|
2178
|
-
int: The
|
|
2363
|
+
int: The number of variables fixed.
|
|
2179
2364
|
'''
|
|
2180
2365
|
nFixedVariables = 0
|
|
2181
2366
|
# do not install/retire power plants and lines if not allowed in this period
|
|
@@ -2273,7 +2458,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2273
2458
|
if mTEPES.pIndHydroTopology == 1:
|
|
2274
2459
|
for p,sc,n,rs in mTEPES.psnrs:
|
|
2275
2460
|
if rs not in mTEPES.rn and mTEPES.pRsrPeriodIni[rs] > p:
|
|
2276
|
-
OptModel.
|
|
2461
|
+
OptModel.vHydroOutflows [p,sc,n,rs].fix(0.0)
|
|
2277
2462
|
OptModel.vReservoirVolume [p,sc,n,rs].fix(0.0)
|
|
2278
2463
|
OptModel.vReservoirSpillage [p,sc,n,rs].fix(0.0)
|
|
2279
2464
|
mTEPES.pIniVolume [p,sc,n,rs] = 0.0
|
|
@@ -2295,18 +2480,18 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2295
2480
|
[OptModel.vLineCommit [p,sc,n,ni,nf,cc].fix(0 ) for p,sc,n,ni,nf,cc in mTEPES.psnla if (ni,nf,cc) not in mTEPES.lc and mTEPES.pElecNetPeriodIni [ni,nf,cc] > p]
|
|
2296
2481
|
[OptModel.vLineOnState [p,sc,n,ni,nf,cc].fix(0 ) for p,sc,n,ni,nf,cc in mTEPES.psnla if (ni,nf,cc) not in mTEPES.lc and mTEPES.pElecNetPeriodIni [ni,nf,cc] > p]
|
|
2297
2482
|
[OptModel.vLineOffState[p,sc,n,ni,nf,cc].fix(0 ) for p,sc,n,ni,nf,cc in mTEPES.psnla if (ni,nf,cc) not in mTEPES.lc and mTEPES.pElecNetPeriodIni [ni,nf,cc] > p]
|
|
2298
|
-
nFixedVariables += sum(
|
|
2483
|
+
nFixedVariables += sum( 4 for p,sc,n,ni,nf,cc in mTEPES.psnla if (ni,nf,cc) not in mTEPES.lc and mTEPES.pElecNetPeriodIni [ni,nf,cc] > p)
|
|
2299
2484
|
|
|
2300
2485
|
[OptModel.vLineLosses [p,sc,n,ni,nf,cc].fix(0.0) for p,sc,n,ni,nf,cc in mTEPES.psnll if (ni,nf,cc) not in mTEPES.lc and mTEPES.pElecNetPeriodIni [ni,nf,cc] > p]
|
|
2301
|
-
nFixedVariables += sum(
|
|
2486
|
+
nFixedVariables += sum( 1 for p,sc,n,ni,nf,cc in mTEPES.psnll if (ni,nf,cc) not in mTEPES.lc and mTEPES.pElecNetPeriodIni [ni,nf,cc] > p)
|
|
2302
2487
|
|
|
2303
2488
|
if mTEPES.pIndHydrogen == 1:
|
|
2304
|
-
[OptModel.vFlowH2 [p,sc,n,ni,nf,cc].fix(0.0) for p,sc,n,ni,nf,cc in mTEPES.psnpa if (ni,nf,cc) not in mTEPES.pc and mTEPES.pH2PipePeriodIni[ni,nf,cc] > p]
|
|
2305
|
-
nFixedVariables += sum(
|
|
2489
|
+
[OptModel.vFlowH2 [p,sc,n,ni,nf,cc].fix(0.0) for p,sc,n,ni,nf,cc in mTEPES.psnpa if (ni,nf,cc) not in mTEPES.pc and mTEPES.pH2PipePeriodIni [ni,nf,cc] > p]
|
|
2490
|
+
nFixedVariables += sum( 4 for p,sc,n,ni,nf,cc in mTEPES.psnpa if (ni,nf,cc) not in mTEPES.pc and mTEPES.pH2PipePeriodIni [ni,nf,cc] > p)
|
|
2306
2491
|
|
|
2307
2492
|
if mTEPES.pIndHeat == 1:
|
|
2308
2493
|
[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]
|
|
2309
|
-
nFixedVariables += sum(
|
|
2494
|
+
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)
|
|
2310
2495
|
|
|
2311
2496
|
# tolerance to consider 0 an investment decision
|
|
2312
2497
|
pEpsilon = 1e-4
|
|
@@ -2314,7 +2499,7 @@ def SettingUpVariables(OptModel, mTEPES):
|
|
|
2314
2499
|
'''
|
|
2315
2500
|
Set small numbers to 0.
|
|
2316
2501
|
|
|
2317
|
-
This function takes
|
|
2502
|
+
This function takes an mTEPES instance and sets values under a certain threshold to be 0.
|
|
2318
2503
|
|
|
2319
2504
|
Parameters:
|
|
2320
2505
|
mTEPES: The instance of mTEPES.
|