openTEPES 4.18.12__py3-none-any.whl → 4.18.13__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/oT_Data_Stage_9n.csv +1 -1
- openTEPES/RTS24/oT_Dict_Technology_RTS24.csv +2 -0
- openTEPES/__init__.py +1 -1
- openTEPES/openTEPES.py +3 -3
- openTEPES/openTEPES_InputData.py +143 -103
- openTEPES/openTEPES_Main.py +2 -2
- openTEPES/openTEPES_ModelFormulation.py +124 -117
- openTEPES/openTEPES_OutputResults.py +7 -7
- openTEPES/openTEPES_ProblemSolving.py +3 -3
- openTEPES/sSEP/oT_Data_RESEnergy_sSEP.csv +1 -1
- openTEPES/sSEP/oT_Data_Stage_sSEP.csv +1 -1
- {opentepes-4.18.12.dist-info → opentepes-4.18.13.dist-info}/METADATA +24 -13
- {opentepes-4.18.12.dist-info → opentepes-4.18.13.dist-info}/RECORD +16 -16
- {opentepes-4.18.12.dist-info → opentepes-4.18.13.dist-info}/WHEEL +0 -0
- {opentepes-4.18.12.dist-info → opentepes-4.18.13.dist-info}/entry_points.txt +0 -0
- {opentepes-4.18.12.dist-info → opentepes-4.18.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -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) - February 05, 2026
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -149,6 +149,11 @@ def GenerationOperationModelFormulationObjFunct(OptModel, mTEPES, pIndLogConsole
|
|
|
149
149
|
if (nd,hp) in mTEPES.n2g:
|
|
150
150
|
h2n[nd].append(hp)
|
|
151
151
|
|
|
152
|
+
g2a = defaultdict(list)
|
|
153
|
+
for ar,g in mTEPES.ar*mTEPES.g:
|
|
154
|
+
if (ar,g) in mTEPES.a2g:
|
|
155
|
+
g2a[ar].append(g)
|
|
156
|
+
|
|
152
157
|
def eTotalGCost(OptModel,n):
|
|
153
158
|
return OptModel.vTotalGCost[p,sc,n] == (mTEPES.pLoadLevelDuration[p,sc,n]() * sum(mTEPES.pLinearVarCost [p,sc,n,nr] * OptModel.vTotalOutput [p,sc,n,nr] +
|
|
154
159
|
mTEPES.pConstantVarCost[p,sc,n,nr] * OptModel.vCommitment [p,sc,n,nr] for nr in mTEPES.nr if (p,nr) in mTEPES.pnr) +
|
|
@@ -176,24 +181,24 @@ def GenerationOperationModelFormulationObjFunct(OptModel, mTEPES, pIndLogConsole
|
|
|
176
181
|
setattr(OptModel, f'eTotalECost_{p}_{sc}_{st}', Constraint(mTEPES.n, rule=eTotalECost, doc='system emission cost [MEUR]'))
|
|
177
182
|
|
|
178
183
|
def eTotalEmissionArea(OptModel,n,ar):
|
|
179
|
-
if mTEPES.pEmission[p,ar] < math.inf and sum(mTEPES.pEmissionRate[g] for g in mTEPES.g if
|
|
180
|
-
return OptModel.vTotalEmissionArea[p,sc,n,ar] == (mTEPES.pLoadLevelDuration[p,sc,n]() * sum(mTEPES.pEmissionRate[nr] * 1e-3 * OptModel.vTotalOutput [p,sc,n,nr] for nr in mTEPES.nr if
|
|
181
|
-
+ mTEPES.pLoadLevelDuration[p,sc,n]() * sum(mTEPES.pEmissionRate[bo] * 1e-3 * OptModel.vTotalOutputHeat[p,sc,n,bo] for bo in mTEPES.bo if
|
|
184
|
+
if mTEPES.pEmission[p,ar] < math.inf and sum(mTEPES.pEmissionRate[g] for g in mTEPES.g if g in g2a[ar] and (p,g) in mTEPES.pg):
|
|
185
|
+
return OptModel.vTotalEmissionArea[p,sc,n,ar] == (mTEPES.pLoadLevelDuration[p,sc,n]() * sum(mTEPES.pEmissionRate[nr] * 1e-3 * OptModel.vTotalOutput [p,sc,n,nr] for nr in mTEPES.nr if nr in g2a[ar] and (p,nr) in mTEPES.pnr) #1e-3 to change from tCO2/MWh to MtCO2/GWh
|
|
186
|
+
+ mTEPES.pLoadLevelDuration[p,sc,n]() * sum(mTEPES.pEmissionRate[bo] * 1e-3 * OptModel.vTotalOutputHeat[p,sc,n,bo] for bo in mTEPES.bo if bo in g2a[ar] and (p,bo) in mTEPES.pbo)) #1e-3 to change from tCO2/MWh to MtCO2/GWh
|
|
182
187
|
else:
|
|
183
188
|
return Constraint.Skip
|
|
184
189
|
setattr(OptModel, f'eTotalEmissionArea_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.ar, rule=eTotalEmissionArea, doc='area total emission [MtCO2 eq]'))
|
|
185
190
|
|
|
186
191
|
def eTotalECostArea(OptModel,n,ar):
|
|
187
|
-
if sum(mTEPES.pEmissionVarCost[p,sc,n,g] for g in mTEPES.g if
|
|
188
|
-
return OptModel.vTotalECostArea[p,sc,n,ar] == (mTEPES.pLoadLevelDuration[p,sc,n]() * (sum(mTEPES.pEmissionVarCost[p,sc,n, g] * OptModel.vTotalOutput [p,sc,n, g] for g in mTEPES.g if
|
|
189
|
-
+ sum(mTEPES.pEmissionVarCost[p,sc,n,bo] * OptModel.vTotalOutputHeat[p,sc,n,bo] for bo in mTEPES.bo if
|
|
192
|
+
if sum(mTEPES.pEmissionVarCost[p,sc,n,g] for g in mTEPES.g if g in g2a[ar] and (p,g) in mTEPES.pg):
|
|
193
|
+
return OptModel.vTotalECostArea[p,sc,n,ar] == (mTEPES.pLoadLevelDuration[p,sc,n]() * (sum(mTEPES.pEmissionVarCost[p,sc,n, g] * OptModel.vTotalOutput [p,sc,n, g] for g in mTEPES.g if g in g2a[ar] and (p, g) in mTEPES.pg )
|
|
194
|
+
+ sum(mTEPES.pEmissionVarCost[p,sc,n,bo] * OptModel.vTotalOutputHeat[p,sc,n,bo] for bo in mTEPES.bo if bo in g2a[ar] and (p,bo) in mTEPES.pbo)))
|
|
190
195
|
else:
|
|
191
196
|
return Constraint.Skip
|
|
192
197
|
setattr(OptModel, f'eTotalECostArea_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.ar, rule=eTotalECostArea, doc='area emission cost [MEUR]'))
|
|
193
198
|
|
|
194
199
|
def eTotalRESEnergyArea(OptModel,n,ar):
|
|
195
200
|
if mTEPES.pRESEnergy[p,ar] and st == mTEPES.Last_st:
|
|
196
|
-
return OptModel.vTotalRESEnergyArea[p,sc,n,ar] == mTEPES.pLoadLevelDuration[p,sc,n]() * sum(OptModel.vTotalOutput[p,sc,n,re] for re in mTEPES.re if
|
|
201
|
+
return OptModel.vTotalRESEnergyArea[p,sc,n,ar] == mTEPES.pLoadLevelDuration[p,sc,n]() * sum(OptModel.vTotalOutput[p,sc,n,re] for re in mTEPES.re if re in g2a[ar] and (p,re) in mTEPES.pre)
|
|
197
202
|
else:
|
|
198
203
|
return Constraint.Skip
|
|
199
204
|
setattr(OptModel, f'eTotalRESEnergyArea_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.ar, rule=eTotalRESEnergyArea, doc='area RES energy [GWh]'))
|
|
@@ -224,6 +229,11 @@ def GenerationOperationModelFormulationInvestment(OptModel, mTEPES, pIndLogConso
|
|
|
224
229
|
|
|
225
230
|
StartTime = time.time()
|
|
226
231
|
|
|
232
|
+
g2a = defaultdict(list)
|
|
233
|
+
for ar,g in mTEPES.ar*mTEPES.g:
|
|
234
|
+
if (ar,g) in mTEPES.a2g:
|
|
235
|
+
g2a[ar].append(g)
|
|
236
|
+
|
|
227
237
|
def eInstallGenComm(OptModel,n,gc):
|
|
228
238
|
if gc in mTEPES.nr and gc not in mTEPES.eh and gc not in mTEPES.bc and (p,gc) in mTEPES.pgc and (mTEPES.pMinPowerElec[p,sc,n,gc] or mTEPES.pConstantVarCost[p,sc,n,gc]):
|
|
229
239
|
if mTEPES.pMustRun[gc] == 0:
|
|
@@ -290,11 +300,10 @@ def GenerationOperationModelFormulationInvestment(OptModel, mTEPES, pIndLogConso
|
|
|
290
300
|
print('eInstallConESS ... ', len(getattr(OptModel, f'eInstallConESS_{p}_{sc}_{st}')), ' rows')
|
|
291
301
|
|
|
292
302
|
def eUninstallGenComm(OptModel,n,gd):
|
|
293
|
-
if (p,gd) in mTEPES.pgd:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return Constraint.Skip
|
|
303
|
+
if (p,gd) not in mTEPES.pgd or gd not in mTEPES.nr or gd in mTEPES.eh:
|
|
304
|
+
return Constraint.Skip
|
|
305
|
+
if mTEPES.pMustRun[gd] == 0 and (mTEPES.pMinPowerElec[p,sc,n,gd] or mTEPES.pConstantVarCost[p,sc,n,gd]):
|
|
306
|
+
return OptModel.vCommitment[p,sc,n,gd] <= 1 - OptModel.vGenerationRetire[p,gd]
|
|
298
307
|
else:
|
|
299
308
|
return Constraint.Skip
|
|
300
309
|
setattr(OptModel, f'eUninstallGenComm_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.gd, rule=eUninstallGenComm, doc='commitment if uninstalled unit [p.u.]'))
|
|
@@ -303,11 +312,10 @@ def GenerationOperationModelFormulationInvestment(OptModel, mTEPES, pIndLogConso
|
|
|
303
312
|
print('eUninstallGenComm ... ', len(getattr(OptModel, f'eUninstallGenComm_{p}_{sc}_{st}')), ' rows')
|
|
304
313
|
|
|
305
314
|
def eUninstallGenCap(OptModel,n,gd):
|
|
306
|
-
if (p,gd) in mTEPES.pgd:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return Constraint.Skip
|
|
315
|
+
if (p,gd) not in mTEPES.pgd:
|
|
316
|
+
return Constraint.Skip
|
|
317
|
+
if mTEPES.pMaxPowerElec[p,sc,n,gd]:
|
|
318
|
+
return OptModel.vTotalOutput[p,sc,n,gd] / mTEPES.pMaxPowerElec[p,sc,n,gd] <= 1 - OptModel.vGenerationRetire[p,gd]
|
|
311
319
|
else:
|
|
312
320
|
return Constraint.Skip
|
|
313
321
|
setattr(OptModel, f'eUninstallGenCap_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.gd, rule=eUninstallGenCap, doc='output if uninstalled gen unit [p.u.]'))
|
|
@@ -316,10 +324,10 @@ def GenerationOperationModelFormulationInvestment(OptModel, mTEPES, pIndLogConso
|
|
|
316
324
|
print('eUninstallGenCap ... ', len(getattr(OptModel, f'eUninstallGenCap_{p}_{sc}_{st}')), ' rows')
|
|
317
325
|
|
|
318
326
|
def eAdequacyReserveMarginElec(OptModel,ar):
|
|
319
|
-
if mTEPES.pReserveMargin[p,ar] and st == mTEPES.Last_st and sum(1 for gc in mTEPES.gc if
|
|
320
|
-
return ((sum( mTEPES.pRatedMaxPowerElec[g ] * mTEPES.pAvailability[g ]() / (1.0-mTEPES.pEFOR[g ]) for g in mTEPES.g if (p,g ) in mTEPES.pg and
|
|
321
|
-
sum( OptModel.vGenerationInvest[p,gc] * mTEPES.pRatedMaxPowerElec[gc] * mTEPES.pAvailability[gc]() / (1.0-mTEPES.pEFOR[gc]) for gc in mTEPES.gc if (p,gc) in mTEPES.pgc and
|
|
322
|
-
sum((1-OptModel.vGenerationRetire[p,gd]) * mTEPES.pRatedMaxPowerElec[gd] * mTEPES.pAvailability[gd]() / (1.0-mTEPES.pEFOR[gd]) for gd in mTEPES.gd if (p,gd) in mTEPES.pgd and
|
|
327
|
+
if mTEPES.pReserveMargin[p,ar] and st == mTEPES.Last_st and sum(1 for gc in mTEPES.gc if gc in g2a[ar]) and sum(mTEPES.pRatedMaxPowerElec[g] * mTEPES.pAvailability[g]() / (1.0-mTEPES.pEFOR[g]) for g in mTEPES.g if (ar,g) in mTEPES.a2g and g not in (mTEPES.gc or mTEPES.gd)) <= mTEPES.pDemandElecPeak[p,ar] * mTEPES.pReserveMargin[p,ar]:
|
|
328
|
+
return ((sum( mTEPES.pRatedMaxPowerElec[g ] * mTEPES.pAvailability[g ]() / (1.0-mTEPES.pEFOR[g ]) for g in mTEPES.g if (p,g ) in mTEPES.pg and g in g2a[ar] and g not in (mTEPES.gc or mTEPES.gd)) +
|
|
329
|
+
sum( OptModel.vGenerationInvest[p,gc] * mTEPES.pRatedMaxPowerElec[gc] * mTEPES.pAvailability[gc]() / (1.0-mTEPES.pEFOR[gc]) for gc in mTEPES.gc if (p,gc) in mTEPES.pgc and gc in g2a[ar] ) +
|
|
330
|
+
sum((1-OptModel.vGenerationRetire[p,gd]) * mTEPES.pRatedMaxPowerElec[gd] * mTEPES.pAvailability[gd]() / (1.0-mTEPES.pEFOR[gd]) for gd in mTEPES.gd if (p,gd) in mTEPES.pgd and gd in g2a[ar] ) ) >= mTEPES.pDemandElecPeak[p,ar] * mTEPES.pReserveMargin[p,ar])
|
|
323
331
|
else:
|
|
324
332
|
return Constraint.Skip
|
|
325
333
|
setattr(OptModel, f'eAdequacyReserveMarginElec_{p}_{sc}_{st}', Constraint(mTEPES.ar, rule=eAdequacyReserveMarginElec, doc='electricity system adequacy reserve margin [p.u.]'))
|
|
@@ -330,9 +338,9 @@ def GenerationOperationModelFormulationInvestment(OptModel, mTEPES, pIndLogConso
|
|
|
330
338
|
def eAdequacyReserveMarginHeat(OptModel,ar):
|
|
331
339
|
if mTEPES.pIndHeat == 1:
|
|
332
340
|
if mTEPES.pReserveMarginHeat[p,ar] and st == mTEPES.Last_st and sum(1 for gc in mTEPES.gc if (ar,gc) in mTEPES.a2g) and sum(mTEPES.pRatedMaxPowerHeat[g] * mTEPES.pAvailability[g]() / (1.0-mTEPES.pEFOR[g]) for g in mTEPES.g if (ar,g) in mTEPES.a2g and g not in (mTEPES.gc or mTEPES.gd)) <= mTEPES.pDemandHeatPeak[p,ar] * mTEPES.pReserveMarginHeat[p,ar]:
|
|
333
|
-
return ((sum( mTEPES.pRatedMaxPowerHeat[g ] * mTEPES.pAvailability[g ]() / (1.0-mTEPES.pEFOR[g ]) for g in mTEPES.g if (p,g ) in mTEPES.pg and
|
|
334
|
-
sum( OptModel.vGenerationInvest[p,gc] * mTEPES.pRatedMaxPowerHeat[gc] * mTEPES.pAvailability[gc]() / (1.0-mTEPES.pEFOR[gc]) for gc in mTEPES.gc if (p,gc) in mTEPES.pgc and
|
|
335
|
-
sum((1-OptModel.vGenerationRetire[p,gd]) * mTEPES.pRatedMaxPowerHeat[gd] * mTEPES.pAvailability[gd]() / (1.0-mTEPES.pEFOR[gd]) for gd in mTEPES.gd if (p,gd) in mTEPES.pgd and
|
|
341
|
+
return ((sum( mTEPES.pRatedMaxPowerHeat[g ] * mTEPES.pAvailability[g ]() / (1.0-mTEPES.pEFOR[g ]) for g in mTEPES.g if (p,g ) in mTEPES.pg and g in g2a[ar] and g not in (mTEPES.gc or mTEPES.gd)) +
|
|
342
|
+
sum( OptModel.vGenerationInvest[p,gc] * mTEPES.pRatedMaxPowerHeat[gc] * mTEPES.pAvailability[gc]() / (1.0-mTEPES.pEFOR[gc]) for gc in mTEPES.gc if (p,gc) in mTEPES.pgc and gc in g2a[ar] ) +
|
|
343
|
+
sum((1-OptModel.vGenerationRetire[p,gd]) * mTEPES.pRatedMaxPowerHeat[gd] * mTEPES.pAvailability[gd]() / (1.0-mTEPES.pEFOR[gd]) for gd in mTEPES.gd if (p,gd) in mTEPES.pgd and gd in g2a[ar] ) ) >= mTEPES.pDemandHeatPeak[p,ar] * mTEPES.pReserveMarginHeat[p,ar])
|
|
336
344
|
else:
|
|
337
345
|
return Constraint.Skip
|
|
338
346
|
else:
|
|
@@ -343,7 +351,7 @@ def GenerationOperationModelFormulationInvestment(OptModel, mTEPES, pIndLogConso
|
|
|
343
351
|
print('eAdeqReserveMarginHeat ... ', len(getattr(OptModel, f'eAdequacyReserveMarginHeat_{p}_{sc}_{st}')), ' rows')
|
|
344
352
|
|
|
345
353
|
def eMaxSystemEmission(OptModel,ar):
|
|
346
|
-
if mTEPES.pEmission[p,ar] < math.inf and sum(mTEPES.pEmissionRate[nr] for nr in mTEPES.nr if
|
|
354
|
+
if mTEPES.pEmission[p,ar] < math.inf and sum(mTEPES.pEmissionRate[nr] for nr in mTEPES.nr if nr in g2a[ar]) and st == mTEPES.Last_st:
|
|
347
355
|
# There is an emission limit, there are generators with emissions in the Area and it is the last stage
|
|
348
356
|
return sum(OptModel.vTotalEmissionArea[p,sc,na,ar] for na in mTEPES.na) <= mTEPES.pEmission[p,ar]
|
|
349
357
|
else:
|
|
@@ -411,7 +419,7 @@ def GenerationOperationModelFormulationDemand(OptModel, mTEPES, pIndLogConsole,
|
|
|
411
419
|
|
|
412
420
|
def eSystemInertia(OptModel,n,ar):
|
|
413
421
|
if mTEPES.pSystemInertia[p,sc,n,ar] and sum(1 for nr in n2a[ar]):
|
|
414
|
-
return sum(OptModel.vCommitment[p,sc,n,nr] * mTEPES.pInertia[nr] for nr in mTEPES.nr if
|
|
422
|
+
return sum(OptModel.vCommitment[p,sc,n,nr] * mTEPES.pInertia[nr] for nr in mTEPES.nr if nr in n2a[ar]) >= mTEPES.pSystemInertia[p,sc,n,ar]
|
|
415
423
|
else:
|
|
416
424
|
return Constraint.Skip
|
|
417
425
|
setattr(OptModel, f'eSystemInertia_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.ar, rule=eSystemInertia, doc='system inertia [s]'))
|
|
@@ -701,7 +709,9 @@ def GenerationOperationModelFormulationStorage(OptModel, mTEPES, pIndLogConsole,
|
|
|
701
709
|
|
|
702
710
|
def eMaxCharge(OptModel,n,eh):
|
|
703
711
|
# Check if generator is available in the period and has variable charging capacity
|
|
704
|
-
if (p,eh) not in mTEPES.peh
|
|
712
|
+
if (p,eh) not in mTEPES.peh:
|
|
713
|
+
return Constraint.Skip
|
|
714
|
+
if mTEPES.pMaxCharge2ndBlock[p,sc,n,eh] == 0.0:
|
|
705
715
|
return Constraint.Skip
|
|
706
716
|
# Hydro units have commitment while ESS units are implicitly always committed
|
|
707
717
|
if eh not in mTEPES.h:
|
|
@@ -1063,9 +1073,11 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1063
1073
|
a2n[nr].append(ar)
|
|
1064
1074
|
|
|
1065
1075
|
def eMaxOutput2ndBlock(OptModel,n,nr):
|
|
1076
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1077
|
+
return Constraint.Skip
|
|
1066
1078
|
if mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0:
|
|
1067
1079
|
return Constraint.Skip
|
|
1068
|
-
if (
|
|
1080
|
+
if (nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr]))):
|
|
1069
1081
|
if sum(mTEPES.pOperReserveUp[p,sc,n,ar] for ar in a2n[nr]):
|
|
1070
1082
|
if mTEPES.pIndRampReserves == 0 or sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) == 0.0:
|
|
1071
1083
|
if mTEPES.pIndOperReserveGen[nr] != 1 and n != mTEPES.n.last():
|
|
@@ -1108,9 +1120,11 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1108
1120
|
print('eMaxOutput2ndBlock ... ', len(getattr(OptModel, f'eMaxOutput2ndBlock_{p}_{sc}_{st}')), ' rows')
|
|
1109
1121
|
|
|
1110
1122
|
def eMinOutput2ndBlock(OptModel,n,nr):
|
|
1123
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1124
|
+
return Constraint.Skip
|
|
1111
1125
|
if mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0:
|
|
1112
1126
|
return Constraint.Skip
|
|
1113
|
-
if (
|
|
1127
|
+
if (nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr]))):
|
|
1114
1128
|
if sum(mTEPES.pOperReserveDw[p,sc,n,ar] for ar in a2n[nr]):
|
|
1115
1129
|
if mTEPES.pIndOperReserveGen[nr] != 1 and (mTEPES.pIndRampReserves == 0 or sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) == 0.0):
|
|
1116
1130
|
return OptModel.vOutput2ndBlock[p,sc,n,nr] - OptModel.vReserveDown[p,sc,n,nr] >= 0.0
|
|
@@ -1131,7 +1145,9 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1131
1145
|
print('eMinOutput2ndBlock ... ', len(getattr(OptModel, f'eMinOutput2ndBlock_{p}_{sc}_{st}')), ' rows')
|
|
1132
1146
|
|
|
1133
1147
|
def eTotalOutput(OptModel,n,nr):
|
|
1134
|
-
if (p,nr)
|
|
1148
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1149
|
+
return Constraint.Skip
|
|
1150
|
+
if (mTEPES.pMustRun[nr] == 0 or mTEPES.pMaxPower2ndBlock[p,sc,n,nr] or nr in mTEPES.gc) and (nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr]))):
|
|
1135
1151
|
if mTEPES.pMaxPowerElec[p,sc,n,nr]:
|
|
1136
1152
|
if mTEPES.pMinPowerElec[p,sc,n,nr] == 0.0:
|
|
1137
1153
|
return OptModel.vTotalOutput[p,sc,n,nr] == OptModel.vOutput2ndBlock[p,sc,n,nr] + mTEPES.pUpReserveActivation * OptModel.vReserveUp[p,sc,n,nr] - mTEPES.pDwReserveActivation * OptModel.vReserveDown[p,sc,n,nr]
|
|
@@ -1147,14 +1163,13 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1147
1163
|
print('eTotalOutput ... ', len(getattr(OptModel, f'eTotalOutput_{p}_{sc}_{st}')), ' rows')
|
|
1148
1164
|
|
|
1149
1165
|
def eUCStrShut(OptModel,n,nr):
|
|
1150
|
-
if (p,nr) in mTEPES.pnr:
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
return OptModel.vCommitment[p,sc,n,nr] - OptModel.vCommitment[p,sc,mTEPES.n.prev(n),nr] == OptModel.vStartUp[p,sc,n,nr] - OptModel.vShutDown[p,sc,n,nr]
|
|
1166
|
+
if (p,nr) not in mTEPES.pnr or nr in mTEPES.eh:
|
|
1167
|
+
return Constraint.Skip
|
|
1168
|
+
if mTEPES.pMustRun[nr] == 0 and (mTEPES.pMinPowerElec[p,sc,n,nr] or mTEPES.pConstantVarCost[p,sc,n,nr]):
|
|
1169
|
+
if n == mTEPES.n.first():
|
|
1170
|
+
return OptModel.vCommitment[p,sc,n,nr] - mTEPES.pInitialUC[p,sc,n,nr]() == OptModel.vStartUp[p,sc,n,nr] - OptModel.vShutDown[p,sc,n,nr]
|
|
1156
1171
|
else:
|
|
1157
|
-
return
|
|
1172
|
+
return OptModel.vCommitment[p,sc,n,nr] - OptModel.vCommitment[p,sc,mTEPES.n.prev(n),nr] == OptModel.vStartUp[p,sc,n,nr] - OptModel.vShutDown[p,sc,n,nr]
|
|
1158
1173
|
else:
|
|
1159
1174
|
return Constraint.Skip
|
|
1160
1175
|
setattr(OptModel, f'eUCStrShut_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.nr, rule=eUCStrShut, doc='relation among commitment startup and shutdown [p.u.]'))
|
|
@@ -1163,13 +1178,11 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1163
1178
|
print('eUCStrShut ... ', len(getattr(OptModel, f'eUCStrShut_{p}_{sc}_{st}')), ' rows')
|
|
1164
1179
|
|
|
1165
1180
|
def eStableStates(OptModel,n,nr):
|
|
1166
|
-
if (p,nr) in mTEPES.pnr:
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
else:
|
|
1170
|
-
return Constraint.Skip
|
|
1171
|
-
else:
|
|
1181
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1182
|
+
return Constraint.Skip
|
|
1183
|
+
if mTEPES.pStableTime[nr] == 0.0 or mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0:
|
|
1172
1184
|
return Constraint.Skip
|
|
1185
|
+
return OptModel.vStableState[p,sc,n,nr] + OptModel.vRampUpState[p,sc,n,nr] + OptModel.vRampDwState[p,sc,n,nr] == OptModel.vCommitment[p,sc,n,nr]
|
|
1173
1186
|
setattr(OptModel, f'eStableStates_{p}_{sc}_{st}', Constraint(mTEPES.n, mTEPES.nr, rule=eStableStates, doc='relation among stable, ramp up and ramp down states [p.u.]'))
|
|
1174
1187
|
|
|
1175
1188
|
if pIndLogConsole == 1:
|
|
@@ -1177,10 +1190,8 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1177
1190
|
|
|
1178
1191
|
def eMaxCommitmentYearly(OptModel,n,group,nr):
|
|
1179
1192
|
# Skip if generator not available on period
|
|
1180
|
-
if (p,nr) not in mTEPES.pnr:
|
|
1181
|
-
return Constraint.Skip
|
|
1182
1193
|
# Skip if the generator is not part of the exclusive group
|
|
1183
|
-
if nr not in mTEPES.GeneratorsInYearlyGroup[group]:
|
|
1194
|
+
if (p,nr) not in mTEPES.pnr or nr not in mTEPES.GeneratorsInYearlyGroup[group]:
|
|
1184
1195
|
return Constraint.Skip
|
|
1185
1196
|
# Skip if there are one or fewer generators in the group
|
|
1186
1197
|
if len(mTEPES.GeneratorsInYearlyGroup[group] & {nr for p,nr in mTEPES.pnr}) <= 1:
|
|
@@ -1194,10 +1205,8 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1194
1205
|
|
|
1195
1206
|
def eMaxCommitGenYearly(OptModel,n,group,nr):
|
|
1196
1207
|
# Skip if generator not available on period
|
|
1197
|
-
if (p,nr) not in mTEPES.pnr:
|
|
1198
|
-
return Constraint.Skip
|
|
1199
1208
|
# Skip if the generator is not part of the exclusive group
|
|
1200
|
-
if nr not in mTEPES.GeneratorsInYearlyGroup[group]:
|
|
1209
|
+
if (p,nr) not in mTEPES.pnr or nr not in mTEPES.GeneratorsInYearlyGroup[group]:
|
|
1201
1210
|
return Constraint.Skip
|
|
1202
1211
|
# Avoid division by 0. If Maximum power is 0 this equation is not needed anyways
|
|
1203
1212
|
if mTEPES.pMaxPowerElec[p,sc,n,nr] == 0.0:
|
|
@@ -1215,7 +1224,7 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1215
1224
|
# Skip if there are one or fewer generators in the group
|
|
1216
1225
|
if len(mTEPES.GeneratorsInYearlyGroup[group] & {nr for p,nr in mTEPES.pnr}) <= 1:
|
|
1217
1226
|
return Constraint.Skip
|
|
1218
|
-
return sum(OptModel.vMaxCommitmentYearly[p,sc,nr,group] + (OptModel.vCommitmentCons[p,sc,nr] if nr in mTEPES.h else 0) for nr in mTEPES.GeneratorsInYearlyGroup[group] if (p,nr) in mTEPES.pnr
|
|
1227
|
+
return sum(OptModel.vMaxCommitmentYearly[p,sc,nr,group] + (OptModel.vCommitmentCons[p,sc,nr] if nr in mTEPES.h else 0) for nr in mTEPES.GeneratorsInYearlyGroup[group] if (p,nr) in mTEPES.pnr) <= 1
|
|
1219
1228
|
setattr(OptModel, f'eExclusiveGensYearly_{p}_{sc}_{st}', Constraint(mTEPES.ExclusiveGroupsYearly, rule=eExclusiveGensYearly, doc='mutually exclusive generators'))
|
|
1220
1229
|
|
|
1221
1230
|
if pIndLogConsole == 1:
|
|
@@ -1227,10 +1236,8 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1227
1236
|
|
|
1228
1237
|
def eMaxCommitmentHourly(OptModel,n,group,nr):
|
|
1229
1238
|
# Skip if generator not available on period
|
|
1230
|
-
if (p,nr) not in mTEPES.pnr:
|
|
1231
|
-
return Constraint.Skip
|
|
1232
1239
|
# Skip if the generator is not part of the exclusive group
|
|
1233
|
-
if nr not in mTEPES.GeneratorsInHourlyGroup[group]:
|
|
1240
|
+
if (p,nr) not in mTEPES.pnr or nr not in mTEPES.GeneratorsInHourlyGroup[group]:
|
|
1234
1241
|
return Constraint.Skip
|
|
1235
1242
|
# Skip if there are one or fewer generators in the group
|
|
1236
1243
|
if len(mTEPES.GeneratorsInHourlyGroup[group] & {nr for p,nr in mTEPES.pnr}) <= 1:
|
|
@@ -1243,10 +1250,8 @@ def GenerationOperationModelFormulationCommitment(OptModel, mTEPES, pIndLogConso
|
|
|
1243
1250
|
|
|
1244
1251
|
def eMaxCommitGenHourly(OptModel,n,group,nr):
|
|
1245
1252
|
# Skip if generator not available on period
|
|
1246
|
-
if (p,nr) not in mTEPES.pnr:
|
|
1247
|
-
return Constraint.Skip
|
|
1248
1253
|
# Skip if the generator is not part of the exclusive group
|
|
1249
|
-
if nr not in mTEPES.GeneratorsInHourlyGroup[group]:
|
|
1254
|
+
if (p,nr) not in mTEPES.pnr or nr not in mTEPES.GeneratorsInHourlyGroup[group]:
|
|
1250
1255
|
return Constraint.Skip
|
|
1251
1256
|
# Avoid division by 0. If Maximum power is 0 this equation is not needed anyways
|
|
1252
1257
|
if mTEPES.pMaxPowerElec[p,sc,n,nr] == 0.0:
|
|
@@ -1284,27 +1289,27 @@ def GenerationOperationModelFormulationRampMinTime(OptModel, mTEPES, pIndLogCons
|
|
|
1284
1289
|
StartTime = time.time()
|
|
1285
1290
|
|
|
1286
1291
|
def eSystemRampUp(OptModel,n):
|
|
1287
|
-
if mTEPES.pIndRampReserves ==
|
|
1288
|
-
return sum(OptModel.vRampReserveUp[p,sc,n,nr] for nr in mTEPES.nr if (p,nr) in mTEPES.pnr and (nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr])))) / mTEPES.pDuration[p,sc,n]() >= sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar)
|
|
1289
|
-
else:
|
|
1292
|
+
if mTEPES.pIndRampReserves == 0 or sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar) == 0.0:
|
|
1290
1293
|
return Constraint.Skip
|
|
1294
|
+
return sum(OptModel.vRampReserveUp[p,sc,n,nr] for nr in mTEPES.nr if (p,nr) in mTEPES.pnr and (nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr])))) / mTEPES.pDuration[p,sc,n]() >= sum(mTEPES.pRampReserveUp[p,sc,n,ar] for ar in mTEPES.ar)
|
|
1291
1295
|
setattr(OptModel, f'eSystemRampUp_{p}_{sc}_{st}', Constraint(mTEPES.n, rule=eSystemRampUp, doc='minimum system ramp up [p.u.]'))
|
|
1292
1296
|
|
|
1293
1297
|
if pIndLogConsole == 1:
|
|
1294
1298
|
print('eSystemRampUp ... ', len(getattr(OptModel, f'eSystemRampUp_{p}_{sc}_{st}')), ' rows')
|
|
1295
1299
|
|
|
1296
1300
|
def eSystemRampDw(OptModel,n):
|
|
1297
|
-
if mTEPES.pIndRampReserves ==
|
|
1298
|
-
return sum(OptModel.vRampReserveDw[p,sc,n,nr] for nr in mTEPES.nr if (p,nr) in mTEPES.pnr and (nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr])))) / mTEPES.pDuration[p,sc,n]() >= sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar)
|
|
1299
|
-
else:
|
|
1301
|
+
if mTEPES.pIndRampReserves == 0 or sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar) == 0.0:
|
|
1300
1302
|
return Constraint.Skip
|
|
1303
|
+
return sum(OptModel.vRampReserveDw[p,sc,n,nr] for nr in mTEPES.nr if (p,nr) in mTEPES.pnr and (nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr])))) / mTEPES.pDuration[p,sc,n]() >= sum(mTEPES.pRampReserveDw[p,sc,n,ar] for ar in mTEPES.ar)
|
|
1301
1304
|
setattr(OptModel, f'eSystemRampDw_{p}_{sc}_{st}', Constraint(mTEPES.n, rule=eSystemRampDw, doc='minimum system ramp down [p.u.]'))
|
|
1302
1305
|
|
|
1303
1306
|
if pIndLogConsole == 1:
|
|
1304
1307
|
print('eSystemRampDw ... ', len(getattr(OptModel, f'eSystemRampDw_{p}_{sc}_{st}')), ' rows')
|
|
1305
1308
|
|
|
1306
1309
|
def eRampUp(OptModel,n,nr):
|
|
1307
|
-
if (p,nr)
|
|
1310
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1311
|
+
return Constraint.Skip
|
|
1312
|
+
if nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr])):
|
|
1308
1313
|
if mTEPES.pRampUp[nr] and mTEPES.pIndBinGenRamps() == 1 and mTEPES.pRampUp[nr]*mTEPES.pDuration[p,sc,n]() < mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and mTEPES.pDuration[p,sc,n]():
|
|
1309
1314
|
if n == mTEPES.n.first():
|
|
1310
1315
|
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr],0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr] + OptModel.vReserveUp [p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] <= OptModel.vCommitment[p,sc,n,nr] - OptModel.vStartUp[p,sc,n,nr]
|
|
@@ -1320,7 +1325,9 @@ def GenerationOperationModelFormulationRampMinTime(OptModel, mTEPES, pIndLogCons
|
|
|
1320
1325
|
print('eRampUp ... ', len(getattr(OptModel, f'eRampUp_{p}_{sc}_{st}')), ' rows')
|
|
1321
1326
|
|
|
1322
1327
|
def eRampDw(OptModel,n,nr):
|
|
1323
|
-
if (p,nr)
|
|
1328
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1329
|
+
return Constraint.Skip
|
|
1330
|
+
if nr not in mTEPES.es or (nr in mTEPES.es and (mTEPES.pTotalMaxCharge[nr] or mTEPES.pTotalEnergyInflows[nr])):
|
|
1324
1331
|
if mTEPES.pRampDw[nr] and mTEPES.pIndBinGenRamps() == 1 and mTEPES.pRampDw[nr]*mTEPES.pDuration[p,sc,n]() < mTEPES.pMaxPower2ndBlock[p,sc,n,nr] and mTEPES.pDuration[p,sc,n]():
|
|
1325
1332
|
if n == mTEPES.n.first():
|
|
1326
1333
|
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr],0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr] + OptModel.vReserveDown[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] >= - mTEPES.pInitialUC[p,sc,n,nr]() + OptModel.vShutDown[p,sc,n,nr]
|
|
@@ -1376,73 +1383,74 @@ def GenerationOperationModelFormulationRampMinTime(OptModel, mTEPES, pIndLogCons
|
|
|
1376
1383
|
pEpsilon = 1e-4
|
|
1377
1384
|
|
|
1378
1385
|
def eRampUpState(OptModel,n,nr):
|
|
1379
|
-
if
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1387
|
+
return Constraint.Skip
|
|
1388
|
+
if mTEPES.pStableTime[nr] == 0.0 or mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0 or mTEPES.pDuration[p,sc,n]() == 0.0:
|
|
1389
|
+
return Constraint.Skip
|
|
1390
|
+
if pIndStableTimeDeadBand:
|
|
1391
|
+
if mTEPES.pRampUp[nr]:
|
|
1392
|
+
if n == mTEPES.n.first():
|
|
1393
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * (OptModel.vRampDwState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1386
1394
|
else:
|
|
1387
|
-
|
|
1388
|
-
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * (OptModel.vRampDwState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1389
|
-
else:
|
|
1390
|
-
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * (OptModel.vRampDwState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1395
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * (OptModel.vRampDwState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1391
1396
|
else:
|
|
1392
|
-
if mTEPES.
|
|
1393
|
-
|
|
1394
|
-
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1395
|
-
else:
|
|
1396
|
-
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1397
|
+
if n == mTEPES.n.first():
|
|
1398
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * (OptModel.vRampDwState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1397
1399
|
else:
|
|
1398
|
-
|
|
1399
|
-
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1400
|
-
else:
|
|
1401
|
-
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1400
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * (OptModel.vRampDwState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1402
1401
|
else:
|
|
1403
|
-
|
|
1402
|
+
if mTEPES.pRampUp[nr]:
|
|
1403
|
+
if n == mTEPES.n.first():
|
|
1404
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1405
|
+
else:
|
|
1406
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampUp[nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1407
|
+
else:
|
|
1408
|
+
if n == mTEPES.n.first():
|
|
1409
|
+
return (- max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1410
|
+
else:
|
|
1411
|
+
return (- OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] + OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampUpState[p,sc,n,nr] - pEpsilon * OptModel.vRampDwState[p,sc,n,nr]
|
|
1404
1412
|
setattr(OptModel, f'eRampUpState_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.nr, rule=eRampUpState, doc='ramp up state [p.u.]'))
|
|
1405
1413
|
|
|
1406
1414
|
if pIndLogConsole == 1:
|
|
1407
1415
|
print('eRampUpState ... ', len(getattr(OptModel, f'eRampUpState_{p}_{sc}_{st}')), ' rows')
|
|
1408
1416
|
|
|
1409
1417
|
def eRampDwState(OptModel,n,nr):
|
|
1410
|
-
if
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1418
|
+
if (p,nr) not in mTEPES.pnr:
|
|
1419
|
+
return Constraint.Skip
|
|
1420
|
+
if mTEPES.pStableTime[nr] == 0.0 or mTEPES.pMaxPower2ndBlock[p,sc,n,nr] == 0.0 or mTEPES.pDuration[p,sc,n]() == 0.0:
|
|
1421
|
+
return Constraint.Skip
|
|
1422
|
+
if pIndStableTimeDeadBand:
|
|
1423
|
+
if mTEPES.pRampDw[nr]:
|
|
1424
|
+
if n == mTEPES.n.first():
|
|
1425
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * (OptModel.vRampUpState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1417
1426
|
else:
|
|
1418
|
-
|
|
1419
|
-
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * (OptModel.vRampUpState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1420
|
-
else:
|
|
1421
|
-
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * (OptModel.vRampUpState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1427
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * (OptModel.vRampUpState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1422
1428
|
else:
|
|
1423
|
-
if mTEPES.
|
|
1424
|
-
|
|
1425
|
-
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1426
|
-
else:
|
|
1427
|
-
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1429
|
+
if n == mTEPES.n.first():
|
|
1430
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * (OptModel.vRampUpState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1428
1431
|
else:
|
|
1429
|
-
|
|
1430
|
-
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1431
|
-
else:
|
|
1432
|
-
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1432
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * (OptModel.vRampUpState[p,sc,n,nr] - OptModel.vStableState[p,sc,n,nr])
|
|
1433
1433
|
else:
|
|
1434
|
-
|
|
1434
|
+
if mTEPES.pRampDw[nr]:
|
|
1435
|
+
if n == mTEPES.n.first():
|
|
1436
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1437
|
+
else:
|
|
1438
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pRampDw[nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1439
|
+
else:
|
|
1440
|
+
if n == mTEPES.n.first():
|
|
1441
|
+
return (max(mTEPES.pInitialOutput[p,sc,n,nr]() - mTEPES.pMinPowerElec[p,sc,n,nr], 0.0) - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1442
|
+
else:
|
|
1443
|
+
return (OptModel.vOutput2ndBlock[p,sc,mTEPES.n.prev(n),nr] - OptModel.vOutput2ndBlock[p,sc,n,nr]) / mTEPES.pDuration[p,sc,n]() / mTEPES.pMaxPower2ndBlock[p,sc,n,nr] <= OptModel.vRampDwState[p,sc,n,nr] - pEpsilon * OptModel.vRampUpState[p,sc,n,nr]
|
|
1435
1444
|
setattr(OptModel, f'eRampDwState_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.nr, rule=eRampDwState, doc='maximum ramp down [p.u.]'))
|
|
1436
1445
|
|
|
1437
1446
|
if pIndLogConsole == 1:
|
|
1438
1447
|
print('eRampDwState ... ', len(getattr(OptModel, f'eRampDwState_{p}_{sc}_{st}')), ' rows')
|
|
1439
1448
|
|
|
1440
1449
|
def eMinUpTime(OptModel,n,t):
|
|
1441
|
-
if (p,t) in mTEPES.pg:
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
return Constraint.Skip
|
|
1450
|
+
if (p,t) not in mTEPES.pg:
|
|
1451
|
+
return Constraint.Skip
|
|
1452
|
+
if mTEPES.pMustRun[t] == 0 and mTEPES.pIndBinGenMinTime() == 1 and (mTEPES.pMinPowerElec[p,sc,n,t] or mTEPES.pConstantVarCost[p,sc,n,t]) and t not in mTEPES.eh and mTEPES.pUpTime[t] > 1 and mTEPES.n.ord(n) >= mTEPES.pUpTime[t]:
|
|
1453
|
+
return sum(OptModel.vStartUp [p,sc,n2,t] for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)+1-mTEPES.pUpTime[t]:mTEPES.n.ord(n)]) <= OptModel.vCommitment[p,sc,n,t]
|
|
1446
1454
|
else:
|
|
1447
1455
|
return Constraint.Skip
|
|
1448
1456
|
setattr(OptModel, f'eMinUpTime_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.tr, rule=eMinUpTime , doc='minimum up time [p.u.]'))
|
|
@@ -1451,11 +1459,10 @@ def GenerationOperationModelFormulationRampMinTime(OptModel, mTEPES, pIndLogCons
|
|
|
1451
1459
|
print('eMinUpTime ... ', len(getattr(OptModel, f'eMinUpTime_{p}_{sc}_{st}')), ' rows')
|
|
1452
1460
|
|
|
1453
1461
|
def eMinDownTime(OptModel,n,t):
|
|
1454
|
-
if (p,t) in mTEPES.pg:
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
return Constraint.Skip
|
|
1462
|
+
if (p,t) not in mTEPES.pg:
|
|
1463
|
+
return Constraint.Skip
|
|
1464
|
+
if mTEPES.pMustRun[t] == 0 and mTEPES.pIndBinGenMinTime() == 1 and (mTEPES.pMinPowerElec[p,sc,n,t] or mTEPES.pConstantVarCost[p,sc,n,t]) and t not in mTEPES.eh and mTEPES.pDwTime[t] > 1 and mTEPES.n.ord(n) >= mTEPES.pDwTime[t]:
|
|
1465
|
+
return sum(OptModel.vShutDown[p,sc,n2,t] for n2 in list(mTEPES.n2)[mTEPES.n.ord(n)+1-mTEPES.pDwTime[t]:mTEPES.n.ord(n)]) <= 1 - OptModel.vCommitment[p,sc,n,t]
|
|
1459
1466
|
else:
|
|
1460
1467
|
return Constraint.Skip
|
|
1461
1468
|
setattr(OptModel, f'eMinDownTime_{p}_{sc}_{st}', Constraint(mTEPES.n*mTEPES.tr, rule=eMinDownTime, doc='minimum down time [p.u.]'))
|
|
@@ -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) - February 05, 2026
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -627,7 +627,7 @@ def GenerationOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnolo
|
|
|
627
627
|
OutputToFile = pd.Series(data=[sum(OutputToFile[p,sc,n,nr] for nr in n2n[nt] if (p,nr) in mTEPES.pnr) for p,sc,n,nt in mTEPES.psnnt], index=mTEPES.psnnt)
|
|
628
628
|
OutputToFile.to_frame(name='MW').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MW').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_TechnologyOperatingReserveDown_{CaseName}.csv', sep=',')
|
|
629
629
|
|
|
630
|
-
if mTEPES.
|
|
630
|
+
if mTEPES.eh:
|
|
631
631
|
OutputToFile = pd.Series(data=[OptModel.vESSReserveDown[p,sc,n,eh]() for p,sc,n,eh in mTEPES.psnehc], index=mTEPES.psnehc)
|
|
632
632
|
OutputToFile = OutputToFile.fillna(0.0)
|
|
633
633
|
OutputToFile *= 1e3
|
|
@@ -912,7 +912,7 @@ def ESSOperationResults(DirName, CaseName, OptModel, mTEPES, pIndTechnologyOutpu
|
|
|
912
912
|
|
|
913
913
|
# Check if there are any ESS with consumption capabilities
|
|
914
914
|
# If there are none, just skip outputting consumption related files
|
|
915
|
-
if mTEPES.
|
|
915
|
+
if mTEPES.eh:
|
|
916
916
|
OutputToFile = pd.Series(data=[OptModel.vESSTotalCharge [p,sc,n,eh]() for p,sc,n,eh in mTEPES.psnehc], index=mTEPES.psnehc)
|
|
917
917
|
OutputToFile *= -1e3
|
|
918
918
|
OutputToFile.to_frame(name='MW').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MW', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_Consumption_{CaseName}.csv', sep=',')
|
|
@@ -1866,7 +1866,7 @@ def MarginalResults(DirName, CaseName, OptModel, mTEPES, pIndPlotOutput):
|
|
|
1866
1866
|
OutputToFile.rename_axis(['Period', 'Scenario', 'LoadLevel', 'Area'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_MarginalIncrementalVariableCost_{CaseName}.csv', sep=',')
|
|
1867
1867
|
IncrementalGens = pd.Series('N/A', index=mTEPES.psnar).to_frame(name='Generator')
|
|
1868
1868
|
for p,sc,n,ar in mTEPES.psnar:
|
|
1869
|
-
if all(g not in mTEPES.eh and g not in mTEPES.bo and (p, g) in mTEPES.pg and
|
|
1869
|
+
if all(g not in mTEPES.eh and g not in mTEPES.bo and (p, g) in mTEPES.pg and g in g2a[ar] for g in mTEPES.g):
|
|
1870
1870
|
if len(OutputToFile.loc[(p,sc,n,ar)]) > 1:
|
|
1871
1871
|
IncrementalGens.loc[p,sc,n,ar] = OutputToFile.loc[[(p,sc,n,ar)]].squeeze().idxmin()
|
|
1872
1872
|
else:
|
|
@@ -1947,7 +1947,7 @@ def MarginalResults(DirName, CaseName, OptModel, mTEPES, pIndPlotOutput):
|
|
|
1947
1947
|
OutputResults = pd.Series(data=[mTEPES.pDuals[''.join([f'eAdequacyReserveMarginHeat_{p}_{sc}_{st}{ar}'])] for p,sc,st,ar in sPSSTAR], index=pd.Index(sPSSTAR))
|
|
1948
1948
|
OutputResults.to_frame(name='RM').reset_index().pivot_table(index=['level_0','level_1'], columns='level_3', values='RM').rename_axis(['Period', 'Scenario'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_MarginalReserveMarginHeat_{CaseName}.csv', sep=',')
|
|
1949
1949
|
|
|
1950
|
-
sPSSTAR = [(p,sc,st,ar) for p,sc,st,ar in mTEPES.ps*mTEPES.st*mTEPES.ar if mTEPES.pEmission[p,ar] < math.inf and st == mTEPES.Last_st and (p,sc,n) in mTEPES.psn and sum(mTEPES.pEmissionVarCost[p,sc,na,g] for na,g in mTEPES.na*mTEPES.g if
|
|
1950
|
+
sPSSTAR = [(p,sc,st,ar) for p,sc,st,ar in mTEPES.ps*mTEPES.st*mTEPES.ar if mTEPES.pEmission[p,ar] < math.inf and st == mTEPES.Last_st and (p,sc,n) in mTEPES.psn and sum(mTEPES.pEmissionVarCost[p,sc,na,g] for na,g in mTEPES.na*mTEPES.g if g in g2a[ar] and (p,g) in mTEPES.pg)]
|
|
1951
1951
|
if sPSSTAR:
|
|
1952
1952
|
OutputResults = pd.Series(data=[mTEPES.pDuals[''.join([f'eMaxSystemEmission_{p}_{sc}_{st}{ar}'])] for p,sc,st,ar in sPSSTAR], index=pd.Index(sPSSTAR))
|
|
1953
1953
|
OutputResults.to_frame(name='EM').reset_index().pivot_table(index=['level_0','level_1'], columns='level_3', values='EM').rename_axis(['Period', 'Scenario'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_MarginalEmission_{CaseName}.csv', sep=',')
|
|
@@ -2305,7 +2305,7 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2305
2305
|
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pConstantVarCost[p,sc,n,nr] * OptModel.vCommitment [p,sc,n,nr]() +
|
|
2306
2306
|
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pStartUpCost [ nr] * OptModel.vStartUp [p,sc,n,nr]() +
|
|
2307
2307
|
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight [p,sc,n]() * mTEPES.pShutDownCost [ nr] * OptModel.vShutDown [p,sc,n,nr]()) for p,sc,n,nr in mTEPES.psnnr], index=mTEPES.psnnr)
|
|
2308
|
-
if mTEPES.
|
|
2308
|
+
if mTEPES.nr:
|
|
2309
2309
|
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_GenerationCostOperation_{CaseName}.csv', sep=',')
|
|
2310
2310
|
|
|
2311
2311
|
if mTEPES.re:
|
|
@@ -2318,7 +2318,7 @@ def EconomicResults(DirName, CaseName, OptModel, mTEPES, pIndAreaOutput, pIndPlo
|
|
|
2318
2318
|
mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelWeight[p,sc,n]() * mTEPES.pOperReserveCost[nr] * OptModel.vReserveDown[p,sc,n,nr]()) for p,sc,n,nr in mTEPES.psnnr], index=mTEPES.psnnr)
|
|
2319
2319
|
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_GenerationCostOperatingReserve_{CaseName}.csv', sep=',')
|
|
2320
2320
|
|
|
2321
|
-
if mTEPES.
|
|
2321
|
+
if mTEPES.eh:
|
|
2322
2322
|
OutputToFile = pd.Series(data=[ mTEPES.pDiscountedWeight[p] * mTEPES.pScenProb[p,sc]() * mTEPES.pLoadLevelDuration[p,sc,n]() * mTEPES.pLinearVarCost[p,sc,n,eh] * OptModel.vESSTotalCharge[p,sc,n,eh]() for p,sc,n,eh in mTEPES.psnehc], index=mTEPES.psnehc)
|
|
2323
2323
|
OutputToFile.to_frame(name='MEUR').reset_index().pivot_table(index=['level_0','level_1','level_2'], columns='level_3', values='MEUR', aggfunc='sum').rename_axis(['Period', 'Scenario', 'LoadLevel'], axis=0).rename_axis([None], axis=1).to_csv(f'{_path}/oT_Result_ConsumptionCostOperation_{CaseName}.csv', sep=',')
|
|
2324
2324
|
if sum(mTEPES.pIndOperReserveGen[eh] for eh in mTEPES.eh if mTEPES.pIndOperReserveGen[eh] == 0) + sum(mTEPES.pIndOperReserveCon[eh] for eh in mTEPES.eh if mTEPES.pIndOperReserveCon[eh] == 0):
|
|
@@ -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) - February 05, 2026
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import time
|
|
@@ -31,7 +31,7 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
31
31
|
# Solver.options['Seed' ] = 104729
|
|
32
32
|
# Solver.options['Presolve' ] = 2
|
|
33
33
|
# Solver.options['RINS' ] = 100
|
|
34
|
-
# Solver.options['BarConvTol'
|
|
34
|
+
# Solver.options['BarConvTol' ] = 1e-10
|
|
35
35
|
# Solver.options['BarQCPConvTol' ] = 0.025
|
|
36
36
|
# Solver.options['IISFile' ] = f'{_path}/openTEPES_gurobi_'+CaseName+'.ilp' # should be uncommented to show results of IIS
|
|
37
37
|
Solver.options['MIPGap' ] = 0.01
|
|
@@ -93,7 +93,7 @@ def ProblemSolving(DirName, CaseName, SolverName, OptModel, mTEPES, pIndLogConso
|
|
|
93
93
|
if SolverResults.solver.termination_condition == TerminationCondition.infeasible or SolverResults.solver.termination_condition == TerminationCondition.maxTimeLimit or SolverResults.solver.termination_condition == TerminationCondition.infeasible.maxIterations:
|
|
94
94
|
log_infeasible_constraints(OptModel, log_expression=True, log_variables=True)
|
|
95
95
|
logging.basicConfig(filename=f'{_path}/openTEPES_infeasibilities_{CaseName}_{p}_{sc}_{st}.log', level=logging.INFO)
|
|
96
|
-
raise ValueError('Problem infeasible')
|
|
96
|
+
raise ValueError(f'### Problem infeasible for period {p}, scenario {sc}, stage {st}')
|
|
97
97
|
SolverResults.write() # summary of the solver results
|
|
98
98
|
|
|
99
99
|
#%% fix values of some variables to get duals and solve it again
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
Period,Area,RESEnergy
|
|
2
2
|
2030,Area1,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
Stage,Weight
|
|
2
2
|
st1,1
|