Skip to main content

surge_network/dynamics/
models.rs

1// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0
2//! Dynamic model types for transient stability analysis.
3//!
4//! This module defines data structures for electromechanical generator models,
5//! excitation systems, turbine-governors, and power system stabilizers loaded
6//! from PSS/E `.dyr` files.
7//!
8//! # Supported models
9//!
10//! - **Generators**: GENCLS, GENROU, GENSAL
11//! - **Exciters**: EXST1, ESST3A, ESDC2A, EXDC2, IEEEX1/IEEEXC1
12//! - **Governors**: TGOV1, IEEEG1
13//! - **PSS**: IEEEST, ST2CUT
14//!
15//! # Simplified vs. detailed models (DYN-01)
16//!
17//! Several models in this module are **standard planning-level simplifications**,
18//! suitable for use when detailed dynamic data is unavailable.  For precise
19//! transient stability studies, use the detailed models when dynamic test data
20//! files (DYRE) provide the necessary parameters.
21//!
22//! ## Exciter model mapping
23//!
24//! | Simplified model | Detailed equivalent       | When to upgrade                        |
25//! |------------------|---------------------------|----------------------------------------|
26//! | SEXS             | AC5A, ESST4B, EXAC1       | Detailed AVR test data available       |
27//! | SCRX             | ESST4B, ESST3A            | Static exciter field test data         |
28//! | IEEET1           | ESDC2A, EXDC2, IEEEX1     | Rotating exciter field data            |
29//!
30//! ## Governor model mapping
31//!
32//! | Simplified model | Detailed equivalent       | When to upgrade                        |
33//! |------------------|---------------------------|----------------------------------------|
34//! | GAST             | GAST2A, GGOV1             | Gas turbine performance test data      |
35//! | TGOV1            | IEEEG1, TGOV5             | Steam turbine test data with reheat    |
36//!
37//! ## IBR (inverter-based resource) models
38//!
39//! | Simplified model | Detailed equivalent       | When to upgrade                        |
40//! |------------------|---------------------------|----------------------------------------|
41//! | REPC_A (simplified) | Full REPC_A with AGC droop | Plant-level control parameters     |
42//! | REEC_A (simplified) | Full REEC_A with Kqv droop | Electrical controller test data    |
43//! | REGC_A           | (already detailed)        | N/A                                    |
44//!
45//! The simplified IBR models use constant Pref/Qref (no AGC droop or plant-level
46//! voltage regulation), which is acceptable for planning-level studies where the
47//! IBR is not the focus.  For studies involving IBR fault ride-through, frequency
48//! response, or voltage regulation, use the full model parameters.
49
50use serde::{Deserialize, Serialize};
51
52// ---------------------------------------------------------------------------
53// Top-level container
54// ---------------------------------------------------------------------------
55
56/// Complete dynamic model database parsed from a `.dyr` file.
57#[derive(Debug, Clone, Default, Serialize, Deserialize)]
58pub struct DynamicModel {
59    /// Generator dynamic models (GENCLS, GENROU, GENSAL).
60    pub generators: Vec<GeneratorDyn>,
61    /// Excitation system models (EXST1, ESST3A, ESDC2A, EXDC2, IEEEX1).
62    pub exciters: Vec<ExciterDyn>,
63    /// Turbine-governor models (TGOV1, IEEEG1).
64    pub governors: Vec<GovernorDyn>,
65    /// Power system stabilizer models (IEEEST, ST2CUT).
66    pub pss: Vec<PssDyn>,
67    /// Load dynamic models (CLOD, INDMOT, MOTOR) — Phase 12.
68    #[serde(default)]
69    pub loads: Vec<LoadDyn>,
70    /// FACTS/HVDC dynamic models (CSVGN1, CSTCON, TCSC, CDC4T, VSCDCT) — Phase 13.
71    #[serde(default)]
72    pub facts: Vec<FACTSDyn>,
73    /// Records with unrecognised model names — stored verbatim for diagnostics.
74    pub unknown_records: Vec<UnknownDyrRecord>,
75    /// Over-excitation limiter models (OEL1B, OEL2C, SCL1C) — Wave 37.
76    #[serde(default)]
77    pub oels: Vec<OelDyn>,
78    /// Under-excitation limiter models (UEL1, UEL2C) — Wave 37.
79    #[serde(default)]
80    pub uels: Vec<UelDyn>,
81    /// Multi-mass torsional shaft models for time-domain SSR simulation.
82    #[serde(default)]
83    pub shafts: Vec<ShaftDyn>,
84}
85
86impl DynamicModel {
87    /// Number of generator dynamic records.
88    pub fn n_generators(&self) -> usize {
89        self.generators.len()
90    }
91
92    /// Number of exciter records.
93    pub fn n_exciters(&self) -> usize {
94        self.exciters.len()
95    }
96
97    /// Number of governor records.
98    pub fn n_governors(&self) -> usize {
99        self.governors.len()
100    }
101
102    /// Number of PSS records.
103    pub fn n_pss(&self) -> usize {
104        self.pss.len()
105    }
106
107    /// Number of load dynamic records.
108    pub fn n_loads(&self) -> usize {
109        self.loads.len()
110    }
111
112    /// Total number of recognised dynamic records.
113    pub fn total(&self) -> usize {
114        self.n_generators()
115            + self.n_exciters()
116            + self.n_governors()
117            + self.n_pss()
118            + self.n_loads()
119            + self.n_facts()
120            + self.oels.len()
121            + self.uels.len()
122            + self.shafts.len()
123    }
124
125    /// Compute supported model coverage.
126    /// Returns `(n_supported, n_total, coverage_pct)`.
127    pub fn coverage(&self) -> (usize, usize, f64) {
128        let n_supported = self.total();
129        let n_unknown = self.unknown_records.len();
130        let n_total = n_supported + n_unknown;
131        let pct = if n_total > 0 {
132            n_supported as f64 / n_total as f64 * 100.0
133        } else {
134            100.0
135        };
136        (n_supported, n_total, pct)
137    }
138
139    /// Number of FACTS/HVDC dynamic records.
140    pub fn n_facts(&self) -> usize {
141        self.facts.len()
142    }
143
144    /// Find the first FACTS/HVDC dynamic record at the given bus with the given device ID.
145    pub fn find_facts(&self, bus: u32, device_id: &str) -> Option<&FACTSDyn> {
146        self.facts
147            .iter()
148            .find(|f| f.bus == bus && f.device_id == device_id)
149    }
150
151    /// Find the first load dynamic record at the given bus with the given load ID.
152    pub fn find_load(&self, bus: u32, load_id: &str) -> Option<&LoadDyn> {
153        self.loads
154            .iter()
155            .find(|l| l.bus == bus && l.load_id == load_id)
156    }
157
158    /// Find the first generator dynamic record at the given bus with the given machine ID.
159    pub fn find_generator(&self, bus: u32, machine_id: &str) -> Option<&GeneratorDyn> {
160        self.generators
161            .iter()
162            .find(|g| g.bus == bus && g.machine_id == machine_id)
163    }
164
165    /// Find the first exciter record at the given bus with the given machine ID.
166    pub fn find_exciter(&self, bus: u32, machine_id: &str) -> Option<&ExciterDyn> {
167        self.exciters
168            .iter()
169            .find(|e| e.bus == bus && e.machine_id == machine_id)
170    }
171
172    /// Find the first governor record at the given bus with the given machine ID.
173    pub fn find_governor(&self, bus: u32, machine_id: &str) -> Option<&GovernorDyn> {
174        self.governors
175            .iter()
176            .find(|g| g.bus == bus && g.machine_id == machine_id)
177    }
178
179    /// Find the first PSS record at the given bus with the given machine ID.
180    pub fn find_pss(&self, bus: u32, machine_id: &str) -> Option<&PssDyn> {
181        self.pss
182            .iter()
183            .find(|p| p.bus == bus && p.machine_id == machine_id)
184    }
185
186    /// Find the first shaft dynamic record at the given bus with the given machine ID.
187    pub fn find_shaft(&self, bus: u32, machine_id: &str) -> Option<&ShaftDyn> {
188        self.shafts
189            .iter()
190            .find(|s| s.bus == bus && s.machine_id == machine_id)
191    }
192
193    // -----------------------------------------------------------------------
194    // USRMDL transparency — summary report & gap analysis
195    // -----------------------------------------------------------------------
196
197    /// Build a summary report of unknown/unrecognized dynamic models.
198    ///
199    /// Groups unknown records by model name, counts occurrences, lists affected
200    /// buses, and suggests standard equivalents where known.
201    pub fn unknown_model_summary(&self) -> Vec<UnknownModelGroup> {
202        use std::collections::BTreeMap;
203
204        let mut groups: BTreeMap<String, UnknownModelGroup> = BTreeMap::new();
205        for rec in &self.unknown_records {
206            let entry = groups.entry(rec.model_name.clone()).or_insert_with(|| {
207                let equiv = crate::dynamics::usrmdl_equiv::suggest_equivalent(&rec.model_name);
208                let category = crate::dynamics::usrmdl_equiv::guess_category(&rec.model_name);
209                UnknownModelGroup {
210                    model_name: rec.model_name.clone(),
211                    count: 0,
212                    buses: Vec::new(),
213                    category: category.label().to_string(),
214                    suggested_equivalent: equiv.map(|e| e.suggested.to_string()),
215                    suggestion_notes: equiv.map(|e| e.notes.to_string()),
216                }
217            });
218            entry.count += 1;
219            if !entry.buses.contains(&rec.bus) {
220                entry.buses.push(rec.bus);
221            }
222        }
223
224        groups.into_values().collect()
225    }
226
227    /// Find generators that have a generator model but are missing one or more
228    /// of: exciter, governor, or PSS.
229    ///
230    /// Returns `(bus, machine_id, has_exciter, has_governor, has_pss)` tuples.
231    pub fn incomplete_machines(&self) -> Vec<IncompleteMachine> {
232        self.generators
233            .iter()
234            .filter_map(|g| {
235                let has_exc = self.find_exciter(g.bus, &g.machine_id).is_some();
236                let has_gov = self.find_governor(g.bus, &g.machine_id).is_some();
237                let has_pss = self.find_pss(g.bus, &g.machine_id).is_some();
238                // Report if missing any component (exciter, governor, or PSS)
239                if !has_exc || !has_gov || !has_pss {
240                    Some(IncompleteMachine {
241                        bus: g.bus,
242                        machine_id: g.machine_id.clone(),
243                        has_exciter: has_exc,
244                        has_governor: has_gov,
245                        has_pss,
246                    })
247                } else {
248                    None
249                }
250            })
251            .collect()
252    }
253}
254
255/// Summary of an unrecognized dynamic model group.
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct UnknownModelGroup {
258    /// Model name as read from the DYR file.
259    pub model_name: String,
260    /// Number of records with this model name.
261    pub count: usize,
262    /// Unique bus numbers where this model appears.
263    pub buses: Vec<u32>,
264    /// Guessed category (Generator, Exciter, Governor, PSS, etc.).
265    pub category: String,
266    /// Suggested standard equivalent model name, if known.
267    pub suggested_equivalent: Option<String>,
268    /// Notes about the suggested mapping.
269    pub suggestion_notes: Option<String>,
270}
271
272/// A generator with an incomplete dynamic model representation.
273#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct IncompleteMachine {
275    /// Bus number.
276    pub bus: u32,
277    /// Machine ID.
278    pub machine_id: String,
279    /// Whether an exciter model was found.
280    pub has_exciter: bool,
281    /// Whether a governor model was found.
282    pub has_governor: bool,
283    /// Whether a PSS model was found.
284    pub has_pss: bool,
285}
286
287// ---------------------------------------------------------------------------
288// Generator dynamic records
289// ---------------------------------------------------------------------------
290
291/// A generator dynamic model record (GENCLS, GENROU, or GENSAL).
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct GeneratorDyn {
294    /// Bus number of the associated static generator.
295    pub bus: u32,
296    /// Machine ID (matches PSS/E machine ID field, e.g. `"1"`, `"G1"`, `"WND"`).
297    pub machine_id: String,
298    /// The specific generator model and its parameters.
299    pub model: GeneratorModel,
300}
301
302/// Discriminated union of supported generator dynamic models.
303#[derive(Debug, Clone, Serialize, Deserialize)]
304#[serde(tag = "type")]
305pub enum GeneratorModel {
306    Gencls(GenclsParams),
307    Genrou(GenrouParams),
308    Gensal(GensalParams),
309    /// REGC_A — IBR inner converter (voltage-source Norton equivalent, no swing equation).
310    Regca(RegcaParams),
311    Gentpj(GentpjParams),
312    Genqec(GenqecParams),
313    /// REGCB — enhanced IBR inner converter.
314    Regcb(RegcbParams),
315    /// WT3G2U — Type 3 (DFIG) wind generator.
316    Wt3g2u(Wt3g2uParams),
317    /// WT4G1 — Type 4 (full-converter) wind generator.
318    Wt4g1(Wt4g1Params),
319    /// REGFM_A1 — Grid-forming inverter (droop).
320    RegfmA1(RegfmA1Params),
321    /// REGFM_B1 — Grid-forming inverter (VSM).
322    RegfmB1(RegfmB1Params),
323    /// DER_A — Distributed energy resource aggregate.
324    Dera(DeraParams),
325    /// GENTRA — Third-order transient generator (3 states: δ, ω, E'q).
326    Gentra(GentraParams),
327    /// GENTPF — Flux-based saturation round-rotor (same state/equations as GENTPJ).
328    Gentpf(GentpjParams),
329    /// REGCC — Next-gen GFM-capable converter (4 states).
330    Regcc(RegccParams),
331    /// WT4G2 — Type 4 wind variant GE (2 states, same as WT4G1).
332    Wt4g2(Wt4g2Params),
333    /// DER_C / DERC — DER_A variant C (3 states).
334    Derc(DercParams),
335    // Phase 21
336    /// GENROA — GENROU with additional AVR interface (Phase 21, same state as GENROU).
337    Genroa(GenrouParams),
338    /// GENSAA — GENSAL with additional AVR interface (Phase 21, same state as GENSAL).
339    Gensaa(GensalParams),
340    /// REGFM_C1 — Grid-forming inverter C1 (Phase 21).
341    RegfmC1(RegfmC1Params),
342    // Phase 22
343    /// PVGU1 — WECC 1st-gen photovoltaic converter unit (Phase 22).
344    Pvgu1(Pvgu1Params),
345    /// PVDG — Distributed/rooftop PV aggregate model (Phase 22).
346    Pvdg(PvdgParams),
347    // Phase 27
348    /// WT3G3 — Type 3 wind generator variant 3 (Phase 27, reuses Wt3g2u dynamics).
349    Wt3g3(Wt3g2uParams),
350    /// REGCO1 — Grid-following converter generator (Phase 27, 4 states).
351    Regco1(Regco1Params),
352    /// GENWTG — Alias for GENROU (Phase 27).
353    Genwtg(GenrouParams),
354    /// GENROE — GENROU with extended saturation (Phase 27, reuses GENROU params).
355    Genroe(GenrouParams),
356    /// GENSAL3 — Third-order salient-pole generator (Phase 27, 3 dynamic states).
357    Gensal3(Gensal3Params),
358    // Phase 28
359    /// DERP — DER with Protection (Phase 28, 2 states).
360    Derp(DerpParams),
361    /// REGFM_D1 — WECC hybrid GFM/GFL converter (Phase 28, 8 states).
362    RegfmD1(Regfmd1Params),
363    // Wave 34
364    /// GENSAE — Salient-pole with exponential saturation (reuses GensalParams).
365    Gensae(GensalParams),
366    // Wave 36: legacy wind and distributed PV
367    /// WT1G1 — Type 1 fixed-speed wind generator (Wave 36, IBR equivalent).
368    Wt1g1(Wt1g1Params),
369    /// WT2G1 — Type 2 variable-slip wind generator (Wave 36, alias Wt1g1Params).
370    Wt2g1(Wt1g1Params),
371    /// PVD1 — Distributed PV aggregate (Wave 36, alias PvdgParams).
372    Pvd1(PvdgParams),
373    /// PVDU1 — PV distributed unit (Wave 36, alias Pvgu1Params).
374    Pvdu1(Pvgu1Params),
375}
376
377// --- GENCLS ------------------------------------------------------------------
378
379/// Classical generator model — swing equation only.
380///
381/// PSS/E params: `H  D`
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct GenclsParams {
384    /// Inertia constant (s).
385    pub h: f64,
386    /// Damping coefficient (pu).
387    pub d: f64,
388}
389
390// --- GENROU ------------------------------------------------------------------
391
392/// Round-rotor synchronous generator (full-order, PSS/E GENROU).
393///
394/// PSS/E params (14 required, Ra optional):
395/// `Td0' Td0'' Tq0' Tq0'' H D Xd Xq Xd' Xq' Xd'' Xl S(1.0) S(1.2) [Ra]`
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct GenrouParams {
398    /// d-axis open-circuit transient time constant (s).
399    pub td0_prime: f64,
400    /// d-axis open-circuit sub-transient time constant (s).
401    pub td0_pprime: f64,
402    /// q-axis open-circuit transient time constant (s).
403    pub tq0_prime: f64,
404    /// q-axis open-circuit sub-transient time constant (s).
405    pub tq0_pprime: f64,
406    /// Inertia constant (s).
407    pub h: f64,
408    /// Damping coefficient (pu).
409    pub d: f64,
410    /// d-axis synchronous reactance (pu).
411    pub xd: f64,
412    /// q-axis synchronous reactance (pu).
413    pub xq: f64,
414    /// d-axis transient reactance (pu).
415    pub xd_prime: f64,
416    /// q-axis transient reactance (pu).
417    pub xq_prime: f64,
418    /// d-axis sub-transient reactance (pu).
419    pub xd_pprime: f64,
420    /// Leakage reactance (pu).
421    pub xl: f64,
422    /// Saturation factor at 1.0 pu.
423    pub s1: f64,
424    /// Saturation factor at 1.2 pu.
425    pub s12: f64,
426    /// Armature resistance (pu) — optional trailing field.
427    #[serde(default, skip_serializing_if = "Option::is_none")]
428    pub ra: Option<f64>,
429}
430
431// --- GENSAL ------------------------------------------------------------------
432
433/// Salient-pole synchronous generator (PSS/E GENSAL).
434///
435/// PSS/E params (13 required):
436/// `Td0' Td0'' Tq0'' H D Xd Xq Xd' Xd'' Xl S(1.0) S(1.2) Xtran`
437#[derive(Debug, Clone, Serialize, Deserialize)]
438pub struct GensalParams {
439    /// d-axis open-circuit transient time constant (s).
440    pub td0_prime: f64,
441    /// d-axis open-circuit sub-transient time constant (s).
442    pub td0_pprime: f64,
443    /// q-axis open-circuit sub-transient time constant (s).
444    pub tq0_pprime: f64,
445    /// Inertia constant (s).
446    pub h: f64,
447    /// Damping coefficient (pu).
448    pub d: f64,
449    /// d-axis synchronous reactance (pu).
450    pub xd: f64,
451    /// q-axis synchronous reactance (pu).
452    pub xq: f64,
453    /// d-axis transient reactance (pu).
454    pub xd_prime: f64,
455    /// d-axis sub-transient reactance (pu).
456    pub xd_pprime: f64,
457    /// Leakage reactance (pu).
458    pub xl: f64,
459    /// Saturation factor at 1.0 pu.
460    pub s1: f64,
461    /// Saturation factor at 1.2 pu.
462    pub s12: f64,
463    /// Transient reactance for saturation (pu).
464    pub xtran: f64,
465}
466
467// --- REGCA ------------------------------------------------------------------
468
469/// IBR inner converter model (PSS/E REGC_A).
470///
471/// Models the converter as a controlled current source behind a small reactance.
472/// No swing equation — IBR is grid-following (ω = 1.0 always).
473///
474/// Key params: `Tg Xeq Imax Tfltr`
475#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct RegcaParams {
477    /// Converter current control time constant (s).
478    pub tg: f64,
479    /// Equivalent reactance for Norton shunt (pu system base) — default 0.02 pu.
480    pub x_eq: f64,
481    /// Maximum current magnitude limit (pu machine base).
482    pub imax: f64,
483    /// Voltage filter time constant (s).
484    pub tfltr: f64,
485    // --- Phase 1: PLL, current ramp, voltage dip parameters ---
486    /// PLL proportional gain.
487    pub kp_pll: f64,
488    /// PLL integral gain.
489    pub ki_pll: f64,
490    /// Current ramp rate limit (pu/s) for LVACM/current recovery.
491    pub rrpwr: f64,
492    /// Low-voltage threshold for momentary cessation (pu).
493    pub vdip: f64,
494    /// High-voltage threshold for momentary cessation (pu).
495    pub vup: f64,
496}
497
498// ---------------------------------------------------------------------------
499// Phase 11: IBR / Wind / GFM / DER generator records
500// ---------------------------------------------------------------------------
501
502/// REGCB — enhanced IBR inner converter with IP filter.
503#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct RegcbParams {
505    pub tg: f64,
506    pub x_eq: f64,
507    pub imax: f64,
508    pub tfltr: f64,
509    pub tip: f64,
510    /// PLL proportional gain.
511    pub kp_pll: f64,
512    /// PLL integral gain.
513    pub ki_pll: f64,
514}
515
516/// WT3G2U — Type 3 (DFIG) wind turbine generator.
517///
518/// Full DFIG model: RSC controls d/q rotor currents via first-order lags
519/// (`t_rotor`). Electrical torque couples through mutual inductance ratio
520/// `lm_over_ls`. PLL tracks grid voltage angle with PI gains `kpll`/`kipll`.
521#[derive(Debug, Clone, Serialize, Deserialize)]
522pub struct Wt3g2uParams {
523    /// Current command time constant (s) — lag from exciter ip/iq_ref to ip/iq_cmd.
524    pub tg: f64,
525    /// Equivalent source reactance (pu).
526    pub x_eq: f64,
527    /// Maximum current limit (pu).
528    pub imax: f64,
529    /// Voltage filter time constant (s).
530    pub tfltr: f64,
531    /// PLL proportional gain.
532    pub kpll: f64,
533    /// PLL integral gain (rad/s per pu freq error).
534    pub kipll: f64,
535    /// Rotor inertia constant (s, MWs/MVA).
536    pub h_rotor: f64,
537    /// Rotor damping coefficient.
538    pub d_rotor: f64,
539    /// Rotor current time constant (s) — RSC current control lag.
540    /// Default 0.02 s if zero or absent.
541    pub t_rotor: f64,
542    /// Mutual-to-stator inductance ratio Lm/Ls (pu).
543    /// Default 0.9 if zero or absent (typical DFIG value).
544    pub lm_over_ls: f64,
545}
546
547/// WT4G1 — Type 4 (full-converter) wind turbine generator.
548#[derive(Debug, Clone, Serialize, Deserialize)]
549pub struct Wt4g1Params {
550    pub tg: f64,
551    pub x_eq: f64,
552    pub imax: f64,
553    /// PLL proportional gain.
554    pub kp_pll: f64,
555    /// PLL integral gain.
556    pub ki_pll: f64,
557}
558
559/// REGFM_A1 — Grid-forming inverter (droop control, 9 dynamic states).
560///
561/// ## States
562///
563/// 1. δ — angle (rad)
564/// 2. ω — speed (pu)
565/// 3. i_d — d-axis current command (pu)
566/// 4. i_q — q-axis current command (pu)
567/// 5. v_filt — voltage measurement filter (pu)
568/// 6. x_pll — PLL frequency estimation state
569/// 7. x_vi — virtual impedance filter state (pu)
570/// 8. x_droop_p — P-f droop integrator (pu)
571/// 9. x_droop_q — Q-V droop integrator (pu)
572#[derive(Debug, Clone, Serialize, Deserialize)]
573pub struct RegfmA1Params {
574    pub x_eq: f64,
575    pub h: f64,
576    pub d: f64,
577    pub imax: f64,
578    /// Current injection time constant (s, default 0.02).
579    pub tg: f64,
580    /// Voltage measurement filter time constant (s, default 0.02).
581    #[serde(default = "default_gfm_tv")]
582    pub tv: f64,
583    /// PLL time constant (s, default 0.02).
584    #[serde(default = "default_gfm_tpll")]
585    pub tpll: f64,
586    /// Virtual impedance filter time constant (s, default 0.05).
587    #[serde(default = "default_gfm_tvi")]
588    pub tvi: f64,
589    /// P-f droop gain (pu power / pu freq, default 20.0 = 5% droop).
590    #[serde(default = "default_gfm_kp_droop")]
591    pub kp_droop: f64,
592    /// P-f droop integral gain (default 0.0 = pure proportional).
593    #[serde(default)]
594    pub ki_droop: f64,
595    /// Q-V droop gain (pu reactive / pu voltage, default 20.0).
596    #[serde(default = "default_gfm_kq_droop")]
597    pub kq_droop: f64,
598    /// Q-V droop integral gain (default 0.0).
599    #[serde(default)]
600    pub ki_q: f64,
601    /// Virtual resistance (pu, default 0.0).
602    #[serde(default)]
603    pub r_vi: f64,
604    /// Virtual reactance (pu, default 0.1).
605    #[serde(default = "default_gfm_x_vi")]
606    pub x_vi: f64,
607}
608
609fn default_gfm_tv() -> f64 {
610    0.02
611}
612fn default_gfm_tpll() -> f64 {
613    0.02
614}
615fn default_gfm_tvi() -> f64 {
616    0.05
617}
618fn default_gfm_kp_droop() -> f64 {
619    20.0
620}
621fn default_gfm_kq_droop() -> f64 {
622    20.0
623}
624fn default_gfm_x_vi() -> f64 {
625    0.1
626}
627
628/// REGFM_B1 — Grid-forming inverter (virtual synchronous machine, 9 dynamic states).
629///
630/// Same state structure as RegfmA1 but with VSM control philosophy:
631/// the swing equation emulates a synchronous machine rather than direct droop.
632#[derive(Debug, Clone, Serialize, Deserialize)]
633pub struct RegfmB1Params {
634    pub x_eq: f64,
635    pub h: f64,
636    pub d: f64,
637    pub imax: f64,
638    /// Current injection time constant (s, default 0.02).
639    pub tg: f64,
640    /// Voltage measurement filter time constant (s, default 0.02).
641    #[serde(default = "default_gfm_tv")]
642    pub tv: f64,
643    /// PLL time constant (s, default 0.02).
644    #[serde(default = "default_gfm_tpll")]
645    pub tpll: f64,
646    /// Virtual impedance filter time constant (s, default 0.05).
647    #[serde(default = "default_gfm_tvi")]
648    pub tvi: f64,
649    /// P-f droop gain (pu power / pu freq, default 20.0 = 5% droop).
650    #[serde(default = "default_gfm_kp_droop")]
651    pub kp_droop: f64,
652    /// P-f droop integral gain (default 0.0).
653    #[serde(default)]
654    pub ki_droop: f64,
655    /// Q-V droop gain (pu reactive / pu voltage, default 20.0).
656    #[serde(default = "default_gfm_kq_droop")]
657    pub kq_droop: f64,
658    /// Q-V droop integral gain (default 0.0).
659    #[serde(default)]
660    pub ki_q: f64,
661    /// Virtual resistance (pu, default 0.0).
662    #[serde(default)]
663    pub r_vi: f64,
664    /// Virtual reactance (pu, default 0.1).
665    #[serde(default = "default_gfm_x_vi")]
666    pub x_vi: f64,
667}
668
669/// DER_A — Distributed energy resource aggregate model.
670#[derive(Debug, Clone, Serialize, Deserialize)]
671pub struct DeraParams {
672    pub x_eq: f64,
673    pub trf: f64,
674    pub imax: f64,
675    pub trv: f64,
676}
677
678// ---------------------------------------------------------------------------
679// Phase 15: Generator variants
680// ---------------------------------------------------------------------------
681
682/// GENTRA — Third-order transient generator (3 states: δ, ω, E'q).
683///
684/// Simplified round-rotor model: classical swing + single transient EMF.
685/// No sub-transient dynamics (Xd'' = Xd', Td0'' → ∞).
686///
687/// PSS/E params: `H D Ra Xd Xd' Td0' Xq`
688#[derive(Debug, Clone, Serialize, Deserialize)]
689pub struct GentraParams {
690    /// Inertia constant (s).
691    pub h: f64,
692    /// Damping coefficient (pu).
693    pub d: f64,
694    /// Armature resistance (pu).
695    pub ra: f64,
696    /// d-axis synchronous reactance (pu).
697    pub xd: f64,
698    /// d-axis transient reactance (pu).
699    pub xd_prime: f64,
700    /// d-axis open-circuit transient time constant (s).
701    pub td0_prime: f64,
702    /// q-axis synchronous reactance (pu).
703    pub xq: f64,
704    /// Saturation factor at 1.0 pu (0.0 = unsaturated).
705    pub s1: f64,
706    /// Saturation factor at 1.2 pu (0.0 = unsaturated).
707    pub s12: f64,
708}
709
710/// REGCC — Next-gen GFM-capable converter (4 states).
711///
712/// Grid-following converter with PLL state for frequency tracking.
713#[derive(Debug, Clone, Serialize, Deserialize)]
714pub struct RegccParams {
715    /// Converter current control time constant (s).
716    pub tg: f64,
717    /// Equivalent reactance (pu system base).
718    pub x_eq: f64,
719    /// Maximum current magnitude (pu machine base).
720    pub imax: f64,
721    /// Voltage filter time constant (s).
722    pub tfltr: f64,
723    /// PLL time constant (s).
724    pub t_pll: f64,
725}
726
727/// WT4G2 — Type 4 wind generator variant GE (2 states, same as WT4G1).
728#[derive(Debug, Clone, Serialize, Deserialize)]
729pub struct Wt4g2Params {
730    pub tg: f64,
731    pub x_eq: f64,
732    pub imax: f64,
733    /// PLL proportional gain.
734    pub kp_pll: f64,
735    /// PLL integral gain.
736    pub ki_pll: f64,
737}
738
739/// DER_C / DERC — DER_A variant C (3 states: p_rec, q_rec, vfilt).
740#[derive(Debug, Clone, Serialize, Deserialize)]
741pub struct DercParams {
742    pub tp: f64,
743    pub tq: f64,
744    pub tv: f64,
745    pub mbase: f64,
746    pub lfac: f64,
747    /// Norton equivalent reactance (pu, default 0.02).
748    pub x_eq: f64,
749}
750
751// ---------------------------------------------------------------------------
752// Exciter records
753// ---------------------------------------------------------------------------
754
755/// An excitation system dynamic model record.
756#[derive(Debug, Clone, Serialize, Deserialize)]
757pub struct ExciterDyn {
758    pub bus: u32,
759    pub machine_id: String,
760    pub model: ExciterModel,
761}
762
763/// REEC_D — IBR electrical controller (drives voltage recovery).
764#[derive(Debug, Clone, Serialize, Deserialize)]
765pub struct ReecdParams {
766    /// Voltage deadband lower (pu, typically negative).
767    pub dbd1: f64,
768    /// Voltage deadband upper (pu, typically positive).
769    pub dbd2: f64,
770    /// Reactive current PI proportional gain.
771    pub kqv: f64,
772    /// Reactive current PI integral gain.
773    pub kqi: f64,
774    /// Voltage measurement filter time constant (s).
775    pub trv: f64,
776    /// Active power measurement filter time constant (s).
777    pub tp: f64,
778    /// Maximum reactive current (pu).
779    pub iqmax: f64,
780    /// Minimum reactive current (pu).
781    pub iqmin: f64,
782    /// Maximum active current (pu).
783    pub ipmax: f64,
784    /// Active power ramp rate limit (pu/s).
785    pub rrpwr: f64,
786    /// Frequency droop gain (down).
787    pub ddn: f64,
788    /// Frequency droop gain (up).
789    pub dup: f64,
790    /// Frequency deadband lower (Hz).
791    pub fdbd1: f64,
792    /// Frequency deadband upper (Hz).
793    pub fdbd2: f64,
794    /// Voltage dip threshold for momentary cessation (pu).
795    pub vdip: f64,
796    /// Voltage up threshold for momentary cessation (pu).
797    pub vup: f64,
798    /// Active power reference (pu).
799    pub pref: f64,
800    /// Max/min active power (pu).
801    pub pmax: f64,
802    pub pmin: f64,
803}
804
805/// REECCU / REECCU1 — IBR electrical controller, current-unlimited (PI + ramp).
806#[derive(Debug, Clone, Serialize, Deserialize)]
807pub struct ReeccuParams {
808    /// Voltage deadband (pu).
809    pub dbd1: f64,
810    /// Reactive current PI proportional gain.
811    pub kqv: f64,
812    /// Reactive current PI integral gain.
813    pub kqi: f64,
814    /// Voltage measurement filter time constant (s).
815    pub trv: f64,
816    /// Active power measurement filter time constant (s).
817    pub tp: f64,
818    /// Active power ramp rate limit (pu/s).
819    pub rrpwr: f64,
820    /// Voltage dip threshold for momentary cessation (pu).
821    pub vdip: f64,
822    /// Voltage up threshold for momentary cessation (pu).
823    pub vup: f64,
824    /// Active power reference (pu).
825    pub pref: f64,
826    /// Max/min active power (pu).
827    pub pmax: f64,
828    pub pmin: f64,
829}
830
831/// REXS — Excitation system with rate feedback.
832#[derive(Debug, Clone, Serialize, Deserialize)]
833pub struct RexsParams {
834    pub te: f64,
835    pub tf: f64,
836    pub ke: f64,
837    pub kf: f64,
838    pub efd1: f64,
839    pub efd2: f64,
840    pub sefd1: f64,
841    pub sefd2: f64,
842    /// Lead-lag numerator time constant (s). Zero = no lead-lag.
843    pub tc: f64,
844    /// Lead-lag denominator time constant (s). Zero = no lead-lag.
845    pub tb: f64,
846}
847
848/// Discriminated union of supported exciter models.
849#[derive(Debug, Clone, Serialize, Deserialize)]
850#[serde(tag = "type")]
851pub enum ExciterModel {
852    Exst1(Exst1Params),
853    Esst3a(Esst3aParams),
854    Esdc2a(Esdc2aParams),
855    Exdc2(Exdc2Params),
856    Ieeex1(Ieeex1Params),
857    /// SEXS — Simplified Exciter (common in planning studies).
858    Sexs(SexsParams),
859    /// IEEET1 — IEEE Type 1 rotating exciter (classic 1968 AVR).
860    Ieeet1(Ieeet1Params),
861    /// SCRX — Simplified Bus-Fed Static Exciter.
862    Scrx(ScrxParams),
863    /// REEC_A — IBR electrical controller (maps to exciter slot).
864    Reeca(ReecaParams),
865    Esst1a(Esst1aParams),
866    Exac1(Exac1Params),
867    /// ESAC1A — same params as EXAC1, different model tag.
868    Esac1a(Exac1Params),
869    Esac7b(Esac7bParams),
870    Esst4b(Esst4bParams),
871    /// REEC_D — IBR electrical controller (voltage recovery).
872    Reecd(ReecdParams),
873    /// REECCU — IBR electrical controller (curtailment).
874    Reeccu(ReeccuParams),
875    /// REXS — Excitation system with rate feedback.
876    Rexs(RexsParams),
877    /// ESAC2A — AC2A high-initial-response rotating exciter (Phase 14).
878    Esac2a(Esac2aParams),
879    /// ESAC5A — AC5A simplified brushless exciter (Phase 14).
880    Esac5a(Esac5aParams),
881    /// ESST5B — IEEE ST5B static exciter (Phase 15, 3 states).
882    Esst5b(Esst5bParams),
883    /// EXAC4 / AC4A — IEEE AC4A controlled-rectifier exciter (Phase 15, 2 states).
884    Exac4(Exac4Params),
885    // Phase 17
886    /// ESST6B — IEEE ST6B Static Exciter (Phase 17).
887    Esst6b(Esst6bParams),
888    /// ESST7B — IEEE ST7B Static Exciter (Phase 17).
889    Esst7b(Esst7bParams),
890    /// ESAC6A — AC6A Rotating Exciter (Phase 17).
891    Esac6a(Esac6aParams),
892    /// ESDC1A — DC1A Rotating Exciter (Phase 17).
893    Esdc1a(Esdc1aParams),
894    /// EXST2 — Static Exciter Type ST2 (Phase 17).
895    Exst2(Exst2Params),
896    /// AC8B — IEEE AC8B High Initial Response Exciter (Phase 17).
897    Ac8b(Ac8bParams),
898    /// BBSEX1 — Bus-Branch Static Exciter 1 (Phase 17).
899    Bbsex1(Bbsex1Params),
900    /// IEEET3 — IEEE Type 3 Rotating Exciter (Phase 17).
901    Ieeet3(Ieeet3Params),
902    // Phase 19
903    /// WT3E1 — Type 3 Wind Electrical Controller (Phase 19).
904    Wt3e1(Wt3e1Params),
905    /// WT3E2 — Type 3 Wind Electrical Controller Variant 2 (Phase 19).
906    Wt3e2(Wt3e2Params),
907    /// WT4E1 — Type 4 Wind Electrical Controller (Phase 19).
908    Wt4e1(Wt4e1Params),
909    /// WT4E2 — Type 4 Wind Electrical Controller Variant 2 (Phase 19).
910    Wt4e2(Wt4e1Params),
911    /// REPCB — REPCA Variant B (Phase 19).
912    Repcb(RepcbParams),
913    /// REPCC — REPCA Variant C (Phase 19, same params as REPCB).
914    Repcc(RepcbParams),
915    // Phase 21
916    /// EXST3 — Static Exciter Type ST3 (Phase 21).
917    Exst3(Exst3Params),
918    /// CBUFR — Buffer-Frequency-Regulated BESS (Phase 21).
919    Cbufr(CbufrParams),
920    /// CBUFD — Buffer-Frequency-Dependent BESS (Phase 21).
921    Cbufd(CbufdParams),
922    // Phase 22
923    /// PVEU1 — WECC 1st-gen PV electrical control unit (Phase 22, maps to exciter slot).
924    Pveu1(Pveu1Params),
925    // Phase 23
926    /// IEEET2 — IEEE Type 2 rotating-machine exciter (Phase 23).
927    Ieeet2(Ieeet2Params),
928    /// EXAC2 — IEEE AC2A high initial response rotating exciter (Phase 23).
929    Exac2(Exac2Params),
930    /// EXAC3 — IEEE AC3A controlled-rectifier exciter (Phase 23).
931    Exac3(Exac3Params),
932    /// ESAC3A — IEEE 421.5-2005 AC3A exciter update (Phase 23).
933    Esac3a(Esac3aParams),
934    /// ESST8C — IEEE 421.5-2016 ST8C static exciter (Phase 23).
935    Esst8c(Esst8cParams),
936    /// ESST9B — IEEE 421.5-2016 ST9B static exciter (Phase 23).
937    Esst9b(Esst9bParams),
938    /// ESST10C — IEEE 421.5-2016 ST10C static exciter (Phase 23).
939    Esst10c(Esst10cParams),
940    /// ESDC3A — IEEE 421.5-2005 DC3A rotating-machine exciter (Phase 23).
941    Esdc3a(Esdc3aParams),
942    // Wave 32
943    /// EXDC1 — IEEE Type DC1A rotating-machine exciter (legacy 13-param form).
944    Exdc1(Exdc1Params),
945    /// ESST2A — IEEE 421.5-2016 Type ST2A static exciter.
946    Esst2a(Esst2aParams),
947    // Wave 33
948    /// EXDC3 — PSS/E non-continuously-acting (relay-type) DC exciter (legacy).
949    Exdc3(Exdc3Params),
950    // Phase 27
951    /// WT3C2 — Type 3 wind electrical controller variant 2 (Phase 27, same params as WT3E1).
952    Wt3c2(Wt3e1Params),
953    // Wave 35
954    /// ESAC7C — IEEE 421.5-2016 AC7C exciter (same structure as ESAC7B, C-series).
955    Esac7c(Esac7cParams),
956    /// ESDC4C — IEEE 421.5-2016 DC4C exciter (PID + DC rotating, 3 states).
957    Esdc4c(Esdc4cParams),
958    // Wave 36: new REEC variants
959    /// REECBU1 — REEC variant B for Unit (Wave 36, alias ReeccuParams).
960    Reecbu1(ReeccuParams),
961    /// REECE — Enhanced REECA with voltage ride-through (Wave 36, alias ReecaParams).
962    Reece(ReecaParams),
963    /// REECEU1 — REECE curtailment variant (Wave 36, alias ReeccuParams).
964    Reeceu1(ReeccuParams),
965    // Wave 37: IEEE 421.5-2016 C-series AC exciters
966    /// ESAC8C — IEEE 421.5-2016 AC8C high-initial-response exciter (alias AC8B structure).
967    Esac8c(Ac8bParams),
968    /// ESAC9C — IEEE 421.5-2016 AC9C exciter (alias ESAC7B structure).
969    Esac9c(Esac7bParams),
970    /// ESAC10C — IEEE 421.5-2016 AC10C exciter (alias ESAC7C structure).
971    Esac10c(Esac7cParams),
972    /// ESAC11C — IEEE 421.5-2016 AC11C high-bandwidth AC exciter (alias AC8B structure).
973    Esac11c(Ac8bParams),
974}
975
976// --- EXST1 ------------------------------------------------------------------
977
978/// Static exciter (PSS/E EXST1).
979///
980/// PSS/E params (12 required; klr, ilr optional):
981/// `TR VIMAX VIMIN TC TB KA TA VRMAX VRMIN KC KF TF [KLR ILR]`
982#[derive(Debug, Clone, Serialize, Deserialize)]
983pub struct Exst1Params {
984    pub tr: f64,
985    pub vimax: f64,
986    pub vimin: f64,
987    pub tc: f64,
988    pub tb: f64,
989    pub ka: f64,
990    pub ta: f64,
991    pub vrmax: f64,
992    pub vrmin: f64,
993    pub kc: f64,
994    pub kf: f64,
995    pub tf: f64,
996    #[serde(default, skip_serializing_if = "Option::is_none")]
997    pub klr: Option<f64>,
998    #[serde(default, skip_serializing_if = "Option::is_none")]
999    pub ilr: Option<f64>,
1000}
1001
1002// --- ESST3A -----------------------------------------------------------------
1003
1004/// IEEE type ST3A static exciter (PSS/E ESST3A).
1005///
1006/// PSS/E params (14 required):
1007/// `TR VIMAX VIMIN KM TC TB KA TA VRMAX VRMIN KG KP KI VBMAX`
1008#[derive(Debug, Clone, Serialize, Deserialize)]
1009pub struct Esst3aParams {
1010    pub tr: f64,
1011    pub vimax: f64,
1012    pub vimin: f64,
1013    pub km: f64,
1014    pub tc: f64,
1015    pub tb: f64,
1016    pub ka: f64,
1017    pub ta: f64,
1018    pub vrmax: f64,
1019    pub vrmin: f64,
1020    pub kg: f64,
1021    pub kp: f64,
1022    pub ki: f64,
1023    pub vbmax: f64,
1024}
1025
1026// --- ESDC2A -----------------------------------------------------------------
1027
1028/// IEEE type DC2A exciter (PSS/E ESDC2A).
1029///
1030/// PSS/E params (12 required):
1031/// `TR KA TA TB TC VRMAX VRMIN KE TE KF TF1 SWITCH`
1032#[derive(Debug, Clone, Serialize, Deserialize)]
1033pub struct Esdc2aParams {
1034    pub tr: f64,
1035    pub ka: f64,
1036    pub ta: f64,
1037    pub tb: f64,
1038    pub tc: f64,
1039    pub vrmax: f64,
1040    pub vrmin: f64,
1041    pub ke: f64,
1042    pub te: f64,
1043    pub kf: f64,
1044    pub tf1: f64,
1045    pub switch_: f64,
1046}
1047
1048// --- EXDC2 ------------------------------------------------------------------
1049
1050/// IEEE type DC2 exciter (PSS/E EXDC2) — used in Kundur 4-machine system.
1051///
1052/// PSS/E params (12 required; E1, SE1, E2, SE2 optional):
1053/// `TR KA TA TB TC VRMAX VRMIN KE TE KF TF1 SWITCH [E1 SE1 E2 SE2]`
1054#[derive(Debug, Clone, Serialize, Deserialize)]
1055pub struct Exdc2Params {
1056    pub tr: f64,
1057    pub ka: f64,
1058    pub ta: f64,
1059    pub tb: f64,
1060    pub tc: f64,
1061    pub vrmax: f64,
1062    pub vrmin: f64,
1063    pub ke: f64,
1064    pub te: f64,
1065    pub kf: f64,
1066    pub tf1: f64,
1067    pub switch_: f64,
1068    #[serde(default, skip_serializing_if = "Option::is_none")]
1069    pub e1: Option<f64>,
1070    #[serde(default, skip_serializing_if = "Option::is_none")]
1071    pub se1: Option<f64>,
1072    #[serde(default, skip_serializing_if = "Option::is_none")]
1073    pub e2: Option<f64>,
1074    #[serde(default, skip_serializing_if = "Option::is_none")]
1075    pub se2: Option<f64>,
1076}
1077
1078// --- IEEEX1 -----------------------------------------------------------------
1079
1080/// IEEE type AC1A exciter (PSS/E IEEEX1 / IEEEXC1).
1081///
1082/// PSS/E params (12 required; E1, SE1, E2, SE2 optional):
1083/// `TR KA TA TB TC VRMAX VRMIN KE TE KF TF AEX BEX [E1 SE1 E2 SE2]`
1084#[derive(Debug, Clone, Serialize, Deserialize)]
1085pub struct Ieeex1Params {
1086    pub tr: f64,
1087    pub ka: f64,
1088    pub ta: f64,
1089    pub tb: f64,
1090    pub tc: f64,
1091    pub vrmax: f64,
1092    pub vrmin: f64,
1093    pub ke: f64,
1094    pub te: f64,
1095    pub kf: f64,
1096    pub tf: f64,
1097    pub aex: f64,
1098    pub bex: f64,
1099    #[serde(default, skip_serializing_if = "Option::is_none")]
1100    pub e1: Option<f64>,
1101    #[serde(default, skip_serializing_if = "Option::is_none")]
1102    pub se1: Option<f64>,
1103    #[serde(default, skip_serializing_if = "Option::is_none")]
1104    pub e2: Option<f64>,
1105    #[serde(default, skip_serializing_if = "Option::is_none")]
1106    pub se2: Option<f64>,
1107}
1108
1109// --- SEXS -------------------------------------------------------------------
1110
1111/// Simplified Exciter (PSS/E SEXS) -- very common in planning studies.
1112///
1113/// This is a standard planning-level model for use when detailed AVR test data
1114/// is unavailable.  For precise transient stability studies, upgrade to a
1115/// detailed exciter model (AC5A, ESST4B, or EXAC1) when field test data is
1116/// available.  See the module-level documentation for the full model mapping.
1117///
1118/// PSS/E params: `TB TC K TE EMIN EMAX`
1119#[derive(Debug, Clone, Serialize, Deserialize)]
1120pub struct SexsParams {
1121    /// Lead-lag denominator time constant (s).
1122    pub tb: f64,
1123    /// Lead-lag numerator time constant (s).
1124    pub tc: f64,
1125    /// Exciter gain.
1126    pub k: f64,
1127    /// Exciter time constant (s).
1128    pub te: f64,
1129    /// Minimum field voltage limit (pu).
1130    pub emin: f64,
1131    /// Maximum field voltage limit (pu).
1132    pub emax: f64,
1133}
1134
1135// --- IEEET1 -----------------------------------------------------------------
1136
1137/// IEEE Type 1 Rotating Exciter (PSS/E IEEET1) — classic 1968 AVR.
1138///
1139/// PSS/E params: `TR KA TA KE TE KF TF E1 SE1 E2 SE2 [VRMAX VRMIN]`
1140#[derive(Debug, Clone, Serialize, Deserialize)]
1141pub struct Ieeet1Params {
1142    pub tr: f64,
1143    pub ka: f64,
1144    pub ta: f64,
1145    pub ke: f64,
1146    pub te: f64,
1147    pub kf: f64,
1148    pub tf: f64,
1149    /// Saturation function reference point 1 (pu).
1150    pub e1: f64,
1151    /// Saturation factor at E1.
1152    pub se1: f64,
1153    /// Saturation function reference point 2 (pu).
1154    pub e2: f64,
1155    /// Saturation factor at E2.
1156    pub se2: f64,
1157    #[serde(default, skip_serializing_if = "Option::is_none")]
1158    pub vrmax: Option<f64>,
1159    #[serde(default, skip_serializing_if = "Option::is_none")]
1160    pub vrmin: Option<f64>,
1161}
1162
1163// --- SCRX -------------------------------------------------------------------
1164
1165/// Simplified Bus-Fed Static Exciter (PSS/E SCRX).
1166///
1167/// This is a standard planning-level model for static exciters when detailed
1168/// parameters are unavailable.  For precise transient stability studies,
1169/// upgrade to ESST4B or ESST3A when static exciter field test data is available.
1170/// See the module-level documentation for the full model mapping.
1171///
1172/// PSS/E params: `TR K TE EMIN EMAX [Rcrfd]`
1173#[derive(Debug, Clone, Serialize, Deserialize)]
1174pub struct ScrxParams {
1175    /// Transducer time constant (s).
1176    pub tr: f64,
1177    /// Exciter gain.
1178    pub k: f64,
1179    /// Exciter time constant (s).
1180    pub te: f64,
1181    /// Minimum field voltage (pu).
1182    pub emin: f64,
1183    /// Maximum field voltage (pu).
1184    pub emax: f64,
1185    /// Ratio of exciter to field current (optional).
1186    #[serde(default, skip_serializing_if = "Option::is_none")]
1187    pub rcrfd: Option<f64>,
1188}
1189
1190// --- REECA ------------------------------------------------------------------
1191
1192/// REEC_A -- IBR electrical controller (maps to exciter slot, Phase 8 simplified).
1193///
1194/// Simplified to voltage-following / constant power factor mode.
1195/// Full REEC_A includes Kqv droop, deadband, FRT limits.
1196///
1197/// This is a standard planning-level model for IBR electrical controllers.
1198/// For studies involving IBR fault ride-through, voltage regulation, or reactive
1199/// power control, use the full REEC_A model with Kqv droop and deadband parameters.
1200/// See the module-level documentation for the full model mapping.
1201///
1202/// Key params: `Trv Kqv Tp Kqp Kqi Vref0 Dbd1 Dbd2 Vdip Vup Iqh1 Iql1 Qmax Qmin`
1203#[derive(Debug, Clone, Serialize, Deserialize)]
1204pub struct ReecaParams {
1205    /// Voltage filter time constant (s).
1206    pub trv: f64,
1207    /// Reactive power control voltage droop gain.
1208    pub kqv: f64,
1209    /// Active current filter time constant (s).
1210    pub tp: f64,
1211    /// Reactive power proportional gain.
1212    pub kqp: f64,
1213    /// Reactive power integral gain (1/s).
1214    pub kqi: f64,
1215    /// Initial voltage reference (pu).
1216    pub vref0: f64,
1217    /// Voltage deadband lower limit (pu, negative).
1218    pub dbd1: f64,
1219    /// Voltage deadband upper limit (pu, positive).
1220    pub dbd2: f64,
1221    /// Low voltage protection threshold (pu).
1222    pub vdip: f64,
1223    /// High voltage protection threshold (pu).
1224    pub vup: f64,
1225    /// Maximum reactive current (pu).
1226    pub iqh1: f64,
1227    /// Minimum reactive current (pu).
1228    pub iql1: f64,
1229    /// Maximum reactive power (pu).
1230    pub qmax: f64,
1231    /// Minimum reactive power (pu).
1232    pub qmin: f64,
1233    /// Active power measurement filter time constant (s).
1234    pub tpfilt: f64,
1235    /// Reactive power measurement filter time constant (s).
1236    pub tqfilt: f64,
1237    /// Active power ramp rate up limit (pu/s).
1238    pub rrpwr: f64,
1239    /// Active power ramp rate down limit (pu/s, negative).
1240    pub rrpwr_dn: f64,
1241    /// Current priority flag: 0 = Q priority, 1 = P priority.
1242    pub pqflag: i32,
1243    /// Maximum total current magnitude (pu).
1244    pub imax: f64,
1245    /// Maximum active current (pu, used with PQFLAG=1).
1246    pub ipmax: f64,
1247    /// VDL1 breakpoints: voltage-dependent reactive current limit (Vq, Iq) pairs.
1248    /// If all zeros or empty, flat iqh1/iql1 limits apply.
1249    pub vdl1: [(f64, f64); 4],
1250    /// VDL2 breakpoints: voltage-dependent active current limit (Vp, Ip) pairs.
1251    /// If all zeros or empty, flat ipmax limit applies.
1252    pub vdl2: [(f64, f64); 4],
1253}
1254
1255// ---------------------------------------------------------------------------
1256// Governor records
1257// ---------------------------------------------------------------------------
1258
1259/// A turbine-governor dynamic model record.
1260#[derive(Debug, Clone, Serialize, Deserialize)]
1261pub struct GovernorDyn {
1262    pub bus: u32,
1263    pub machine_id: String,
1264    pub model: GovernorModel,
1265}
1266
1267/// REPCD — IBR plant power controller (drives active power).
1268#[derive(Debug, Clone, Serialize, Deserialize)]
1269pub struct RepdcParams {
1270    pub tp: f64,
1271    pub kpg: f64,
1272    pub kig: f64,
1273    pub pmax: f64,
1274    pub pmin: f64,
1275    pub tlag: f64,
1276}
1277
1278/// WT3T1 — Type 3 wind turbine drive train.
1279#[derive(Debug, Clone, Serialize, Deserialize)]
1280pub struct Wt3t1Params {
1281    pub h: f64,
1282    pub damp: f64,
1283    pub ka: f64,
1284    pub theta: f64,
1285}
1286
1287/// WT3P1 — Type 3 wind turbine pitch controller.
1288#[derive(Debug, Clone, Serialize, Deserialize)]
1289pub struct Wt3p1Params {
1290    pub tp: f64,
1291    pub kpp: f64,
1292    pub kip: f64,
1293    pub pmax: f64,
1294    pub pmin: f64,
1295}
1296
1297/// GGOV1D — Enhanced GGOV1 with droop deadband.
1298#[derive(Debug, Clone, Serialize, Deserialize)]
1299pub struct Ggov1dParams {
1300    pub r: f64,
1301    pub t_pelec: f64,
1302    pub maxerr: f64,
1303    pub minerr: f64,
1304    pub kpgov: f64,
1305    pub kigov: f64,
1306    pub kdgov: f64,
1307    pub fdbd1: f64,
1308    pub fdbd2: f64,
1309    pub pmax: f64,
1310    pub pmin: f64,
1311    pub tact: f64,
1312    pub kturb: f64,
1313    pub wfnl: f64,
1314    pub tb: f64,
1315    pub tc: f64,
1316    pub flag: f64,
1317    pub teng: f64,
1318    pub tfload: f64,
1319    pub kpload: f64,
1320    pub kiload: f64,
1321    pub ldref: f64,
1322    pub dm: f64,
1323    pub ropen: f64,
1324    pub rclose: f64,
1325    pub kimw: f64,
1326    pub pmwset: f64,
1327    pub aset: f64,
1328    pub ka: f64,
1329    pub ta: f64,
1330    pub db: f64,
1331    pub tsa: f64,
1332    pub tsb: f64,
1333    pub rup: f64,
1334    pub rdown: f64,
1335    pub load_ref: f64,
1336}
1337
1338/// TGOV1N / TGOV1NDB — TGOV1 with null deadband.
1339#[derive(Debug, Clone, Serialize, Deserialize)]
1340pub struct Tgov1nParams {
1341    pub r: f64,
1342    pub dt: f64,
1343    pub t1: f64,
1344    pub vmax: f64,
1345    pub vmin: f64,
1346    pub t2: f64,
1347    pub t3: f64,
1348    pub d: f64,
1349    pub db: f64,
1350}
1351
1352/// Discriminated union of supported governor models.
1353#[derive(Debug, Clone, Serialize, Deserialize)]
1354#[serde(tag = "type")]
1355pub enum GovernorModel {
1356    Tgov1(Tgov1Params),
1357    Ieeeg1(Ieeeg1Params),
1358    Ggov1(Ggov1Params),
1359    /// GAST — Gas Turbine Simplified governor (Rowen model).
1360    Gast(GastParams),
1361    /// REPC_A — IBR plant controller (maps to governor slot, simplified Phase 8).
1362    Repca(RepcaParams),
1363    Hygov(HygovParams),
1364    Hygovd(HygovdParams),
1365    Tgov1d(Tgov1dParams),
1366    Ieeeg1d(Ieeeg1dParams),
1367    /// WSIEG1 — WECC IEEEG1 (same structure as Ieeeg1).
1368    Wsieg1(Ieeeg1Params),
1369    Ieeeg2(Ieeeg2Params),
1370    /// REPCD — IBR plant power controller.
1371    Repcd(RepdcParams),
1372    /// WT3T1 — Type 3 wind drive train.
1373    Wt3t1(Wt3t1Params),
1374    /// WT3P1 — Type 3 wind pitch controller.
1375    Wt3p1(Wt3p1Params),
1376    /// GGOV1D — Enhanced GGOV1 with droop deadband.
1377    Ggov1d(Ggov1dParams),
1378    /// TGOV1N / TGOV1NDB — TGOV1 with null deadband.
1379    Tgov1n(Tgov1nParams),
1380    /// CBEST — PSS/E native BESS model (Phase 14).
1381    Cbest(CbestParams),
1382    /// CHAAUT — BESS active power controller with frequency droop (Phase 14).
1383    Chaaut(ChaautParams),
1384    /// PIDGOV — PID governor for any prime mover (Phase 14).
1385    Pidgov(PidgovParams),
1386    /// DEGOV1 — Diesel governor Type 1 (Phase 14).
1387    Degov1(Degov1Params),
1388    /// TGOV5 — Multi-reheat steam governor HP+IP (Phase 15, 4 states).
1389    Tgov5(Tgov5Params),
1390    /// GAST2A — Advanced Rowen gas turbine with ambient temperature (Phase 15, 4 states).
1391    Gast2a(Gast2aParams),
1392    // Phase 18
1393    /// H6E — Hydro Governor 6 Elements (Phase 18).
1394    H6e(H6eParams),
1395    /// WSHYGP — Wind-Synchronous Hydro Governor+Pitch (Phase 18).
1396    Wshygp(WshygpParams),
1397    // Phase 25
1398    /// GGOV2 — GE GGOV1 variant 2 with supplemental load reference input (Phase 25, 4 states).
1399    Ggov2(Ggov2Params),
1400    /// GGOV3 — GE GGOV1 variant 3 with washout filter (Phase 25, 4 states).
1401    Ggov3(Ggov3Params),
1402    /// WPIDHY — Woodward PID Hydro Governor (Phase 25, 4 states).
1403    Wpidhy(WpidhyParams),
1404    /// H6B — Six-State Hydro Governor Variant B (Phase 25, 5 states).
1405    H6b(H6bParams),
1406    /// WSHYDD — WSHYGP with speed deadband (Phase 25, 4 states).
1407    Wshydd(WshyddParams),
1408    // Phase 28
1409    /// REPCGFM_C1 — GFM plant Volt/Var controller (Phase 28, 3 states).
1410    Repcgfmc1(Repcgfmc1Params),
1411    /// WTDTA1 — Wind turbine two-mass drive-train (Phase 28, 2 states).
1412    Wtdta1(Wtdta1Params),
1413    /// WTARA1 — Wind turbine aerodynamic aggregation (Phase 28, 2 states).
1414    Wtara1(Wtara1Params),
1415    /// WTPTA1 — Wind turbine pitch angle control (Phase 28, 2 states).
1416    Wtpta1(Wtpta1Params),
1417    // Wave 34
1418    /// IEESGO — IEEE Standard Governor (5-state steam turbine).
1419    Ieesgo(IeesgoParams),
1420    /// WTTQA1 — WECC Type 2 Wind Torque Controller (2 states).
1421    Wttqa1(Wttqa1Params),
1422    // Wave 35
1423    /// HYGOV4 — Hydro Governor with Surge Tank (5 states).
1424    Hygov4(Hygov4Params),
1425    /// WEHGOV — WECC Enhanced Hydro Governor (4 states).
1426    Wehgov(WehgovParams),
1427    /// IEEEG3 — IEEE Type G3 Hydro Governor (3 states).
1428    Ieeeg3(Ieeeg3Params),
1429    /// IEEEG4 — IEEE Type G4 Hydro Governor (3 states, lead-lag form).
1430    Ieeeg4(Ieeeg4Params),
1431    // Wave 36: combined cycle + steam + wind governors
1432    /// GOVCT1 — Single-shaft combined cycle turbine governor (Wave 36, 5 states).
1433    Govct1(Govct1Params),
1434    /// GOVCT2 — Two-shaft combined cycle turbine governor (7 states: 5 GT + HRSG + ST).
1435    Govct2(Govct2Params),
1436    /// TGOV3 — TGOV1 variant with two-reheat steam turbine (Wave 36, 3 states).
1437    Tgov3(Tgov3Params),
1438    /// TGOV4 — TGOV1 with IP/LP split (Wave 36, alias Tgov3Params).
1439    Tgov4(Tgov3Params),
1440    /// WT2E1 — Type 2 wind electrical controller (Wave 36, 2 states).
1441    Wt2e1(Wt2e1Params),
1442    /// WT12T1 — Type 1/2 wind drive train (Wave 36, alias Wt3t1Params).
1443    Wt12t1(Wt3t1Params),
1444    /// WT12A1 — Type 1/2 wind aerodynamics (Wave 36, alias Wt3p1Params).
1445    Wt12a1(Wt3p1Params),
1446    /// WTAERO — Full aerodynamic Cp(λ,β) wind turbine model (B4).
1447    Wtaero(WtaeroParams),
1448}
1449
1450// --- TGOV1 ------------------------------------------------------------------
1451
1452/// Steam turbine-governor (PSS/E TGOV1).
1453///
1454/// PSS/E params (6 required; DT optional):
1455/// `R T1 VMAX VMIN T2 T3 [DT]`
1456#[derive(Debug, Clone, Serialize, Deserialize)]
1457pub struct Tgov1Params {
1458    pub r: f64,
1459    pub t1: f64,
1460    pub vmax: f64,
1461    pub vmin: f64,
1462    pub t2: f64,
1463    pub t3: f64,
1464    #[serde(default, skip_serializing_if = "Option::is_none")]
1465    pub dt: Option<f64>,
1466}
1467
1468// --- IEEEG1 -----------------------------------------------------------------
1469
1470/// IEEE type G1 turbine-governor (PSS/E IEEEG1).
1471///
1472/// PSS/E params (9 required; K1..K8, T5..T7 optional):
1473/// `K T1 T2 T3 UO UC PMAX PMIN T4 [K1 K2 T5 K3 K4 T6 K5 K6 T7 K7 K8]`
1474#[derive(Debug, Clone, Serialize, Deserialize)]
1475pub struct Ieeeg1Params {
1476    pub k: f64,
1477    pub t1: f64,
1478    pub t2: f64,
1479    pub t3: f64,
1480    pub uo: f64,
1481    pub uc: f64,
1482    pub pmax: f64,
1483    pub pmin: f64,
1484    pub t4: f64,
1485    #[serde(default, skip_serializing_if = "Option::is_none")]
1486    pub k1: Option<f64>,
1487    #[serde(default, skip_serializing_if = "Option::is_none")]
1488    pub k2: Option<f64>,
1489    #[serde(default, skip_serializing_if = "Option::is_none")]
1490    pub t5: Option<f64>,
1491    #[serde(default, skip_serializing_if = "Option::is_none")]
1492    pub k3: Option<f64>,
1493    #[serde(default, skip_serializing_if = "Option::is_none")]
1494    pub k4: Option<f64>,
1495    #[serde(default, skip_serializing_if = "Option::is_none")]
1496    pub t6: Option<f64>,
1497    #[serde(default, skip_serializing_if = "Option::is_none")]
1498    pub k5: Option<f64>,
1499    #[serde(default, skip_serializing_if = "Option::is_none")]
1500    pub k6: Option<f64>,
1501    #[serde(default, skip_serializing_if = "Option::is_none")]
1502    pub t7: Option<f64>,
1503    #[serde(default, skip_serializing_if = "Option::is_none")]
1504    pub k7: Option<f64>,
1505    #[serde(default, skip_serializing_if = "Option::is_none")]
1506    pub k8: Option<f64>,
1507}
1508
1509// ---------------------------------------------------------------------------
1510// PSS records
1511// ---------------------------------------------------------------------------
1512
1513/// A power system stabilizer dynamic model record.
1514#[derive(Debug, Clone, Serialize, Deserialize)]
1515pub struct PssDyn {
1516    pub bus: u32,
1517    pub machine_id: String,
1518    pub model: PssModel,
1519}
1520
1521/// Discriminated union of supported PSS models.
1522#[derive(Debug, Clone, Serialize, Deserialize)]
1523#[serde(tag = "type")]
1524pub enum PssModel {
1525    Ieeest(IeeestParams),
1526    St2cut(St2cutParams),
1527    Pss2a(Pss2aParams),
1528    Pss2b(Pss2bParams),
1529    Stab1(Stab1Params),
1530    /// PSS1A — Single-input single lead-lag PSS (Phase 14).
1531    Pss1a(Pss1aParams),
1532    /// STAB2A — WSCC stabilizer variant A, double lead-lag (Phase 15, 3 states).
1533    Stab2a(Stab2aParams),
1534    /// PSS4B — Four-band multi-frequency PSS (Phase 15, 4 states).
1535    Pss4b(Pss4bParams),
1536    // Phase 18
1537    /// STAB3 — Three-Band PSS (Phase 18).
1538    Stab3(Stab3Params),
1539    /// PSS3B — Three-Input Power System Stabilizer (Phase 18).
1540    Pss3b(Pss3bParams),
1541    // Phase 24
1542    /// PSS2C — PSS2B with ramp-tracking filter on input 2 (Phase 24).
1543    Pss2c(Pss2cParams),
1544    /// PSS5 — Five-band multi-frequency PSS (Phase 24).
1545    Pss5(Pss5Params),
1546    /// PSS6C — Six-input multi-band PSS (Phase 24).
1547    Pss6c(Pss6cParams),
1548    /// PSSSB — WSCC/BPA simple PSS vendor variant B (Phase 24).
1549    Psssb(PsssbParams),
1550    /// STAB4 — WSCC stabilizer variant 4 (Phase 24).
1551    Stab4(Stab4Params),
1552    /// STAB5 — WSCC stabilizer variant 5 (Phase 24).
1553    Stab5(Stab5Params),
1554    // Wave 35
1555    /// PSS3C — IEEE 421.5-2016 3-band PSS (alias to PSS3B params/dynamics).
1556    Pss3c(Pss3bParams),
1557    /// PSS4C — IEEE 421.5-2016 4-band PSS (alias to PSS4B params/dynamics).
1558    Pss4c(Pss4bParams),
1559    /// PSS5C — IEEE 421.5-2016 5-band PSS (alias to PSS5 params/dynamics).
1560    Pss5c(Pss5Params),
1561    /// PSS7C — IEEE 421.5-2016 7-input multi-band PSS (2 states: washout + lead-lag).
1562    Pss7c(Pss7cParams),
1563}
1564
1565// --- IEEEST -----------------------------------------------------------------
1566
1567/// IEEE standard PSS (PSS/E IEEEST).
1568///
1569/// PSS/E params (13 required; LSMAX, LSMIN, VCU, VCL optional):
1570/// `A1 A2 A3 A4 A5 A6 T1 T2 T3 T4 T5 T6 KS [LSMAX LSMIN VCU VCL]`
1571#[derive(Debug, Clone, Serialize, Deserialize)]
1572pub struct IeeestParams {
1573    pub a1: f64,
1574    pub a2: f64,
1575    pub a3: f64,
1576    pub a4: f64,
1577    pub a5: f64,
1578    pub a6: f64,
1579    pub t1: f64,
1580    pub t2: f64,
1581    pub t3: f64,
1582    pub t4: f64,
1583    pub t5: f64,
1584    pub t6: f64,
1585    pub ks: f64,
1586    #[serde(default, skip_serializing_if = "Option::is_none")]
1587    pub lsmax: Option<f64>,
1588    #[serde(default, skip_serializing_if = "Option::is_none")]
1589    pub lsmin: Option<f64>,
1590    #[serde(default, skip_serializing_if = "Option::is_none")]
1591    pub vcu: Option<f64>,
1592    #[serde(default, skip_serializing_if = "Option::is_none")]
1593    pub vcl: Option<f64>,
1594}
1595
1596// --- GGOV1 ------------------------------------------------------------------
1597
1598/// General-purpose turbine-governor model (PSS/E GGOV1).
1599///
1600/// Simplified 3-state implementation capturing the key dynamics of gas turbines
1601/// and combined-cycle units.  Covers ~40% of ERCOT/SPP generation fleet.
1602///
1603/// PSS/E params (17 required):
1604/// `R RSELECT TPELEC MAXERR MINERR KPGOV KIGOV KDGOV TDGOV VMAX VMIN TSA FSR TSB TSE IANG KCMF KTURB WFNL TB TC TRATE FLAG`
1605#[derive(Debug, Clone, Serialize, Deserialize)]
1606pub struct Ggov1Params {
1607    /// Droop (pu) — speed regulation.
1608    pub r: f64,
1609    /// Electrical power transducer time constant (s).
1610    pub tpelec: f64,
1611    /// Maximum governor output (pu on machine base).
1612    pub vmax: f64,
1613    /// Minimum governor output (pu on machine base).
1614    pub vmin: f64,
1615    /// Proportional governor gain.
1616    pub kpgov: f64,
1617    /// Integral governor gain (1/s).
1618    pub kigov: f64,
1619    /// Turbine gain.
1620    pub kturb: f64,
1621    /// No-load fuel flow (pu on machine base).
1622    pub wfnl: f64,
1623    /// Turbine reheat lead time constant (s).
1624    pub tb: f64,
1625    /// Turbine reheat lag time constant (s).
1626    pub tc: f64,
1627    /// Rated turbine power (MW, 0 = use machine mbase).
1628    #[serde(default, skip_serializing_if = "Option::is_none")]
1629    pub trate: Option<f64>,
1630    /// Load reference set point (pu) — initialized to Pm0.
1631    #[serde(default, skip_serializing_if = "Option::is_none")]
1632    pub ldref: Option<f64>,
1633    /// Damping constant (pu).
1634    #[serde(default, skip_serializing_if = "Option::is_none")]
1635    pub dm: Option<f64>,
1636}
1637
1638// --- GAST -------------------------------------------------------------------
1639
1640/// Gas Turbine Simplified governor (PSS/E GAST) -- Rowen model.
1641///
1642/// This is a standard planning-level model for gas turbines when detailed
1643/// performance test data is unavailable.  For precise transient stability
1644/// studies, upgrade to GAST2A (with ambient temperature effects) or GGOV1
1645/// (with detailed PID governor) when gas turbine test data is available.
1646/// See the module-level documentation for the full model mapping.
1647///
1648/// PSS/E params: `R T1 T2 T3 AT KT VMIN VMAX`
1649#[derive(Debug, Clone, Serialize, Deserialize)]
1650pub struct GastParams {
1651    /// Droop (speed regulation, pu).
1652    pub r: f64,
1653    /// Governor valve time constant (s).
1654    pub t1: f64,
1655    /// Turbine time constant (s).
1656    pub t2: f64,
1657    /// Exhaust temperature time constant (s).
1658    pub t3: f64,
1659    /// Ambient temperature load limit (pu).
1660    pub at: f64,
1661    /// Exhaust temperature coefficient.
1662    pub kt: f64,
1663    /// Minimum governor output (pu).
1664    pub vmin: f64,
1665    /// Maximum governor output (pu).
1666    pub vmax: f64,
1667}
1668
1669// --- REPCA ------------------------------------------------------------------
1670
1671/// REPC_A -- IBR plant controller (maps to governor slot, Phase 8 simplified).
1672///
1673/// Simplified to constant Pref/Qref -- no AGC droop or plant-level control.
1674///
1675/// This is a standard planning-level model for IBR plant controllers.
1676/// For studies involving plant-level AGC response, frequency droop, or
1677/// coordinated voltage/reactive control, use the full REPC_A model with droop
1678/// and PI controller parameters.  See the module-level documentation for details.
1679#[derive(Debug, Clone, Serialize, Deserialize)]
1680pub struct RepcaParams {
1681    // --- Reactive power / voltage control ---
1682    /// Voltage / reactive power control flag: 0 = Q control, 1 = voltage control.
1683    pub vrflag: f64,
1684    /// Reactive power droop gain (pu).
1685    pub rc: f64,
1686    /// Reactive power / voltage measurement filter time constant (s).
1687    pub tfltr: f64,
1688    /// Voltage PI proportional gain.
1689    pub kp: f64,
1690    /// Voltage PI integral gain (1/s).
1691    pub ki: f64,
1692    /// Maximum PI output (pu).
1693    pub vmax: f64,
1694    /// Minimum PI output (pu).
1695    pub vmin: f64,
1696    /// Plant voltage reference (pu).
1697    pub vref: f64,
1698    /// Reactive power reference (pu).
1699    pub qref: f64,
1700    /// Plant-level reactive power max (pu).
1701    pub qmax: f64,
1702    /// Plant-level reactive power min (pu).
1703    pub qmin: f64,
1704
1705    // --- Active power / frequency control ---
1706    /// Frequency deadband lower (Hz, negative).
1707    pub fdbd1: f64,
1708    /// Frequency deadband upper (Hz, positive).
1709    pub fdbd2: f64,
1710    /// Frequency droop down gain.
1711    pub ddn: f64,
1712    /// Frequency droop up gain.
1713    pub dup: f64,
1714    /// Active power measurement filter time constant (s).
1715    pub tp: f64,
1716    /// Active power PI proportional gain.
1717    pub kpg: f64,
1718    /// Active power PI integral gain (1/s).
1719    pub kig: f64,
1720    /// Active power reference (pu).
1721    pub pref: f64,
1722    /// Maximum active power (pu).
1723    pub pmax: f64,
1724    /// Minimum active power (pu).
1725    pub pmin: f64,
1726    /// Active power ramp rate (pu/s).
1727    pub rrpwr: f64,
1728    /// Voltage measurement filter time constant (s) for frequency control.
1729    pub tlag: f64,
1730}
1731
1732// --- ST2CUT -----------------------------------------------------------------
1733
1734/// Dual-input PSS (PSS/E ST2CUT).
1735///
1736/// PSS/E params (11 required; LSMIN, VCU, VCL optional):
1737/// `K1 T1 T2 T3 T4 K2 T5 T6 T7 T8 LSMAX [LSMIN VCU VCL]`
1738#[derive(Debug, Clone, Serialize, Deserialize)]
1739pub struct St2cutParams {
1740    pub k1: f64,
1741    pub t1: f64,
1742    pub t2: f64,
1743    pub t3: f64,
1744    pub t4: f64,
1745    pub k2: f64,
1746    pub t5: f64,
1747    pub t6: f64,
1748    pub t7: f64,
1749    pub t8: f64,
1750    pub lsmax: f64,
1751    #[serde(default, skip_serializing_if = "Option::is_none")]
1752    pub lsmin: Option<f64>,
1753    #[serde(default, skip_serializing_if = "Option::is_none")]
1754    pub vcu: Option<f64>,
1755    #[serde(default, skip_serializing_if = "Option::is_none")]
1756    pub vcl: Option<f64>,
1757}
1758
1759// ---------------------------------------------------------------------------
1760
1761// --- GENTPJ -----------------------------------------------------------------
1762
1763/// GENTPJ — flux-based sixth-order round-rotor generator (WECC standard until Dec 2024).
1764///
1765/// Identical state equations to GENROU; saturation applied to all flux linkages
1766/// simultaneously via Se(ψ) = S1*(ψ/1.0)^2 (quadratic), with additional stator
1767/// current correction term Kii.
1768///
1769/// PSS/E params (14 required + Kii optional):
1770/// `Td0' Td0'' Tq0' Tq0'' H D Xd Xq Xd' Xq' Xd'' Xl S(1.0) S(1.2) [Kii]`
1771#[derive(Debug, Clone, Serialize, Deserialize)]
1772pub struct GentpjParams {
1773    pub td0_prime: f64,
1774    pub td0_pprime: f64,
1775    pub tq0_prime: f64,
1776    pub tq0_pprime: f64,
1777    pub h: f64,
1778    pub d: f64,
1779    pub xd: f64,
1780    pub xq: f64,
1781    pub xd_prime: f64,
1782    pub xq_prime: f64,
1783    pub xd_pprime: f64,
1784    pub xl: f64,
1785    pub s1: f64,
1786    pub s12: f64,
1787    /// Stator current coefficient for saturation (default 0).
1788    #[serde(default, skip_serializing_if = "Option::is_none")]
1789    pub kii: Option<f64>,
1790    #[serde(default, skip_serializing_if = "Option::is_none")]
1791    pub ra: Option<f64>,
1792}
1793
1794// --- GENQEC -----------------------------------------------------------------
1795
1796/// GENQEC — sixth-order round-rotor generator with quadratic saturation.
1797///
1798/// **Mandatory WECC standard from December 2024.**
1799/// Quadratic saturation applied simultaneously to all EMF and impedance terms.
1800/// Same state variables as GENROU.
1801///
1802/// PSS/E params (14 required + Ra optional):
1803/// `Td0' Td0'' Tq0' Tq0'' H D Xd Xq Xd' Xq' Xd'' Xl S(1.0) S(1.2) [Ra]`
1804#[derive(Debug, Clone, Serialize, Deserialize)]
1805pub struct GenqecParams {
1806    pub td0_prime: f64,
1807    pub td0_pprime: f64,
1808    pub tq0_prime: f64,
1809    pub tq0_pprime: f64,
1810    pub h: f64,
1811    pub d: f64,
1812    pub xd: f64,
1813    pub xq: f64,
1814    pub xd_prime: f64,
1815    pub xq_prime: f64,
1816    pub xd_pprime: f64,
1817    pub xl: f64,
1818    pub s1: f64,
1819    pub s12: f64,
1820    #[serde(default, skip_serializing_if = "Option::is_none")]
1821    pub ra: Option<f64>,
1822}
1823
1824// --- ESST1A -----------------------------------------------------------------
1825
1826/// IEEE Type ST1A static exciter (PSS/E ESST1A) — 2005 enhanced standard.
1827///
1828/// PSS/E params: `TR VIMAX VIMIN TC TB TC1 TB1 KA TA VAMAX VAMIN VRMAX VRMIN KC KF TF`
1829#[derive(Debug, Clone, Serialize, Deserialize)]
1830pub struct Esst1aParams {
1831    pub tr: f64,
1832    pub vimax: f64,
1833    pub vimin: f64,
1834    pub tc: f64,
1835    pub tb: f64,
1836    pub tc1: f64,
1837    pub tb1: f64,
1838    pub ka: f64,
1839    pub ta: f64,
1840    pub vamax: f64,
1841    pub vamin: f64,
1842    pub vrmax: f64,
1843    pub vrmin: f64,
1844    pub kc: f64,
1845    pub kf: f64,
1846    pub tf: f64,
1847    pub klr: f64,
1848    pub ilr: f64,
1849}
1850
1851// --- EXAC1 / ESAC1A ---------------------------------------------------------
1852
1853/// IEEE Type AC1A rotating exciter (PSS/E EXAC1 / ESAC1A).
1854///
1855/// AC alternator-fed rectifier exciter. The rectifier regulation is modelled
1856/// via the FEX function (IN = KC * Ifd / Ve).
1857///
1858/// PSS/E params: `TR TB TC KA TA VRMAX VRMIN TE KF TF KE E1 SE1 E2 SE2 KC`
1859#[derive(Debug, Clone, Serialize, Deserialize)]
1860pub struct Exac1Params {
1861    pub tr: f64,
1862    pub tb: f64,
1863    pub tc: f64,
1864    pub ka: f64,
1865    pub ta: f64,
1866    pub vrmax: f64,
1867    pub vrmin: f64,
1868    pub te: f64,
1869    pub kf: f64,
1870    pub tf: f64,
1871    pub kc: f64,
1872    pub kd: f64,
1873    pub ke: f64,
1874    pub e1: f64,
1875    pub se1: f64,
1876    pub e2: f64,
1877    pub se2: f64,
1878}
1879
1880// --- ESAC7B -----------------------------------------------------------------
1881
1882/// IEEE Type AC7B rotating exciter (PSS/E ESAC7B / AC7B).
1883///
1884/// High-performance AC exciter with PI voltage regulator (Hitachi/ABB digital).
1885///
1886/// PSS/E params: `TR KPA KIA VRH VRL KPF VFH TF TE KE E1 SE1 E2 SE2 KD KC KL`
1887#[derive(Debug, Clone, Serialize, Deserialize)]
1888pub struct Esac7bParams {
1889    pub tr: f64,
1890    pub kpa: f64,
1891    pub kia: f64,
1892    pub vrh: f64,
1893    pub vrl: f64,
1894    pub kpf: f64,
1895    pub vfh: f64,
1896    pub tf: f64,
1897    pub te: f64,
1898    pub ke: f64,
1899    pub e1: f64,
1900    pub se1: f64,
1901    pub e2: f64,
1902    pub se2: f64,
1903    pub kd: f64,
1904    pub kc: f64,
1905    pub kl: f64,
1906}
1907
1908// --- ESST4B -----------------------------------------------------------------
1909
1910/// IEEE Type ST4B static exciter (PSS/E ESST4B) — dual-loop PI controller.
1911///
1912/// PSS/E params: `TR KPR KIR VRMAX VRMIN KPM KIM VMMAX VMMIN KG KP KI VBMAX VGMAX`
1913#[derive(Debug, Clone, Serialize, Deserialize)]
1914pub struct Esst4bParams {
1915    pub tr: f64,
1916    pub kpr: f64,
1917    pub kir: f64,
1918    pub vrmax: f64,
1919    pub vrmin: f64,
1920    pub kpm: f64,
1921    pub kim: f64,
1922    pub vmmax: f64,
1923    pub vmmin: f64,
1924    pub kg: f64,
1925    pub kp: f64,
1926    pub ki: f64,
1927    pub vbmax: f64,
1928    pub vgmax: f64,
1929}
1930
1931// --- HYGOV -----------------------------------------------------------------
1932
1933/// WECC hydro turbine governor (PSS/E HYGOV).
1934///
1935/// PID servo + penstock water column + turbine output.
1936///
1937/// PSS/E params: `R TP VELM TG GMAX GMIN TW At Dturb QNLL`
1938#[derive(Debug, Clone, Serialize, Deserialize)]
1939pub struct HygovParams {
1940    /// Droop (pu).
1941    pub r: f64,
1942    /// Pilot valve time constant (s).
1943    pub tp: f64,
1944    /// Gate velocity limit (pu/s).
1945    pub velm: f64,
1946    /// Gate servo time constant (s).
1947    pub tg: f64,
1948    /// Maximum gate position (pu).
1949    pub gmax: f64,
1950    /// Minimum gate position (pu).
1951    pub gmin: f64,
1952    /// Water time constant (s).
1953    pub tw: f64,
1954    /// Turbine gain (pu power / pu gate).
1955    pub at: f64,
1956    /// Turbine damping (pu).
1957    pub dturb: f64,
1958    /// No-load flow (pu).
1959    pub qnl: f64,
1960}
1961
1962// --- HYGOVD ----------------------------------------------------------------
1963
1964/// WECC hydro governor with deadband (PSS/E HYGOVD — WECC recommended).
1965#[derive(Debug, Clone, Serialize, Deserialize)]
1966pub struct HygovdParams {
1967    pub r: f64,
1968    pub tp: f64,
1969    pub velm: f64,
1970    pub tg: f64,
1971    pub gmax: f64,
1972    pub gmin: f64,
1973    pub tw: f64,
1974    pub at: f64,
1975    pub dturb: f64,
1976    pub qnl: f64,
1977    /// Deadband lower limit (pu, negative).
1978    pub db1: f64,
1979    /// Deadband upper limit (pu, positive).
1980    pub db2: f64,
1981}
1982
1983// --- TGOV1D ----------------------------------------------------------------
1984
1985/// Steam turbine governor with speed deadband (PSS/E TGOV1D — WECC recommended).
1986#[derive(Debug, Clone, Serialize, Deserialize)]
1987pub struct Tgov1dParams {
1988    pub r: f64,
1989    pub t1: f64,
1990    pub vmax: f64,
1991    pub vmin: f64,
1992    pub t2: f64,
1993    pub t3: f64,
1994    #[serde(default, skip_serializing_if = "Option::is_none")]
1995    pub dt: Option<f64>,
1996    /// Deadband lower limit (pu, negative).
1997    pub db1: f64,
1998    /// Deadband upper limit (pu, positive).
1999    pub db2: f64,
2000}
2001
2002// --- IEEEG1D ---------------------------------------------------------------
2003
2004/// IEEE Type G1 steam governor with deadband (PSS/E IEEEG1D — WECC recommended).
2005#[derive(Debug, Clone, Serialize, Deserialize)]
2006pub struct Ieeeg1dParams {
2007    pub k: f64,
2008    pub t1: f64,
2009    pub t2: f64,
2010    pub t3: f64,
2011    pub uo: f64,
2012    pub uc: f64,
2013    pub pmax: f64,
2014    pub pmin: f64,
2015    pub t4: f64,
2016    #[serde(default, skip_serializing_if = "Option::is_none")]
2017    pub k1: Option<f64>,
2018    #[serde(default, skip_serializing_if = "Option::is_none")]
2019    pub k2: Option<f64>,
2020    #[serde(default, skip_serializing_if = "Option::is_none")]
2021    pub t5: Option<f64>,
2022    #[serde(default, skip_serializing_if = "Option::is_none")]
2023    pub k3: Option<f64>,
2024    #[serde(default, skip_serializing_if = "Option::is_none")]
2025    pub k4: Option<f64>,
2026    #[serde(default, skip_serializing_if = "Option::is_none")]
2027    pub t6: Option<f64>,
2028    #[serde(default, skip_serializing_if = "Option::is_none")]
2029    pub k5: Option<f64>,
2030    #[serde(default, skip_serializing_if = "Option::is_none")]
2031    pub k6: Option<f64>,
2032    #[serde(default, skip_serializing_if = "Option::is_none")]
2033    pub t7: Option<f64>,
2034    #[serde(default, skip_serializing_if = "Option::is_none")]
2035    pub k7: Option<f64>,
2036    #[serde(default, skip_serializing_if = "Option::is_none")]
2037    pub k8: Option<f64>,
2038    /// Deadband lower limit (pu, negative).
2039    pub db1: f64,
2040    /// Deadband upper limit (pu, positive).
2041    pub db2: f64,
2042}
2043
2044// --- IEEEG2 ----------------------------------------------------------------
2045
2046/// IEEE Type G2 hydro governor with dashpot, gate servo, and water column.
2047///
2048/// Full block diagram: speed error → dashpot (temporary droop) → gate servo
2049/// → water column (non-minimum phase) → Pm.
2050///
2051/// 3 states: x_dashpot, x_gate, x_water.
2052///
2053/// PSS/E params: `K T1 T2 T3 Pmin Pmax At Dturb Qnl`
2054/// Legacy 4-param format: `K T1 T2 PZ` (PZ mapped to Qnl).
2055#[derive(Debug, Clone, Serialize, Deserialize)]
2056pub struct Ieeeg2Params {
2057    /// Governor gain (1/R permanent droop).
2058    pub k: f64,
2059    /// Gate servo time constant Tp (s).
2060    pub t1: f64,
2061    /// Water starting time Tw (s).
2062    pub t2: f64,
2063    /// Dashpot reset time constant Tr (s). 0 = no dashpot.
2064    #[serde(default)]
2065    pub t3: f64,
2066    /// Temporary droop coefficient Rt. 0 = no temporary droop.
2067    #[serde(default)]
2068    pub rt: f64,
2069    /// Minimum gate position (pu).
2070    #[serde(default)]
2071    pub pmin: f64,
2072    /// Maximum gate position (pu).
2073    #[serde(default = "Ieeeg2Params::default_pmax")]
2074    pub pmax: f64,
2075    /// Turbine gain.
2076    #[serde(default = "Ieeeg2Params::default_at")]
2077    pub at: f64,
2078    /// Turbine damping coefficient.
2079    #[serde(default)]
2080    pub dturb: f64,
2081    /// No-load flow (pu). Legacy `pz` field maps here.
2082    #[serde(default)]
2083    pub qnl: f64,
2084}
2085
2086impl Ieeeg2Params {
2087    fn default_pmax() -> f64 {
2088        1.0
2089    }
2090    fn default_at() -> f64 {
2091        1.0
2092    }
2093}
2094
2095// --- PSS2A -----------------------------------------------------------------
2096
2097/// IEEE dual-input PSS (PSS/E PSS2A) — IEEE 421.5-2005 standard.
2098///
2099/// Two inputs: speed (ω) and electrical power (Pe).
2100///
2101/// PSS/E params: `M1 T6 T7 KS2 T8 T9 M2 TW1 TW2 TW3 TW4 T1 T2 T3 T4 KS1 KS3 VSTMAX VSTMIN`
2102#[derive(Debug, Clone, Serialize, Deserialize)]
2103pub struct Pss2aParams {
2104    /// Input 1 signal selector (1=speed, 2=freq, 3=power).
2105    pub m1: f64,
2106    /// Input 1 transducer time constant (s).
2107    pub t6: f64,
2108    /// Input 2 transducer time constant (s).
2109    pub t7: f64,
2110    /// Input 2 gain.
2111    pub ks2: f64,
2112    /// Input 2 ramp tracking filter TC (s).
2113    pub t8: f64,
2114    /// Input 2 ramp tracking filter TB (s).
2115    pub t9: f64,
2116    /// Input 2 signal selector (1=speed, 2=freq, 3=power).
2117    pub m2: f64,
2118    /// Input 1 washout TC 1 (s).
2119    pub tw1: f64,
2120    /// Input 1 washout TC 2 (s).
2121    pub tw2: f64,
2122    /// Input 2 washout TC 1 (s).
2123    pub tw3: f64,
2124    /// Input 2 washout TC 2 (s).
2125    pub tw4: f64,
2126    /// Lead-lag 1 numerator TC (s).
2127    pub t1: f64,
2128    /// Lead-lag 1 denominator TC (s).
2129    pub t2: f64,
2130    /// Lead-lag 2 numerator TC (s).
2131    pub t3: f64,
2132    /// Lead-lag 2 denominator TC (s).
2133    pub t4: f64,
2134    /// PSS gain (pu/pu).
2135    pub ks1: f64,
2136    /// Input signal scaling (usually 1.0).
2137    pub ks3: f64,
2138    /// Maximum PSS output (pu).
2139    pub vstmax: f64,
2140    /// Minimum PSS output (pu).
2141    pub vstmin: f64,
2142}
2143
2144// --- PSS2B -----------------------------------------------------------------
2145
2146/// IEEE dual-input PSS enhanced (PSS/E PSS2B) — additional transducer TC.
2147///
2148/// Identical to PSS2A with additional time constants T10/T11.
2149#[derive(Debug, Clone, Serialize, Deserialize)]
2150pub struct Pss2bParams {
2151    pub m1: f64,
2152    pub t6: f64,
2153    pub t7: f64,
2154    pub ks2: f64,
2155    pub t8: f64,
2156    pub t9: f64,
2157    pub m2: f64,
2158    pub tw1: f64,
2159    pub tw2: f64,
2160    pub tw3: f64,
2161    pub tw4: f64,
2162    pub t1: f64,
2163    pub t2: f64,
2164    pub t3: f64,
2165    pub t4: f64,
2166    pub ks1: f64,
2167    pub ks3: f64,
2168    pub vstmax: f64,
2169    pub vstmin: f64,
2170    /// Additional transducer lead TC (s).
2171    pub t10: f64,
2172    /// Additional transducer lag TC (s).
2173    pub t11: f64,
2174}
2175
2176// --- STAB1 -----------------------------------------------------------------
2177
2178/// WSCC simple stabilizer (PSS/E STAB1).
2179///
2180/// Single-input (speed), one washout, one lead-lag.
2181///
2182/// PSS/E params: `KS T1 T2 T3 T4 HLIM`
2183#[derive(Debug, Clone, Serialize, Deserialize)]
2184pub struct Stab1Params {
2185    /// Stabilizer gain (pu/pu).
2186    pub ks: f64,
2187    /// Washout time constant (s).
2188    pub t1: f64,
2189    /// Lead-lag denominator time constant (s).
2190    pub t2: f64,
2191    /// Lead-lag numerator time constant (s).
2192    pub t3: f64,
2193    /// Second lead-lag denominator TC (s).
2194    pub t4: f64,
2195    /// PSS output limit (pu, symmetric ±HLIM).
2196    pub hlim: f64,
2197}
2198
2199// ---------------------------------------------------------------------------
2200// Phase 14: BESS, remaining exciters, governors, PSS
2201// ---------------------------------------------------------------------------
2202
2203// --- CBEST -----------------------------------------------------------------
2204
2205/// CBEST — PSS/E native BESS model (Phase 14, 4 states).
2206///
2207/// States: p_cmd, q_cmd, soc, e_dc.
2208#[derive(Debug, Clone, Serialize, Deserialize)]
2209pub struct CbestParams {
2210    /// Maximum active power output (pu machine base, typ 1.0).
2211    pub p_max: f64,
2212    /// Minimum active power (pu, can be negative = charging, typ -1.0).
2213    pub p_min: f64,
2214    /// Maximum reactive power (pu, typ 0.5).
2215    pub q_max: f64,
2216    /// Minimum reactive power (pu, typ -0.5).
2217    pub q_min: f64,
2218    /// Active power time constant (s, typ 0.05).
2219    pub tp: f64,
2220    /// Reactive power time constant (s, typ 0.05).
2221    pub tq: f64,
2222    /// Energy capacity (MWh normalized to MVA base).
2223    pub e_cap: f64,
2224    /// Machine MVA base.
2225    pub mbase: f64,
2226    /// Initial state of charge (0..1, default 0.5).
2227    pub soc_init: f64,
2228}
2229
2230// --- CHAAUT ----------------------------------------------------------------
2231
2232/// CHAAUT — BESS active power controller with frequency droop (Phase 14, 2 states).
2233///
2234/// States: p_cmd, freq_state.
2235#[derive(Debug, Clone, Serialize, Deserialize)]
2236pub struct ChaautParams {
2237    /// Frequency droop gain (typ 20.0).
2238    pub kf: f64,
2239    /// Frequency filter time constant (s, typ 0.1).
2240    pub tf: f64,
2241    /// Maximum power output (pu).
2242    pub p_max: f64,
2243    /// Minimum power output (pu, can be negative).
2244    pub p_min: f64,
2245    /// Power time constant (s).
2246    pub tp: f64,
2247    /// Machine MVA base.
2248    pub mbase: f64,
2249}
2250
2251// --- ESAC2A ----------------------------------------------------------------
2252
2253/// ESAC2A — IEEE AC2A high-initial-response rotating exciter (Phase 14, 5 states).
2254///
2255/// States: vm (transducer), vr (voltage regulator), ve (exciter EMF), vf (rate feedback), efd.
2256#[derive(Debug, Clone, Serialize, Deserialize)]
2257pub struct Esac2aParams {
2258    pub tr: f64,
2259    pub tb: f64,
2260    pub tc: f64,
2261    pub ka: f64,
2262    pub ta: f64,
2263    pub vamax: f64,
2264    pub vamin: f64,
2265    pub ke: f64,
2266    pub te: f64,
2267    pub kf: f64,
2268    pub tf: f64,
2269    pub e1: f64,
2270    pub se1: f64,
2271    pub e2: f64,
2272    pub se2: f64,
2273    pub vrmax: f64,
2274    pub vrmin: f64,
2275    pub kb: f64,
2276    pub kc: f64,
2277    pub kd: f64,
2278    pub kh: f64,
2279}
2280
2281// --- ESAC5A ----------------------------------------------------------------
2282
2283/// ESAC5A — IEEE AC5A simplified brushless exciter (Phase 14, 2 states).
2284///
2285/// States: vr (regulator), efd.
2286#[derive(Debug, Clone, Serialize, Deserialize)]
2287pub struct Esac5aParams {
2288    pub ka: f64,
2289    pub ta: f64,
2290    pub ke: f64,
2291    pub te: f64,
2292    pub kf: f64,
2293    pub tf: f64,
2294    pub e1: f64,
2295    pub se1: f64,
2296    pub e2: f64,
2297    pub se2: f64,
2298    pub vrmax: f64,
2299    pub vrmin: f64,
2300}
2301
2302// --- PSS1A -----------------------------------------------------------------
2303
2304/// PSS1A — Single-input single lead-lag PSS (Phase 14, 2 states).
2305///
2306/// States: x1 (washout), x2 (lead-lag).
2307#[derive(Debug, Clone, Serialize, Deserialize)]
2308pub struct Pss1aParams {
2309    /// PSS gain.
2310    pub ks: f64,
2311    /// Washout time constant (s).
2312    pub t1: f64,
2313    /// Second washout / lead-lag denominator TC (s).
2314    pub t2: f64,
2315    /// Lead-lag numerator TC (s).
2316    pub t3: f64,
2317    /// Second lead-lag denominator TC (s).
2318    pub t4: f64,
2319    /// Maximum PSS output (pu).
2320    pub vstmax: f64,
2321    /// Minimum PSS output (pu).
2322    pub vstmin: f64,
2323}
2324
2325// --- PIDGOV ----------------------------------------------------------------
2326
2327/// PIDGOV — PID governor for any prime mover (Phase 14, 3 states).
2328///
2329/// States: x_int (integrator), x_der (derivative filter), pm.
2330#[derive(Debug, Clone, Serialize, Deserialize)]
2331pub struct PidgovParams {
2332    /// Maximum power output (pu).
2333    pub pmax: f64,
2334    /// Minimum power output (pu).
2335    pub pmin: f64,
2336    /// Proportional gain.
2337    pub kp: f64,
2338    /// Integral gain (1/s).
2339    pub ki: f64,
2340    /// Derivative gain.
2341    pub kd: f64,
2342    /// Derivative filter time constant (s).
2343    pub td: f64,
2344    /// Power output time constant (s).
2345    pub tf: f64,
2346}
2347
2348// --- DEGOV1 ----------------------------------------------------------------
2349
2350/// DEGOV1 — Woodward diesel/gas engine governor (6 states).
2351///
2352/// Full PID electronic controller + actuator + 3-stage engine dynamics.
2353///
2354/// States: x_ecl (derivative filter), x_int (integrator), x_act (actuator),
2355///         x1 (engine lag 1), x2 (engine lag 2), x3 (engine lag 3).
2356#[derive(Debug, Clone, Serialize, Deserialize)]
2357pub struct Degov1Params {
2358    /// Droop (pu).
2359    pub r: f64,
2360    /// First engine time constant (s).
2361    pub t1: f64,
2362    /// Second engine time constant (s).
2363    pub t2: f64,
2364    /// Third engine time constant (s).
2365    pub t3: f64,
2366    /// Derivative action time constant (s) — PID controller.
2367    #[serde(default = "default_degov1_t4")]
2368    pub t4: f64,
2369    /// Derivative filter time constant (s) — PID controller.
2370    #[serde(default = "default_degov1_t5")]
2371    pub t5: f64,
2372    /// Actuator time constant (s).
2373    #[serde(default = "default_degov1_t6")]
2374    pub t6: f64,
2375    /// Controller integral time constant (s).
2376    pub td: f64,
2377    /// Controller gain.
2378    #[serde(default = "default_degov1_k")]
2379    pub k: f64,
2380    /// Maximum actuator output (pu).
2381    pub vmax: f64,
2382    /// Minimum actuator output (pu).
2383    pub vmin: f64,
2384    /// Actuator rate limit (pu/s).
2385    #[serde(default = "default_degov1_velm")]
2386    pub velm: f64,
2387    /// Ambient temperature load limit (pu) — engine output gain.
2388    pub at: f64,
2389    /// Exhaust temperature coefficient — engine cross-coupling.
2390    pub kt: f64,
2391}
2392
2393fn default_degov1_t4() -> f64 {
2394    0.0
2395}
2396fn default_degov1_t5() -> f64 {
2397    0.01
2398}
2399fn default_degov1_t6() -> f64 {
2400    0.01
2401}
2402fn default_degov1_k() -> f64 {
2403    1.0
2404}
2405fn default_degov1_velm() -> f64 {
2406    99.0
2407}
2408
2409// ---------------------------------------------------------------------------
2410// Phase 13: FACTS/HVDC dynamic model records
2411// ---------------------------------------------------------------------------
2412
2413/// A FACTS or HVDC dynamic model record — attaches to a bus (shunt) or two buses (branch).
2414#[derive(Debug, Clone, Serialize, Deserialize)]
2415pub struct FACTSDyn {
2416    /// Primary bus number (for shunt devices; rectifier bus for HVDC).
2417    pub bus: u32,
2418    /// Device ID string (matches PSS/E device ID).
2419    pub device_id: String,
2420    /// The specific FACTS/HVDC model and its parameters.
2421    pub model: FACTSModel,
2422    /// Second terminal bus: inverter bus for HVDC, to-bus for series FACTS.
2423    /// Populated from model params (HVDC) or network topology (TCSC/SSSC/UPFC).
2424    #[serde(default)]
2425    pub to_bus: Option<u32>,
2426}
2427
2428/// Discriminated union of supported FACTS/HVDC dynamic models.
2429#[derive(Debug, Clone, Serialize, Deserialize)]
2430#[serde(tag = "type")]
2431pub enum FACTSModel {
2432    /// CSVGN1 — Static VAr Compensator (most common SVC model).
2433    Csvgn1(Csvgn1Params),
2434    /// CSTCON — STATCOM (current-source reactive control).
2435    Cstcon(CstconParams),
2436    /// TCSC — Thyristor-Controlled Series Capacitor.
2437    Tcsc(TcscParams),
2438    /// CDC4T — Generic LCC HVDC Two-Terminal.
2439    Cdc4t(Cdc4tParams),
2440    /// VSCDCT — Generic VSC HVDC Two-Terminal.
2441    Vscdct(VscdctParams),
2442    /// CSVGN3 — SVC with slope/droop regulator (Phase 15, 3 states).
2443    Csvgn3(Csvgn3Params),
2444    /// CDC7T — LCC HVDC + runback + current order controllers (Phase 15, 6 states).
2445    Cdc7t(Cdc7tParams),
2446    // Phase 20
2447    /// CSVGN4 — SVC with 4 states (adds POD) (Phase 20).
2448    Csvgn4(Csvgn4Params),
2449    /// CSVGN5 — SVC with 4 states (voltage support mode) (Phase 20).
2450    Csvgn5(Csvgn5Params),
2451    /// CDC6T — LCC HVDC with enhanced controls (Phase 20).
2452    Cdc6t(Cdc6tParams),
2453    /// CSTCNT — STATCOM with N controls (4 states) (Phase 20).
2454    Cstcnt(CstcntParams),
2455    /// MMC1 — Modular Multilevel Converter (5 states) (Phase 20).
2456    Mmc1(Mmc1Params),
2457    // Phase 26
2458    /// HVDCPLU1 — Siemens HVDC Plus VSC (Phase 26, 6 states, reuses Vscdct layout).
2459    Hvdcplu1(HvdcPlu1Params),
2460    /// CSVGN6 — SVC Variant 6 with Auxiliary Inputs (Phase 26, 5 states).
2461    Csvgn6(Csvgn6Params),
2462    /// STCON1 — STATCOM with Inner Current Control (Phase 26, 4 states).
2463    Stcon1(Stcon1Params),
2464    /// GCSC — Gate-Controlled Series Compensator (Phase 26, 3 states).
2465    Gcsc(GcscParams),
2466    /// SSSC — Static Synchronous Series Compensator (Phase 26, 4 states).
2467    Sssc(SsscParams),
2468    /// UPFC — Unified Power Flow Controller (Phase 26, 6 states).
2469    Upfc(UpfcParams),
2470    /// CDC3T — Three-Terminal LCC HVDC (Phase 26, 8 states).
2471    Cdc3t(Cdc3tParams),
2472    // Wave 34: WECC SVC/STATCOM variants
2473    /// SVSMO1 — WECC Generic SVC voltage regulator (1 state).
2474    Svsmo1(Svsmo1Params),
2475    /// SVSMO2 — WECC Generic STATCOM (1 state).
2476    Svsmo2(Svsmo2Params),
2477    /// SVSMO3 — WECC Advanced SVC (2 states: b_svc + vr).
2478    Svsmo3(Svsmo3Params),
2479}
2480
2481// --- CSVGN1 -----------------------------------------------------------------
2482
2483/// CSVGN1 — Static VAr Compensator (SVC) with lead-lag voltage regulator.
2484///
2485/// PSS/E params: `t1 t2 t3 t4 t5 k vmax vmin bmax bmin /`
2486///
2487/// 3 states: `vr` (regulator), `vfilt` (voltage filter), `b_svc` (susceptance).
2488#[derive(Debug, Clone, Serialize, Deserialize)]
2489pub struct Csvgn1Params {
2490    /// Regulator time constant (s, typ 0.05).
2491    pub t1: f64,
2492    /// Lead time constant (s, typ 0.0).
2493    pub t2: f64,
2494    /// Lag time constant (s, typ 0.1).
2495    pub t3: f64,
2496    /// Filter time constant (s, typ 0.04).
2497    pub t4: f64,
2498    /// Output time constant (s, typ 0.05).
2499    pub t5: f64,
2500    /// Regulator gain (typ 100.0).
2501    pub k: f64,
2502    /// Max voltage reference (pu, typ 1.05).
2503    pub vmax: f64,
2504    /// Min voltage reference (pu, typ 0.95).
2505    pub vmin: f64,
2506    /// Max susceptance (pu, capacitive positive, typ 2.0).
2507    pub bmax: f64,
2508    /// Min susceptance (pu, inductive negative, typ -2.0).
2509    pub bmin: f64,
2510    /// MVA base.
2511    pub mbase: f64,
2512    /// TCR reactor susceptance (pu, inductive). If set, enables firing-angle physics.
2513    /// B_tcr(α) = b_l * (2*(π-α) + sin(2α)) / π
2514    #[serde(default)]
2515    pub b_l: Option<f64>,
2516    /// TSC fixed capacitor susceptance (pu, capacitive positive). Defaults to bmax.
2517    #[serde(default)]
2518    pub b_c: Option<f64>,
2519    /// Firing angle lag time constant (s). Defaults to t5.
2520    #[serde(default)]
2521    pub t_alpha: Option<f64>,
2522}
2523
2524// --- CSTCON -----------------------------------------------------------------
2525
2526/// CSTCON — STATCOM (current-source reactive control).
2527///
2528/// PSS/E params: `tr k tiq imax imin /`
2529///
2530/// 3 states: `vr` (voltage regulator), `vfilt` (filter), `iq_cmd` (current command).
2531#[derive(Debug, Clone, Serialize, Deserialize)]
2532pub struct CstconParams {
2533    /// Regulator time constant (s, typ 0.02).
2534    pub tr: f64,
2535    /// Gain (typ 50.0).
2536    pub k: f64,
2537    /// Current command time constant (s, typ 0.01).
2538    pub tiq: f64,
2539    /// Max current (pu, typ 1.0).
2540    pub imax: f64,
2541    /// Min current (pu, typ -1.0).
2542    pub imin: f64,
2543    /// MVA base.
2544    pub mbase: f64,
2545    /// DC-link capacitance (pu on mbase, typ 0.1). Defaults to 0.1.
2546    #[serde(default)]
2547    pub c_dc: Option<f64>,
2548    /// DC-link voltage reference (pu, typ 1.0). Defaults to 1.0.
2549    #[serde(default)]
2550    pub vdc_ref: Option<f64>,
2551    /// DC-link voltage PI proportional gain. Defaults to 5.0.
2552    #[serde(default)]
2553    pub kp_vdc: Option<f64>,
2554}
2555
2556// --- TCSC -------------------------------------------------------------------
2557
2558/// TCSC — Thyristor-Controlled Series Capacitor.
2559///
2560/// PSS/E params: `t1 t2 t3 xmax xmin k /`
2561///
2562/// 3 states: `x_tcsc` (reactance), `vfilt` (line current filter), `x_order` (order tracking).
2563#[derive(Debug, Clone, Serialize, Deserialize)]
2564pub struct TcscParams {
2565    /// Time constant (s, typ 0.05).
2566    pub t1: f64,
2567    /// Time constant (s, typ 0.02).
2568    pub t2: f64,
2569    /// Time constant (s, typ 0.1).
2570    pub t3: f64,
2571    /// Max reactance (pu, capacitive positive).
2572    pub xmax: f64,
2573    /// Min reactance (pu, can be negative = inductive).
2574    pub xmin: f64,
2575    /// Control gain.
2576    pub k: f64,
2577    /// MVA base.
2578    pub mbase: f64,
2579}
2580
2581// --- CDC4T ------------------------------------------------------------------
2582
2583/// CDC4T — Generic LCC HVDC Two-Terminal.
2584///
2585/// PSS/E params: `setvl vschd tr td alpha_min alpha_max gamma_min rectifier_bus inverter_bus /`
2586///
2587/// 6 states: `id`, `vd_r`, `vd_i`, `alpha`, `gamma`, `p_ord`.
2588#[derive(Debug, Clone, Serialize, Deserialize)]
2589pub struct Cdc4tParams {
2590    /// Power or current setpoint (pu).
2591    pub setvl: f64,
2592    /// Scheduled DC voltage (pu, typ 1.0).
2593    pub vschd: f64,
2594    /// MVA base.
2595    pub mbase: f64,
2596    /// Regulator time constant (s, typ 0.02).
2597    pub tr: f64,
2598    /// DC time constant (s, typ 0.01).
2599    pub td: f64,
2600    /// Min firing angle (deg, typ 5.0).
2601    pub alpha_min: f64,
2602    /// Max firing angle (deg, typ 165.0).
2603    pub alpha_max: f64,
2604    /// Min extinction angle (deg, typ 15.0).
2605    pub gamma_min: f64,
2606    /// Inverter extinction angle reference (rad). Defaults to `gamma_min` in radians.
2607    pub gamma_ref: Option<f64>,
2608    /// Power order ramp rate (pu/s). Defaults to 10.0.
2609    pub ramp: Option<f64>,
2610    /// Rectifier current controller proportional gain (rad/pu). Defaults to 0.5.
2611    #[serde(default)]
2612    pub kp_alpha: Option<f64>,
2613    /// Rectifier current controller integral gain (rad/pu/s). Defaults to 20.0.
2614    #[serde(default)]
2615    pub ki_alpha: Option<f64>,
2616    /// Inverter gamma controller integral gain (1/s). Defaults to 20.0.
2617    #[serde(default)]
2618    pub ki_gamma: Option<f64>,
2619    /// Rectifier terminal bus number.
2620    pub rectifier_bus: u32,
2621    /// Inverter terminal bus number.
2622    pub inverter_bus: u32,
2623    // Phase 2.1: CIGRE control + VDCOL
2624    /// VDCOL breakpoint voltage (pu DC) — below this, current order is reduced.
2625    #[serde(default)]
2626    pub vdcol_v1: Option<f64>,
2627    /// VDCOL breakpoint voltage upper (pu DC).
2628    #[serde(default)]
2629    pub vdcol_v2: Option<f64>,
2630    /// VDCOL current limit at v1 (pu).
2631    #[serde(default)]
2632    pub vdcol_i1: Option<f64>,
2633    /// VDCOL current limit at v2 (pu, typically 1.0 = full current).
2634    #[serde(default)]
2635    pub vdcol_i2: Option<f64>,
2636    /// Current order filter time constant (s).
2637    #[serde(default)]
2638    pub t_iord: Option<f64>,
2639}
2640
2641// --- VSCDCT -----------------------------------------------------------------
2642
2643/// VSCDCT — Generic VSC HVDC Two-Terminal.
2644///
2645/// PSS/E params: `p_order vdc_ref t_dc t_ac imax rectifier_bus inverter_bus /`
2646///
2647/// 6 states: `id`, `iq_r`, `iq_i`, `vd`, `p_ref`, `q_ref`.
2648#[derive(Debug, Clone, Serialize, Deserialize)]
2649pub struct VscdctParams {
2650    /// Power order (pu, positive = rectifier to inverter).
2651    pub p_order: f64,
2652    /// DC voltage reference (pu, typ 1.0).
2653    pub vdc_ref: f64,
2654    /// DC time constant (s, typ 0.02).
2655    pub t_dc: f64,
2656    /// AC time constant (s, typ 0.01).
2657    pub t_ac: f64,
2658    /// Max DC current (pu, typ 1.1).
2659    pub imax: f64,
2660    /// Reactive power order (pu). Defaults to 0.0.
2661    pub q_order: Option<f64>,
2662    /// DC voltage PI proportional gain. Defaults to 2.0.
2663    #[serde(default)]
2664    pub kp_vdc: Option<f64>,
2665    /// DC voltage PI integral gain (1/s). Defaults to 50.0.
2666    #[serde(default)]
2667    pub ki_vdc: Option<f64>,
2668    /// AC voltage/Q PI proportional gain. Defaults to 5.0.
2669    #[serde(default)]
2670    pub kp_q: Option<f64>,
2671    /// AC voltage/Q PI integral gain (1/s). Defaults to 20.0.
2672    #[serde(default)]
2673    pub ki_q: Option<f64>,
2674    /// DC voltage measurement filter time constant (s). Defaults to 0.01.
2675    #[serde(default)]
2676    pub t_vdc_filt: Option<f64>,
2677    /// Inner d-axis current PI proportional gain. Defaults to 1.0.
2678    #[serde(default)]
2679    pub kp_id: Option<f64>,
2680    /// Inner d-axis current PI integral gain (1/s). Defaults to 100.0.
2681    #[serde(default)]
2682    pub ki_id: Option<f64>,
2683    /// Inner q-axis current PI proportional gain. Defaults to 1.0.
2684    #[serde(default)]
2685    pub kp_iq: Option<f64>,
2686    /// Inner q-axis current PI integral gain (1/s). Defaults to 100.0.
2687    #[serde(default)]
2688    pub ki_iq: Option<f64>,
2689    /// MVA base.
2690    pub mbase: f64,
2691    /// Rectifier terminal bus number.
2692    pub rectifier_bus: u32,
2693    /// Inverter terminal bus number.
2694    pub inverter_bus: u32,
2695}
2696
2697// ---------------------------------------------------------------------------
2698// Phase 12: Load dynamic model records
2699// ---------------------------------------------------------------------------
2700
2701/// A load dynamic model record — attaches to a load bus (not a generator bus).
2702#[derive(Debug, Clone, Serialize, Deserialize)]
2703pub struct LoadDyn {
2704    /// Bus number of the associated static load.
2705    pub bus: u32,
2706    /// Load ID (matches PSS/E load record ID, e.g. `"1"`, `"L1"`).
2707    pub load_id: String,
2708    /// The specific load model and its parameters.
2709    pub model: LoadModel,
2710}
2711
2712/// Discriminated union of supported load dynamic models.
2713#[derive(Debug, Clone, Serialize, Deserialize)]
2714#[serde(tag = "type")]
2715pub enum LoadModel {
2716    /// CLOD — PSS/E composite load (large motor, small motor, discharge lighting, non-conforming).
2717    Clod(ClodParams),
2718    /// INDMOT — Generic 3rd-order induction motor aggregate.
2719    Indmot(IndmotParams),
2720    /// MOTOR — Single-phase induction motor / AC compressor (2nd-order).
2721    Motor(MotorParams),
2722    /// CMPLDW — Composite load model with motors (Phase 16).
2723    Cmpldw(CmpldwParams),
2724    /// CMPLDWG — CMPLDW with embedded generation (Phase 16).
2725    Cmpldwg(CmpldwgParams),
2726    /// CMLDBLU2 — Composite load simplified blue model (Phase 16).
2727    Cmldblu2(Cmldblu2Params),
2728    /// CMLDARU2 — Composite load ARU2 model (Phase 16).
2729    Cmldaru2(Cmldblu2Params),
2730    /// MOTORW — Type W induction motor (Phase 16).
2731    Motorw(MotorwParams),
2732    /// CIM5 — Current injection motor model 5th order (Phase 16).
2733    Cim5(Cim5Params),
2734    // Phase 27
2735    /// LCFB1 — Load compensator with frequency bias (Phase 27, 2 states).
2736    Lcfb1(Lcfb1Params),
2737    /// LDFRAL — Dynamic load frequency regulation (Phase 27, 2 states).
2738    Ldfral(LdfralParams),
2739    /// FRQTPLT — Frequency relay trip (Phase 27, 1 state + bool flag).
2740    Frqtplt(FrqtpltParams),
2741    /// LVSHBL — Low-voltage shunt block (Phase 27, 1 state + bool flag).
2742    Lvshbl(LvshblParams),
2743    // Wave 34: Additional load variants
2744    /// CIM6 — 6th-order induction motor (extends CIM5 with q-axis transient state).
2745    Cim6(Cim6Params),
2746    /// CIMW — Composite Wind Induction Motor (alias to INDMOT dynamics).
2747    Cimw(IndmotParams),
2748    /// EXTL — External Load (simplified composite, 2 states).
2749    Extl(ExtlParams),
2750    /// IEELAR — IEEE Load Aggregation (alias to EXTL structure).
2751    Ieelar(ExtlParams),
2752    /// CMLDOWU2 — CLM Owner variant (alias to CMPLDW).
2753    Cmldowu2(CmpldwParams),
2754    /// CMLDXNU2 — CLM Zone variant (alias to CMPLDW).
2755    Cmldxnu2(CmpldwParams),
2756    /// CMLDALU2 — CLM All-utilities variant (alias to CMPLDW).
2757    Cmldalu2(CmpldwParams),
2758    /// CMLDBLU2W — CLM Blue with wind (alias to CMLDBLU2).
2759    Cmldblu2w(Cmldblu2Params),
2760    /// CMLDARU2W — CLM ARU2 with wind (alias to CMLDARU2).
2761    Cmldaru2w(Cmldblu2Params),
2762    // Wave 35: Generator protection relays
2763    /// VTGTPAT — Voltage-Time Generator Protection Trip (continuous).
2764    Vtgtpat(VtgtpatParams),
2765    /// VTGDCAT — Voltage-Time Discrete Generator Protection (alias to VTGTPAT dynamics).
2766    Vtgdcat(VtgtpatParams),
2767    /// FRQTPAT — Frequency-Time Generator Protection Trip (continuous).
2768    Frqtpat(FrqtpatParams),
2769    /// FRQDCAT — Frequency-Time Discrete Generator Protection (alias to FRQTPAT dynamics).
2770    Frqdcat(FrqtpatParams),
2771    // Wave 36: protection models
2772    /// DISTR1 — Distance relay (line protection, Wave 36).
2773    Distr1(Distr1Params),
2774    /// BFR50 — Breaker failure relay (ANSI 50BF).
2775    Bfr50(Bfr50Params),
2776    /// LVSHC1 — Low voltage shunt capacitor (Wave 36, alias LvshblParams).
2777    Lvshc1(LvshblParams),
2778    // Wave 7 (B10): additional protection relay models
2779    /// 87T — Transformer differential relay.
2780    TransDiff87(TransDiff87Params),
2781    /// 87L — Line differential relay (trips branch, not generator).
2782    LineDiff87l(LineDiff87lParams),
2783    /// 79 — Automatic recloser (trips + auto-reclose sequence).
2784    Recloser79(Recloser79Params),
2785    // Wave 37: CLM DG variants
2786    /// CMLDDGU2 — CMPLDW with embedded distributed generation.
2787    Cmlddgu2(CmpldwParams),
2788    /// CMLDDGGU2 — CMPLDWG with embedded distributed generation.
2789    Cmlddggu2(CmpldwgParams),
2790    /// CMLDOWDGU2 — CMLDOWU2 with embedded distributed generation.
2791    Cmldowdgu2(CmpldwParams),
2792    /// CMLDXNDGU2 — CMLDXNU2 with embedded distributed generation.
2793    Cmldxndgu2(CmpldwParams),
2794    /// UVLS1 — Under-Voltage Load Shedding relay (single stage, per-bus).
2795    Uvls1(Uvls1Params),
2796}
2797
2798// --- CLOD -------------------------------------------------------------------
2799
2800/// PSS/E composite load model (CLOD).
2801///
2802/// Models a mix of load components at a load bus: large motors, small motors,
2803/// discharge lighting, and non-conforming constant-power loads.
2804///
2805/// PSS/E params: `lfac rfrac xfrac lfrac nfrac dsli tv tf vtd vtu ftd ftu td`
2806#[derive(Debug, Clone, Serialize, Deserialize)]
2807pub struct ClodParams {
2808    /// MVA base (from load record; used to scale power).
2809    pub mbase: f64,
2810    /// Load factor — fraction of total bus load this model represents (0..1].
2811    pub lfac: f64,
2812    /// Large motor fraction (of modelled load).
2813    pub rfrac: f64,
2814    /// Small motor fraction (of modelled load).
2815    pub xfrac: f64,
2816    /// Discharge lighting fraction (of modelled load).
2817    pub lfrac_dl: f64,
2818    /// Non-conforming constant power fraction (of modelled load).
2819    pub nfrac: f64,
2820    /// Discharge lighting initial state.
2821    pub dsli: f64,
2822    /// Voltage transient time constant (s) — motor slip recovery (typ 0.025 s).
2823    pub tv: f64,
2824    /// Frequency transient time constant (s) — lighting recovery (typ 0.02 s).
2825    pub tf: f64,
2826    /// Low voltage trip threshold (pu, typ 0.75).
2827    pub vtd: f64,
2828    /// High voltage trip threshold (pu, typ 1.2).
2829    pub vtu: f64,
2830    /// Low frequency trip threshold (Hz, typ 57.5).
2831    pub ftd: f64,
2832    /// High frequency trip threshold (Hz, typ 61.5).
2833    pub ftu: f64,
2834    /// Trip delay (s, typ 0.05).
2835    pub td: f64,
2836}
2837
2838// --- INDMOT -----------------------------------------------------------------
2839
2840/// Generic 3rd-order induction motor aggregate (PSS/E INDMOT).
2841///
2842/// Single-cage rotor with slip dynamics — represents a group of similar
2843/// induction motors aggregated to a single equivalent.
2844///
2845/// PSS/E params: `h d ra xs xr xm rr mbase lfac`
2846#[derive(Debug, Clone, Serialize, Deserialize)]
2847pub struct IndmotParams {
2848    /// Inertia constant (s).
2849    pub h: f64,
2850    /// Damping coefficient.
2851    pub d: f64,
2852    /// Stator resistance (pu machine base).
2853    pub ra: f64,
2854    /// Stator leakage reactance (pu machine base).
2855    pub xs: f64,
2856    /// Rotor leakage reactance (pu machine base).
2857    pub xr: f64,
2858    /// Magnetizing reactance (pu machine base).
2859    pub xm: f64,
2860    /// Rotor resistance (pu machine base).
2861    pub rr: f64,
2862    /// Transient time constant: `t0p = (xr + xm) / (omega0 * rr)` (s).
2863    pub t0p: f64,
2864    /// Transient reactance: `x0p = xs + xr*xm/(xr+xm)` (pu machine base).
2865    pub x0p: f64,
2866    /// MVA base of the motor aggregate.
2867    pub mbase: f64,
2868    /// Load fraction at this bus (0..1].
2869    pub lfac: f64,
2870    /// Initial (rated) slip — computed during init from power flow solution.
2871    pub slip0: f64,
2872    /// Initial electrical torque — computed during init for torque normalization.
2873    pub te0: f64,
2874}
2875
2876// --- MOTOR ------------------------------------------------------------------
2877
2878/// Single-phase induction motor / AC compressor — 2nd-order (PSS/E MOTOR).
2879///
2880/// Simplified model capturing the dominant dynamics of residential AC
2881/// compressors and single-phase induction motors.
2882///
2883/// PSS/E params: `h ra xs x0p t0p mbase lfac`
2884#[derive(Debug, Clone, Serialize, Deserialize)]
2885pub struct MotorParams {
2886    /// Inertia constant (s).
2887    pub h: f64,
2888    /// Stator resistance (pu machine base).
2889    pub ra: f64,
2890    /// Stator+rotor synchronous reactance (pu machine base).
2891    pub xs: f64,
2892    /// Transient reactance (pu machine base).
2893    pub x0p: f64,
2894    /// Transient time constant (s).
2895    pub t0p: f64,
2896    /// MVA base.
2897    pub mbase: f64,
2898    /// Load fraction at this bus (0..1].
2899    pub lfac: f64,
2900}
2901
2902// Unknown / fallback
2903// ---------------------------------------------------------------------------
2904
2905// ---------------------------------------------------------------------------
2906// Phase 15: Exciter param structs
2907// ---------------------------------------------------------------------------
2908
2909/// ESST5B — IEEE ST5B static exciter (Phase 15, 3 states: vr, vfilt, efd).
2910#[derive(Debug, Clone, Serialize, Deserialize)]
2911pub struct Esst5bParams {
2912    pub tr: f64,
2913    pub kc: f64,
2914    pub kf: f64,
2915    pub tf: f64,
2916    pub ka: f64,
2917    pub tb: f64,
2918    pub tc: f64,
2919    pub vrmax: f64,
2920    pub vrmin: f64,
2921    pub t1: f64,
2922    pub t2: f64,
2923}
2924
2925/// EXAC4 / AC4A — IEEE AC4A controlled-rectifier exciter (Phase 15, 2 states: vr, efd).
2926#[derive(Debug, Clone, Serialize, Deserialize)]
2927pub struct Exac4Params {
2928    pub tr: f64,
2929    pub tc: f64,
2930    pub tb: f64,
2931    pub ka: f64,
2932    pub ta: f64,
2933    pub vrmax: f64,
2934    pub vrmin: f64,
2935    pub kc: f64,
2936}
2937
2938// ---------------------------------------------------------------------------
2939// Phase 15: Governor param structs
2940// ---------------------------------------------------------------------------
2941
2942/// TGOV5 — Multi-reheat steam governor HP+IP (Phase 15, 4 states).
2943#[derive(Debug, Clone, Serialize, Deserialize)]
2944pub struct Tgov5Params {
2945    /// Droop (speed regulation, pu).
2946    pub r: f64,
2947    /// Governor valve time constant (s).
2948    pub t1: f64,
2949    /// HP stage time constant (s).
2950    pub t2: f64,
2951    /// IP+LP stage time constant (s).
2952    pub t3: f64,
2953    /// Output time constant (s).
2954    pub t4: f64,
2955    /// HP power fraction.
2956    pub k1: f64,
2957    /// IP power fraction.
2958    pub k2: f64,
2959    /// LP power fraction.
2960    pub k3: f64,
2961    /// Maximum power output (pu).
2962    pub pmax: f64,
2963    /// Minimum power output (pu).
2964    pub pmin: f64,
2965}
2966
2967/// GAST2A — Advanced Rowen gas turbine governor (Phase 15, 4 states).
2968///
2969/// Extension of GAST with ambient temperature (radiation loss) state.
2970#[derive(Debug, Clone, Serialize, Deserialize)]
2971pub struct Gast2aParams {
2972    /// Droop (speed regulation, pu).
2973    pub r: f64,
2974    /// Governor valve time constant (s).
2975    pub t1: f64,
2976    /// Turbine output time constant (s).
2977    pub t2: f64,
2978    /// Exhaust temperature time constant (s).
2979    pub t3: f64,
2980    /// Ambient temperature time constant (s).
2981    pub t4: f64,
2982    /// Ambient temperature load limit (pu).
2983    pub at: f64,
2984    /// Exhaust temperature coefficient.
2985    pub kt: f64,
2986    /// Minimum governor output (pu).
2987    pub vmin: f64,
2988    /// Maximum governor output (pu).
2989    pub vmax: f64,
2990}
2991
2992// ---------------------------------------------------------------------------
2993// Phase 15: PSS param structs
2994// ---------------------------------------------------------------------------
2995
2996/// STAB2A — WSCC stabilizer variant A (Phase 15, 3 states: x1, x2, x3).
2997///
2998/// Double lead-lag washout stabilizer.
2999#[derive(Debug, Clone, Serialize, Deserialize)]
3000pub struct Stab2aParams {
3001    /// Stabilizer gain.
3002    pub ks: f64,
3003    /// Washout time constant (s).
3004    pub t1: f64,
3005    /// Lead-lag 1 numerator (s).
3006    pub t2: f64,
3007    /// Lead-lag 1 denominator (s).
3008    pub t3: f64,
3009    /// Lead-lag 2 numerator (s).
3010    pub t4: f64,
3011    /// Lead-lag 2 denominator (s).
3012    pub t5: f64,
3013    /// Output limit (pu, symmetric ±HLIM).
3014    pub hlim: f64,
3015}
3016
3017/// PSS4B — Four-band multi-frequency PSS (Phase 15, 4 states).
3018#[derive(Debug, Clone, Serialize, Deserialize)]
3019pub struct Pss4bParams {
3020    /// Low-band gain.
3021    pub kl: f64,
3022    /// High-band gain.
3023    pub kh: f64,
3024    /// Low-band washout time constant (s).
3025    pub tw1: f64,
3026    /// High-band washout time constant (s).
3027    pub tw2: f64,
3028    /// Lead-lag 1 numerator (s).
3029    pub t1: f64,
3030    /// Lead-lag 1 denominator (s).
3031    pub t2: f64,
3032    /// Lead-lag 2 numerator (s).
3033    pub t3: f64,
3034    /// Lead-lag 2 denominator (s).
3035    pub t4: f64,
3036    /// Maximum PSS output (pu).
3037    pub vstmax: f64,
3038    /// Minimum PSS output (pu).
3039    pub vstmin: f64,
3040}
3041
3042// ---------------------------------------------------------------------------
3043// Phase 15: FACTS param structs
3044// ---------------------------------------------------------------------------
3045
3046/// CSVGN3 — SVC with slope/droop regulator (Phase 15, 3 states, same as CSVGN1 + slope).
3047#[derive(Debug, Clone, Serialize, Deserialize)]
3048pub struct Csvgn3Params {
3049    pub t1: f64,
3050    pub t2: f64,
3051    pub t3: f64,
3052    pub t4: f64,
3053    pub t5: f64,
3054    pub k: f64,
3055    /// Slope (droop) coefficient: Vref = Vref_base + slope*b_svc.
3056    pub slope: f64,
3057    pub vmax: f64,
3058    pub vmin: f64,
3059    pub bmax: f64,
3060    pub bmin: f64,
3061    pub mbase: f64,
3062}
3063
3064/// CDC7T — LCC HVDC + runback + current order controllers (Phase 15, 6 states).
3065///
3066/// Extends CDC4T with runback rate and current order max.
3067#[derive(Debug, Clone, Serialize, Deserialize)]
3068pub struct Cdc7tParams {
3069    pub setvl: f64,
3070    pub vschd: f64,
3071    pub mbase: f64,
3072    pub tr: f64,
3073    pub td: f64,
3074    pub alpha_min: f64,
3075    pub alpha_max: f64,
3076    pub gamma_min: f64,
3077    pub rectifier_bus: u32,
3078    pub inverter_bus: u32,
3079    /// Runback rate (pu/s).
3080    pub runback_rate: f64,
3081    /// Maximum current order (pu).
3082    pub current_order_max: f64,
3083}
3084
3085// ---------------------------------------------------------------------------
3086// Phase 16: Composite Load param structs
3087// ---------------------------------------------------------------------------
3088
3089/// Per-motor circuit parameters for WECC CMPLDW composite load model.
3090#[derive(Debug, Clone, Serialize, Deserialize)]
3091pub struct CmpldwMotorParams {
3092    /// Stator resistance (pu).
3093    pub ra: f64,
3094    /// Magnetizing reactance (pu).
3095    pub xm: f64,
3096    /// Rotor resistance (pu).
3097    pub r1: f64,
3098    /// Rotor reactance (pu).
3099    pub x1: f64,
3100    /// Second cage rotor resistance (pu, 0 = single-cage).
3101    pub r2: f64,
3102    /// Second cage rotor reactance (pu, 0 = single-cage).
3103    pub x2: f64,
3104    /// Motor inertia constant H (s). Default 0.5.
3105    pub h: f64,
3106    /// Voltage trip threshold (pu). Motor trips below this voltage.
3107    pub vtr: f64,
3108    /// Motor load torque exponent (0=constant torque, 2=fan/pump).
3109    pub etrq: f64,
3110}
3111
3112impl Default for CmpldwMotorParams {
3113    fn default() -> Self {
3114        Self {
3115            ra: 0.0,
3116            xm: 3.0,
3117            r1: 0.04,
3118            x1: 0.1,
3119            r2: 0.04,
3120            x2: 0.1,
3121            h: 0.5,
3122            vtr: 0.0,
3123            etrq: 0.0,
3124        }
3125    }
3126}
3127
3128/// CMPLDW — Full WECC composite load model with 3 motors (Phase 4.1, 10 states).
3129///
3130/// The model represents a composite load bus with three induction motor types
3131/// (A=large industrial, B=small commercial, C=A/C compressor) plus static ZIP
3132/// and electronic load fractions. Each motor has independent slip/flux dynamics.
3133///
3134/// # States (10 total)
3135/// - Motor A: slip_a, ed_a, eq_a (3)
3136/// - Motor B: slip_b, ed_b, eq_b (3)
3137/// - Motor C: slip_c, ed_c, eq_c (3)
3138/// - Voltage filter: vfilt (1)
3139#[derive(Debug, Clone, Serialize, Deserialize)]
3140pub struct CmpldwParams {
3141    /// Load fraction: Motor A (large 3-phase industrial).
3142    pub lfma: f64,
3143    /// Load fraction: Motor B (small 3-phase commercial).
3144    pub lfmb: f64,
3145    /// Load fraction: Motor C (1-phase A/C compressor).
3146    pub lfmc: f64,
3147    /// Static load P coefficient 1.
3148    pub kp1: f64,
3149    /// Static load P exponent 1.
3150    pub np1: f64,
3151    /// Static load P coefficient 2.
3152    pub kp2: f64,
3153    /// Static load P exponent 2.
3154    pub np2: f64,
3155    /// Static load Q coefficient 1.
3156    pub kq1: f64,
3157    /// Static load Q exponent 1.
3158    pub nq1: f64,
3159    /// Static load Q coefficient 2.
3160    pub kq2: f64,
3161    /// Static load Q exponent 2.
3162    pub nq2: f64,
3163    // Legacy single-motor fields (kept for backward compat with existing DYR files).
3164    // Used as Motor A defaults when per-motor params are None.
3165    pub ra: f64,
3166    pub xm: f64,
3167    pub r1: f64,
3168    pub x1: f64,
3169    pub r2: f64,
3170    pub x2: f64,
3171    pub vtr1: f64,
3172    pub vtr2: f64,
3173    pub mbase: f64,
3174    /// Per-motor parameters for Motor A (overrides ra/xm/r1/x1 if present).
3175    #[serde(default)]
3176    pub motor_a: Option<CmpldwMotorParams>,
3177    /// Per-motor parameters for Motor B.
3178    #[serde(default)]
3179    pub motor_b: Option<CmpldwMotorParams>,
3180    /// Per-motor parameters for Motor C.
3181    #[serde(default)]
3182    pub motor_c: Option<CmpldwMotorParams>,
3183    /// Voltage filter time constant (s). Default 0.02.
3184    #[serde(default = "default_tv")]
3185    pub tv: f64,
3186    /// Electronic load fraction. Default 0.0.
3187    #[serde(default)]
3188    pub fel: f64,
3189    /// Frequency sensitivity of P (pu/Hz). Default 0.0.
3190    #[serde(default)]
3191    pub pfreq: f64,
3192}
3193
3194fn default_tv() -> f64 {
3195    0.02
3196}
3197
3198impl CmpldwParams {
3199    /// Get Motor A parameters (uses per-motor if set, else legacy single-motor fields).
3200    pub fn motor_a_params(&self) -> CmpldwMotorParams {
3201        self.motor_a.clone().unwrap_or(CmpldwMotorParams {
3202            ra: self.ra,
3203            xm: self.xm,
3204            r1: self.r1,
3205            x1: self.x1,
3206            r2: self.r2,
3207            x2: self.x2,
3208            h: 0.5,
3209            vtr: self.vtr1,
3210            etrq: 0.0,
3211        })
3212    }
3213
3214    /// Get Motor B parameters (uses per-motor if set, else default small motor).
3215    pub fn motor_b_params(&self) -> CmpldwMotorParams {
3216        self.motor_b.clone().unwrap_or(CmpldwMotorParams {
3217            ra: self.ra,
3218            xm: self.xm * 1.5, // higher Xm for small motor
3219            r1: self.r1 * 1.2,
3220            x1: self.x1 * 1.2,
3221            r2: self.r2 * 1.2,
3222            x2: self.x2 * 1.2,
3223            h: 0.3, // lower inertia
3224            vtr: self.vtr2,
3225            etrq: 0.0,
3226        })
3227    }
3228
3229    /// Get Motor C parameters (uses per-motor if set, else default A/C compressor).
3230    pub fn motor_c_params(&self) -> CmpldwMotorParams {
3231        self.motor_c.clone().unwrap_or(CmpldwMotorParams {
3232            ra: self.ra * 0.8,
3233            xm: self.xm * 2.0, // higher Xm for single-phase
3234            r1: self.r1 * 0.8,
3235            x1: self.x1 * 0.8,
3236            r2: self.r2 * 0.8,
3237            x2: self.x2 * 0.8,
3238            h: 0.1, // very low inertia (compressor)
3239            vtr: self.vtr2,
3240            etrq: 2.0, // fan/pump torque
3241        })
3242    }
3243}
3244
3245/// CMPLDWG — CMPLDW with embedded generation (Phase 16, 10 states).
3246#[derive(Debug, Clone, Serialize, Deserialize)]
3247pub struct CmpldwgParams {
3248    pub lfma: f64,
3249    pub lfmb: f64,
3250    pub lfmc: f64,
3251    pub kp1: f64,
3252    pub np1: f64,
3253    pub kp2: f64,
3254    pub np2: f64,
3255    pub kq1: f64,
3256    pub nq1: f64,
3257    pub kq2: f64,
3258    pub nq2: f64,
3259    pub ra: f64,
3260    pub xm: f64,
3261    pub r1: f64,
3262    pub x1: f64,
3263    pub r2: f64,
3264    pub x2: f64,
3265    pub vtr1: f64,
3266    pub vtr2: f64,
3267    pub mbase: f64,
3268    pub gen_mw: f64,
3269    /// Per-motor parameters for Motor A.
3270    #[serde(default)]
3271    pub motor_a: Option<CmpldwMotorParams>,
3272    /// Per-motor parameters for Motor B.
3273    #[serde(default)]
3274    pub motor_b: Option<CmpldwMotorParams>,
3275    /// Per-motor parameters for Motor C.
3276    #[serde(default)]
3277    pub motor_c: Option<CmpldwMotorParams>,
3278    /// Voltage filter time constant (s). Default 0.02.
3279    #[serde(default = "default_tv")]
3280    pub tv: f64,
3281    /// Electronic load fraction. Default 0.0.
3282    #[serde(default)]
3283    pub fel: f64,
3284    /// Frequency sensitivity of P (pu/Hz). Default 0.0.
3285    #[serde(default)]
3286    pub pfreq: f64,
3287}
3288
3289impl CmpldwgParams {
3290    /// Get Motor A parameters.
3291    pub fn motor_a_params(&self) -> CmpldwMotorParams {
3292        self.motor_a.clone().unwrap_or(CmpldwMotorParams {
3293            ra: self.ra,
3294            xm: self.xm,
3295            r1: self.r1,
3296            x1: self.x1,
3297            r2: self.r2,
3298            x2: self.x2,
3299            h: 0.5,
3300            vtr: self.vtr1,
3301            etrq: 0.0,
3302        })
3303    }
3304    /// Get Motor B parameters.
3305    pub fn motor_b_params(&self) -> CmpldwMotorParams {
3306        self.motor_b.clone().unwrap_or(CmpldwMotorParams {
3307            ra: self.ra,
3308            xm: self.xm * 1.5,
3309            r1: self.r1 * 1.2,
3310            x1: self.x1 * 1.2,
3311            r2: self.r2 * 1.2,
3312            x2: self.x2 * 1.2,
3313            h: 0.3,
3314            vtr: self.vtr2,
3315            etrq: 0.0,
3316        })
3317    }
3318    /// Get Motor C parameters.
3319    pub fn motor_c_params(&self) -> CmpldwMotorParams {
3320        self.motor_c.clone().unwrap_or(CmpldwMotorParams {
3321            ra: self.ra * 0.8,
3322            xm: self.xm * 2.0,
3323            r1: self.r1 * 0.8,
3324            x1: self.x1 * 0.8,
3325            r2: self.r2 * 0.8,
3326            x2: self.x2 * 0.8,
3327            h: 0.1,
3328            vtr: self.vtr2,
3329            etrq: 2.0,
3330        })
3331    }
3332}
3333
3334/// CMLDBLU2 — Composite load simplified blue model (Phase 16).
3335#[derive(Debug, Clone, Serialize, Deserialize)]
3336pub struct Cmldblu2Params {
3337    pub t1: f64,
3338    pub t2: f64,
3339    pub k1: f64,
3340    pub k2: f64,
3341    pub pf: f64,
3342    pub kp: f64,
3343    pub kq: f64,
3344    pub vmin: f64,
3345    pub vmax: f64,
3346    pub mbase: f64,
3347}
3348
3349/// CMLDARU2 — Composite load ARU2 model (Phase 16, same params as CMLDBLU2).
3350pub type Cmldaru2Params = Cmldblu2Params;
3351
3352/// MOTORW — Type W induction motor (Phase 16).
3353#[derive(Debug, Clone, Serialize, Deserialize)]
3354pub struct MotorwParams {
3355    pub ra: f64,
3356    pub xm: f64,
3357    pub r1: f64,
3358    pub x1: f64,
3359    pub r2: f64,
3360    pub x2: f64,
3361    pub h: f64,
3362    pub vtr1: f64,
3363    pub vtr2: f64,
3364    pub mbase: f64,
3365}
3366
3367/// CIM5 — Current injection motor model 5th order (Phase 16).
3368#[derive(Debug, Clone, Serialize, Deserialize)]
3369pub struct Cim5Params {
3370    pub ra: f64,
3371    pub xs: f64,
3372    pub xm: f64,
3373    pub xr1: f64,
3374    pub xr2: f64,
3375    pub rr1: f64,
3376    pub rr2: f64,
3377    pub h: f64,
3378    pub e1: f64,
3379    pub s1: f64,
3380    pub e2: f64,
3381    pub s2: f64,
3382    pub mbase: f64,
3383}
3384
3385// ---------------------------------------------------------------------------
3386// Phase 17: Exciter param structs
3387// ---------------------------------------------------------------------------
3388
3389/// ESST6B — IEEE ST6B Static Exciter (Phase 17).
3390#[derive(Debug, Clone, Serialize, Deserialize)]
3391pub struct Esst6bParams {
3392    pub tr: f64,
3393    pub ilr: f64,
3394    pub klr: f64,
3395    pub ka: f64,
3396    pub ta: f64,
3397    pub kc: f64,
3398    pub vrmax: f64,
3399    pub vrmin: f64,
3400    pub kff: f64,
3401    pub kgff: f64,
3402    pub t1: f64,
3403    pub t2: f64,
3404}
3405
3406/// ESST7B — IEEE ST7B Static Exciter (Phase 17).
3407#[derive(Debug, Clone, Serialize, Deserialize)]
3408pub struct Esst7bParams {
3409    pub tr: f64,
3410    pub kpa: f64,
3411    pub kia: f64,
3412    pub vrmax: f64,
3413    pub vrmin: f64,
3414    pub kpff: f64,
3415    pub kh: f64,
3416    pub vmax: f64,
3417    pub vmin: f64,
3418    pub t1: f64,
3419    pub t2: f64,
3420    pub t3: f64,
3421    pub t4: f64,
3422    pub kl: f64,
3423}
3424
3425/// ESAC6A — AC6A Rotating Exciter (Phase 17).
3426#[derive(Debug, Clone, Serialize, Deserialize)]
3427pub struct Esac6aParams {
3428    pub tr: f64,
3429    pub ka: f64,
3430    pub ta: f64,
3431    pub tk: f64,
3432    pub tb: f64,
3433    pub tc: f64,
3434    pub vamax: f64,
3435    pub vamin: f64,
3436    pub vrmax: f64,
3437    pub vrmin: f64,
3438    pub te: f64,
3439    pub kh: f64,
3440    pub kf: f64,
3441    pub tf: f64,
3442    pub kc: f64,
3443    pub kd: f64,
3444    pub ke: f64,
3445}
3446
3447/// ESDC1A — DC1A Rotating Exciter (Phase 17).
3448#[derive(Debug, Clone, Serialize, Deserialize)]
3449pub struct Esdc1aParams {
3450    pub tr: f64,
3451    pub ka: f64,
3452    pub ta: f64,
3453    pub kf: f64,
3454    pub tf: f64,
3455    pub ke: f64,
3456    pub te: f64,
3457    pub se1: f64,
3458    pub e1: f64,
3459    pub se2: f64,
3460    pub e2: f64,
3461    pub vrmax: f64,
3462    pub vrmin: f64,
3463}
3464
3465/// EXST2 — Static Exciter Type ST2 (Phase 17).
3466#[derive(Debug, Clone, Serialize, Deserialize)]
3467pub struct Exst2Params {
3468    pub tr: f64,
3469    pub ka: f64,
3470    pub ta: f64,
3471    pub vrmax: f64,
3472    pub vrmin: f64,
3473    pub kc: f64,
3474    pub ki: f64,
3475    pub ke: f64,
3476    pub te: f64,
3477}
3478
3479/// AC8B — IEEE AC8B High Initial Response Exciter (Phase 17).
3480#[derive(Debug, Clone, Serialize, Deserialize)]
3481pub struct Ac8bParams {
3482    pub tr: f64,
3483    pub ka: f64,
3484    pub ta: f64,
3485    pub kc: f64,
3486    pub vrmax: f64,
3487    pub vrmin: f64,
3488    pub kd: f64,
3489    pub ke: f64,
3490    pub te: f64,
3491    pub pid_kp: f64,
3492    pub pid_ki: f64,
3493    pub pid_kd: f64,
3494}
3495
3496/// BBSEX1 — Bus-Branch Static Exciter 1 (Phase 17).
3497#[derive(Debug, Clone, Serialize, Deserialize)]
3498pub struct Bbsex1Params {
3499    pub t1r: f64,
3500    pub t2r: f64,
3501    pub t3r: f64,
3502    pub t4r: f64,
3503    pub t1i: f64,
3504    pub t2i: f64,
3505    pub ka: f64,
3506    pub ta: f64,
3507    pub vrmax: f64,
3508    pub vrmin: f64,
3509}
3510
3511/// IEEET3 — IEEE Type 3 Rotating Exciter (Phase 17).
3512#[derive(Debug, Clone, Serialize, Deserialize)]
3513pub struct Ieeet3Params {
3514    pub tr: f64,
3515    pub ka: f64,
3516    pub ta: f64,
3517    pub vrmax: f64,
3518    pub vrmin: f64,
3519    pub kf: f64,
3520    pub tf: f64,
3521    pub ke: f64,
3522    pub te: f64,
3523    pub e1: f64,
3524    pub se1: f64,
3525    pub e2: f64,
3526    pub se2: f64,
3527    pub kp: f64,
3528    pub ki: f64,
3529    pub kc: f64,
3530}
3531
3532// ---------------------------------------------------------------------------
3533// Phase 18: Governor + PSS param structs
3534// ---------------------------------------------------------------------------
3535
3536/// H6E — Hydro Governor 6 Elements (Phase 18).
3537#[derive(Debug, Clone, Serialize, Deserialize)]
3538pub struct H6eParams {
3539    pub r: f64,
3540    pub tr: f64,
3541    pub tf: f64,
3542    pub tg: f64,
3543    pub tw: f64,
3544    pub t1: f64,
3545    pub t2: f64,
3546    pub t3: f64,
3547    pub t4: f64,
3548    pub t5: f64,
3549    pub dt: f64,
3550    pub pmax: f64,
3551    pub pmin: f64,
3552}
3553
3554/// WSHYGP — Wind-Synchronous Hydro Governor+Pitch (Phase 18).
3555#[derive(Debug, Clone, Serialize, Deserialize)]
3556pub struct WshygpParams {
3557    pub r: f64,
3558    pub tf: f64,
3559    pub tg: f64,
3560    pub tw: f64,
3561    pub kd: f64,
3562    pub pmax: f64,
3563    pub pmin: f64,
3564    pub kp: f64,
3565    pub ki: f64,
3566}
3567
3568/// STAB3 — Three-Band PSS (Phase 18).
3569#[derive(Debug, Clone, Serialize, Deserialize)]
3570pub struct Stab3Params {
3571    pub ks: f64,
3572    pub t1: f64,
3573    pub t2: f64,
3574    pub t3: f64,
3575    pub t4: f64,
3576    pub t5: f64,
3577    pub t6: f64,
3578    pub vstmax: f64,
3579    pub vstmin: f64,
3580}
3581
3582/// PSS3B — Three-Input Power System Stabilizer (Phase 18).
3583#[derive(Debug, Clone, Serialize, Deserialize)]
3584pub struct Pss3bParams {
3585    pub a1: f64,
3586    pub a2: f64,
3587    pub a3: f64,
3588    pub a4: f64,
3589    pub a5: f64,
3590    pub a6: f64,
3591    pub a7: f64,
3592    pub a8: f64,
3593    pub vsi1max: f64,
3594    pub vsi1min: f64,
3595    pub vsi2max: f64,
3596    pub vsi2min: f64,
3597    pub vstmax: f64,
3598    pub vstmin: f64,
3599}
3600
3601// ---------------------------------------------------------------------------
3602// Phase 19: IBR Wind Controller param structs (go in exciter slot)
3603// ---------------------------------------------------------------------------
3604
3605/// WT3E1 — Type 3 Wind Electrical Controller (Phase 19).
3606#[derive(Debug, Clone, Serialize, Deserialize)]
3607pub struct Wt3e1Params {
3608    pub kpv: f64,
3609    pub kiv: f64,
3610    pub kqv: f64,
3611    pub xd: f64,
3612    pub kpq: f64,
3613    pub kiq: f64,
3614    pub tpe: f64,
3615    pub pmin: f64,
3616    pub pmax: f64,
3617    pub qmin: f64,
3618    pub qmax: f64,
3619    pub imax: f64,
3620    /// Voltage measurement filter time constant (s, default 0.05).
3621    pub tv: f64,
3622}
3623
3624/// WT3E2 — Type 3 Wind Electrical Controller Variant 2 (Phase 19).
3625#[derive(Debug, Clone, Serialize, Deserialize)]
3626pub struct Wt3e2Params {
3627    pub kpv: f64,
3628    pub kiv: f64,
3629    pub kqv: f64,
3630    pub xd: f64,
3631    pub kpq: f64,
3632    pub kiq: f64,
3633    pub tpe: f64,
3634    pub pmin: f64,
3635    pub pmax: f64,
3636    pub qmin: f64,
3637    pub qmax: f64,
3638    pub imax: f64,
3639    pub tiq: f64,
3640    /// Voltage measurement filter time constant (s, default 0.05).
3641    pub tv: f64,
3642}
3643
3644/// WT4E1 — Type 4 Wind Electrical Controller (Phase 19).
3645#[derive(Debug, Clone, Serialize, Deserialize)]
3646pub struct Wt4e1Params {
3647    pub kpv: f64,
3648    pub kiv: f64,
3649    pub tpe: f64,
3650    pub pmin: f64,
3651    pub pmax: f64,
3652    pub qmin: f64,
3653    pub qmax: f64,
3654    pub imax: f64,
3655}
3656
3657/// WT4E2 — Type 4 Wind Electrical Controller Variant 2 (Phase 19, same as WT4E1).
3658pub type Wt4e2Params = Wt4e1Params;
3659
3660/// REPCB — REPCA Variant B (Phase 19).
3661#[derive(Debug, Clone, Serialize, Deserialize)]
3662pub struct RepcbParams {
3663    pub tp: f64,
3664    pub tfltr: f64,
3665    pub kp: f64,
3666    pub ki: f64,
3667    pub tft: f64,
3668    pub tfv: f64,
3669    pub qmax: f64,
3670    pub qmin: f64,
3671    pub vmax: f64,
3672    pub vmin: f64,
3673    pub kc: f64,
3674    pub refs: f64,
3675}
3676
3677// ---------------------------------------------------------------------------
3678// Phase 20: FACTS param structs
3679// ---------------------------------------------------------------------------
3680
3681/// CSVGN4 — SVC with 4 states (adds POD) (Phase 20).
3682#[derive(Debug, Clone, Serialize, Deserialize)]
3683pub struct Csvgn4Params {
3684    pub t1: f64,
3685    pub t2: f64,
3686    pub t3: f64,
3687    pub t4: f64,
3688    pub t5: f64,
3689    pub k: f64,
3690    pub slope: f64,
3691    pub kpod: f64,
3692    pub tpod: f64,
3693    pub vmax: f64,
3694    pub vmin: f64,
3695    pub bmax: f64,
3696    pub bmin: f64,
3697    pub mbase: f64,
3698}
3699
3700/// CSVGN5 — SVC with 4 states (voltage support mode) (Phase 20).
3701#[derive(Debug, Clone, Serialize, Deserialize)]
3702pub struct Csvgn5Params {
3703    pub t1: f64,
3704    pub t2: f64,
3705    pub t3: f64,
3706    pub t4: f64,
3707    pub t5: f64,
3708    pub k: f64,
3709    pub kv: f64,
3710    pub kpod: f64,
3711    pub tpod: f64,
3712    pub vmax: f64,
3713    pub vmin: f64,
3714    pub bmax: f64,
3715    pub bmin: f64,
3716    pub mbase: f64,
3717}
3718
3719/// CDC6T — LCC HVDC with enhanced controls (Phase 20).
3720#[derive(Debug, Clone, Serialize, Deserialize)]
3721pub struct Cdc6tParams {
3722    pub setvl: f64,
3723    pub vschd: f64,
3724    pub mbase: f64,
3725    pub tr: f64,
3726    pub td: f64,
3727    pub alpha_min: f64,
3728    pub alpha_max: f64,
3729    pub gamma_min: f64,
3730    pub rectifier_bus: u32,
3731    pub inverter_bus: u32,
3732    pub i_limit: f64,
3733}
3734
3735/// CSTCNT — STATCOM with N controls (4 states) (Phase 20).
3736#[derive(Debug, Clone, Serialize, Deserialize)]
3737pub struct CstcntParams {
3738    pub t1: f64,
3739    pub t2: f64,
3740    pub t3: f64,
3741    pub ka: f64,
3742    pub ta: f64,
3743    pub iqmax: f64,
3744    pub iqmin: f64,
3745    pub mbase: f64,
3746}
3747
3748/// MMC1 — Modular Multilevel Converter (5 states) (Phase 20).
3749#[derive(Debug, Clone, Serialize, Deserialize)]
3750pub struct Mmc1Params {
3751    pub tr: f64,
3752    pub kp_v: f64,
3753    pub ki_v: f64,
3754    pub kp_i: f64,
3755    pub ki_i: f64,
3756    pub vdc: f64,
3757    pub larm: f64,
3758    pub pmax: f64,
3759    pub pmin: f64,
3760    pub qmax: f64,
3761    pub qmin: f64,
3762    pub mbase: f64,
3763}
3764
3765// ---------------------------------------------------------------------------
3766// Phase 21: EXST3 + BESS param structs
3767// ---------------------------------------------------------------------------
3768
3769/// EXST3 — Static Exciter Type ST3 (Phase 21).
3770#[derive(Debug, Clone, Serialize, Deserialize)]
3771pub struct Exst3Params {
3772    pub tr: f64,
3773    pub ka: f64,
3774    pub ta: f64,
3775    pub tb: f64,
3776    pub tc: f64,
3777    pub vrmax: f64,
3778    pub vrmin: f64,
3779    pub kc: f64,
3780    pub ki: f64,
3781    pub km: f64,
3782    pub vmmax: f64,
3783    pub vmmin: f64,
3784    pub xm: f64,
3785}
3786
3787/// CBUFR — Buffer-Frequency-Regulated BESS (Phase 21).
3788#[derive(Debug, Clone, Serialize, Deserialize)]
3789pub struct CbufrParams {
3790    pub kf: f64,
3791    pub tf: f64,
3792    pub tp: f64,
3793    pub p_base: f64,
3794    pub p_min: f64,
3795    pub p_max: f64,
3796    pub e_cap: f64,
3797    /// Initial state of charge (0..1, default 0.5).
3798    pub soc_init: f64,
3799}
3800
3801/// CBUFD — Buffer-Frequency-Dependent BESS (Phase 21).
3802#[derive(Debug, Clone, Serialize, Deserialize)]
3803pub struct CbufdParams {
3804    pub kf: f64,
3805    pub tf: f64,
3806    pub tp: f64,
3807    pub tq: f64,
3808    pub p_base: f64,
3809    pub p_min: f64,
3810    pub p_max: f64,
3811    pub q_base: f64,
3812    pub q_min: f64,
3813    pub q_max: f64,
3814    pub e_cap: f64,
3815    /// Initial state of charge (0..1, default 0.5).
3816    pub soc_init: f64,
3817}
3818
3819/// REGFM_C1 — Grid-forming inverter C1 (Phase 21).
3820#[derive(Debug, Clone, Serialize, Deserialize)]
3821pub struct RegfmC1Params {
3822    pub kd: f64,
3823    pub ki: f64,
3824    pub kq: f64,
3825    pub tg: f64,
3826    pub ddn: f64,
3827    pub dup: f64,
3828    pub pmax: f64,
3829    pub pmin: f64,
3830    pub qmax: f64,
3831    pub qmin: f64,
3832    pub mbase: f64,
3833}
3834
3835// ---------------------------------------------------------------------------
3836// Phase 22: Solar PV Models
3837// ---------------------------------------------------------------------------
3838
3839/// PVGU1 — WECC 1st-gen photovoltaic converter unit.
3840/// Norton current injection, reuses RegcaState (4 fields: ip_cmd, iq_cmd, v_filt, x_eq/pm0).
3841#[derive(Debug, Clone, Serialize, Deserialize)]
3842pub struct Pvgu1Params {
3843    pub lvplsw: f64,
3844    pub rrpwr: f64,
3845    pub brkpt: f64,
3846    pub zerox: f64,
3847    pub lvpl1: f64,
3848    pub volim: f64,
3849    pub lvpnt1: f64,
3850    pub lvpnt0: f64,
3851    pub iolim: f64,
3852    pub tfltr: f64,
3853    pub khv: f64,
3854    pub iqrmax: f64,
3855    pub iqrmin: f64,
3856    pub accel: f64,
3857    pub vsmax: f64,
3858    pub mbase: f64,
3859}
3860
3861/// PVEU1 — WECC 1st-gen PV electrical control unit (maps to exciter slot).
3862#[derive(Debug, Clone, Serialize, Deserialize)]
3863pub struct Pveu1Params {
3864    pub tiq: f64,
3865    pub dflag: f64,
3866    pub vref0: f64,
3867    pub tv: f64,
3868    pub dbd: f64,
3869    pub kqv: f64,
3870    pub iqhl: f64,
3871    pub iqll: f64,
3872    pub pmax: f64,
3873    pub pmin: f64,
3874    pub qmax: f64,
3875    pub qmin: f64,
3876    pub vmax: f64,
3877    pub vmin: f64,
3878    pub tpord: f64,
3879    pub mbase: f64,
3880}
3881
3882/// PVDG — Distributed/rooftop PV aggregate model.
3883#[derive(Debug, Clone, Serialize, Deserialize)]
3884pub struct PvdgParams {
3885    pub tp: f64,
3886    pub tq: f64,
3887    pub vtrip1: f64,
3888    pub vtrip2: f64,
3889    pub vtrip3: f64,
3890    pub ftrip1: f64,
3891    pub ftrip2: f64,
3892    pub pmax: f64,
3893    pub qmax: f64,
3894    pub qmin: f64,
3895    pub mbase: f64,
3896}
3897
3898// ---------------------------------------------------------------------------
3899// Phase 23: Exciter param structs
3900// ---------------------------------------------------------------------------
3901
3902/// IEEET2 — IEEE Type 2 rotating-machine exciter.
3903#[derive(Debug, Clone, Serialize, Deserialize)]
3904pub struct Ieeet2Params {
3905    pub tr: f64,
3906    pub ka: f64,
3907    pub ta: f64,
3908    pub vrmax: f64,
3909    pub vrmin: f64,
3910    pub ke: f64,
3911    pub te: f64,
3912    pub e1: f64,
3913    pub se1: f64,
3914    pub e2: f64,
3915    pub se2: f64,
3916    pub kf: f64,
3917    pub tf: f64,
3918}
3919
3920/// EXAC2 — IEEE AC2A high initial response rotating exciter.
3921#[derive(Debug, Clone, Serialize, Deserialize)]
3922pub struct Exac2Params {
3923    pub tr: f64,
3924    pub tb: f64,
3925    pub tc: f64,
3926    pub ka: f64,
3927    pub ta: f64,
3928    pub vamax: f64,
3929    pub vamin: f64,
3930    pub te: f64,
3931    pub kf: f64,
3932    pub tf: f64,
3933    pub ke: f64,
3934    pub e1: f64,
3935    pub se1: f64,
3936    pub e2: f64,
3937    pub se2: f64,
3938    pub kc: f64,
3939    pub kd: f64,
3940    pub kh: f64,
3941}
3942
3943/// EXAC3 — IEEE AC3A controlled-rectifier exciter.
3944#[derive(Debug, Clone, Serialize, Deserialize)]
3945pub struct Exac3Params {
3946    pub tr: f64,
3947    pub kc: f64,
3948    pub ki: f64,
3949    pub vmin: f64,
3950    pub vmax: f64,
3951    pub ke: f64,
3952    pub te: f64,
3953    pub kf: f64,
3954    pub tf: f64,
3955    pub e1: f64,
3956    pub se1: f64,
3957    pub e2: f64,
3958    pub se2: f64,
3959    pub ka: f64,
3960    pub ta: f64,
3961    pub efdn: f64,
3962}
3963
3964/// ESAC3A — IEEE 421.5-2005 AC3A exciter update.
3965#[derive(Debug, Clone, Serialize, Deserialize)]
3966pub struct Esac3aParams {
3967    pub tr: f64,
3968    pub tb: f64,
3969    pub tc: f64,
3970    pub ka: f64,
3971    pub ta: f64,
3972    pub vamax: f64,
3973    pub vamin: f64,
3974    pub te: f64,
3975    pub ke: f64,
3976    pub kf1: f64,
3977    pub tf: f64,
3978    pub e1: f64,
3979    pub se1: f64,
3980    pub e2: f64,
3981    pub se2: f64,
3982    pub kc: f64,
3983    pub kd: f64,
3984    pub ki: f64,
3985    pub efdn: f64,
3986    pub kn: f64,
3987    pub vfemax: f64,
3988}
3989
3990/// ESST8C — IEEE 421.5-2016 ST8C static exciter (PID voltage regulator).
3991#[derive(Debug, Clone, Serialize, Deserialize)]
3992pub struct Esst8cParams {
3993    pub tr: f64,
3994    pub kpr: f64,
3995    pub kir: f64,
3996    pub vrmax: f64,
3997    pub vrmin: f64,
3998    pub ka: f64,
3999    pub ta: f64,
4000    pub kc: f64,
4001    pub vbmax: f64,
4002    pub xl: f64,
4003    pub kf: f64,
4004    pub tf: f64,
4005}
4006
4007/// ESST9B — IEEE 421.5-2016 ST9B static exciter (simplified ST8C variant).
4008#[derive(Debug, Clone, Serialize, Deserialize)]
4009pub struct Esst9bParams {
4010    pub tr: f64,
4011    pub kpa: f64,
4012    pub kia: f64,
4013    pub vrmax: f64,
4014    pub vrmin: f64,
4015    pub ka: f64,
4016    pub ta: f64,
4017    pub vbmax: f64,
4018    pub kc: f64,
4019    pub t1: f64,
4020    pub t2: f64,
4021    pub t3: f64,
4022    pub t4: f64,
4023}
4024
4025/// ESST10C — IEEE 421.5-2016 ST10C static exciter (multi-stage PI with UEL/OEL).
4026#[derive(Debug, Clone, Serialize, Deserialize)]
4027pub struct Esst10cParams {
4028    pub tr: f64,
4029    pub kpa: f64,
4030    pub kia: f64,
4031    pub kpb: f64,
4032    pub kib: f64,
4033    pub vrmax: f64,
4034    pub vrmin: f64,
4035    pub ka: f64,
4036    pub ta: f64,
4037    pub vbmax: f64,
4038    pub kc: f64,
4039    pub t1: f64,
4040    pub t2: f64,
4041}
4042
4043/// ESDC3A — IEEE 421.5-2005 DC3A rotating-machine exciter.
4044#[derive(Debug, Clone, Serialize, Deserialize)]
4045pub struct Esdc3aParams {
4046    pub tr: f64,
4047    pub ka: f64,
4048    pub ta: f64,
4049    pub vrmax: f64,
4050    pub vrmin: f64,
4051    pub te: f64,
4052    pub ke: f64,
4053    pub e1: f64,
4054    pub se1: f64,
4055    pub e2: f64,
4056    pub se2: f64,
4057    pub kp: f64,
4058    pub ki: f64,
4059    pub kf: f64,
4060    pub tf: f64,
4061}
4062
4063// ---------------------------------------------------------------------------
4064// Wave 32: EXDC1 + ESST2A param structs
4065// ---------------------------------------------------------------------------
4066
4067/// EXDC1 — IEEE Type DC1A rotating-machine exciter (legacy 13-param form).
4068///
4069/// PSS/E format: TR KA TA VRMAX VRMIN KE TE KF TF E1 SE1 E2 SE2
4070#[derive(Debug, Clone, Serialize, Deserialize)]
4071pub struct Exdc1Params {
4072    pub tr: f64,
4073    pub ka: f64,
4074    pub ta: f64,
4075    pub vrmax: f64,
4076    pub vrmin: f64,
4077    pub ke: f64,
4078    pub te: f64,
4079    pub kf: f64,
4080    pub tf: f64,
4081    pub e1: f64,
4082    pub se1: f64,
4083    pub e2: f64,
4084    pub se2: f64,
4085}
4086
4087/// ESST2A — IEEE 421.5-2016 Type ST2A static exciter.
4088///
4089/// PSS/E format: TR KA TA TB TC KE TE KF TF VRMAX VRMIN EFD1 SE1 EFD2 SE2 KC KP KI \[TP\]
4090#[derive(Debug, Clone, Serialize, Deserialize)]
4091pub struct Esst2aParams {
4092    pub tr: f64,
4093    pub ka: f64,
4094    pub ta: f64,
4095    pub tb: f64,
4096    pub tc: f64,
4097    pub ke: f64,
4098    pub te: f64,
4099    pub kf: f64,
4100    pub tf: f64,
4101    pub vrmax: f64,
4102    pub vrmin: f64,
4103    pub e1: f64,  // EFD1 — first saturation breakpoint (pu)
4104    pub se1: f64, // SE(EFD1)
4105    pub e2: f64,  // EFD2 — second saturation breakpoint (pu)
4106    pub se2: f64, // SE(EFD2)
4107    pub kc: f64,  // rectifier loading factor
4108    pub kp: f64,  // potential circuit gain (terminal voltage)
4109    pub ki: f64,  // current circuit gain
4110    pub tp: f64,  // potential-circuit transducer time constant (s)
4111}
4112
4113// Wave 33: EXDC3 param struct
4114// ---------------------------------------------------------------------------
4115
4116/// EXDC3 — PSS/E non-continuously-acting (relay-type) DC exciter.
4117///
4118/// PSS/E format: TR KV TSTALL TCON TB TC VRMAX VRMIN VEFF TLIM VLIM KE TE
4119#[derive(Debug, Clone, Serialize, Deserialize)]
4120pub struct Exdc3Params {
4121    pub tr: f64,
4122    pub kv: f64,     // regulator deadband threshold (pu)
4123    pub tstall: f64, // stalling/feedback time constant (s)
4124    pub tcon: f64,   // relay control time constant (s)
4125    pub tb: f64,
4126    pub tc: f64,
4127    pub vrmax: f64,
4128    pub vrmin: f64,
4129    pub veff: f64,
4130    pub tlim: f64,
4131    pub vlim: f64,
4132    pub ke: f64,
4133    pub te: f64,
4134}
4135
4136// ---------------------------------------------------------------------------
4137// Phase 24: PSS variant param structs
4138// ---------------------------------------------------------------------------
4139
4140/// PSS2C — PSS2B with ramp-tracking filter on input 2.
4141#[derive(Debug, Clone, Serialize, Deserialize)]
4142pub struct Pss2cParams {
4143    pub m1: f64,
4144    pub t6: f64,
4145    pub m2: f64,
4146    pub t7: f64,
4147    pub tw1: f64,
4148    pub tw2: f64,
4149    pub tw3: f64,
4150    pub tw4: f64,
4151    pub t1: f64,
4152    pub t2: f64,
4153    pub t3: f64,
4154    pub t4: f64,
4155    pub t8: f64,
4156    pub t9: f64,
4157    pub n: i32,
4158    pub ks1: f64,
4159    pub ks2: f64,
4160    pub ks3: f64,
4161    pub vstmax: f64,
4162    pub vstmin: f64,
4163}
4164
4165/// PSS5 — Five-band multi-frequency power system stabilizer.
4166#[derive(Debug, Clone, Serialize, Deserialize)]
4167pub struct Pss5Params {
4168    pub kl: f64,
4169    pub km: f64,
4170    pub kh: f64,
4171    pub tw1: f64,
4172    pub tw2: f64,
4173    pub tw3: f64,
4174    pub t1: f64,
4175    pub t2: f64,
4176    pub t3: f64,
4177    pub t4: f64,
4178    pub t5: f64,
4179    pub t6: f64,
4180    pub vstmax: f64,
4181    pub vstmin: f64,
4182}
4183
4184/// PSS6C — Six-input multi-band PSS.
4185#[derive(Debug, Clone, Serialize, Deserialize)]
4186pub struct Pss6cParams {
4187    pub kl: f64,
4188    pub km: f64,
4189    pub kh: f64,
4190    pub kl2: f64,
4191    pub km2: f64,
4192    pub kh2: f64,
4193    pub tw1: f64,
4194    pub tw2: f64,
4195    pub tw3: f64,
4196    pub tw4: f64,
4197    pub tw5: f64,
4198    pub tw6: f64,
4199    pub t1: f64,
4200    pub t2: f64,
4201    pub t3: f64,
4202    pub t4: f64,
4203    pub vstmax: f64,
4204    pub vstmin: f64,
4205}
4206
4207/// PSSSB — WSCC/BPA simple power system stabilizer (vendor variant B).
4208#[derive(Debug, Clone, Serialize, Deserialize)]
4209pub struct PsssbParams {
4210    pub ks: f64,
4211    pub t1: f64,
4212    pub t2: f64,
4213    pub t3: f64,
4214    pub t4: f64,
4215    pub t5: f64,
4216    pub t6: f64,
4217    pub tw: f64,
4218    pub vstmax: f64,
4219    pub vstmin: f64,
4220}
4221
4222/// STAB4 — WSCC power system stabilizer variant 4.
4223#[derive(Debug, Clone, Serialize, Deserialize)]
4224pub struct Stab4Params {
4225    pub ks: f64,
4226    pub t1: f64,
4227    pub t2: f64,
4228    pub t3: f64,
4229    pub t4: f64,
4230    pub t5: f64,
4231    pub t6: f64,
4232    pub t7: f64,
4233    pub t8: f64,
4234    pub hlim: f64,
4235}
4236
4237/// STAB5 — WSCC power system stabilizer variant 5.
4238#[derive(Debug, Clone, Serialize, Deserialize)]
4239pub struct Stab5Params {
4240    pub ks: f64,
4241    pub t1: f64,
4242    pub t2: f64,
4243    pub t3: f64,
4244    pub t4: f64,
4245    pub t5: f64,
4246    pub t6: f64,
4247    pub t7: f64,
4248    pub t8: f64,
4249    pub t9: f64,
4250    pub t10: f64,
4251    pub hlim: f64,
4252}
4253
4254// ---------------------------------------------------------------------------
4255// Phase 25: Governor variant param structs
4256// ---------------------------------------------------------------------------
4257
4258/// GGOV2 — GE GGOV1 variant 2 with supplemental load reference input.
4259#[derive(Debug, Clone, Serialize, Deserialize)]
4260pub struct Ggov2Params {
4261    pub r: f64,
4262    pub rselect: f64,
4263    pub tpelec: f64,
4264    pub maxerr: f64,
4265    pub minerr: f64,
4266    pub kpgov: f64,
4267    pub kigov: f64,
4268    pub kdgov: f64,
4269    pub tdgov: f64,
4270    pub vmax: f64,
4271    pub vmin: f64,
4272    pub tact: f64,
4273    pub kturb: f64,
4274    pub wfnl: f64,
4275    pub tb: f64,
4276    pub tc: f64,
4277    pub flag: f64,
4278    pub teng: f64,
4279    pub tfload: f64,
4280    pub kpload: f64,
4281    pub kiload: f64,
4282    pub ldref: f64,
4283    pub dm: f64,
4284    pub ropen: f64,
4285    pub rclose: f64,
4286    pub kimw: f64,
4287    pub pmwset: f64,
4288    pub aset: f64,
4289    pub ka: f64,
4290    pub ta: f64,
4291    pub db: f64,
4292    pub tsa: f64,
4293    pub tsb: f64,
4294    pub rup: f64,
4295    pub rdown: f64,
4296    pub pmax: f64,
4297    pub pmin: f64,
4298}
4299
4300/// GGOV3 — GE GGOV1 variant 3 with washout filter on speed signal.
4301#[derive(Debug, Clone, Serialize, Deserialize)]
4302pub struct Ggov3Params {
4303    pub r: f64,
4304    pub rselect: f64,
4305    pub tpelec: f64,
4306    pub maxerr: f64,
4307    pub minerr: f64,
4308    pub kpgov: f64,
4309    pub kigov: f64,
4310    pub kdgov: f64,
4311    pub tdgov: f64,
4312    pub vmax: f64,
4313    pub vmin: f64,
4314    pub tact: f64,
4315    pub kturb: f64,
4316    pub wfnl: f64,
4317    pub tb: f64,
4318    pub tc: f64,
4319    pub flag: f64,
4320    pub teng: f64,
4321    pub tfload: f64,
4322    pub kpload: f64,
4323    pub kiload: f64,
4324    pub ldref: f64,
4325    pub dm: f64,
4326    pub ropen: f64,
4327    pub rclose: f64,
4328    pub kimw: f64,
4329    pub pmwset: f64,
4330    pub aset: f64,
4331    pub ka: f64,
4332    pub ta: f64,
4333    pub db: f64,
4334    pub tsa: f64,
4335    pub tsb: f64,
4336    pub tw: f64,
4337    pub rup: f64,
4338    pub rdown: f64,
4339    pub pmax: f64,
4340    pub pmin: f64,
4341}
4342
4343/// WPIDHY — Woodward PID Hydro Governor.
4344#[derive(Debug, Clone, Serialize, Deserialize)]
4345pub struct WpidhyParams {
4346    pub gatmax: f64,
4347    pub gatmin: f64,
4348    pub reg: f64,
4349    pub kp: f64,
4350    pub ki: f64,
4351    pub kd: f64,
4352    pub ta: f64,
4353    pub tb: f64,
4354    pub tw: f64,
4355    pub at: f64,
4356    pub dturb: f64,
4357    pub gmax: f64,
4358    pub gmin: f64,
4359    pub pmax: f64,
4360    pub pmin: f64,
4361}
4362
4363/// H6B — Six-State Hydro Governor Variant B.
4364#[derive(Debug, Clone, Serialize, Deserialize)]
4365pub struct H6bParams {
4366    pub tg: f64, // governor time constant
4367    pub tp: f64, // pilot valve time constant
4368    pub uo: f64, // max gate opening rate
4369    pub uc: f64, // max gate closing rate
4370    pub pmax: f64,
4371    pub pmin: f64,
4372    pub beta: f64,  // turbine gain
4373    pub tw: f64,    // water starting time
4374    pub dbinf: f64, // dead band inferior
4375    pub dbsup: f64, // dead band superior
4376}
4377
4378/// WSHYDD — WSHYGP with speed deadband.
4379#[derive(Debug, Clone, Serialize, Deserialize)]
4380pub struct WshyddParams {
4381    pub r: f64,
4382    pub tf: f64,
4383    pub tg: f64,
4384    pub tw: f64,
4385    pub db: f64, // speed deadband (pu)
4386    pub kd: f64,
4387    pub pmax: f64,
4388    pub pmin: f64,
4389    pub kp: f64,
4390    pub ki: f64,
4391}
4392
4393// ---------------------------------------------------------------------------
4394// Phase 26: HVDC/FACTS Advanced param structs
4395// ---------------------------------------------------------------------------
4396
4397/// HVDCPLU1 — PSS/E LCC (line-commutated converter) HVDC two-terminal model.
4398///
4399/// Implements 6-pulse bridge firing-angle / extinction-angle physics with constant-current
4400/// control at the rectifier, CEA control at the inverter, VDCOL, and a DC circuit ODE.
4401/// This replaces the former VSC proxy (which was wrong physics).
4402#[derive(Debug, Clone, Serialize, Deserialize)]
4403pub struct HvdcPlu1Params {
4404    /// Scheduled DC power (pu on mbase).
4405    pub setvl: f64,
4406    /// Scheduled DC voltage (pu on system base).
4407    pub vschd: f64,
4408    /// MVA base of the HVDC link.
4409    pub mbase: f64,
4410    /// Commutation reactance at rectifier (pu).
4411    pub xcr: f64,
4412    /// Commutation reactance at inverter (pu).
4413    pub xci: f64,
4414    /// DC line resistance (pu).
4415    pub rdc: f64,
4416    /// DC circuit time constant (s).
4417    pub td: f64,
4418    /// Measurement / control filter time constant (s).
4419    pub tr: f64,
4420    /// Minimum firing angle at rectifier (rad).
4421    pub alpha_min: f64,
4422    /// Maximum firing angle at rectifier (rad).
4423    pub alpha_max: f64,
4424    /// Minimum extinction angle at inverter (rad).
4425    pub gamma_min: f64,
4426    /// CC control proportional gain.
4427    pub kp_id: f64,
4428    /// CC control integral gain.
4429    pub ki_id: f64,
4430    /// Power order ramp time constant (s).
4431    pub t_ramp: f64,
4432    /// Max power (pu).
4433    pub pmax: f64,
4434    /// Min power (pu, usually 0 or small positive).
4435    pub pmin: f64,
4436    /// Rectifier AC bus number.
4437    pub rectifier_bus: u32,
4438    /// Inverter AC bus number.
4439    pub inverter_bus: u32,
4440}
4441
4442/// CSVGN6 — SVC Variant 6 with Auxiliary Inputs.
4443#[derive(Debug, Clone, Serialize, Deserialize)]
4444pub struct Csvgn6Params {
4445    pub t1: f64,
4446    pub t2: f64,
4447    pub t3: f64,
4448    pub t4: f64,
4449    pub t5: f64,
4450    pub k: f64,
4451    pub k_aux: f64,
4452    pub t_aux: f64,
4453    pub vmax: f64,
4454    pub vmin: f64,
4455    pub bmax: f64,
4456    pub bmin: f64,
4457}
4458
4459/// STCON1 — STATCOM with Inner Current Control.
4460#[derive(Debug, Clone, Serialize, Deserialize)]
4461pub struct Stcon1Params {
4462    pub tr: f64,
4463    pub kp: f64,
4464    pub ki: f64,
4465    pub kp_i: f64,
4466    pub ki_i: f64,
4467    pub vmax: f64,
4468    pub vmin: f64,
4469    pub iqmax: f64,
4470    pub iqmin: f64,
4471    pub mbase: f64,
4472}
4473
4474/// GCSC — Gate-Controlled Series Compensator.
4475#[derive(Debug, Clone, Serialize, Deserialize)]
4476pub struct GcscParams {
4477    pub tr: f64,
4478    pub kp: f64,
4479    pub ki: f64,
4480    pub xmax: f64,
4481    pub xmin: f64,
4482    pub mbase: f64,
4483}
4484
4485/// SSSC — Static Synchronous Series Compensator.
4486#[derive(Debug, Clone, Serialize, Deserialize)]
4487pub struct SsscParams {
4488    pub tr: f64,
4489    pub kp: f64,
4490    pub ki: f64,
4491    pub kp_i: f64,
4492    pub ki_i: f64,
4493    pub vqmax: f64,
4494    pub vqmin: f64,
4495    pub mbase: f64,
4496}
4497
4498/// UPFC — Unified Power Flow Controller.
4499#[derive(Debug, Clone, Serialize, Deserialize)]
4500pub struct UpfcParams {
4501    pub tr: f64,
4502    pub kp_p: f64,
4503    pub ki_p: f64,
4504    pub kp_q: f64,
4505    pub ki_q: f64,
4506    pub kp_v: f64,
4507    pub ki_v: f64,
4508    pub pmax: f64,
4509    pub pmin: f64,
4510    pub qmax: f64,
4511    pub qmin: f64,
4512    pub mbase: f64,
4513}
4514
4515/// CDC3T — Three-Terminal LCC HVDC (extends CDC4T with third terminal).
4516#[derive(Debug, Clone, Serialize, Deserialize)]
4517pub struct Cdc3tParams {
4518    pub tr: f64,
4519    pub kp1: f64,
4520    pub ki1: f64,
4521    pub kp2: f64,
4522    pub ki2: f64,
4523    pub kp3: f64,
4524    pub ki3: f64,
4525    pub pmax: f64,
4526    pub pmin: f64,
4527    pub mbase: f64,
4528}
4529
4530// ---------------------------------------------------------------------------
4531// Phase 27: Generator / Load / Protection param structs
4532// ---------------------------------------------------------------------------
4533
4534/// REGCO1 — Grid-following converter generator (4 states).
4535#[derive(Debug, Clone, Serialize, Deserialize)]
4536pub struct Regco1Params {
4537    pub tr: f64,
4538    pub kp_v: f64,
4539    pub ki_v: f64,
4540    pub kp_i: f64,
4541    pub ki_i: f64,
4542    pub vmax: f64,
4543    pub vmin: f64,
4544    pub iqmax: f64,
4545    pub iqmin: f64,
4546    pub pmax: f64,
4547    pub pmin: f64,
4548    pub mbase: f64,
4549}
4550
4551/// GENSAL3 — Third-order salient-pole synchronous generator (3 dynamic states).
4552#[derive(Debug, Clone, Serialize, Deserialize)]
4553pub struct Gensal3Params {
4554    pub td0_prime: f64,
4555    pub h: f64,
4556    pub d: f64,
4557    pub xd: f64,
4558    pub xq: f64,
4559    pub xd_prime: f64,
4560    pub xl: f64,
4561    pub s1: f64,
4562    pub s12: f64,
4563}
4564
4565/// LCFB1 — Load compensator with frequency bias (2 states).
4566#[derive(Debug, Clone, Serialize, Deserialize)]
4567pub struct Lcfb1Params {
4568    pub tc: f64,
4569    pub tb: f64,
4570    pub kf: f64,
4571    pub pmax: f64,
4572    pub pmin: f64,
4573    pub mbase: f64,
4574}
4575
4576/// LDFRAL — Dynamic load frequency regulation (2 states).
4577#[derive(Debug, Clone, Serialize, Deserialize)]
4578pub struct LdfralParams {
4579    pub tc: f64,
4580    pub tb: f64,
4581    pub kf: f64,
4582    pub kp: f64,
4583    pub pmax: f64,
4584    pub pmin: f64,
4585    pub mbase: f64,
4586}
4587
4588/// FRQTPLT — Frequency relay trip (1 state + bool flag).
4589#[derive(Debug, Clone, Serialize, Deserialize)]
4590pub struct FrqtpltParams {
4591    pub tf: f64,
4592    pub fmin: f64,
4593    pub fmax: f64,
4594    pub p_trip: f64,
4595}
4596
4597/// LVSHBL — Low-voltage shunt block (1 state + bool flag).
4598#[derive(Debug, Clone, Serialize, Deserialize)]
4599pub struct LvshblParams {
4600    pub tv: f64,
4601    pub vmin: f64,
4602    pub p_block: f64,
4603}
4604
4605// ---------------------------------------------------------------------------
4606
4607/// UVLS1 — Under-Voltage Load Shedding relay (single stage, per-bus).
4608/// Multi-stage UVLS via multiple UVLS1 records at the same bus.
4609/// DYR: UVLS1 bus 'id' tv vmin t_delay p_shed v_reconnect t_reconnect /
4610#[derive(Debug, Clone, Serialize, Deserialize)]
4611pub struct Uvls1Params {
4612    /// Voltage measurement filter time constant (seconds).
4613    pub tv: f64,
4614    /// Undervoltage threshold (pu).
4615    pub vmin: f64,
4616    /// Trip delay after continuous undervoltage (seconds).
4617    pub t_delay: f64,
4618    /// Fraction of load to shed (0.0–1.0).
4619    pub p_shed: f64,
4620    /// Voltage threshold for reconnection (pu); 0.0 disables reconnection.
4621    pub v_reconnect: f64,
4622    /// Reconnection delay (seconds).
4623    pub t_reconnect: f64,
4624}
4625
4626// ---------------------------------------------------------------------------
4627
4628/// A `.dyr` record with an unrecognised model name — stored verbatim.
4629#[derive(Debug, Clone, Serialize, Deserialize)]
4630pub struct UnknownDyrRecord {
4631    /// Bus number extracted from the record (0 if the bus field was not numeric).
4632    pub bus: u32,
4633    /// Raw model name as read from the file.
4634    pub model_name: String,
4635    /// Machine ID string.
4636    pub machine_id: String,
4637    /// All numeric parameter tokens from the record.
4638    pub params: Vec<f64>,
4639}
4640
4641// ---------------------------------------------------------------------------
4642// Phase 28: REPCGFM_C1 / DERP / REGFM_D1 / WTDTA1 / WTARA1 / WTPTA1
4643// ---------------------------------------------------------------------------
4644
4645/// REPCGFM_C1 — GFM plant-level Volt/Var controller (3 states).
4646///
4647/// Plant-level companion to REGFM_C1. Integrating PI loops for voltage and
4648/// reactive power, plus a frequency droop state.
4649#[derive(Debug, Clone, Serialize, Deserialize)]
4650pub struct Repcgfmc1Params {
4651    pub kp_v: f64,
4652    pub ki_v: f64,
4653    pub vmax: f64,
4654    pub vmin: f64,
4655    pub kp_q: f64,
4656    pub ki_q: f64,
4657    pub qmax: f64,
4658    pub qmin: f64,
4659    pub tlag: f64,
4660    pub fdroop: f64,
4661    pub dbd1: f64,
4662    pub dbd2: f64,
4663}
4664
4665/// DERP — DER with Protection (2 states).
4666///
4667/// DER_A variant with explicit frequency and voltage protection relay
4668/// trip logic on top of the DERA inverter output.
4669#[derive(Debug, Clone, Serialize, Deserialize)]
4670pub struct DerpParams {
4671    pub x_eq: f64,
4672    pub trf: f64,
4673    pub imax: f64,
4674    pub trv: f64,
4675    /// PLL filter time constant (s).
4676    pub tpll: f64,
4677    /// Lower frequency trip threshold (pu).
4678    pub flow: f64,
4679    /// Upper frequency trip threshold (pu).
4680    pub fhigh: f64,
4681    /// Lower voltage trip threshold (pu).
4682    pub vlow: f64,
4683    /// Upper voltage trip threshold (pu).
4684    pub vhigh: f64,
4685    /// Protection trip time constant (s).
4686    pub trip: f64,
4687    /// Reconnect time constant (s).
4688    pub treconnect: f64,
4689}
4690
4691/// REGFM_D1 — WECC Sep-2025 hybrid GFM/GFL converter (8 states).
4692///
4693/// Droop-based voltage/frequency forming with current-limit handoff to GFL
4694/// mode. Structurally extends REGFM_C1 with 2 extra VOC + anti-windup states.
4695#[derive(Debug, Clone, Serialize, Deserialize)]
4696pub struct Regfmd1Params {
4697    pub rrv: f64,
4698    pub lrv: f64,
4699    pub kpv: f64,
4700    pub kiv: f64,
4701    pub kpg: f64,
4702    pub kig: f64,
4703    pub kdroop: f64,
4704    pub kvir: f64,
4705    pub kfir: f64,
4706    pub imax: f64,
4707    pub dpf: f64,
4708    pub dqf: f64,
4709    pub x_eq: f64,
4710    pub mbase: f64,
4711    /// PLL tracking filter time constant (s, default 0.02).
4712    pub tpll: f64,
4713    /// Voltage measurement filter time constant (s, default 0.02).
4714    pub tv: f64,
4715}
4716
4717/// WTDTA1 — Wind turbine two-mass drive-train (2 states).
4718///
4719/// ωr (rotor speed deviation) and θtwist (shaft twist angle).
4720/// Works alongside REGCA/WT3G2U generators.
4721#[derive(Debug, Clone, Serialize, Deserialize)]
4722pub struct Wtdta1Params {
4723    /// Inertia constant of the rotor (s).
4724    pub h: f64,
4725    /// Shaft damping coefficient (pu).
4726    pub dshaft: f64,
4727    /// Shaft stiffness coefficient (pu/rad).
4728    pub kshaft: f64,
4729    /// Second mass damping.
4730    pub d2: f64,
4731}
4732
4733/// WTARA1 — Wind turbine aerodynamic aggregation (2 states).
4734///
4735/// State: Paero (aerodynamic power), Pmech (mechanical power output).
4736/// Cp-lambda power curve simplified as first-order lag.
4737#[derive(Debug, Clone, Serialize, Deserialize)]
4738pub struct Wtara1Params {
4739    /// Aerodynamic power gain.
4740    pub ka: f64,
4741    /// Aerodynamic time constant (s).
4742    pub ta: f64,
4743    /// Mechanical power gain.
4744    pub km: f64,
4745    /// Mechanical time constant (s).
4746    pub tm: f64,
4747    /// Maximum power output (pu).
4748    pub pmax: f64,
4749    /// Minimum power output (pu).
4750    pub pmin: f64,
4751}
4752
4753/// WTPTA1 — Wind turbine pitch angle control (2 states).
4754///
4755/// States: θcmd (pitch command), θact (actual pitch, rate-limited servo).
4756#[derive(Debug, Clone, Serialize, Deserialize)]
4757pub struct Wtpta1Params {
4758    /// Proportional gain of pitch PI controller.
4759    pub kpp: f64,
4760    /// Integral gain of pitch PI controller.
4761    pub kip: f64,
4762    /// Maximum pitch angle (deg).
4763    pub theta_max: f64,
4764    /// Minimum pitch angle (deg).
4765    pub theta_min: f64,
4766    /// Maximum pitch rate (deg/s).
4767    pub rate_max: f64,
4768    /// Minimum pitch rate (deg/s) — typically negative.
4769    pub rate_min: f64,
4770    /// Servo time constant (s).
4771    pub te: f64,
4772    /// Pitch-to-power gain (pu MW per deg).
4773    pub kp_pitch: f64,
4774}
4775
4776/// Cp(λ,β) lookup table for full aerodynamic wind turbine models.
4777///
4778/// Power coefficient Cp is tabulated as a function of tip-speed ratio λ
4779/// (lambda) and blade pitch angle β (beta). Values are stored row-major
4780/// in `cp_values[i_lambda * n_beta + i_beta]`.
4781#[derive(Debug, Clone, Serialize, Deserialize)]
4782pub struct CpTable {
4783    /// Tip-speed ratio breakpoints (dimensionless).
4784    pub lambda_bp: Vec<f64>,
4785    /// Pitch angle breakpoints (degrees).
4786    pub beta_bp: Vec<f64>,
4787    /// Cp values, row-major: `[n_lambda × n_beta]`.
4788    pub cp_values: Vec<f64>,
4789}
4790
4791impl CpTable {
4792    /// Bilinear interpolation of Cp at given (lambda, beta).
4793    pub fn interpolate(&self, lambda: f64, beta: f64) -> f64 {
4794        let (il, il1, fl) = Self::find_bracket(&self.lambda_bp, lambda);
4795        let (ib, ib1, fb) = Self::find_bracket(&self.beta_bp, beta);
4796        let nb = self.beta_bp.len();
4797
4798        let c00 = self.cp_values[il * nb + ib];
4799        let c01 = self.cp_values[il * nb + ib1];
4800        let c10 = self.cp_values[il1 * nb + ib];
4801        let c11 = self.cp_values[il1 * nb + ib1];
4802
4803        let c0 = c00 + fb * (c01 - c00);
4804        let c1 = c10 + fb * (c11 - c10);
4805        (c0 + fl * (c1 - c0)).max(0.0)
4806    }
4807
4808    /// NREL 5-MW reference turbine Cp table (public domain data).
4809    ///
4810    /// Simplified 8×6 table covering λ ∈ [2, 16] and β ∈ [0, 25]°.
4811    pub fn nrel_5mw() -> Self {
4812        let lambda_bp = vec![2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0];
4813        let beta_bp = vec![0.0, 5.0, 10.0, 15.0, 20.0, 25.0];
4814        #[rustfmt::skip]
4815        let cp_values = vec![
4816            // β=0     β=5    β=10   β=15   β=20   β=25
4817            0.10,  0.06,  0.02,  0.01,  0.00,  0.00,  // λ=2
4818            0.30,  0.20,  0.10,  0.04,  0.01,  0.00,  // λ=4
4819            0.42,  0.32,  0.18,  0.08,  0.03,  0.01,  // λ=6
4820            0.48,  0.38,  0.22,  0.11,  0.04,  0.01,  // λ=8
4821            0.44,  0.34,  0.20,  0.10,  0.04,  0.01,  // λ=10
4822            0.35,  0.26,  0.15,  0.07,  0.03,  0.01,  // λ=12
4823            0.22,  0.16,  0.09,  0.04,  0.02,  0.01,  // λ=14
4824            0.10,  0.07,  0.04,  0.02,  0.01,  0.00,  // λ=16
4825        ];
4826        Self {
4827            lambda_bp,
4828            beta_bp,
4829            cp_values,
4830        }
4831    }
4832
4833    /// Find bracketing indices and interpolation fraction.
4834    fn find_bracket(bp: &[f64], val: f64) -> (usize, usize, f64) {
4835        if bp.len() < 2 {
4836            return (0, 0, 0.0);
4837        }
4838        if val <= bp[0] {
4839            return (0, 0, 0.0);
4840        }
4841        if val >= bp[bp.len() - 1] {
4842            let n = bp.len() - 1;
4843            return (n, n, 0.0);
4844        }
4845        // Binary search for bracket.
4846        let pos = bp.partition_point(|&x| x <= val);
4847        let i = if pos > 0 { pos - 1 } else { 0 };
4848        let i1 = (i + 1).min(bp.len() - 1);
4849        let span = bp[i1] - bp[i];
4850        let frac = if span.abs() > 1e-15 {
4851            (val - bp[i]) / span
4852        } else {
4853            0.0
4854        };
4855        (i, i1, frac)
4856    }
4857}
4858
4859/// WTAERO — Full aerodynamic wind turbine model with Cp(λ,β) table.
4860///
4861/// Computes aerodynamic power from wind speed, rotor speed, and pitch angle
4862/// using a tabulated power coefficient surface. Optionally models the
4863/// two-mass drive train (rotor + generator) with shaft flexibility.
4864///
4865/// States (single-mass): p_aero (aerodynamic power, filtered).
4866/// States (two-mass): omega_r (rotor speed), theta_tw (shaft twist), p_aero.
4867#[derive(Debug, Clone, Serialize, Deserialize)]
4868pub struct WtaeroParams {
4869    /// Air density (kg/m³). Default: 1.225 (ISA sea level).
4870    pub rho: f64,
4871    /// Rotor radius (m).
4872    pub r_rotor: f64,
4873    /// Gear ratio (generator_speed / rotor_speed).
4874    pub gear_ratio: f64,
4875    /// Cp(λ,β) lookup table.
4876    pub cp_table: CpTable,
4877    /// Base wind speed for steady-state initialization (m/s).
4878    pub v_wind_base: f64,
4879    /// Generator MVA base (for per-unit conversion).
4880    pub mbase_mw: f64,
4881    /// Two-mass rotor inertia (s). None = single-mass.
4882    pub h_rotor: Option<f64>,
4883    /// Shaft stiffness (pu/rad). Required for two-mass.
4884    pub k_shaft: Option<f64>,
4885    /// Shaft damping (pu). Required for two-mass.
4886    pub d_shaft: Option<f64>,
4887}
4888
4889// ---------------------------------------------------------------------------
4890// Wave 34: New generator, governor, load, FACTS param structs
4891// ---------------------------------------------------------------------------
4892
4893/// IEESGO — IEEE Standard Governor (simplified 5-state steam turbine governor).
4894///
4895/// PSS/E params: `T1 T2 T3 T4 T5 T6 K1 K2 K3 PMAX PMIN`
4896///
4897/// T1=lead TC, T2=lag TC (lead-lag), T3=valve TC,
4898/// T4=HP TC, T5=reheat TC, T6=LP TC; K1+K2+K3=1 (HP/IP/LP fractions).
4899#[derive(Debug, Clone, Serialize, Deserialize)]
4900pub struct IeesgoParams {
4901    pub t1: f64,
4902    pub t2: f64,
4903    pub t3: f64,
4904    pub t4: f64,
4905    pub t5: f64,
4906    pub t6: f64,
4907    pub k1: f64,
4908    pub k2: f64,
4909    pub k3: f64,
4910    pub pmax: f64,
4911    pub pmin: f64,
4912}
4913
4914/// WTTQA1 — WECC Type 2 Wind Torque Controller (2 states, governor slot).
4915///
4916/// PSS/E params: `Kp Ki Tp Pmax Pmin`
4917#[derive(Debug, Clone, Serialize, Deserialize)]
4918pub struct Wttqa1Params {
4919    pub kp: f64,
4920    pub ki: f64,
4921    pub tp: f64,
4922    pub pmax: f64,
4923    pub pmin: f64,
4924    /// Torque mode flag: 0 = speed error mode, 1 = power error mode.
4925    pub tflag: i32,
4926    /// Speed reference filter time constant (s).
4927    pub twref: f64,
4928    /// Maximum torque (pu).
4929    pub temax: f64,
4930    /// Minimum torque (pu).
4931    pub temin: f64,
4932    /// Speed-power lookup breakpoints: (power, speed_ref) pairs.
4933    /// Piecewise linear mapping from measured power to speed reference.
4934    pub spl: [(f64, f64); 4],
4935}
4936
4937/// CIM6 — 6th-order induction motor (extends CIM5 with q-axis transient).
4938///
4939/// Uses same 3-state structure as CIM5 (slip, ed_prime, eq_prime) but adds
4940/// `tq0p` and `xq_prime` for q-axis transient dynamics.
4941///
4942/// PSS/E params: `RA XS XM XR1 XR2 RR1 RR2 [H E1 S1 E2 S2 MBASE TQ0P XQP]`
4943#[derive(Debug, Clone, Serialize, Deserialize)]
4944pub struct Cim6Params {
4945    pub ra: f64,
4946    pub xs: f64,
4947    pub xm: f64,
4948    pub xr1: f64,
4949    pub xr2: f64,
4950    pub rr1: f64,
4951    pub rr2: f64,
4952    pub h: f64,
4953    pub e1: f64,
4954    pub s1: f64,
4955    pub e2: f64,
4956    pub s2: f64,
4957    pub mbase: f64,
4958    pub tq0p: f64,
4959    pub xq_prime: f64,
4960}
4961
4962/// EXTL — External Load (simplified composite 2-state load model).
4963///
4964/// Models voltage/frequency-dependent load with first-order filters.
4965///
4966/// PSS/E params: `Tp Tq Kpv Kqv Kpf Kqf mbase lfac`
4967#[derive(Debug, Clone, Serialize, Deserialize)]
4968pub struct ExtlParams {
4969    pub tp: f64,
4970    pub tq: f64,
4971    pub kpv: f64,
4972    pub kqv: f64,
4973    pub kpf: f64,
4974    pub kqf: f64,
4975    pub mbase: f64,
4976    pub lfac: f64,
4977}
4978
4979/// SVSMO1 — WECC Generic SVC voltage regulator (1-state, FACTS slot).
4980///
4981/// Simple first-order SVC model.  PSS/E params: `Tr K Ta Bmin Bmax`
4982#[derive(Debug, Clone, Serialize, Deserialize)]
4983pub struct Svsmo1Params {
4984    pub tr: f64,
4985    pub k: f64,
4986    pub ta: f64,
4987    pub b_min: f64,
4988    pub b_max: f64,
4989}
4990
4991/// SVSMO2 — WECC Generic STATCOM (1-state, FACTS slot).
4992///
4993/// Similar to CSTCON. PSS/E params: `Tr K Ta IqMin IqMax`
4994#[derive(Debug, Clone, Serialize, Deserialize)]
4995pub struct Svsmo2Params {
4996    pub tr: f64,
4997    pub k: f64,
4998    pub ta: f64,
4999    pub iq_min: f64,
5000    pub iq_max: f64,
5001}
5002
5003/// SVSMO3 — WECC Advanced SVC (2-state: b_svc + vr, FACTS slot).
5004///
5005/// Lead-lag voltage regulator driving susceptance.
5006/// PSS/E params: `Tr Ka Ta Tb Bmin Bmax`
5007#[derive(Debug, Clone, Serialize, Deserialize)]
5008pub struct Svsmo3Params {
5009    pub tr: f64,
5010    pub ka: f64,
5011    pub ta: f64,
5012    pub tb: f64,
5013    pub b_min: f64,
5014    pub b_max: f64,
5015}
5016
5017// ---------------------------------------------------------------------------
5018// Wave 35: New param structs
5019// ---------------------------------------------------------------------------
5020
5021// --- Generator protection relays -------------------------------------------
5022
5023/// VTGTPAT / VTGDCAT — Voltage-Time Generator Protection Trip params.
5024#[derive(Debug, Clone, Serialize, Deserialize)]
5025pub struct VtgtpatParams {
5026    /// Voltage filter time constant (s).
5027    pub tv: f64,
5028    /// Trip voltage threshold (pu, below which generator trips).
5029    pub vtrip: f64,
5030    /// Reset voltage threshold (pu, above which relay resets).
5031    pub vreset: f64,
5032}
5033
5034/// FRQTPAT / FRQDCAT — Frequency-Time Generator Protection Trip params.
5035#[derive(Debug, Clone, Serialize, Deserialize)]
5036pub struct FrqtpatParams {
5037    /// Frequency filter time constant (s).
5038    pub tf: f64,
5039    /// High-frequency trip threshold (pu, above which generator trips).
5040    pub ftrip_hi: f64,
5041    /// Low-frequency trip threshold (pu, below which generator trips).
5042    pub ftrip_lo: f64,
5043    /// Reset frequency threshold (pu).
5044    pub freset: f64,
5045}
5046
5047// --- Hydro governors -------------------------------------------------------
5048
5049/// HYGOV4 — Hydro Governor with Surge Tank (5 states).
5050///
5051/// PSS/E params: `R TF TG TR HDAM TW QNL AT DG GMAX GMIN TS KS PMAX PMIN`
5052#[derive(Debug, Clone, Serialize, Deserialize)]
5053pub struct Hygov4Params {
5054    /// Governor time constant (pilot valve, s).
5055    pub tr: f64,
5056    /// Pilot filter time constant (s).
5057    pub tf: f64,
5058    /// Turbine damping (pu).
5059    pub dturb: f64,
5060    /// Head at zero flow (pu).
5061    pub hdam: f64,
5062    /// Penstock water time constant (s).
5063    pub tw: f64,
5064    /// No-load flow at nominal head (pu).
5065    pub qnl: f64,
5066    /// Turbine gain (pu).
5067    pub at: f64,
5068    /// Governor servo gain (pu/pu).
5069    pub dg: f64,
5070    /// Maximum gate position (pu).
5071    pub gmax: f64,
5072    /// Minimum gate position (pu).
5073    pub gmin: f64,
5074    /// Surge tank time constant (s).
5075    pub ts: f64,
5076    /// Surge tank orifice loss coefficient (pu).
5077    pub ks: f64,
5078    /// Maximum power output (pu).
5079    pub pmax: f64,
5080    /// Minimum power output (pu).
5081    pub pmin: f64,
5082}
5083
5084/// WEHGOV — WECC Enhanced Hydro Governor (4 states).
5085///
5086/// PSS/E params: `R TR TF TG TW AT DTURB QNL GMAX GMIN DBD1 DBD2 PMAX PMIN`
5087#[derive(Debug, Clone, Serialize, Deserialize)]
5088pub struct WehgovParams {
5089    /// Droop (pu).
5090    pub r: f64,
5091    /// Governor filter time constant (s).
5092    pub tr: f64,
5093    /// Pilot filter time constant (s).
5094    pub tf: f64,
5095    /// Gate servo time constant (s).
5096    pub tg: f64,
5097    /// Penstock water time constant (s).
5098    pub tw: f64,
5099    /// Turbine gain (pu).
5100    pub at: f64,
5101    /// Turbine damping (pu).
5102    pub dturb: f64,
5103    /// No-load flow (pu).
5104    pub qnl: f64,
5105    /// Maximum gate position (pu).
5106    pub gmax: f64,
5107    /// Minimum gate position (pu).
5108    pub gmin: f64,
5109    /// Speed deadband lower limit (pu, negative).
5110    pub dbd1: f64,
5111    /// Speed deadband upper limit (pu, positive).
5112    pub dbd2: f64,
5113    /// Maximum power output (pu).
5114    pub pmax: f64,
5115    /// Minimum power output (pu).
5116    pub pmin: f64,
5117}
5118
5119/// IEEEG3 — IEEE Type G3 Hydro Governor (3 states).
5120///
5121/// PSS/E params: `TG TP UO UC PMAX PMIN TW AT DTURB QNL`
5122#[derive(Debug, Clone, Serialize, Deserialize)]
5123pub struct Ieeeg3Params {
5124    /// Gate servo time constant (s).
5125    pub tg: f64,
5126    /// Pilot valve time constant (s).
5127    pub tp: f64,
5128    /// Maximum gate opening rate (pu/s).
5129    pub uo: f64,
5130    /// Maximum gate closing rate (pu/s, negative in PSS/E).
5131    pub uc: f64,
5132    /// Maximum power output (pu).
5133    pub pmax: f64,
5134    /// Minimum power output (pu).
5135    pub pmin: f64,
5136    /// Water time constant (s).
5137    pub tw: f64,
5138    /// Turbine gain (pu).
5139    pub at: f64,
5140    /// Turbine damping (pu).
5141    pub dturb: f64,
5142    /// No-load flow (pu).
5143    pub qnl: f64,
5144}
5145
5146/// IEEEG4 — IEEE Type G4 Hydro Governor (3 states, lead-lag form).
5147///
5148/// PSS/E params: `T1 T2 T3 KI PMAX PMIN TW AT DTURB QNL`
5149#[derive(Debug, Clone, Serialize, Deserialize)]
5150pub struct Ieeeg4Params {
5151    /// Lead-lag time constant 1 (s).
5152    pub t1: f64,
5153    /// Lead-lag time constant 2 (s).
5154    pub t2: f64,
5155    /// Lead-lag time constant 3 (s).
5156    pub t3: f64,
5157    /// Integral gain (pu/pu/s).
5158    pub ki: f64,
5159    /// Maximum power output (pu).
5160    pub pmax: f64,
5161    /// Minimum power output (pu).
5162    pub pmin: f64,
5163    /// Water time constant (s).
5164    pub tw: f64,
5165    /// Turbine gain (pu).
5166    pub at: f64,
5167    /// Turbine damping (pu).
5168    pub dturb: f64,
5169    /// No-load flow (pu).
5170    pub qnl: f64,
5171}
5172
5173// --- IEEE 421.5-2016 exciters (C-series) -----------------------------------
5174
5175/// ESAC7C — IEEE 421.5-2016 AC7C exciter params (6 states).
5176///
5177/// Structurally identical to ESAC7B; used as a C-series alias.
5178/// PSS/E params same as ESAC7B.
5179#[derive(Debug, Clone, Serialize, Deserialize)]
5180pub struct Esac7cParams {
5181    pub tr: f64,
5182    pub kpr: f64,
5183    pub kir: f64,
5184    pub kdr: f64,
5185    pub tdr: f64,
5186    pub vrmax: f64,
5187    pub vrmin: f64,
5188    pub ka: f64,
5189    pub ta: f64,
5190    pub kp: f64,
5191    pub kl: f64,
5192    pub te: f64,
5193    pub ke: f64,
5194    pub vfemax: f64,
5195    pub vemin: f64,
5196    pub e1: f64,
5197    pub se1: f64,
5198    pub e2: f64,
5199    pub se2: f64,
5200}
5201
5202/// ESDC4C — IEEE 421.5-2016 DC4C exciter params (3 states).
5203///
5204/// PSS/E params: `TR KA TA KPR KIR KDR TDR VRMAX VRMIN KE TE KF TF E1 SE1 E2 SE2`
5205#[derive(Debug, Clone, Serialize, Deserialize)]
5206pub struct Esdc4cParams {
5207    pub tr: f64,
5208    pub ka: f64,
5209    pub ta: f64,
5210    pub kpr: f64,
5211    pub kir: f64,
5212    pub kdr: f64,
5213    pub tdr: f64,
5214    pub vrmax: f64,
5215    pub vrmin: f64,
5216    pub ke: f64,
5217    pub te: f64,
5218    pub kf: f64,
5219    pub tf: f64,
5220    pub e1: f64,
5221    pub se1: f64,
5222    pub e2: f64,
5223    pub se2: f64,
5224}
5225
5226// --- IEEE 421.5-2016 PSS (C-series) ----------------------------------------
5227
5228/// PSS7C — IEEE 421.5-2016 multi-band PSS (6 states).
5229///
5230/// Three frequency bands (low / intermediate / high), each with a washout
5231/// filter and a lead-lag compensator.  Output is the gain-weighted sum of
5232/// all three bands, clamped to `[vstmin, vstmax]`.
5233///
5234/// DYR params (extended): `KL TWL T1L T2L  KI TWI T1I T2I  KH TWH T1H T2H  VSTMAX VSTMIN`
5235///
5236/// Legacy 9-param form (`KSS TW1 TW2 T1 T2 T3 T4 VSMAX VSMIN`) is still
5237/// accepted — the old fields are mapped to the intermediate band and the
5238/// low/high bands default to zero gain.
5239#[derive(Debug, Clone, Serialize, Deserialize)]
5240pub struct Pss7cParams {
5241    // --- legacy fields (kept for DYR round-trip / serde compat) -------------
5242    /// Legacy overall gain — mapped to intermediate band if per-band gains
5243    /// are all zero.
5244    #[serde(default)]
5245    pub kss: f64,
5246    /// Legacy washout time constant 1 (s).
5247    #[serde(default)]
5248    pub tw1: f64,
5249    /// Legacy washout time constant 2 (s).
5250    #[serde(default)]
5251    pub tw2: f64,
5252    /// Legacy lead time constant 1 (s).
5253    #[serde(default)]
5254    pub t1: f64,
5255    /// Legacy lag time constant 1 (s).
5256    #[serde(default)]
5257    pub t2: f64,
5258    /// Legacy lead time constant 2 (s).
5259    #[serde(default)]
5260    pub t3: f64,
5261    /// Legacy lag time constant 2 (s).
5262    #[serde(default)]
5263    pub t4: f64,
5264    /// Maximum PSS output (pu)  — legacy alias for `vstmax`.
5265    #[serde(default = "default_vsmax")]
5266    pub vsmax: f64,
5267    /// Minimum PSS output (pu)  — legacy alias for `vstmin`.
5268    #[serde(default = "default_vsmin")]
5269    pub vsmin: f64,
5270
5271    // --- per-band parameters (multi-band PSS) ------------------------------
5272    /// Low-frequency band gain (pu).
5273    #[serde(default)]
5274    pub kl: f64,
5275    /// Low-frequency washout time constant (s).
5276    #[serde(default = "default_tw")]
5277    pub tw_l: f64,
5278    /// Low-frequency lead time constant (s).
5279    #[serde(default)]
5280    pub t1_l: f64,
5281    /// Low-frequency lag time constant (s).
5282    #[serde(default = "default_tlag")]
5283    pub t2_l: f64,
5284
5285    /// Intermediate-frequency band gain (pu).
5286    #[serde(default)]
5287    pub ki: f64,
5288    /// Intermediate-frequency washout time constant (s).
5289    #[serde(default = "default_tw")]
5290    pub tw_i: f64,
5291    /// Intermediate-frequency lead time constant (s).
5292    #[serde(default)]
5293    pub t1_i: f64,
5294    /// Intermediate-frequency lag time constant (s).
5295    #[serde(default = "default_tlag")]
5296    pub t2_i: f64,
5297
5298    /// High-frequency band gain (pu).
5299    #[serde(default)]
5300    pub kh: f64,
5301    /// High-frequency washout time constant (s).
5302    #[serde(default = "default_tw")]
5303    pub tw_h: f64,
5304    /// High-frequency lead time constant (s).
5305    #[serde(default)]
5306    pub t1_h: f64,
5307    /// High-frequency lag time constant (s).
5308    #[serde(default = "default_tlag")]
5309    pub t2_h: f64,
5310
5311    /// Multi-band maximum PSS output (pu).
5312    #[serde(default = "default_vsmax")]
5313    pub vstmax: f64,
5314    /// Multi-band minimum PSS output (pu).
5315    #[serde(default = "default_vsmin")]
5316    pub vstmin: f64,
5317}
5318
5319fn default_vsmax() -> f64 {
5320    0.1
5321}
5322fn default_vsmin() -> f64 {
5323    -0.1
5324}
5325fn default_tw() -> f64 {
5326    10.0
5327}
5328fn default_tlag() -> f64 {
5329    0.04
5330}
5331
5332impl Pss7cParams {
5333    /// Effective per-band parameters.  When the new per-band gains are all
5334    /// zero **and** `kss != 0`, fall back to the legacy single-band mapping
5335    /// (intermediate band only).
5336    pub fn effective_bands(&self) -> Pss7cBands {
5337        if self.kl == 0.0 && self.ki == 0.0 && self.kh == 0.0 && self.kss != 0.0 {
5338            // Legacy mode: map kss → intermediate band.
5339            Pss7cBands {
5340                kl: 0.0,
5341                tw_l: self.tw1,
5342                t1_l: 0.0,
5343                t2_l: 0.04,
5344                ki: self.kss,
5345                tw_i: self.tw1,
5346                t1_i: self.t1,
5347                t2_i: self.t2,
5348                kh: 0.0,
5349                tw_h: self.tw2,
5350                t1_h: 0.0,
5351                t2_h: 0.04,
5352                vstmax: self.vsmax,
5353                vstmin: self.vsmin,
5354            }
5355        } else {
5356            Pss7cBands {
5357                kl: self.kl,
5358                tw_l: self.tw_l,
5359                t1_l: self.t1_l,
5360                t2_l: self.t2_l,
5361                ki: self.ki,
5362                tw_i: self.tw_i,
5363                t1_i: self.t1_i,
5364                t2_i: self.t2_i,
5365                kh: self.kh,
5366                tw_h: self.tw_h,
5367                t1_h: self.t1_h,
5368                t2_h: self.t2_h,
5369                vstmax: self.vstmax,
5370                vstmin: self.vstmin,
5371            }
5372        }
5373    }
5374}
5375
5376/// Resolved per-band parameters for PSS7C evaluation.
5377#[derive(Debug, Clone)]
5378pub struct Pss7cBands {
5379    pub kl: f64,
5380    pub tw_l: f64,
5381    pub t1_l: f64,
5382    pub t2_l: f64,
5383    pub ki: f64,
5384    pub tw_i: f64,
5385    pub t1_i: f64,
5386    pub t2_i: f64,
5387    pub kh: f64,
5388    pub tw_h: f64,
5389    pub t1_h: f64,
5390    pub t2_h: f64,
5391    pub vstmax: f64,
5392    pub vstmin: f64,
5393}
5394
5395// ---------------------------------------------------------------------------
5396// Wave 36: New governor, generator, exciter, and load model structs
5397// ---------------------------------------------------------------------------
5398
5399// --- Combined Cycle Governors ---
5400
5401/// GOVCT1 — Single-shaft combined cycle turbine governor (common in ERCOT/WECC).
5402///
5403/// PSS/E params: `R T1 VMAX VMIN T2 T3 K1 K2 K3 T4 T5 T6 K7 K8 PMAX PMIN [TD]`
5404#[derive(Debug, Clone, Serialize, Deserialize)]
5405pub struct Govct1Params {
5406    /// Speed regulation (droop, pu).
5407    pub r: f64,
5408    /// Governor time constant (s).
5409    pub t1: f64,
5410    /// Maximum valve position (pu).
5411    pub vmax: f64,
5412    /// Minimum valve position (pu).
5413    pub vmin: f64,
5414    /// Lead time constant (s).
5415    pub t2: f64,
5416    /// Lag time constant (s).
5417    pub t3: f64,
5418    /// HP turbine fraction.
5419    pub k1: f64,
5420    /// LP1 turbine fraction.
5421    pub k2: f64,
5422    /// LP2 turbine fraction (= 1-k1-k2).
5423    pub k3: f64,
5424    /// HP turbine time constant (s).
5425    pub t4: f64,
5426    /// LP1 time constant (s).
5427    pub t5: f64,
5428    /// LP2 time constant (s).
5429    pub t6: f64,
5430    /// Gas turbine coefficient 1.
5431    pub k7: f64,
5432    /// Gas turbine coefficient 2.
5433    pub k8: f64,
5434    /// Maximum power output (pu).
5435    pub pmax: f64,
5436    /// Minimum power output (pu).
5437    pub pmin: f64,
5438    /// Governor deadband (optional, default 0).
5439    #[serde(default)]
5440    pub td: f64,
5441}
5442
5443/// GOVCT2 — Two-shaft combined cycle gas turbine governor (7 states).
5444///
5445/// Models a gas turbine (GT) driving one generator plus a steam turbine (ST)
5446/// driven by heat recovery from the GT exhaust via an HRSG.
5447///
5448/// PSS/E params: `R T1 VMAX VMIN T2 T3 K1 K2 K3 T4 T5 T6 K7 K8 PMAX PMIN [TD T_HRSG K_ST T_ST]`
5449///
5450/// States x1–x5 are the gas turbine (identical to GOVCT1).
5451/// State x_hrsg captures HRSG steam generation dynamics.
5452/// State x_st captures steam turbine output dynamics.
5453/// Total Pm = Pm_gt + x_st.
5454#[derive(Debug, Clone, Serialize, Deserialize)]
5455pub struct Govct2Params {
5456    // --- Gas turbine fields (same as Govct1Params) ---
5457    /// Speed regulation (droop, pu).
5458    pub r: f64,
5459    /// Governor time constant (s).
5460    pub t1: f64,
5461    /// Maximum valve position (pu).
5462    pub vmax: f64,
5463    /// Minimum valve position (pu).
5464    pub vmin: f64,
5465    /// Lead time constant (s).
5466    pub t2: f64,
5467    /// Lag time constant (s).
5468    pub t3: f64,
5469    /// HP turbine fraction.
5470    pub k1: f64,
5471    /// LP1 turbine fraction.
5472    pub k2: f64,
5473    /// LP2 turbine fraction (= 1-k1-k2).
5474    pub k3: f64,
5475    /// HP turbine time constant (s).
5476    pub t4: f64,
5477    /// LP1 time constant (s).
5478    pub t5: f64,
5479    /// LP2 time constant (s).
5480    pub t6: f64,
5481    /// Gas turbine coefficient 1.
5482    pub k7: f64,
5483    /// Gas turbine coefficient 2.
5484    pub k8: f64,
5485    /// Maximum power output (pu).
5486    pub pmax: f64,
5487    /// Minimum power output (pu).
5488    pub pmin: f64,
5489    /// Governor deadband (optional, default 0).
5490    #[serde(default)]
5491    pub td: f64,
5492    // --- Steam turbine / HRSG fields (GOVCT2-specific) ---
5493    /// HRSG time constant (s) — typically 60-120s.
5494    pub t_hrsg: f64,
5495    /// Steam-to-gas power ratio — typically 0.33-0.5.
5496    pub k_st: f64,
5497    /// Steam turbine lag time constant (s) — typically 5-15s.
5498    pub t_st: f64,
5499}
5500
5501// --- Advanced Steam Governors ---
5502
5503/// TGOV3 — TGOV1 variant with two-reheat steam turbine (3 states).
5504///
5505/// PSS/E params: `R T1 VMAX VMIN T2 T3 DT KD`
5506#[derive(Debug, Clone, Serialize, Deserialize)]
5507pub struct Tgov3Params {
5508    /// Speed regulation (droop, pu).
5509    pub r: f64,
5510    /// Governor time constant (s).
5511    pub t1: f64,
5512    /// Maximum valve position (pu).
5513    pub vmax: f64,
5514    /// Minimum valve position (pu).
5515    pub vmin: f64,
5516    /// First reheat time constant (s).
5517    pub t2: f64,
5518    /// Second reheat time constant (s).
5519    pub t3: f64,
5520    /// Turbine damping (pu).
5521    pub dt: f64,
5522    /// Derivative gain.
5523    pub kd: f64,
5524}
5525
5526// --- Legacy Wind Generator Params ---
5527
5528/// WT1G1 / WT2G1 — Type 1/2 induction-machine wind generator (3rd-order model).
5529///
5530/// PSS/E DYR record: `H D RA X_EQ IMAX`
5531///
5532/// WT1G1 is a squirrel-cage induction generator (Type 1) directly coupled to
5533/// the grid with no power electronics.  WT2G1 adds external rotor resistance
5534/// control via the WT2E1 governor.
5535///
5536/// The 3rd-order model tracks transient EMFs (E'_d, E'_q) and rotor slip:
5537/// ```text
5538/// dE'_q/dt = -s·ω_s·E'_d - (E'_q + (X - X')·I_d) / T'_0
5539/// dE'_d/dt =  s·ω_s·E'_q - (E'_d - (X - X')·I_q) / T'_0
5540/// ds/dt    = (T_e - T_m) / (2H)
5541/// ```
5542///
5543/// # Decomposition from PSS/E X_EQ
5544///
5545/// PSS/E provides only the transient reactance X' = X_EQ.  The full IM circuit
5546/// parameters (Xs, Xm, Xr, Rr) are derived using standard decomposition defaults:
5547/// - Xs  = 0.10 · X'           (stator leakage)
5548/// - Xm  = 3.0                 (magnetizing reactance)
5549/// - Xr  = Xs                  (rotor leakage ≈ stator leakage)
5550/// - Rr  = 0.01                (rotor resistance)
5551/// - X   = Xs + Xm             (open-circuit reactance)
5552/// - T'_0 = (Xm + Xr) / (ω_s · Rr)
5553#[derive(Debug, Clone, Serialize, Deserialize)]
5554pub struct Wt1g1Params {
5555    /// Inertia constant (s).
5556    pub h: f64,
5557    /// Damping coefficient (pu).
5558    pub d: f64,
5559    /// Stator resistance Rs (pu).  PSS/E field `RA`.
5560    pub ra: f64,
5561    /// Transient reactance X' (pu).  PSS/E field `X_EQ`.
5562    /// Equal to Xs + Xm·Xr/(Xm+Xr).
5563    pub x_eq: f64,
5564    /// Current limit (pu).
5565    pub imax: f64,
5566    /// Stator leakage reactance (pu).  Derived: 0.10 × X_EQ.
5567    pub xs: f64,
5568    /// Magnetizing reactance (pu).  Default 3.0.
5569    pub xm: f64,
5570    /// Rotor leakage reactance (pu).  Derived ≈ Xs.
5571    pub xr: f64,
5572    /// Rotor resistance (pu).  Default 0.01.
5573    pub rr: f64,
5574}
5575
5576/// WT2E1 — Type 2 wind electrical controller (governor slot).
5577///
5578/// PSS/E params: `KP KI PMAX PMIN TE`
5579#[derive(Debug, Clone, Serialize, Deserialize)]
5580pub struct Wt2e1Params {
5581    /// Proportional gain.
5582    pub kp: f64,
5583    /// Integral gain.
5584    pub ki: f64,
5585    /// Maximum active power (pu).
5586    pub pmax: f64,
5587    /// Minimum active power (pu).
5588    pub pmin: f64,
5589    /// Electrical control time constant (s).
5590    pub te: f64,
5591}
5592
5593/// DISTR1 — Distance relay (line protection, attaches to bus in load slot).
5594///
5595/// PSS/E params: `Z1 Z2 T1 T2 MBASE LFAC`
5596///
5597/// Extended with Zone 3, Mho circle angle, protected branch info, and
5598/// measurement filter time constant for proper impedance-measuring relay.
5599#[derive(Debug, Clone, Serialize, Deserialize)]
5600pub struct Distr1Params {
5601    /// Zone 1 impedance magnitude (pu).
5602    pub z1: f64,
5603    /// Zone 2 impedance magnitude (pu).
5604    pub z2: f64,
5605    /// Zone 3 impedance magnitude (pu). Default: 1.5 × z2.
5606    pub z3: f64,
5607    /// Zone 1 trip delay (s). Typically ~0 (instantaneous).
5608    pub t1: f64,
5609    /// Zone 2 trip delay (s). Typically 0.3–0.5 s.
5610    pub t2: f64,
5611    /// Zone 3 trip delay (s). Typically 0.8–1.2 s.
5612    pub t3: f64,
5613    /// Mho circle reach angle (degrees). Typical: 60–85°.
5614    pub reach_angle_deg: f64,
5615    /// MVA base of protected element.
5616    pub mbase: f64,
5617    /// Load fraction this relay monitors.
5618    pub lfac: f64,
5619    /// From-bus of the protected branch.
5620    pub branch_from: u32,
5621    /// To-bus of the protected branch.
5622    pub branch_to: u32,
5623    /// Protected branch resistance (pu).
5624    pub branch_r: f64,
5625    /// Protected branch reactance (pu).
5626    pub branch_x: f64,
5627    /// Measurement filter time constant (s). Default: 0.02 s (1.2 cycles).
5628    pub tf: f64,
5629}
5630
5631/// BFR50 — Breaker failure relay (ANSI 50BF).
5632///
5633/// DYR params: `T_BFR  I_SUP  BRANCH_IDX`
5634///
5635/// Monitors the breaker on the protected branch. When a trip command is active
5636/// and current exceeds the supervision threshold for `t_bfr` seconds, the BFR
5637/// issues a backup trip to adjacent generators.
5638#[derive(Debug, Clone, Serialize, Deserialize)]
5639pub struct Bfr50Params {
5640    /// BFR timer duration (s). Typical: 5-10 cycles (83-167ms @ 60Hz).
5641    pub t_bfr: f64,
5642    /// Current supervision threshold (pu). BFR only active if I > this.
5643    pub i_sup: f64,
5644    /// Internal branch index of the monitored breaker.
5645    pub branch_idx: usize,
5646}
5647
5648// ---------------------------------------------------------------------------
5649// Wave 7 (B10): Additional protection relay parameter types
5650// ---------------------------------------------------------------------------
5651
5652/// 87T — Transformer differential relay.
5653///
5654/// Compares high-side and low-side currents of a transformer. Trips when the
5655/// differential current `|I_H - I_L/ratio|` exceeds the restraint characteristic
5656/// `slope × I_restraint + I_pickup`.
5657///
5658/// DYR params: `SLOPE1 SLOPE2 I_PICKUP HARMONIC_RESTRAINT FROM_BUS TO_BUS CKT TURNS_RATIO TF`
5659#[derive(Debug, Clone, Serialize, Deserialize)]
5660pub struct TransDiff87Params {
5661    /// Slope 1 of restraint characteristic (typical 0.2–0.3).
5662    pub slope1: f64,
5663    /// Slope 2 (high-current region, typical 0.6–0.8).
5664    pub slope2: f64,
5665    /// Minimum pickup current (pu).
5666    pub i_pickup: f64,
5667    /// 2nd harmonic blocking ratio (typical 0.15–0.20). Trip blocked if
5668    /// 2nd-harmonic content > ratio × fundamental.
5669    pub harmonic_restraint: f64,
5670    /// High-side bus number.
5671    pub from_bus: u32,
5672    /// Low-side bus number.
5673    pub to_bus: u32,
5674    /// Circuit identifier.
5675    pub circuit: String,
5676    /// Turns ratio (high-side / low-side rated voltage).
5677    pub turns_ratio: f64,
5678    /// Measurement filter time constant (s). Default: 0.01 (10ms).
5679    pub tf: f64,
5680}
5681
5682/// 87L — Line differential relay.
5683///
5684/// Compares currents at both ends of a transmission line. Trips the branch
5685/// (not a generator) when the differential current exceeds the restraint.
5686///
5687/// DYR params: `SLOPE1 SLOPE2 I_PICKUP FROM_BUS TO_BUS CKT TF`
5688#[derive(Debug, Clone, Serialize, Deserialize)]
5689pub struct LineDiff87lParams {
5690    /// Slope 1 of restraint characteristic (typical 0.2–0.3).
5691    pub slope1: f64,
5692    /// Slope 2 (high-current region, typical 0.6–0.8).
5693    pub slope2: f64,
5694    /// Minimum pickup current (pu).
5695    pub i_pickup: f64,
5696    /// From-bus of protected line.
5697    pub from_bus: u32,
5698    /// To-bus of protected line.
5699    pub to_bus: u32,
5700    /// Circuit identifier.
5701    pub circuit: String,
5702    /// Measurement filter time constant (s). Default: 0.01 (10ms).
5703    pub tf: f64,
5704}
5705
5706/// 79 — Automatic recloser.
5707///
5708/// After a relay trips a branch, the recloser waits a dead-time delay then
5709/// recloses the branch. If the fault persists, it re-trips and repeats up to
5710/// `max_attempts` times before locking out.
5711///
5712/// DYR params: `DEAD1 DEAD2 DEAD3 MAX_ATTEMPTS FROM_BUS TO_BUS CKT RESET_TIME`
5713#[derive(Debug, Clone, Serialize, Deserialize)]
5714pub struct Recloser79Params {
5715    /// First reclose dead-time delay (s).
5716    pub dead_time_1: f64,
5717    /// Second reclose dead-time delay (s).
5718    pub dead_time_2: f64,
5719    /// Third reclose dead-time delay (s).
5720    pub dead_time_3: f64,
5721    /// Maximum reclose attempts before lockout (1–3).
5722    pub max_attempts: u32,
5723    /// From-bus of reclosable branch.
5724    pub from_bus: u32,
5725    /// To-bus of reclosable branch.
5726    pub to_bus: u32,
5727    /// Circuit identifier.
5728    pub circuit: String,
5729    /// Time after successful reclose to reset attempt counter (s).
5730    pub reset_time: f64,
5731}
5732
5733// ---------------------------------------------------------------------------
5734// Wave 37: OEL/UEL Limiter types
5735// ---------------------------------------------------------------------------
5736
5737/// OEL1B — Over-Excitation Limiter Type 1B (inverse-time ramp limiter).
5738///
5739/// PSS/E params: `IFDMAX IFDLIM VRMAX VAMIN KRAMP TFF`
5740#[derive(Debug, Clone, Serialize, Deserialize)]
5741pub struct Oel1bParams {
5742    /// Maximum continuous field current (pu) — ramp starts above this.
5743    pub ifdmax: f64,
5744    /// Instantaneous trip limit (pu).
5745    pub ifdlim: f64,
5746    /// Maximum regulator output (pu).
5747    pub vrmax: f64,
5748    /// Minimum amplifier output (pu, negative).
5749    pub vamin: f64,
5750    /// Limiter ramp rate (pu/s).
5751    pub kramp: f64,
5752    /// Field current filter time constant (s).
5753    pub tff: f64,
5754}
5755
5756/// OEL2C — Over-Excitation Limiter Type 2C (fixed-current with time delay).
5757///
5758/// PSS/E params: `IFDMAX T_OEL VAMIN VRMAX K_OEL`
5759#[derive(Debug, Clone, Serialize, Deserialize)]
5760pub struct Oel2cParams {
5761    /// Maximum field current (pu).
5762    pub ifdmax: f64,
5763    /// Time delay before limiting (s).
5764    pub t_oel: f64,
5765    /// Minimum output (pu, negative).
5766    pub vamin: f64,
5767    /// Maximum regulator output (pu).
5768    pub vrmax: f64,
5769    /// Gain.
5770    pub k_oel: f64,
5771}
5772
5773/// SCL1C — Stator Current Limiter Type 1C.
5774///
5775/// PSS/E params: `IRATED KR TR VCLMAX VCLMIN`
5776#[derive(Debug, Clone, Serialize, Deserialize)]
5777pub struct Scl1cParams {
5778    /// Rated stator current (pu).
5779    pub irated: f64,
5780    /// Gain.
5781    pub kr: f64,
5782    /// Current filter time constant (s).
5783    pub tr: f64,
5784    /// Maximum clamp output (pu).
5785    pub vclmax: f64,
5786    /// Minimum clamp output (pu, negative).
5787    pub vclmin: f64,
5788}
5789
5790/// UEL1 — Under-Excitation Limiter Type 1 (single-input integrator).
5791///
5792/// PSS/E params: `KUL TU1 VUCMAX VUCMIN KUR`
5793#[derive(Debug, Clone, Serialize, Deserialize)]
5794pub struct Uel1Params {
5795    /// UEL gain.
5796    pub kul: f64,
5797    /// UEL time constant (s).
5798    pub tu1: f64,
5799    /// Maximum UEL output (pu).
5800    pub vucmax: f64,
5801    /// Minimum UEL output (pu).
5802    pub vucmin: f64,
5803    /// Reactive power sensitivity.
5804    pub kur: f64,
5805}
5806
5807/// UEL2C — Under-Excitation Limiter Type 2C (P-Q plane limiter).
5808///
5809/// PSS/E params: `KUL TU1 TU2 TU3 TU4 VUIMAX VUIMIN P0 Q0`
5810#[derive(Debug, Clone, Serialize, Deserialize)]
5811pub struct Uel2cParams {
5812    /// UEL gain.
5813    pub kul: f64,
5814    /// Filter time constant 1 (s).
5815    pub tu1: f64,
5816    /// Filter time constant 2 (s).
5817    pub tu2: f64,
5818    /// Filter time constant 3 (s).
5819    pub tu3: f64,
5820    /// Filter time constant 4 (s).
5821    pub tu4: f64,
5822    /// Maximum integrator output (pu).
5823    pub vuimax: f64,
5824    /// Minimum integrator output (pu).
5825    pub vuimin: f64,
5826    /// Reference active power (pu).
5827    pub p0: f64,
5828    /// Reference reactive power (pu).
5829    pub q0: f64,
5830}
5831
5832/// OEL dynamic record — over-excitation limiter attached to a generator.
5833#[derive(Debug, Clone, Serialize, Deserialize)]
5834pub struct OelDyn {
5835    /// Bus number.
5836    pub bus: u32,
5837    /// Machine ID string (matches the generator record).
5838    pub machine_id: String,
5839    /// OEL model and parameters.
5840    pub model: OelModel,
5841}
5842
5843/// UEL dynamic record — under-excitation limiter attached to a generator.
5844#[derive(Debug, Clone, Serialize, Deserialize)]
5845pub struct UelDyn {
5846    /// Bus number.
5847    pub bus: u32,
5848    /// Machine ID string (matches the generator record).
5849    pub machine_id: String,
5850    /// UEL model and parameters.
5851    pub model: UelModel,
5852}
5853
5854/// Discriminated union of OEL models.
5855#[derive(Debug, Clone, Serialize, Deserialize)]
5856#[serde(tag = "type")]
5857pub enum OelModel {
5858    /// OEL1B — inverse-time ramp limiter.
5859    Oel1b(Oel1bParams),
5860    /// OEL2C — fixed current + time delay limiter.
5861    Oel2c(Oel2cParams),
5862    /// OEL3C — alias to OEL2C (C-series 2016 standard).
5863    Oel3c(Oel2cParams),
5864    /// OEL4C — alias to OEL2C (C-series 2016 standard).
5865    Oel4c(Oel2cParams),
5866    /// OEL5C — alias to OEL2C (C-series 2016 standard).
5867    Oel5c(Oel2cParams),
5868    /// SCL1C — stator current limiter (uses OEL slot).
5869    Scl1c(Scl1cParams),
5870}
5871
5872/// Discriminated union of UEL models.
5873#[derive(Debug, Clone, Serialize, Deserialize)]
5874#[serde(tag = "type")]
5875pub enum UelModel {
5876    /// UEL1 — single-input integrator UEL.
5877    Uel1(Uel1Params),
5878    /// UEL2C — P-Q plane limiter (two-state filter).
5879    Uel2c(Uel2cParams),
5880}
5881
5882/// Shaft dynamic model assignment — keyed by (bus, machine_id).
5883/// Follows the same pattern as ExciterDyn, GovernorDyn, PssDyn, OelDyn, UelDyn.
5884#[derive(Debug, Clone, Serialize, Deserialize)]
5885pub struct ShaftDyn {
5886    /// Bus number of the associated generator.
5887    pub bus: u32,
5888    /// Machine ID (matches the generator record).
5889    pub machine_id: String,
5890    /// N-mass torsional shaft model.
5891    pub model: crate::dynamics::shaft::ShaftModel,
5892}
5893
5894#[cfg(test)]
5895mod tests {
5896    use super::*;
5897
5898    fn empty_dm() -> DynamicModel {
5899        DynamicModel::default()
5900    }
5901
5902    #[test]
5903    fn test_coverage_all_supported() {
5904        let mut dm = empty_dm();
5905        dm.generators.push(GeneratorDyn {
5906            bus: 1,
5907            machine_id: "1".into(),
5908            model: GeneratorModel::Gencls(GenclsParams { h: 3.0, d: 0.0 }),
5909        });
5910        let (n_sup, n_tot, pct) = dm.coverage();
5911        assert_eq!(n_sup, 1);
5912        assert_eq!(n_tot, 1);
5913        assert!((pct - 100.0).abs() < 1e-10);
5914    }
5915
5916    #[test]
5917    fn test_coverage_with_unknown() {
5918        let mut dm = empty_dm();
5919        dm.generators.push(GeneratorDyn {
5920            bus: 1,
5921            machine_id: "1".into(),
5922            model: GeneratorModel::Gencls(GenclsParams { h: 3.0, d: 0.0 }),
5923        });
5924        dm.unknown_records.push(UnknownDyrRecord {
5925            bus: 2,
5926            model_name: "GENCC".into(),
5927            machine_id: "1".into(),
5928            params: vec![1.0, 2.0],
5929        });
5930        let (n_sup, n_tot, pct) = dm.coverage();
5931        assert_eq!(n_sup, 1);
5932        assert_eq!(n_tot, 2);
5933        assert!((pct - 50.0).abs() < 1e-10);
5934    }
5935
5936    #[test]
5937    fn test_coverage_empty() {
5938        let dm = empty_dm();
5939        let (n_sup, n_tot, pct) = dm.coverage();
5940        assert_eq!(n_sup, 0);
5941        assert_eq!(n_tot, 0);
5942        assert!((pct - 100.0).abs() < 1e-10);
5943    }
5944}