跳到主内容

v0.7.0 版本说明

EnerOS v0.7.0

发布日期:2024年7月7日 版本代号:Equipment(设备) Git Tag:v0.7.0 支持状态:内部预览(Internal Preview) Crate 总数:6 测试用例数:942

版本概述

EnerOS v0.7.0「Equipment」引入了电力设备模型库。本版本发布 eneros-equipment crate,提供了变压器(两绕组、三绕组)、输电线路(PI 型等值电路)、开关(断路器、隔离开关)与负荷(ZIP 模型)四大类设备的详细数学模型。v0.3.0 的拓扑模型仅包含设备的「连接关系」,而 v0.7.0 补齐了设备的「物理特性」——变压器不仅有变比,还有分接头档位、空载损耗、短路阻抗;线路不仅有 R/X/B,还有温度特性与频率特性;负荷不仅有有功无功,还有电压频率静态特性。

设备模型库的设计遵循 IEC 61970 CIM 与中国电力行业标准 DL/T 698 的双重规范。CIM 提供了设备类的本体论框架,而 DL/T 698 定义了设备参数的工程取值范围与命名约定。EnerOS 在两者之间做了映射:对外可导出 CIM 兼容的 RDF/JSON,对内使用 Rust enum 表达设备类型,使用 trait 表达设备行为。例如,所有设备都实现 Equipment trait,提供 impedance()admittance()status() 等方法;变压器额外实现 TapChanger trait,提供分接头调节能力。

v0.7.0 的一个重要设计决策是将「设备模型」与「设备实例」分离。TransformerModel 描述某型号变压器的参数模板(如 SZ11-31500/110),而 TransformerInstance 描述具体部署的某一台变压器(如「变电站 A 的 1 号主变」)。这种分离使得设备库可以基于厂家提供的型式试验数据建立模型库,运行时根据实际安装的设备实例化。模型库预置了 50+ 常见型号的参数,覆盖 10kV 至 500kV 各电压等级。

关键数据

指标数值说明
设备类型4 大类变压器/线路/开关/负荷
预置型号52覆盖常见厂家
设备模型参数32单设备平均
变压器分接头档位±8标准配置
ZIP 负荷特性系数3Z/I/P 比例

新特性

1. 变压器模型

两绕组变压器

// crates/eneros-equipment/src/transformer/two_winding.rs
use serde::{Deserialize, Serialize};
use eneros_topology::bus::BusId;

/// 两绕组变压器
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TwoWindingTransformer {
    pub id: String,
    pub name: String,
    pub hv_bus: BusId,          // 高压侧母线
    pub lv_bus: BusId,          // 低压侧母线
    pub rated_hv_kv: f64,       // 高压侧额定电压
    pub rated_lv_kv: f64,       // 低压侧额定电压
    pub rated_mva: f64,         // 额定容量
    /// 短路阻抗百分比(%)
    pub short_circuit_z_pct: f64,
    /// 短路电阻百分比(%),通常 = 铜耗/(额定容量*10)
    pub short_circuit_r_pct: f64,
    /// 空载电流百分比(%)
    pub no_load_i_pct: f64,
    /// 空载损耗(kW)
    pub no_load_loss_kw: f64,
    /// 分接头配置
    pub tap_changer: TapChanger,
    /// 是否在运
    pub in_service: bool,
}

/// 分接头调节器
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TapChanger {
    /// 额定档位对应的变比
    pub nominal_tap: f64,
    /// 每档调节量(百分比)
    pub step_pct: f64,
    /// 最大档位(正)
    pub max_tap: i32,
    /// 最小档位(负)
    pub min_tap: i32,
    /// 当前档位
    pub current_tap: i32,
}

impl TapChanger {
    /// 当前实际变比
    pub fn current_ratio(&self) -> f64 {
        self.nominal_tap * (1.0 + self.current_tap as f64 * self.step_pct / 100.0)
    }

    /// 升档
    pub fn raise(&mut self) -> Result<(), EquipmentError> {
        if self.current_tap >= self.max_tap {
            return Err(EquipmentError::TapAtMax);
        }
        self.current_tap += 1;
        Ok(())
    }

    /// 降档
    pub fn lower(&mut self) -> Result<(), EquipmentError> {
        if self.current_tap <= self.min_tap {
            return Err(EquipmentError::TapAtMin);
        }
        self.current_tap -= 1;
        Ok(())
    }
}

impl TwoWindingTransformer {
    /// 计算标幺值参数(基于给定基准容量)
    pub fn pi_parameters(&self, base_mva: f64) -> PiParameters {
        let tap = self.tap_changer.current_ratio();
        // 阻抗从百分比换算到标幺值
        let z_pu = (self.short_circuit_z_pct / 100.0) * (base_mva / self.rated_mva);
        let r_pu = (self.short_circuit_r_pct / 100.0) * (base_mva / self.rated_mva);
        let x_pu = (z_pu * z_pu - r_pu * r_pu).sqrt();
        // 励磁导纳
        let b_half = (self.no_load_i_pct / 100.0) * (base_mva / self.rated_mva);
        PiParameters {
            r_pu,
            x_pu,
            b_half_pu: b_half / 2.0,
            tap_ratio: tap,
            phase_shift: 0.0,
        }
    }
}

三绕组变压器

// crates/eneros-equipment/src/transformer/three_winding.rs
/// 三绕组变压器:用三个两绕组变压器等值(星形等值电路)
pub struct ThreeWindingTransformer {
    pub id: String,
    pub hv_bus: BusId,      // 高压侧
    pub mv_bus: BusId,      // 中压侧
    pub lv_bus: BusId,      // 低压侧
    pub rated_hv_mva: f64,
    pub rated_mv_mva: f64,
    pub rated_lv_mva: f64,
    pub z_hm_pct: f64,  // 高-中 短路阻抗
    pub z_hl_pct: f64,  // 高-低 短路阻抗
    pub z_ml_pct: f64,  // 中-低 短路阻抗
    // ... 三对短路参数
}

impl ThreeWindingTransformer {
    /// 转换为星形等值电路的三个支路
    pub fn to_star_equivalent(&self) -> [PiParameters; 3] {
        // 将三绕组参数转换为星形等值
        let z_h = 0.5 * (self.z_hm_pct + self.z_hl_pct - self.z_ml_pct);
        let z_m = 0.5 * (self.z_hm_pct + self.z_ml_pct - self.z_hl_pct);
        let z_l = 0.5 * (self.z_hl_pct + self.z_ml_pct - self.z_hm_pct);
        // 返回 H/M/L 三个支路的 PI 参数
        [
            PiParameters::transformer(z_h / 100.0, 0.0, 1.0),
            PiParameters::transformer(z_m / 100.0, 0.0, 1.0),
            PiParameters::transformer(z_l / 100.0, 0.0, 1.0),
        ]
    }
}

2. 线路模型(PI 型等值电路)

// crates/eneros-equipment/src/line.rs
use eneros_topology::bus::BusId;

/// 输电线路(PI 型等值电路)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransmissionLine {
    pub id: String,
    pub from_bus: BusId,
    pub to_bus: BusId,
    pub length_km: f64,
    /// 单位长度电阻(Ω/km)
    pub r_per_km: f64,
    /// 单位长度电抗(Ω/km)
    pub x_per_km: f64,
    /// 单位长度电纳(S/km)
    pub b_per_km: f64,
    /// 单位长度电导(S/km)
    pub g_per_km: f64,
    /// 并联线路数(1, 2, 3, 4)
    pub num_circuits: u32,
    /// 额定电流(A,单回路)
    pub rated_current_a: f64,
    /// 20°C 时的电阻(温度修正基准)
    pub reference_temp_c: f64,
    /// 电阻温度系数(1/°C)
    pub temp_coefficient: f64,
    pub in_service: bool,
}

impl TransmissionLine {
    /// 计算给定温度下的 PI 参数
    pub fn pi_parameters(&self, base_mva: f64, base_kv: f64, temp_c: f64) -> PiParameters {
        let z_base = base_kv * base_kv / base_mva;

        // 温度修正
        let temp_factor = 1.0 + self.temp_coefficient * (temp_c - self.reference_temp_c);
        let r_total = self.r_per_km * self.length_km * temp_factor / z_base;
        let x_total = self.x_per_km * self.length_km / z_base;
        let b_total = self.b_per_km * self.length_km * z_base;

        // 多回路并联
        let n = self.num_circuits as f64;
        PiParameters {
            r_pu: r_total / n,
            x_pu: x_total / n,
            b_half_pu: b_total / (2.0 * n),
            tap_ratio: 1.0,
            phase_shift: 0.0,
        }
    }

    /// 计算线路热稳定极限(A)
    pub fn thermal_limit_a(&self) -> f64 {
        self.rated_current_a * self.num_circuits as f64
    }
}

3. 开关模型

// crates/eneros-equipment/src/switch.rs
use eneros_topology::bus::BusId;

/// 开关类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SwitchKind {
    Breaker,        // 断路器
    Disconnector,   // 隔离开关
    LoadBreakSwitch,// 负荷开关
    EarthingSwitch, // 接地开关
    Fuse,           // 熔断器
}

/// 开关状态
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SwitchState {
    Closed, // 合闸
    Open,   // 分闸
    Tripped,// 跳闸(故障)
}

/// 开关设备
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Switch {
    pub id: String,
    pub from_bus: BusId,
    pub to_bus: BusId,
    pub kind: SwitchKind,
    pub state: SwitchState,
    /// 额定电流(A)
    pub rated_current_a: f64,
    /// 额定开断电流(A,断路器用)
    pub breaking_capacity_a: f64,
    /// 操作次数计数
    pub operation_count: u32,
    /// 上次操作时间戳
    pub last_operated_at: Option<i64>,
}

impl Switch {
    pub fn new(id: impl Into<String>, from: BusId, to: BusId, kind: SwitchKind) -> Self {
        Self {
            id: id.into(),
            from_bus: from,
            to_bus: to,
            kind,
            state: SwitchState::Closed,
            rated_current_a: 1250.0,
            breaking_capacity_a: 25000.0,
            operation_count: 0,
            last_operated_at: None,
        }
    }

    /// 合闸
    pub fn close(&mut self) -> Result<(), EquipmentError> {
        if self.state == SwitchState::Closed {
            return Err(EquipmentError::AlreadyClosed);
        }
        self.state = SwitchState::Closed;
        self.operation_count += 1;
        self.last_operated_at = Some(chrono::Utc::now().timestamp());
        Ok(())
    }

    /// 分闸
    pub fn open(&mut self) -> Result<(), EquipmentError> {
        if self.state == SwitchState::Open {
            return Err(EquipmentError::AlreadyOpen);
        }
        self.state = SwitchState::Open;
        self.operation_count += 1;
        self.last_operated_at = Some(chrono::Utc::now().timestamp());
        Ok(())
    }

    /// 是否导通
    pub fn is_conducting(&self) -> bool {
        self.state == SwitchState::Closed
    }
}

4. 负荷模型(ZIP)

ZIP 负荷模型将负荷表示为恒阻抗(Z)、恒电流(I)、恒功率(P)三部分的加权和,是电力系统稳态分析的标准负荷模型。

// crates/eneros-equipment/src/load.rs
use eneros_topology::bus::BusId;

/// ZIP 负荷模型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZipLoad {
    pub id: String,
    pub bus: BusId,
    /// 额定有功(MW)
    pub p_nominal_mw: f64,
    /// 额定无功(MVar)
    pub q_nominal_mvar: f64,
    /// 恒阻抗比例
    pub a_z: f64,
    /// 恒电流比例
    pub a_i: f64,
    /// 恒功率比例
    pub a_p: f64,
    /// 频率静特性系数(有功)
    pub kp_f: f64,
    /// 频率静特性系数(无功)
    pub kq_f: f64,
    pub in_service: bool,
}

impl ZipLoad {
    pub fn new(bus: BusId, p_mw: f64, q_mvar: f64) -> Self {
        Self {
            id: format!("load_{}", bus),
            bus,
            p_nominal_mw: p_mw,
            q_nominal_mvar: q_mvar,
            a_z: 0.4,
            a_i: 0.3,
            a_p: 0.3,
            kp_f: 1.0,
            kq_f: -1.0,
            in_service: true,
        }
    }

    /// 计算给定电压与频率下的实际功率
    pub fn power_at(&self, v_pu: f64, freq_hz: f64) -> (f64, f64) {
        let df = (freq_hz - 50.0) / 50.0;
        let v2 = v_pu * v_pu;
        let v3 = v2 * v_pu;

        let p = self.p_nominal_mw * (
            self.a_z * v2 + self.a_i * v_pu + self.a_p
        ) * (1.0 + self.kp_f * df);

        let q = self.q_nominal_mvar * (
            self.a_z * v2 + self.a_i * v_pu + self.a_p
        ) * (1.0 + self.kq_f * df);

        (p, q)
    }
}

ZIP 模型特性对比:

负荷类型电压依赖功率-电压关系适用设备
恒阻抗 ZP ∝ V²加热器、白炽灯
恒电流 IP ∝ V通用工业负荷
恒功率 PV⁰P = const电机驱动(变频)
典型混合-40%Z+30%I+30%P综合负荷

5. 预置设备型号库

// crates/eneros-equipment/src/catalog.rs
use crate::transformer::TwoWindingTransformer;

/// 预置变压器型号库
pub fn transformer_catalog() -> Vec<TwoWindingTransformer> {
    vec![
        // SZ11-31500/110(110kV/10kV,31.5MVA)
        TwoWindingTransformer {
            id: "SZ11-31500/110".into(),
            name: "SZ11-31500/110 三相双绕组有载调压".into(),
            rated_mva: 31.5,
            rated_hv_kv: 110.0,
            rated_lv_kv: 10.5,
            short_circuit_z_pct: 10.5,
            short_circuit_r_pct: 0.5,
            no_load_i_pct: 0.7,
            no_load_loss_kw: 29.0,
            // ...
        },
        // SZ11-63000/220
        // SZ11-150000/500
        // ...
    ]
}

改进

  • eneros-topologyBranch 支持关联详细设备模型
  • eneros-powerflow:潮流计算考虑变压器分接头变比
  • 依赖:新增 chronouuid

Bug 修复

  • 修复 TwoWindingTransformer::pi_parameters 中基准容量换算错误(#103)
  • 修复 TransmissionLine::pi_parameters 在多回路并联时未除以回路数的问题(#107)
  • 修复 Switch::open 在已分闸时未返回错误的问题(#110)

破坏性变更

  • eneros_topology::Branchparams 字段改为 Option<PiParameters>,运行时由设备模型计算

性能提升

操作耗时
变压器 PI 参数计算240 ns
线路 PI 参数计算180 ns
ZIP 负荷功率计算95 ns
开关状态切换32 ns
型号库加载(52 型号)1.4 ms

贡献者

贡献者角色提交数
@eneros-foundation架构师28
@equipment-expert设备建模专家52
@transformer-guru变压器专家31
@grid-rustaceanRust 工程师22

升级指南

新增依赖

[dependencies]
eneros-equipment = { version = "0.7", path = "../eneros-equipment" }

使用设备模型

use eneros_equipment::transformer::TwoWindingTransformer;
use eneros_topology::bus::BusId;

let mut transformer = TwoWindingTransformer {
    id: "T1".into(),
    hv_bus: BusId(1),
    lv_bus: BusId(2),
    rated_hv_kv: 110.0,
    rated_lv_kv: 10.5,
    rated_mva: 31.5,
    short_circuit_z_pct: 10.5,
    // ...
};
transformer.tap_changer.raise()?;  // 升一档
let pi = transformer.pi_parameters(100.0);