Dynamic Models
Below is the list of all dynamic models used in the system. The corresponding classes are located in the ./devices directory. The following documentation describes the mathematical equations and the python implementation. Detailed instructions on how to create user-defined models aro not yet available.
Metaclass
All devices are derived from
- class pydynamicestimator.devices.device.DeviceRect[source]
Dynamic or static device modeled in rectangular coordinates. Used as a parent class for all devices modeled in rectangular coordinates.
- properties
Flags for method calls (e.g., ‘gcall’, ‘fcall’).
- Type:
dict[str, bool]
- xf
Final state results for simulation/estimation.
- Type:
dict[str, np.ndarray]
- xinit
Initial state values for all devices of the instance.
- Type:
dict[State, list[float]]
- xmin
Minimum limited state value.
- Type:
list[float]
- xmax
Maximum limited state value.
- Type:
list[float]
- _params
Device parameters, such as rated voltage, power, and frequency and internal parameters.
- Type:
dict[str, float]
- _data
Additional data which will be used in a list, one entry for each device of this object instance.
- Type:
dict[str, str]
- bus
Buses where the device is connected. Each device has an entry in the list.
- Type:
list[Optional[str]]
- states
State variables.
- Type:
list[States]
- units
Units of state variables.
- Type:
list[States]
- ns
Total number of states in the model.
- Type:
float
- _states_noise
Noise for each state variable.
- Type:
dict[States, float]
- _states_init_error
Initial error for each state variable.
- Type:
dict[States, float]
- vre
Order of the real voltage value for each device in the overall DAE model.
- Type:
list[float]
- vim
Order of the real voltage value for each device in the overall DAE model.
- Type:
list[float]
- _algebs
Algebraic variables (‘vre’, ‘vim’).
- Type:
list[str]
- _descr
Descriptions for key parameters.
- Type:
dict[str, str]
The method for generating the differential and algebraic equations in each model is:
- pydynamicestimator.devices.device.DeviceRect.fgcall(self, dae)
A method that executes the differential and algebraic equations of the model and adds them to the appropriate places in the Dae class
- Parameters:
dae (
Dae) – an instance of a class Dae- Returns:
None
- Return type:
None
—
Synchronous Generator Transient Model
The Synchronous Generator Transient Model provides a two-axis dynamic approximation of a synchronous generator. It is based on the formulation provided in [1].
Python Implementation
- class pydynamicestimator.devices.synchronous.SynchronousTransient(avr=None, governor=None, pss=None, shaft=None)[source]
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}\]
Synchronous Generator Subtransient Model
The Synchronous Generator Subransient Model provides a dynamic approximation of a synchronous generator based on Anderson-Fauad’s model. It is based on the formulation provided in [1].
Python Implementation
- class pydynamicestimator.devices.synchronous.SynchronousSubtransient(avr=None, governor=None, pss=None, shaft=None)[source]
- 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}\]
Synchronous Generator Subtransient Model (with Stator Dynamics)
The Synchronous Generator Subtransient Model with Stator Dynamics provides a dynamic approximation of a synchronous generator, including stator dynamics, based on the formulation by Sauer and Pai. It extends the standard subtransient model by incorporating additional stator field dynamics, as described in [3].
Python Implementation
- class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP(avr=None, governor=None, pss=None, shaft=None)[source]
- 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}\]
Synchronous Generator Subtransient Model (without Stator Dynamics)
The Synchronous Generator Subtransient Model without Stator Dynamics provides a dynamic approximation of a synchronous generator, based on the formulation by Sauer and Pai. The stator dynamics are neglected making the model 6th order [3].
Python Implementation
- class pydynamicestimator.devices.synchronous.SynchronousSubtransientSP6(avr=None, governor=None, pss=None, shaft=None)[source]
- 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}\]
Converter Metaclass
This is the parent class for all inverter models. It introduces all necessary parameters and also stores the model initialization details with a custom script.
- class pydynamicestimator.devices.inverter.Inverter(filter=None, pll=None, angle=None, voltage=None, inner=None)[source]
Metaclass for inverters
- current_ref(dae)[source]
Inner current references
(ifd_ref, ifq_ref), host-mediated: published by the inner strategy and read back by its own current loop. A symbolic intermediate today; when the current limiter (b1) lands it will resolve to a saturatedvar_symalgebraic (i* = sat(iref)), with no consumer change – this accessor is that interception seam.- Parameters:
dae (Dae)
- fgcall(dae)[source]
Orchestrate the pluggable converter strategies into the DAE residual.
The host owns only the cross-cutting glue: the reference-frame frequency, the Park transforms into the converter frame, the power measurement, and the power-measurement filters. Everything else is delegated, in signal-flow order: PLL -> angle source (omega_c, delta_c) -> voltage control (Vcd) -> inner control (Vsw) -> filter (LCL). Each strategy reads host params/states by attribute and writes its own equations; the host threads the symbolic intermediates (omega_c, Vcd, Vsw) between them.
- Return type:
None- Parameters:
dae (Dae)
- finit_anchor_residuals(dae)[source]
Extra steady-state anchor residuals (each an n-vector that must equal 0) a device declares to keep its initialization Newton system square.
The generic
finit()pins each device’s state ODEs (f = 0) plus two quantities through the bus-current balance (g[vre]/g[vim]), so the square-system invariant isn_setpoints == 2(the SG: Pref + Vref). A device with more setpoints returnsn_setpoints - 2anchor residuals to balance them. Empty by default -> existing devices are unaffected.Example – the inverter has 3 setpoints (Pref/Qref/Vref) but the bus equations pin only 2; it returns one anchor (Qref = Qc) here. See
docs/inverter_modernization_design.md§7.- Return type:
list- Parameters:
dae (Dae)
- pll_frequency(dae)[source]
Synchronizing frequency from the PLL, host-mediated (the PSS-style coupling): the PLL’s
omega_pllwhen a PLL strategy is present, else the nominalomega_net(no PLL => no estimate). A consumer reads this accessor and never references the PLL strategy directly.- Return type:
SX- Parameters:
dae (Dae)
- switching_voltage(dae)[source]
Switching voltage
(Vswd, Vswq)in the network frame, host-mediated: published by the inner strategy and read by the filter. A symbolic intermediate, not a DAE variable (byte-identical).- Parameters:
dae (Dae)
- static to_external(d_int, q_int, delta)[source]
Park-rotate
(d, q)from the converter (internal) frame back to the network (external) frame defined by angledelta.
- static to_internal(d_ext, q_ext, delta)[source]
Park-rotate
(d, q)from the network (external) frame into the converter (internal) frame defined by angledelta.
- var_sym(dae, name)[source]
Resolve a registered variable name to its symbol wherever it lives:
dae.yfor a device-private algebraic,dae.xfor a differential state. Lets the converter equations read a coupling quantity (a filter state, the frame angle, …) without knowing whether a given realization made it a state or an algebraic – e.g. the dynamicLCLfilter (states) vs a future quasi-staticLCL_static(algebraics). MirrorsSynchronous.var_sym; today every inverter variable is a state, so this resolves todae.x.- Return type:
SX- Parameters:
dae (Dae)
name (str)
Grid-Following Converter
The Grid-Following Converter Model provides a dynamic representation of a grid-following inverter. It captures the small-signal stability behavior in low-inertia power systems, as described in [2].
Python Implementation
- class pydynamicestimator.devices.inverter.GridFollowing(filter=None, angle=None, voltage=None, inner=None, pll=None)[source]
- Grid-Following Inverter (with Droop)
Based on the grid-following inverter model in https://doi.org/10.1109/TPWRS.2021.3061434
The dynamic behavior of the grid-following converter is described by the following differential equations:
Converter Voltage Dynamics
\[\dot{v}_{fd_{ext}} = \frac{\omega_{b}}{c_{f}}(i_{fd_{ext}} - i_{td_{ext}}) + \omega_{net}\omega_{b}v_{fq_{ext}}\]\[\dot{v}_{fq_{ext}} = \frac{\omega_{b}}{c_{f}}(i_{fq_{ext}} - i_{tq_{ext}}) - \omega_{net}\omega_{b}v_{fd_{ext}}\]Converter Current Dynamics
\[\dot{i}_{fd_{ext}} = \frac{\omega_{b}}{l_{f}}(v_{swd} - v_{fd_{ext}}) - \frac{\omega_{b}r_{f}}{l_{f}}i_{fd_{ext}} + \omega_{net}\omega_{b}i_{fq_{ext}}\]\[\dot{i}_{fq_{ext}} = \frac{\omega_{b}}{l_{f}}(v_{swq} - v_{fq_{ext}}) - \frac{\omega_{b}r_{f}}{l_{f}}i_{fq_{ext}} - \omega_{net}\omega_{b}i_{fd_{ext}}\]Grid-Side Current Dynamics
\[\dot{i}_{td_{ext}} = \frac{\omega_b}{l_t}(v_{fd_{ext}} - v_{n_{re}}) - \frac{\omega_b r_t}{l_t}i_{td_{ext}} + \omega_{net} \omega_b i_{tq_{ext}}\]\[\dot{i}_{tq_{ext}} = \frac{\omega_b}{l_t}(v_{fq_{ext}} - v_{n_{im}}) - \frac{\omega_b r_t}{l_t}i_{tq_{ext}} - \omega_{net} \omega_b i_{td_{ext}}\]Phase-Locked Loop (PLL) Dynamics
\[\dot{\epsilon} = v_{fq_{pll}}\]\[\delta\dot{\theta}_{pll} = \omega_{b}\delta\omega_{pll}\]Power and Frequency Dynamics
\[\dot{\tilde{p}}_{c} = \omega_{f}(p_{c} - \tilde{p}_{c})\]\[\delta\dot{\theta}_{c} = \omega_{b}\delta\omega_{c}\]\[\dot{\tilde{q}}_{c} = \omega_{f}(q_{c} - \tilde{q}_{c})\]Control Dynamics
\[\dot{\xi}_{d} = v_{fd^{*}} - v_{fd_{int}}\]\[\dot{\xi}_{q} = v_{fq^{*}} - v_{fq_{int}}\]\[\dot{\gamma}_{d} = i_{fd^{*}} - i_{fd_{int}}\]\[\dot{\gamma}_{q} = i_{fq^{*}} - i_{fq_{int}}\]
Grid-Forming Converter
The Grid-Forming Converter Model provides a dynamic representation of a grid-forming inverter. It captures the small-signal stability behavior in low-inertia power systems, as described in [2].
Python Implementation
- class pydynamicestimator.devices.inverter.GridForming(filter=None, angle=None, voltage=None, inner=None, pll=None)[source]
- Grid-Forming Inverter (Droop-Based)
Based on the grid-forming inverter model in https://doi.org/10.1109/TPWRS.2021.3061434 The dynamic behavior of the grid-forming converter is described by the following differential equations:
Converter Voltage Dynamics
\[\dot{v}_{fd_{ext}} = \frac{\omega_{b}}{c_{f}}(i_{fd_{ext}} - i_{td_{ext}}) + \omega_{net}\omega_{b}v_{fq_{ext}}\]\[\dot{v}_{fq_{ext}} = \frac{\omega_{b}}{c_{f}}(i_{fq_{ext}} - i_{tq_{ext}}) - \omega_{net}\omega_{b}v_{fd_{ext}}\]Converter Current Dynamics
\[\dot{i}_{fd_{ext}} = \frac{\omega_{b}}{l_{f}}(v_{swd} - v_{fd_{ext}}) - \frac{\omega_{b}r_{f}}{l_{f}}i_{fd_{ext}} + \omega_{net}\omega_{b}i_{fq_{ext}}\]\[\dot{i}_{fq_{ext}} = \frac{\omega_{b}}{l_{f}}(v_{swq} - v_{fq_{ext}}) - \frac{\omega_{b}r_{f}}{l_{f}}i_{fq_{ext}} - \omega_{net}\omega_{b}i_{fd_{ext}}\]Grid-Side Current Dynamics
\[\dot{i}_{td_{ext}} = \frac{\omega_b}{l_t}(v_{fd_{ext}} - v_{n_{re}}) - \frac{\omega_b r_t}{l_t}i_{td_{ext}} + \omega_{net} \omega_b i_{tq_{ext}}\]\[\dot{i}_{tq_{ext}} = \frac{\omega_b}{l_t}(v_{fq_{ext}} - v_{n_{im}}) - \frac{\omega_b r_t}{l_t}i_{tq_{ext}} - \omega_{net} \omega_b i_{td_{ext}}\]Power and Frequency Dynamics
\[\dot{\tilde{p}}_{c} = \omega_{f}(p_{c} - \tilde{p}_{c})\]\[\delta\dot{\theta}_{c} = \omega_{b}\delta\omega_{c}\]\[\dot{\tilde{q}}_{c} = \omega_{f}(q_{c} - \tilde{q}_{c})\]Control Dynamics
\[\dot{\xi}_{d} = v_{fd^{*}} - v_{fd_{int}}\]\[\dot{\xi}_{q} = v_{fq^{*}} - v_{fq_{int}}\]\[\dot{\gamma}_{d} = i_{fd^{*}} - i_{fd_{int}}\]\[\dot{\gamma}_{q} = i_{fq^{*}} - i_{fq_{int}}\]