pydynamicestimator.devices.inverter_angle ========================================= .. py:module:: pydynamicestimator.devices.inverter_angle .. autoapi-nested-parse:: Inverter angle-source strategies (what makes a converter forming vs following). The angle source produces the converter frequency ``omega_c`` and integrates the converter-frame angle ``delta_c`` -- it fuses the synchronous machine's governor *and* shaft roles (there is no separable mechanical-power intermediate in an inverter), so the active-power droop and the power-measurement state ``Pc_tilde`` live here. It is the **mandatory** axis: a grid-forming converter sets its own frequency from the droop off nominal (``DroopAngle``, exposing ``host.omega_c``); a grid-following converter rides on a PLL's frequency (``PLLAngle``, reading ``host.pll_frequency``). Future variants: VSM (swing ODE), dVOC, matching control. See ``docs/inverter_modernization_design.md`` ยง4.1. NOTE (Phase 2 boundary): the angle source owns the ``Pc_tilde`` *state* and the droop, but the first-order power-measurement filter equation ``d Pc_tilde/dt = omega_f (Pc - Pc_tilde)`` is written by the host, because ``Pc`` is computed from the shared Park-transform loop in ``Inverter.fgcall``. A later cleanup can move power computation behind a host accessor and the filter equation into the strategy. Attributes ---------- .. autoapisummary:: pydynamicestimator.devices.inverter_angle.ANGLE_REGISTRY Classes ------- .. autoapisummary:: pydynamicestimator.devices.inverter_angle.AngleSource pydynamicestimator.devices.inverter_angle._PowerDroopAngle pydynamicestimator.devices.inverter_angle.DroopAngle pydynamicestimator.devices.inverter_angle.PLLAngle Module Contents --------------- .. py:class:: AngleSource Bases: :py:obj:`abc.ABC` Abstract base class for inverter angle-source strategies (pluggable). Must own ``delta_c`` (and, for the droop family, ``Pc_tilde``) and expose the converter frequency: :meth:`fgcall` returns the ``omega_c`` vector consumed by the host's inner control ladder and writes ``dae.f[delta_c]``. The strategy reads host params/states/setpoints by attribute (``host.Kp``, ``host.Pref``, ``dae.x[host.Pc_tilde]``) and, for the following variant, the synchronizing frequency via ``host.pll_frequency(dae)``. .. py:method:: states() -> List[str] :abstractmethod: .. py:method:: algebs() -> List[str] .. py:method:: algebs_units() -> Dict[str, str] .. py:method:: algebs_noise() -> Dict[str, float] .. py:method:: algebs_x0() -> Dict[str, float] .. py:method:: units() -> List[str] :abstractmethod: .. py:method:: params() -> Dict[str, float] :abstractmethod: .. py:method:: states_noise() -> Dict[str, float] :abstractmethod: .. py:method:: states_init_error() -> Dict[str, float] :abstractmethod: .. py:method:: x0() -> Dict[str, float] :abstractmethod: .. py:method:: descriptions() -> Dict[str, str] :abstractmethod: .. py:method:: setpoints() -> Dict[str, float] :abstractmethod: .. py:method:: fgcall(host, dae: pydynamicestimator.system.Dae, omega_ref_vec, omega_b) :abstractmethod: Return the ``omega_c`` vector and write ``dae.f[delta_c]`` (the angle dynamics). Grid-forming variants also set ``host.omega_c``; grid-following variants leave it unset and read ``host.pll_frequency(dae)`` instead. .. py:method:: steady_frequency(host, dae: pydynamicestimator.system.Dae) -> numpy.ndarray The converter frequency at steady state, fed to the inner-control init's decoupling terms. Nominal for ANY synchronizing source: ``delta_c`` steady forces ``omega_c = omega_ref = omega_net``, independent of the angle law. .. py:method:: finit_sequential(host, dae: pydynamicestimator.system.Dae, Pc: numpy.ndarray, delta_c: numpy.ndarray) -> Dict[str, numpy.ndarray] :abstractmethod: Resolve the angle source's states/setpoints from the (frame-invariant) active power ``Pc`` and the inner-controller's frame angle ``delta_c``. The base raises so a new angle source declares its own. .. py:class:: _PowerDroopAngle Bases: :py:obj:`AngleSource` Shared declarations for the power-droop angle family (``Pc_tilde`` + ``delta_c`` states, ``Kp`` droop gain, ``Pref`` setpoint). Concrete subclasses differ only in the frequency anchor inside :meth:`fgcall`. .. py:method:: states() -> List[str] .. py:method:: units() -> List[str] .. py:method:: params() -> Dict[str, float] .. py:method:: states_noise() -> Dict[str, float] .. py:method:: states_init_error() -> Dict[str, float] .. py:method:: x0() -> Dict[str, float] .. py:method:: setpoints() -> Dict[str, float] .. py:method:: descriptions() -> Dict[str, str] .. py:method:: finit_sequential(host, dae: pydynamicestimator.system.Dae, Pc: numpy.ndarray, delta_c: numpy.ndarray) -> Dict[str, numpy.ndarray] Resolve the angle source's states/setpoints from the (frame-invariant) active power ``Pc`` and the inner-controller's frame angle ``delta_c``. The base raises so a new angle source declares its own. .. py:class:: DroopAngle Bases: :py:obj:`_PowerDroopAngle` Grid-forming droop: the converter sets its own frequency from the active-power droop off nominal, ``omega_c = omega_net + Kp (Pref - Pc_tilde)``, and exposes it as ``host.omega_c`` (read by the COI / single reference-frame machinery in ``system.py``). Byte-identical to the previous GridForming angle. .. py:method:: fgcall(host, dae: pydynamicestimator.system.Dae, omega_ref_vec, omega_b) Return the ``omega_c`` vector and write ``dae.f[delta_c]`` (the angle dynamics). Grid-forming variants also set ``host.omega_c``; grid-following variants leave it unset and read ``host.pll_frequency(dae)`` instead. .. py:class:: PLLAngle Bases: :py:obj:`_PowerDroopAngle` Grid-following: the converter frequency rides on the PLL's synchronizing frequency (read host-mediated via ``host.pll_frequency``) plus the active-power droop, ``omega_c = omega_pll + Kp (Pref - Pc_tilde)``. Pairs with a PLL strategy (which owns ``omega_pll`` and the PLL states). Byte-identical to the previous GridFollowing angle. .. py:method:: fgcall(host, dae: pydynamicestimator.system.Dae, omega_ref_vec, omega_b) Return the ``omega_c`` vector and write ``dae.f[delta_c]`` (the angle dynamics). Grid-forming variants also set ``host.omega_c``; grid-following variants leave it unset and read ``host.pll_frequency(dae)`` instead. .. py:data:: ANGLE_REGISTRY :type: Dict[str, type]