pydynamicestimator.devices.synchronous
Classes
Metaclass for SG in rectangular coordinates with TGOV1 governor and pluggable AVR |
|
Transient two-axis SG with TGOV1 governor and IEEEDC1A AVR |
|
Subtransient Anderson Fouad SG with TGOV1 governor and IEEEDC1A AVR |
|
Subtransient Sauer and Pai SG model with stator dynamics |
|
Subtransient Sauer and Pai SG 6th order model with neglected stator dynamics |
|
Subtransient Sauer-Pai 6th-order machine expressed as an explicit DAE. |
|
Subtransient Sauer-Pai machine (with stator dynamics) with the stator |
Module Contents
- class pydynamicestimator.devices.synchronous.Synchronous(avr: pydynamicestimator.devices.avr.AVR = None, governor: pydynamicestimator.devices.governor.Governor = None, pss: pydynamicestimator.devices.pss.PSS = None, shaft: pydynamicestimator.devices.shaft.Shaft = None)[source]
Bases:
pydynamicestimator.devices.device.DeviceRectMetaclass for SG in rectangular coordinates with TGOV1 governor and pluggable AVR
- Parameters:
- _pss: pydynamicestimator.devices.pss.PSS = None
- fn
- H
- R_s
- x_d
- x_q
- x_l
- D
- f
- ns = 0
- gcall(dae: pydynamicestimator.system.Dae, i_d: casadi.SX, i_q: casadi.SX) None[source]
- Parameters:
i_d (casadi.SX)
i_q (casadi.SX)
- Return type:
None
- _omega_ref(dae: pydynamicestimator.system.Dae) casadi.SX[source]
Per-machine reference-frame frequency: the nominal
dae.omega_net(= 1 p.u.) by default, or the estimated per-bus reference frequency whendae.omega_ref_busesis present. Shared by the rotor seam (was duplicated inside every electromagnetic method).- Parameters:
- Return type:
casadi.SX
- rotor(dae: pydynamicestimator.system.Dae, Pe: casadi.SX) None[source]
Rotor mechanics seam: delegate the swing (rotor-motion) equations to the pluggable shaft strategy. The default
SingleMasswrites the classic single-mass swing equation (reading the governor portvar_sym("pm"), the air-gap powerPeand the reference frequency); a multi-mass (torsional) shaft adds further rotor masses while keeping the generator mass on thedelta/omeganames, so the electromagnetic models, the network injection and the controllers are untouched. See docs/algebraic_equations_design.md (§9.6/§9.7).- Parameters:
Pe (casadi.SX)
- Return type:
None
- fgcall(dae: pydynamicestimator.system.Dae) None[source]
Template orchestration shared by all machine models: the EM subclass defines stator currents + flux dynamics and returns the air-gap power; the base wires the rotor, the controller ports, and the network.
- Parameters:
- Return type:
None
- abstract electromagnetic(dae: pydynamicestimator.system.Dae)[source]
Subclass hook: define the stator currents (i_d, i_q), write the flux/emf differential equations, and return
(i_d, i_q, Pe)wherePeis the air-gap power/torque consumed byrotor().- Parameters:
- governor_fcall(dae: pydynamicestimator.system.Dae) None[source]
Delegate the governor’s equations to the strategy object. The strategy writes its differential equations into
dae.fand, if it declares private algebraics (e.g. an algebraic ‘pm’), their residuals intodae.g– hence a singlefgcall.- Parameters:
- Return type:
None
- tgov1(dae: pydynamicestimator.system.Dae) None[source]
Deprecated: use governor_fcall instead.
- Parameters:
- Return type:
None
- avr_fcall(dae: pydynamicestimator.system.Dae) None[source]
Delegate the AVR’s equations to the strategy object. The strategy writes its differential equations into
dae.fand, if it declares private algebraics (e.g. an algebraic ‘Efd’), their residuals intodae.g– hence a singlefgcall.- Parameters:
- Return type:
None
- var_sym(dae: pydynamicestimator.system.Dae, name: str) casadi.SX[source]
Resolve a registered variable to its symbol, wherever it lives.
A coupling variable such as ‘Efd’ may be a differential state (in
dae.x) or a device-private algebraic (indae.y), depending on the AVR strategy. This resolver lets the machine’s electromagnetic equations reference it without knowing which – the keystone that makes state-vs-algebraic exciters interchangeable.- Parameters:
name (str)
- Return type:
casadi.SX
- pss_fcall(dae: pydynamicestimator.system.Dae) None[source]
Delegate the PSS’s equations to the strategy object (no-op if there is no PSS). The strategy writes its differential equations into
dae.fand, if it declares private algebraics (e.g. an algebraic ‘Vs’), their residuals intodae.g.- Parameters:
- Return type:
None
- pss_signal(dae: pydynamicestimator.system.Dae) casadi.SX[source]
The supplementary stabilizing signal ‘Vs’ summed into the AVR voltage error – the host-mediated PSS->AVR coupling. Returns the ‘Vs’ symbol (state or private algebraic, via
var_sym()) when a PSS is present, else 0 (no PSS => no signal, byte-identical to the unstabilized AVR).- Parameters:
- Return type:
casadi.SX
- ieeedc1a(dae: pydynamicestimator.system.Dae) None[source]
Deprecated: use avr_fcall instead.
- Parameters:
- Return type:
None
- class pydynamicestimator.devices.synchronous.SynchronousTransient(avr=None, governor=None, pss=None, shaft=None)[source]
Bases:
SynchronousTransient two-axis SG with TGOV1 governor and IEEEDC1A AVR
Rotor Dynamics
\[\begin{split}\dot{\delta} &= 2 \pi f_n \Delta \omega \\ \Delta \dot{\omega} &= \frac{1}{2 H} \left( P_m - E_d I_d - E_q I_q + (X_q' - X_d') I_d I_q - D \Delta \omega - f (\Delta \omega + 1) \right)\end{split}\]Electromagnetic Equations
\[\begin{split}\dot{E}_q &= \frac{1}{T_{d'}} \left( -E_q + E_f + (X_d - X_d') I_d \right)\\ \dot{E}_d &= \frac{1}{T_{q'}} \left( -E_d - (X_q - X_q') I_q \right)\end{split}\]Excitation System Equations
\[\begin{split} \dot{E}_{\textup{fd}} &= \frac{1}{T_{E}} \left( -K_{E,i}E_{\textup{fd},i} + V_{R,i}\right)\\ \dot{R}_{f} &= \frac{1}{T_{F} } \left(- R_{f} + \frac{K_{F}}{T_{F}}E_{\textup{fd}} \right)\\ \dot{V}_{R} &= \frac{1}{T_{A}} \left(-V_{R} + K_{A}R_{f} - \frac{K_{A}K_{F}}{T_{F}}E_{\textup{fd}} + K_{A}(v_{\textup{ref}} - v_i) \right)\end{split}\]Turbine-Governor System Equations
\[\begin{split}\dot{p}_{\textup{m}} &= \frac{1}{T_{ch}} \left( p_{\textup{sv}}- p_{\textup{m}} \right)\\ \dot{p}_{sv} &= \frac{1}{T_{sv}} \left( - \frac{\Delta \omega}{R_d} - p_{\textup{sv}} + p_{\textup{ref}} \right)\end{split}\]- _type = 'Synchronous_machine'
- _name = 'Synchronous_machine_transient_model'
- e_dprim
- e_qprim
- x_dprim
- x_qprim
- T_dprim
- T_qprim
- input_current(dae: pydynamicestimator.system.Dae) Tuple[casadi.SX, casadi.SX][source]
- Parameters:
- Return type:
Tuple[casadi.SX, casadi.SX]
- electromagnetic(dae: pydynamicestimator.system.Dae)[source]
Two-axis (transient) electromagnetic model: stator currents, the e’_d/e’_q dynamics, and the air-gap power. The swing equation and orchestration live in the base (rotor / fgcall template).
- Parameters:
- class pydynamicestimator.devices.synchronous.SynchronousSubtransient(avr=None, governor=None, pss=None, shaft=None)[source]
Bases:
SynchronousSubtransient Anderson Fouad SG with TGOV1 governor and IEEEDC1A AVR The subtransient behavior of the synchronous generator is described by the following differential equations:
Rotor Dynamics
\[\begin{split}\dot{\delta} &= 2 \pi f_n \Delta \omega \\ \Delta \dot{\omega} &= \frac{1}{2 H} \Big( P_m - E_{d}^{\prime\prime} I_d - E_{q}^{\prime\prime} I_q + (X_q^{\prime\prime} - X_d^{\prime\prime}) I_d I_q - D \Delta \omega - f (\Delta \omega + 1) \Big) \\\end{split}\]Electromagnetic Equations
\[\begin{split}\dot{E}_q^{\prime} &= \frac{1}{T_{d}^{\prime}} \Big( -E_q + E_f + (X_d - X_d^{\prime}) I_d \Big) \\ \dot{E}_d^{\prime} &= \frac{1}{T_{q}^{\prime}} \Big( -E_d - (X_q - X_q^{\prime}) I_q \Big) \\ \dot{E}_{q}^{\prime\prime} &= \frac{1}{T_{d}^{\prime\prime}} \Big( E_q - E_{q}^{\prime\prime} + (X_d^{\prime} - X_d^{\prime\prime}) I_d \Big) \\ \dot{E}_{d}^{\prime\prime} &= \frac{1}{T_{q}^{\prime\prime}} \Big( E_d - E_{d}^{\prime\prime} - (X_q^{\prime} - X_q^{\prime\prime}) I_q \Big) \\\end{split}\]Excitation System Equations
\[\begin{split} \dot{E}_{\textup{fd}} &= \frac{1}{T_{E}} \left( -K_{E,i}E_{\textup{fd},i} + V_{R,i}\right)\\ \dot{R}_{f} &= \frac{1}{T_{F} } \left(- R_{f} + \frac{K_{F}}{T_{F}}E_{\textup{fd}} \right)\\ \dot{V}_{R} &= \frac{1}{T_{A}} \left(-V_{R} + K_{A}R_{f} - \frac{K_{A}K_{F}}{T_{F}}E_{\textup{fd}} + K_{A}(v_{\textup{ref}} - v_i) \right)\end{split}\]Turbine-Governor System Equations
\[\begin{split}\dot{p}_{\textup{m}} &= \frac{1}{T_{ch}} \left( p_{\textup{sv}}- p_{\textup{m}} \right)\\ \dot{p}_{sv} &= \frac{1}{T_{sv}} \left( - \frac{\Delta \omega}{R_d} - p_{\textup{sv}} + p_{\textup{ref}} \right)\end{split}\]- _type = 'Synchronous_machine'
- _name = 'Synchronous_machine_subtransient_model'
- e_dprim
- e_qprim
- e_dsec
- e_qsec
- x_dprim
- x_qprim
- T_dprim
- T_qprim
- x_dsec
- x_qsec
- T_dsec
- T_qsec
- input_current(dae: pydynamicestimator.system.Dae) Tuple[casadi.SX, casadi.SX][source]
- Parameters:
- Return type:
Tuple[casadi.SX, casadi.SX]
- electromagnetic(dae: pydynamicestimator.system.Dae)[source]
Anderson-Fouad (subtransient) electromagnetic model: stator currents, the e’/e’’ dynamics, and the air-gap power. Swing/orchestration in base.
- Parameters:
- class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP(avr=None, governor=None, pss=None, shaft=None)[source]
Bases:
SynchronousSubtransient Sauer and Pai SG model with stator dynamics with TGOV1 governor and IEEEDC1A AVR
The model includes the following equations for rotor dynamics, stator dynamics, and the excitation system:
Rotor Dynamics
\[\begin{split}\dot{\delta} &= 2 \pi f_n \Delta \omega \\ \Delta \dot{\omega} &= \frac{1}{2 H} \Big( P_m - (\psi_d I_q - \psi_q I_d) - D \Delta \omega - f (\Delta \omega + 1) \Big)\end{split}\]Electromagnetic Equations
The stator dynamics include the following equations for the flux linkages in the d and q axes:
\[\begin{split}\dot{E}_d' &= \frac{1}{T_q'} \Big( -E_d' + (X_q - X_q') (i_q - g_{q2} \Psi_{q2} - (1 - g_{q1}) i_q - g_{q2} E_d') \Big) \\ \dot{E}_q' &= \frac{1}{T_d'} \Big( -E_q' - (X_d - X_d') (i_d - g_{d2} \Psi_{d2} - (1 - g_{d1}) i_d + g_{d2} E_q') + E_f \Big)\\ \dot{\Psi}_{d2} &= \frac{1}{T_{d2}} \Big( -\Psi_{d2} + E_q' - (X_d' - X_l) i_d \Big) \\ \dot{\Psi}_{q2} &= \frac{1}{T_{q2}} \Big( -\Psi_{q2} - E_d' - (X_q' - X_l) i_q \Big)\end{split}\]Flux Linkage Dynamics
The following equations describe the stator flux linkage dynamics in the d and q axes:
\[\begin{split}\dot{\Psi}_d &= 2 \pi f_n (R_s i_d + (1 + \Delta \omega) \Psi_q + v_d) \\ \dot{\Psi}_q &= 2 \pi f_n (R_s i_q - (1 + \Delta \omega) \Psi_d + v_q)\end{split}\]Algebraic Equations
The following algebraic equations govern the system:
\[\begin{split}i_d &= \frac{1}{x_d''} \Big( -\psi_d + g_{d1} e_q' + (1 - g_{d1}) \psi_{d2} \Big)\\ i_q &= \frac{1}{x_q''} \Big( -\psi_q - g_{q1} e_d' + (1 - g_{q1}) \psi_{q2} \Big)\\ g_{d1} &= \frac{x_d'' - x_l}{x_d' - x_l}\\ g_{q1} &= \frac{x_q'' - x_l}{x_q' - x_l}\\ g_{d2} &= \frac{1 - g_{d1}}{x_d' - x_l}\\ g_{q2} &= \frac{1 - g_{q1}}{x_q' - x_l}\end{split}\]- _type = 'Synchronous_machine'
- _name = 'Synchronous_machine_subtransient_model_Sauer_Pai'
- e_dprim
- e_qprim
- psid
- psiq
- psid2
- psiq2
- x_l
- gd1
- gq1
- gd2
- gq2
- x_dprim
- x_qprim
- T_dprim
- T_qprim
- x_dsec
- x_qsec
- T_dsec
- T_qsec
- sauer_pai(dae: pydynamicestimator.system.Dae, i_d: casadi.SX, i_q: casadi.SX)[source]
Sauer and Pai model. :param dae: :type dae: DAE :param i_d: :type i_d: casadi.SX :param i_q: :type i_q: casadi.SX
- Parameters:
i_d (casadi.SX)
i_q (casadi.SX)
- _gd_coeffs() None[source]
Subtransient coupling coefficients used by both the stator-current expressions and the flux ODEs.
- Return type:
None
- input_current(dae: pydynamicestimator.system.Dae) Tuple[casadi.SX, casadi.SX][source]
Stator currents as inlined explicit expressions of the flux states.
- Parameters:
- Return type:
Tuple[casadi.SX, casadi.SX]
- electromagnetic(dae: pydynamicestimator.system.Dae)[source]
Sauer-Pai (subtransient, stator dynamics) electromagnetic model. The stator-current derivation is
input_current()(overridden by the _DAE variant to expose i_d/i_q as private algebraics); the shared flux/ stator physics and the air-gap power live insauer_pai().- Parameters:
- class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP6(avr=None, governor=None, pss=None, shaft=None)[source]
Bases:
SynchronousSubtransient Sauer and Pai SG 6th order model with neglected stator dynamics (stator modeled with algebraic equations) and included TGOV1 governor and IEEEDC1A AVR
The model includes the following equations:
Rotor Dynamics
\[\begin{split}\dot{\delta} &= 2 \pi f_n \Delta \omega \\ \Delta \dot{\omega} &= \frac{1}{2 H} \Big( P_m - (\psi_d I_q - \psi_q I_d) - D \Delta \omega - f (\Delta \omega + 1) \Big)\end{split}\]Electromagnetic Equations
The stator dynamics include the following equations for the flux linkages in the d and q axes:
\[\begin{split}\dot{E}_d' &= \frac{1}{T_q'} \Big( -E_d' + (X_q - X_q') (i_q - g_{q2} \Psi_{q2} - (1 - g_{q1}) i_q - g_{q2} E_d') \Big) \\ \dot{E}_q' &= \frac{1}{T_d'} \Big( -E_q' - (X_d - X_d') (i_d - g_{d2} \Psi_{d2} - (1 - g_{d1}) i_d + g_{d2} E_q') + E_f \Big)\\ \dot{\Psi}_{d2} &= \frac{1}{T_{d2}} \Big( -\Psi_{d2} + E_q' - (X_d' - X_l) i_d \Big) \\ \dot{\Psi}_{q2} &= \frac{1}{T_{q2}} \Big( -\Psi_{q2} - E_d' - (X_q' - X_l) i_q \Big)\end{split}\]Flux Linkage Dynamics
The following equations describe the stator flux linkage dynamics in the d and q axes:
Algebraic Equations
The following algebraic equations govern the system:
\[\begin{split}0 &=-i_d +\frac{1}{x_d''} \Big( -\psi_d + g_{d1} e_q' + (1 - g_{d1}) \psi_{d2} \Big)\\ 0&=-i_q +\frac{1}{x_q''} \Big( -\psi_q - g_{q1} e_d' + (1 - g_{q1}) \psi_{q2} \Big)\\ 0&= R_s i_d + (1 + \Delta \omega) \Psi_q + v_d \\ 0&= R_s i_q - (1 + \Delta \omega) \Psi_d + v_q \\ g_{d1} &= \frac{x_d'' - x_l}{x_d' - x_l}\\ g_{q1} &= \frac{x_q'' - x_l}{x_q' - x_l}\\ g_{d2} &= \frac{1 - g_{d1}}{x_d' - x_l}\\ g_{q2} &= \frac{1 - g_{q1}}{x_q' - x_l}\end{split}\]- _type = 'Synchronous_machine'
- _name = 'Synchronous_machine_subtransient_model_Sauer_Pai_6th_order'
- e_dprim
- e_qprim
- psid2
- psiq2
- x_l
- gd1
- gq1
- gd2
- gq2
- x_dprim
- x_qprim
- T_dprim
- T_qprim
- x_dsec
- x_qsec
- T_dsec
- T_qsec
- sauer_pai_6(dae: pydynamicestimator.system.Dae, i_d: casadi.SX, i_q: casadi.SX, psid: casadi.SX, psiq: casadi.SX)[source]
Sauer and Pai model. :param dae: :type dae: DAE :param i_d: :type i_d: casadi.SX :param i_q: :type i_q: casadi.SX :param psid: :type psid: casadi.SX :param psiq: :type psiq: casadi.SX
- Returns:
Args – psid (): psiq ():
- Parameters:
i_d (casadi.SX)
i_q (casadi.SX)
psid (casadi.SX)
psiq (casadi.SX)
- stator_solve(dae: pydynamicestimator.system.Dae)[source]
Eliminate the four algebraic stator unknowns (i_d, i_q, psi_d, psi_q) by a symbolic 4x4 solve per machine. Returns (i_d, i_q, psid, psiq). The _DAE variant overrides this to expose them as private algebraics.
- Parameters:
- electromagnetic(dae: pydynamicestimator.system.Dae)[source]
Sauer-Pai 6th-order (algebraic stator) electromagnetic model. The four stator unknowns come from
stator_solve()(overridden by the _DAE variant to expose them as private algebraics); the shared flux physics and the air-gap power live insauer_pai_6().- Parameters:
- class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP6DAE(avr=None, governor=None, pss=None, shaft=None)[source]
Bases:
SynchronousSubtransientSP6Subtransient Sauer-Pai 6th-order machine expressed as an explicit DAE.
Identical physics to
SynchronousSubtransientSP6, but instead of symbolically eliminating the four stator unknowns (i_d, i_q, psi_d, psi_q) with a per-instanceca.solve, the four stator equations are declared as device-private algebraic variables/equations (_algebs_int) and handed to the DAE solver. This validates the private-algebraic mechanism: it must reproduceSynchronousSubtransientSP6to integrator tolerance. Seedocs/algebraic_equations_design.mdandCHANGELOG_algebraic_equations.md.The stator block is linear in the unknowns, so the parent’s
ca.solveis the exact solution ofg = 0– the DAE integrator converges to the same values, only via a numerical Newton solve per step instead of a closed-form expression built once.Note
SynchronousSubtransientSP6initially appeared ill-posed at init, but the root cause was a spurious2*pi*f_nfactor on its algebraic stator equations (a leftover from the stator dynamic equations) that inflated the per-devicefinitJacobian condition number to ~1e11. With that factor removed (see the parent’sfgcall), SP6 is well-posed and this DAE form reproduces it to ~9e-7 (seepydynamicestimator/tests/test_algebraic_parity.py).- _name = 'Synchronous_machine_subtransient_model_Sauer_Pai_6th_order_DAE'
- stator_solve(dae: pydynamicestimator.system.Dae)[source]
The four stator unknowns are genuine device-private algebraic variables in dae.y; write their defining equations and return them (the integrator solves the 4x4 block instead of ca.solve). Inherits SP6’s
electromagnetic()and the base orchestration.- Parameters:
- class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP_DAE(avr=None, governor=None, pss=None, shaft=None)[source]
Bases:
SynchronousSubtransientSPSubtransient Sauer-Pai machine (with stator dynamics) with the stator currents exposed as explicit device-private algebraic variables.
Identical physics to
SynchronousSubtransientSP, but the stator currentsi_d, i_q– normally inlined explicit expressions of the flux states – are declared as private algebraic variables (_algebs_int) with their defining equations0 = -i + <expr>handed to the DAE solver. Must reproduceSynchronousSubtransientSPto integrator tolerance.This is the working parity vehicle for the private-algebraic mechanism: the parent model initialises robustly and the defining equations are linear with a trivially non-singular Jacobian (index-1). See
docs/algebraic_equations_design.mdandCHANGELOG_algebraic_equations.md.- _name = 'Synchronous_machine_subtransient_model_Sauer_Pai_DAE'
- input_current(dae: pydynamicestimator.system.Dae) Tuple[casadi.SX, casadi.SX][source]
Stator currents as device-private algebraic variables in dae.y, defined by 0 = -i + <explicit expression> (the same expression the parent inlines). The defining Jacobian dg/di = -1 is trivially non-singular (index-1). Inherits SP’s
electromagnetic()and the base orchestration.- Parameters:
- Return type:
Tuple[casadi.SX, casadi.SX]