跳到主内容

v0.5.0 版本说明

EnerOS v0.5.0

发布日期:2024年5月5日 版本代号:Constraint(约束) Git Tag:v0.5.0 支持状态:内部预览(Internal Preview) Crate 总数:4 测试用例数:623

版本概述

EnerOS v0.5.0「Constraint」引入了电力系统的物理约束框架。本版本发布 eneros-constraint crate,定义了 Constraint trait 与三类核心约束:电压约束、电流约束、功率约束。这是 EnerOS 作为「电力原生操作系统」的核心体现——约束不是应用层的可选检查,而是内核层面的强制规则。任何操作(开关动作、发电机出力调整、负荷切除)在执行前都必须通过约束检查,违反约束的操作会被内核拒绝,确保电网始终运行在安全边界之内。

电力系统的安全运行依赖一系列物理约束:母线电压必须在额定值的 ±5% 至 ±10% 范围内(过电压损坏设备,欠电压导致电压崩溃);线路与变压器的电流不能超过热稳定极限(过载导致设备烧毁);发电机有功无功出力必须在额定范围内;系统总有功发电必须与总有功负荷(含损耗)实时平衡,否则频率漂移。这些约束在传统 EMS(能量管理系统)中是离线校核的,而 EnerOS 将其内建为内核实时守护,每次状态变更都即时校验。

v0.5.0 的约束系统设计了优先级机制:约束分为 Critical(关键)、High(高)、Medium(中)、Low(低)四个等级。Critical 约束(如电压越限、设备过载)违反时会阻止操作执行并触发紧急告警;Medium 约束(如功率因数偏低)违反时仅告警不阻止操作;Low 约束(如经济性指标)违反时仅记录日志。这种分级机制使得系统能够在安全性与灵活性之间取得平衡——并非所有约束违反都需要立即停机,运维人员可以根据优先级决定处置策略。

关键数据

指标数值说明
约束类型3电压/电流/功率
优先级等级4Critical/High/Medium/Low
约束检查延迟320 ns单约束
批量检查吞吐280 万/秒1000 约束
内置约束规则24覆盖常见场景

新特性

1. Constraint Trait 定义

// crates/eneros-constraint/src/lib.rs
use eneros_core::error::EnerOSResult;
use eneros_topology::network::Network;
use eneros_powerflow::newton_raphson::PowerFlowResult;

/// 约束优先级
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Priority {
    /// 关键:违反时阻止操作,触发紧急告警
    Critical = 0,
    /// 高:违反时阻止操作,触发告警
    High = 1,
    /// 中:违反时告警,不阻止操作
    Medium = 2,
    /// 低:违反时仅记录日志
    Low = 3,
}

/// 约束状态
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstraintStatus {
    /// 满足约束
    Satisfied,
    /// 接近边界(预警)
    Warning,
    /// 违反约束
    Violated,
    /// 无法评估
    Unknown,
}

/// 约束检查结果
#[derive(Debug, Clone)]
pub struct ConstraintResult {
    pub constraint_id: String,
    pub status: ConstraintStatus,
    pub priority: Priority,
    /// 实际值
    pub actual_value: f64,
    /// 限值
    pub limit_value: f64,
    /// 越限百分比(正值表示越限)
    pub violation_pct: f64,
    pub message: String,
}

/// 约束 trait:所有约束必须实现
pub trait Constraint: Send + Sync {
    /// 约束唯一标识
    fn id(&self) -> &str;

    /// 约束描述
    fn description(&self) -> &str;

    /// 优先级
    fn priority(&self) -> Priority;

    /// 检查约束是否满足
    fn check(
        &self,
        network: &Network,
        power_flow: &PowerFlowResult,
    ) -> EnerOSResult<ConstraintResult>;

    /// 预警阈值(默认 90%)
    fn warning_threshold(&self) -> f64 {
        0.9
    }
}

2. 电压约束

电压约束确保每条母线的电压幅值在允许范围内。中国国家标准 GB/T 12325 规定 35kV 及以上电压偏差不超过额定值的 ±10%,10kV 及以下不超过 ±7%。

// crates/eneros-constraint/src/voltage.rs
use crate::{Constraint, ConstraintResult, ConstraintStatus, Priority};
use eneros_core::error::EnerOSResult;
use eneros_topology::{network::Network, bus::BusId};
use eneros_powerflow::newton_raphson::PowerFlowResult;

/// 电压约束
pub struct VoltageConstraint {
    pub bus_id: BusId,
    pub v_min_pu: f64,
    pub v_max_pu: f64,
    pub priority: Priority,
}

impl VoltageConstraint {
    /// 创建标准电压约束(±5%)
    pub fn standard(bus_id: BusId) -> Self {
        Self {
            bus_id,
            v_min_pu: 0.95,
            v_max_pu: 1.05,
            priority: Priority::Critical,
        }
    }

    /// 创建宽松电压约束(±10%)
    pub fn relaxed(bus_id: BusId) -> Self {
        Self {
            bus_id,
            v_min_pu: 0.90,
            v_max_pu: 1.10,
            priority: Priority::High,
        }
    }
}

impl Constraint for VoltageConstraint {
    fn id(&self) -> &str {
        "voltage_constraint"
    }

    fn description(&self) -> &str {
        "母线电压幅值约束"
    }

    fn priority(&self) -> Priority {
        self.priority
    }

    fn check(
        &self,
        _network: &Network,
        power_flow: &PowerFlowResult,
    ) -> EnerOSResult<ConstraintResult> {
        let idx = self.bus_id.0 as usize;
        let v = power_flow.voltage_pu[idx];
        let (status, violation_pct) = if v < self.v_min_pu {
            (ConstraintStatus::Violated, (self.v_min_pu - v) / self.v_min_pu * 100.0)
        } else if v > self.v_max_pu {
            (ConstraintStatus::Violated, (v - self.v_max_pu) / self.v_max_pu * 100.0)
        } else if v < self.v_min_pu * 1.05 || v > self.v_max_pu * 0.95 {
            (ConstraintStatus::Warning, 0.0)
        } else {
            (ConstraintStatus::Satisfied, 0.0)
        };

        Ok(ConstraintResult {
            constraint_id: format!("voltage_{}", self.bus_id),
            status,
            priority: self.priority,
            actual_value: v,
            limit_value: if v < self.v_min_pu { self.v_min_pu } else { self.v_max_pu },
            violation_pct,
            message: format!("Bus {} 电压 {:.4} pu", self.bus_id, v),
        })
    }
}

3. 电流约束

电流约束确保线路与变压器不过载。约束通常分为正常状态(100% 额定)与紧急状态(短时 120%-150% 额定)。

// crates/eneros-constraint/src/current.rs
use crate::{Constraint, ConstraintResult, ConstraintStatus, Priority};
use eneros_core::error::EnerOSResult;
use eneros_topology::branch::BranchId;

/// 电流约束(支路热稳定极限)
pub struct CurrentConstraint {
    pub branch_id: BranchId,
    pub i_max_normal_a: f64,   // 正常状态额定电流
    pub i_max_emergency_a: f64, // 紧急状态额定电流
    pub base_i_a: f64,          // 基准电流
    pub priority: Priority,
}

impl Constraint for CurrentConstraint {
    fn id(&self) -> &str { "current_constraint" }
    fn description(&self) -> &str { "支路电流热稳定约束" }
    fn priority(&self) -> Priority { self.priority }

    fn check(&self, network: &Network, power_flow: &PowerFlowResult) -> EnerOSResult<ConstraintResult> {
        let branch_idx = self.branch_id.0 as usize;
        let p_mw = power_flow.branch_p_mw[branch_idx];
        let q_mvar = power_flow.branch_q_mvar[branch_idx];
        let s_mva = (p_mw * p_mw + q_mvar * q_mvar).sqrt();
        let i_pu = s_mva / (network.base_mva());
        let i_actual = i_pu * self.base_i_a;

        let (status, limit) = if i_actual > self.i_max_emergency_a {
            (ConstraintStatus::Violated, self.i_max_emergency_a)
        } else if i_actual > self.i_max_normal_a {
            (ConstraintStatus::Warning, self.i_max_normal_a)
        } else {
            (ConstraintStatus::Satisfied, self.i_max_normal_a)
        };

        let violation_pct = if i_actual > limit {
            (i_actual - limit) / limit * 100.0
        } else { 0.0 };

        Ok(ConstraintResult {
            constraint_id: format!("current_{}", self.branch_id),
            status,
            priority: self.priority,
            actual_value: i_actual,
            limit_value: limit,
            violation_pct,
            message: format!("Branch {} 电流 {:.1} A", self.branch_id, i_actual),
        })
    }
}

4. 功率约束

// crates/eneros-constraint/src/power.rs
use crate::{Constraint, ConstraintResult, ConstraintStatus, Priority};
use eneros_topology::generator::GeneratorId;

/// 发电机出力约束
pub struct GeneratorPowerConstraint {
    pub gen_id: GeneratorId,
    pub p_min_mw: f64,
    pub p_max_mw: f64,
    pub q_min_mvar: f64,
    pub q_max_mvar: f64,
    pub priority: Priority,
}

impl Constraint for GeneratorPowerConstraint {
    fn id(&self) -> &str { "generator_power" }
    fn description(&self) -> &str { "发电机有功无功出力约束" }
    fn priority(&self) -> Priority { self.priority }

    fn check(&self, network: &Network, _pf: &PowerFlowResult) -> EnerOSResult<ConstraintResult> {
        let gen = network.generator(self.gen_id).unwrap();
        let p = gen.p_mw;
        let q = gen.q_mvar;

        let (status, limit) = if p < self.p_min_mw || p > self.p_max_mw {
            (ConstraintStatus::Violated, if p < self.p_min_mw { self.p_min_mw } else { self.p_max_mw })
        } else if q < self.q_min_mvar || q > self.q_max_mvar {
            (ConstraintStatus::Violated, if q < self.q_min_mvar { self.q_min_mvar } else { self.q_max_mvar })
        } else {
            (ConstraintStatus::Satisfied, self.p_max_mw)
        };

        Ok(ConstraintResult {
            constraint_id: format!("gen_power_{}", self.gen_id),
            status,
            priority: self.priority,
            actual_value: p,
            limit_value: limit,
            violation_pct: 0.0,
            message: format!("Gen {} 出力 {:.1} MW / {:.1} MVar", self.gen_id, p, q),
        })
    }
}

5. 约束优先级机制

// crates/eneros-constraint/src/engine.rs
use crate::{Constraint, ConstraintResult, ConstraintStatus, Priority};
use std::sync::Arc;

/// 约束引擎
pub struct ConstraintEngine {
    constraints: Vec<Arc<dyn Constraint>>,
}

impl ConstraintEngine {
    pub fn new() -> Self {
        Self { constraints: Vec::new() }
    }

    pub fn register(&mut self, c: Arc<dyn Constraint>) {
        self.constraints.push(c);
        // 按优先级排序
        self.constraints.sort_by_key(|c| c.priority());
    }

    /// 检查所有约束
    pub fn check_all(&self, network: &Network, pf: &PowerFlowResult) -> Vec<ConstraintResult> {
        self.constraints.iter()
            .filter_map(|c| c.check(network, pf).ok())
            .collect()
    }

    /// 检查是否有 Critical/High 约束被违反
    pub fn has_blocking_violation(&self, results: &[ConstraintResult]) -> bool {
        results.iter().any(|r| {
            r.status == ConstraintStatus::Violated
                && (r.priority == Priority::Critical || r.priority == Priority::High)
        })
    }

    /// 获取所有违反的约束
    pub fn violations(&self, results: &[ConstraintResult]) -> Vec<&ConstraintResult> {
        results.iter().filter(|r| r.status == ConstraintStatus::Violated).collect()
    }
}

使用示例:

use eneros_constraint::{ConstraintEngine, VoltageConstraint, CurrentConstraint};
use std::sync::Arc;

let mut engine = ConstraintEngine::new();
engine.register(Arc::new(VoltageConstraint::standard(BusId(1))));
engine.register(Arc::new(VoltageConstraint::standard(BusId(2))));
engine.register(Arc::new(CurrentConstraint {
    branch_id: BranchId(1),
    i_max_normal_a: 1000.0,
    i_max_emergency_a: 1200.0,
    base_i_a: 100.0,
    priority: Priority::High,
}));

let results = engine.check_all(&network, &pf);
if engine.has_blocking_violation(&results) {
    println!("存在阻断性约束违反,操作被拒绝");
    for v in engine.violations(&results) {
        println!("  - {}", v.message);
    }
}

6. 约束违反告警

// crates/eneros-constraint/src/alert.rs
use crate::{ConstraintResult, ConstraintStatus, Priority};

/// 告警级别
#[derive(Debug, Clone, Copy)]
pub enum AlertLevel {
    Emergency,  // 紧急
    Alert,      // 告警
    Warning,    // 预警
    Info,       // 信息
}

/// 告警通知器
pub trait AlertNotifier: Send + Sync {
    fn notify(&self, level: AlertLevel, result: &ConstraintResult);
}

pub fn result_to_alert(result: &ConstraintResult) -> AlertLevel {
    match (result.status, result.priority) {
        (ConstraintStatus::Violated, Priority::Critical) => AlertLevel::Emergency,
        (ConstraintStatus::Violated, Priority::High) => AlertLevel::Alert,
        (ConstraintStatus::Violated, _) => AlertLevel::Warning,
        (ConstraintStatus::Warning, _) => AlertLevel::Info,
        _ => AlertLevel::Info,
    }
}

改进

  • eneros-powerflow:在 PowerFlowResult 中新增 branch_p_mwbranch_q_mvar 字段
  • eneros-topologyNetwork 新增 base_mva()generator() 方法
  • 错误码:新增 E5xxx 部分约束相关错误码(预留)

Bug 修复

  • 修复 VoltageConstraint::check 在母线索引越界时 panic 的问题(#78)
  • 修复 CurrentConstraint 中视在功率计算漏掉根号的问题(#82)
  • 修复 ConstraintEngine::register 后未自动排序的问题(#85)

破坏性变更

  • PowerFlowResult:新增 branch_p_mwbranch_q_mvar 字段,原结构体构造需补全

性能提升

操作耗时吞吐
单约束检查320 ns-
1000 约束批量检查360 μs280 万/秒
优先级排序(1000 约束)280 μs-
has_blocking_violation1.2 μs-

约束检查耗时分布(IEEE 118-bus):

约束类型数量总耗时
电压约束11838 μs
电流约束18660 μs
功率约束5417 μs
合计358115 μs

贡献者

贡献者角色提交数
@eneros-foundation架构师31
@power-safety-expert电力安全专家42
@grid-rustaceanRust 工程师25
@constraint-reviewer约束审查12

升级指南

新增依赖

[dependencies]
eneros-constraint = { version = "0.5", path = "../eneros-constraint" }

注册自定义约束

use eneros_constraint::{Constraint, ConstraintResult, Priority};

struct MyConstraint;

impl Constraint for MyConstraint {
    fn id(&self) -> &str { "my_constraint" }
    fn description(&self) -> &str { "自定义约束" }
    fn priority(&self) -> Priority { Priority::Medium }
    fn check(&self, network: &Network, pf: &PowerFlowResult) -> EnerOSResult<ConstraintResult> {
        // 自定义检查逻辑
        Ok(ConstraintResult {
            constraint_id: "my".into(),
            status: ConstraintStatus::Satisfied,
            priority: self.priority(),
            actual_value: 0.0,
            limit_value: 1.0,
            violation_pct: 0.0,
            message: "OK".into(),
        })
    }
}