接到一个配电网N-1扩展规划的需求,第一反应往往是:这不就是把每条线依次断开算个潮流吗?真动手做才发现,问题远不止校核那么轻巧。中压配电网规划里,N-1准则指的是任一馈线、主变压器或开关设备退出运行后,系统仍能通过网架重构与负荷转供,保证全部或重要负荷供电,而扩展规划则是在这个前提下确定新增线路的位置、型号和投产时序。这类问题一旦进入Matlab建模,就同时撞上0-1整数规划、多场景约束、辐射状拓扑校验、求解器选型等一堆硬骨头。这篇文章把我从理论理解、数学模型搭建到代码调试的完整路径记录下来,写给配网规划工程师、电力系统方向研究生,以及所有准备用Matlab做网架优化的人。我会尽量把每个决策背后的理由讲清楚,而不是只甩一段能跑但看不懂的程序。
1. 配电网N-1扩展规划到底在研究什么:概念与应用场景
1.1 N-1准则的本质:配电网的“单点故障不掉链子”底线
传统认知里,N-1准则似乎更多属于输电网。输电网强调网架坚强,任意一条线路或一台主变跳闸后,系统还能稳定运行,负荷不损失。配电网长期采用“闭环设计、开环运行”,大多以单电源辐射状网络轻载运行,天然缺少转供通道,以前对N-1的校核并不严格。但随着城市核心区供电可靠性要求不断升高,以及大量分布式电源、充电桩接入带来新的负荷形态,配电网的N-1能力已经成为不少电网公司可研评审的硬性门槛。
需要注意的是,配电网的N-1和输电网的N-1在工程语义上并不完全一样。输电网关心暂态稳定、断面潮流转移和电压稳定,而配电网更关心开断一个元件后,被甩开的负荷能否通过另一条路径重新带上电。这意味着规划阶段就要回答三个问题:断开的元件是哪一个?转供路径存在吗?转供路径上有没有容量余量?很多新建和改造方案图纸看着很漂亮,一到N-1校验就暴露出联络线容量不足、末端线路载流量卡脖子、分段开关位置不合理等问题,根源就是规划阶段没有把这些“故障状态”一并放进优化模型里。
1.2 传统网架规划与N-1扩展规划的本质差异
传统配电网规划一般只考虑正常运行方式下的单一场景,用负荷预测结果去推导线截面和线路路径,目标是满足各节点负荷需求、降低网损、控制短路电流水平。这种做法在负荷增长平缓、供电面积不大的年代没有问题,但放到今天,负荷密度高、供电走廊紧张、新能源反向送电频繁,单一场景的优化结果往往经不起故障态的推敲。
N-1扩展规划的核心差异在于,它把“正常运行状态”和“每一个元件退出运行后的故障状态”作为一个联合优化整体。正常运行状态决定网络的基础接线,故障状态约束反过来限制基础接线。最典型的情况是:正常态下两条馈线各自带自己的负荷,看起来干净利落,但如果其中一条馈线检修,另一条馈线并没有足够的备用容量把负荷全部转带,那么方案就不能通过N-1校核。于是规划模型必须在正常状态基础上,为每一条馈线预留给对侧的联络通道,要么新建一条联络线,要么加大某一段线路截面。
两类规划问题的对比可以这样看:
| 对比维度 | 传统网架规划 | N-1扩展规划 |
|---|---|---|
| 考虑场景数 | 通常1个正常态 | 正常态+多个故障态 |
| 决策变量 | 线路新建/改造与否 | 线路新建/改造+故障态转供状态 |
| 约束规模 | 节点平衡、载流量、电压 | 节点平衡、载流量、电压、辐射状、故障连通性 |
| 求解难度 | 小规模时可用启发式 | 大规模时必须用MILP或分解算法 |
| 产出结果 | 一组新建线路 | 新建线路+故障态转供路径方案 |
1.3 谁需要这套方法和代码:工程师、研究生还是算法爱好者
从我这几年接触的情况看,需要N-1扩展规划Matlab代码的人群大致分三类。第一类是设计院和地市供电公司的规划专责,他们通常面对的是具体工程:某个开发区要新增负荷,现有10kV线路重载,需要确定新建线路和联络开关位置,让方案通过评审。这类人群最需要的是能直接改数据、快速出图、导出N-1校核表格的工具,而不是复杂的算法论文复现。
第二类是高校电力系统方向的研究生。他们研究配电网规划、网架重构、可靠性提升,经常需要把一个数学模型落地成可运行的代码,用于验证论文中的方法。这类人群更关心模型扩展性,比如加储能、加分布式电源、改成多目标优化,因此代码结构要清晰,变量和约束条例分明。
第三类是刚接触Matlab优化建模、想用YALMIP或intlinprog解决实际问题的开发者。他们可能从输电网项目转过来,对配电网运行方式不太熟悉,容易把辐射状运行约束、N-1开断逻辑搞错。下面从数学模型开始,一步步把整个问题打开。
2. 数学模型怎么建:目标函数、约束条件与N-1场景枚举
2.1 目标函数:投资成本、运行费用与可靠性代价的权衡
N-1扩展规划的目标函数,最简单的形式是年费用最小化,把一次性投资折算成等年值,再加上年网损费用。写出来大致是:
目标 = 新建线路投资的等年值 + 年网损费用 + 惩罚项
投资等年值通常用资金回收系数γ计算,γ = r(1+r)^n / ((1+r)^n - 1),其中r为贴现率,n为设备使用年限。比如贴现率取8%,线路使用期按20年算,γ大约为0.10185,意味着投资100万元等价于每年支出约10.185万元。这个折算很有必要,否则年份不同的投资方案没有可比口径,评审时也容易产生争议。
网损费用的计算采用简化年损耗小时数法,即单位电价乘以最大负荷损耗小时数τ,再乘以规划年的峰值网损功率。规划模型一般用线性化潮流近似,所以网损本身也做了线性化处理,只保留线路电流平方项的一阶近似或直接忽略。若想让结果更贴近实际,可以在优化结束后用交流潮流对方案做一次复校,把误差控制在规划阶段可接受的范围内。
惩罚项通常对应两类情况:一类是N-1校核不通过时,需要对应切除的负荷价值,用单位缺电成本表示;另一类是目标函数里硬性约束无法满足时,用松弛变量让模型先“可解”,再从松弛量的数值判断哪条约束卡得最紧。在实际代码里,缺电成本往往设置得远高于线路投资成本,比如按每千瓦时几十元甚至上百元计,这样才能保证优化结果优先满足N-1,而不是优先省钱。
2.2 正常运行工况约束:线性化潮流与节点功率平衡
配电网规划阶段,节点功率平衡约束是基础中的基础。典型写法是:对每个节点,所有流入该节点的线路功率和该节点电源注入之和,等于该节点负荷。线路功率受到容量上限约束,同时满足允许电压偏移范围。为了能交给整数规划求解器处理,通常采用线性化的DistFlow模型或者直流潮流模型。雖然直流潮流在输电网更常见,但在配电网规划里作为“初步选线”工具也被广泛使用,原因是中压配电网线路较短、压降主要受线路电阻影响,纯直流模型会忽略无功和电压幅值,因此更严谨的做法是用线性DistFlow。
线性DistFlow的核心是把支路功率方程简化为:支路首端流出的功率等于末端节点负荷与下游支路功率之和,再对每条支路添加容量限制,并对节点电压偏移添加近似的线性约束。这样做避免了潮流迭代和三角函数项,让整个模型变成一个标准的混合整数线性规划。学过电力系统分析的人会问,这样一来电压精确吗?答案是不精确,但规划阶段看的是“网架结构是否具备N-1转供能力”,电压校核完全可以放到后续的潮流复核环节去完成,否则模型维数会失控。
2.3 辐射状约束:单商品流还是生成树
配电网正常运行时必须保持辐射状,这既是继电保护配合的需要,也是限制短路电流的手段。在N-1扩展规划里,每个故障场景下的临时转供网络也必须保持辐射状,否则合环运行会产生保护误动风险。辐射状约束的建模方式有几种,我推荐的是单商品流法。
单商品流法的思路很直观:把网络想象成一个物流系统,根节点向每个非根节点配送1单位虚拟商品,每个节点需求恰好为1单位,那么任何连通而且不含环的网络都能满足流量平衡。若网络中出现了环,必然存在某个环内商品流无法同时满足平衡条件,从而被约束排除。实现时,给每条线路设一个虚拟流变量,方向可正可负,容量约束用一个大M值协调线路状态。根节点的虚拟注入为节点总数减1,其余节点虚拟注入为-1。对每个运行状态(包括正常态和每个N-1场景),独立建立一组虚拟流约束。
这种方法的优点是表达简单、求解器识别友好,缺点是会产生大量辅助变量。每增加一个场景,就增加线缆数倍的连续变量和约束,当候选线路很多时,模型规模会迅速膨胀。另一种生成树约束(cutset等式)变量更少,但需要枚举节点子集,在代码实现上麻烦不少,大规模工程中很少从零手写。因此,单商品流是平衡了易实现性和求解性能的折中选择。
2.4 N-1场景枚举:把“故障态”也写进优化模型
N-1校验对象可以是馈线段、主变、母线,甚至是一组接线单元。扩展规划阶段最常用的是对已有线路和候选线路逐条开断。对于每个开断元件k,建立一个场景s_k,场景中强制该元件退出运行,然后要求其余已有线路和候选线路可以通过状态变量重新组合出一个辐射状网络,满足故障态负荷供电要求。
这里有个容易混淆的点:正常态下选定的网架是“基础网架”,但故障态下为了实现转供,模型允许临时断开原本接通的线路,也允许临时接通原本未建的候选线路吗?答案取决于工程设定。通常在规划模型中,候选线路的建设状态是全局决策变量,场景s中能否使用它,由两个变量共同决定:建设状态x(全局不变)和场景投入状态y_s(运行决策)。只有建设了,场景中才可能投入。而已有线路的建设状态恒为1,但在场景中可以因故障或转供需求而断开。这样处理既符合实际,又自然生成了转供方案。
N-1场景约束和正常运行约束之间,通过全局建设变量紧密耦合。正常态决定哪些线路存在,故障态决定这些线路怎么用。缺少任何一个状态,模型就会偏向过于乐观或不切实际的方案。这也解释了为什么N-1扩展规划模型规模比普通规划大一个数量级:正常态一组约束、每个N-1场景一组约束,还要加上连接两层变量的大M约束和辅助变量。
3. Matlab代码实现要点:数据结构、YALMIP建模与求解流程
3.1 输入数据结构怎么设计:节点、线路、负荷的定义方式
写Matlab代码之前,先把数据格式定下来,这一步决定了后面所有代码的简洁度。我习惯用两个结构体数组存数据:node和line。
node结构体每个元素对应一个节点,包含节点编号、坐标、有功负荷、无功负荷,以及节点类型。负载数据可以是规划年的峰值负荷,也可以是逐年负荷序列。若做的是单阶段规划,直接用峰值负荷即可。node(1)通常是上级电源节点,电压等级高,作为整个网络的平衡节点和单商品流的根节点。
line结构体每个元素对应一条线路,包含首端节点、末端节点、线路电阻电抗、载流量上限、当前是否存在、候选建设造价。线路数据一般来自GIS台账或现场勘测,使用Excel维护比较方便,Matlab里用readtable批量读取,然后转换为结构体或矩阵。
下面是一个简化的数据结构示意:
| 字段名 | 含义 | 数据类型 |
|---|---|---|
| node.id | 节点编号 | double |
| node.pLoad | 节点有功负荷 | double |
| node.qLoad | 节点无功负荷 | double |
| line.from | 首端节点编号 | double |
| line.to | 末端节点编号 | double |
| line.r | 线路电阻 | double |
| line.x | 线路电抗 | double |
| line.capacity | 载流量上限 | double |
| line.exist | 是否已有线路 | logical |
| line.cost | 候选线路造价 | double |
保存数据时,建议把已有线路和候选线路放在同一个line数组里,用exist字段区分。这样在写约束时,仅需判断isExist即可决定该线路的建设状态是否固定为1,无需额外维护两套线路索引。这个设计能减少很多烦人的下标错位问题。
3.2 为什么用YALMIP而不是手写intlinprog
Matlab自带的intlinprog可以求解混合整数线性规划,理论上足够处理N-1扩展规划。但实际写起来非常痛苦:所有约束要手动拼成A*x<=b的标准形式,场景一多,矩阵维度稍有变化就得重写全盘。我强烈建议用YALMIP。YALMIP是一个建模层,可以让你用符号变量和约束列表直接描述优化问题,底层统一调度CPLEX、Gurobi或intlinprog求解器。
YALMIP安装很简单:下载压缩包解压后,在Matlab里把文件夹加入路径,运行savespath即可。求解器方面,如果只有教育版Matlab,可以先用intlinprog兜底;如果手上有CPLEX或Gurobi授权,则优先选择后者,性能差别在候选线路超过30条时会非常明显。
YALMIP的优点不只是语法简洁,更重要的是它天然支持约束的循环追加。N-1场景约束是“同一模式重复很多次”的结构,用YALMIP可以很方便地通过for循环累积到同一个Constraints变量里。而手写intlinprog时,每个场景都要重新定位矩阵的行号、列号,排查bug的时间会淹没建模时间。
3.3 核心代码框架:变量定义、约束循环与求解调用
先给出主程序框架。注意下面的代码只体现核心结构,完整数据读取和参数设置请参考你自己的工程文件。
% 配电网N-1扩展规划主程序框架 % 使用YALMIP建模,求解器建议:gurobi / cplex / intlinprog loadData(); % 载入node和line数据,并在工作区生成node,line变量 nNode = length(node); nLine = length(line); existFlag = [line.exist]'; candIdx = find(~existFlag); % 候选线路索引 nScenario = nLine; % 每条线路开断对应一个N-1场景 % 变量定义:建设状态x为nLine维0-1变量,正常态线路状态为0-1变量 x = binvar(nLine,1); % 1表示线路建成投运 y0 = binvar(nLine,1); % 正常态线路投入状态 P0 = sdpvar(nLine,1); % 正常态线路有功功率 f0 = sdpvar(nLine,1); % 正常态虚拟流 Constraints = []; % 正常态约束 Constraints = [Constraints, y0(existFlag == 1) == 1]; % 已有线路正常运行 Constraints = [Constraints, y0(candIdx) == x(candIdx)];% 候选线路只有建成才能投入 Constraints = [Constraints, P0 <= lineCap .* y0]; % 容量约束 Constraints = [Constraints, ... 节点功率平衡 ...]; % 自行补充 Constraints = [Constraints, ... 单商品流辐射状约束 ...];进入N-1场景循环前,定义每个场景下的状态变量。为了清晰,我采用元胞数组存储场景变量:
yS = cell(nScenario,1); PS = cell(nScenario,1); fS = cell(nScenario,1); for s = 1:nScenario yS{s} = binvar(nLine,1); PS{s} = sdpvar(nLine,1); fS{s} = sdpvar(nLine,1); % 场景s中,被开断的线路强制退出 Constraints = [Constraints, yS{s}(s) == 0]; % 未开断的已有线路允许运行,也可断开展开转供 otherExist = find(existFlag == 1); otherExist(otherExist == s) = []; Constraints = [Constraints, yS{s}(otherExist) <= 1]; % 候选线路仍然受建设状态约束 Constraints = [Constraints, yS{s}(candIdx) <= x(candIdx)]; % 容量与功率平衡约束 Constraints = [Constraints, PS{s} <= lineCap .* yS{s}]; Constraints = [Constraints, ... 场景s节点功率平衡 ...]; Constraints = [Constraints, ... 场景s辐射状约束 ...]; end需要注意的是,容量约束写成PS <= lineCap .* yS,其中的乘法是逐元素乘法,YALMIP会将其转化为大M约束。如果lineCap是0,则对应线路功率必须为0,这与线路退出状态一致。对于被开断的线路,由于yS(s)==0,功率自动被限制为0,不需要额外判断。
目标函数和求解调用比较简单:
% 目标:等年值投资 + 网损 investCost = sum(x(candIdx) .* lineCost(candIdx)); energyLoss = C_loss * tau * sum( P0 .^ 2 .* lineR ./ lineV .^ 2); Objective = gamma * investCost + energyLoss; % 求解设置,gap设小一点有助于提高最优性 ops = sdpsettings('solver','gurobi','verbose',2,'gurobi.MIPGap',0.0001); sol = optimize(Constraints, Objective, ops); % 输出结果 xOpt = value(x); y0Opt = value(y0);这里有三个细节值得强调。第一,P0.^2是二次项,会导致模型变成MIQP而非MILP,如果没有二次规划求解器,就把网损项做分段线性化或忽略,规划阶段问题不大。第二,大M约束里的M值选取很关键,若取得过大,求解器数值稳定性会变差;若取得过小,可能错误限制线路功率。通常取该线路容量上限的1.1倍比较合适。第三,输出求解结果前,务必检查sol.info是0还是非0。
3.4 求解器配置与运行流程:从建模到出结果的一次完整跑通
求解配置的实操顺序是:先确认Matlab工作目录正确,再加载YALMIP;调用ops时把solver指定为实际可用的求解器,不确定时可以用[]让YALMIP自动选择。若运行提示找不到求解器,多半是YALMIP路径没设好或求解器未加入Matlab路径。
跑通一个小算例的完整流程大概是:加载数据 -> 定义变量 -> 构建正常态约束 -> 循环构建N-1场景约束 -> 设定目标函数 -> 优化求解 -> 提取并保存结果。建议一开始不要追求完美目标函数,先用最小投资作为目标,跑通后再加入网损项,这样排查问题时有清晰的起点。
4. 算例验证:6节点系统从数据到规划结果的全过程
4.1 一个可以直接抄的6节点算例数据
为了验证代码逻辑,我设计了一个6节点中压配电网算例。节点1是上级电源点,节点2到节点6为负荷节点,所有线路电压等级10kV。已有线路三条:1-2、2-3、1-4。候选线路五条:2-4、4-6、3-5、5-6、2-5。数据如下。
节点负荷数据:
| 节点 | 类型 | 有功负荷(kW) | 无功负荷(kVar) |
|---|---|---|---|
| 1 | 电源 | 0 | 0 |
| 2 | 负荷 | 800 | 300 |
| 3 | 负荷 | 600 | 250 |
| 4 | 负荷 | 1000 | 350 |
| 5 | 负荷 | 700 | 250 |
| 6 | 负荷 | 900 | 300 |
线路数据可自行按工程习惯设定。这里给一组参考值:线路阻抗按0.2+0.3j欧姆/km计算,已有线路长度取2km,候选线路长度在1.5km到3km之间;载流量上限设为5MW,候选线路造价按长度折算,每公里约80万元。这个小算例的妙处在于,节点3和节点6相对孤立,一旦1-2或1-4中的某一条检修,必须立即找到另一条路径,否则必然损失负荷。
4.2 规划结果对比:基础规划与N-1扩展规划究竟差在哪里
先做基础规划:目标为最小投资,约束只有正常态功率平衡和辐射状,不加入N-1场景。这种情况下优化结果会倾向于只建设最便宜的线路把5个负荷节点联通即可,典型的解可能是建设2-4和4-6,让全部负荷在正常态下由节点1经1-4转供。这样投资很低,但故障态非常脆弱:断开1-4后,节点4、6直接失电,断开1-2后节点2、3失电。
再加入N-1场景后,模型不得不为每一个故障场景寻找可行的转供路径。以断开已有线路1-4为例,为保证节点4、5、6供电,至少需要从节点2或者节点3侧引入通道。于是可能的结果是新建2-5和4-6,形成1-2-5-4-6的通路,节点3则由2-3供电。对应地,断开3-5场景时,节点5和6还可以通过4-6由节点4转供。这样一套方案投资明显高于基础规划,但每条线路故障后都不会有负荷被甩开。
对比结果可以用表格直观呈现:
| 方案类型 | 新建线路组合 | 等年值投资(万元/年) | 能否通过全部N-1校验 |
|---|---|---|---|
| 基础规划 | 2-4、4-6 | 较低 | 否,多个故障场景失负荷 |
| N-1扩展规划 | 2-4、2-5、4-6 | 较高 | 是,所有开断场景均满足供电 |
从实际工程角度看,N-1方案多出的投资,本质上是在购买“每一段线路故障时的备用通道和备用容量”。评审会上如果只对比投资额,N-1方案肯定吃亏;但结合每个方案的供电可靠性指标和缺电损失期望,情况往往反过来。规划人员需要处理的就是这样一个“多花钱买一张安全网”的决策问题。
4.3 从6节点扩展到33节点:数据准备和结果判读的注意点
6节点只是验证代码逻辑,真正看书、写论文可能要用IEEE 33节点这种典型算例。幸运的是,代码不需要改逻辑,只需要替换数据文件。IEEE 33节点系统的节点坐标、负荷、支路参数在相关文献中很容易查到,导入时留意首端电源节点编号、负荷单位是kW还是MW、线路容量是否需要折算,这三点是换数据时最常出错的地方。
跑完33节点模型,先看求解器退出信息,再检查各场景的转供路径,最后看新建线路集合和投资费用。有一个容易被忽略的点:33节点中有联络开关构成的闭环,在规划模型里要小心处理。如果把这些联络开关当作普通线路,则正常态辐射状约束会自动要求它们断开,N-1场景中它们可以作为转供通道投入,这与实际运行逻辑完全一致。
5. 调试教训:求解慢、无解、辐射状失效这些坑怎么填
5.1 模型报“无解”时,先检查是不是N-1场景自身矛盾
新手最崩溃的是N-1约束一加上去,模型立刻无解。我的排查经验是先手动分析一个最简单的N-1场景:断开某条已有线路S之后,剩余网络在跑通优化之前,是否还连着一个根节点?如果被开断线路把某个负荷块变成了孤岛,而这个孤岛内没有候选线路能够连接回电源,那么任何求解器都救不了你,必须修正输入数据。
把模型简化到只保留这一个场景,去掉其他场景约束,用线性规划求解器解一下,往往几秒钟就能暴露出问题所在。如果单个场景可行,再逐步增加其他场景,二分定位到是哪一个场景引发了不可行。配合YALMIP的check指令逐条检查约束余量,效率会高很多。
5.2 辐射状约束失效:环网和孤岛同时存在的经典错误
辐射状约束出错时,结果网络会出现环甚至孤岛。单商品流法出错最常见的原因是根节点序号设错,或者虚拟流约束里方向符号写反。另一个原因是候选线路建设状态x和场景投入状态y_s之间的大M约束遗漏了:正常态下线路没有建设,但场景里却因为y_s为1而强行投入,虚拟流在这里绕过约束,导致孤岛也能形成连通伪像。
调试这类问题,建议把优化后的y0和yS矩阵打印出来,逐个场景数一数投入线路的数量。正常态如果是6节点,投运线路必须是5条,否则一定不满足生成树条件。故障场景中投运线路数必须小于等于5条,且节点之间连通。直观的人工检查往往比看求解器日志更快。
5.3 求解时间爆炸:整数变量多、场景多时的降维手段
N-1扩展规划模型对规模和求解时间极其敏感。每条候选线路增加若干个0-1变量,每个N-1场景又复制一遍状态变量,30条候选线路配上30个场景,整数变量轻轻松松上千。如果求解器长时间无法收敛,可以从三个方向优化。
第一,预筛选候选线路。在进入MILP之前,用一个连通性算法或最大流检验排除掉那些在建与不建对N-1毫无帮助的线路,通常能减少大量无意义变量。第二,设置合理的MIP gap。工程规划不需要证明全局最优,0.5%的gap足够满足评审要求,盲目追求gap=0会跑很久。第三,用Benders分解思想,把N-1场景约束从主问题中释放,逐步添加被违反的场景,也就是“延迟约束生成法”。这个思路我在实际项目里试过,效果显著,但写代码前要对YALMIP和求解器接口足够熟悉。
5.4 YALMIP与求解器联调的常见报错和处理办法
YALMIP最常见的两个报错是“No suitable solver for this problem”和“Solver not found”。前者说明模型里存在求解器不支持的约束类型,比如二次项、绝对值、逻辑与约束,需要检查目标函数和约束中是否混入了非线性项。后者通常是路径问题,安装好CPLEX或Gurobi后没有把它们的接口文件夹加入Matlab路径。
还有一个非常隐蔽的错误:变量定义时用了sdpvar,但求值时误用value单变量对应,而实际上变量是矩阵或向量。YALMIP里size不一致时不会立即报错,而是在value输出后通过plot或表格检查时才发现结果完全错位。每次求解结束后,建议立即打印value(x)和value(y0)的维度,防患于未然。
我个人调试这段代码时最大的体会是:N-1扩展规划的问题本身不难理解,难的是把“故障场景”和“正常场景”两个层次的变量关系梳理清楚。如果一开始就把正常态和故障态的状态变量混用,后面所有求解结果都会被认为是“不可行”,然后你会怀疑模型、怀疑求解器、怀疑自己,结果只是那个大M的取值少乘了一个0。建议在写代码时,让每个场景的变量都独立命名,约束分块注释,跑通后再考虑优化合并。后续如果你想把储能容量配置、分布式电源选址定容、甚至是多目标可靠性优化加进来,这套N-1扩展规划代码完全可以作为底座,只是约束和决策变量的数量会再上一个台阶,届时优先考虑分解算法而不必执著于单层MILP。