
这几年做配电网仿真我几乎每年都要碰一次同一个问题怎么把“碳排放”从发电侧精确落到每一个用户、每一条母线上。给园区做光伏接入方案时甲方最喜欢问的一句话是“光伏装在哪条线路上对整片区域的减碳贡献最大”。如果只看全网平均排放因子你会觉得所有节点都一样——这显然不对因为不同线路末端买到的电来源和损耗都不一样。后来我把问题整理成一个标准的配电网碳流分析问题拿 IEEE33节点这个经典算例做节点碳势计算再用 Python 把结果画成拓扑热力图。这篇文章就把完整过程记录下来包括碳排放流的原理、IEEE33节点系统的数据组织、碳势计算的核心递推公式、可复现的代码以及我在实际调试里踩过的几个坑。做低碳配电网、碳排放流追踪、分布式电源接入分析的朋友可以直接拿这套流程当模板用。1. 为什么算“节点碳势”而不是直接用全网平均排放因子1.1 平均排放因子解决不了的问题先看一个很现实的问题。一个大区电网里同时有煤电、气电、光伏、风电不同时段、不同区域的外来电比例完全不同。电网公司发布年度平均排放因子比如常规做法是取全国或区域电网的均值然后所有用户按这个值乘以用电量算碳排放。这个结果在宏观层面成立但放到具体配电网里就很粗糙靠近某个燃煤电厂区域的用户实际消纳的电力中煤电占比高单位电量碳排放理应更高光伏、风电接入点下游的用户只要本地新能源出力足够用电的“含碳浓度”就会显著更低线路较长、负载较重时网损会让单位有效用电分摊到更多碳排末端用户的有效电碳排放强度会悄悄升高。这些空间差异平均排放因子完全体现不出来。要做精细化碳排放核算就得把碳排放在电网里“追着功率流走”这就是碳排放流分析要做的事。1.2 碳排放流与节点碳势的关系碳排放流的思路和经典潮流计算高度类似。有功功率在网络里从发电端流向负荷端我们可以把碳排放想象成一种“附着在电功率上的有色流体”每兆瓦时电携带多少吨二氧化碳这个浓度跟着功率一起在支路上传输经过潮流分岔、汇合后重新混合最终每个节点都有自己特有的“含碳浓度”。节点碳势英文常叫 Nodal Carbon Intensity定义就是该节点所有注入功率对应的加权平均碳排放强度单位常用 tCO2/MWh 或 kgCO2/kWh。它回答的问题只有一个此时此刻从这个节点取 1 度电等效产生多少克二氧化碳。这个概念可以类比成水管里的盐水浓度。不同水源以不同盐度注入同一个水箱水箱出口浓度由各注入流量加权平均决定而不是简单把所有水源浓度加起来。电网节点就是那个水箱上级电网、光伏、储能是不同盐度的注入源负荷就是取水口。节点碳势就是水箱出口的“盐度”支路碳流密度就是支路管道里的“盐度”负荷碳流率则是用户单位时间内实际“喝掉”的二氧化碳总量。2. IEEE33节点系统数据、拓扑与为什么选它做碳势验证2.1 系统结构与典型参数IEEE33节点系统是配电网领域最经典的辐射状标准算例之一由 33 个节点、32 条支路组成根节点通过上级变电站供电系统基准电压 12.66 kV。我习惯取基准功率 Sbase 10 MVA全部负荷有功约 3.715 MW无功约 2.3 Mvar。拓扑结构很有代表性一条较长的主干链节点 1-2-3-…-18节点 2 引出一条分支19-22节点 3 引出一条分支23-25节点 6 再引出一条长分支26-33。这种“主干多分支”的结构非常接近实际的城市中压配电网而且所有线路都是纯辐射状没有环网潮流方向天然确定——这对碳势追踪来说特别舒服。支路数据就是经典的电阻电抗表我用的数据如下形式# 支路: [首端节点, 末端节点, R(Ω), X(Ω)] branches [ [1, 2, 0.0922, 0.0470], [2, 3, 0.4930, 0.2511], [3, 4, 0.3660, 0.1864], [4, 5, 0.3811, 0.1941], [5, 6, 0.8190, 0.7070], [6, 7, 0.1872, 0.6188], # ... 一共32条 ]负荷数据每个节点一组有功无功比如节点 24 负荷是 420 kW j200 kvar是整个系统里最大的负荷点之一。2.2 为什么标准算例适合碳流研究我不是第一次做碳排放相关仿真试过自己在随机拓扑上搭模型但最终还是会切回 IEEE33节点。原因很简单标准算例的价值在于“结果可对照”。潮流结果是公开可验证的。IEEE33节点在不接入分布式电源时的潮流计算结果、网损数值在很多文献里都能查到我可以方便地确认自己的前推回代算法没写错拓扑规模适中。33 个节点不算大手写数据、单机跑脚本完全够用但又有足够的分支结构去观察碳势的空间差异扩展场景容易。在任意节点挂一个光伏、储能或负荷突变就能对照分析碳排放影响不需要重新搭系统辐射状无环。碳流追踪算法在辐射网上可以顺序前推求解逻辑直观不容易出矩阵病态问题。所以做节点碳势计算的第一个模型我强烈建议用 IEEE33节点它就像算法验证的“标准试验田”。3. 节点碳势的数学模型把碳排放变成网络中的“流量”3.1 从潮流到碳流三个核心概念碳势计算需要三类数据支路首端有功功率、支路末端有功功率、以及发电节点的碳排放强度。支路首端功率是上一级节点流出到该支路的功率支路末端功率是经过线路损耗后实际到达下一个节点的功率两者之差就是线损。围绕碳排放定义三个量节点碳势 e_j节点 j 处单位有功功率对应的碳排放强度支路碳流率 R_ij单位时间内流经支路 i-j 的二氧化碳量等于上游节点碳势乘以支路首端有功功率负荷碳流率 R_load_j负荷实际消耗的碳排放速率等于节点碳势乘以负荷有功功率。关键假设是比例共享原则当多个注入源同时向一个节点供电时潮流在网络会合后充分混合下游支路和负荷无法区分电来自哪个电源因此流出功率的碳浓度统一取该节点碳势。3.2 节点碳势递推公式与网损的影响对任意节点 j假设上游支路集合为 incoming(j)该节点本地接入的发电功率为 P_g对应的发电碳势为 e_g则节点碳势为e_j (Σ e_i × P_ij_head e_g × P_gj) / P_j_in其中 P_ij_head 是支路 i-j 首端有功功率P_j_in 是节点 j 的总注入有功功率。注意分子用的碳流率要跟随支路首端功率而分母的注入功率必须是支路末端到达节点的功率——这个区别来自线损。为什么末端节点碳势往往比根节点高因为线损是线路电阻消耗的功率这部分功率对应的碳排放在线路传输过程中已经“散失”了但它没有转化为到达下游节点的有效电量。于是同样的碳排放量被分摊到更小的有效电量上单位电量的含碳浓度自然上升。线路越长、负载越重网损越大末端碳势抬升越明显。如果节点接入零碳电源比如光伏分母加入了光伏注入功率而分子没有增加碳排放节点碳势被显著稀释。这就是空间尺度上“绿电”集中接入带来的减碳效果。3.3 求解顺序与一般网络的矩阵推广对于辐射状配电网如果节点编号满足“上游节点编号永远小于下游节点编号”这一性质碳势可以按节点编号从小到大直接顺序求解。IEEE33节点系统恰好满足这个条件所有支路的首端编号都小于末端编号。因此不需要求解线性方程组直接从根节点出发一路前推到所有末端节点即可。如果遇到环网或者节点编号乱序的配网顺序求解就不成立了。这时需要把每个节点的递推关系组装成线性方程组(D - A) × E B其中 D 是节点注入功率对角阵A 是支路潮流邻接矩阵B 是发电节点碳排放流率向量。解这个稀疏线性方程组就能得到所有节点的碳势。这就是把碳势计算从“顺序追踪”推广到“矩阵计算”的通用思路。4. Python实现前推回代潮流与碳势递推4.1 数据定义与单位制我习惯把潮流和碳势都放在标幺制下计算最后换算成有名值展示。基准功率 Sbase 10 MVA基准电压 Vbase 12.66 kV阻抗基准 Zbase Vbase² / Sbase。负荷数据以 kW/kvar 给出除以 Sbase×1000 换算成标幺。碳势计算的单位处理容易出错我统一这么约定功率标幺值乘以 Sbase 得到有名功率 MW节点碳势单位取 tCO2/MWh碳排放流率就是两者相乘得到 tCO2/h。4.2 前推回代潮流辐射状配网最常用的潮流算法是前推回代法。原理很简单回代过程从末端节点开始每个节点的负荷加上所有下游支路首端功率再加上本支路线损得到本支路首端的注入功率逐层往上游推前推过程从根节点开始由支路功率和首端电压计算支路电流和电压降落向后更新每个下游节点的电压反复迭代直到各节点电压幅值变化小于阈值。核心代码如下import numpy as np def power_flow(branches, loads, Sbase10.0, Vbase12.66, max_iter200, tol1e-6): n 33 m len(branches) Zbase Vbase ** 2 / Sbase R_pu np.array([b[2] for b in branches]) / Zbase X_pu np.array([b[3] for b in branches]) / Zbase P_load np.zeros(n 1) Q_load np.zeros(n 1) for nd, (p, q) in loads.items(): P_load[nd] p / (Sbase * 1000) Q_load[nd] q / (Sbase * 1000) # start_branches[i] 所有以 i 为首端的支路索引 start_branches [[] for _ in range(n 1)] for k, b in enumerate(branches): start_branches[b[0]].append(k) V np.ones(n 1, dtypecomplex) for it in range(max_iter): P_br np.zeros(m) # 支路首端有功 Q_br np.zeros(m) # 支路首端无功 P_end np.zeros(m) # 支路末端有功到达注入功率 Q_end np.zeros(m) # 回代从末端到首端 for k in range(m - 1, -1, -1): j branches[k][1] P_sum P_load[j] Q_sum Q_load[j] for k2 in start_branches[j]: P_sum P_br[k2] Q_sum Q_br[k2] P_end[k] P_sum Q_end[k] Q_sum Vj2 abs(V[j]) ** 2 Ploss R_pu[k] * (P_sum ** 2 Q_sum ** 2) / Vj2 Qloss X_pu[k] * (P_sum ** 2 Q_sum ** 2) / Vj2 P_br[k] P_sum Ploss Q_br[k] Q_sum Qloss # 前推从首端到末端更新电压 V_new V.copy() for k in range(m): i branches[k][0] j branches[k][1] # 支路电流用末端流出功率和末端电压计算 I np.conj((P_end[k] 1j * Q_end[k]) / V[j]) V_new[j] V[i] - (R_pu[k] 1j * X_pu[k]) * I diff np.max(np.abs(V_new - V)) V V_new if diff tol: break return V, P_br, Q_br, P_end这套实现里有个关键细节回代求支路首端功率时损耗的计算用的是“末端下游功率 P_sum”除以末端电压 V[j] 来算电流而不是用首端功率。这样损耗计算更接近实际物理过程收敛性也更稳。4.3 碳势递推与结果对比潮流收敛后P_br 是支路首端功率P_end 是支路末端注入功率。碳势计算就很简单了def carbon_intensity(branches, P_br, P_end, Sbase10.0, e_grid0.581, dgNone): n 33 e np.zeros(n 1) e[1] e_grid # 根节点碳势取上级电网平均排放因子 # 组装每个节点的注入功率 P_in np.zeros(n 1) for k, b in enumerate(branches): j b[1] P_in[j] P_end[k] C_in np.zeros(n 1) # 节点碳流率 for k, b in enumerate(branches): i, j b[0], b[1] C_in[j] e[i] * P_br[k] * Sbase # 加入分布式电源默认光伏碳势为0 if dg: for nd, Pg in dg.items(): P_in[nd] Pg / (Sbase * 1000) # kW - MW - pu C_in[nd] 0.0 # 光伏/风电零碳 # 按节点编号从小到大顺序求解 for j in range(2, n 1): if P_in[j] 0: e[j] C_in[j] / (P_in[j] * Sbase) return e我对根节点碳势取 0.581 tCO2/MWh代表区域电网平均排放因子。这个值你可以根据研究场景自由替换。在我本地的运行结果里无分布式电源时根节点碳势 0.581主干末端节点比如节点 18、22、25碳势会升到 0.5850.60 左右升幅不大但趋势稳定越靠末端、负载越重的节点碳势越高。原因就是网损累积末端每度电分摊了更多本来被线路损耗掉的碳排放。如果在节点 18 接入 600 kW 光伏碳排放强度为 0节点 18 及其下游节点的碳势会明显下降节点 18 附近可能从 0.585 降到 0.55 以下。这就是节点碳势最有价值的应用——量化评估分布式电源对局部用电碳强度的改善而不是拍脑袋说“光伏装哪都一样”。5. 可视化一张图看懂全网的碳势分布5.1 可视化设计思路节点碳势算完之后只看数字也能分析但好的可视化能让结论直观得多。我的设计思路是节点颜色映射碳势大小颜色越深代表碳势越高节点大小映射负荷功率或注入功率功率越大节点画得越大支路颜色或粗细映射支路碳流率线越粗代表单位时间流过的碳排放越多加上色条一眼就能看出全网哪个区域“含碳浓度”最高。5.2 拓扑坐标与绘制代码IEEE33节点拓扑可以画成“一条横向主干 三条斜分支”我用手工坐标方式构建位置字典这样比 random layout 稳定也不像 spring layout 每次跑都会变。import matplotlib.pyplot as plt import networkx as nx def build_pos(): pos {} # 主干 1-18 横向排列 for k, nd in enumerate(range(1, 19)): pos[nd] (k * 0.6, 0.0) # 分支 2-19-20-21-22向上延伸 branch2 [19, 20, 21, 22] bx 0.3 for nd in branch2: pos[nd] (bx, 1.0) bx 0.5 # 分支 3-23-24-25向下延伸 branch3 [23, 24, 25] bx 0.7 for nd in branch3: pos[nd] (bx, -1.0) bx 0.6 # 分支 6-26-27-...-33下斜延伸 branch6 [26, 27, 28, 29, 30, 31, 32, 33] bx 2.4 for k, nd in enumerate(branch6): pos[nd] (bx, -1.5) bx 0.55 return pos def draw_carbon(e, branches, P_br, Sbase10.0): G nx.DiGraph() for b in branches: G.add_edge(b[0], b[1]) pos build_pos() fig, ax plt.subplots(figsize(12, 6)) node_colors [e[nd] for nd in G.nodes()] nodes nx.draw_networkx_nodes( G, pos, node_colornode_colors, cmapplt.cm.coolwarm, node_size400, axax ) # 支路宽度映射碳流率 flow [e[b[0]] * P_br[k] * Sbase for k, b in enumerate(branches)] max_flow max(flow) widths [1.0 f / max_flow * 5 for f in flow] nx.draw_networkx_edges(G, pos, widthwidths, edge_colorgray, arrowsTrue, axax) nx.draw_networkx_labels(G, pos, font_size8, axax) plt.colorbar(nodes, axax, label节点碳势 (tCO2/MWh)) plt.axis(off) return fig5.3 基态与接入光伏后的碳势图解读基态时整张图上节点颜色基本是“从根节点往外逐渐加深”但变化幅度不大视觉上呈现出一个暖色调的辐射网。如果把节点 18 接入 600 kW 光伏再看图节点 18 附近会出现一个明显的“冷色区域”而且下游节点 17-18 段以及该分支更远节点的颜色都比基态浅很多。这种可视化的价值在于不用看密密麻麻的表格直接就能定位碳势最高的瓶颈区域和零碳电源影响范围。后续如果做时序碳势分析把同一张图按时间轴依次渲染成动画还能看出白天光伏大发与夜间纯网购电时全网碳势空间分布如何动态变化。6. 实操中的坑与下一步可以玩的方向6.1 我在实操时踩过的几个坑先说最容易翻车的单位换算。IEEE33节点数据里负荷单位是 kW/kvar支路阻抗单位是欧姆基准功率选 10 MVA 还是 1 MVA 直接改变标幺数值。我一开始基准功率选 1 MVA负荷标幺值全部除以 1000结果网损比例对不上折腾了很久才发现是基准选择不一致。建议固定 Sbase10 MVA所有功率换算写清楚注释。第二个坑是前推回代的支路顺序。我的代码直接利用了“IEEE33支路末端编号都大于首端编号”这个性质回代时从 m-1 到 0 扫描即可。但如果你把标准数据里的支路顺序打乱或者换成其他配网拓扑这个顺序就不再成立会直接算出错误结果。通用做法是先做拓扑排序或者按节点深度从深到浅排序不要依赖原始数据的排列。第三个坑是线损在碳势计算里的处理。很多人会直接用支路首端功率 P_br 计算节点注入功率这会导致分母被高估节点碳势偏低。正确做法是用支路末端功率 P_end 作为节点注入而碳流率分子用支路首端功率 P_br 乘以碳势。这个“一用首端一用末端”的不对称不是笔误它恰恰反映了“碳被线损消耗了一部分”的物理事实。还有一个容易忽略的点根节点不需要从递推公式算碳势它直接取边界条件。如果上级电网的排放因子是 0.581根节点的碳势就赋 0.581。很多初学者会给根节点也套公式结果分母里没有上游支路注入直接除以零或者得到一个奇怪的值。6.2 从节点碳势还能做什么完成基态节点碳势计算只是第一步这个模型可以扩展的方向非常多。时序碳势接入光伏、负荷的 24 小时曲线逐时段计算碳势可以得到每个节点的日内碳势波动曲线。储能就可以根据“节点碳势高低”来决定充电还是放电——碳势低时充电碳势高时放电实现真正意义上的减碳调度碳排放责任分摊节点碳势结合负荷功率可以把碳排放精确分摊到每个用户为园区内部碳核算、绿电交易提供按节点级别的数据基础多能源系统扩展在配电网模型上再接上天然气网、热网用同样的思路计算气碳势、热碳势就能做综合能源系统的碳流分析环网与三相不平衡场景把顺序递推改成矩阵求解把单相潮流扩展成三相潮流再配合三相碳势定义就能处理更复杂的实际馈线。我个人最推荐先做“光伏节点选址”这个扩展把光伏依次放到不同节点对比全网平均碳势的变化。你会发现同样的装机容量放在长线路末端和放在变电站附近的减碳效果完全不同。这是因为末端节点网损大、碳势高零碳电源对高碳势节点的“稀释”作用更明显。这个结论放在实际规划里可以直接指导逆变器接入点的选择。最后分享一个小技巧调试碳势程序时先跑一个“所有节点碳势强制设为 1”的对照组——如果全网无损耗且无 DG所有节点碳势应该全部等于根节点碳势。如果程序结果里末端碳势比根节点低说明注入功率或碳流率的方向处理错了。用这种简单对照组验证逻辑比直接对着真实数据排查快得多。