Engee 文档
Notebook

发电机配电优化

导言

这个例子演示了如何最佳地安排两个发电机,以最大限度地增加收入减去成本。 虽然该示例有些简化,但它显示了如何考虑取决于决策时间的成本。

设置任务

电力市场的特点是价格在白天变化。 如果您有发电机组可供使用,则可以使用此可变价格来计划价格高的时段的运行。 让我们假设你控制两个发电机。 每个发电机有三种模式(关闭,额定和峰值)。 为每个发电机设置在每个功率水平下的特定燃料消耗和所产生的电力量。 当发电机关闭时,没有燃料消耗。

您可以为白天的每个半小时间隔(24小时,即48间隔)设置每台发电机的功率水平。 让我们假设我们知道每个时间间隔收到的每兆瓦时(MWh)收入。 该任务的目的是确定一个启动发电机的时间表,其中使用发电机的盈利能力将是最大的。

添加必要的库:

In [ ]:
# import Pkg
# Pkg.add(["JuMP", "HiGHS"])
using JuMP, HiGHS

让我们表示每个时间间隔内收到的收入金额。

In [ ]:
价格=[
    58.0, 56.5, 55.8, 54.0, 52.1, 52.5, 58.2, 58.0, 56.0, 48.5,
    49.5, 54.5, 53.0, 52.5, 55.0, 55.0, 62.0, 62.0, 62.0, 62.0,
    62.0, 62.0, 62.0, 62.5, 62.5, 62.5, 69.5, 87.0, 97.0, 157.0,
    280.0, 280.0, 280.0, 103.0, 97.0, 69.0, 61.0, 67.0, 82.0, 64.0,
    61.5, 69.0, 62.5, 62.0, 58.5, 62.0, 62.0, 57.5
]

让我们绘制电力成本对时间段的依赖性。

In [ ]:
p1=酒吧(价格, 
    bar_width=0.5,
    legend=false,
    xlims=(0.5, 48.5),
    xlabel="期间",
    ylabel="₽/MWh",
    ylim=(0, 299),
    title="电费",
    color=:blue,
    alpha=0.7)
display(p1)

发电机闲置后启动是有成本的. 此外,每天的最大油耗也有限制。 这种限制是由于燃料是提前一天购买的,并且只能使用预先购买的体积。

任务指定和参数

规划问题可以被公式化为二进制整数规划问题。 我们定义索引i,j,k和二元规划向量y如下:

  • периодов =时间段的数量,在本例中为48.

  • i =时间段, .

  • j =生成器索引, 对于这个例子。

  • y[i,j,k] =1如果发电机j在时段I期间以功率电平操作。 . 让额定功率对应于 ,而峰 — . 发电机关闭时y[i,j,k] = 0.

让我们确定发电机在空闲时间后何时启动。 为此,我们将引入一个辅助二进制变量。 z[i,j],其表示是否为在时段I期间接通发电机j而收取费用。

  • z[i,j] =1如果发生器 ,但包括在期 . z[i,j] 否则=0。 那就是, z[i,j] =1,当 y[i,j,k] = 0 y[i+1,j,k] = 1.

必须自动设置这些值 基于价值观 . 这是通过使用下面给出的线性约束来实现的。

您还需要任务参数:成本,每个单元的发电水平,燃料消耗水平和可用燃料量。

  • Цены[i] -范围内每兆瓦时的收入额 ;

  • генератор[j,k] -发电机产生的功率(以MW为单位) ;

  • топливо[j,k] -发电机消耗的燃料量 ;

  • общееТопливо —每天可用燃料量;

  • стоимостьЗапуска -停机后启动发电机的成本(以美元计);

  • ценаТоплива -单位燃料的成本。

设置其他参数:

In [ ]:
燃油价格=8
一般燃料=6.5e4
периодов = length(Цены)  # 48期
генераторов = 2  # 两台发电机
генератор = [71 172; 60 150] # 发电机的标称和峰值模式
топливо = [400 950; 280 800] # 发电机在不同工作模式下的燃料消耗
стоимостьЗапуска = 1000.0;  # 停机后启动发电机的成本

燃料消耗

我们研究了发电机在标称和峰值模式下的具体性能。

In [ ]:
性能=发电机。/燃料
p2=bar([0.875,1.875],[性能[1,1],性能[1,2]],
        color = :green,
        label = "发电机1",
        alpha = 0.9,
        bar_width = 0.15,
        xlabel = "操作模式",
        ylabel = "每单位燃料的功率",
        title = "特定生产力",
        ylim = (0.1, 0.2),
        legend = :outerright)
酒吧!(p2,[1.125,2.125],[性能[2.1],性能[2.2]],
     color = :cyan,
     label = "发电机2",
     alpha = 0.9,
     bar_width = 0.15)
xticks!([1, 2], ["名义", "山顶"])
xlims!(0.4, 2.6)
ylims!(0.0, 0.24)
display(p2)

在标称模式下,发电机2的比容量略高于发电机1的比容量。 在峰值模式下,发电机1的比生产率略高于标称模式,而发电机2略低于标称模式。 与此同时,在峰值模式下,发电机2的比生产率略高于发电机1。

要解决的变量

要设置任务,必须以解决方案所需的形式呈现所有数据和约束。 变量 y[i,j,k] 表示问题的解,以及辅助变量 z[i,j] 指示是否存在用于启动发电机的电荷。 所有变量都是二进制的。

In [ ]:
模型=模型()。优化器)
@variable(model,y[1:periodes,1:generators,1:2],Bin) 
@variable(model,z[1:periodes,1:generators],Bin) 

线性约束

为了确保功率电平不超过一个等于1的分量,我们定义了线性约束-不等式。

In [ ]:
空间约束=@constraint(model,[i=1:periodes,j=1:generators],y[i,j,1]+y[i,j,2]<=1)

一段时间的运行成本是该段时间的燃料成本。 对于在k级运行的发电机j,成本为 ценаТоплива * Топливо[j,k].

让我们创建一个考虑到所有使用的燃料的表达式。

In [ ]:
二手燃料=@expression(model,sum(y[i,j,k]*fuel[j,k]for i in1:periodes,j in1:generators,k in1:2))

限制是所使用的燃料不得超过可用容积。

In [ ]:
燃料限制=@约束(燃料使用的模型<=一般燃料)

发电机启动指示器变量

如何确保求解器根据变量的活动/非活动周期自动设置z变量 ? 回想一下必须满足的条件: z(i,j) = 1 确切的时间 y[i,j,k] = 0y[i+1,j,k] = 1.

请注意 ( - y[i,j,k] + y[i+1,j,k] ) > 0 只是在需要的情况下 z[i,j] = 1.

因此,我们在问题的制定中包括以下线性约束-不等式:

( - y[i,j,k] + y[i+1,j,k] ) - z[i,j] <= 0.

此外,我们将包括变量 到目标函数。 因为变量 它们包含在目标函数中,算法试图最小化它们的值,即它试图将它们全部设置为相等。 .

创建辅助变量 ,其代表 y(i+1,j,k) - y(i,j,k). 让我们想象一下通过以下方式启动发电机的限制 .

In [ ]:
w=@表达式(模型,[i=1:周期,j=1:生成器], 
    如果i<句点
        y[i+1,j,1] - y[i,j,1] + y[i+1,j,2] - y[i,j,2]
    else
        y[1,j,1] - y[i,j,1] + y[1,j,2] - y[i,j,2]
    end)
包含约束=@constraint(model,[i=1:periodes,j=1:generators],w[i,j]-z[i,j]<=0)

定义目标函数

目标函数包括发电机的燃料成本,其运行的盈利能力以及启动它们的成本。

In [ ]:
生成器级别=零(周期,生成器,2)
生成器级别为[:,1,1]=发电机[1,1]  
生成器级别[:,1,2]=发电机[1,2]  
生成器级别[:,2,1]=发电机[2,1]  
生成器级别为[:,2,2]=发电机[2,2]

发电机级别

让我们定义一个收入金额的表达式。

In [ ]:
收入=@表达式(模型,[i=1:周期,j=1:发电机,k=1:2],
    价格[i]*y[i,j,k]*发电机级别[i,j,k])

让我们定义一个燃料成本的表达式。

In [ ]:
燃料成本=使用过的燃料*燃料价格

让我们为启动生成器的成本定义一个表达式。

In [ ]:
启动成本=z*启动成本

让我们定义利润金额的表达式。

In [ ]:
利润=@表达式(模型, 
    sum(Prices[I]*y[i,j,k]*Generator level[i,j,k] 
        for i in1:periodes,j in1:generators,k in1:2)-sum(y[i,j,k]*fuel[j,k]*Fuel price 
        for i in1:periodes,j in1:generators,k in1:2)-sum(z[i,j] 
        对于i in1:周期,j in1:发电机)*启动成本)

解决问题

让我们创建一个优化任务,并在其中包含目标函数和约束。

In [ ]:
@目标(模型、最大值、利润)

让我们来解决这个问题。

In [ ]:
优化!(型号)
=(y=值。(y),z=值。(z))  
=objective_value(模型)                               
Running HiGHS 1.12.0 (git hash: 755a8e027a): Copyright (c) 2025 HiGHS under MIT licence terms
MIP has 193 rows; 288 cols; 864 nonzeros; 288 integer variables (288 binary)
Coefficient ranges:
  Matrix  [1e+00, 1e+03]
  Cost    [2e+02, 4e+04]
  Bound   [1e+00, 1e+00]
  RHS     [1e+00, 6e+04]
Presolving model
193 rows, 288 cols, 864 nonzeros  0s
193 rows, 288 cols, 864 nonzeros  0s
Presolve reductions: rows 193(-0); columns 288(-0); nonzeros 864(-0) - Not reduced
Objective function is integral with scale 10

Solving MIP model with:
   193 rows
   288 cols (288 binary, 0 integer, 0 implied int., 0 continuous, 0 domain fixed)
   864 nonzeros

Src: B => Branching; C => Central rounding; F => Feasibility pump; H => Heuristic;
     I => Shifting; J => Feasibility jump; L => Sub-MIP; P => Empty MIP; R => Randomized rounding;
     S => Solve LP; T => Evaluate node; U => Unbounded; X => User solution; Y => HiGHS solution;
     Z => ZI Round; l => Trivial lower; p => Trivial point; u => Trivial upper; z => Trivial zero

        Nodes      |    B&B Tree     |            Objective Bounds              |  Dynamic Constraints |       Work      
Src  Proc. InQueue |  Leaves   Expl. | BestBound       BestSol              Gap |   Cuts   InLp Confl. | LpIters     Time

 z       0       0         0   0.00%   inf             -0                 Large        0      0      0         0     0.1s
 J       0       0         0   0.00%   inf             560                Large        0      0      0         0     0.1s
 R       0       0         0   0.00%   514387.540741   504531.6           1.95%        0      0      0       115     0.1s
 L       0       0         0   0.00%   514255.480826   514015.6           0.05%     1045     21     11       241     0.6s

82.3% inactive integer columns, restarting
Model after restart has 24 rows, 37 cols (35 bin., 2 int., 0 impl., 0 cont., 0 dom.fix.), and 104 nonzeros

         0       0         0   0.00%   514255.326734   514015.6           0.05%        7      0      0       581     0.7s
         0       0         0   0.00%   514255.326734   514015.6           0.05%        7      7      2       602     0.7s

2.7% inactive integer columns, restarting
Model after restart has 22 rows, 36 cols (35 bin., 1 int., 0 impl., 0 cont., 0 dom.fix.), and 97 nonzeros

         0       0         0   0.00%   514238.65312    514015.6           0.04%        7      0      0       778     0.8s
         0       0         0   0.00%   514238.65312    514015.6           0.04%        7      7      2       804     0.8s

2.8% inactive integer columns, restarting
Model after restart has 21 rows, 35 cols (34 bin., 1 int., 0 impl., 0 cont., 0 dom.fix.), and 93 nonzeros

         0       0         0   0.00%   514232.533911   514015.6           0.04%        7      0      0       889     0.9s
         0       0         0   0.00%   514232.533911   514015.6           0.04%        7      7      4       905     0.9s
        49       0        20 100.00%   514067.0025     514015.6           0.01%      656      7    354      1824     1.2s

Solving report
  Status            Optimal
  Primal bound      514015.6
  Dual bound        514067
  Gap               0.01% (tolerance: 0.01%)
  P-D integral      4.02219234811
  Solution status   feasible
                    514015.6 (objective)
                    0 (bound viol.)
                    6.17542280566e-15 (int. viol.)
                    0 (row viol.)
  Timing            1.20
  Max sub-MIP depth 4
  Nodes             49
  Repair LPs        0
  LP iterations     1824
                    397 (strong br.)
                    347 (separation)
                    679 (heuristics)
Out[0]:
514015.60000000003

解决方案分析

让我们绘制发电机的最佳时间表作为时间的函数。

In [ ]:
p3 = plot(layout = (3,1), size = (800, 600))
bar!(p3[1], 
     决定。y[:,1,1]*generator[1,1]+解。y[:,1,2]*生成器[1,2],
     bar_width = 0.5,
     color = :green,
     xlims = (0.5, 48.5),
     ylims = (0, 179),
     ylabel = "MWh",
     title = "发电机1-最佳时间表",
     titlefontweight = :bold,
     legend = false)
bar!(p3[2],
     决定。y[:,2,1]*generator[2,1]+解。y[:,2,2]*生成器[2,2],
     bar_width = 0.5,
     color = :cyan,
     ylims = (0, 159),
     xlims = (0.5, 48.5),
     ylabel = "MWh",
     title = "发电机2-最佳时间表",
     titlefontweight = :bold,
     legend = false)
bar!(p3[3],
     价格;价格,
     bar_width = 0.5,
     ylims = (0, 299),
     xlims = (0.5, 48.5),
     xlabel = "期间",
     ylabel = "₽/MWh",
     title = "电价",
     titlefontweight = :bold,
     legend = false
)
display(p3)

根据最佳时间表,发电机1在峰值模式下操作,关闭几个周期,然后再次打开并在峰值模式下操作。 发电机2为所有48个周期工作,从峰值切换到标称模式2次。

与最低启动成本的比较

为启动成本设置较低的值,然后再次运行优化任务。

In [ ]:
启动成本=100
启动成本=sum(z)*启动成本 
利润=@表达式(模型, 
    sum(revenue[i,j,k]for i in1:periodes,j in1:generators,k in1:2) 
    -燃料消耗 
    -启动成本)
@目标(模型、最大值、利润)
优化!(型号)
新生代=(y=值。(y),z=值。(z))
新值=objective_value(模型)
MIP has 193 rows; 288 cols; 864 nonzeros; 288 integer variables (288 binary)
Coefficient ranges:
  Matrix  [1e+00, 1e+03]
  Cost    [1e+02, 4e+04]
  Bound   [1e+00, 1e+00]
  RHS     [1e+00, 6e+04]
Presolving model
193 rows, 288 cols, 864 nonzeros  0s
193 rows, 288 cols, 864 nonzeros  0s
Presolve reductions: rows 193(-0); columns 288(-0); nonzeros 864(-0) - Not reduced
Objective function is integral with scale 10

Solving MIP model with:
   193 rows
   288 cols (288 binary, 0 integer, 0 implied int., 0 continuous, 0 domain fixed)
   864 nonzeros

Src: B => Branching; C => Central rounding; F => Feasibility pump; H => Heuristic;
     I => Shifting; J => Feasibility jump; L => Sub-MIP; P => Empty MIP; R => Randomized rounding;
     S => Solve LP; T => Evaluate node; U => Unbounded; X => User solution; Y => HiGHS solution;
     Z => ZI Round; l => Trivial lower; p => Trivial point; u => Trivial upper; z => Trivial zero

        Nodes      |    B&B Tree     |            Objective Bounds              |  Dynamic Constraints |       Work      
Src  Proc. InQueue |  Leaves   Expl. | BestBound       BestSol              Gap |   Cuts   InLp Confl. | LpIters     Time

 z       0       0         0   0.00%   inf             -0                 Large        0      0      0         0     0.0s
 J       0       0         0   0.00%   inf             36560              Large        0      0      0         0     0.0s
 R       0       0         0   0.00%   516033.193684   508237             1.53%        0      0      0       111     0.0s
 C       0       0         0   0.00%   516010.462282   508323.4           1.51%       67      5      0       119     0.0s
 L       0       0         0   0.00%   515939.624512   515785.2           0.03%      662     17      0       196     0.4s

55.9% inactive integer columns, restarting
Model after restart has 36 rows, 53 cols (53 bin., 0 int., 0 impl., 0 cont., 0 dom.fix.), and 136 nonzeros

         0       0         0   0.00%   515939.515044   515785.2           0.03%        7      0      0       506     0.5s
         0       0         0   0.00%   515939.515044   515785.2           0.03%        7      7      2       520     0.5s

5.7% inactive integer columns, restarting
Model after restart has 34 rows, 50 cols (50 bin., 0 int., 0 impl., 0 cont., 0 dom.fix.), and 129 nonzeros

         0       0         0   0.00%   515916.892919   515785.2           0.03%        8      0      0       922     0.9s
         0       0         0   0.00%   515916.892919   515785.2           0.03%        8      8      4       940     0.9s
        44       0        12 100.00%   515835.4        515785.2           0.01%     1547     12    414      2218     1.5s

Solving report
  Status            Optimal
  Primal bound      515785.2
  Dual bound        515835.4
  Gap               0.00973% (tolerance: 0.01%)
  P-D integral      0.0494135065927
  Solution status   feasible
                    515785.2 (objective)
                    0 (bound viol.)
                    8.881784197e-16 (int. viol.)
                    0 (row viol.)
  Timing            1.46
  Max sub-MIP depth 4
  Nodes             44
  Repair LPs        0
  LP iterations     2218
                    538 (strong br.)
                    394 (separation)
                    921 (heuristics)
Out[0]:
515785.2

让我们绘制具有降低启动成本的发电机的最佳运行时间表。

In [ ]:
p4 = plot(layout = (3,1), size = (800, 600))
bar!(p4[1], 
     [新一代。y[i,1,1]*generator[1,1]+新一代。y[i,1,2]*generator[1,2]for i in1:periodes],
     bar_width = 0.5, color = :green, xlims = (0.5, 48.5), ylabel = "MWh", ylims = (0, 179),
     title = "发电机1-最佳时间表", titlefontweight = :bold, legend = false)  
bar!(p4[2],
     [新一代。y[i,2,1]*generator[2,1]+新一代。y[i,2,2]*generator[2,2]for i in1:periodes],
     bar_width = 0.5, color = :cyan, xlims = (0.5, 48.5), ylabel = "MWh", ylims = (0, 159),
     title = "发电机2-最佳时间表", titlefontweight = :bold, legend = false)
酒吧!(p4[3],价格,
     bar_width = 0.5, xlims = (0.5, 48.5), xlabel = "期间", ylabel = "₽/MWh", ylims = (0, 299),
     title = "电价", titlefontweight = :bold, legend = false)
display(p4)

这些图表显示了发电机的最佳运行计划,考虑到降低的启动成本。 发电机1主要工作在峰值模式,在运行中有两个中断。 发电机2最初以标称模式操作,然后切换到峰值模式,并且仅在结束时两次切换到标称模式,并且在所有时段期间连续操作。

结论

该研究证实了使用二进制整数规划优化发电机日常运行时间表的有效性。 开发的模型成功地考虑了关键因素:不断变化的市场价格,离散功率水平,燃料消耗和启动成本。

结果表明,成本结构对策略的影响是至关重要的. 高启动成本导致长时间的连续工作周期,而其减少使得更灵活地使用多个夹杂物来精确跟踪价格峰值在经济上有利。 该模型还量化了发电机模式的选择,不仅考虑了它们的特定效率,还考虑了能量的时变成本。

计算实验证明了该方法的实际适用性:在可接受的时间内将中等规模的问题求解为接近最优的解决方案。 这为在派遣支助系统中实施这种模式提供了机会,确保在最大限度地增加收入和遵守技术限制之间取得平衡。