Я хочу смоделировать гидроэнергетику в Норвегии, принимая во внимание все каскадные зависимости (для почасового разрешения). Для начала я поставил задачу оптимизации в Pyomo для набора из трех заводов и трех водохранилищ на один день. На данный момент я хочу максимизировать прибыль за все часы, и все установки / резервуары рассчитываются следующим образом: цена электроэнергии [$ / МВтч] * мощность турбины [МВт] * 3600 [с = 1 ч] + объем [м3] * эквивалент энергии [МВтч / м3] * цена на электроэнергию [$ / МВтч]
Мои ограничения:
Часовая нагрузка = генерируемая мощность должна равняться нагрузке в каждый час
Водный баланс = уровни хранения обновляются должным образом на каждом временном шаге
Начальный и конечный уровни хранения = при 60% от максимального хранения начало и конец периода моделирования (это в ограничении водного баланса)
ВОПРОСЫ:
1) почему оператор print печатает 0.0 для cascade_inflow и discharged_flow: потому что он печатается во время создания модели, а не при ее решении?
2A) Условие завершения является оптимальным, и у меня есть значение для целевой функции, но число решений равно 0: я думаю, что проблема заключается в ограничении водный баланс , я опубликую результаты ниже на 6 часов. Нужно ли как-то устанавливать ограничения верхней и нижней границ для водного баланса ?
edit : если я запускаю скрипт из командной строки с pyomo решить --solver = glpk script.py input.dat Я получаю отображаемое решение ..?!
2B) Функция ограничения воды не работает должным образом. Если я посмотрю на результаты, то скачок громкости от временного шага 1 к 2 неосуществим. Что там не так? Является ли способ добавления каскадных потоков некорректным, или переменная m.volume просто делает то, что хочет?
3) Может быть, лучше создать проблему сетевого потока? Примеры кодов для подобных проблем есть в Pyomo Gallery . Но я еще не уверен, как моделировать узлы как резервуары. (Скорее всего, я сделаю для этого новый пост, как только попробую реализовать скрипт).
4) (Это мой первый пост: что-то я сделал не так или в следующий раз лучше?)
Код (чтение параметров пропущено)
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;