Pyomo 优化不起作用(天然气厂调度)
背景
我正在尝试编写一个 pyomo 脚本,以根据对电价的完美预见来优化调度燃气厂。我相信我已经完成了 90%,只是还有一些问题。
问题
我的脚本有效,但解算器从未调度工厂,即使它应该在哪里,在下面提供的示例中,我可以手动计算至少 8131 美元的潜在利润。
我怀疑我的零结果的原因是由于我如何编写约束,其中有 2 个;
- 燃气厂需要 10 分钟才能从冷启动启动。
- 一旦预热,燃气厂就有一个必须在/以上运行的最小负载。
- 天然气厂一天只能消耗 9000 GJ 的天然气,
具体来说,在进一步测试中,我认为是“gas_volume_used”约束导致了问题。
请求帮助
有人可以看一下我的代码并看看我在约束方程中缺少什么吗?
代码
import pandas as pd
from pyomo.environ import *
from pyomo.core import *
# ---------------------
# Main Functions
# ---------------------
def model_to_df(model, first_period, last_period):
# Need to increase the first & last period by 1 because of pyomo indexing
# and add 1 to the value of last model period because of the range
periods = range(model.T[first_period + 1], model.T[last_period + 1] + 1)
rrp = [model.P.extract_values()[None][i] for i in periods]
Gascold = [value(model.Gascold[i]) for i in periods]
Gaswarm = [value(model.Gaswarm[i]) for i in periods]
Gashot = [value(model.Gashot[i]) for i in periods]
GasUnit_mw = [value(model.GasUnit_mw[i]) for i in periods]
GasUse = [value(model.GasUse[i]) for i in periods]
df_dict = dict(
period=periods,
rrp=rrp,
Gascold=Gascold,
Gaswarm=Gaswarm,
Gashot=Gashot,
GasUnit_mw=GasUnit_mw,
GasUse=GasUse
)
df = pd.DataFrame(df_dict)
return df
def optimize_year(df, gunits, gnet, gprice, vom, cold_gas_p_unit, hr, max_gas, sys_use,
first_model_period, last_model_period):
"""
Optimize behavior of a gas plant over a time period
Assume perfect foresight of electricity prices.
Parameters
----------
df : dataframe
dataframe with columns of hourly LBMP and the hour of the year
gunits : int
number of gas turbines
gnet : double
size in MW's of each gas turbine
gprice : double
the price of gas $/GJ
vom : double
the variable cost of gas $/MWh dispatched
cold_gas_p_unit : double
the amount of gas in GJ used in 5 minutes while booting up from cold start
hr : double
heat rate, the amount of gas consumed per MWh produced (GJ/MWh)
sys_use : double
auxillary gas usage, percentage
max_gas : double
the maximum amount of gas (GJ) available to use in a period
first_model_period : int, optional
Set the first period of the year to be considered in the optimization
last_model_period : int, optional
Set the last period of the year to be considered in the optimization
Returns
-------
dataframe
Gas plant operational stats and time stamp
"""
# Filter the data
df = df.loc[first_model_period:last_model_period, :]
model = ConcreteModel()
# Define model parameters
model.T = Set(initialize=df.period.tolist(), doc='periods of year', ordered=True)
model.P = Param(initialize=df.rrp.tolist(), doc='price for each period', within=Any)
model.MaxGas = Param(initialize=(gnet/12) * gunits * hr * (1+sys_use), doc='Max gas usage (GJ) in interval', within=Any)
model.MaxVol = Param(initialize=max_gas, doc='Max gas usage (GJ) in 24 hour period', within=Any)
model.MaxMW = Param(initialize=(gnet/12) * gunits, doc='Max MWs', within=Any)
model.Gascold = Var(model.T, doc='binary', within=Binary, initialize=1)
model.Gaswarm = Var(model.T, doc='binary', within=Binary, initialize=0)
model.Gashot = Var(model.T, doc='binary', within=Binary, initialize=0)
model.GasUse = Var(model.T, bounds=(0, model.MaxGas))
model.GasUnit_mw = Var(model.T, bounds=(0, model.MaxMW))
# *********************
# GAS CONSTRAINTS
# *********************
"Ensure that all three Gas State flags sum to 1"
def set_on(model, t):
return model.Gascold[t] + model.Gaswarm[t] + model.Gashot[t] == 1
model.limit_flags = Constraint(model.T, rule=set_on)
"Set t0 to GAS COLD = 1"
def gas_cold(model, t):
if t == model.T.first():
return model.Gascold[t] == 1
else:
return model.Gascold[t] >= 0
model.gas_cold_check = Constraint(model.T, rule=gas_cold)
"Gas can only warm up from cold"
def gas_warm(model, t):
if t < 2:
return model.Gaswarm[t] == 0
elif (model.Gaswarm[t-2] + model.Gaswarm[t-1] + model.Gascold[t-1]) == 1:
return model.Gaswarm[t] >= 0
else:
return model.Gaswarm[t] == 0
model.gas_warm_check = Constraint(model.T, rule=gas_warm)
"gas can only get hot from warm"
def gas_hot(model, t):
if t <= 2:
return model.Gashot[t] == 0
elif value(model.Gaswarm[t-2] + model.Gaswarm[t-1] + model.Gashot[t-2] + model.Gashot[t-1]) < 2:
return model.Gashot[t] == 0
else:
return model.Gashot[t] >= 0
model.gas_hot_check = Constraint(model.T, rule=gas_hot)
"Gas hot min load"
def gas_hot_min_load(model, t):
if model.Gashot[t] == 1:
return model.GasUnit_mw[t] >= 30 / 12
else:
return model.GasUnit_mw[t] == 0
model.gas_hot_min_load_check = Constraint(model.T, rule=gas_hot_min_load)
"Gas volume used"
def gas_volume_used(model, t):
'how much gas is being used?'
return model.GasUse[t] == (model.GasUnit_mw[t] * hr * (1+sys_use)) + (model.Gaswarm[t] * gunits * cold_gas_p_unit * (1+sys_use))
model.gas_vol = Constraint(model.T, rule=gas_volume_used)
"only 9000 GJ of gas can be used in a single day"
def daily_max_gas(model, t):
min_t = model.T.first()
max_t = model.T.last()
# Check all t until the last 24 hours
if t <= max_t:
return sum(model.GasUse[i] for i in range(min_t, max_t+1)) <= model.MaxVol
else:
return Constraint.Skip
model.max_gas_vol = Constraint(model.T, rule=daily_max_gas)
# Define the income, expenses, and profit
gas_income = sum(df.loc[t, 'rrp'] * model.GasUnit_mw[t] for t in model.T)
gas_expenses = sum(model.GasUse[t] * gprice + model.GasUnit_mw[t] * vom for t in model.T)
profit = gas_income - gas_expenses
model.objective = Objective(expr=profit, sense=maximize)
# Solve the model
solver = SolverFactory('glpk')
solver.solve(model)
results_df = model_to_df(model, first_period=first_model_period, last_period=last_model_period)
results_df['local_time'] = df.loc[:, 'local_time']
return results_df
# *********************
# RUN SCRIPT
# *********************
# Import file
df = pd.read_csv('raw_prices.csv')
# New Index
df['period'] = df.reset_index().index
# get current Year
first_model_period = 0
last_model_period = df['period'].iloc[-1]
# Gas Inputs
gunits = 3 # No units
gnet = 58.5 # MW/unit
srmc = 115.49 # $/MWh
vom = 6.18 # $/MWh
gprice = 10.206 # $/GJ
cold_gas_p_unit = 15.75 # GJ/unit/5mins
hr = 10.5 # GJ/MWh
sys_use = 0.02 # %
max_gas = 9000 # GJ
# Run Model
results_df = optimize_year(df=df, gunits=gunits, gnet=gnet, gprice=gprice, vom=vom, cold_gas_p_unit=cold_gas_p_unit,
hr=hr, max_gas=max_gas, sys_use=sys_use,
first_model_period=first_model_period, last_model_period=last_model_period)
# ---------------------
# Write the file out and remove the index
# ---------------------
print('ran successfully')
results_df.to_csv('optimised_output.csv', index=False)
示例数据
local_time rrp
3/07/2022 16:40 64.99
3/07/2022 16:45 73.10744
3/07/2022 16:50 74.21746
3/07/2022 16:55 95.90964
3/07/2022 17:00 89.60728
3/07/2022 17:05 90.37018
3/07/2022 17:10 92.52947
3/07/2022 17:15 100.1449
3/07/2022 17:20 103.70199
3/07/2022 17:25 290.2848
3/07/2022 17:30 115.40232
3/07/2022 17:35 121.00009
3/07/2022 17:40 294.62543
3/07/2022 17:45 121
3/07/2022 17:50 294.69446
3/07/2022 17:55 124.90664
3/07/2022 18:00 117.21929
3/07/2022 18:05 115.60193
3/07/2022 18:10 121
3/07/2022 18:15 148.20186
3/07/2022 18:20 123.11763
3/07/2022 18:25 120.99996
3/07/2022 18:30 121
3/07/2022 18:35 121
3/07/2022 18:40 121
3/07/2022 18:45 120.77726
3/07/2022 18:50 105.44435
3/07/2022 18:55 104.22928
3/07/2022 19:00 102.26646
3/07/2022 19:05 99.5896
3/07/2022 19:10 95.5881
3/07/2022 19:15 88
3/07/2022 19:20 88
3/07/2022 19:25 88
3/07/2022 19:30 96.97537
3/07/2022 19:35 88
3/07/2022 19:40 86.39322
3/07/2022 19:45 88
3/07/2022 19:50 85.97703
3/07/2022 19:55 86
3/07/2022 20:00 87.61047
3/07/2022 20:05 88
3/07/2022 20:10 88
3/07/2022 20:15 85.34908
3/07/2022 20:20 85.1
3/07/2022 20:25 71.01181
3/07/2022 20:30 84.88422
3/07/2022 20:35 85.39669
3/07/2022 20:40 86.02542
3/07/2022 20:45 84.87425
3/07/2022 20:50 70.0652
3/07/2022 20:55 70.65106
3/07/2022 21:00 70.53063
3/07/2022 21:05 68.27
3/07/2022 21:10 68.86159
3/07/2022 21:15 68.32018
3/07/2022 21:20 67.30473
3/07/2022 21:25 66.78292
3/07/2022 21:30 66.02195
3/07/2022 21:35 66.40755
background
I am trying to write an pyomo script to optimally dispatch a gas plant based on perfect foresight of electricity prices. I believe I am 90% of the way there, just a few issues.
Problem
My script works, but the solver is never dispatching the plant, even where it should be, in the example provided below, manually I can calculate at least $8131 of potential profit.
I suspect the reason for my zero results is due to how I've written the constraints, of which there are 2;
- Gas Plant takes 10 minutes to boot up from a cold start
- Once warmed up, the gas plant has a min load it must operate at/above.
- Gas Plant can only consume 9000 GJ of gas in a single day
Specifically on further testing, I think it is the 'gas_volume_used' constraint which is causing the issue.
Help Requested
Could someone please have a look at my code and see what I am missing in the constraint equations?
Code
import pandas as pd
from pyomo.environ import *
from pyomo.core import *
# ---------------------
# Main Functions
# ---------------------
def model_to_df(model, first_period, last_period):
# Need to increase the first & last period by 1 because of pyomo indexing
# and add 1 to the value of last model period because of the range
periods = range(model.T[first_period + 1], model.T[last_period + 1] + 1)
rrp = [model.P.extract_values()[None][i] for i in periods]
Gascold = [value(model.Gascold[i]) for i in periods]
Gaswarm = [value(model.Gaswarm[i]) for i in periods]
Gashot = [value(model.Gashot[i]) for i in periods]
GasUnit_mw = [value(model.GasUnit_mw[i]) for i in periods]
GasUse = [value(model.GasUse[i]) for i in periods]
df_dict = dict(
period=periods,
rrp=rrp,
Gascold=Gascold,
Gaswarm=Gaswarm,
Gashot=Gashot,
GasUnit_mw=GasUnit_mw,
GasUse=GasUse
)
df = pd.DataFrame(df_dict)
return df
def optimize_year(df, gunits, gnet, gprice, vom, cold_gas_p_unit, hr, max_gas, sys_use,
first_model_period, last_model_period):
"""
Optimize behavior of a gas plant over a time period
Assume perfect foresight of electricity prices.
Parameters
----------
df : dataframe
dataframe with columns of hourly LBMP and the hour of the year
gunits : int
number of gas turbines
gnet : double
size in MW's of each gas turbine
gprice : double
the price of gas $/GJ
vom : double
the variable cost of gas $/MWh dispatched
cold_gas_p_unit : double
the amount of gas in GJ used in 5 minutes while booting up from cold start
hr : double
heat rate, the amount of gas consumed per MWh produced (GJ/MWh)
sys_use : double
auxillary gas usage, percentage
max_gas : double
the maximum amount of gas (GJ) available to use in a period
first_model_period : int, optional
Set the first period of the year to be considered in the optimization
last_model_period : int, optional
Set the last period of the year to be considered in the optimization
Returns
-------
dataframe
Gas plant operational stats and time stamp
"""
# Filter the data
df = df.loc[first_model_period:last_model_period, :]
model = ConcreteModel()
# Define model parameters
model.T = Set(initialize=df.period.tolist(), doc='periods of year', ordered=True)
model.P = Param(initialize=df.rrp.tolist(), doc='price for each period', within=Any)
model.MaxGas = Param(initialize=(gnet/12) * gunits * hr * (1+sys_use), doc='Max gas usage (GJ) in interval', within=Any)
model.MaxVol = Param(initialize=max_gas, doc='Max gas usage (GJ) in 24 hour period', within=Any)
model.MaxMW = Param(initialize=(gnet/12) * gunits, doc='Max MWs', within=Any)
model.Gascold = Var(model.T, doc='binary', within=Binary, initialize=1)
model.Gaswarm = Var(model.T, doc='binary', within=Binary, initialize=0)
model.Gashot = Var(model.T, doc='binary', within=Binary, initialize=0)
model.GasUse = Var(model.T, bounds=(0, model.MaxGas))
model.GasUnit_mw = Var(model.T, bounds=(0, model.MaxMW))
# *********************
# GAS CONSTRAINTS
# *********************
"Ensure that all three Gas State flags sum to 1"
def set_on(model, t):
return model.Gascold[t] + model.Gaswarm[t] + model.Gashot[t] == 1
model.limit_flags = Constraint(model.T, rule=set_on)
"Set t0 to GAS COLD = 1"
def gas_cold(model, t):
if t == model.T.first():
return model.Gascold[t] == 1
else:
return model.Gascold[t] >= 0
model.gas_cold_check = Constraint(model.T, rule=gas_cold)
"Gas can only warm up from cold"
def gas_warm(model, t):
if t < 2:
return model.Gaswarm[t] == 0
elif (model.Gaswarm[t-2] + model.Gaswarm[t-1] + model.Gascold[t-1]) == 1:
return model.Gaswarm[t] >= 0
else:
return model.Gaswarm[t] == 0
model.gas_warm_check = Constraint(model.T, rule=gas_warm)
"gas can only get hot from warm"
def gas_hot(model, t):
if t <= 2:
return model.Gashot[t] == 0
elif value(model.Gaswarm[t-2] + model.Gaswarm[t-1] + model.Gashot[t-2] + model.Gashot[t-1]) < 2:
return model.Gashot[t] == 0
else:
return model.Gashot[t] >= 0
model.gas_hot_check = Constraint(model.T, rule=gas_hot)
"Gas hot min load"
def gas_hot_min_load(model, t):
if model.Gashot[t] == 1:
return model.GasUnit_mw[t] >= 30 / 12
else:
return model.GasUnit_mw[t] == 0
model.gas_hot_min_load_check = Constraint(model.T, rule=gas_hot_min_load)
"Gas volume used"
def gas_volume_used(model, t):
'how much gas is being used?'
return model.GasUse[t] == (model.GasUnit_mw[t] * hr * (1+sys_use)) + (model.Gaswarm[t] * gunits * cold_gas_p_unit * (1+sys_use))
model.gas_vol = Constraint(model.T, rule=gas_volume_used)
"only 9000 GJ of gas can be used in a single day"
def daily_max_gas(model, t):
min_t = model.T.first()
max_t = model.T.last()
# Check all t until the last 24 hours
if t <= max_t:
return sum(model.GasUse[i] for i in range(min_t, max_t+1)) <= model.MaxVol
else:
return Constraint.Skip
model.max_gas_vol = Constraint(model.T, rule=daily_max_gas)
# Define the income, expenses, and profit
gas_income = sum(df.loc[t, 'rrp'] * model.GasUnit_mw[t] for t in model.T)
gas_expenses = sum(model.GasUse[t] * gprice + model.GasUnit_mw[t] * vom for t in model.T)
profit = gas_income - gas_expenses
model.objective = Objective(expr=profit, sense=maximize)
# Solve the model
solver = SolverFactory('glpk')
solver.solve(model)
results_df = model_to_df(model, first_period=first_model_period, last_period=last_model_period)
results_df['local_time'] = df.loc[:, 'local_time']
return results_df
# *********************
# RUN SCRIPT
# *********************
# Import file
df = pd.read_csv('raw_prices.csv')
# New Index
df['period'] = df.reset_index().index
# get current Year
first_model_period = 0
last_model_period = df['period'].iloc[-1]
# Gas Inputs
gunits = 3 # No units
gnet = 58.5 # MW/unit
srmc = 115.49 # $/MWh
vom = 6.18 # $/MWh
gprice = 10.206 # $/GJ
cold_gas_p_unit = 15.75 # GJ/unit/5mins
hr = 10.5 # GJ/MWh
sys_use = 0.02 # %
max_gas = 9000 # GJ
# Run Model
results_df = optimize_year(df=df, gunits=gunits, gnet=gnet, gprice=gprice, vom=vom, cold_gas_p_unit=cold_gas_p_unit,
hr=hr, max_gas=max_gas, sys_use=sys_use,
first_model_period=first_model_period, last_model_period=last_model_period)
# ---------------------
# Write the file out and remove the index
# ---------------------
print('ran successfully')
results_df.to_csv('optimised_output.csv', index=False)
Example Data
local_time rrp
3/07/2022 16:40 64.99
3/07/2022 16:45 73.10744
3/07/2022 16:50 74.21746
3/07/2022 16:55 95.90964
3/07/2022 17:00 89.60728
3/07/2022 17:05 90.37018
3/07/2022 17:10 92.52947
3/07/2022 17:15 100.1449
3/07/2022 17:20 103.70199
3/07/2022 17:25 290.2848
3/07/2022 17:30 115.40232
3/07/2022 17:35 121.00009
3/07/2022 17:40 294.62543
3/07/2022 17:45 121
3/07/2022 17:50 294.69446
3/07/2022 17:55 124.90664
3/07/2022 18:00 117.21929
3/07/2022 18:05 115.60193
3/07/2022 18:10 121
3/07/2022 18:15 148.20186
3/07/2022 18:20 123.11763
3/07/2022 18:25 120.99996
3/07/2022 18:30 121
3/07/2022 18:35 121
3/07/2022 18:40 121
3/07/2022 18:45 120.77726
3/07/2022 18:50 105.44435
3/07/2022 18:55 104.22928
3/07/2022 19:00 102.26646
3/07/2022 19:05 99.5896
3/07/2022 19:10 95.5881
3/07/2022 19:15 88
3/07/2022 19:20 88
3/07/2022 19:25 88
3/07/2022 19:30 96.97537
3/07/2022 19:35 88
3/07/2022 19:40 86.39322
3/07/2022 19:45 88
3/07/2022 19:50 85.97703
3/07/2022 19:55 86
3/07/2022 20:00 87.61047
3/07/2022 20:05 88
3/07/2022 20:10 88
3/07/2022 20:15 85.34908
3/07/2022 20:20 85.1
3/07/2022 20:25 71.01181
3/07/2022 20:30 84.88422
3/07/2022 20:35 85.39669
3/07/2022 20:40 86.02542
3/07/2022 20:45 84.87425
3/07/2022 20:50 70.0652
3/07/2022 20:55 70.65106
3/07/2022 21:00 70.53063
3/07/2022 21:05 68.27
3/07/2022 21:10 68.86159
3/07/2022 21:15 68.32018
3/07/2022 21:20 67.30473
3/07/2022 21:25 66.78292
3/07/2022 21:30 66.02195
3/07/2022 21:35 66.40755
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我对此有点极客。上瘾了,有点有趣的问题。
因此,我做了很多更改,并在此示例中留下了一些代码。我还削减了一些成本变量并使它们变得相当简单,因为我有点迷失在酱汁中,所以我(大部分)确信事情正在发挥作用,所以单位/转换/成本现在有点荒谬,但应该很容易恢复。
希望这里有一些概念可供您在完成此操作时使用。一些注释...
决定显示了非常令人信服的证据表明它正在运行正如所希望的那样,它启动,在价格高时以最大速度运行,并遵守滚动燃气限制,然后关闭并再次执行。
代码
Well, I went a little geek on this. Got hooked, kinda interesting problem.
So, I made a bunch of changes and left some of your code in this example. I also chopped down a handful of the cost variables and made them rather simple as I was getting a little lost in the sauce and so that I was (mostly) convinced things were working, so the units/conversions/costs are a bit nonsensical now, but should be easily recovered.
Hopefully there are a couple concepts in here that you can use as you work through this. A few notes...
Plot shows pretty convincing evidence that it is running as hoped, it starts up, runs at max blast when price is high, and adheres to rolling gas limit, then shuts down and does it again.
Code