☰
电力系统可靠性评估代码包:序贯与非序贯蒙特卡洛仿真及指标计算实战
2026/10/9 21:29:56 网站建设 项目流程

简介:这份资源面向电气工程专业学生、电力系统运维人员及可靠性分析初学者,围绕电气代码086展开,系统梳理可靠性评估的核心知识体系,帮助读者理解如何在设计、制造、运行与维护各阶段保障电气系统达到预期性能标准。压缩包共16个文件,以12个m源码文件为主,辅以2个txt说明、1个md文档和1个license授权文件,整体约16KB,体量轻便,便于快速查阅与二次开发。内容涵盖可靠性定义与MTBF、MTBR、故障率等衡量指标,并延伸至故障树分析、事件树分析、FMEA、寿命数据分析等常用方法,同时涉及冗余设计、状态监测、预测性维护及基于IEC、IEEE、GB标准的风险评估思路。目前已有144人学习下载,适合希望建立可靠性评估框架、对照代码理解分析流程并用于课程设计或工程实践的读者参考。

1. 电气可靠性评估代码包:从蒙特卡洛到工程落地的第一道门槛

电力系统可靠性评估这件事,很多做电气仿真的同行都有体会:理论公式背得滚瓜烂熟,真到要算一个含几十个元件的系统,手算根本不现实。这个「电气代码:086 可靠性评估.zip」就是冲着这个痛点来的——它把序贯蒙特卡洛、非序贯蒙特卡洛两套仿真流程,连同可靠性指标计算、算例数据、说明文档打包在一起,解压就能跑。包里能看到PowerSystemsReliabilityAssessment-main主目录、Montecarlo_nsq_single(非序贯单次仿真)、Montecarlo_seq(序贯仿真)两个核心脚本目录,还有README.md、LICENSE和几份中文说明 txt。适合谁?做电力系统规划、发输电可靠性分析的研究生和工程师,尤其是需要拿一套能改、能扩、能出图的代码当毕设或项目底子的人。它不解决"可靠性理论是什么",它解决"指标怎么算出来、代码怎么跑通"。

2. 序贯与非序贯蒙特卡洛:两套仿真流程的选型逻辑

2.1 两种方法到底差在哪

可靠性评估里最核心的仿真手段就是蒙特卡洛。这个包里同时给了Montecarlo_seq和Montecarlo_nsq_single两套,不是凑数,是因为它们适用的场景完全不同。

非序贯蒙特卡洛(nsq)把系统状态当作一个个独立的随机抽样:每个元件按强迫停运率(FOR)抽"运行/停运",组合成一个系统状态,判断这个状态下负荷能不能被满足,统计缺电概率。它不关心时间先后,抽 N 次统计 N 次,实现简单、收敛快,适合算概率类指标——比如 LOLP(缺电概率)、EPNS(期望缺供电量)。

序贯蒙特卡洛(seq)则要模拟时间轴:给每个元件抽一个"无故障运行时间"(TTF)和一个"修复时间"(TTR),按时间顺序推进,记录系统在每个时刻的状态。它能算出频率类、持续时间类指标——比如 LOLF(缺电频率)、LOLE(缺电时间期望),还能反映负荷的时序特性和检修安排。

选型判断很简单:只要你的指标里出现"次/年""小时/次"这类带时间维度的量,就必须用序贯;如果只关心"一年缺多少电"这种累积量,非序贯够用且快得多。

2.2 非序贯仿真的核心代码拆解

先看非序贯这条线。典型实现逻辑是:读入元件可靠性参数表 → 对每个元件生成 [0,1] 均匀随机数 → 与 FOR 比较判定状态 → 组装系统状态 → 潮流/连通性校验 → 累计失负荷指标。

import numpy as np # 元件强迫停运率表,每行一个元件 FOR = np.array([0.02, 0.03, 0.01, 0.05]) # 强迫停运率 N = 100000 # 抽样次数,越大越稳 # 生成 N 次抽样下每个元件的状态矩阵 # rand < FOR 判定为停运(1),否则运行(0) states = (np.random.rand(N, len(FOR)) < FOR).astype(int) # 逐次判断系统是否失负荷(此处用简化判据:任一关键元件停运即失负荷) # 实际项目里这里要接潮流计算或最小割集判断 failure = states.any(axis=1) LOLP = failure.mean() # 缺电概率 print(f"LOLP = {LOLP:.6f}")

这段代码的关键在三个参数:FOR数组必须和你的元件清单一一对应,顺序错了结果全废;N是抽样次数,非序贯的收敛速度大致按 1/√N 走,10 万次通常能把 LOLP 的方差压到可接受范围,但元件数一多、失负荷事件一稀疏,就得往上加;failure的判据是整段代码的灵魂——上面用的是"任一关键元件停运即失负荷"的简化版,真实系统里必须换成潮流越限判断或网络连通性分析,否则算出来的 LOLP 会严重偏大。

2.3 序贯仿真的时间推进机制

序贯这条线复杂在时间管理。核心是维护一个"下一个事件时刻"的优先队列,每次弹出最早发生的事件,更新系统状态,再给受影响的元件重新抽样 TTF/TTR。

import numpy as np import heapq # 元件参数:MTTF(平均无故障时间,小时), MTTR(平均修复时间,小时) MTTF = np.array([5000, 8000, 3000]) MTTR = np.array([50, 80, 30]) T_total = 8760 # 仿真一年,小时 # 初始化:每个元件抽首个故障时刻 ttf = np.random.exponential(MTTF) ttr = np.random.exponential(MTTR) events = [(ttf[i], i, 'fail') for i in range(len(MTTF))] heapq.heapify(events) up_time = 0.0 # 累计失负荷时间 last_t = 0.0 state = np.zeros(len(MTTF), dtype=int) # 0运行 1停运 while events: t, idx, etype = heapq.heappop(events) if t > T_total: break # 推进到当前事件时刻,若系统处于失负荷状态则累计时长 if state.any(): up_time += t - last_t last_t = t if etype == 'fail': state[idx] = 1 heapq.heappush(events, (t + np.random.exponential(MTTR[idx]), idx, 'repair')) else: state[idx] = 0 heapq.heappush(events, (t + np.random.exponential(MTTF[idx]), idx, 'fail')) LOLE = up_time / (T_total / 8760) # 折算到年 print(f"LOLE = {LOLE:.4f} 小时/年")

这里有几个容易翻车的点。np.random.exponential的参数是均值,不是率参数,别把 λ 直接塞进去;heapq里存的是元组,比较时会先比时间再比索引,索引必须唯一否则会报错;state.any()判断的是"当前是否有元件停运",同样需要替换成真实的系统失负荷判据。序贯仿真的收敛比非序贯慢,通常要跑到几万年的仿真时长才能让 LOLE 稳定,所以实际跑的时候别只仿真一年就下结论。

2.4 两套流程的收敛判据与停止条件

非序贯看的是指标方差系数 β:当 β = σ/(均值·√N) 小于设定阈值(工程上常取 0.01~0.05)时停止。序贯则看仿真年数,一般要求至少覆盖 1000 年以上的等效运行时间,或者指标的年际波动小于 2%。包里Montecarlo_nsq_single是单次仿真入口,意味着你需要自己写外层循环做多次独立重复,再统计均值和方差——这一点在README.md里应该有说明,跑之前先确认清楚,别拿单次结果当最终值。

3. 可靠性指标计算:从原始仿真输出到 MTBF、LOLE 的换算

3.1 指标定义与代码里的对应关系

仿真跑完只是拿到一堆状态序列,真正要交出去的是指标。这个包里涉及的指标体系和摘要描述里列的一致:MTBF(平均无故障时间)、MTTR(平均修复时间)、λ(故障率)、LOLP、LOLE、EPNS。它们之间的换算关系必须搞清楚,否则代码输出的数字你都不知道对不对。

指标含义单位从仿真结果的计算方式
MTBF平均无故障时间小时总运行时间 / 故障次数
MTTR平均修复时间小时总修复时间 / 故障次数
λ故障率次/年故障次数 / 总运行年数
LOLP缺电概率无量纲失负荷状态数 / 总状态数
LOLE缺电时间期望小时/年失负荷总时长 / 仿真年数
EPNS期望缺供电量MWh/年Σ(失负荷量 × 持续时间) / 仿真年数

3.2 从状态序列反算指标的代码

序贯仿真输出的是事件序列,需要后处理才能得到上表的指标。下面这段是典型的后处理逻辑:

def compute_indices(event_log, T_total): """ event_log: [(时刻, 元件索引, 事件类型), ...] T_total: 总仿真时长(小时) """ fail_count = sum(1 for e in event_log if e[2] == 'fail') repair_time = 0.0 last_fail_t = {} for t, idx, etype in event_log: if etype == 'fail': last_fail_t[idx] = t elif etype == 'repair' and idx in last_fail_t: repair_time += t - last_fail_t[idx] years = T_total / 8760 MTBF = T_total / fail_count if fail_count else float('inf') MTTR = repair_time / fail_count if fail_count else 0 lam = fail_count / years return {'MTBF': MTBF, 'MTTR': MTTR, 'lambda': lam} # 调用示例 # result = compute_indices(event_log, 8760 * 1000) # print(result)

参数说明:event_log必须按时间排序,否则last_fail_t的配对会错乱;T_total是总仿真时长,不是仿真年数,换算时注意单位;fail_count为 0 时 MTBF 返回无穷大,这是数学上的正确处理,但报告里要注明"仿真期内未出现故障",不能直接写 inf。

3.3 指标置信区间的估计

单点估计不够,工程报告里通常要给置信区间。非序贯的 LOLP 服从二项分布,标准误是 √(p(1-p)/N);序贯的 LOLE 需要把仿真分成若干段,算段间方差。包里没有现成的置信区间函数,需要自己补。常见做法是把 N 次独立重复的结果收集起来,用 t 分布或正态近似给出 95% 置信区间。这一步不做,评审时大概率被追问"你这个 LOLE 的误差范围是多少",答不上来就很被动。

4. 避坑与排查:跑这套代码最容易翻车的五个地方

4.1 随机数种子没固定,结果每次都不一样

现象:同一套参数跑两遍,LOLP 差了百分之十几。原因:np.random.rand和np.random.exponential默认用系统时间做种子,每次启动都是新序列。解决:在仿真入口加np.random.seed(42),并在报告里注明种子值。做对比实验时,不同方案必须用同一种子,否则差异里混了随机噪声,结论不可信。

4.2 元件参数单位不统一,MTTF 写成天但代码按小时算

现象:算出来的 MTBF 比预期小 24 倍。原因:参数表里 MTTF 用的是"天",代码里np.random.exponential(MTTF)默认按小时理解,量纲对不上。解决:在数据读入层做一次强制单位检查,所有时间参数统一转成小时再进仿真。我一般会在读表后加一行断言:assert MTTF.max() > 100, "MTTF 疑似单位错误",因为正常的电力元件 MTTF 不会低于 100 小时。

4.3 失负荷判据过于简化,指标虚高

现象:LOLP 算出来 0.3 以上,明显不合理。原因:判据写成了"任一元件停运即失负荷",但实际系统有冗余,单台设备退出不一定导致负荷损失。解决:把判据替换成直流潮流越限判断或最小割集分析。如果只是做方法验证,至少要用"关键元件集合"代替"全部元件",并在文档里写清楚简化假设。

4.4 序贯仿真事件队列溢出或死循环

现象:程序跑着跑着内存暴涨,或者卡在某一年不动。原因:修复事件和故障事件的时间戳相同,heapq比较时陷入无限推入;或者 TTR 抽样出极小值,导致事件密度爆炸。解决:给事件元组加一个自增序号作为第二比较键,保证唯一性;对 TTF/TTR 抽样值设下限,比如max(sample, 0.1),避免零间隔事件。

4.5 收敛判据设得太松,结果没稳定就停了

现象:仿真跑了 1000 次就停,指标和跑 10 万次差很多。原因:停止条件写成了固定次数,而不是方差系数。解决:改成动态判据——每 1000 次抽样算一次 β,β < 0.02 才停,同时设一个最大次数上限(比如 50 万)防止死循环。序贯那边同理,用年际指标波动代替固定年数。

5. 进阶用法:把单次仿真改造成批量实验框架

这套代码给的是单次仿真入口,但真实项目里你要做的是参数扫描——比如改变某条线路的 FOR,看 LOLE 怎么变;或者对比不同冗余方案下的 EPNS。手动改参数跑一遍记一遍结果,效率太低,而且容易记错。

我的做法是在外层包一个批量实验脚本,把待扫描的参数做成配置列表,循环调用仿真函数,结果直接落成 CSV。下面是一个可复用的框架:

import itertools import pandas as pd from your_sim_module import run_sequential_sim # 替换成包里的实际入口 # 定义扫描空间 param_grid = { 'line1_FOR': [0.01, 0.02, 0.05], 'line2_FOR': [0.01, 0.03], 'redundancy': [0, 1], # 0无冗余 1有冗余 } keys = list(param_grid.keys()) results = [] for combo in itertools.product(*param_grid.values()): params = dict(zip(keys, combo)) np.random.seed(42) # 固定种子保证可比性 indices = run_sequential_sim(params, years=2000) indices.update(params) results.append(indices) df = pd.DataFrame(results) df.to_csv('sensitivity_results.csv', index=False) print(df.groupby('redundancy')['LOLE'].mean())

这个框架的关键在于:种子固定,保证不同参数组合之间的差异只来自参数本身;仿真年数统一,避免"有的组合跑 1000 年有的跑 5000 年"这种不可比的情况;结果直接落表,后续画灵敏度曲线或者做 ANOVA 都方便。

验证方法上,我习惯做两个检查。一是极限验证:把某个元件的 FOR 设成 0,LOLE 应该降到接近 0;设成 1,系统应该持续失负荷。如果这两个极端情况不对,说明判据或状态更新逻辑有 bug。二是收敛验证:同一组参数跑三次不同种子,指标差异应该在置信区间内,如果三次结果散得很开,说明抽样次数不够或者方差估计有问题。

从那以后我每次拿到这类可靠性仿真代码,都强制先跑一遍极限验证再动业务参数——这个习惯帮我省过好几次返工。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询