pydynamicestimator.devices.shaft ================================ .. py:module:: pydynamicestimator.devices.shaft Attributes ---------- .. autoapisummary:: pydynamicestimator.devices.shaft.SHAFT_REGISTRY Classes ------- .. autoapisummary:: pydynamicestimator.devices.shaft.Shaft pydynamicestimator.devices.shaft.SingleMass pydynamicestimator.devices.shaft.MultiMassShaft pydynamicestimator.devices.shaft.Shaft4Mass pydynamicestimator.devices.shaft.Shaft5Mass Module Contents --------------- .. py:class:: Shaft Bases: :py:obj:`abc.ABC` Abstract base class for the rotor-shaft mechanics (pluggable strategy). The shaft owns the rotor angle/speed states and writes the swing (rotor motion) equations. Every shaft must expose the **generator mass** as the two differential states ``delta`` (rotor angle wrt the synchronous frame) and ``omega`` (the ABSOLUTE per-unit speed, = 1.0 at synchronism, NOT the deviation). Those are the names the electromagnetic model, the network current injection (``gcall``), the governor, the PSS and the initialisation all read, so a multi-mass (torsional) shaft adds *further* rotor masses (``delta_`` / ``omega_``) while keeping the generator mass on the canonical names -- nothing downstream changes. Coupling ports the shaft consumes: * ``pm`` -- the governor's mechanical-power output, read via ``host.var_sym(dae, "pm")`` and distributed across the turbine masses; * ``Pe`` -- the electromagnetic air-gap power, passed into :meth:`fgcall` and applied to the generator mass only. It reads the per-machine reference-frame frequency via ``host._omega_ref(dae)`` (= 1 p.u. by default, or the estimated per-bus reference when present). Symmetric to :class:`~pydynamicestimator.devices.avr.AVR`, :class:`~pydynamicestimator.devices.governor.Governor` and :class:`~pydynamicestimator.devices.pss.PSS`: the shaft does NOT own state arrays or DAE indices. It declares what states / parameters / noise values it needs and the host :class:`Synchronous` machine registers them on itself. Rotor angles and speeds are inherently *differential* states, so -- unlike the AVR/governor/PSS -- a shaft declares no private algebraic variables. .. py:method:: states() -> List[str] :abstractmethod: Return ordered differential-state names. MUST contain 'delta' and 'omega' (the generator mass). .. py:method:: units() -> List[str] :abstractmethod: Return units for each state, same length as :meth:`states`. .. py:method:: params() -> Dict[str, float] :abstractmethod: Return dict of parameter names -> default values (empty for the single-mass shaft, which reuses the machine's own H, D, f). .. py:method:: states_noise() -> Dict[str, float] :abstractmethod: Return process-noise specification for each state. .. py:method:: states_init_error() -> Dict[str, float] :abstractmethod: Return initial error for each state. .. py:method:: x0() -> Dict[str, float] :abstractmethod: Return default initial guess for each state. .. py:method:: descriptions() -> Dict[str, str] :abstractmethod: Return descriptions for states and params. .. py:method:: setpoints() -> Dict[str, float] Return setpoint names -> defaults. The shaft mechanics have none. .. py:method:: fgcall(host, dae: pydynamicestimator.system.Dae, Pe) -> None :abstractmethod: Write the rotor-motion (swing) differential equations into ``dae.f``. :param host: The Synchronous machine instance. Access state indices via host.delta, host.omega (generator mass) and, for a multi-mass shaft, host.delta_/host.omega_; inertia/damping via host.H, host.D, host.f (generator mass) and host.H_, ...; the governor port via host.var_sym(dae, "pm") and the reference frequency via host._omega_ref(dae). :param dae: The DAE system object. :param Pe: The electromagnetic air-gap power (CasADi SX, one entry per machine), applied to the generator mass. .. py:class:: SingleMass Bases: :py:obj:`Shaft` Single rigid rotor mass -- the classic swing equation. Framework default. .. math:: \dot{\delta} &= 2 \pi f_n (\omega - \omega_{\text{ref}}) \\ \dot{\omega} &= \frac{1}{2H} \big( P_m - P_e - D (\omega - \omega_{\text{ref}}) - f\,\omega \big) Reuses the machine's own inertia/damping parameters (``host.H``, ``host.D``, ``host.f``), so it declares no parameters of its own. This is byte-identical to the rotor dynamics previously hardcoded in ``Synchronous.rotor`` (the positional baselines in ``tests/`` gate that equivalence). .. py:method:: states() -> List[str] Return ordered differential-state names. MUST contain 'delta' and 'omega' (the generator mass). .. py:method:: units() -> List[str] Return units for each state, same length as :meth:`states`. .. py:method:: params() -> Dict[str, float] Return dict of parameter names -> default values (empty for the single-mass shaft, which reuses the machine's own H, D, f). .. py:method:: states_noise() -> Dict[str, float] Return process-noise specification for each state. .. py:method:: states_init_error() -> Dict[str, float] Return initial error for each state. .. py:method:: x0() -> Dict[str, float] Return default initial guess for each state. .. py:method:: descriptions() -> Dict[str, str] Return descriptions for states and params. .. py:method:: fgcall(host, dae: pydynamicestimator.system.Dae, Pe) -> None Write the rotor-motion (swing) differential equations into ``dae.f``. :param host: The Synchronous machine instance. Access state indices via host.delta, host.omega (generator mass) and, for a multi-mass shaft, host.delta_/host.omega_; inertia/damping via host.H, host.D, host.f (generator mass) and host.H_, ...; the governor port via host.var_sym(dae, "pm") and the reference frequency via host._omega_ref(dae). :param dae: The DAE system object. :param Pe: The electromagnetic air-gap power (CasADi SX, one entry per machine), applied to the generator mass. .. py:class:: MultiMassShaft Bases: :py:obj:`Shaft` Generic multi-mass (torsional) turbine-generator shaft: a linear chain of lumped rotor masses connected by elastic shaft sections. Each mass :math:`i` carries an angle :math:`\delta_i` and an absolute p.u. speed :math:`\omega_i`. With ``omega_b`` :math:`= 2\pi f_n` and the reference speed :math:`\omega_{\text{ref}}` (= 1 p.u.): .. math:: \dot{\delta}_i &= \omega_b (\omega_i - \omega_{\text{ref}}) \\ 2 H_i \dot{\omega}_i &= T_{m,i} - T_{e,i} - \!\!\sum_{j \sim i}\! K_{ij}(\delta_i - \delta_j) - D_i (\omega_i - \omega_{\text{ref}}) - \!\!\sum_{j \sim i}\! D_{ij}(\omega_i - \omega_j) where the sums run over the adjacent masses :math:`j`. The mechanical power :math:`T_{m,i} = F_i P_m` is applied only to the turbine masses (fractions :math:`F_i` summing to 1), and the electromagnetic air-gap power :math:`T_{e,i} = P_e` only to the generator mass. The generator mass also carries the machine's rotor friction :math:`f\,\omega`. The **generator mass keeps the canonical names ``delta`` / ``omega``** and reuses the machine's own ``H`` / ``D`` / ``f``; every other mass ```` contributes states ``delta_`` / ``omega_`` and parameters ``H_`` (inertia) and ``D_`` (self damping). Each shaft section between adjacent masses ``a`` and ``b`` contributes a stiffness ``K__`` and an optional mutual damping ``Dm__`` (default 0). Each turbine mass contributes a mechanical fraction ``F_``. This base is configured by the class attributes below; concrete shafts (:class:`Shaft4Mass`, :class:`Shaft5Mass`) just set them. With a single mass and no sections it reduces exactly to :class:`SingleMass`. .. py:attribute:: _masses :type: List[str] :value: [] .. py:attribute:: _gen :type: str :value: 'gen' .. py:attribute:: _turbines :type: List[str] :value: [] .. py:attribute:: _H_def :type: Dict[str, float] .. py:attribute:: _D_def :type: Dict[str, float] .. py:attribute:: _K_def :type: Dict[str, float] .. py:attribute:: _Dm_def :type: Dict[str, float] .. py:attribute:: _F_def :type: Dict[str, float] .. py:method:: _angle(m: str) -> str .. py:method:: _speed(m: str) -> str .. py:method:: _others() -> List[str] .. py:method:: _sections() -> List[Tuple[str, str]] .. py:method:: states() -> List[str] Return ordered differential-state names. MUST contain 'delta' and 'omega' (the generator mass). .. py:method:: units() -> List[str] Return units for each state, same length as :meth:`states`. .. py:method:: params() -> Dict[str, float] Return dict of parameter names -> default values (empty for the single-mass shaft, which reuses the machine's own H, D, f). .. py:method:: states_noise() -> Dict[str, float] Return process-noise specification for each state. .. py:method:: states_init_error() -> Dict[str, float] Return initial error for each state. .. py:method:: x0() -> Dict[str, float] Return default initial guess for each state. .. py:method:: descriptions() -> Dict[str, str] Return descriptions for states and params. .. py:method:: fgcall(host, dae: pydynamicestimator.system.Dae, Pe) -> None Write the rotor-motion (swing) differential equations into ``dae.f``. :param host: The Synchronous machine instance. Access state indices via host.delta, host.omega (generator mass) and, for a multi-mass shaft, host.delta_/host.omega_; inertia/damping via host.H, host.D, host.f (generator mass) and host.H_, ...; the governor port via host.var_sym(dae, "pm") and the reference frequency via host._omega_ref(dae). :param dae: The DAE system object. :param Pe: The electromagnetic air-gap power (CasADi SX, one entry per machine), applied to the generator mass. .. py:class:: Shaft4Mass Bases: :py:obj:`MultiMassShaft` Four-mass turbine-generator shaft: HP -- IP -- LP -- GEN (linear chain). GEN is the generator mass (carries the air-gap power Pe and the machine's own H/D/f); HP, IP and LP are the turbine masses sharing the mechanical power Pm through the fractions F_hp/F_ip/F_lp (default 0.3/0.3/0.4). Select with ``shaft="Shaft4Mass"`` on the machine row; override any of the default H_*/K_*/F_*/D_*/Dm_* parameters in the data file. .. py:attribute:: _masses :value: ['hp', 'ip', 'lp', 'gen'] .. py:attribute:: _gen :value: 'gen' .. py:attribute:: _turbines :value: ['hp', 'ip', 'lp'] .. py:attribute:: _H_def .. py:attribute:: _K_def .. py:attribute:: _F_def .. py:class:: Shaft5Mass Bases: :py:obj:`MultiMassShaft` Five-mass turbine-generator shaft: HP -- IP -- LP -- GEN -- EXC (linear chain). As :class:`Shaft4Mass` but with an additional exciter mass (EXC) on the far side of the generator. EXC carries no mechanical and no electrical power -- it is a passive inertia coupled through the GEN--EXC shaft section, relevant for the higher torsional modes. Select with ``shaft="Shaft5Mass"``. .. py:attribute:: _masses :value: ['hp', 'ip', 'lp', 'gen', 'exc'] .. py:attribute:: _gen :value: 'gen' .. py:attribute:: _turbines :value: ['hp', 'ip', 'lp'] .. py:attribute:: _H_def .. py:attribute:: _K_def .. py:attribute:: _F_def .. py:data:: SHAFT_REGISTRY :type: Dict[str, type]