资讯动态

配电网运算组件设计与前推回代潮流计算实践

发布时间:2026/10/3 18:20:51 来源:尧图企业网站定制
简介基于Python的FPowerKit配电网运算组件设计源码面向配电网运维人员、电力系统自动化开发者及Python电力计算学习者可支撑运行分析、状态估计、故障处理等核心运算场景。压缩包共24个文件以15个Python源文件为主体完成潮流计算、分布式电源/储能建模、网络拓扑解析等功能辅以4个XML配置、2个Markdown文档及许可说明便于理解配置与二次开发。包体仅314KB结构紧凑适合快速集成或教学研究。目前已有78人学习该资源源码内含测试用例与数据文件可对照验证算法逻辑、复用组网计算模块为扩展智能配电网应用提供基础。1. 配电网运算组件到底在算什么先想清楚组件边界配电网运算组件说白了就是把一张真实的10kV馈线图变成一组能算的数据结构然后回答三个问题每个节点的电压是多少、每条支路的电流和线损是多少、如果接入分布式电源或合上联络开关会怎样。FPowerKit就是这样一套基于Python的配电网运算组件源码——它不是开着就能用的仿真软件而是一套把潮流计算、短路计算、线损分摊封装成类的可复用代码。对做配网规划、线损治理、分布式光伏接入评估的工程师来说最痛苦的不是算法不会写而是数据格式千奇百怪、拓扑校验没人管、计算内核换个项目就要重写。这套组件的价值恰恰是把“数据清洗—拓扑建模—数值计算—结果导出”这条链路固定下来。适合刚接触配电网计算的研究生也适合要在Python里跑批量场景的电力工程师。2. 把配电网装进Python对象FPowerKit的元件类与拓扑组装配电网计算程序和输电网计算程序最大的区别在建模粒度。输电网节点少、环网多、R/X比低可以用节点导纳矩阵一把梭配电网馈线动辄几十上百个节点主干、分支、分段开关、联络开关层层叠叠。我一般把组件拆成三层元件层负责描述“有什么”网络层负责描述“怎么连”运算层负责回答“算什么”。这层划分直接决定了后面前推回代算法能不能写清楚也决定了新增一个设备类型时改动范围有多大——加一个储能模型只动元件层运算层不用重写。2.1 元件模型节点、支路、负荷、分布式电源的类设计元件是整个组件的地基。节点是最基本的对象它需要同时承载拓扑属性和电气属性。用dataclass定义节点可以让属性一目了然也省掉一堆init模板代码。下面这段是我常用的一组定义# fpowerkit/core/element.py from dataclasses import dataclass from enum import Enum class BusType(Enum): SLACK 1 # 平衡节点变电站10kV母线进线 PQ 2 # PQ节点普通负荷、恒定功率负荷 PV 3 # PV节点分布式电源并网点电压幅值恒定 dataclass class Bus: idx: int # 节点全局编号导入后由组件重新编号从0连续 name: str # 站内名称例如10kV I段#03杆 voltage_kv: float # 额定线电压单位kV用于标幺值折算 bus_type: BusType BusType.PQ p_load_mw: float 0.0 # 有功负荷单位MW q_load_mvar: float 0.0 # 无功负荷单位MVar v_spec: float 1.0 # PV节点电压控制目标标幺值 dataclass class Branch: bus_from: int # 首端节点靠近电源方向 bus_to: int # 末端节点远离电源方向 r_ohm: float # 线路电阻单位欧姆 x_ohm: float # 线路电抗单位欧姆 b_siemens: float 0.0 # 对地电纳单位西门子 tap_ratio: float 1.0 # 变压器变比非标幺值如10.5/0.4kV取26.25 length_km: float 0.0 # 线路长度用于网损和可靠性统计这里有几个容易被新手忽略的设计决策。第一个是bus_type用枚举而不是字符串因为后面算法里要用if/else判断节点类型字符串比较容易写错且没有编译期校验。第二个是功率单位统一约定为MW/MVar而不是kW/kVar原因是配电网计算里所有公式都以MVA为基准功率节点注入电流为S/VS用MVar单位时量级最合适不会出现数值太小导致浮点精度问题。第三个是Branch的bus_from和bus_to方向写死在导入阶段——元件本身不校验方向但Network在add_branch时会校验这个放在下一节说。2.2 拓扑组装Network把元件串成馈线并校验连通性元件类解决“有什么”Network解决“怎么连”。配电网运算组件里Network除了维护bus和branch两个容器还要承担三项职责节点编号统一、支路方向校验、深度优先遍历。下面是最小可用的Network实现# fpowerkit/core/network.py from collections import defaultdict from fpowerkit.core.element import Bus, Branch, BusType class Network: def __init__(self, base_mva: float 100.0): self.base_mva base_mva # 基准功率潮流计算统一用 self.buses: dict[int, Bus] {} self.branches: list[Branch] [] self.adj_br: dict[int, list[Branch]] defaultdict(list) def add_node(self, bus: Bus) - None: if bus.idx in self.buses: raise ValueError(fbus id {bus.idx} already exists) self.buses[bus.idx] bus def add_branch(self, br: Branch) - None: if br.bus_from not in self.buses or br.bus_to not in self.buses: raise KeyError(branch endpoint bus is not added yet) # 方向校验不允许末端指向电源避免前推回代时电流方向混乱 if self._is_downstream(br.bus_to, br.bus_from): raise ValueError(branch direction is reversed, swap bus_from/bus_to) self.branches.append(br) self.adj_br[br.bus_from].append(br) def _is_downstream(self, target: int, start: int) - bool: 从start做DFS看能否到达target用于方向校验 stack list(start) visited set() while stack: cur stack.pop() if cur in visited: continue visited.add(cur) if cur target: return True for br in self.adj_br.get(cur, []): stack.append(br.bus_to) return False def dfs_branches(self) - list[Branch]: 先根遍历支路返回从电源到末端的顺序供前推回代使用 roots [b.idx for b in self.buses.values() if b.bus_type BusType.SLACK] if len(roots) ! 1: raise ValueError(network must have exactly one slack bus) root roots[0] order [] visited {root} stack [root] while stack: cur stack.pop() for br in self.adj_br.get(cur, []): if br.bus_to not in visited: visited.add(br.bus_to) order.append(br) stack.append(br.bus_to) return order这里有个取舍要说清楚_is_downstream每加一条支路就做一次DFS节点多时导入会变慢正确做法是先全部加载支路最后统一做一次辐射性检查和方向修正。我保留这个逐条校验是为了让错误尽早暴露——加载到第35条支路时告诉你方向反了远比算完潮流发现电压全乱再回头排查省时间。dfs_branches返回的支路顺序是电源先、末端后同一层的兄弟支路顺序不影响前推回代正确性。2.3 数据输入CSV/Excel导入时的字段与单位约定组件内部对象模型再清晰数据进不来也是白搭。配电网现场的台账往往是一张张Excel表格字段名五花八门A/B/C三相容量、线径、杆塔号、配变容量。我的经验是不要在组件里做字段名的“智能识别”而是用映射层把外部数据统一成内部模型字段。下面是CSV导入的骨架# fpowerkit/io/csv_loader.py import csv from fpowerkit.core.element import Bus, Branch, BusType from fpowerkit.core.network import Network def load_buses(csv_path: str, net: Network) - None: CSV列约定idx,name,voltage_kv,p_load_mw,q_load_mvar,type with open(csv_path, newline, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: bus_type BusType[row[type].strip().upper()] net.add_node(Bus( idxint(row[idx]), namerow[name].strip(), voltage_kvfloat(row[voltage_kv]), p_load_mwfloat(row.get(p_load_mw, 0.0)), q_load_mvarfloat(row.get(q_load_mvar, 0.0)), bus_typebus_type, v_specfloat(row.get(v_spec, 1.0)), )) def load_branches(csv_path: str, net: Network) - None: CSV列约定bus_from,bus_to,r_ohm,x_ohm,b_siemens,tap_ratio with open(csv_path, newline, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: net.add_branch(Branch( bus_fromint(row[bus_from]), bus_toint(row[bus_to]), r_ohmfloat(row[r_ohm]), x_ohmfloat(row[x_ohm]), b_siemensfloat(row.get(b_siemens, 0.0)), tap_ratiofloat(row.get(tap_ratio, 1.0)), ))字段映射有一个务实原则row.get()设置默认值而不是要求每个CSV都填满字段。实际台账里电纳、变比经常缺失对辐射网潮流影响不大缺省为0和1即可。而bus_from/bus_to/r_ohm/x_ohm四个字段缺了就不能算所以用row[...]直接取值缺了会抛KeyError让导入过程尽早失败。另外注意CSV读取用了utf-8-sig这是给Excel存UTF-8中文带BOM头准备的——不加这个编码第一行中文字段名会多出一个不可见字符整个表结构解析全错。这种细节属于谁踩谁知道的黑匣子。CSV字段内部属性单位必填idxBus.idx整数连续编号是nameBus.name文本否voltage_kvBus.voltage_kvkV是p_load_mw / q_load_mvarBus.p_load_mw / q_load_mvarMW / MVar否缺省0bus_from / bus_toBranch.bus_from / bus_to节点编号是r_ohm / x_ohmBranch.r_ohm / x_ohm欧姆是b_siemensBranch.b_siemens西门子否缺省0提示不要在组件里做数据格式的“智能识别”。Excel字段千奇百怪映射层统一转换是唯一可靠的做法识别不了就报错别猜。3. 前推回代潮流计算高R/X比辐射网的收敛算法配电网运算组件最核心的运算是潮流计算。没有潮流结果线损分析、无功优化、电压越限评估都无从谈起。这一章把前推回代算法的工程实现写透包括它为什么适合配电网、每一步在算什么、收敛参数怎么设。3.1 高R/X比下牛顿法为什么翻车配电网的支路参数和输电网有一个本质差别R/X比可以达到1到3甚至更高。输电网的电抗远大于电阻雅可比矩阵对角占优牛顿法迭代又稳又快配电网架空线和电缆的电阻占比大雅可比矩阵条件数差用牛顿法容易在迭代初期电压修正量过大直接发散。更麻烦的是配电网节点数多、辐射状连边稀疏牛顿法每次迭代都要重新计算和分解雅可比矩阵对几百个节点的馈线计算耗时也难看。所以配电网潮流计算的主流选择是前推回代法也叫支路电流法——它不组装导纳矩阵不迭代矩阵求逆而是顺着网络拓扑来回走两趟。这正好和上一章组件的层级设计对上dfs_branches的层序遍历结果就是前推回代的组织顺序。3.2 两步走回代求电流、前推更新电压前推回代的核心思想非常朴素假设已知各节点电压就能算每个节点的注入电流电流沿线路流动从末端拽到电源端就得到每条支路的电流再拿支路电流从电源端往末端推电压。如此往复直到前后两次电压差足够小。下面这段代码把完整迭代写在一个函数里是偏工程实现而不是教学伪代码# fpowerkit/analysis/powerflow.py import numpy as np from fpowerkit.core.network import Network from fpowerkit.core.element import BusType def z_base(voltage_kv: float, base_mva: float) - float: 标幺值基准阻抗Z_base V_base^2 / S_base单位欧姆 return voltage_kv * voltage_kv / base_mva def run_powerflow(net: Network, max_iter: int 50, tol: float 1e-6, v_init: float 1.0, omega: float 0.6, z_base_ohm: float 1.0): 前推回代潮流 z_base_ohm: 支路阻抗已折算到统一电压等级时对应的基准阻抗 10kV/100MVA 下 z_base 1.0 欧姆 n len(net.buses) idx_list sorted(net.buses.keys()) pos {idx: i for i, idx in enumerate(idx_list)} branches_dfs net.dfs_branches() # 电源 - 末端 branches_rev list(reversed(branches_dfs)) # 末端 - 电源 v np.full(n, v_init 0j) accum_i np.zeros(n, dtypecomplex) # 节点累计流出电流 for it in range(max_iter): v_old v.copy() accum_i[:] 0 # 1) 回代各节点注入电流 从末端向电源累加支路电流 for idx in idx_list: bus net.buses[idx] if bus.bus_type ! BusType.PQ: continue s_pu (bus.p_load_mw 1j * bus.q_load_mvar) / net.base_mva if abs(v[pos[idx]]) 1e-8: # 防止除零 accum_i[pos[idx]] np.conj(s_pu / v[pos[idx]]) for br in branches_rev: from_p pos[br.bus_from] to_p pos[br.bus_to] # 支路电流 末端节点累计流出的总电流 i_br accum_i[to_p] accum_i[from_p] i_br # 向上游累加 # 2) 前推从电源沿支路更新末端电压 for br in branches_dfs: from_p pos[br.bus_from] to_p pos[br.bus_to] z_pu (br.r_ohm 1j * br.x_ohm) / z_base_ohm v[to_p] v[from_p] - z_pu * accum_i[to_p] # 3) 阻尼收敛抑制高阻抗网络里的震荡 v omega * v (1 - omega) * v_old if np.max(np.abs(v - v_old)) tol: return v, it 1 return v, max_iter这段代码有三个关键点。第一回代顺序是逆支路序保证子支路电流先算完再算父支路第二accum_i这个数组全程复用一个不需要每轮迭代重新分配内存几百个节点时性能差别不大但上万节点的时间序列潮流就能感到差距第三阻尼系数omega的作用被很多人忽视——高R/X比网络里前推回代容易在末端电压上震荡取0.5到0.8能显著改善收敛性代价是迭代次数变多。3.3 收敛判据、迭代上限与松弛因子收敛判据用所有节点电压复数的最大绝对增量比只比较幅值更严格——相位的小幅漂移也会被捕捉到。tol取1e-6标幺值通常偏保守常规配电网规划分析用1e-4已经足够但如果要做分布式电源并网点的电压波动分析1e-6能避免迭代末期的微小振荡干扰结论。max_iter取50对典型3到50节点馈线是足够的到上限还不收敛先查拓扑方向而不是无脑加大迭代次数。参数典型值调参方向tol1e-6工程计算1e-4研究仿真1e-6max_iter50500个节点以上可放到80omega0.6震荡时降到0.4收敛慢时提到0.8v_init1.0从上一轮潮流结果热启动更快4. 从单相到三相不平衡配电网与分布式电源接入的扩展配电网和输电网另一个显著区别是三相不平衡。台区里单相负荷、两相负荷大量存在三相电流不对称电压也不再是三相对称的。如果组件的运算层只做单相模型算出来的线损和电压偏差在工程上完全不可信。这一章讲清楚怎么扩维、怎么处理分布式电源的电压控制、以及遇到弱环时怎么补救。4.1 三相模型负荷、线路、变压器怎么扩维三相不平衡计算有两种组织方式。一种是直接把每个物理节点拆成A/B/C三个子节点网络规模乘以3支路用3x3阻抗矩阵另一种是把三相不对称量分解成正序、负序、零序分量分别求解再合成。配电网组件里我推荐前者因为组件要处理的是实际开关状态和单相负荷序分量法在环网和不对称故障时反而绕。下面这段是三相支路的定义# fpowerkit/core/element_3p.py import numpy as np from dataclasses import dataclass dataclass class Branch3Phase: bus_from: int bus_to: int # 3x3复数阻抗矩阵[ABC]对角线为自阻抗非对角线为互阻抗 # 单位欧姆由Carson公式或线径参数表查得组件只接收不计算 z_abc: np.ndarray tap_ratio: float 1.0这里把“算阻抗矩阵”和“用阻抗矩阵”分开了。三相线路的互阻抗受大地回线、相间距、导线换位影响严格计算需要电磁场分析软件或Carson公式这不是配电网运算组件该干的活。组件只接收外部算好的z_abc负责把它放进前推回代公式里。如果哪一天你从线径表里拿到的是单位长度阻抗就在导入层乘上length_km不要让运算层知道“单位长度”这回事。4.2 PV节点处理DG的无功迭代与越限处理分布式电源并网点通常要求电压幅值在某个范围内逆变器用无功功率做恒压控制。在前推回代框架里PV节点不能像PQ节点那样直接代入恒定功率因为无功此时是未知数。常见做法是外层加一个无功迭代内层先按当前无功做潮流看电压是高了还是低了再调整无功注入反复几次直到电压落到目标附近。实现如下# fpowerkit/analysis/dg_pv.py from fpowerkit.core.element import BusType def run_powerflow_with_pv(net, dg_nodes: list[int], max_q_iter: int 15, q_step: float 0.02, q_limits: tuple[float, float] (0.0, 0.5)): 分布式电源恒压外环 q_step: 无功调节步长标幺值 q_limits: 逆变器无功吸收/注入限值正为吸收负为注入 pv_buses {idx: net.buses[idx] for idx in dg_nodes} idx_list sorted(net.buses.keys()) v None for _ in range(max_q_iter): v, _ run_powerflow(net) # 使用第3章的纯PQ潮流 converged True for idx, bus in pv_buses.items(): pos idx_list.index(idx) v_mag abs(v[pos]) q_bus bus.q_load_mvar # 电压低增发无功q_load_mvar变负表示注入 if v_mag bus.v_spec - 1e-4: q_bus - q_step # 电压高吸收无功 elif v_mag bus.v_spec 1e-4: q_bus q_step q_min, q_max q_limits if q_bus q_min or q_bus q_max: # 越限把无功固定在边界退出恒压控制转PQ节点 q_bus min(max(q_bus, q_min), q_max) bus.bus_type BusType.PQ converged False bus.q_load_mvar q_bus if converged: return v, _ return v, max_q_iter符号规则是这里最大的坑。前推回代里S P jQQ为正表示从电网吸收无功所以分布式电源“发无功”要把q_load_mvar写成负值。我见过不止一个新手把符号搞反结果电压越调越低外环直接发散。另外q_limits要按MVar而不是标幺值传组件内部会除以base_mva换算。4.3 弱环处理联络开关闭合后的补偿电流法辐射网是前推回代法的适用边界但配电网常常有联络开关故障转移或负荷转供时两个馈线末端连成一个弱环。此时直接前推回代会顺着环来回推支路电流累加逻辑失效。工程上最实用的解法是断环补偿法先在联络开关处把环断开按辐射网算一次记录联络点两端电压差然后在该处叠加一个补偿电流使两端等电位再迭代几次。补偿电流的初值用两端电压差除以联络开关两侧的戴维南等效阻抗估算。在FPowerKit的组件划分里这一步写在分析层网络层只负责提供联络开关的端点和状态——运算层不直接修改元件层而是通过一个补偿源数组注入。这样组件在弱环场景下依然保持“网络结构不变、运算方法切换”的边界。5. 配电网运算组件最容易翻车的五个细节与排查组件跑不起来是小问题跑起来结果不对是大问题。这里五条是我在不同项目里真实踩过的坑每一条都按“现象—原因—解决”写适合对照排查。5.1 现象导纳矩阵索引错位算出来的电压乱跳原因台账里的节点编号不是连续的比如现场杆号直接拿来当数组下标中间缺几个编号数组长度和节点数对不上稀疏矩阵组装时索引错位。解决组件内部禁止用台账号做数组下标导入时统一重新编号idx从0连续保留一个name字段存原始编号分支的bus_from/bus_to跟着映射走。不要图省事跳过重编号这一步否则后面加节点、删节点都会出诡异问题。5.2 现象线损偏大30%甚至100%单位没换算原因台账给的是kW和kVar组件内部按MW和MVar读取数值差1000倍线损自然差1000倍。这个问题最容易出现在“看起来跑了三天三夜终于能出结果”的时刻数据一填进去全盘皆错。解决在csv_loader里就统一除以1000不要在运算层做任何单位猜测字段命名上把单位写进名字比如p_load_kw从源头杜绝歧义。台账数据组件内部换算kWMW除以1000kVarMVar除以1000kVAMVA除以1000欧姆/km欧姆乘以长度km5.3 现象前推回代不收敛永远是最大迭代次数原因拓扑里某个支路的bus_from和bus_to挂反了回代时电流方向相反电压越算越大最终震荡发散。解决导入完成后做一次整体方向校验以平衡节点为根做DFS所有支路必须从父节点指向子节点发现反的支路自动交换两端而不是抛异常。注意交换后邻接表也要重建我见过只改支路不改邻接表导致下一轮DFS还是错的。5.4 现象电压整体偏低0.05pu变压器变比忘折算原因10kV/0.4kV配变低压侧线路阻抗没有归算到高压侧低压侧电压幅值偏差放大。解决前推回代经过变压器时低压侧支路阻抗先乘变比平方再统一用高压侧基准电压折算组件里z_base_ohm按支路所在电压等级分别计算不能全网用一个值。tap_ratio字段就是为这一步准备的。5.5 现象PV节点电压震荡无功越限没修原因DG无功外环迭代中q_bus不断累加但没检查上下限逆变器实际容量已经到顶电压控制失效外环继续盲目调于是震荡。解决每次迭代后检查q范围越限立即把bus_type转成PQ固定无功为边界值不再参与后续电压调节。这个检查不能放在内层潮流收敛之后统一做要放在每轮无功迭代里否则一两个节点越限拖垮整个外环收敛速度。注意前推回代“看起来简单”是错觉。它不组装雅可比矩阵但每一步都依赖正确的拓扑顺序和符号约定。出问题时先检查顺序再检查符号最后才看数值精度。6. 把组件复用起来时间序列潮流与批量场景的三个技巧潮流组件最常规的应用不是单次计算而是8760小时的时间序列潮流或者配电网规划里几百上千个负荷场景批量计算。直接循环调用run_powerflow会反复做拓扑遍历性能很难看。我一般用三个技巧把复用成本压下来。第一个技巧是拓扑一次解析、场景只改负荷数组。把run_powerflow拆成prepare和solve两步prepare做节点排序、支路顺序、位置映射返回一个上下文对象solve接收负荷数组只跑迭代。这样组件在批量场景里不再重复建对象几百节点上百场景的性能差别非常明显。第二个技巧是固定迭代顺序缓存。前推回代每轮都要按branches_dfs和branches_rev遍历这两个列表在prepare阶段就已经固定运行时不要每次重新从邻接表生成。第三个技巧是多进程并行每个worker持有只读的拓扑上下文from multiprocessing import Pool def solve_one(scenario): load_matrix, row scenario ctx prepare_ctx(net) # 每个worker各自prepare一次 set_load_ctx(ctx, load_matrix[row]) v, iters solve_ctx(ctx) return row, v, iters if __name__ __main__: scenarios [(load_matrix, i) for i in range(load_matrix.shape[0])] with Pool(4) as pool: results pool.map(solve_one, scenarios)这里有个容易忽略的细节Pool的worker进程会复制net但prepare_ctx里不能有全局可变状态否则多个场景之间互相污染。我习惯让set_load_ctx只修改ctx内部的负荷数组不碰net.buses上的p_load_mw这样并行时不需要加锁。最后无论怎么优化组件上线前一定要拿一个已知结果的算例做回测——IEEE 33节点或自己现场的历史馈线都行用结果差值和迭代次数验证组件行为不要相信第一次算出来的数。这是我在这类配电网运算组件上最深的教训算法写对只完成了一半把数据、单位、方向这三个环节管住才敢说组件真的能落地。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑