pydynamicestimator.devices.synchronous

Classes

Synchronous

Metaclass for SG in rectangular coordinates with TGOV1 governor and pluggable AVR

SynchronousTransient

Transient two-axis SG with TGOV1 governor and IEEEDC1A AVR

SynchronousSubtransient

Subtransient Anderson Fouad SG with TGOV1 governor and IEEEDC1A AVR

SynchronousSubtransientSP

Subtransient Sauer and Pai SG model with stator dynamics

SynchronousSubtransientSP6

Subtransient Sauer and Pai SG 6th order model with neglected stator dynamics

SynchronousSubtransientSP6DAE

Subtransient Sauer-Pai 6th-order machine expressed as an explicit DAE.

SynchronousSubtransientSP_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.DeviceRect

Metaclass for SG in rectangular coordinates with TGOV1 governor and pluggable AVR

Parameters:
_shaft: pydynamicestimator.devices.shaft.Shaft
_avr: pydynamicestimator.devices.avr.AVR
_governor: pydynamicestimator.devices.governor.Governor
_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:
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 when dae.omega_ref_buses is present. Shared by the rotor seam (was duplicated inside every electromagnetic method).

Parameters:

dae (pydynamicestimator.system.Dae)

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 SingleMass writes the classic single-mass swing equation (reading the governor port var_sym("pm"), the air-gap power Pe and the reference frequency); a multi-mass (torsional) shaft adds further rotor masses while keeping the generator mass on the delta / omega names, so the electromagnetic models, the network injection and the controllers are untouched. See docs/algebraic_equations_design.md (§9.6/§9.7).

Parameters:
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:

dae (pydynamicestimator.system.Dae)

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) where Pe is the air-gap power/torque consumed by rotor().

Parameters:

dae (pydynamicestimator.system.Dae)

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.f and, if it declares private algebraics (e.g. an algebraic ‘pm’), their residuals into dae.g – hence a single fgcall.

Parameters:

dae (pydynamicestimator.system.Dae)

Return type:

None

tgov1(dae: pydynamicestimator.system.Dae) None[source]

Deprecated: use governor_fcall instead.

Parameters:

dae (pydynamicestimator.system.Dae)

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.f and, if it declares private algebraics (e.g. an algebraic ‘Efd’), their residuals into dae.g – hence a single fgcall.

Parameters:

dae (pydynamicestimator.system.Dae)

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 (in dae.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:
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.f and, if it declares private algebraics (e.g. an algebraic ‘Vs’), their residuals into dae.g.

Parameters:

dae (pydynamicestimator.system.Dae)

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:

dae (pydynamicestimator.system.Dae)

Return type:

casadi.SX

ieeedc1a(dae: pydynamicestimator.system.Dae) None[source]

Deprecated: use avr_fcall instead.

Parameters:

dae (pydynamicestimator.system.Dae)

Return type:

None

class pydynamicestimator.devices.synchronous.SynchronousTransient(avr=None, governor=None, pss=None, shaft=None)[source]

Bases: Synchronous

Transient 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:

dae (pydynamicestimator.system.Dae)

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:

dae (pydynamicestimator.system.Dae)

class pydynamicestimator.devices.synchronous.SynchronousSubtransient(avr=None, governor=None, pss=None, shaft=None)[source]

Bases: Synchronous

Subtransient 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:

dae (pydynamicestimator.system.Dae)

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:

dae (pydynamicestimator.system.Dae)

class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP(avr=None, governor=None, pss=None, shaft=None)[source]

Bases: Synchronous

Subtransient 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:
_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:

dae (pydynamicestimator.system.Dae)

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 in sauer_pai().

Parameters:

dae (pydynamicestimator.system.Dae)

class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP6(avr=None, governor=None, pss=None, shaft=None)[source]

Bases: Synchronous

Subtransient 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:
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:

dae (pydynamicestimator.system.Dae)

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 in sauer_pai_6().

Parameters:

dae (pydynamicestimator.system.Dae)

class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP6DAE(avr=None, governor=None, pss=None, shaft=None)[source]

Bases: SynchronousSubtransientSP6

Subtransient 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-instance ca.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 reproduce SynchronousSubtransientSP6 to integrator tolerance. See docs/algebraic_equations_design.md and CHANGELOG_algebraic_equations.md.

The stator block is linear in the unknowns, so the parent’s ca.solve is the exact solution of g = 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

SynchronousSubtransientSP6 initially appeared ill-posed at init, but the root cause was a spurious 2*pi*f_n factor on its algebraic stator equations (a leftover from the stator dynamic equations) that inflated the per-device finit Jacobian condition number to ~1e11. With that factor removed (see the parent’s fgcall), SP6 is well-posed and this DAE form reproduces it to ~9e-7 (see pydynamicestimator/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:

dae (pydynamicestimator.system.Dae)

class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP_DAE(avr=None, governor=None, pss=None, shaft=None)[source]

Bases: SynchronousSubtransientSP

Subtransient Sauer-Pai machine (with stator dynamics) with the stator currents exposed as explicit device-private algebraic variables.

Identical physics to SynchronousSubtransientSP, but the stator currents i_d, i_q – normally inlined explicit expressions of the flux states – are declared as private algebraic variables (_algebs_int) with their defining equations 0 = -i + <expr> handed to the DAE solver. Must reproduce SynchronousSubtransientSP to 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.md and CHANGELOG_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:

dae (pydynamicestimator.system.Dae)

Return type:

Tuple[casadi.SX, casadi.SX]