openTEPES 4.18.7__py3-none-any.whl → 4.18.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,7 @@
1
1
  """
2
- Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - November 22, 2025
2
+ Open Generation, Storage, and Transmission Operation and Expansion Planning Model with RES and ESS (openTEPES) - January 19, 2026
3
3
  """
4
4
 
5
- import datetime
6
5
  import time
7
6
  import math
8
7
  import os
@@ -11,6 +10,10 @@ from collections import defaultdict
11
10
  from pyomo.environ import DataPortal, Set, Param, Var, Binary, NonNegativeReals, NonNegativeIntegers, PositiveReals, PositiveIntegers, Reals, UnitInterval, Any
12
11
  from pyomo.environ import Block, Boolean
13
12
 
13
+ # from line_profiler import profile
14
+
15
+
16
+ # @profile
14
17
  def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
15
18
  print('Input data ****')
16
19
 
@@ -49,6 +52,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
49
52
  'VariablePTDF' : [0, 1, 2, 3],
50
53
  }
51
54
  FLAG_MAPPING = {
55
+ 'RampReserveUp' : ('pIndRampReserves', None, 'No ramp reserves' ),
56
+ 'RampReserveDown' : ('pIndRampReserves', None, 'No ramp reserves' ),
52
57
  'VariableTTCFrw' : ('pIndVarTTC' , None, 'No variable transmission line TTCs' ),
53
58
  'VariableTTCBck' : ('pIndVarTTC' , None, 'No variable transmission line TTCs' ),
54
59
  'VariablePTDF' : ('pIndPTDF' , None, 'No flow-based market coupling method'),
@@ -122,8 +127,8 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
122
127
  return dfs, par
123
128
 
124
129
  dfs, par = read_input_data(_path, CaseName)
125
- # if 'pIndVarTTC', 'pIndPTDF', 'pIndHydroTopology', 'pIndHydrogen', 'pIndHeat' not in par include them and set value to zero
126
- for key in ['pIndVarTTC', 'pIndPTDF', 'pIndHydroTopology', 'pIndHydrogen', 'pIndHeat']:
130
+ # if 'pIndRampReserves', 'pIndVarTTC', 'pIndPTDF', 'pIndHydroTopology', 'pIndHydrogen', 'pIndHeat' not in par include them and set value to zero
131
+ for key in ['pIndRampReserves', 'pIndVarTTC', 'pIndPTDF', 'pIndHydroTopology', 'pIndHydrogen', 'pIndHeat']:
127
132
  if key not in par.keys():
128
133
  par[key] = 0
129
134
 
@@ -149,7 +154,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
149
154
  'VariableFuelCost',
150
155
  'VariableEmissionCost',]
151
156
  mTEPES.node_frames_suffixes = ['Demand', 'Inertia']
152
- mTEPES.area_frames_suffixes = ['OperatingReserveUp', 'OperatingReserveDown', 'ReserveMargin', 'Emission', 'RESEnergy']
157
+ mTEPES.area_frames_suffixes = ['RampReserveUp', 'RampReserveDown', 'OperatingReserveUp', 'OperatingReserveDown', 'ReserveMargin', 'Emission', 'RESEnergy']
153
158
  mTEPES.hydro_frames_suffixes = ['Reservoir', 'VariableMinVolume', 'VariableMaxVolume', 'HydroInflows', 'HydroOutflows']
154
159
  mTEPES.hydrogen_frames_suffixes = ['DemandHydrogen']
155
160
  mTEPES.heat_frames_suffixes = ['DemandHeat', 'ReserveMarginHeat']
@@ -238,22 +243,26 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
238
243
  par['pReserveMargin'] = dfs['dfReserveMargin']['ReserveMargin' ] # minimum adequacy reserve margin [p.u.]
239
244
  par['pEmission'] = dfs['dfEmission'] ['CO2Emission' ] # maximum CO2 emission [MtCO2]
240
245
  par['pRESEnergy'] = dfs['dfRESEnergy'] ['RESEnergy' ] # minimum RES energy [GWh]
241
- par['pDemandElec'] = dfs['dfDemand'].reindex (columns=mTEPES.nd, fill_value=0.0) * 1e-3 # electric demand [GW]
242
- par['pSystemInertia'] = dfs['dfInertia'].reindex (columns=mTEPES.ar, fill_value=0.0) # inertia [s]
243
- par['pOperReserveUp'] = dfs['dfOperatingReserveUp'].reindex (columns=mTEPES.ar, fill_value=0.0) * 1e-3 # upward operating reserve [GW]
244
- par['pOperReserveDw'] = dfs['dfOperatingReserveDown'].reindex (columns=mTEPES.ar, fill_value=0.0) * 1e-3 # downward operating reserve [GW]
245
- par['pVariableMinPowerElec'] = dfs['dfVariableMinGeneration'].reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable minimum power [GW]
246
- par['pVariableMaxPowerElec'] = dfs['dfVariableMaxGeneration'].reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable maximum power [GW]
246
+ par['pDemandElec'] = dfs['dfDemand' ].reindex(columns=mTEPES.nd, fill_value=0.0) * 1e-3 # electric demand [GW]
247
+ par['pSystemInertia'] = dfs['dfInertia' ].reindex(columns=mTEPES.ar, fill_value=0.0) # inertia [s]
248
+ par['pOperReserveUp'] = dfs['dfOperatingReserveUp' ].reindex(columns=mTEPES.ar, fill_value=0.0) * 1e-3 # upward operating reserve [GW]
249
+ par['pOperReserveDw'] = dfs['dfOperatingReserveDown' ].reindex(columns=mTEPES.ar, fill_value=0.0) * 1e-3 # downward operating reserve [GW]
250
+ par['pVariableMinPowerElec'] = dfs['dfVariableMinGeneration' ].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable minimum power [GW]
251
+ par['pVariableMaxPowerElec'] = dfs['dfVariableMaxGeneration' ].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable maximum power [GW]
247
252
  par['pVariableMinCharge'] = dfs['dfVariableMinConsumption'].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable minimum charge [GW]
248
253
  par['pVariableMaxCharge'] = dfs['dfVariableMaxConsumption'].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable maximum charge [GW]
249
- par['pVariableMinStorage'] = dfs['dfVariableMinStorage'].reindex (columns=mTEPES.gg, fill_value=0.0) # dynamic variable minimum storage [GWh]
250
- par['pVariableMaxStorage'] = dfs['dfVariableMaxStorage'].reindex (columns=mTEPES.gg, fill_value=0.0) # dynamic variable maximum storage [GWh]
251
- par['pVariableMinEnergy'] = dfs['dfVariableMinEnergy'].reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable minimum energy [GW]
252
- par['pVariableMaxEnergy'] = dfs['dfVariableMaxEnergy'].reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable maximum energy [GW]
253
- par['pVariableFuelCost'] = dfs['dfVariableFuelCost'].reindex (columns=mTEPES.gg, fill_value=0.0) # dynamic variable fuel cost [EUR/MJ]
254
- par['pVariableEmissionCost'] = dfs['dfVariableEmissionCost'].reindex (columns=mTEPES.gg, fill_value=0.0) # dynamic variable emission cost [EUR/tCO2]
255
- par['pEnergyInflows'] = dfs['dfEnergyInflows'].reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic energy inflows [GW]
256
- par['pEnergyOutflows'] = dfs['dfEnergyOutflows'].reindex (columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic energy outflows [GW]
254
+ par['pVariableMinStorage'] = dfs['dfVariableMinStorage' ].reindex(columns=mTEPES.gg, fill_value=0.0) # dynamic variable minimum storage [GWh]
255
+ par['pVariableMaxStorage'] = dfs['dfVariableMaxStorage' ].reindex(columns=mTEPES.gg, fill_value=0.0) # dynamic variable maximum storage [GWh]
256
+ par['pVariableMinEnergy'] = dfs['dfVariableMinEnergy' ].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable minimum energy [GW]
257
+ par['pVariableMaxEnergy'] = dfs['dfVariableMaxEnergy' ].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic variable maximum energy [GW]
258
+ par['pVariableFuelCost'] = dfs['dfVariableFuelCost' ].reindex(columns=mTEPES.gg, fill_value=0.0) # dynamic variable fuel cost [EUR/MJ]
259
+ par['pVariableEmissionCost'] = dfs['dfVariableEmissionCost' ].reindex(columns=mTEPES.gg, fill_value=0.0) # dynamic variable emission cost [EUR/tCO2]
260
+ par['pEnergyInflows'] = dfs['dfEnergyInflows' ].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic energy inflows [GW]
261
+ par['pEnergyOutflows'] = dfs['dfEnergyOutflows' ].reindex(columns=mTEPES.gg, fill_value=0.0) * 1e-3 # dynamic energy outflows [GW]
262
+
263
+ if par['pIndRampReserves'] == 1:
264
+ par['pRampReserveUp'] = dfs['dfRampReserveUp' ].reindex(columns=mTEPES.ar, fill_value=0.0) * 1e-3 # system ramp up reserves [GW/h]
265
+ par['pRampReserveDw'] = dfs['dfRampReserveDown' ].reindex(columns=mTEPES.ar, fill_value=0.0) * 1e-3 # system ramp down reserves [GW/h]
257
266
 
258
267
  if par['pIndVarTTC'] == 1:
259
268
  par['pVariableNTCFrw'] = dfs['dfVariableTTCFrw'] * 1e-3 # variable TTC forward [GW]
@@ -262,10 +271,10 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
262
271
  par['pVariablePTDF'] = dfs['dfVariablePTDF'] # variable PTDF [p.u.]
263
272
 
264
273
  if par['pIndHydroTopology'] == 1:
265
- par['pVariableMinVolume'] = dfs['dfVariableMinVolume'].reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic variable minimum reservoir volume [hm3]
266
- par['pVariableMaxVolume'] = dfs['dfVariableMaxVolume'].reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic variable maximum reservoir volume [hm3]
267
- par['pHydroInflows'] = dfs['dfHydroInflows'].reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic hydro inflows [m3/s]
268
- par['pHydroOutflows'] = dfs['dfHydroOutflows'].reindex (columns=mTEPES.rs, fill_value=0.0) # dynamic hydro outflows [m3/s]
274
+ par['pVariableMinVolume'] = dfs['dfVariableMinVolume'].reindex(columns=mTEPES.rs, fill_value=0.0) # dynamic variable minimum reservoir volume [hm3]
275
+ par['pVariableMaxVolume'] = dfs['dfVariableMaxVolume'].reindex(columns=mTEPES.rs, fill_value=0.0) # dynamic variable maximum reservoir volume [hm3]
276
+ par['pHydroInflows'] = dfs['dfHydroInflows' ].reindex(columns=mTEPES.rs, fill_value=0.0) # dynamic hydro inflows [m3/s]
277
+ par['pHydroOutflows'] = dfs['dfHydroOutflows' ].reindex(columns=mTEPES.rs, fill_value=0.0) # dynamic hydro outflows [m3/s]
269
278
 
270
279
  if par['pIndHydrogen'] == 1:
271
280
  par['pDemandH2'] = dfs['dfDemandHydrogen'] [mTEPES.nd] # hydrogen demand [tH2/h]
@@ -301,6 +310,10 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
301
310
  par['pEnergyInflows'] = ProcessParameter(par['pEnergyInflows'], par['pTimeStep'])
302
311
  par['pEnergyOutflows'] = ProcessParameter(par['pEnergyOutflows'], par['pTimeStep'])
303
312
 
313
+ if par['pIndRampReserves'] == 1:
314
+ par['pRampReserveUp'] = ProcessParameter(par['pRampReserveUp'], par['pTimeStep'])
315
+ par['pRampReserveDw'] = ProcessParameter(par['pRampReserveDw'], par['pTimeStep'])
316
+
304
317
  if par['pIndVarTTC'] == 1:
305
318
  par['pVariableNTCFrw'] = ProcessParameter(par['pVariableNTCFrw'], par['pTimeStep'])
306
319
  par['pVariableNTCBck'] = ProcessParameter(par['pVariableNTCBck'], par['pTimeStep'])
@@ -497,6 +510,7 @@ def InputData(DirName, CaseName, mTEPES, pIndLogConsole):
497
510
  print('Reading input data ... ', round(ReadingDataTime), 's')
498
511
 
499
512
 
513
+ # @profile
500
514
  def DataConfiguration(mTEPES):
501
515
 
502
516
  StartTime = time.time()
@@ -649,12 +663,11 @@ def DataConfiguration(mTEPES):
649
663
  mTEPES.plc = Set(initialize = [(p, ni,nf,cc) for p, ni,nf,cc in mTEPES.p *mTEPES.lc if (p,ni,nf,cc) in mTEPES.pla])
650
664
  mTEPES.pll = Set(initialize = [(p, ni,nf,cc) for p, ni,nf,cc in mTEPES.p *mTEPES.ll if (p,ni,nf,cc) in mTEPES.pla])
651
665
 
652
- mTEPES.psc = Set(initialize = [(p,sc ) for p,sc in mTEPES.p *mTEPES.sc])
653
666
  mTEPES.psg = Set(initialize = [(p,sc, g ) for p,sc, g in mTEPES.ps *mTEPES.g if (p,g ) in mTEPES.pg ])
654
667
  mTEPES.psnr = Set(initialize = [(p,sc, nr) for p,sc, nr in mTEPES.ps *mTEPES.nr if (p,nr) in mTEPES.pnr ])
655
668
  mTEPES.pses = Set(initialize = [(p,sc, es) for p,sc, es in mTEPES.ps *mTEPES.es if (p,es) in mTEPES.pes ])
656
669
  mTEPES.pseh = Set(initialize = [(p,sc, eh) for p,sc, eh in mTEPES.ps *mTEPES.eh if (p,eh) in mTEPES.peh ])
657
- mTEPES.psn = Set(initialize = [(p,sc,n ) for p,sc,n in mTEPES.ps *mTEPES.n ])
670
+ mTEPES.psn = Set(initialize = [(p,sc,n ) for p,sc,n in mTEPES.ps *mTEPES.n if mTEPES.dPar['pDuration'][p,sc,n]])
658
671
  mTEPES.psng = Set(initialize = [(p,sc,n,g ) for p,sc,n,g in mTEPES.psn*mTEPES.g if (p,g ) in mTEPES.pg ])
659
672
  mTEPES.psngc = Set(initialize = [(p,sc,n,gc) for p,sc,n,gc in mTEPES.psn*mTEPES.gc if (p,gc) in mTEPES.pgc ])
660
673
  mTEPES.psngb = Set(initialize = [(p,sc,n,gb) for p,sc,n,gb in mTEPES.psn*mTEPES.gb if (p,gb) in mTEPES.pgc ])
@@ -670,10 +683,10 @@ def DataConfiguration(mTEPES):
670
683
  mTEPES.psnnd = Set(initialize = [(p,sc,n,nd) for p,sc,n,nd in mTEPES.psn*mTEPES.nd ])
671
684
  mTEPES.psnar = Set(initialize = [(p,sc,n,ar) for p,sc,n,ar in mTEPES.psn*mTEPES.ar ])
672
685
 
673
- 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 ])
674
- 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 ])
675
- 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 ])
676
- 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 ])
686
+ 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 ])
687
+ 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 ])
688
+ 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 ])
689
+ 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 ])
677
690
 
678
691
  mTEPES.psnehc = Set(initialize = [(p,sc,n,eh) for p,sc,n,eh in mTEPES.psneh if mTEPES.dPar['pRatedMaxCharge'][eh] > 0.0 ])
679
692
 
@@ -998,20 +1011,20 @@ def DataConfiguration(mTEPES):
998
1011
  # initial inventory must be between minimum and maximum
999
1012
  for p,sc,n,es in mTEPES.psnes:
1000
1013
  if (p,sc,st,n) in mTEPES.s2n and mTEPES.n.ord(n) == mTEPES.dPar['pStorageTimeStep'][es]:
1001
- if mTEPES.dPar['pIniInventory'][es].loc[p,sc,n] < mTEPES.dPar['pMinStorage'][es].loc[p,sc,n]:
1002
- mTEPES.dPar['pIniInventory'][es].loc[p,sc,n] = mTEPES.dPar['pMinStorage'][es].loc[p,sc,n]
1003
- print('### Initial inventory lower than minimum storage ', p, sc, st, es)
1004
- if mTEPES.dPar['pIniInventory'][es].loc[p,sc,n] > mTEPES.dPar['pMaxStorage'][es].loc[p,sc,n]:
1005
- mTEPES.dPar['pIniInventory'][es].loc[p,sc,n] = mTEPES.dPar['pMaxStorage'][es].loc[p,sc,n]
1014
+ if mTEPES.dPar['pIniInventory'].at[(p,sc,n),es] < mTEPES.dPar['pMinStorage'].at[(p,sc,n),es]:
1015
+ mTEPES.dPar['pIniInventory'].at[(p,sc,n),es] = mTEPES.dPar['pMinStorage'].at[(p,sc,n),es]
1016
+ print('### Initial inventory lower than minimum storage ', p, sc, st, es)
1017
+ if mTEPES.dPar['pIniInventory'].at[(p,sc,n),es] > mTEPES.dPar['pMaxStorage'].at[(p,sc,n),es]:
1018
+ mTEPES.dPar['pIniInventory'].at[(p,sc,n),es] = mTEPES.dPar['pMaxStorage'].at[(p,sc,n),es]
1006
1019
  print('### Initial inventory greater than maximum storage ', p, sc, st, es)
1007
1020
  if mTEPES.dPar['pIndHydroTopology'] == 1:
1008
1021
  for p,sc,n,rs in mTEPES.psnrs:
1009
1022
  if (p,sc,st,n) in mTEPES.s2n and mTEPES.n.ord(n) == mTEPES.dPar['pReservoirTimeStep'][rs]:
1010
- if mTEPES.dPar['pIniVolume'][rs].loc[p,sc,n] < mTEPES.dPar['pMinVolume'][rs].loc[p,sc,n]:
1011
- mTEPES.dPar['pIniVolume'][rs].loc[p,sc,n] = mTEPES.dPar['pMinVolume'][rs].loc[p,sc,n]
1023
+ if mTEPES.dPar['pIniVolume'].at[(p,sc,n),rs] < mTEPES.dPar['pMinVolume'].at[(p,sc,n),rs]:
1024
+ mTEPES.dPar['pIniVolume'].at[(p,sc,n),rs] = mTEPES.dPar['pMinVolume'].at[(p,sc,n),rs]
1012
1025
  print('### Initial volume lower than minimum volume ', p, sc, st, rs)
1013
- if mTEPES.dPar['pIniVolume'][rs].loc[p,sc,n] > mTEPES.dPar['pMaxVolume'][rs].loc[p,sc,n]:
1014
- mTEPES.dPar['pIniVolume'][rs].loc[p,sc,n] = mTEPES.dPar['pMaxVolume'][rs].loc[p,sc,n]
1026
+ if mTEPES.dPar['pIniVolume'].at[(p,sc,n),rs] > mTEPES.dPar['pMaxVolume'].at[(p,sc,n),rs]:
1027
+ mTEPES.dPar['pIniVolume'].at[(p,sc,n),rs] = mTEPES.dPar['pMaxVolume'].at[(p,sc,n),rs]
1015
1028
  print('### Initial volume greater than maximum volume ', p, sc, st, rs)
1016
1029
 
1017
1030
  # drop load levels with duration 0
@@ -1044,8 +1057,12 @@ def DataConfiguration(mTEPES):
1044
1057
  mTEPES.dPar['pOutflowsTimeStep'] = mTEPES.dPar['pOutflowsTimeStep'].loc [mTEPES.es ]
1045
1058
  mTEPES.dPar['pStorageType'] = mTEPES.dPar['pStorageType'].loc [mTEPES.es ]
1046
1059
 
1060
+ if mTEPES.dPar['pIndRampReserves'] == 1:
1061
+ mTEPES.dPar['pRampReserveUp'] = mTEPES.dPar['pRampReserveUp'].loc [mTEPES.psnar]
1062
+ mTEPES.dPar['pRampReserveDw'] = mTEPES.dPar['pRampReserveDw'].loc [mTEPES.psnar]
1063
+
1047
1064
  if mTEPES.dPar['pIndHydroTopology'] == 1:
1048
- mTEPES.dPar['pHydroInflows'] = mTEPES.dPar['pHydroInflows'].loc [mTEPES.psn ]
1065
+ mTEPES.dPar['pHydroInflows' ] = mTEPES.dPar['pHydroInflows' ].loc [mTEPES.psn ]
1049
1066
  mTEPES.dPar['pHydroOutflows'] = mTEPES.dPar['pHydroOutflows'].loc [mTEPES.psn ]
1050
1067
  mTEPES.dPar['pIniVolume'] = mTEPES.dPar['pIniVolume'].loc [mTEPES.psn ]
1051
1068
  mTEPES.dPar['pMinVolume'] = mTEPES.dPar['pMinVolume'].loc [mTEPES.psn ]
@@ -1134,7 +1151,7 @@ def DataConfiguration(mTEPES):
1134
1151
  # mTEPES.dPar['pMaxPowerElec'] = mTEPES.dPar['pMaxPowerElec'].where(mTEPES.dPar['pMaxPowerElec'] >= mTEPES.dPar['pMinPowerElec'], mTEPES.dPar['pMinPowerElec'])
1135
1152
  # mTEPES.dPar['pMaxCharge'] = mTEPES.dPar['pMaxCharge'].where (mTEPES.dPar['pMaxCharge'] >= mTEPES.dPar['pMinCharge'], mTEPES.dPar['pMinCharge'] )
1136
1153
 
1137
- # Decrease Minimum to reach maximum
1154
+ # Decrease minimum to reach maximum
1138
1155
  mTEPES.dPar['pMinPowerElec'] = mTEPES.dPar['pMinPowerElec'].where(mTEPES.dPar['pMinPowerElec'] <= mTEPES.dPar['pMaxPowerElec'], mTEPES.dPar['pMaxPowerElec'])
1139
1156
  mTEPES.dPar['pMinCharge'] = mTEPES.dPar['pMinCharge'].where (mTEPES.dPar['pMinCharge'] <= mTEPES.dPar['pMaxCharge'], mTEPES.dPar['pMaxCharge'] )
1140
1157
 
@@ -1363,6 +1380,10 @@ def DataConfiguration(mTEPES):
1363
1380
  mTEPES.dPar['pOperReserveUp'] = filter_rows(mTEPES.dPar['pOperReserveUp'] , mTEPES.psnar)
1364
1381
  mTEPES.dPar['pOperReserveDw'] = filter_rows(mTEPES.dPar['pOperReserveDw'] , mTEPES.psnar)
1365
1382
 
1383
+ if mTEPES.dPar['pIndRampReserves'] == 1:
1384
+ mTEPES.dPar['pRampReserveUp'] = filter_rows(mTEPES.dPar['pRampReserveUp'] , mTEPES.psnar)
1385
+ mTEPES.dPar['pRampReserveDw'] = filter_rows(mTEPES.dPar['pRampReserveDw'] , mTEPES.psnar)
1386
+
1366
1387
  mTEPES.dPar['pMaxNTCBck'] = filter_rows(mTEPES.dPar['pMaxNTCBck'] , mTEPES.psnla)
1367
1388
  mTEPES.dPar['pMaxNTCFrw'] = filter_rows(mTEPES.dPar['pMaxNTCFrw'] , mTEPES.psnla)
1368
1389
  mTEPES.dPar['pMaxNTCMax'] = filter_rows(mTEPES.dPar['pMaxNTCMax'] , mTEPES.psnla)
@@ -1386,6 +1407,7 @@ def DataConfiguration(mTEPES):
1386
1407
  mTEPES.pIndBinGenMinTime = Param(initialize=mTEPES.dPar['pIndBinGenMinTime'] , within=Binary, doc='Indicator of using or not the min up/dw time constraints', mutable=True)
1387
1408
  mTEPES.pIndBinLineCommit = Param(initialize=mTEPES.dPar['pIndBinLineCommit'] , within=Binary, doc='Indicator of binary electric network switching decisions', mutable=True)
1388
1409
  mTEPES.pIndBinNetLosses = Param(initialize=mTEPES.dPar['pIndBinNetLosses'] , within=Binary, doc='Indicator of binary electric network ohmic losses', mutable=True)
1410
+ mTEPES.pIndRampReserves = Param(initialize=mTEPES.dPar['pIndRampReserves'] , within=Binary, doc='Indicator of ramp reserves' )
1389
1411
  mTEPES.pIndHydroTopology = Param(initialize=mTEPES.dPar['pIndHydroTopology'] , within=Binary, doc='Indicator of reservoir and hydropower topology' )
1390
1412
  mTEPES.pIndHydrogen = Param(initialize=mTEPES.dPar['pIndHydrogen'] , within=Binary, doc='Indicator of hydrogen demand and pipeline network' )
1391
1413
  mTEPES.pIndHeat = Param(initialize=mTEPES.dPar['pIndHeat'] , within=Binary, doc='Indicator of heat demand and pipe network' )
@@ -1413,7 +1435,7 @@ def DataConfiguration(mTEPES):
1413
1435
  mTEPES.pDemandElecPos = Param(mTEPES.psnnd, initialize=mTEPES.dPar['pDemandElecPos'].to_dict() , within=NonNegativeReals, doc='Electric demand positive' )
1414
1436
  mTEPES.pPeriodWeight = Param(mTEPES.p, initialize=mTEPES.dPar['pPeriodWeight'].to_dict() , within=NonNegativeReals, doc='Period weight', mutable=True)
1415
1437
  mTEPES.pDiscountedWeight = Param(mTEPES.p, initialize=mTEPES.dPar['pDiscountedWeight'].to_dict() , within=NonNegativeReals, doc='Discount factor' )
1416
- mTEPES.pScenProb = Param(mTEPES.psc, initialize=mTEPES.dPar['pScenProb'].to_dict() , within=UnitInterval , doc='Probability', mutable=True)
1438
+ mTEPES.pScenProb = Param(mTEPES.ps, initialize=mTEPES.dPar['pScenProb'].to_dict() , within=UnitInterval , doc='Probability', mutable=True)
1417
1439
  mTEPES.pStageWeight = Param(mTEPES.stt, initialize=mTEPES.dPar['pStageWeight'].to_dict() , within=NonNegativeReals, doc='Stage weight' )
1418
1440
  mTEPES.pDuration = Param(mTEPES.psn, initialize=mTEPES.dPar['pDuration'].to_dict() , within=NonNegativeIntegers, doc='Duration', mutable=True)
1419
1441
  mTEPES.pNodeLon = Param(mTEPES.nd, initialize=mTEPES.dPar['pNodeLon'].to_dict() , doc='Longitude' )
@@ -1477,6 +1499,10 @@ def DataConfiguration(mTEPES):
1477
1499
  mTEPES.pGenLoRetire = Param(mTEPES.gd, initialize=mTEPES.dPar['pGenLoRetire'].to_dict() , within=NonNegativeReals, doc='Lower bound of the retirement decision', mutable=True)
1478
1500
  mTEPES.pGenUpRetire = Param(mTEPES.gd, initialize=mTEPES.dPar['pGenUpRetire'].to_dict() , within=NonNegativeReals, doc='Upper bound of the retirement decision', mutable=True)
1479
1501
 
1502
+ if mTEPES.dPar['pIndRampReserves'] == 1:
1503
+ mTEPES.pRampReserveUp = Param(mTEPES.psnar, initialize=mTEPES.dPar['pRampReserveUp'].to_dict() , within=NonNegativeReals, doc='Ramp up reserve' )
1504
+ mTEPES.pRampReserveDw = Param(mTEPES.psnar, initialize=mTEPES.dPar['pRampReserveDw'].to_dict() , within=NonNegativeReals, doc='Ramp down reserve' )
1505
+
1480
1506
  if mTEPES.dPar['pIndHydrogen'] == 1:
1481
1507
  mTEPES.pProductionFunctionH2 = Param(mTEPES.el, initialize=mTEPES.dPar['pProductionFunctionH2'].to_dict(), within=NonNegativeReals, doc='Production function of an electrolyzer plant')
1482
1508
 
@@ -1663,6 +1689,7 @@ def DataConfiguration(mTEPES):
1663
1689
  print('Setting up input data ... ', round(SettingUpDataTime), 's')
1664
1690
 
1665
1691
 
1692
+ # @profile
1666
1693
  def SettingUpVariables(OptModel, mTEPES):
1667
1694
 
1668
1695
  StartTime = time.time()
@@ -1701,6 +1728,10 @@ def SettingUpVariables(OptModel, mTEPES):
1701
1728
  OptModel.vESSSpillage = Var(mTEPES.psnes, within=NonNegativeReals, doc='ESS spillage [GWh]')
1702
1729
  OptModel.vIniInventory = Var(mTEPES.psnec, within=NonNegativeReals, doc='initial inventory for ESS candidate [GWh]')
1703
1730
 
1731
+ if mTEPES.pIndRampReserves == 1:
1732
+ OptModel.vRampReserveUp = Var(mTEPES.psnnr, within=NonNegativeReals, doc='ramp up reserve of the unit [GW/h]')
1733
+ OptModel.vRampReserveDw = Var(mTEPES.psnnr, within=NonNegativeReals, doc='ramp down reserve of the unit [GW/h]')
1734
+
1704
1735
  OptModel.vESSTotalCharge = Var(mTEPES.psneh, within=NonNegativeReals, doc='ESS total charge power [GW]')
1705
1736
  OptModel.vCharge2ndBlock = Var(mTEPES.psneh, within=NonNegativeReals, doc='ESS charge power [GW]')
1706
1737
  OptModel.vESSReserveUp = Var(mTEPES.psneh, within=NonNegativeReals, doc='ESS upward operating reserve [GW]')
@@ -1837,6 +1868,10 @@ def SettingUpVariables(OptModel, mTEPES):
1837
1868
  [OptModel.vIniInventory [p,sc,n,ec].setlb(mTEPES.pMinStorage [p,sc,n,ec] ) for p,sc,n,ec in mTEPES.psnec]
1838
1869
  [OptModel.vIniInventory [p,sc,n,ec].setub(mTEPES.pMaxStorage [p,sc,n,ec] ) for p,sc,n,ec in mTEPES.psnec]
1839
1870
 
1871
+ if mTEPES.pIndRampReserves == 1:
1872
+ [OptModel.vRampReserveUp[p,sc,n,nr].setub(min(mTEPES.pMaxPower2ndBlock[p,sc,n,nr], mTEPES.pRampUp[nr])) if mTEPES.pRampUp[nr] else OptModel.vRampReserveUp[p,sc,n,nr].setub(mTEPES.pMaxPower2ndBlock[p,sc,n,nr]) for p,sc,n,nr in mTEPES.psnnr]
1873
+ [OptModel.vRampReserveDw[p,sc,n,nr].setub(min(mTEPES.pMaxPower2ndBlock[p,sc,n,nr], mTEPES.pRampDw[nr])) if mTEPES.pRampUp[nr] else OptModel.vRampReserveDw[p,sc,n,nr].setub(mTEPES.pMaxPower2ndBlock[p,sc,n,nr]) for p,sc,n,nr in mTEPES.psnnr]
1874
+
1840
1875
  [OptModel.vESSTotalCharge[p,sc,n,eh].setub(mTEPES.pMaxCharge [p,sc,n,eh] ) for p,sc,n,eh in mTEPES.psneh]
1841
1876
  [OptModel.vCharge2ndBlock[p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh] ) for p,sc,n,eh in mTEPES.psneh]
1842
1877
  [OptModel.vESSReserveUp [p,sc,n,eh].setub(mTEPES.pMaxCharge2ndBlock[p,sc,n,eh] ) for p,sc,n,eh in mTEPES.psneh]
@@ -1866,7 +1901,7 @@ def SettingUpVariables(OptModel, mTEPES):
1866
1901
  int: The number of fixed variables.
1867
1902
  '''
1868
1903
 
1869
- nFixedBinaries = 0
1904
+ nFixedVariables = 0
1870
1905
 
1871
1906
  # relax binary condition in generation, boiler, and electric network investment decisions
1872
1907
  for p,eb in mTEPES.peb:
@@ -1874,14 +1909,16 @@ def SettingUpVariables(OptModel, mTEPES):
1874
1909
  OptModel.vGenerationInvest [p,eb ].domain = UnitInterval
1875
1910
  if mTEPES.pIndBinGenInvest() == 2:
1876
1911
  OptModel.vGenerationInvest [p,eb ].fix(0)
1877
- nFixedBinaries += 1
1912
+ OptModel.vGenerationInvest [p,eb ].domain = UnitInterval
1913
+ nFixedVariables += 1
1878
1914
 
1879
1915
  for p,ni,nf,cc in mTEPES.plc:
1880
1916
  if mTEPES.pIndBinNetElecInvest() != 0 and mTEPES.pIndBinLineInvest[ni,nf,cc] == 0:
1881
1917
  OptModel.vNetworkInvest [p,ni,nf,cc].domain = UnitInterval
1882
1918
  if mTEPES.pIndBinNetElecInvest() == 2:
1883
1919
  OptModel.vNetworkInvest [p,ni,nf,cc].fix(0)
1884
- nFixedBinaries += 1
1920
+ OptModel.vNetworkInvest [p,ni,nf,cc].domain = UnitInterval
1921
+ nFixedVariables += 1
1885
1922
 
1886
1923
  # relax binary condition in generation retirement decisions
1887
1924
  for p,gd in mTEPES.pgd:
@@ -1889,7 +1926,8 @@ def SettingUpVariables(OptModel, mTEPES):
1889
1926
  OptModel.vGenerationRetire [p,gd ].domain = UnitInterval
1890
1927
  if mTEPES.pIndBinGenRetire() == 2:
1891
1928
  OptModel.vGenerationRetire [p,gd ].fix(0)
1892
- nFixedBinaries += 1
1929
+ OptModel.vGenerationRetire [p,gd ].domain = UnitInterval
1930
+ nFixedVariables += 1
1893
1931
 
1894
1932
  # relax binary condition in reservoir investment decisions
1895
1933
  if mTEPES.pIndHydroTopology == 1:
@@ -1898,7 +1936,8 @@ def SettingUpVariables(OptModel, mTEPES):
1898
1936
  OptModel.vReservoirInvest [p,rc ].domain = UnitInterval
1899
1937
  if mTEPES.pIndBinRsrInvest() == 2:
1900
1938
  OptModel.vReservoirInvest [p,rc ].fix(0)
1901
- nFixedBinaries += 1
1939
+ OptModel.vReservoirInvest [p,rc ].domain = UnitInterval
1940
+ nFixedVariables += 1
1902
1941
 
1903
1942
  # relax binary condition in hydrogen network investment decisions
1904
1943
  if mTEPES.pIndHydrogen == 1:
@@ -1907,7 +1946,8 @@ def SettingUpVariables(OptModel, mTEPES):
1907
1946
  OptModel.vH2PipeInvest [p,ni,nf,cc].domain = UnitInterval
1908
1947
  if mTEPES.pIndBinNetH2Invest() == 2:
1909
1948
  OptModel.vH2PipeInvest [p,ni,nf,cc].fix(0)
1910
- nFixedBinaries += 1
1949
+ OptModel.vH2PipeInvest [p,ni,nf,cc].domain = UnitInterval
1950
+ nFixedVariables += 1
1911
1951
 
1912
1952
  if mTEPES.pIndHeat == 1:
1913
1953
  # relax binary condition in heat network investment decisions
@@ -1916,7 +1956,8 @@ def SettingUpVariables(OptModel, mTEPES):
1916
1956
  OptModel.vHeatPipeInvest [p,ni,nf,cc].domain = UnitInterval
1917
1957
  if mTEPES.pIndBinNetHeatInvest() == 2:
1918
1958
  OptModel.vHeatPipeInvest [p,ni,nf,cc].fix(0)
1919
- nFixedBinaries += 1
1959
+ OptModel.vHeatPipeInvest [p,ni,nf,cc].domain = UnitInterval
1960
+ nFixedVariables += 1
1920
1961
 
1921
1962
  # relax binary condition in unit generation, startup and shutdown decisions
1922
1963
  for p,sc,n,nr in mTEPES.psnnr:
@@ -1929,6 +1970,9 @@ def SettingUpVariables(OptModel, mTEPES):
1929
1970
  OptModel.vStableState [p,sc,n,nr].fix(0)
1930
1971
  OptModel.vRampDwState [p,sc,n,nr].fix(0)
1931
1972
  OptModel.vRampUpState [p,sc,n,nr].fix(0)
1973
+ OptModel.vStableState [p,sc,n,nr].domain = UnitInterval
1974
+ OptModel.vRampDwState [p,sc,n,nr].domain = UnitInterval
1975
+ OptModel.vRampUpState [p,sc,n,nr].domain = UnitInterval
1932
1976
 
1933
1977
  for p,sc,nr, group in mTEPES.psnr * mTEPES.ExclusiveGroups:
1934
1978
  if mTEPES.pIndBinUnitCommit[nr] == 0 and nr in mTEPES.ExclusiveGeneratorsYearly:
@@ -1942,9 +1986,10 @@ def SettingUpVariables(OptModel, mTEPES):
1942
1986
  OptModel.vCommitmentCons[p,sc,n,h].domain = UnitInterval
1943
1987
  if mTEPES.pMaxCharge[p,sc,n,h] == 0.0:
1944
1988
  OptModel.vCommitmentCons[p,sc,n,h].fix(0)
1945
- nFixedBinaries += 1
1989
+ OptModel.vCommitmentCons[p,sc,n,h].domain = UnitInterval
1990
+ nFixedVariables += 1
1946
1991
 
1947
- return nFixedBinaries
1992
+ return nFixedVariables
1948
1993
 
1949
1994
  # call the relaxing variables function and add its output to nFixedVariables
1950
1995
  nFixedBinaries = RelaxBinaryInvestmentConditions(mTEPES, mTEPES)
@@ -1967,14 +2012,21 @@ def SettingUpVariables(OptModel, mTEPES):
1967
2012
 
1968
2013
  nFixedVariables = 0
1969
2014
  # existing lines are always committed if no switching decision is modeled
1970
- [OptModel.vLineCommit[p,sc,n,ni,nf,cc].fix(1) for p,sc,n,ni,nf,cc in mTEPES.psnle if mTEPES.pIndBinLineSwitch[ni,nf,cc] == 0]
1971
- nFixedVariables += sum( 1 for p,sc,n,ni,nf,cc in mTEPES.psnle if mTEPES.pIndBinLineSwitch[ni,nf,cc] == 0)
2015
+ for p,sc,n,ni,nf,cc in mTEPES.psnle:
2016
+ if mTEPES.pIndBinLineSwitch[ni,nf,cc] == 0:
2017
+ OptModel.vLineCommit[p,sc,n,ni,nf,cc].fix(1)
2018
+ OptModel.vLineCommit[p,sc,n,ni,nf,cc].domain = UnitInterval
2019
+ nFixedVariables += 1
1972
2020
 
1973
2021
  # no on/off state for lines if no switching decision is modeled
1974
2022
  if sum(mTEPES.pIndBinLineSwitch[:,:,:]):
1975
- [OptModel.vLineOnState [p,sc,n,ni,nf,cc].fix(0) for p,sc,n,ni,nf,cc in mTEPES.psnla if mTEPES.pIndBinLineSwitch[ni,nf,cc] == 0]
1976
- [OptModel.vLineOffState[p,sc,n,ni,nf,cc].fix(0) for p,sc,n,ni,nf,cc in mTEPES.psnla if mTEPES.pIndBinLineSwitch[ni,nf,cc] == 0]
1977
- nFixedVariables += sum( 2 for p,sc,n,ni,nf,cc in mTEPES.psnla if mTEPES.pIndBinLineSwitch[ni,nf,cc] == 0)
2023
+ for p,sc,n,ni,nf,cc in mTEPES.psnla:
2024
+ if mTEPES.pIndBinLineSwitch[ni,nf,cc] == 0:
2025
+ OptModel.vLineOnState [p,sc,n,ni,nf,cc].fix(0)
2026
+ OptModel.vLineOffState[p,sc,n,ni,nf,cc].fix(0)
2027
+ OptModel.vLineOnState [p,sc,n,ni,nf,cc].domain = UnitInterval
2028
+ OptModel.vLineOffState[p,sc,n,ni,nf,cc].domain = UnitInterval
2029
+ nFixedVariables += 2
1978
2030
 
1979
2031
  OptModel.vLineLosses = Var(mTEPES.psnll, within=NonNegativeReals, doc='half line losses [GW]')
1980
2032
  OptModel.vFlowElec = Var(mTEPES.psnla, within=Reals, doc='electric flow [GW]')
@@ -1988,12 +2040,14 @@ def SettingUpVariables(OptModel, mTEPES):
1988
2040
  if mTEPES.pIndPTDF == 1:
1989
2041
  OptModel.vNetPosition = Var(mTEPES.psnnd, within=Reals, doc='net position in node [GW]')
1990
2042
 
1991
- [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]
1992
2043
  if mTEPES.pIndBinSingleNode() == 0:
1993
- [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]
1994
- [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]
1995
- [OptModel.vTheta [p,sc,n,nd ].setlb(-mTEPES.pMaxTheta [p,sc,n,nd ]() ) for p,sc,n,nd in mTEPES.psnnd]
1996
- [OptModel.vTheta [p,sc,n,nd ].setub( mTEPES.pMaxTheta [p,sc,n,nd ]() ) for p,sc,n,nd in mTEPES.psnnd]
2044
+ [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]
2045
+ [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]
2046
+ [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]
2047
+ else:
2048
+ [OptModel.vLineLosses[p,sc,n,ni,nf,cc].fix(0.0) for p,sc,n,ni,nf,cc in mTEPES.psnll]
2049
+ [OptModel.vTheta [p,sc,n,nd ].setlb(-mTEPES.pMaxTheta [p,sc,n,nd ]()) for p,sc,n,nd in mTEPES.psnnd]
2050
+ [OptModel.vTheta [p,sc,n,nd ].setub( mTEPES.pMaxTheta [p,sc,n,nd ]()) for p,sc,n,nd in mTEPES.psnnd]
1997
2051
 
1998
2052
  if mTEPES.pIndHydrogen == 1:
1999
2053
  OptModel.vFlowH2 = Var(mTEPES.psnpa, within=Reals, doc='pipeline flow [tH2]')
@@ -2044,6 +2098,9 @@ def SettingUpVariables(OptModel, mTEPES):
2044
2098
  OptModel.vCommitment [p,sc,n,nr].fix(1)
2045
2099
  OptModel.vStartUp [p,sc,n,nr].fix(0)
2046
2100
  OptModel.vShutDown [p,sc,n,nr].fix(0)
2101
+ OptModel.vCommitment [p,sc,n,nr].domain = UnitInterval
2102
+ OptModel.vStartUp [p,sc,n,nr].domain = UnitInterval
2103
+ OptModel.vShutDown [p,sc,n,nr].domain = UnitInterval
2047
2104
  nFixedVariables += 3
2048
2105
  # If there are mutually exclusive groups do not fix variables from ESS in mutually exclusive groups
2049
2106
  elif len(mTEPES.ExclusiveGroups) > 0 and nr not in mTEPES.ExclusiveGenerators:
@@ -2051,14 +2108,23 @@ def SettingUpVariables(OptModel, mTEPES):
2051
2108
  OptModel.vCommitment [p,sc,n,nr].fix(1)
2052
2109
  OptModel.vStartUp [p,sc,n,nr].fix(0)
2053
2110
  OptModel.vShutDown [p,sc,n,nr].fix(0)
2111
+ OptModel.vCommitment [p,sc,n,nr].domain = UnitInterval
2112
+ OptModel.vStartUp [p,sc,n,nr].domain = UnitInterval
2113
+ OptModel.vShutDown [p,sc,n,nr].domain = UnitInterval
2054
2114
  nFixedVariables += 3
2055
2115
 
2056
- # if min and max power coincide there are neither second block, nor operating reserve
2116
+ # if min and max power coincide there are neither second block, nor operating reserve, nor ramp reserve
2057
2117
  if mTEPES.pMaxPower2ndBlock [p,sc,n,nr] == 0.0:
2058
2118
  OptModel.vOutput2ndBlock [p,sc,n,nr].fix(0.0)
2059
2119
  OptModel.vReserveUp [p,sc,n,nr].fix(0.0)
2060
2120
  OptModel.vReserveDown [p,sc,n,nr].fix(0.0)
2061
2121
  nFixedVariables += 3
2122
+ if mTEPES.pIndRampReserves == 1:
2123
+ if mTEPES.pMaxPower2ndBlock[p, sc, n, nr] == 0.0 or mTEPES.pRampUp[nr] == 0.0:
2124
+ OptModel.vRampReserveUp[p,sc,n,nr].fix(0.0)
2125
+ OptModel.vRampReserveDw[p,sc,n,nr].fix(0.0)
2126
+ nFixedVariables += 2
2127
+
2062
2128
  if mTEPES.pIndOperReserveGen[ nr] == 1:
2063
2129
  OptModel.vReserveUp [p,sc,n,nr].fix(0.0)
2064
2130
  OptModel.vReserveDown [p,sc,n,nr].fix(0.0)
@@ -2100,7 +2166,8 @@ def SettingUpVariables(OptModel, mTEPES):
2100
2166
  # ESS with no charge capacity or not storage capacity can't charge
2101
2167
  if mTEPES.pMaxCharge [p,sc,n,h ] == 0.0:
2102
2168
  OptModel.vESSTotalCharge [p,sc,n,h ].fix(0.0)
2103
- OptModel.vCommitmentCons [p,sc,n,h ].fix(0.0)
2169
+ OptModel.vCommitmentCons [p,sc,n,h ].fix(0 )
2170
+ OptModel.vCommitmentCons [p,sc,n,h ].domain = UnitInterval
2104
2171
  nFixedVariables += 2
2105
2172
  if mTEPES.pMaxCharge2ndBlock[p,sc,n,h ] == 0.0:
2106
2173
  OptModel.vCharge2ndBlock [p,sc,n,h ].fix(0.0)
@@ -2300,27 +2367,36 @@ def SettingUpVariables(OptModel, mTEPES):
2300
2367
  if mTEPES.pElecGenPeriodIni[eb] > p:
2301
2368
  OptModel.vGenerationInvest[p,eb].fix(0)
2302
2369
  OptModel.vGenerationInvPer[p,eb].fix(0)
2370
+ OptModel.vGenerationInvest[p,eb].domain = UnitInterval
2371
+ OptModel.vGenerationInvPer[p,eb].domain = UnitInterval
2303
2372
  nFixedVariables += 2
2304
2373
  if mTEPES.pElecGenPeriodFin[eb] < p:
2305
2374
  OptModel.vGenerationInvPer[p,eb].fix(0)
2375
+ OptModel.vGenerationInvPer[p,eb].domain = UnitInterval
2306
2376
  nFixedVariables += 1
2307
2377
 
2308
2378
  for p,gd in mTEPES.pgd:
2309
2379
  if mTEPES.pElecGenPeriodIni[gd] > p:
2310
2380
  OptModel.vGenerationRetire[p,gd].fix(0)
2311
2381
  OptModel.vGenerationRetPer[p,gd].fix(0)
2382
+ OptModel.vGenerationRetire[p,gd].domain = UnitInterval
2383
+ OptModel.vGenerationRetPer[p,gd].domain = UnitInterval
2312
2384
  nFixedVariables += 2
2313
2385
  if mTEPES.pElecGenPeriodFin[gd] < p:
2314
2386
  OptModel.vGenerationRetPer[p,gd].fix(0)
2387
+ OptModel.vGenerationRetPer[p,gd].domain = UnitInterval
2315
2388
  nFixedVariables += 1
2316
2389
 
2317
2390
  for p,ni,nf,cc in mTEPES.plc:
2318
2391
  if mTEPES.pElecNetPeriodIni[ni,nf,cc] > p:
2319
2392
  OptModel.vNetworkInvest[p,ni,nf,cc].fix(0)
2320
2393
  OptModel.vNetworkInvPer[p,ni,nf,cc].fix(0)
2394
+ OptModel.vNetworkInvest[p,ni,nf,cc].domain = UnitInterval
2395
+ OptModel.vNetworkInvPer[p,ni,nf,cc].domain = UnitInterval
2321
2396
  nFixedVariables += 2
2322
2397
  if mTEPES.pElecNetPeriodFin[ni,nf,cc] < p:
2323
2398
  OptModel.vNetworkInvPer[p,ni,nf,cc].fix(0)
2399
+ OptModel.vNetworkInvPer[p,ni,nf,cc].domain = UnitInterval
2324
2400
  nFixedVariables += 1
2325
2401
 
2326
2402
  if mTEPES.pIndHydroTopology == 1:
@@ -2328,9 +2404,12 @@ def SettingUpVariables(OptModel, mTEPES):
2328
2404
  if mTEPES.pRsrPeriodIni[rc] > p:
2329
2405
  OptModel.vReservoirInvest[p,rc].fix(0)
2330
2406
  OptModel.vReservoirInvPer[p,rc].fix(0)
2407
+ OptModel.vReservoirInvest[p,rc].domain = UnitInterval
2408
+ OptModel.vReservoirInvPer[p,rc].domain = UnitInterval
2331
2409
  nFixedVariables += 2
2332
2410
  if mTEPES.pRsrPeriodFin[rc] < p:
2333
2411
  OptModel.vReservoirInvPer[p,rc].fix(0)
2412
+ OptModel.vReservoirInvPer[p,rc].domain = UnitInterval
2334
2413
  nFixedVariables += 1
2335
2414
 
2336
2415
  if mTEPES.pIndHydrogen == 1:
@@ -2338,9 +2417,12 @@ def SettingUpVariables(OptModel, mTEPES):
2338
2417
  if mTEPES.pH2PipePeriodIni[ni,nf,cc] > p:
2339
2418
  OptModel.vH2PipeInvest[p,ni,nf,cc].fix(0)
2340
2419
  OptModel.vH2PipeInvPer[p,ni,nf,cc].fix(0)
2420
+ OptModel.vH2PipeInvest[p,ni,nf,cc].domain = UnitInterval
2421
+ OptModel.vH2PipeInvPer[p,ni,nf,cc].domain = UnitInterval
2341
2422
  nFixedVariables += 2
2342
2423
  if mTEPES.pH2PipePeriodFin[ni,nf,cc] < p:
2343
2424
  OptModel.vH2PipeInvPer[p,ni,nf,cc].fix(0)
2425
+ OptModel.vH2PipeInvPer[p,ni,nf,cc].domain = UnitInterval
2344
2426
  nFixedVariables += 1
2345
2427
 
2346
2428
  if mTEPES.pIndHeat == 1:
@@ -2348,9 +2430,12 @@ def SettingUpVariables(OptModel, mTEPES):
2348
2430
  if mTEPES.pHeatPipePeriodIni[ni,nf,cc] > p:
2349
2431
  OptModel.vHeatPipeInvest[p,ni,nf,cc].fix(0)
2350
2432
  OptModel.vHeatPipeInvPer[p,ni,nf,cc].fix(0)
2433
+ OptModel.vHeatPipeInvest[p,ni,nf,cc].domain = UnitInterval
2434
+ OptModel.vHeatPipeInvPer[p,ni,nf,cc].domain = UnitInterval
2351
2435
  nFixedVariables += 2
2352
2436
  if mTEPES.pHeatPipePeriodFin[ni,nf,cc] < p:
2353
2437
  OptModel.vHeatPipeInvPer[p,ni,nf,cc].fix(0)
2438
+ OptModel.vHeatPipeInvPer[p,ni,nf,cc].domain = UnitInterval
2354
2439
  nFixedVariables += 1
2355
2440
 
2356
2441
  # remove power plants and lines not installed in this period
@@ -2363,6 +2448,7 @@ def SettingUpVariables(OptModel, mTEPES):
2363
2448
  for p,sc,nr in mTEPES.psnr:
2364
2449
  if nr not in mTEPES.eb and mTEPES.pElecGenPeriodIni[nr] > p:
2365
2450
  OptModel.vMaxCommitment[p,sc,nr].fix(0)
2451
+ OptModel.vMaxCommitment[p,sc,nr].domain = UnitInterval
2366
2452
  nFixedVariables += 1
2367
2453
  for p,sc,n,nr in mTEPES.psnnr:
2368
2454
  if nr not in mTEPES.eb and mTEPES.pElecGenPeriodIni[nr] > p:
@@ -2372,6 +2458,9 @@ def SettingUpVariables(OptModel, mTEPES):
2372
2458
  OptModel.vCommitment [p,sc,n,nr].fix(0 )
2373
2459
  OptModel.vStartUp [p,sc,n,nr].fix(0 )
2374
2460
  OptModel.vShutDown [p,sc,n,nr].fix(0 )
2461
+ OptModel.vCommitment [p,sc,n,nr].domain = UnitInterval
2462
+ OptModel.vStartUp [p,sc,n,nr].domain = UnitInterval
2463
+ OptModel.vShutDown [p,sc,n,nr].domain = UnitInterval
2375
2464
  nFixedVariables += 6
2376
2465
 
2377
2466
  for p,sc,n,es in mTEPES.psnes:
@@ -2408,22 +2497,29 @@ def SettingUpVariables(OptModel, mTEPES):
2408
2497
  nFixedInstallationsAndRetirements = AvoidForbiddenInstallationsAndRetirements(mTEPES, mTEPES)
2409
2498
  nFixedVariables += nFixedInstallationsAndRetirements
2410
2499
 
2411
- [OptModel.vFlowElec [p,sc,n,ni,nf,cc].fix(0.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]
2412
- [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]
2413
- [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]
2414
- [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]
2415
- 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)
2500
+ for p,sc,n,ni,nf,cc in mTEPES.psnla:
2501
+ if (ni,nf,cc) not in mTEPES.lc and mTEPES.pElecNetPeriodIni[ni,nf,cc] > p:
2502
+ OptModel.vLineCommit [p,sc,n,ni,nf,cc].fix(0)
2503
+ OptModel.vLineOnState [p,sc,n,ni,nf,cc].fix(0)
2504
+ OptModel.vLineOffState[p,sc,n,ni,nf,cc].fix(0)
2505
+ OptModel.vLineCommit [p,sc,n,ni,nf,cc].domain = UnitInterval
2506
+ OptModel.vLineOnState [p,sc,n,ni,nf,cc].domain = UnitInterval
2507
+ OptModel.vLineOffState[p,sc,n,ni,nf,cc].domain = UnitInterval
2508
+ nFixedVariables += 3
2509
+
2510
+ [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]
2511
+ 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)
2416
2512
 
2417
- [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]
2418
- 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)
2513
+ [OptModel.vFlowElec [p,sc,n,ni,nf,cc].fix(0.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]
2514
+ nFixedVariables += sum( 1 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)
2419
2515
 
2420
2516
  if mTEPES.pIndHydrogen == 1:
2421
2517
  [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]
2422
- 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)
2518
+ nFixedVariables += sum( 1 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)
2423
2519
 
2424
2520
  if mTEPES.pIndHeat == 1:
2425
2521
  [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]
2426
- 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)
2522
+ nFixedVariables += sum( 1 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)
2427
2523
 
2428
2524
  # tolerance to consider 0 an investment decision
2429
2525
  pEpsilon = 1e-4
@@ -2442,31 +2538,31 @@ def SettingUpVariables(OptModel, mTEPES):
2442
2538
  None: Changes are performed directly onto the model object.
2443
2539
  '''
2444
2540
  for p,eb in mTEPES.peb:
2445
- if mTEPES.pGenLoInvest[ eb ]() < pEpsilon:
2446
- mTEPES.pGenLoInvest[ eb ] = 0
2447
- if mTEPES.pGenUpInvest[ eb ]() < pEpsilon:
2448
- mTEPES.pGenUpInvest[ eb ] = 0
2449
- if mTEPES.pGenLoInvest[ eb ]() > 1.0 - pEpsilon:
2450
- mTEPES.pGenLoInvest[ eb ] = 1
2451
- if mTEPES.pGenUpInvest[ eb ]() > 1.0 - pEpsilon:
2452
- mTEPES.pGenUpInvest[ eb ] = 1
2453
- if mTEPES.pGenLoInvest[ eb ]() > mTEPES.pGenUpInvest[eb ]():
2454
- mTEPES.pGenLoInvest[ eb ] = mTEPES.pGenUpInvest[eb ]()
2455
- [OptModel.vGenerationInvest[p,eb ].setlb(mTEPES.pGenLoInvest[eb ]()) for p,eb in mTEPES.peb]
2456
- [OptModel.vGenerationInvest[p,eb ].setub(mTEPES.pGenUpInvest[eb ]()) for p,eb in mTEPES.peb]
2541
+ if mTEPES.pGenLoInvest[ eb]() < pEpsilon:
2542
+ mTEPES.pGenLoInvest[ eb] = 0
2543
+ if mTEPES.pGenUpInvest[ eb]() < pEpsilon:
2544
+ mTEPES.pGenUpInvest[ eb] = 0
2545
+ if mTEPES.pGenLoInvest[ eb]() > 1.0 - pEpsilon:
2546
+ mTEPES.pGenLoInvest[ eb] = 1
2547
+ if mTEPES.pGenUpInvest[ eb]() > 1.0 - pEpsilon:
2548
+ mTEPES.pGenUpInvest[ eb] = 1
2549
+ if mTEPES.pGenLoInvest[ eb]() > mTEPES.pGenUpInvest[eb]():
2550
+ mTEPES.pGenLoInvest[ eb] = mTEPES.pGenUpInvest[eb]()
2551
+ [OptModel.vGenerationInvest[p,eb].setlb(mTEPES.pGenLoInvest[eb]()) for p,eb in mTEPES.peb]
2552
+ [OptModel.vGenerationInvest[p,eb].setub(mTEPES.pGenUpInvest[eb]()) for p,eb in mTEPES.peb]
2457
2553
  for p,gd in mTEPES.pgd:
2458
- if mTEPES.pGenLoRetire[ gd ]() < pEpsilon:
2459
- mTEPES.pGenLoRetire[ gd ] = 0
2460
- if mTEPES.pGenUpRetire[ gd ]() < pEpsilon:
2461
- mTEPES.pGenUpRetire[ gd ] = 0
2462
- if mTEPES.pGenLoRetire[ gd ]() > 1.0 - pEpsilon:
2463
- mTEPES.pGenLoRetire[ gd ] = 1
2464
- if mTEPES.pGenUpRetire[ gd ]() > 1.0 - pEpsilon:
2465
- mTEPES.pGenUpRetire[ gd ] = 1
2466
- if mTEPES.pGenLoRetire[ gd ]() > mTEPES.pGenUpRetire[gd ]():
2467
- mTEPES.pGenLoRetire[ gd ] = mTEPES.pGenUpRetire[gd ]()
2468
- [OptModel.vGenerationRetire[p,gd ].setlb(mTEPES.pGenLoRetire[gd ]()) for p,gd in mTEPES.pgd]
2469
- [OptModel.vGenerationRetire[p,gd ].setub(mTEPES.pGenUpRetire[gd ]()) for p,gd in mTEPES.pgd]
2554
+ if mTEPES.pGenLoRetire[ gd]() < pEpsilon:
2555
+ mTEPES.pGenLoRetire[ gd] = 0
2556
+ if mTEPES.pGenUpRetire[ gd]() < pEpsilon:
2557
+ mTEPES.pGenUpRetire[ gd] = 0
2558
+ if mTEPES.pGenLoRetire[ gd]() > 1.0 - pEpsilon:
2559
+ mTEPES.pGenLoRetire[ gd] = 1
2560
+ if mTEPES.pGenUpRetire[ gd]() > 1.0 - pEpsilon:
2561
+ mTEPES.pGenUpRetire[ gd] = 1
2562
+ if mTEPES.pGenLoRetire[ gd]() > mTEPES.pGenUpRetire[gd]():
2563
+ mTEPES.pGenLoRetire[ gd] = mTEPES.pGenUpRetire[gd]()
2564
+ [OptModel.vGenerationRetire[p,gd].setlb(mTEPES.pGenLoRetire[gd]()) for p,gd in mTEPES.pgd]
2565
+ [OptModel.vGenerationRetire[p,gd].setub(mTEPES.pGenUpRetire[gd]()) for p,gd in mTEPES.pgd]
2470
2566
  for p,ni,nf,cc in mTEPES.plc:
2471
2567
  if mTEPES.pNetLoInvest[ ni,nf,cc]() < pEpsilon:
2472
2568
  mTEPES.pNetLoInvest[ ni,nf,cc] = 0
@@ -2483,18 +2579,18 @@ def SettingUpVariables(OptModel, mTEPES):
2483
2579
 
2484
2580
  if mTEPES.pIndHydroTopology == 1:
2485
2581
  for p,rc in mTEPES.prc:
2486
- if mTEPES.pRsrLoInvest[ rc ]() < pEpsilon:
2487
- mTEPES.pRsrLoInvest[ rc ] = 0
2488
- if mTEPES.pRsrUpInvest[ rc ]() < pEpsilon:
2489
- mTEPES.pRsrUpInvest[ rc ] = 0
2490
- if mTEPES.pRsrLoInvest[ rc ]() > 1.0 - pEpsilon:
2491
- mTEPES.pRsrLoInvest[ rc ] = 1
2492
- if mTEPES.pRsrUpInvest[ rc ]() > 1.0 - pEpsilon:
2493
- mTEPES.pRsrUpInvest[ rc ] = 1
2494
- if mTEPES.pRsrLoInvest[ rc ]() > mTEPES.pRsrUpInvest[rc ]():
2495
- mTEPES.pRsrLoInvest[ rc ] = mTEPES.pRsrUpInvest[rc ]()
2496
- [OptModel.vReservoirInvest [p,rc ].setlb(mTEPES.pRsrLoInvest[rc ]()) for p,rc in mTEPES.prc]
2497
- [OptModel.vReservoirInvest [p,rc ].setub(mTEPES.pRsrUpInvest[rc ]()) for p,rc in mTEPES.prc]
2582
+ if mTEPES.pRsrLoInvest[ rc]() < pEpsilon:
2583
+ mTEPES.pRsrLoInvest[ rc] = 0
2584
+ if mTEPES.pRsrUpInvest[ rc]() < pEpsilon:
2585
+ mTEPES.pRsrUpInvest[ rc] = 0
2586
+ if mTEPES.pRsrLoInvest[ rc]() > 1.0 - pEpsilon:
2587
+ mTEPES.pRsrLoInvest[ rc] = 1
2588
+ if mTEPES.pRsrUpInvest[ rc]() > 1.0 - pEpsilon:
2589
+ mTEPES.pRsrUpInvest[ rc] = 1
2590
+ if mTEPES.pRsrLoInvest[ rc]() > mTEPES.pRsrUpInvest[rc]():
2591
+ mTEPES.pRsrLoInvest[ rc] = mTEPES.pRsrUpInvest[rc]()
2592
+ [OptModel.vReservoirInvest [p,rc].setlb(mTEPES.pRsrLoInvest[rc]()) for p,rc in mTEPES.prc]
2593
+ [OptModel.vReservoirInvest [p,rc].setub(mTEPES.pRsrUpInvest[rc]()) for p,rc in mTEPES.prc]
2498
2594
 
2499
2595
  if mTEPES.pIndHydrogen == 1:
2500
2596
  for p,ni,nf,cc in mTEPES.ppc:
@@ -2549,16 +2645,20 @@ def SettingUpVariables(OptModel, mTEPES):
2549
2645
  raise ValueError('### No active stages in the case study' )
2550
2646
 
2551
2647
  # detecting infeasibility: sum of scenario probabilities must be 1 in each period
2552
- # for p in mTEPES.p:
2553
- # if abs(sum(mTEPES.pScenProb[p,sc] for sc in mTEPES.sc)-1.0) > 1e-6:
2554
- # raise ValueError('### Sum of scenario probabilities different from 1 in period ', p)
2648
+ # tolerance to consider 0 the difference between parameters
2649
+ pEpsilon = 1e-6
2650
+ for p in mTEPES.p:
2651
+ if abs(sum(mTEPES.pScenProb[p,sc]() for sc in mTEPES.sc if (p,sc) in mTEPES.ps)-1.0) > pEpsilon:
2652
+ raise ValueError('### Sum of scenario probabilities different from 1 in period ', p)
2555
2653
 
2556
2654
  for es in mTEPES.es:
2557
- # detecting infeasibility: total min ESS output greater than total inflows, total max ESS charge lower than total outflows
2558
- if sum(mTEPES.pMinPowerElec[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyInflows [p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) > 0.0:
2655
+ # detecting infeasibility: total min ESS output greater than total inflows, total max ESS charge lower than total outflows, or no storage capacity for charging
2656
+ if sum(mTEPES.pMinPowerElec[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyInflows [p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) > 0.0:
2559
2657
  raise ValueError('### Total minimum output greater than total inflows for ESS unit ', es, ' by ', sum(mTEPES.pMinPowerElec[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyInflows [p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes), ' GWh')
2560
- if sum(mTEPES.pMaxCharge [p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyOutflows[p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) < 0.0:
2658
+ if sum(mTEPES.pMaxCharge [p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyOutflows[p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) < 0.0:
2561
2659
  raise ValueError('### Total maximum charge lower than total outflows for ESS unit ', es, ' by ', sum(mTEPES.pMaxCharge [p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) - sum(mTEPES.pEnergyOutflows[p,sc,n,es]() for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes), ' GWh')
2660
+ if max(mTEPES.pMaxCharge[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) and max(mTEPES.pMaxPowerElec[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) and max(mTEPES.pMaxStorage[p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes) == 0.0:
2661
+ raise ValueError('### This ESS unit has no storage capacity for charging ', es, ' ', sum(mTEPES.pMaxCharge [p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes), ' MW ', sum(mTEPES.pMaxStorage [p,sc,n,es] for p,sc,n in mTEPES.psn if (p,es) in mTEPES.pes), ' GWh')
2562
2662
 
2563
2663
  # detect inventory infeasibility
2564
2664
  for p,sc,n,es in mTEPES.ps*mTEPES.nesc: