电网拓扑一等公民
EnerOS 将电网拓扑作为操作系统内核的一等公民数据结构,而非应用层的数据文件。这意味着 NetworkGraph、Bus、Branch 等抽象在内核态即被原生支持,Agent、调度器、保护逻辑、数字孪生等所有上层组件都共享同一份电网拓扑视图,避免重复建模与一致性同步开销。
「拓扑一等公民」是 Power-Native First 设计哲学的直接落地:电网的物理结构本身就是操作系统世界观的一部分,就像 Linux 内核理解文件系统与进程树一样,EnerOS 内核理解母线、支路、电气岛与连通性。
设计动机
传统电力软件将电网拓扑建模为应用层的 JSON / XML / CIM 文件,每次潮流计算、状态估计、保护整定都需要重新加载并解析。这带来三个问题:
- 重复建模:调度、运检、保护各维护一份拓扑,状态漂移严重
- 事件失效:拓扑变更无法实时通知所有订阅者,决策基于过期视图
- 性能浪费:每次解析与构建拓扑图占用 10-100ms,无法支撑实时应用
EnerOS 通过将拓扑下沉到内核态,所有组件通过系统调用访问同一份拓扑对象,变更事件由内核广播,单次拓扑分析延迟降至 1ms 以下。
架构总览
┌─────────────────────────────────────────────────┐
│ 应用层:DispatchAgent / ProtectionAgent / Twin │
└───────────────────┬─────────────────────────────┘
│ 系统调用 get_network / subscribe
┌───────────────────┴─────────────────────────────┐
│ eneros-topology (内核原生拓扑引擎) │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ NetworkGraph│ │ TopologyOps │ │ EventBus │ │
│ └─────────────┘ └─────────────┘ └───────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ Bus / Branch│ │ IslandIndex │ │ VersionLog│ │
│ └─────────────┘ └─────────────┘ └───────────┘ │
└─────────────────────────────────────────────────┘
核心抽象
NetworkGraph
NetworkGraph 是 EnerOS 中所有电网拓扑操作的入口,采用基于 petgraph 的有向图结构,并附加电力系统专用的索引与元数据。
use eneros_topology::{NetworkGraph, Bus, Branch, BusType, BranchParams, Voltage};
let mut network = NetworkGraph::new();
// 添加母线
network.add_bus(Bus::new(1, BusType::PQ, Voltage::new(1.0, 0.0)));
network.add_bus(Bus::new(2, BusType::PV, Voltage::new(1.02, 0.0)));
network.add_bus(Bus::new(3, BusType::Slack, Voltage::new(1.05, 0.0)));
// 添加支路
network.add_branch(Branch::new(1, 2, BranchParams::line(0.1, 0.2, 0.02)));
network.add_branch(Branch::new(2, 3, BranchParams::transformer(0.05, 0.1, 1.0)));
Bus 字段说明
| 字段 | 类型 | 含义 | 默认值 |
|---|---|---|---|
| id | u32 | 母线编号,全局唯一 | 必填 |
| bus_type | BusType | PQ / PV / Slack / Isolated | PQ |
| voltage | Voltage | 电压幅值与相角(p.u.) | (0.0, 0.0) |
| area | u16 | 所属区域编号 | 0 |
| zone | u16 | 所属分区编号 | 0 |
| base_kv | f64 | 基准电压(kV) | 110.0 |
| v_max | f64 | 最高运行电压(p.u.) | 1.10 |
| v_min | f64 | 最低运行电压(p.u.) | 0.90 |
| name | String | 可读名称 | "" |
Branch 字段说明
| 字段 | 类型 | 含义 | 默认值 |
|---|---|---|---|
| from | u32 | 起始母线 ID | 必填 |
| to | u32 | 终止母线 ID | 必填 |
| params | BranchParams | 电阻、电抗、对地导纳、变比 | 必填 |
| status | BranchStatus | Closed / Open / Grounded | Closed |
| rating_mva | f64 | 额定容量(MVA) | 100.0 |
| emergency_mva | f64 | 15 分钟紧急容量 | rating_mva * 1.2 |
| length_km | f64 | 线路长度(km) | 0.0 |
BranchParams 构造器
use eneros_topology::BranchParams;
// 传输线 Π 型参数:(r, x, b/2)
let line = BranchParams::line(0.1, 0.2, 0.02);
// 变压器参数:(r, x, 变比)
let xfmr = BranchParams::transformer(0.05, 0.1, 1.0);
// 三绕组变压器(折算到高压侧)
let three_winding = BranchParams::three_winding(
(0.01, 0.15, 1.0),
(0.01, 0.10, 1.0),
(0.01, 0.05, 1.0),
);
// 分布参数线路(Bergeron 模型)
let distributed = BranchParams::distributed(
0.025, // r (Ω/km)
0.3, // x (Ω/km)
0.015, // b (μS/km)
150.0, // length (km)
);
拓扑分析
内核内置常见拓扑分析算法,全部基于一次遍历完成,无需应用层重复实现。
let topology = network.topology();
// 电气岛检测
let islands = topology.find_islands();
// 连通性检查
let connected = topology.is_connected(bus1, bus2);
// 最短路径(按跳数)
let path = topology.shortest_path(bus1, bus2)?;
// 最短路径(按阻抗)
let path_z = topology.shortest_path_by_impedance(bus1, bus2)?;
// 邻居节点
let neighbors = topology.get_neighbors(bus_id);
// 上游电源
let sources = topology.upstream_sources(bus_id);
// 下游负荷
let loads = topology.downstream_loads(bus_id);
// 供电范围
let scope = topology.supply_scope(bus_id);
TopologyOps API 一览
| 方法 | 入参 | 出参 | 时间复杂度 |
|---|---|---|---|
| find_islands | - | Vec | O(V+E) |
| is_connected | bus1, bus2 | bool | O(V+E) |
| shortest_path | from, to | Vec | O((V+E) log V) |
| get_neighbors | bus_id | Vec | O(deg) |
| upstream_sources | bus_id | Vec | O(V+E) |
| downstream_loads | bus_id | Vec | O(V+E) |
| supply_scope | bus_id | Scope | O(V+E) |
| find_loops | - | Vec<Vec | O(V+E) |
| cut_set | bus_id | Vec | O(V+E) |
| radial_check | - | RadialReport | O(V+E) |
拓扑变更与事件
任何拓扑变更都会触发内核事件总线广播,订阅者通过 subscribe 接收:
use eneros_topology::event::{TopologyChanged, BranchStatusChanged, BusAdded};
// 订阅拓扑变更
ctx.subscribe::<TopologyChanged>().await?;
ctx.subscribe::<BranchStatusChanged>().await?;
// 增量更新拓扑
network.update_branch(1, 2, |b| b.status = BranchStatus::Open);
// 内核自动完成:
// 1. 失效潮流缓存
// 2. 重新计算电气岛索引
// 3. 推送 BranchStatusChanged 事件
// 4. 推送 TopologyChanged 事件
// 5. 触发约束重算
// 6. 通知数字孪生同步镜像
事件类型
| 事件 | 触发时机 | 携带数据 |
|---|---|---|
| BusAdded | 新增母线 | bus_id, voltage |
| BusRemoved | 删除母线 | bus_id |
| BranchStatusChanged | 支路开合 | branch_id, old, new |
| TopologyChanged | 任意拓扑变更 | 变更摘要、affected_buses |
| IslandFormed | 形成新电气岛 | island_id, buses |
| IslandMerged | 电气岛合并 | island_a, island_b |
事件订阅完整示例
use eneros_topology::event::TopologyChanged;
impl Agent for TopologyListenerAgent {
async fn run(&mut self, ctx: &mut AgentContext) -> AgentResult<()> {
let mut rx = ctx.subscribe::<TopologyChanged>().await?;
while let Some(event) = rx.recv().await {
println!("拓扑变更: 影响 {} 条母线", event.affected_buses.len());
self.recompute_dispatch(ctx).await?;
}
Ok(())
}
}
与 Agent 集成
Agent 通过内核 API 直接访问拓扑,无需额外建模。这是 Power-Native 内核区别于通用 AgentOS 的关键所在——Agent 从诞生起即运行在电网的物理世界模型之上。
use eneros_agent::{Agent, AgentContext, AgentResult};
use eneros_topology::BusType;
struct TopologyAwareAgent {
interval: std::time::Duration,
}
impl Agent for TopologyAwareAgent {
fn name(&self) -> &str { "topology_aware" }
async fn run(&mut self, ctx: &mut AgentContext) -> AgentResult<()> {
loop {
// 获取完整拓扑
let network = ctx.get_network().await?;
// 分析拓扑
let islands = network.topology().find_islands();
// 基于拓扑决策
for island in &islands {
if island.buses.len() > 1 {
self.optimize_island(island, ctx).await?;
} else {
self.handle_isolated(island, ctx).await?;
}
}
ctx.sleep(self.interval).await;
}
}
}
impl TopologyAwareAgent {
async fn optimize_island(
&mut self,
island: &eneros_topology::Island,
ctx: &mut AgentContext,
) -> AgentResult<()> {
let network = ctx.get_network().await?;
let total_load: f64 = island.buses.iter()
.filter_map(|&b| network.bus_load(b))
.sum();
let total_gen: f64 = island.buses.iter()
.filter_map(|&b| network.bus_generation(b))
.sum();
let balance = total_gen - total_load;
println!("岛 {} 平衡量: {:.2} MW", island.id, balance);
Ok(())
}
async fn handle_isolated(
&mut self,
island: &eneros_topology::Island,
ctx: &mut AgentContext,
) -> AgentResult<()> {
println!("检测到孤岛: {:?}", island.buses);
ctx.publish(eneros_topology::event::IslandFormed {
island_id: island.id,
buses: island.buses.clone(),
}).await?;
Ok(())
}
}
与潮流计算联动
拓扑变更会自动触发潮流计算缓存失效,并通过事件总线向所有订阅者推送 TopologyChanged 事件,保证状态一致性。
use eneros_powerflow::{PowerFlow, NewtonRaphson};
// 监听拓扑变更,自动重算潮流
ctx.subscribe::<TopologyChanged>().await?;
let mut pf = NewtonRaphson::new()
.tolerance(1e-8)
.max_iter(50);
// 拓扑变更后潮流缓存自动失效,下次调用自动重算
let result = pf.solve(&network)?;
println!("收敛迭代次数: {}", result.iterations);
println!("最大失配量: {:.2e}", result.max_mismatch);
持久化与版本
拓扑对象支持持久化到内核管理的对象存储,并保留完整的变更历史:
// 保存当前拓扑
network.save("case_2026_summer").await?;
// 加载历史版本
let historical = NetworkGraph::load("case_2025_winter").await?;
// 查看变更历史
let history = network.version_log();
for entry in history.iter().rev().take(10) {
println!("{}: {} by {}", entry.timestamp, entry.action, entry.author);
}
// 回滚到指定版本
network.rollback_to("v2025.12.01-1200").await?;
性能指标
| 操作 | 延迟 | 吞吐 |
|---|---|---|
| 添加母线/支路 | < 1μs | - |
| 删除母线/支路 | < 2μs | - |
| 拓扑分析(1000 节点) | < 1ms | - |
| 电气岛检测(1000 节点) | < 5ms | - |
| 电气岛检测(10万 节点) | < 80ms | - |
| 最短路径(1000 节点) | < 100μs | - |
| 连通性检查 | < 50μs | - |
| 拓扑变更事件广播 | < 10μs | 100k events/s |
| 拓扑序列化(CIM XML) | - | 10k buses/ms |
| 拓扑快照写入 | < 5ms | - |
测试环境:4 核 / 8GB / Ubuntu 22.04,节点数为 1000(IEEE 118 + 扩展)。
配置参数
eneros.toml 中拓扑相关配置:
[topology]
# 拓扑缓存大小(拓扑快照数)
cache_size = 8
# 是否启用电气岛增量索引
incremental_island_index = true
# 拓扑变更事件订阅者上限
max_subscribers = 1024
# 序列化格式:cim_xml / cim_json / cgmes / binary
default_format = "binary"
# 是否启用拓扑变更审计日志
audit_log = true
# 拓扑快照自动保存间隔(秒)
snapshot_interval_secs = 300
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| cache_size | u32 | 8 | 缓存的拓扑快照数量 |
| incremental_island_index | bool | true | 启用增量电气岛索引,加速变更后重算 |
| max_subscribers | u32 | 1024 | 单个拓扑事件最大订阅者数 |
| default_format | enum | binary | 默认序列化格式 |
| audit_log | bool | true | 是否记录拓扑变更审计日志 |
| snapshot_interval_secs | u32 | 300 | 自动快照间隔 |
与其他能力的关系
| 关联能力 | 互动方式 |
|---|---|
| 物理约束决策 | 拓扑变更触发约束重算 |
| 设备模型库 | 母线/支路引用设备模型参数 |
| 时序原生操作 | 拓扑变更事件写入时序存储 |
| 数字孪生引擎 | 孪生镜像基于拓扑构建 |
| 安全守卫 | 操作票校验依赖拓扑连通性 |
| 多智能体协作 | Agent 通过拓扑定位管辖范围 |
| 实时双执行域 | 拓扑变更实时同步到实时域 |
| 多租户与隔离 | 拓扑按租户命名空间隔离 |