GLPK/電力市場
本教程包含一組示例,演示了用於市場化排程的競爭性電力市場的特徵。雖然澳大利亞國家電力市場 (NEM) 被用作這些示例的參考,但該市場與其他競爭性電力市場具有許多共同特徵,可以為更普遍的市場化排程提供有用的見解。
單週期電力排程可以被視為一個流量網路問題 - 每個區域(以及稍後的每條傳輸線路)由一個節點表示,每個無損連結由一個雙向弧表示。這裡提供的示例不是作為線性規劃構建的。這兩種公式在數學上是等效的(儘管首選的演算法通常不同)。儘管如此,在研究這些示例時,將網路檢視保留在腦海中仍然是有用的。
本示例的目的是演示在一定時間範圍內,考慮到燃料成本和爬坡速率,對單個發電機組進行排程,並在整個時間範圍內最大化該機組的總利潤。
/*
Simple single unit dispatch
Dr. H J Mackenzie
2010-03-24
*/
# set of dispatch intervals
set I;
# dispatch price is $
param regionalprice {I};
# unit characteristics
param unit_max_capacity >= 0;
param fuel_cost >= 0;
param max_ramp_rate >= 0;
param start_dispatch >= 0;
# dispatch variables
var dispatch {I} >= 0;
var ramp {I}, >= - max_ramp_rate, <= max_ramp_rate;
var profit {I};
# objective function
maximize totalprofit: sum {i in I} profit[i];
# constraints
s.t. initial_dispatch: dispatch[0] = start_dispatch;
s.t. dispatch_profit {i in I}: profit[i] =
dispatch[i] * (regionalprice[i] - fuel_cost);
s.t. dispatch_ramp {i in I}: ramp[i] =
dispatch[i] - dispatch[if i > 0 then i-1 else 0];
s.t. unit_capacity {i in I}: dispatch[i] <=
unit_max_capacity;
# solve the problem
solve;
# output input and determined values
printf {i in I} "%d regionalprice = %.1f; dispatch = %.1f; ramp = %.1f; profit = %.1f\n",
i, regionalprice[i], dispatch[i], ramp[i], profit[i];
data;
param unit_max_capacity := 100; /* MW */
param fuel_cost := 30; /* $/MWh */
param max_ramp_rate := 20; /* max MW up or down */
param start_dispatch := 0; /* present dispatch point MW */
param : I : regionalprice :=
0 15
1 17
2 18
3 22
4 55
5 40
6 65
7 10
8 12
9 4
;
end;
該解決方案在大約 10 次迭代中找到,顯示了機組在有利可圖的執行之前進行的排程,以最大限度地提高整個時間集的總利潤,為 2680 美元。不幸的是,在現實世界中,區域價格在排程間隔之前是未知的,因此該方法僅適用於排程發生後或在預排程定價的情況下,隱含假設機組的排程相對於區域價格來說微不足道。
* 10: obj = 2.680000000e+003 infeas = 0.000e+000 (0) OPTIMAL SOLUTION FOUND Time used: 0.0 secs Memory used: 0.1 Mb (135015 bytes) 0 regionalprice = 15.0; dispatch = 0.0; ramp = 0.0; profit = 0.0 1 regionalprice = 17.0; dispatch = 0.0; ramp = 0.0; profit = 0.0 2 regionalprice = 18.0; dispatch = 20.0; ramp = 20.0; profit = -240.0 3 regionalprice = 22.0; dispatch = 40.0; ramp = 20.0; profit = -320.0 4 regionalprice = 55.0; dispatch = 60.0; ramp = 20.0; profit = 1500.0 5 regionalprice = 40.0; dispatch = 80.0; ramp = 20.0; profit = 800.0 6 regionalprice = 65.0; dispatch = 60.0; ramp = -20.0; profit = 2100.0 7 regionalprice = 10.0; dispatch = 40.0; ramp = -20.0; profit = -800.0 8 regionalprice = 12.0; dispatch = 20.0; ramp = -20.0; profit = -360.0 9 regionalprice = 4.0; dispatch = 0.0; ramp = -20.0; profit = 0.0
本示例類似於上一個示例,但引入了市場排程輔助服務的複雜性,以進行系統調節。在真實的澳大利亞 NEM 中,有六種市場排程服務,分別用於 6 秒(R6SEC)、60 秒(R60SEC)、5 分鐘(R5MIN)和調節(RREG)的升壓,以及 6 秒(L6SEC)、60 秒(L60SEC)、5 分鐘(L5MIN)和調節(LREG)的降壓。在本示例中,僅考慮單一區域的 RREG 服務,該區域的市場成本最小化。該示例沒有考慮有關輔助服務排程的現實世界約束,在這些約束中,不同輸出級別可以提供不同數量的服務,在澳大利亞市場中,這些服務是透過為每種輔助服務定義的梯形以及每個機組的物理特性來實現的。
/*
Least cost unit dispatch for energy with regulation
Dr. H J Mackenzie
2010-03-24
*/
# set of units
set U;
# maximum capacity of the units in MW
param capacity {U};
# offer price of the energy in $/MWh
param offerprice {U};
# maximum regulation capacity of the units in MW
param regcapacity {U};
# regulation offer price of the regulation energy in $/MWh
param regofferprice {U};
# market parameters
param dispatch_demand >= 0;
param regdispatch_demand >= 0;
# dispatch variables
var dispatch {U} >= 0;
var cost {U};
var regdispatch {U} >= 0;
var regcost {U};
# objective function
minimize market_cost: sum {u in U} (cost[u] + regcost[u]);
# constraints
s.t. dispatch_cost {u in U}: cost[u] = dispatch[u] * offerprice[u];
s.t. regdispatch_cost {u in U}: regcost[u] = regdispatch[u] * regofferprice[u];
s.t. feasible_dispatch {u in U}: dispatch[u] + regdispatch[u] <= capacity[u];
s.t. feasible_regdispatch {u in U}: regdispatch[u] <= regcapacity[u];
s.t. dispatch_matches_demand: sum {u in U} dispatch[u] = dispatch_demand;
s.t. regdispatch_matches_demand: sum {u in U} regdispatch[u] = regdispatch_demand;
# solve the problem
solve;
# output input and determined values
printf {u in U} "%d capacity = %.1f; offerprice = %.1f; regcapacity = %.1f; regofferprice = %.1f\n",
u, capacity[u], offerprice[u], regcapacity[u], regofferprice[u];
printf {u in U} "%d dispatch = %.1f; cost = %.1f; regdispatch = %.1f; regcost = %.1f;\n",
u, dispatch[u], cost[u], regdispatch[u], regcost[u];
data;
param dispatch_demand := 45; /* MW */
param regdispatch_demand := 10; /* MW */
param : U : capacity offerprice regcapacity regofferprice:=
1 10 10 5 1
2 10 15 5 7
3 10 20 5 13
4 10 25 5 19
5 10 30 5 25
6 10 35 5 31
7 10 40 5 37
8 10 45 5 43
9 10 50 5 49
10 10 55 5 55
;
end;
結果表明,該區域的(電力)能源排程受到輔助服務提供的影響,因此導致總市場成本高於僅考慮能源的情況。請注意,調節服務的增加導致了排程,如果僅檢查能源排程,則不會很明顯。
* 13: obj = 1.090000000e+003 infeas = 0.000e+000 (0) OPTIMAL SOLUTION FOUND Time used: 0.0 secs Memory used: 0.1 Mb (137757 bytes) 1 capacity = 10.0; offerprice = 10.0; regcapacity = 5.0; regofferprice = 1.0 2 capacity = 10.0; offerprice = 15.0; regcapacity = 5.0; regofferprice = 7.0 3 capacity = 10.0; offerprice = 20.0; regcapacity = 5.0; regofferprice = 13.0 4 capacity = 10.0; offerprice = 25.0; regcapacity = 5.0; regofferprice = 19.0 5 capacity = 10.0; offerprice = 30.0; regcapacity = 5.0; regofferprice = 25.0 6 capacity = 10.0; offerprice = 35.0; regcapacity = 5.0; regofferprice = 31.0 7 capacity = 10.0; offerprice = 40.0; regcapacity = 5.0; regofferprice = 37.0 8 capacity = 10.0; offerprice = 45.0; regcapacity = 5.0; regofferprice = 43.0 9 capacity = 10.0; offerprice = 50.0; regcapacity = 5.0; regofferprice = 49.0 10 capacity = 10.0; offerprice = 55.0; regcapacity = 5.0; regofferprice = 55.0 1 dispatch = 5.0; cost = 50.0; regdispatch = 5.0; regcost = 5.0; 2 dispatch = 5.0; cost = 75.0; regdispatch = 5.0; regcost = 35.0; 3 dispatch = 10.0; cost = 200.0; regdispatch = 0.0; regcost = 0.0; 4 dispatch = 10.0; cost = 250.0; regdispatch = 0.0; regcost = 0.0; 5 dispatch = 10.0; cost = 300.0; regdispatch = 0.0; regcost = 0.0; 6 dispatch = 5.0; cost = 175.0; regdispatch = 0.0; regcost = 0.0; 7 dispatch = 0.0; cost = 0.0; regdispatch = 0.0; regcost = 0.0; 8 dispatch = 0.0; cost = 0.0; regdispatch = 0.0; regcost = 0.0; 9 dispatch = 0.0; cost = 0.0; regdispatch = 0.0; regcost = 0.0; 10 dispatch = 0.0; cost = 0.0; regdispatch = 0.0; regcost = 0.0;
在本示例中,我們研究了在單個時間段內,每個區域在不同價格區間具有不同的容量以及不同的區域需求的情況下,兩個區域的排程。目標函式尋求找到兩個區域的最低成本解決方案。對於本示例,假設每個區域之間沒有連結損失。在這種情況下,能源是指電力能源。
/*
Least cost unit dispatch for energy with two regions
Dr. H J Mackenzie
HARD software
hjm@hardsoftware.com
2010-03-25
*/
# set of units
set U;
# maximum capacity of the units in MW for each region a and b
param regiona_capacity {U};
param regionb_capacity {U};
# offer price of the energy in $/MWh
param offerprice {U};
# market parameters
param regiona_dispatch_demand >= 0;
param regionb_dispatch_demand >= 0;
param ab_link_capacity >= 0;
# dispatch variables
var regiona_dispatch {U} >= 0;
var regionb_dispatch {U} >= 0;
var unit_dispatch {U} >= 0;
var ab_link_dispatch; /* positive flow is defined as flow from region a to region b */
var cost {U};
# objective function
minimize market_cost: sum {u in U} cost[u];
# constraints
s.t. dispatch_cost {u in U}: cost[u] = ((regiona_dispatch[u] + ab_link_dispatch)
* offerprice[u]) + ((regionb_dispatch[u] - ab_link_dispatch) * offerprice[u]);
s.t. feasible_regiona_dispatch {u in U}: regiona_dispatch[u] <= regiona_capacity[u];
s.t. feasible_regionb_dispatch {u in U}: regionb_dispatch[u] <= regionb_capacity[u];
s.t. total_unit_dispatch {u in U}: unit_dispatch[u] = regiona_dispatch[u] + regionb_dispatch[u];
s.t. feasible_ab_link_dispatch: ab_link_dispatch <= ab_link_capacity;
s.t. feasible_ba_link_dispatch: -ab_link_dispatch <= ab_link_capacity;
s.t. regiona_dispatch_matches_demand: sum {u in U} regiona_dispatch[u]
- ab_link_dispatch = regiona_dispatch_demand;
s.t. regionb_dispatch_matches_demand: sum {u in U} regionb_dispatch[u]
+ ab_link_dispatch = regionb_dispatch_demand;
# solve the problem
solve;
# output input and determined values
printf {u in U} "%d regiona_capacity = %.1f; regionb_capacity = %.1f; offerprice = %.1f\n",
u, regiona_capacity[u], regionb_capacity[u], offerprice[u];
printf {u in U} "%d dispatch = %.1f; ab link dispatch = %.1f; cost = %.1f\n",
u, unit_dispatch[u], ab_link_dispatch, cost[u];
data;
param ab_link_capacity := 10; /* MW */
param regiona_dispatch_demand := 9; /* MW */
param regionb_dispatch_demand := 22; /* MW */
param : U : regiona_capacity regionb_capacity offerprice:=
1 10 0 10
2 10 0 20
3 10 0 30
4 0 10 15
5 0 10 25
6 0 10 35
;
end;
解決方案顯示了每個區域的排程,由於區域 A 的需求較低且發電成本較低,因此連結以滿負荷執行。
* 5: obj = 4.800000000e+002 infeas = 0.000e+000 (0) OPTIMAL SOLUTION FOUND Time used: 0.0 secs Memory used: 0.1 Mb (140601 bytes) 1 regiona_capacity = 10.0; regionb_capacity = 0.0; offerprice = 10.0 2 regiona_capacity = 10.0; regionb_capacity = 0.0; offerprice = 20.0 3 regiona_capacity = 10.0; regionb_capacity = 0.0; offerprice = 30.0 4 regiona_capacity = 0.0; regionb_capacity = 10.0; offerprice = 15.0 5 regiona_capacity = 0.0; regionb_capacity = 10.0; offerprice = 25.0 6 regiona_capacity = 0.0; regionb_capacity = 10.0; offerprice = 35.0 1 dispatch = 10.0; ab link dispatch = 10.0; cost = 100.0 2 dispatch = 9.0; ab link dispatch = 10.0; cost = 180.0 3 dispatch = 0.0; ab link dispatch = 10.0; cost = 0.0 4 dispatch = 10.0; ab link dispatch = 10.0; cost = 150.0 5 dispatch = 2.0; ab link dispatch = 10.0; cost = 50.0 6 dispatch = 0.0; ab link dispatch = 10.0; cost = 0.0
傳輸線路中的損耗是二次方形式,例如 ,因此需要用線性形式近似,以便使用線性規劃 (LP) 公式求解。解決這個問題的方法是使用以下公式近似函式 : ,受制於 。這是一個使用可分離規劃來近似凸非線性函式的例子(參見 HP Williams 的《數學規劃中的模型構建》[1]——關於非線性模型的章節)。可分離函式是可以分解為單變數函式的函式,而這些函式又可以用分段線性函式來近似。
對於 GLPK 公式,我已經對連結上的正向和負向流量進行了建模,以檢查它對兩個方向的流量是否都能正常工作。很容易混淆符號並出錯,因此檢查兩種流量是驗證該技術的一種好方法。這種技術將應用於所有區域之間的互連器流量,因此需要在以後更復雜的模型中正確使用。
/*
Linearised loss model
Dr. H J Mackenzie
2010-04-13
Problem description
Losses on the link are loss = 0.01 x flow^2 modeled
as a 10 step linear approximation [-10, 10]
Modeling of loss for loss = k x flow^2
k = 0.01
steps = 10
min flow = -10
max flow = 10
interval flow losses slope midpoint mp losses
10 1
1 -8 0.64 -0.18 -9 0.81
2 -6 0.36 -0.14 -7 0.49
3 -4 0.16 -0.1 -5 0.25
4 -2 0.04 -0.06 -3 0.09
5 0 0 -0.02 -1 0.01
6 2 0.04 0.02 1 0.01
7 4 0.16 0.06 3 0.09
8 6 0.36 0.1 5 0.25
9 8 0.64 0.14 7 0.49
10 10 1 0.18 9 0.81
*/
# set of regions
set REGIONS;
# set of transmission lines
set LINES;
param line_start {LINES} symbolic in REGIONS;
param line_end {LINES} symbolic in REGIONS;
param max_flow {LINES} >= 0;
param min_flow {LINES} <= 0;
param no_loss_segments;
set LOSS_SEGMENTS := 1.. no_loss_segments;
param loss_segment_length {LINES, LOSS_SEGMENTS} >= 0;
param loss_at_minimum_flow {LINES} >= 0;
param loss_coefficient {LINES, LOSS_SEGMENTS}; # can be positive or negative
# dispatch variables
param midline_flow {l in LINES} >= min_flow[l], <= max_flow[l];
var line_loss {LINES};
var loss_segment_dispatch {l in LINES, s in LOSS_SEGMENTS} >= 0 , <= loss_segment_length[l, s];
var line_start_flows {LINES};
var line_end_flows {LINES};
# objective function
minimize line_losses: sum {l in LINES} line_loss[l];
# constraints
s.t. transmission_line_flow {l in LINES}: sum {s in LOSS_SEGMENTS} loss_segment_dispatch[l, s]
+ min_flow[l] = midline_flow[l];
s.t. transmission_line_loss {l in LINES}: sum {s in LOSS_SEGMENTS} loss_segment_dispatch[l, s]
* loss_coefficient[l, s] + loss_at_minimum_flow[l] = line_loss[l];
s.t. transmission_line_start_flows {l in LINES}: midline_flow[l]
+ 0.5 * line_loss[l] = line_start_flows[l];
s.t. transmission_line_end_flows {l in LINES}: midline_flow[l]
- 0.5 * line_loss[l] = line_end_flows[l];
# solve the problem
solve;
# output input and determined values
printf "\n\nLine losses\n\n%-10s %-10s %10s %10s %10s\n", 'Line', 'Segment', 'Length', 'Coeff', 'Dispatch';
printf {l in LINES, s in LOSS_SEGMENTS} "%-10s %-10d %10d %10.2f %10.2f\n",
l, s, loss_segment_length[l, s], loss_coefficient[l, s], loss_segment_dispatch[l, s];
printf "\n\nLine dispatch\n\n%-10s %-10s %-10s %10s %10s %12s %10s %10s %10s %10s\n",
'Line', 'Line start', 'Line end', 'Max flow', 'Min flow', 'Minflow loss', 'Line flow',
'Line start', 'Line end', 'Line loss';
printf {l in LINES} "%-10s %-10s %-10s %10.2f %10.2f %12.2f %10.2f %10.2f %10.2f %10.2f\n",
l, line_start[l], line_end[l], max_flow[l], min_flow[l], loss_at_minimum_flow[l],
midline_flow[l], line_start_flows[l], line_end_flows[l], line_loss[l];
printf "\n";
data;
set REGIONS := 'REGION_A', 'REGION_B';
param : LINES : line_start line_end max_flow min_flow loss_at_minimum_flow midline_flow :=
'AtoBneg' 'REGION_A' 'REGION_B' 10 -10 1.0 -6
'AtoBpos' 'REGION_A' 'REGION_B' 10 -10 1.0 6
;
param no_loss_segments := 10;
param : loss_segment_length loss_coefficient :=
'AtoBneg' 1 2 -0.18
'AtoBneg' 2 2 -0.14
'AtoBneg' 3 2 -0.1
'AtoBneg' 4 2 -0.06
'AtoBneg' 5 2 -0.02
'AtoBneg' 6 2 0.02
'AtoBneg' 7 2 0.06
'AtoBneg' 8 2 0.1
'AtoBneg' 9 2 0.14
'AtoBneg' 10 2 0.18
'AtoBpos' 1 2 -0.18
'AtoBpos' 2 2 -0.14
'AtoBpos' 3 2 -0.1
'AtoBpos' 4 2 -0.06
'AtoBpos' 5 2 -0.02
'AtoBpos' 6 2 0.02
'AtoBpos' 7 2 0.06
'AtoBpos' 8 2 0.1
'AtoBpos' 9 2 0.14
'AtoBpos' 10 2 0.18
;
end;
該解決方案對兩個流量方向都一致有效,並在線路中點的名義流量下產生了合理的結果。
* 10: obj = 7.200000000e-001 infeas = 0.000e+000 (0) OPTIMAL SOLUTION FOUND Time used: 0.0 secs Memory used: 0.1 Mb (141776 bytes) Line losses Line Segment Length Coeff Dispatch AtoBneg 1 2 -0.18 2.00 AtoBneg 2 2 -0.14 2.00 AtoBneg 3 2 -0.10 0.00 AtoBneg 4 2 -0.06 0.00 AtoBneg 5 2 -0.02 0.00 AtoBneg 6 2 0.02 0.00 AtoBneg 7 2 0.06 0.00 AtoBneg 8 2 0.10 0.00 AtoBneg 9 2 0.14 0.00 AtoBneg 10 2 0.18 0.00 AtoBpos 1 2 -0.18 2.00 AtoBpos 2 2 -0.14 2.00 AtoBpos 3 2 -0.10 2.00 AtoBpos 4 2 -0.06 2.00 AtoBpos 5 2 -0.02 2.00 AtoBpos 6 2 0.02 2.00 AtoBpos 7 2 0.06 2.00 AtoBpos 8 2 0.10 2.00 AtoBpos 9 2 0.14 0.00 AtoBpos 10 2 0.18 0.00 Line dispatch Line Line start Line end Max flow Min flow Minflow loss Line flow Line start Line end Line loss AtoBneg REGION_A REGION_B 10.00 -10.00 1.00 -6.00 -5.82 -6.18 0.36 AtoBpos REGION_A REGION_B 10.00 -10.00 1.00 6.00 6.18 5.82 0.36
參考文獻
[edit | edit source]- ↑ Williams, H. Paul (1999). Model Building in Mathematical Programming. Wiley. ISBN 978-0-471-99788-7.
