首页 / 资讯中心 / 文章详情

从零手写原对偶内点法求解电力系统最优潮流

从零手写原对偶内点法求解电力系统最优潮流 ★ FEATURED ARTICLE
1. 为什么我决定自己手写内点法而不是继续用MATPOWER先说个背景。我研究电力系统最优潮流OPFOptimal Power Flow有一阵子了之前的工作流很简单把IEEE标准节点数据整理好丢给MATPOWER的runopf函数几秒钟就出结果。方便是真的方便但问题也很明显——MATPOWER是个高度封装的黑箱里面用的是哪种内点法变体、障碍参数怎么衰减、修正方程怎么组装我完全看不到。等到我想改进算法、把OPF嵌入到自己的论文框架里做动态安全校正、或者想给某个目标函数加个自定义惩罚项时就开始束手束脚了。后来我干脆决定从头用Matlab手写一个原对偶内点法Primal-Dual Interior Point Method来解最优潮流。这篇博客就是把整个过程整理出来包括数学模型推导、算法原理、关键代码实现、算例验证以及我调试过程中踩过的坑。内容面向那些已经学过电力系统分析或者潮流计算、但没系统接触过最优潮流和内点法的同学也适合想摆脱MATPOWER依赖、自己掌控算法的研究者。我选Matlab而不是Python的原因很简单Matlab的稀疏矩阵运算非常成熟Ybus储能节点导纳矩阵之后A\b求解稀疏线性方程组的速度和稳定性都很靠谱而且电力系统领域积累了大量Matlab工具包和教程沟通成本低。另外Matlab的调试环境和变量探查能力对写数值算法来说太友好了迭代中间过程随时可以停下来看残差和矩阵状态。当然如果你手头有R2022b之后的版本直接跑下面的代码没问题。老版本我建议先确认支持稀疏矩阵的lu分解和mldivide实际上只要不是远古版本都没问题。2. 先把数学模型理清楚从潮流计算到最优潮流多了哪些东西2.1 潮流计算解的是“给定发电机出力后系统是什么状态”经典潮流计算Power Flow的任务是已知负荷有功无功、发电机有功和机端电压幅值求解整个电网各节点的电压幅值和相角。它的数学本质就是节点功率平衡方程对于节点i有功和无功必须满足[ P_i(V,\theta) V_i \sum_{j1}^{n} V_j (G_{ij}\cos\theta_{ij} B_{ij}\sin\theta_{ij}) ][ Q_i(V,\theta) V_i \sum_{j1}^{n} V_j (G_{ij}\sin\theta_{ij} - B_{ij}\cos\theta_{ij}) ]其中(G_{ij})和(B_{ij})是从节点导纳矩阵(Y_{bus})实部和虚部来的(\theta_{ij} \theta_i - \theta_j)是相角差。如果发电机出力给定那么PQ节点的注入功率已知PV节点的有功和电压幅值已知平衡节点电压和相角已知方程组个数和未知量个数相等的可以用牛顿-拉夫逊法迭代求解。但现实运行中“发电机出力给定”这个前提本身就是需要优化的——同样是满足负荷有的机组出力组合花了100万元燃料成本有的只需要80万元。潮流计算不管成本只管物理可行。这就要上最优潮流了。2.2 OPF的决策变量、目标函数和约束条件最优潮流OPF在潮流计算的基础上增加了优化维度在满足潮流方程和运行安全约束的前提下寻找一组控制变量使目标函数通常是发电成本最小。先看决策变量怎么分控制变量u各发电机有功出力(P_g)、发电机机端电压幅值(V_g)。这部分是调度员可以直接调整的。状态变量x各节点的电压幅值(V)和相角(\theta)。它们是控制变量作用下系统自动达到的响应。我在代码里为了编程方便直接把所有变量统一成一个向量用索引区分。以IEEE 5节点系统为例系统里有3台发电机总变量个数大约是5个节点的电压幅值5个加相角5个平衡节点相角固定为0°再加发电机有功(P_g)3个大约13个左右。实际系统中变量数量是(2N N_g)量级N是节点数N_g是发电机数。目标函数我用最常见的二次成本函数[ \min \quad f(P_g) \sum_{g} (a_{2,g}P_g^2 a_{1,g}P_g a_{0,g}) ]等式约束就是潮流方程本身[ P_{g,i} - P_{d,i} - P_i(V,\theta) 0, \quad \forall i ][ Q_{g,i} - Q_{d,i} - Q_i(V,\theta) 0, \quad \forall i ]不等式约束包括发电机有功上下限(P_g^{\min} \le P_g \le P_g^{\max})发电机无功上下限(Q_g^{\min} \le Q_g \le Q_g^{\max})机端电压幅值限制(V_i^{\min} \le V_i \le V_i^{\max})节点电压幅值限制(V_i^{\min} \le V_i \le V_i^{\max})线路潮流输电功率上限(|P_{ij}| \le P_{ij}^{\max})这里有个关键认知为什么不能用简单的梯度下降或者遗传算法应付因为OPF问题的约束条件高度非线性、变量之间强耦合而且实际电网动辄上千节点问题规模很大。启发式算法遗传、粒子群虽然能搜但计算量大、收敛性没有保证学术界和工程界的主流还是基于梯度的确定性算法内点法就是其中最实用的一种。3. 内点法的核心思想屏障函数与KKT条件的配合3.1 为什么叫“内点法”内点法的英文是Interior Point Method核心思想很直白把不等式约束变成目标函数里的惩罚项而且这个惩罚项在靠近边界时趋向无穷大从而“逼着”迭代点始终待在可行域内部不会越界。举个生活中的类比你在院子里遛狗院子边界有电子围栏狗接近围栏时项圈会发出越来越强的警告声狗就不敢越界了。内点法里的对数障碍函数就是这个电子围栏。对不等式约束(g(x) \ge 0)构造障碍函数[ \phi(x, \mu) -\mu \sum \ln(g(x)) ]其中(\mu)是障碍参数。当(g(x))趋近于0也就是点接近边界时(\ln(g(x)))趋于负无穷乘以负号后整个项趋于正无穷目标函数瞬间飙升迭代点自然被推回内部。3.2 原对偶内点法与KKT条件严格来说内点法有很多种变体。我用的这种叫原对偶内点法Primal-Dual Interior Point Method它的名称含义是同时更新原始变量和对偶变量拉格朗日乘子迭代过程中两层变量一起推进收敛速度比单纯障碍法快得多。把不等式约束引入松弛变量s变成等式约束[ g(x) - s 0, \quad s \ge 0 ]然后构造拉格朗日函数[ L f(x) - y^T h(x) - z^T (g(x) - s) - \mu \sum \ln(s_i) ]其中(h(x))是等式约束潮流方程(y)是等式约束的对偶乘子(z)是不等式约束的对偶乘子。对(L)分别对(x, s, y, z)求偏导并令其为零就得到KKT条件的非线性方程组。再加上障碍项对(s)求导的表达式最终核心方程组可以整理成一个非常经典的稀疏分块形式[ \begin{bmatrix} H J^T 0 \ J 0 0 \ 0 Z S \end{bmatrix} \begin{bmatrix} \Delta x \ \Delta y \ \Delta s \end{bmatrix} - \begin{bmatrix} \nabla f(x) - J^T y - z \ h(x) \ S z - \mu e \end{bmatrix} ]这里(H)是拉格朗日函数的海森矩阵Hessian(J)是等式约束雅可比矩阵Jacobian(S \text{diag}(s))(Z \text{diag}(z))(J^T)是雅可比矩阵的转置代表对偶变量对等式约束的耦合别被矩阵吓住。编程时你只需要按照这个结构把各个子块填进去然后用\一次解出修正量(\Delta x, \Delta y, \Delta s)迭代更新即可。3.3 障碍参数(\mu)怎么选互补间隙是灵魂障碍参数(\mu)在每次迭代后都要缩减它是内点法收敛性的关键。标准做法是计算互补间隙complementary gap[ \mu \sigma \frac{s^T z}{n_{ineq}} ]其中(s^T z)是松弛变量和对偶乘子的内积(n_{ineq})是不等式约束个数。这个量实际上度量了“当前点离边界有多远、最优性条件满足了多少”。收敛时(s^T z \to 0)障碍参数也趋于0。(\sigma)是中心化参数通常在0.1到0.5之间取。它的作用是控制迭代轨迹不要完全贴近边界保持一点“居中”流动性避免过早扎进某个约束角落。我在测试中发现(\sigma0.1)这个值收敛快但偶尔振荡(\sigma0.2)更稳健后面代码里我就用0.2。4. Matlab实现从节点数据到内点法主循环的完整框架4.1 输入数据与Ybus构建编写之前先把输入数据结构定好。为了方便对照MATPOWER的标准格式我直接沿用了bus、gen、branch三个结构体数组的字段定义bus矩阵每行一个节点列有节点编号、类型1为PQ、2为PV、3为平衡、负荷有功(P_d)、负荷无功(Q_d)、电压幅值初值、电压相角初值等。gen矩阵每行一台发电机有节点编号、有功出力初值、无功出力初值、电压幅值设定值、有功上下限、无功上下限、成本系数。branch矩阵每行一条线路有首端节点、末端节点、电阻、电抗、对地导纳、线路容量上限。我写了一个小函数makeYbus来构建节点导纳矩阵。注意处理变压器支路和线路对地导纳时要仔细这部分容易出错。实际测试时我用了IEEE 5节点系统数据如下节点类型电压初值(pu)有功负荷(MW)无功负荷(MVar)1平衡节点1.06002PV节点1.020103PQ节点1.045154PQ节点1.04055PQ节点1.06010三台发电机分别挂在节点1、节点2、节点3各自的成本和出力上下限不同。以节点1的发电机为例成本系数(a_20.005, a_110, a_00)出力范围[10, 100]MW。这是典型的热电厂成本模型二次项模拟燃料消耗的边际递增特性。4.2 主函数框架与变量索引设计变量索引是整个代码最容易乱的地方。我建议用一个结构体idx把索引集中管理而不是硬编码数字。因为变量多了之后改一个数字会牵一发动全身硬编码容易酿成灾难。function [x_opt, f_opt, iter_info] opf_interior_point() % 最优潮流 - 原对偶内点法主函数 % 返回值最优状态变量、最优目标函数值、迭代过程信息 % 加载系统数据 [bus, gen, branch] load_sys_data(ieee5); % 构建节点导纳矩阵 Ybus makeYbus(bus, branch); % 变量索引设计 % 变量排列顺序所有节点相角theta(1:N), 所有节点电压幅值V(N1:2N), % 发电机有功Pg(2N1:2NNg), 发电机无功Qg(2NNg1:2N2*Ng) nb size(bus, 1); ng size(gen, 1); idx.theta 1:nb; idx.V nb1:2*nb; idx.Pg 2*nb1:2*nbng; idx.Qg 2*nbng1:2*nb2*ng; n_x 2*nb 2*ng; % 原始变量总数 % 初始化变量后续会详细说明 x0 initialize_x(bus, gen, idx); ...初始化时所有节点电压幅值设为1.0标幺值相角设为0发电机有功设在上下限的中间值附近无功初值设在无功下限附近的内侧保证初始点在不等式约束的严格内部。这个过程叫“可行初始点构造”做不好内点法开局就会翻车后面会细讲。4.3 目标函数、等式约束与雅可比矩阵的计算目标函数和约束表达式看起来简单但求导要注意细节。先对目标函数解析求导[ \frac{\partial f}{\partial P_{g,k}} 2a_{2,k}P_{g,k} a_{1,k} ]其他变量对目标函数不直接贡献。拉格朗日函数的海森矩阵要包含等式约束的二次项——潮流方程的雅可比对状态变量再求导。这一块计算量最大也是最容易写错的部分我在调试时花了很多时间核对数值梯度。潮流方程雅可比矩阵的构建我用了小技巧先用稀疏结构预分配再逐项填充非零元素。例如对于节点i的方程对节点j的电压幅值的偏导[ \frac{\partial P_i}{\partial V_j} V_i (G_{ij}\cos\theta_{ij} B_{ij}\sin\theta_{ij}), \quad j \ne i ]对角项要额外加上节点本身的自导纳贡献和负荷项。标准公式在几乎所有电力系统分析教材里都有但真正编程时很容易把符号搞反我的建议是写完之后用一个3节点小系统做数值梯度验证。这里给出一个核对梯度的小工具函数function check_grad(x0, fh, grad_fh, desc) % 数值梯度与解析梯度对比验证 % fh函数句柄输入x返回标量 % grad_fh函数句柄输入x返回梯度向量 eps0 1e-6; g_num zeros(length(x0), 1); for i 1:length(x0) xp x0; xm x0; xp(i) xp(i) eps0; xm(i) xm(i) - eps0; g_num(i) (fh(xp) - fh(xm)) / (2 * eps0); end g_ana grad_fh(x0); fprintf(%s: 最大误差 %.2e\n, desc, max(abs(g_num - g_ana))); end这个函数我强烈建议每个写优化代码的人都保留。三次独立验证了潮流方程雅可比矩阵正确之后我才敢往下走。4.4 不等式约束与搜索方向求解不等式约束的整理也很有讲究。机组出力上下限、电压上下限、线路潮流上限全部统一写成(g(x) \ge 0)的形式例如对上限约束[ g(x) P_g^{\max} - P_g \ge 0 ]对下限约束[ g(x) P_g - P_g^{\min} \ge 0 ]线路潮流约束用直流潮流近似还是交流精确模型我在正式代码里用的是交流模型下的有功潮流表达式[ P_{ij} V_i^2 (g_{ij} g_{si}) - V_i V_j (g_{ij}\cos\theta_{ij} b_{ij}\sin\theta_{ij}) ]注意这里(g_{si})是线路对地电导的一半。这个表达式对电压幅值和相角的偏导也要算清楚写进约束的雅可比矩阵中。搜索方向的求解是核心。整个修正方程组组装完之后我用Matlab的稀疏\求解% 组装KKT系统的稀疏矩阵 A [H, J_eq; J_eq, sparse(n_eq, n_eq)]; ... dxy A \ (-rhs);Matlab对稀疏对称不定矩阵的处理很成熟直接\就好。如果矩阵规模大到上千节点可以考虑用lu分解并保存分解结果复用能显著提速。迭代更新的步长有一个细节对松弛变量s和对偶变量z要分别计算最大步长再乘以一个安全系数通常取0.99到0.995确保更新后s和z仍然严格大于零[ \alpha_s 0.99 \cdot \min\left(1, \min_{i: \Delta s_i 0} \frac{s_i}{-\Delta s_i}\right) ][ \alpha_z 0.99 \cdot \min\left(1, \min_{i: \Delta z_i 0} \frac{z_i}{-\Delta z_i}\right) ]安全系数不取1.0是因为如果恰好踩到边界对数障碍会直接变成无穷大计算浮点溢出。取0.99相当于给迭代点留了1%的“呼吸空间”。4.5 完整迭代主循环代码核心主循环不长我贴出来并加了详细注释function [x, s, z, y, mu_hist, res_hist] solve_opf_pdip() % 原对偶内点法求解最优潮流主迭代 % 返回最优点、松弛变量、对偶变量、障碍参数历程、残差历程 % ... 前面加载数据、初始化变量省略 ... % 常数设置 max_iter 50; tol 1e-6; sigma 0.2; % 中心化参数 safe_factor 0.99; % 步长安全系数 % 初始化松弛变量和对偶变量 s ones(n_ineq, 1); % 松弛变量初始为1 z ones(n_ineq, 1); % 对偶变量初始为1 mu (s * z) / n_ineq; % 初始障碍参数 for iter 1:max_iter % 1. 计算目标函数梯度、等式约束残差、不等式约束值 [f, g_obj, h_eq, g_ineq, J_eq, J_ineq, H] eval_functions(x); % 2. 计算等式约束雅可比与目标梯度的组合 % 拉格朗日函数对x的梯度∇f - J_eq^T * y - J_ineq^T * z grad_L g_obj - J_eq * y - J_ineq * z; % 3. 组装KKT残差 r_x grad_L; r_y h_eq; % 等式约束残差 r_s s .* z - mu; % 互补条件残差此处mu是标量 r_z g_ineq - s; % 定义约束 g(x) - s 0 % 4. 收敛判定原始残差、对偶残差、互补间隙 res [r_x; r_y; r_s; r_z]; if norm(r_y, inf) tol norm(r_s, inf) tol mu tol fprintf(收敛于第%d次迭代\n, iter); break; end % 5. 求解修正方程核心组装大规模稀疏矩阵 % 构成 [H修正项, J_eq; J_eq, 0] 系统 KKT [H sp_修正项, J_eq; J_eq, sparse(n_eq, n_eq)]; rhs -[r_x - J_ineq * (z .* r_z ./ s); r_y]; % 6. 回代求dx, dy, 再求ds, dz delta_xy KKT \ rhs; dx delta_xy(1:n_x); dy delta_xy(n_x1:end); ds -r_z - J_ineq * dx; % 由 r_z 方程解 ds dz -z - (r_s z .* ds) ./ s; % 由 r_s 方程解 dz % 7. 确定步长并更新 [alpha_s, alpha_z] compute_step(s, z, ds, dz, safe_factor); x x alpha_s * dx; y y alpha_z * dy; s s alpha_s * ds; z z alpha_z * dz; % 8. 更新障碍参数mu mu sigma * (s * z) / n_ineq; % 记录迭代历史 mu_hist(iter) mu; res_hist(iter) norm(r_y, inf); end end需要特别说明的是上面的修正方程里我做了一个化简。更完整的推导会把原始的3×3分块系统消元成2×2的KKT系统把松弛变量(\Delta s)和(\Delta z)先消掉减少未知量规模。这个消元过程可以参考任何一本关于内点法优化的参考书比如Andersson的《Power System Analysis》讲义或者Wright的《Primal-Dual Interior-Point Methods》。消元之后最终求(\Delta x, \Delta y)的核心系统是[ \begin{bmatrix} H J^T \ J 0 \end{bmatrix} \begin{bmatrix} \Delta x \ \Delta y \end{bmatrix}\begin{bmatrix} -\text{rhs}_1 \ -\text{rhs}_2 \end{bmatrix} ]其中对角修正项来自不等式约束的对偶变量与松弛变量的比值(z_i / s_i)的加权海森项。这个结构有个好处矩阵对称、稀疏度高而且不涉及原问题不等式约束的直接大矩阵数值稳定性好。4.6 步长策略一个被很多人忽略的细节很多教程只讲“更新步长取0.995”但实际代码里更稳妥的写法是限制总的原始步长不超过2.0防止某些情况下修正方向过大导致目标函数反而升上去。虽然理论上牛顿法在局部是二次收敛的但在远离最优点时步长可能过于激进我在测试中确实碰到过一次步长超过3导致迭代发散的情况。我的经验法则是如果互补间隙还很大(1e-2)说明远离最优点步长用障碍边界限制即可如果互补间隙小于(1e-3)说明接近最优点了可以放宽到1.0以内的标准牛顿步长如果目标函数在连续两次迭代中上升立刻把步长减半并且重新计算当次迭代的方向——虽然这会浪费一次函数计算但比发散重启好得多。5. 算例测试IEEE 5节点系统的收敛表现与结果对照5.1 测试环境与参数设置我用上面这套代码在Matlab R2023b上测试了IEEE 5节点系统。发电机和线路参数先说明一下发电机G1节点1成本系数([0.005, 10, 0])有功范围[10, 100]MW无功范围[-30, 50]MVAR发电机G2节点2成本系数([0.008, 12, 0])有功范围[10, 80]MW无功范围[-20, 40]MVAR发电机G3节点3成本系数([0.012, 14, 0])有功范围[5, 60]MW无功范围[-15, 30]MVAR负载总量加起来约165MW、40MVar。总负荷比总最大出力小不少存在充足调节空间。5.2 迭代过程与收敛曲线跑出来的结果和MATPOWER高度一致。最优发电成本约2001美元/小时而MATPOWER的相应结果也是2001.5左右误差在0.02%以内。这个误差主要来自我用的收敛阈值是1e-6MATPOWER内部可能更严。迭代过程里我记录了两组关键数据迭代次数互补间隙mu等式约束最大残差目标函数值($/h)02.05.8e-1235030.352.0e-2205060.0423.1e-4201290.00585.2e-62002120.00032.0e-72001.2可以看到大约10次迭代后目标函数就已经基本稳定后面消耗的迭代纯粹是让互补间隙和残差继续往下压到容差内。这种“快收敛-慢精修”的形态是内点法的典型行为。5.3 结果校验拉格朗日乘子与运行经济学解释除了变量值本身我特别看了一遍拉格朗日乘子这是内点法的一大红利——它直接给出约束的影子价格。系统里某条线路的有功潮流越靠近上限对应的乘子就越大。比如有一条支路传输功率达到上限的98%乘子约为3.8美元/MWh这意味着如果线路容量放宽1MW总发电成本可以下降约3.8美元。这个信息对电网扩建规划特别有用比单纯看最优解多了“瓶颈识别”维度。同时平衡节点的电压幅值在最优解里大约为1.055pu高于1.06上限的约束接近激活对应的电压乘子为负数——提高电压上限将允许系统放宽电压约束从而以更低成本满足负荷。这些经济学解释是MATPOWER黑箱用法很难直接感受到的。6. 调试过程中的踩坑记录没有数值梯度检验我会一直错下去6.1 坑一潮流方程雅可比矩阵的符号错误这个坑我踩得最深。牛顿法求解潮流的关键公式[ \frac{\partial P_i}{\partial \theta_j} -V_i V_j (G_{ij}\sin\theta_{ij} - B_{ij}\cos\theta_{ij}), \quad j \neq i ]我在写代码时把括号里的符号搞反了导致早期迭代还可以但后期永远无法收敛到高精度。这个问题靠眼睛看代码根本看不出来因为每一项看起来都很合理。最后是check_grad函数抓出来的——解析梯度与数值梯度的最大误差有1e-3虽然不大但足以破坏最优解的精度。6.2 坑二初始点必须严格在可行域内部内点法对初始点有一个硬性要求不等式约束的松弛变量必须严格大于零。如果初始(P_g)恰好等于上限那么(P_g^{\max} - P_g 0)对数障碍函数计算出来是无穷大整个迭代直接崩掉。我的解决办法是初始化时把变量放在上下限之间某个比例位置留出安全余量function x0 initialize_x(bus, gen, idx) nb size(bus, 1); ng size(gen, 1); x0 zeros(2*nb 2*ng, 1); % 电压幅值在1.0附近绝对不设成边界值 x0(idx.V) bus(:, 8); % bus第8列通常是电压幅值设定 x0(idx.theta) zeros(nb, 1); % 发电机有功取上下限的30%位置附近 Pg_lo gen(:, 9); Pg_hi gen(:, 10); x0(idx.Pg) Pg_lo 0.3 * (Pg_hi - Pg_lo); % 发电机无功初始在无功下限附近的内侧预留空间 Qg_lo gen(:, 11); Qg_hi gen(:, 12); x0(idx.Qg) Qg_lo 0.2 * (Qg_hi - Qg_lo); end这里最反直觉的一点是按常规潮流计算经验电压初值设1.0肯定没问题但如果你一开始把某个节点电压精确设成上限1.06内点法起步就把电压上限约束激活了障碍项直接爆炸。所以初值宁可靠“中间”不要靠“边界”。6.3 坑三障碍参数衰减过快导致振荡我一开始天真地认为障碍参数(\mu)应该每次迭代缩小10倍结果在迭代到第5次左右时出现振荡目标函数上下跳。原因在于(\mu)衰减太快互补间隙(s^T z/n)还没来得及跟上去障碍项已经失去约束力迭代点跑到边界外侧对数函数计算浮点溢出。后来的解决方案是使用标准的中心化更新公式(\mu \sigma \cdot s^T z / n)并让(\sigma)保持在0.2而不是独立地缩小。这个公式的本质是让障碍参数与当前互补间隙联动两者的衰减速度匹配不容易失控。6.4 坑四松弛变量更新后出现负值有一次我发现迭代到后期出现某个松弛变量为-1e-8量级很小但符号是负的导致下一步对数障碍函数计算出错。原因是步长安全系数取成了0.6太小导致变量更新不足KKT残差长期不下降。解决办法是永远用边界限制计算最大允许步长而不是拍脑袋定一个0.9或0.99。即使理论上牛顿步长允许也要通过检查(\Delta s)中哪些分量是负数然后求出对应的最大步长。这个步骤在代码里不能省。步长计算的参考实现function [alpha_s, alpha_z] compute_step(s, z, ds, dz, safe) % 根据边界限制求解最大安全步长 % 分别对原始变量和对偶变量计算 alpha_s 1; idx_neg find(ds 0); if ~isempty(idx_neg) alpha_s min(alpha_s, min(-s(idx_neg) ./ ds(idx_neg))); end alpha_s min(alpha_s, 1); alpha_s alpha_s * safe; alpha_z 1; idx_neg find(dz 0); if ~isempty(idx_neg) alpha_z min(alpha_z, min(-z(idx_neg) ./ dz(idx_neg))); end alpha_z min(alpha_z, 1); alpha_z alpha_z * safe; end7. 从这个小系统走向大系统性能优化与后续扩展手里这套代码跑通IEEE 5节点之后我又在IEEE 30节点和IEEE 118节点系统上做了扩展测试。30节点系统迭代大约13次收敛118节点系统大约14次耗时都在几百毫秒到一两秒之间完全可接受。如果你的研究也需要处理更大规模的系统我会建议从这几个方向优化第一海森矩阵的稀疏组装。目前我的代码在每次迭代都重建海森矩阵和雅可比矩阵规模增大后这部分计算占大头。可以预分配稀疏矩阵结构只更新非零元素值速度能提升不少。第二采用Mehrotra预测-校正算法。标准原对偶内点法其实有一个著名的改进版叫Mehrotra算法它每次迭代多解一次方程做校正整体迭代次数能减少约30%。MATPOWER里面用的就是这种变体。如果想把代码升级成这个版本核心是在求出(\Delta x, \Delta y, \Delta s, \Delta z)之后额外用校正公式计算(\mu^2)项再解一次KKT系统更新的时候用校正后的方向。第三与求解器对比验证。在实际项目中如果代码与标准结果出现了无法解释的偏差我通常采用的就是双重验证策略一是用MATPOWER的结果做工程参考二是用数值梯度检验做数学参考。两者如果冲突大概率是我的代码有细节错误而不是工具不安全。这套思路也适用于你自己读到这里的实践——别盲目相信任何一个工具输出包括我这份代码。顺带提一嘴我的工程目录里还保留了一套profile对比数据5节点、30节点、118节点下手写内点法与MATPOWER的最优成本差异都在0.05%以内。这些数据放在论文附录里可以作为“算法实现有效性”的佐证。最后再分享一个实用小技巧如果某次迭代出现了不收敛但残差又小得离奇的情况检查一下是不是有约束被重复施加了。比如同一个上限既写进了母线电压约束又写进了发电机端电压约束对同一变量造成双重限制会导致对偶变量数值异常但原始变量看着正常。这种问题数值梯度检验依然能查出来只是解析梯度看起来完全正确要仔细核对拉格朗日乘子是否有明显不合理的大值或负值。电力系统的经典问题里最优潮流算是“既基础又深奥”的代表。它表面上是优化模型加数值求解实际上是物理约束、经济目标与算法技巧的交叉地带。真正动手写一遍内点法之后你对KKT条件、稀疏矩阵、步长策略的理解会和看教程完全不一样。希望这篇记录能帮你少走一点弯路。
阅读完成 · 觉得有帮助?
咨询建站