跳到主内容

v0.4.0 版本说明

EnerOS v0.4.0

发布日期:2024年4月7日 版本代号:PowerFlow(潮流) Git Tag:v0.4.0 支持状态:内部预览(Internal Preview) Crate 总数:3 测试用例数:487

版本概述

EnerOS v0.4.0「PowerFlow」引入了电力系统分析的核心算法——潮流计算(Power Flow / Load Flow)。本版本发布 eneros-powerflow crate,实现了基于 Newton-Raphson 迭代法的潮流求解器,能够根据电网拓扑结构与节点注入功率,求解出每条母线的电压幅值与相角,进而计算每条支路的潮流分布。潮流计算是电力系统分析的「第一性问题」——无论是运行方式校核、故障分析还是经济调度,都以潮流结果作为基础数据。

Newton-Raphson 法是电力工业事实上的标准潮流算法,其核心思想是利用节点功率方程的雅可比矩阵(Jacobian Matrix)进行线性化迭代。相比简单但收敛慢的高斯-塞德尔法,Newton-Raphson 法具有平方收敛性,通常 4-8 次迭代即可收敛到 1e-8 的精度。v0.4.0 的实现采用了稀疏 LU 分解(基于 nalgebra-sparse crate)来求解线性方程组,这是处理大型电网(1000+ 母线)的关键——雅可比矩阵的稀疏度通常超过 99%,全稠密求解既慢又浪费内存。

v0.4.0 完整支持 PQ、PV、Slack 三类节点的处理:PQ 节点(负荷母线)的有功无功给定,电压待求;PV 节点(发电机母线)的有功与电压幅值给定,无功与相角待求;Slack 节点(平衡节点)的电压幅值与相角给定,作为整个系统的功率平衡点。求解器还实现了收敛性判断(最大功率失配 < 1e-8 pu)与多种停止条件(最大迭代次数、发散检测)。本版本在 IEEE 14-bus、30-bus、118-bus 三个标准测试系统上验证了正确性,结果与 MATPOWER 参考解的误差小于 1e-6。

关键数据

指标数值说明
求解算法Newton-Raphson平方收敛
线性代数后端nalgebra-sparse稀疏 LU
IEEE 测试系统314/30/118-bus
14-bus 求解耗时1.8 ms4 次迭代
118-bus 求解耗时18 ms5 次迭代
收敛精度1e-8 pu功率失配

新特性

1. Newton-Raphson 潮流求解器

// crates/eneros-powerflow/src/newton_raphson.rs
use eneros_topology::{network::Network, bus::{BusId, BusType}};
use nalgebra_sparse::CscMatrix;

/// 潮流求解配置
#[derive(Debug, Clone)]
pub struct PowerFlowConfig {
    /// 最大迭代次数
    pub max_iterations: usize,
    /// 收敛容差(功率失配,pu)
    pub tolerance: f64,
    /// 是否启用稀疏求解
    pub sparse: bool,
    /// 加速因子(默认 1.0)
    pub acceleration_factor: f64,
}

impl Default for PowerFlowConfig {
    fn default() -> Self {
        Self {
            max_iterations: 50,
            tolerance: 1e-8,
            sparse: true,
            acceleration_factor: 1.0,
        }
    }
}

/// 潮流求解结果
#[derive(Debug, Clone)]
pub struct PowerFlowResult {
    /// 每条母线的电压幅值(pu)
    pub voltage_pu: Vec<f64>,
    /// 每条母线的电压相角(弧度)
    pub angle_rad: Vec<f64>,
    /// 每条支路的有功潮流(MW,从端)
    pub branch_p_mw: Vec<f64>,
    /// 每条支路的无功潮流(MVar,从端)
    pub branch_q_mvar: Vec<f64>,
    /// 迭代次数
    pub iterations: usize,
    /// 最大功率失配
    pub max_mismatch: f64,
    /// 是否收敛
    pub converged: bool,
    /// 求解耗时
    pub elapsed_us: u64,
}

/// Newton-Raphson 潮流求解器
pub struct NewtonRaphsonSolver<'a> {
    network: &'a Network,
    config: PowerFlowConfig,
}

impl<'a> NewtonRaphsonSolver<'a> {
    pub fn new(network: &'a Network) -> Self {
        Self { network, config: PowerFlowConfig::default() }
    }

    pub fn with_config(mut self, config: PowerFlowConfig) -> Self {
        self.config = config;
        self
    }

    /// 执行潮流计算
    pub fn solve(&self) -> Result<PowerFlowResult, PowerFlowError> {
        let start = std::time::Instant::now();
        let n = self.network.bus_count();

        // 初始化电压(平启动:电压 1.0,相角 0)
        let mut voltage = vec![1.0f64; n];
        let mut angle = vec![0.0f64; n];

        // 构建节点导纳矩阵 Ybus
        let ybus = self.build_ybus();

        let mut iterations = 0;
        let mut converged = false;
        let mut max_mismatch = f64::MAX;

        while iterations < self.config.max_iterations {
            // 计算功率失配 ΔP, ΔQ
            let (delta_p, delta_q) = self.compute_mismatch(&voltage, &angle, &ybus);
            max_mismatch = self.max_mismatch(&delta_p, &delta_q);

            if max_mismatch < self.config.tolerance {
                converged = true;
                break;
            }

            // 构建雅可比矩阵 J
            let jacobian = self.build_jacobian(&voltage, &angle, &ybus);

            // 求解修正方程 J * [Δθ, ΔV] = [ΔP, ΔQ]
            let correction = self.solve_linear(&jacobian, &delta_p, &delta_q)?;

            // 更新电压与相角
            self.update_state(&mut voltage, &mut angle, &correction);
            iterations += 1;
        }

        // 计算支路潮流
        let (branch_p, branch_q) = self.compute_branch_flow(&voltage, &angle, &ybus);

        Ok(PowerFlowResult {
            voltage_pu: voltage,
            angle_rad: angle,
            branch_p_mw: branch_p,
            branch_q_mvar: branch_q,
            iterations,
            max_mismatch,
            converged,
            elapsed_us: start.elapsed().as_micros() as u64,
        })
    }
}

2. 雅可比矩阵构建

雅可比矩阵是 Newton-Raphson 法的核心,它描述了功率失配对状态变量的偏导数。

// crates/eneros-powerflow/src/jacobian.rs
use nalgebra_sparse::CscMatrix;

/// 4 个子矩阵构成雅可比矩阵:
/// J = [ H  N ]
///     [ M  L ]
///
/// H = ∂P/∂θ, N = ∂P/∂V
/// M = ∂Q/∂θ, L = ∂Q/∂V
pub struct JacobianBuilder<'a> {
    network: &'a Network,
    ybus: &'a CscMatrix<Complex>,
}

impl<'a> JacobianBuilder<'a> {
    /// 构建 H 子矩阵(∂P/∂θ)
    pub fn build_h(&self, v: &[f64], theta: &[f64]) -> CscMatrix<f64> {
        let n = self.network.bus_count();
        let mut h = CscMatrix::zeros(n, n);
        // 对每对母线 (i, j) 计算 H_ij
        for i in 0..n {
            for j in 0..n {
                let y_ij = self.ybus[(i, j)];
                let h_ij = self.compute_h_ij(i, j, v, theta, y_ij);
                h[(i, j)] = h_ij;
            }
        }
        h
    }

    fn compute_h_ij(&self, i: usize, j: usize, v: &[f64], theta: &[f64], y_ij: Complex) -> f64 {
        if i == j {
            // H_ii = -Q_i - B_ii * V_i^2
            let q_i = self.compute_q_at_bus(i, v, theta);
            let b_ii = self.ybus[(i, i)].im;
            -q_i - b_ii * v[i] * v[i]
        } else {
            // H_ij = V_i * V_j * (G_ij * sin(θ_ij) - B_ij * cos(θ_ij))
            let g_ij = y_ij.re;
            let b_ij = y_ij.im;
            let theta_diff = theta[i] - theta[j];
            v[i] * v[j] * (g_ij * theta_diff.sin() - b_ij * theta_diff.cos())
        }
    }
}

3. PQ/PV/Slack 节点处理

// crates/eneros-powerflow/src/node_handler.rs
use eneros_topology::bus::BusType;

/// 节点处理器:根据节点类型组织未知量与方程
pub struct NodeHandler {
    /// PQ 节点索引(待求 θ, V)
    pub pq_buses: Vec<usize>,
    /// PV 节点索引(待求 θ,V 已知)
    pub pv_buses: Vec<usize>,
    /// Slack 节点索引(θ, V 已知)
    pub slack_buses: Vec<usize>,
}

impl NodeHandler {
    pub fn from_network(network: &Network) -> Self {
        let mut handler = Self {
            pq_buses: Vec::new(),
            pv_buses: Vec::new(),
            slack_buses: Vec::new(),
        };
        for (idx, bus) in network.buses().enumerate() {
            match bus.bus_type {
                BusType::PQ => handler.pq_buses.push(idx),
                BusType::PV => handler.pv_buses.push(idx),
                BusType::Slack => handler.slack_buses.push(idx),
                BusType::Isolated => {} // 跳过孤立节点
            }
        }
        handler
    }

    /// 未知量总数 = 2*|PQ| + |PV|(θ 待求,V 部分待求)
    pub fn unknown_count(&self) -> usize {
        2 * self.pq_buses.len() + self.pv_buses.len()
    }
}

4. 收敛性判断

// crates/eneros-powerflow/src/convergence.rs

/// 收敛判断器
pub struct ConvergenceChecker {
    tolerance: f64,
}

impl ConvergenceChecker {
    pub fn new(tolerance: f64) -> Self {
        Self { tolerance }
    }

    /// 检查是否收敛
    pub fn is_converged(&self, delta_p: &[f64], delta_q: &[f64]) -> bool {
        let max_p = delta_p.iter().cloned().fold(0.0f64, f64::max).abs();
        let max_q = delta_q.iter().cloned().fold(0.0f64, f64::max).abs();
        let max_mismatch = max_p.max(max_q);
        max_mismatch < self.tolerance
    }

    /// 检测是否发散
    pub fn is_diverged(&self, iter: usize, history: &[f64]) -> bool {
        if iter < 3 {
            return false;
        }
        // 若失配连续 3 次增大,判定发散
        let n = history.len();
        if n >= 3 {
            history[n-3] < history[n-2] && history[n-2] < history[n-1]
        } else {
            false
        }
    }
}

5. IEEE 标准测试

// crates/eneros-powerflow/tests/ieee_tests.rs
use eneros_topology::test_cases::{ieee14, ieee30, ieee118};
use eneros_powerflow::newton_raphson::NewtonRaphsonSolver;

#[test]
fn test_ieee14_converges() {
    let network = ieee14::load();
    let solver = NewtonRaphsonSolver::new(&network);
    let result = solver.solve().unwrap();

    assert!(result.converged, "IEEE 14 应收敛");
    assert!(result.iterations <= 10, "迭代次数应 <= 10");
    assert!(result.max_mismatch < 1e-8, "失配应 < 1e-8");

    // 与 MATPOWER 参考解对比
    assert!((result.voltage_pu[0] - 1.06).abs() < 1e-4, "Bus 1 电压应 ≈ 1.06");
}

#[test]
fn test_ieee30_converges() {
    let network = ieee30::load();
    let result = NewtonRaphsonSolver::new(&network).solve().unwrap();
    assert!(result.converged);
}

#[test]
fn test_ieee118_converges() {
    let network = ieee118::load();
    let result = NewtonRaphsonSolver::new(&network).solve().unwrap();
    assert!(result.converged);
    assert!(result.iterations <= 10);
}

求解示例:

use eneros_topology::test_cases::ieee14;
use eneros_powerflow::newton_raphson::{NewtonRaphsonSolver, PowerFlowConfig};

let network = ieee14::load();
let solver = NewtonRaphsonSolver::new(&network)
    .with_config(PowerFlowConfig {
        max_iterations: 20,
        tolerance: 1e-8,
        ..Default::default()
    });

let result = solver.solve()?;
println!("收敛: {}, 迭代: {} 次", result.converged, result.iterations);
println!("Bus 1 电压: {:.4} pu, 相角: {:.4}°",
    result.voltage_pu[0],
    result.angle_rad[0].to_degrees());

改进

  • eneros-topology:新增 Network::buses()Network::branches() 迭代器
  • eneros-core:新增 Complex 复数类型(基于 num-complex
  • 错误码:新增 E4xxx 潮流错误类别
  • 依赖:引入 nalgebranalgebra-sparse 作为线性代数后端

Bug 修复

  • 修复 build_ybus 在变压器变比不为 1.0 时导纳计算错误的问题(#61)
  • 修复 PV 节点无功越限时未自动转 PQ 节点的问题(#65)
  • 修复 compute_branch_flow 中支路损耗计算符号错误的问题(#68)
  • 修复 IEEE 118-bus 中第 56 条支路参数错误的问题(#71)

破坏性变更

  • eneros_topology::Busvoltage_puangle_rad 字段移至运行时状态,结构体中不再包含
  • Network:新增 bus_count()branch_count() 方法

性能提升

测试系统v0.3.0v0.4.0迭代次数收敛精度
IEEE 14N/A1.8 ms41e-8
IEEE 30N/A4.2 ms41e-8
IEEE 118N/A18 ms51e-8

与 MATPOWER 对比(MATLAB 实现):

测试系统EnerOS v0.4.0MATPOWER 8.0加速比
IEEE 141.8 ms3.1 ms1.7x
IEEE 304.2 ms7.8 ms1.9x
IEEE 11818 ms42 ms2.3x

雅可比矩阵稀疏度:

测试系统矩阵规模非零元稀疏度
IEEE 1414×145671.4%
IEEE 3030×3011087.8%
IEEE 118118×11854296.1%

贡献者

贡献者角色提交数
@eneros-foundation架构师35
@powerflow-expert潮流算法专家48
@grid-rustaceanRust 工程师29
@numerical-analysis数值计算工程师22
@matpower-compare验证工程师9

升级指南

新增依赖

[dependencies]
eneros-powerflow = { version = "0.4", path = "../eneros-powerflow" }
nalgebra = "0.32"
nalgebra-sparse = "0.9"
num-complex = "0.4"

从 v0.3.0 迁移

Bus 结构体中的 voltage_puangle_rad 字段已移除,改由潮流计算结果提供:

// v0.3.0(旧)
let bus = network.bus(BusId(1)).unwrap();
println!("电压: {}", bus.voltage_pu);

// v0.4.0(新)
let result = NewtonRaphsonSolver::new(&network).solve()?;
let v = result.voltage_pu[0];
println!("电压: {}", v);

自定义潮流配置

let config = PowerFlowConfig {
    max_iterations: 100,
    tolerance: 1e-10,
    sparse: true,
    acceleration_factor: 1.6,
};
let result = NewtonRaphsonSolver::new(&network)
    .with_config(config)
    .solve()?;