Source code for soft4pes.control.lin.rfpsc

"""
Reference-feedforward power synchronization control (RFPSC). 

Reference: 
L. Harnefors, F. M. M. Rahman, M. Hinkkanen, and M. Routimo, “Reference-feedforward 
power-synchronization control,” IEEE Trans. Power Electron., vol. 35, no. 9, pp. 8878-8881, Sep. 
2020.
"""

from types import SimpleNamespace
import numpy as np
from soft4pes.control.common.controller import Controller
from soft4pes.model.grid import RLGridLCLFilter
from soft4pes.utils import alpha_beta_2_dq, dq_2_alpha_beta
from soft4pes.control.common.utils import wrap_theta, get_modulating_signal, FirstOrderFilter


[docs]class RFPSC(Controller): """ Reference-feedforward power synchronization control (RFPSC). Parameters ---------- sys : system object System model. Ra : float, optional Virtual damping resistance [p.u.]. Kp : float, optional Proportional gain of the active power droop control [p.u.]. If not provided, it is calculated based on the nominal frequency, nominal grid peak voltage and the virtual damping resistance. w_bw : float, optional Current filter bandwidth [p.u.]. Attributes ---------- Ra : float Virtual damping resistance [p.u.]. theta_c : float The angle of the synchronous reference frame set by the droop control. The initial angle is set to -pi/2 to align the q-axis with the grid voltage. ig_filter : FirstOrderFilter First-order filter for the current. Kp : float Proportional gain of the active power droop control [p.u.]. Recommended selection is Kp = wg * Ra / Vg, where wg is the nominal grid angular frequency, Ra is the virtual damping resistance (default 0.2 p.u.) and Vg is the nominal grid peak voltage. If value is not provided, nominal grid angular frequency and nominal grid peak voltage are assumed to be 1 p.u. For more details on the tuning and default values, see the reference above. """ def __init__(self, sys, Ra=0.2, Kp=None, w_bw=0.1): super().__init__()
[docs] self.Ra = Ra
[docs] self.theta_c = -np.pi / 2
[docs] self.ig_filter = FirstOrderFilter(w_bw=w_bw, size=2)
if Kp is not None: self.Kp = Kp else: # If Kp is not provided, calculate it based on the nominal frequency, nominal grid peak # voltage and the virtual damping resistance according to the reference. Nominal grid # frequency and nominal grid peak voltage are assumed to be 1 p.u. Vg = 1 wg = 1 self.Kp = wg * self.Ra / Vg
[docs] def execute(self, sys, kTs): """ Execute the RFPSC control algorithm. Parameters ---------- sys : system object The system model. kTs : float Current discrete time instant [s]. Returns ------- SimpleNamespace A SimpleNamespace object containing the modulating signal for the converter (u_abc) and a capacitor voltage reference in case LC(L) filter is used (vc_ref). """ V_ref = self.input.V_ref P_ref = self.input.P_ref vg = sys.get_grid_voltage(kTs) ig_dq = alpha_beta_2_dq(sys.ig, self.theta_c) P = np.dot(vg, sys.ig) # Droop control wc = sys.par.wg + self.Kp * (P_ref - P) # Calculate the reference current in dq-frame ig_ref_d = P_ref / V_ref ig_ref_q = self.ig_filter.output[1] ig_ref_dq = np.array([ig_ref_d, ig_ref_q]) # Calculate the reference voltage v_ref_dq = V_ref * np.array([1, 0]) + self.Ra * (ig_ref_dq - ig_dq) v_ref = dq_2_alpha_beta(v_ref_dq, self.theta_c) # Calculate the dq-frame reference with a frame that is aligned with the grid voltage theta = np.arctan2(vg[1], vg[0]) v_ref_dq = alpha_beta_2_dq(v_ref, theta) self.output = SimpleNamespace( u_abc=get_modulating_signal(v_ref, sys.conv.v_dc), vc_ref_dq=v_ref_dq, ) self.ig_filter.update(ig_dq, self.Ts, sys.base) self.theta_c += wc * self.Ts * sys.base.w self.theta_c = wrap_theta(self.theta_c) return self.output