qsim_elements/
branch.rs

1//! Branch element — lines and transformers
2
3use qsim_core::{GridElement, StateStore};
4use serde::{Deserialize, Serialize};
5
6/// A branch (line or transformer) connecting two buses
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Branch {
9    /// From bus index
10    pub from_bus: usize,
11    /// To bus index
12    pub to_bus: usize,
13    /// Resistance (per-unit)
14    pub resistance: f64,
15    /// Reactance (per-unit)
16    pub reactance: f64,
17    /// Total line charging susceptance (per-unit)
18    pub susceptance: f64,
19    /// Transformer tap ratio (1.0 for lines)
20    pub tap_ratio: f64,
21    /// Transformer phase shift (radians)
22    pub phase_shift: f64,
23    /// Branch status (true = in service)
24    pub in_service: bool,
25}
26
27impl Branch {
28    /// Create a transmission line
29    pub fn line(from_bus: usize, to_bus: usize, resistance: f64, reactance: f64) -> Self {
30        Self {
31            from_bus,
32            to_bus,
33            resistance,
34            reactance,
35            susceptance: 0.0,
36            tap_ratio: 1.0,
37            phase_shift: 0.0,
38            in_service: true,
39        }
40    }
41
42    /// Create a transmission line with charging
43    pub fn line_with_charging(
44        from_bus: usize,
45        to_bus: usize,
46        resistance: f64,
47        reactance: f64,
48        susceptance: f64,
49    ) -> Self {
50        Self {
51            from_bus,
52            to_bus,
53            resistance,
54            reactance,
55            susceptance,
56            tap_ratio: 1.0,
57            phase_shift: 0.0,
58            in_service: true,
59        }
60    }
61
62    /// Create a transformer
63    pub fn transformer(
64        from_bus: usize,
65        to_bus: usize,
66        resistance: f64,
67        reactance: f64,
68        tap_ratio: f64,
69    ) -> Self {
70        Self {
71            from_bus,
72            to_bus,
73            resistance,
74            reactance,
75            susceptance: 0.0,
76            tap_ratio,
77            phase_shift: 0.0,
78            in_service: true,
79        }
80    }
81
82    /// Calculate series impedance magnitude
83    pub fn impedance(&self) -> f64 {
84        (self.resistance.powi(2) + self.reactance.powi(2)).sqrt()
85    }
86
87    /// Calculate series admittance (1/Z)
88    pub fn admittance(&self) -> (f64, f64) {
89        let z2 = self.resistance.powi(2) + self.reactance.powi(2);
90        if z2 > 0.0 {
91            (self.resistance / z2, -self.reactance / z2)
92        } else {
93            (0.0, 0.0)
94        }
95    }
96}
97
98impl GridElement for Branch {
99    fn element_type(&self) -> &'static str {
100        if (self.tap_ratio - 1.0).abs() < 1e-6 {
101            "Branch::Line"
102        } else {
103            "Branch::Transformer"
104        }
105    }
106
107    fn apply(&self, _state: &mut StateStore) {
108        // Branch contributions applied during matrix construction
109    }
110}