surge_network/network/dc_line.rs
1// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0
2//! Two-terminal LCC-HVDC line data structures.
3//!
4//! A two-terminal DC line connects a rectifier bus (AC → DC) and an
5//! inverter bus (DC → AC) through a DC circuit with resistance `resistance_ohm`.
6//! PSS/E RAW section: "TWO-TERMINAL DC DATA".
7
8use serde::{Deserialize, Serialize};
9
10/// Control mode for a two-terminal LCC-HVDC link.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
12pub enum LccHvdcControlMode {
13 /// Line is blocked (out of service).
14 Blocked = 0,
15 /// Power control: SETVL is scheduled MW.
16 #[default]
17 PowerControl = 1,
18 /// Current control: SETVL is scheduled kA.
19 CurrentControl = 2,
20}
21
22impl LccHvdcControlMode {
23 /// Convert a PSS/E MDC integer to a `LccHvdcControlMode`. Out-of-range values map to `Blocked`.
24 pub fn from_u32(v: u32) -> Self {
25 match v {
26 1 => Self::PowerControl,
27 2 => Self::CurrentControl,
28 _ => Self::Blocked,
29 }
30 }
31}
32
33/// One converter terminal (rectifier or inverter) of a two-terminal LCC-HVDC link.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct LccConverterTerminal {
36 /// AC bus number this converter is connected to.
37 pub bus: u32,
38 /// Number of 6-pulse converter bridges (NBR / NBI).
39 #[serde(alias = "num_bridges")]
40 pub n_bridges: u32,
41 /// Maximum firing/extinction angle in degrees (ALFMX / GAMMX).
42 pub alpha_max: f64,
43 /// Minimum firing/extinction angle in degrees (ALFMN / GAMMN).
44 pub alpha_min: f64,
45 /// Commutating resistance per bridge in ohms (RCR / RCI).
46 pub commutation_resistance_ohm: f64,
47 /// Commutating reactance per bridge in ohms (XCR / XCI).
48 pub commutation_reactance_ohm: f64,
49 /// Converter transformer rated AC voltage (line-to-line kV) on the network side (EBASR / EBASI).
50 pub base_voltage_kv: f64,
51 /// Transformer turns ratio (EBASR/EBASI side to converter side) (TRR / TRI).
52 pub turns_ratio: f64,
53 /// Off-nominal tap ratio (TAPR / TAPI), default 1.0.
54 pub tap: f64,
55 /// Maximum tap ratio (TMXR / TMXI).
56 pub tap_max: f64,
57 /// Minimum tap ratio (TMNR / TMNI).
58 pub tap_min: f64,
59 /// Tap step size (STPR / STPI), 0 = continuous.
60 pub tap_step: f64,
61 /// Converter in-service flag (ICR / ICI == 1).
62 pub in_service: bool,
63}
64
65impl Default for LccConverterTerminal {
66 fn default() -> Self {
67 Self {
68 bus: 0,
69 n_bridges: 1,
70 alpha_max: 90.0,
71 alpha_min: 5.0,
72 commutation_resistance_ohm: 0.0,
73 commutation_reactance_ohm: 0.0,
74 base_voltage_kv: 0.0,
75 turns_ratio: 1.0,
76 tap: 1.0,
77 tap_max: 1.1,
78 tap_min: 0.9,
79 tap_step: 0.00625,
80 in_service: true,
81 }
82 }
83}
84
85/// Two-terminal LCC-HVDC link (PSS/E TWO-TERMINAL DC DATA section).
86///
87/// Models a point-to-point high-voltage DC link with line-commutated
88/// converters (thyristor bridges). The rectifier converts AC power to DC;
89/// the inverter converts DC power back to AC.
90///
91/// For power flow with `FixedSchedule` DC model the converter power is
92/// computed from `setvl`/`vschd` and injected as constant PQ at the
93/// converter buses. With `SequentialAcDc`, the AC/DC operating point is
94/// iterated to convergence.
95///
96/// When `p_dc_min_mw < p_dc_max_mw` the link exposes an optimization
97/// variable rather than a fixed setpoint — used by the joint AC-DC OPF
98/// path (`build_hvdc_p2p_nlp_data`) to put HVDC P into the NLP.
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct LccHvdcLink {
101 /// Name / identifier of the DC link.
102 pub name: String,
103 /// Control mode (0 = blocked, 1 = power control, 2 = current control).
104 pub mode: LccHvdcControlMode,
105 /// DC circuit resistance in ohms.
106 pub resistance_ohm: f64,
107 /// Scheduled DC power (MW) for MDC=1, or scheduled current (kA) for MDC=2.
108 pub scheduled_setpoint: f64,
109 /// Scheduled DC voltage in kV.
110 pub scheduled_voltage_kv: f64,
111 /// Max DC voltage for mode switching in kV (VCMOD).
112 pub voltage_mode_switch_kv: f64,
113 /// Compounding resistance in ohms (RCOMP).
114 pub compounding_resistance_ohm: f64,
115 /// Current margin in kA (DELTI).
116 pub current_margin_ka: f64,
117 /// Metering end: `'R'` = rectifier-metered, `'I'` = inverter-metered.
118 pub meter: char,
119 /// Minimum DC voltage in kV (DCVMIN).
120 pub voltage_min_kv: f64,
121 /// Maximum AC/DC outer-loop iterations (CCCITMX).
122 pub ac_dc_iteration_max: u32,
123 /// Acceleration factor for AC/DC iteration (CCCACC).
124 pub ac_dc_iteration_acceleration: f64,
125 /// Rectifier terminal (AC bus → DC).
126 pub rectifier: LccConverterTerminal,
127 /// Inverter terminal (DC → AC bus).
128 pub inverter: LccConverterTerminal,
129 /// Minimum DC power setpoint in MW for joint AC-DC OPF.
130 ///
131 /// When `p_dc_min_mw < p_dc_max_mw`, the joint AC-DC OPF will treat
132 /// this link's DC power as a decision variable bounded in
133 /// `[p_dc_min_mw, p_dc_max_mw]` rather than using the fixed
134 /// `scheduled_setpoint`. Default `0.0` (no variable range → fall back
135 /// to the sequential-iteration path with a fixed setpoint).
136 #[serde(default)]
137 pub p_dc_min_mw: f64,
138 /// Maximum DC power setpoint in MW for joint AC-DC OPF.
139 ///
140 /// When `p_dc_min_mw < p_dc_max_mw`, the joint AC-DC OPF will treat
141 /// this link's DC power as a decision variable bounded in
142 /// `[p_dc_min_mw, p_dc_max_mw]`. Default `0.0`.
143 #[serde(default)]
144 pub p_dc_max_mw: f64,
145}
146
147impl Default for LccHvdcLink {
148 fn default() -> Self {
149 Self {
150 name: String::new(),
151 mode: LccHvdcControlMode::PowerControl,
152 resistance_ohm: 0.0,
153 scheduled_setpoint: 0.0,
154 scheduled_voltage_kv: 500.0,
155 voltage_mode_switch_kv: 0.0,
156 compounding_resistance_ohm: 0.0,
157 current_margin_ka: 0.0,
158 meter: 'I',
159 voltage_min_kv: 0.0,
160 ac_dc_iteration_max: 20,
161 ac_dc_iteration_acceleration: 1.0,
162 rectifier: LccConverterTerminal::default(),
163 inverter: LccConverterTerminal::default(),
164 p_dc_min_mw: 0.0,
165 p_dc_max_mw: 0.0,
166 }
167 }
168}
169
170impl LccHvdcLink {
171 /// Returns `true` when the joint AC-DC OPF should treat this link's
172 /// DC power as an NLP decision variable (i.e. `[p_dc_min_mw,
173 /// p_dc_max_mw]` is a non-degenerate interval).
174 pub fn has_variable_p_dc(&self) -> bool {
175 self.p_dc_min_mw < self.p_dc_max_mw
176 }
177}