1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0
//! Bus (node) representation in the power system network.
use serde::{Deserialize, Serialize};
use crate::market::AmbientConditions;
/// Bus type classification for power flow analysis.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BusType {
/// PQ bus — real and reactive power specified (load bus).
PQ = 1,
/// PV bus — real power and voltage magnitude specified (generator bus).
PV = 2,
/// Slack (reference) bus — voltage magnitude and angle specified.
Slack = 3,
/// Isolated bus — disconnected from the network.
Isolated = 4,
}
/// A bus (node) in the power system network.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Bus {
/// Bus number (unique identifier).
pub number: u32,
/// Bus name.
pub name: String,
/// Bus type (PQ, PV, Slack, Isolated).
pub bus_type: BusType,
/// Shunt conductance (MW demanded at V = 1.0 p.u.).
pub shunt_conductance_mw: f64,
/// Shunt susceptance (MVAr injected at V = 1.0 p.u.).
pub shunt_susceptance_mvar: f64,
/// Area number.
pub area: u32,
/// Voltage magnitude in per-unit.
pub voltage_magnitude_pu: f64,
/// Voltage angle in radians.
pub voltage_angle_rad: f64,
/// Base voltage in kV.
pub base_kv: f64,
/// Zone number.
pub zone: u32,
/// Maximum voltage magnitude in per-unit.
pub voltage_max_pu: f64,
/// Minimum voltage magnitude in per-unit.
pub voltage_min_pu: f64,
/// Connected-component island ID (0 = largest island).
/// Populated by CGMES importer; other importers default to 0.
/// The NR/DC solvers perform their own island detection at solve time
/// using in-service branches (which may differ from import topology).
pub island_id: u32,
/// Latitude in decimal degrees (WGS84). None if unknown.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub latitude: Option<f64>,
/// Longitude in decimal degrees (WGS84). None if unknown.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub longitude: Option<f64>,
/// Bus frequency (Hz). None = nominal (Network.freq_hz).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub freq_hz: Option<f64>,
/// Ambient conditions at this location. None = use Network.market_data.ambient.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ambient: Option<AmbientConditions>,
/// Reserve zone name. References a ReserveZone on Network.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reserve_zone: Option<String>,
/// Ownership entries (PSS/E OWNER field). Single-owner for buses.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub owners: Vec<super::owner::OwnershipEntry>,
}
impl Default for Bus {
fn default() -> Self {
Self {
number: 0,
name: String::new(),
bus_type: BusType::PQ,
shunt_conductance_mw: 0.0,
shunt_susceptance_mvar: 0.0,
base_kv: 0.0,
voltage_magnitude_pu: 1.0,
voltage_angle_rad: 0.0,
area: 1,
zone: 1,
voltage_max_pu: 1.1,
voltage_min_pu: 0.9,
island_id: 0,
latitude: None,
longitude: None,
freq_hz: None,
ambient: None,
reserve_zone: None,
owners: Vec::new(),
}
}
}
impl Bus {
pub fn new(number: u32, bus_type: BusType, base_kv: f64) -> Self {
Self {
number,
bus_type,
base_kv,
..Default::default()
}
}
/// True if this bus is the slack (reference) bus.
pub fn is_slack(&self) -> bool {
self.bus_type == BusType::Slack
}
/// True if this bus is a PV (generator) bus.
pub fn is_pv(&self) -> bool {
self.bus_type == BusType::PV
}
/// True if this bus is a PQ (load) bus.
pub fn is_pq(&self) -> bool {
self.bus_type == BusType::PQ
}
}