pydynamicestimator.devices.inverter
Classes
Metaclass for inverters |
|
Grid-Following Inverter (with Droop) |
|
Grid-Forming Inverter (Droop-Based) |
Module Contents
- class pydynamicestimator.devices.inverter.Inverter(filter=None, pll=None, angle=None, voltage=None, inner=None)[source]
Bases:
pydynamicestimator.devices.device.DeviceRectMetaclass for inverters
- _init_method = 'sequential'
- _angle: pydynamicestimator.devices.inverter_angle.AngleSource = None
- _pll: pydynamicestimator.devices.inverter_pll.PLL = None
- omega_f
- ns = 0
- Vcd = None
- ifd_ref = None
- ifq_ref = None
- Vswd = None
- Vswq = None
- gcall(dae: pydynamicestimator.system.Dae) None[source]
- Parameters:
- Return type:
None
- var_sym(dae: pydynamicestimator.system.Dae, name: str) casadi.SX[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.- Parameters:
name (str)
- Return type:
casadi.SX
- 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.
- 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.
- _pll_fgcall(dae: pydynamicestimator.system.Dae, omega_ref_vec, omega_b) None[source]
Delegate to the PLL strategy (no-op if there is no PLL). Runs before the angle source so that
self.omega_pllis set when a PLL-consuming angle source reads it viapll_frequency().- Parameters:
- Return type:
None
- pll_frequency(dae: pydynamicestimator.system.Dae) casadi.SX[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.- Parameters:
- Return type:
casadi.SX
- voltage_command(dae: pydynamicestimator.system.Dae) casadi.SX[source]
Voltage-magnitude reference
Vcd, host-mediated: published on the host by the voltage strategy’sfgcalland read here by the inner controller. A symbolic intermediate, not a DAE variable (byte-identical).- Parameters:
- Return type:
casadi.SX
- current_ref(dae: pydynamicestimator.system.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:
- switching_voltage(dae: pydynamicestimator.system.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:
- fgcall(dae: pydynamicestimator.system.Dae) None[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.
- Parameters:
- Return type:
None
- finit_anchor_residuals(dae: pydynamicestimator.system.Dae) list[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.- Parameters:
- Return type:
list
- _finit_sequential(dae: pydynamicestimator.system.Dae) None[source]
Strategy-composed staged initialization (the inverter default), shared by GridForming and GridFollowing. Each strategy owns its own stage; the host threads the operating point between them and assembles the result by name. See
docs/inverter_init_schematic.htmlfor the data flow.filter (decoupled) -> pll (decoupled) -> powers from the filter op-point (frame-invariant) -> inner (frame
delta_c+ commandVcd+ the four integrators) -> angle (Pref/Pc_tilde, ownsdelta_c) -> voltage (Qref/Qc_tilde, unpacksVcd->Vrefvia its gauge).- Parameters:
- Return type:
None
- _load_finit(dae: pydynamicestimator.system.Dae, vals: dict) None[source]
Load the computed init values by NAME: differential states -> dae.xinit, device-private algebraics -> dae.yinit, setpoints -> the device’s setpoint arrays. Driven by self.states / self._algebs_int / self._setpoints, so it adapts to any strategy combo and to the state-vs-algebraic realization (replacing the previous hardcoded, layout-specific solution list).
- Parameters:
vals (dict)
- Return type:
None
- class pydynamicestimator.devices.inverter.GridFollowing(filter=None, angle=None, voltage=None, inner=None, pll=None)[source]
Bases:
InverterGrid-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}}\]- _type = 'Inverter'
- _name = 'GridFollowing_inverter_model'
- class pydynamicestimator.devices.inverter.GridForming(filter=None, angle=None, voltage=None, inner=None, pll=None)[source]
Bases:
InverterGrid-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}}\]- _type = 'Inverter'
- _name = 'GridForming_inverter_model'
- omega_c = None