已找到 Pyomo 最优解,但解数为 0(水电模拟)

Pyomo optimal solution found but number of solutions 0 (hydropower simulation)

我希望模拟挪威的水力发电,同时考虑所有级联依赖性(每小时分辨率)。首先,我在 Pyomo 中为一组三个工厂和三个水库设置了一天的优化问题。目前,我想在所有时间和所有 plant/reservoirs 中实现利润最大化,计算公式为:电价[$/MWh]*涡轮机输出[MW]*3600[s=1h] + 体积[m3 ]*能源当量[MWh/m3]*电价[$/MWh]

我的约束是:

每小时负载 = 每小时产生的功率必须等于负载

水平衡 = 存储水平在每个时间步正确更新

开始和结束存储水平 = 在模拟期开始和结束时最大存储量的 60%(这是在水平衡约束中)

问题:

1) 为什么打印语句为 cascade_inflow 和 discharged_flow 打印出 0.0:因为这是在创建模型时打印的,而不是在求解模型时打印的?

2A) 终止条件是最优的,我有一个 objective 函数的值,但是解决方案的数量是 0:我认为问题出在约束 water balance,我将post的结果再往下看6个小时。我是否需要以某种方式为 水平衡 设置上限和下限约束? edit:如果我 运行 来自命令行的脚本 pyomo solve --solver=glpk script.py input.dat 我得到了显示的解决方案..?!

2B) 水约束功能运行不正常。如果我查看结果,那么从时间步长 1 到时间步长 2 的音量跳跃是不可行的。那里出了什么问题?是我添加级联流的方式不正确,还是变量 m.volume 只是做它想做的事?

3) 创建一个网络流量问题可能是个更好的主意? Pyomo Gallery 中有此类问题的示例代码。但我还不确定如何将节点建模为水库。 (一旦我尝试实现脚本,我很可能会为此制作一个新的 post)。

4)(这是我的第一次post:我做错了什么或者下次应该做得更好吗?)

代码(省略读取参数)

from pyomo.environ import *
from pyomo.opt import SolverFactory

opt = SolverFactory("glpk")

# Initiate model instance
m = AbstractModel()

# Define the variable power for each time step
m.turbine = Var(m.stage, m.res, initialize=0, bounds=turbine_bounds, within=NonNegativeReals)
# Define the variable volume for each time step
m.volume = Var(m.stage, m.res, initialize=volume_Init,  bounds=volume_bounds, within=NonNegativeReals)
# Define the variable spill flow for each time step
m.spilledFlow = Var(m.stage, m.res, initialize=0, bounds=spill_bounds, within=NonNegativeReals)


# Constrain total power generated over day
def hourly_load_rule(model, t):
    return model.P_load*model.hourly_demand[t] <= sum(model.turbine[t, res] for res in model.res) <= model.P_load*model.hourly_demand[t]
m.hourly_load = Constraint(m.stage, rule=hourly_load_rule)


# Water balance equation
def water_balance_rule(model, t, r):
    if t == model.T:  # final volume is same as initial volume at 60%
        return model.volume[t, r] == model.max_Vol[r]*0.6
    elif t > 1:
        cascade_inflow = 0
        for stor in model.res:
            # connectMat is a matrix that has a 1 where there is a connection and 0 where there is not
            cascade_inflow = cascade_inflow + model.connectMat[stor, r]*(model.turbine[t, stor]/model.slope[stor]+model.spilledFlow[t, stor])
            if model.connectMat[stor, r] == 1:
                print(stor, r, t, value(cascade_inflow))  # this always prints out 0.0 for cascade_inflow
         discharged_flow = model.turbine[t, r]/model.slope[r]  # model.turbine is in MW: divide by slope to get discharge flow [m3/s]
         print(value(discharged_flow))  # this always prints out 0.0
         return model.volume[t, r] == model.volume[t-1, r]+(cascade_inflow+model.inflow[t, r]-model.spilledFlow[t, r]-discharged_flow)*model.secPerTimeStep
    else:
        # start volume constrained to 60% of max volume
        return model.volume[t, r] == model.max_Vol[r]*0.6
m.water_balance = Constraint(m.stage, m.res, rule=water_balance_rule)


# Revenue = Objective function (sum over all hours and all plants/reservoirs)
def revenue_rule(model):
    return sum(sum(model.el_price[i]*model.turbine[i, j]*model.secPerTimeStep+model.volume[i, j]*model.energy_stored[j]*model.el_price[i] for i in model.stage) for j in model.res)
m.obj = Objective(rule=revenue_rule, sense=maximize)

instance = m.create("three_input_stack.dat")

results = opt.solve(instance)

instance.display()

results.write()

结果

0.0
(1, 2, 2, 0.0)
0.0
(2, 3, 2, 0.0)
0.0
0.0
(1, 2, 3, 0.0)
0.0
(2, 3, 3, 0.0)
0.0
0.0
(1, 2, 4, 0.0)
0.0
(2, 3, 4, 0.0)
0.0
0.0
(1, 2, 5, 0.0)
0.0
(2, 3, 5, 0.0)
0.0
Model unknown

  Variables:
    turbine : Size=18, Index=turbine_index
        Key    : Lower : Value          : Upper : Fixed : Stale : Domain
        (1, 1) :     0 :           3.31 :  3.31 : False : False : NonNegativeReals
        (1, 2) :     0 :           3.71 :   5.9 : False : False : NonNegativeReals
        (1, 3) :     0 :            0.0 :   9.0 : False : False : NonNegativeReals
        (2, 1) :     0 :            0.8 :  3.31 : False : False : NonNegativeReals
        (2, 2) :     0 :            5.9 :   5.9 : False : False : NonNegativeReals
        (2, 3) :     0 :            0.0 :   9.0 : False : False : NonNegativeReals
        (3, 1) :     0 :            0.0 :  3.31 : False : False : NonNegativeReals
        (3, 2) :     0 : 0.242202133966 :   5.9 : False : False : NonNegativeReals
        (3, 3) :     0 :  6.31779786603 :   9.0 : False : False : NonNegativeReals
        (4, 1) :     0 :            0.0 :  3.31 : False : False : NonNegativeReals
        (4, 2) :     0 :            0.0 :   5.9 : False : False : NonNegativeReals
        (4, 3) :     0 :            6.5 :   9.0 : False : False : NonNegativeReals
        (5, 1) :     0 :            0.0 :  3.31 : False : False : NonNegativeReals
        (5, 2) :     0 :         1.9665 :   5.9 : False : False : NonNegativeReals
        (5, 3) :     0 :         4.6535 :   9.0 : False : False : NonNegativeReals
        (6, 1) :     0 :           3.31 :  3.31 : False : False : NonNegativeReals
        (6, 2) :     0 :           3.83 :   5.9 : False : False : NonNegativeReals
        (6, 3) :     0 :            0.0 :   9.0 : False : False : NonNegativeReals
    volume : Size=18, Index=volume_index
        Key    : Lower : Value         : Upper      : Fixed : Stale : Domain
        (1, 1) :   0.0 :    39600000.0 : 66000000.0 : False : False : NonNegativeReals
        (1, 2) :   0.0 :    10020000.0 : 16700000.0 : False : False : NonNegativeReals
        (1, 3) :   0.0 :     1260000.0 :  2100000.0 : False : False : NonNegativeReals
        (2, 1) :   0.0 : 32149783.0468 : 66000000.0 : False : False : NonNegativeReals
        (2, 2) :   0.0 : 16684216.9532 : 16700000.0 : False : False : NonNegativeReals
        (2, 3) :   0.0 :     2100000.0 :  2100000.0 : False : False : NonNegativeReals
        (3, 1) :   0.0 : 32167783.0468 : 66000000.0 : False : False : NonNegativeReals
        (3, 2) :   0.0 :    16700000.0 : 16700000.0 : False : False : NonNegativeReals
        (3, 3) :   0.0 :     2100000.0 :  2100000.0 : False : False : NonNegativeReals
        (4, 1) :   0.0 : 32185783.0468 : 66000000.0 : False : False : NonNegativeReals
        (4, 2) :   0.0 :    16700000.0 : 16700000.0 : False : False : NonNegativeReals
        (4, 3) :   0.0 :     2100000.0 :  2100000.0 : False : False : NonNegativeReals
        (5, 1) :   0.0 : 32203783.0468 : 66000000.0 : False : False : NonNegativeReals
        (5, 2) :   0.0 :    16700000.0 : 16700000.0 : False : False : NonNegativeReals
        (5, 3) :   0.0 :     2100000.0 :  2100000.0 : False : False : NonNegativeReals
        (6, 1) :   0.0 :    39600000.0 : 66000000.0 : False : False : NonNegativeReals
        (6, 2) :   0.0 :    10020000.0 : 16700000.0 : False : False : NonNegativeReals
        (6, 3) :   0.0 :     1260000.0 :  2100000.0 : False : False : NonNegativeReals
    spilledFlow : Size=18, Index=spilledFlow_index
        Key    : Lower : Value         : Upper   : Fixed : Stale : Domain
        (1, 1) :   0.0 :             0 : 10000.0 : False :  True : NonNegativeReals
        (1, 2) :   0.0 :             0 : 10000.0 : False :  True : NonNegativeReals
        (1, 3) :   0.0 :             0 : 10000.0 : False :  True : NonNegativeReals
        (2, 1) :   0.0 : 2069.67087236 : 10000.0 : False : False : NonNegativeReals
        (2, 2) :   0.0 : 213.332062039 : 10000.0 : False : False : NonNegativeReals
        (2, 3) :   0.0 :           0.0 : 10000.0 : False : False : NonNegativeReals
        (3, 1) :   0.0 :           0.0 : 10000.0 : False : False : NonNegativeReals
        (3, 2) :   0.0 :           0.0 : 10000.0 : False : False : NonNegativeReals
        (3, 3) :   0.0 :           0.0 : 10000.0 : False : False : NonNegativeReals
        (4, 1) :   0.0 :           0.0 : 10000.0 : False : False : NonNegativeReals
        (4, 2) :   0.0 :           5.0 : 10000.0 : False : False : NonNegativeReals
        (4, 3) :   0.0 : 4.22222222222 : 10000.0 : False : False : NonNegativeReals
        (5, 1) :   0.0 :           0.0 : 10000.0 : False : False : NonNegativeReals
        (5, 2) :   0.0 :           0.0 : 10000.0 : False : False : NonNegativeReals
        (5, 3) :   0.0 : 5.86355555556 : 10000.0 : False : False : NonNegativeReals
        (6, 1) :   0.0 :             0 : 10000.0 : False :  True : NonNegativeReals
        (6, 2) :   0.0 :             0 : 10000.0 : False :  True : NonNegativeReals
        (6, 3) :   0.0 :             0 : 10000.0 : False :  True : NonNegativeReals

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True : 39250323.6272

  Constraints:
    hourly_load : Size=6
        Key : Lower : Body : Upper
          1 :  7.02 : 7.02 :  7.02
          2 :   6.7 :  6.7 :   6.7
          3 :  6.56 : 6.56 :  6.56
          4 :   6.5 :  6.5 :   6.5
          5 :  6.62 : 6.62 :  6.62
          6 :  7.14 : 7.14 :  7.14
    water_balance : Size=18
         Key    : Lower      : Body               : Upper
         (1, 1) : 39600000.0 :         39600000.0 : 39600000.0
         (1, 2) : 10020000.0 :         10020000.0 : 10020000.0
         (1, 3) :  1260000.0 :          1260000.0 :  1260000.0
         (2, 1) :        0.0 :  2.14204192162e-08 :        0.0
         (2, 2) :        0.0 : -2.23517417908e-08 :        0.0
         (2, 3) :        0.0 : -5.82076609135e-10 :        0.0
         (3, 1) :        0.0 :                0.0 :        0.0
         (3, 2) :        0.0 :  1.55250745593e-08 :        0.0
         (3, 3) :        0.0 : -7.13669123797e-09 :        0.0
         (4, 1) :        0.0 :                0.0 :        0.0
         (4, 2) :        0.0 : -7.35411731512e-11 :        0.0
         (4, 3) :        0.0 : -6.39488462184e-12 :        0.0
         (5, 1) :        0.0 :                0.0 :        0.0
         (5, 2) :        0.0 : -5.49960077478e-10 :        0.0
         (5, 3) :        0.0 : -1.79056769412e-10 :        0.0
         (6, 1) : 39600000.0 :         39600000.0 : 39600000.0
         (6, 2) : 10020000.0 :         10020000.0 : 10020000.0
         (6, 3) :  1260000.0 :          1260000.0 :  1260000.0
# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem: 
- Name: unknown
  Lower bound: 39250323.6272
  Upper bound: 39250323.6272
  Number of objectives: 1
  Number of constraints: 25
  Number of variables: 49
  Number of nonzeros: 89
  Sense: maximize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver: 
- Status: ok
  Termination condition: optimal
  Statistics: 
    Branch and bound: 
      Number of bounded subproblems: 0
      Number of created subproblems: 0
  Error rc: 0
  Time: 0.0750000476837
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 0
  number of solutions displayed: 0

输入文件

param secPerTimeStep:=3600;
param T:=6;
param numReservoirs:=3;
param connectMat:=
1   1   0
1   2   1
1   3   0
2   1   0
2   2   0
2   3   1
3   1   0
3   2   0
3   3   0;
param el_price:=
1   242.16
2   242.09
3   239.3
4   231.52
5   224.25
6   219.77;
param inflow:=
1   1   5
2   1   5
3   1   5
4   1   5
5   1   5
6   1   5
1   2   5
2   2   5
3   2   5
4   2   5
5   2   5
6   2   5
1   3   5
2   3   5
3   3   5
4   3   5
5   3   5
6   3   5;
param min_Vol:=
1   0.0
2   0.0
3   0.0;
param max_Vol:=
1   66000000.0
2   16700000.0
3   2100000.0;
param min_Turb_gen:=
1   0
2   0
3   0;
param max_Turb_gen:=
1   3.31
2   5.9
3   9.0;
param min_spill:=
1   0.0
2   0.0
3   0.0;
param max_spill:=
1   10000.0
2   10000.0
3   10000.0;
param min_discharge:=
1   0.0
2   0.0
3   0.0;
param max_discharge:=
1   20.0
2   15.0
3   8.0;
param slope:=
1   0.1655
2   0.3933
3   1.125;
param energy_stored:=
1   0.000046
2   0.000109
3   0.00031;
param hourly_demand:=
1   0.0351
2   0.0335
3   0.0328
4   0.0325
5   0.0331
6   0.0357;
param P_load:=200;

1) 总是打印零,因为 m.tubine 被初始化为 0。在模型构建期间(即执行打印语句时),表达式的计算结果为 0。

2A) 结果在调用求解器后自动加载回模型,因此命令 instance.display() 正在打印结果。 results 对象仅在您想延迟加载解决方案时使用(这是您可以在调用求解器时设置的选项)。

2B) 不确定。您始终可以 运行 命令 instance.water_balance.pprint() 来确认约束表达式是您所期望的。