def __init__(self, system, config): DC2Term.__init__(self, system, config) self.flags.pflow = True self.group = 'DCLink' self.R = NumParam( unit='p.u.', tex_name='R', info='DC line resistance', non_zero=True, default=0.01, r=True, ) self.L = NumParam( unit='p.u.', tex_name='L', info='DC line inductance', non_zero=True, default=0.001, r=True, ) self.C = NumParam( unit='p.u.', tex_name='C', info='DC capacitance', non_zero=True, default=0.001, g=True, ) self.IL = State( tex_name='I_L', info='Inductance current', unit='p.u.', v_str='0', e_str='u * vC', t_const=self.L, ) self.vC = State( tex_name='v_C', info='Capacitor current', unit='p.u.', e_str='-u * (Idc - vC/R - IL)', v_str='v1 - v2', t_const=self.C, ) self.Idc = Algeb( tex_name='I_{dc}', info='Current from node 2 to 1', unit='p.u.', e_str='u * (vC - (v1 - v2)) + ' '(1 - u) * Idc', v_str='-(v1 - v2) / R', diag_eps=True, ) self.v1.e_str = '-Idc' self.v2.e_str = '+Idc'
def __init__(self, system, config): TGBase.__init__(self, system, config) self.pref = Algeb( info='Reference power input', tex_name='P_{ref}', v_str='tm0 * R', e_str='pref0 * R - pref', ) self.wd = Algeb( info='Generator speed deviation', unit='p.u.', tex_name=r'\omega_{dev}', v_str='0', e_str='ue * (omega - wref) - wd', ) self.pd = Algeb(info='Pref plus speed deviation times gain', unit='p.u.', tex_name="P_d", v_str='tm0', e_str='(- wd + pref + paux) * gain - pd') self.LAG_y = State( info='State in lag transfer function', tex_name=r"x'_{LAG}", e_str='LAG_lim_zi * (1 * pd - LAG_y)', t_const=self.T1, v_str='pd', ) self.LAG_lim = AntiWindup( u=self.LAG_y, lower=self.VMIN, upper=self.VMAX, tex_name='lim_{lag}', ) self.LL_x = State(info='State in lead-lag transfer function', tex_name="x'_{LL}", v_str='LAG_y', e_str='(LAG_y - LL_x)', t_const=self.T3) self.LL_y = Algeb( info='Lead-lag Output', tex_name='y_{LL}', v_str='LAG_y', e_str='T2 / T3 * (LAG_y - LL_x) + LL_x - LL_y', ) self.pout.e_str = 'ue * (LL_y - Dt * wd) - pout'
def __init__(self): self.psid = State( info='d-axis flux', tex_name=r'\psi_d', v_str='u * psid0', e_str='u * 2 * pi * fn * (ra * Id + vd + omega * psiq)', ) self.psiq = State( info='q-axis flux', tex_name=r'\psi_q', v_str='u * psiq0', e_str='u * 2 * pi * fn * (ra * Iq + vq - omega * psid)', ) self.Id.e_str += '+ psid' self.Iq.e_str += '+ psiq'
def __init__(self, system, config): DC2Term.__init__(self, system, config) self.flags.pflow = True self.group = 'DCLink' self.C = NumParam( unit='p.u.', info='DC capacitance', non_zero=True, default=0.001, g=True, ) self.vC = State(tex_name='v_C', info='Capacitor current', unit='p.u.', v_str='0', e_str='-u * Idc', t_const=self.C) self.Idc = Algeb( tex_name='I_{dc}', info='Current from node 2 to 1', unit='p.u.', v_str='0', e_str='u * (vC - (v1 - v2)) + ' '(1 - u) * Idc', diag_eps=True, ) self.v1.e_str = '-Idc' self.v2.e_str = '+Idc'
def __init__(self, system, config): Model.__init__(self, system, config) self.flags.tds = True self.group = 'RenGovernor' self.reg = ExtParam(model='RenExciter', src='reg', indexer=self.ree, export=False, ) self.Sn = ExtParam(model='RenGen', src='Sn', indexer=self.reg, tex_name='S_n', export=False, ) self.wge = ExtAlgeb(model='RenExciter', src='wg', indexer=self.ree, export=False, e_str='-1.0 + s1_y', ename='wg', tex_ename=r'\omega_g', ) self.Pe = ExtAlgeb(model='RenGen', src='Pe', indexer=self.reg, export=False, info='Retrieved Pe of RenGen') self.Pe0 = ExtService(model='RenGen', src='Pe', indexer=self.reg, tex_name='P_{e0}', ) self.H2 = ConstService(v_str='2 * H', tex_name='2H') self.Pm = Algeb(tex_name='P_m', info='Mechanical power', e_str='Pe0 - Pm', v_str='Pe0', ) self.wr0 = Algeb(tex_name=r'\omega_{r0}', unit='p.u.', v_str='w0', e_str='w0 - wr0', info='speed set point', ) # `s1_y` is `w_m` self.s1 = Integrator(u='(Pm - Pe) / wge - D * (s1_y - wr0)', T=self.H2, K=1.0, y0='wr0', ) # make two alias states, `wt` and `wg`, pointing to `s1_y` self.wt = AliasState(self.s1_y, tex_name=r'\omega_t') self.wg = AliasState(self.s1_y, tex_name=r'\omega_g') self.s3_y = State(info='Unused state variable', tex_name='y_{s3}', ) self.Kshaft = ConstService(v_str='1.0', tex_name='K_{shaft}', info='Dummy Kshaft', )
def __init__(self, system, config): MotorBaseModel.__init__(self, system, config) self.x2 = ConstService( v_str='xs + xr1*xr2*xm / (xr1*xr2 + xr1*xm + xr2*xm)', tex_name="x''", ) self.T20 = ConstService( v_str='(xr2 + xr1*xm / (xr1 + xm) ) / (wb * rr2)', tex_name="T''_0", ) self.e2d = State( info='real part of 2nd cage voltage', e_str='u * ' '(-wb*slip*(e1q - e2q) + ' '(wb*slip*e1q - (e1d + (x0 - x1) * Iq)/T10) + ' '(e1d - e2d - (x1 - x2) * Iq)/T20)', v_str='0.05 * u', tex_name="e''_d", diag_eps=True, ) self.e2q = State( info='imag part of 2nd cage voltage', e_str='u * ' '(wb*slip*(e1d - e2d) + ' '(-wb*slip*e1d - (e1q - (x0 - x1) * Id)/T10) + ' '(e1q - e2q + (x1 - x2) * Id) / T20)', v_str='0.9 * u', tex_name="e''_q", diag_eps=True, ) self.Id.e_str = 'u * (vd - e2d - rs * Id + x2 * Iq)' self.Id.v_str = '0.9 * u' self.Iq.e_str = 'u * (vq - e2q - rs * Iq - x2 * Id)' self.Iq.v_str = '0.1 * u' self.te.v_str = 'u * (e2d * Id + e2q * Iq)' self.te.e_str = f'{self.te.v_str} - te'
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'Experimental' self.flags.update({'tds': True}) self.uin = State( v_str=0, e_str= 'Piecewise((0, dae_t<= 0), (1, dae_t <= 2), (-1, dae_t <6), (1, True))', tex_name='u_{in}', ) self.x = State( e_str='uin * Ki * HL_zi', v_str=0.05, ) self.y = Algeb(e_str='uin * Kp + x - y', v_str=0.05) self.HL = HardLimiter(u=self.y, lower=self.Wmin, upper=self.Wmax) self.w = Algeb(e_str='HL_zi * y + HL_zl * Wmin + HL_zu * Wmax - w', v_str=0.05)
def __init__(self, system, config): PMUData.__init__(self) Model.__init__(self, system, config) self.flags.tds = True self.group = 'PhasorMeasurement' self.a = ExtAlgeb( model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', info='Bus voltage phase angle', ) self.v = ExtAlgeb( model='Bus', src='v', indexer=self.bus, tex_name=r'V', info='Bus voltage magnitude', ) self.am = State( tex_name=r'\theta_m', info='phase angle measurement', unit='rad.', e_str='a - am', t_const=self.Ta, v_str='a', ) self.vm = State( tex_name='V_m', info='voltage magnitude measurement', unit='p.u.(kV)', e_str='v - vm', t_const=self.Tv, v_str='v', )
def __init__(self, system, config): DC2Term.__init__(self, system, config) self.flags.pflow = True self.group = 'DCLink' self.L = NumParam( unit='p.u.', info='DC line inductance', non_zero=True, default=0.001, r=True, ) self.IL = State( tex_name='I_L', info='Inductance current', unit='p.u.', v_str='0', e_str='-u * (v1 - v2)', t_const=self.L, ) self.v1.e_str = '-IL' self.v2.e_str = '+IL'
def __init__(self, system, config): DC2Term.__init__(self, system, config) self.flags.pflow = True self.group = 'DCLink' self.R = NumParam( unit='p.u.', tex_name='R', info='DC line resistance', non_zero=True, default=0.01, r=True, ) self.L = NumParam( unit='p.u.', tex_name='L', info='DC line inductance', non_zero=True, default=0.001, r=True, ) self.IL = State( tex_name='I_L', info='Inductance current', unit='p.u.', e_str='u * (v1 - v2 - R * IL)', v_str='(v1 - v2) / R', t_const=self.L, ) self.Idc = Algeb( tex_name='I_{dc}', info='Current from node 2 to 1', unit='p.u.', e_str='-u * IL - Idc', v_str='-u * (v1 - v2) / R', ) self.v1.e_str = '-Idc' self.v2.e_str = '+Idc'
def __init__(self, system, config): Model.__init__(self, system, config) self.flags.tds = True self.group = 'RenGen' self.a = ExtAlgeb( model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', info='Bus voltage angle', e_str='-u * Pe', ) self.v = ExtAlgeb( model='Bus', src='v', indexer=self.bus, tex_name='V', info='Bus voltage magnitude', e_str='-u * Qe', ) self.p0s = ExtService( model='StaticGen', src='p', indexer=self.gen, tex_name=r'P_{0s}', info='total P of the static gen', ) self.q0s = ExtService( model='StaticGen', src='q', indexer=self.gen, tex_name=r'Q_{0s}', info='total Q of the static gen', ) self.Pref = ConstService( v_str='gammap * p0s', tex_name='P_{ref}', info='Initial P for the REGCV1 device', ) self.Qref = ConstService( v_str='gammaq * q0s', tex_name='Q_{ref}', info='Initial Q for the REGCV1 device', ) self.vref = ExtService( model='StaticGen', src='v', indexer=self.gen, tex_name=r'V_{ref}', info='initial v of the static gen', ) # --- INITIALIZATION --- self.ixs = ConstService( v_str='1/xs', tex_name=r'1/xs', ) self.Id0 = ConstService( tex_name=r'I_{d0}', v_str='u * Pref / v', ) self.Iq0 = ConstService( tex_name=r'I_{q0}', v_str='- u * Qref / v', ) self.vd0 = ConstService( tex_name=r'v_{d0}', v_str='u * v', ) self.vq0 = ConstService( tex_name=r'v_{q0}', v_str='0', ) self.Pref2 = Algeb( tex_name=r'P_{ref2}', info='active power reference after adjusting by frequency', e_str='u * Pref - dw * kw - Pref2', v_str='u * Pref') self.vref2 = Algeb( tex_name=r'v_{ref2}', info='voltage reference after adjusted by reactive power', e_str='(u * Qref - Qe) * kv + vref - vref2', v_str='u * vref') self.dw = State(info='delta virtual rotor speed', unit='pu (Hz)', v_str='0', tex_name=r'\Delta\omega', e_str='Pref2 - Pe - D * dw', t_const=self.M) self.omega = Algeb(info='virtual rotor speed', unit='pu (Hz)', v_str='u', tex_name=r'\omega', e_str='1 + dw - omega') self.delta = State(info='virtual delta', unit='rad', v_str='a', tex_name=r'\delta', e_str='2 * pi * fn * dw') self.vd = Algeb(tex_name='V_d', info='d-axis voltage', e_str='u * v * cos(delta - a) - vd', v_str='vd0') self.vq = Algeb(tex_name='V_q', info='q-axis voltage', e_str='- u * v * sin(delta - a) - vq', v_str='vq0') self.Pe = Algeb(tex_name='P_e', info='active power injection from VSC', e_str='vd * Id + vq * Iq - Pe', v_str='Pref') self.Qe = Algeb(tex_name='Q_e', info='reactive power injection from VSC', e_str='- vd * Iq + vq * Id - Qe', v_str='Qref') self.Id = Algeb( tex_name='I_d', info='d-axis current', v_str='Id0', diag_eps=True, ) self.Iq = Algeb( tex_name='I_q', info='q-axis current', v_str='Iq0', diag_eps=True, )
def __init__(self): # parameter checking for `xl` self._xlc = InitChecker(u=self.xl, info='(xl <= xd2)', upper=self.xd2) self.gd1 = ConstService(v_str='(xd2 - xl) / (xd1 - xl)', tex_name=r"\gamma_{d1}") self.gq1 = ConstService(v_str='(xq2 - xl) / (xq1 - xl)', tex_name=r"\gamma_{q1}") self.gd2 = ConstService(v_str='(xd1 - xd2) / (xd1 - xl) ** 2', tex_name=r"\gamma_{d2}") self.gq2 = ConstService(v_str='(xq1 - xq2) / (xq1 - xl) ** 2', tex_name=r"\gamma_{q2}") self.gqd = ConstService(v_str='(xq - xl) / (xd - xl)', tex_name=r"\gamma_{qd}") # correct S12 to 1.0 if is zero self._fS12 = FlagValue(self.S12, value=0) self._S12 = ConstService(v_str='S12 + (1-_fS12)', info='Corrected S12', tex_name='S_{1.2}') # Saturation services # Note: # To disable saturation, set S10 = 0, S12 = 1 so that SAT_B = 0. self.SAT = ExcQuadSat(1.0, self.S10, 1.2, self.S12, tex_name='S_{AT}') # Initialization reference: OpenIPSL at # https://github.com/OpenIPSL/OpenIPSL/blob/master/OpenIPSL/Electrical/Machines/PSSE/GENROU.mo # internal voltage and rotor angle calculation self._V = ConstService(v_str='v * exp(1j * a)', tex_name='V_c', info='complex bus voltage', vtype=complex) self._S = ConstService(v_str='p0 - 1j * q0', tex_name='S', info='complex terminal power', vtype=complex) self._Zs = ConstService(v_str='ra + 1j * xd2', tex_name='Z_s', info='equivalent impedance', vtype=complex) self._It = ConstService(v_str='_S / conj(_V)', tex_name='I_t', info='complex terminal current', vtype=complex) self._Is = ConstService(tex_name='I_s', v_str='_It + _V / _Zs', info='equivalent current source', vtype=complex) self.psi20 = ConstService( tex_name=r"\psi''_0", v_str='_Is * _Zs', info='sub-transient flux linkage in stator reference', vtype=complex) self.psi20_arg = ConstService(tex_name=r"\theta_{\psi''0}", v_str='arg(psi20)') self.psi20_abs = ConstService(tex_name=r"|\psi''_0|", v_str='abs(psi20)') self._It_arg = ConstService(tex_name=r"\theta_{It0}", v_str='arg(_It)') self._psi20_It_arg = ConstService(tex_name=r"\theta_{\psi a It}", v_str='psi20_arg - _It_arg') self.Se0 = ConstService( tex_name=r"S_{e0}", v_str= 'Indicator(psi20_abs>=SAT_A) * (psi20_abs - SAT_A) ** 2 * SAT_B / psi20_abs' ) self._a = ConstService(tex_name=r"a'", v_str='psi20_abs * (1 + Se0*gqd)') self._b = ConstService(tex_name=r"b'", v_str='abs(_It) * (xq2 - xq)') # xd2 == xq2 self.delta0 = ConstService( tex_name=r'\delta_0', v_str= 'atan(_b * cos(_psi20_It_arg) / (_b * sin(_psi20_It_arg) - _a)) + ' 'psi20_arg') self._Tdq = ConstService(tex_name=r"T_{dq}", v_str='cos(delta0) - 1j * sin(delta0)', vtype=complex) self.psi20_dq = ConstService(tex_name=r"\psi''_{0,dq}", v_str='psi20 * _Tdq', vtype=complex) self.It_dq = ConstService(tex_name=r"I_{t,dq}", v_str='conj(_It * _Tdq)', vtype=complex) self.psi2d0 = ConstService(tex_name=r"\psi_{ad0}", v_str='re(psi20_dq)') self.psi2q0 = ConstService(tex_name=r"\psi_{aq0}", v_str='-im(psi20_dq)') self.Id0 = ConstService(v_str='im(It_dq)', tex_name=r'I_{d0}') self.Iq0 = ConstService(v_str='re(It_dq)', tex_name=r'I_{q0}') self.vd0 = ConstService(v_str='psi2q0 + xq2*Iq0 - ra * Id0', tex_name=r'V_{d0}') self.vq0 = ConstService(v_str='psi2d0 - xd2*Id0 - ra*Iq0', tex_name=r'V_{q0}') self.tm0 = ConstService( tex_name=r'\tau_{m0}', v_str='u * ((vq0 + ra * Iq0) * Iq0 + (vd0 + ra * Id0) * Id0)') # `vf0` is also equal to `vq + xd*Id +ra*Iq + Se*psi2d` from phasor diagram self.vf0 = ConstService(tex_name=r'v_{f0}', v_str='(Se0 + 1)*psi2d0 + (xd - xd2) * Id0') self.psid0 = ConstService(tex_name=r"\psi_{d0}", v_str='u * (ra * Iq0) + vq0') self.psiq0 = ConstService(tex_name=r"\psi_{q0}", v_str='-u * (ra * Id0) - vd0') # initialization of internal voltage and delta self.e1q0 = ConstService(tex_name="e'_{q0}", v_str='Id0*(-xd + xd1) - Se0*psi2d0 + vf0') self.e1d0 = ConstService(tex_name="e'_{d0}", v_str='Iq0*(xq - xq1) - Se0*gqd*psi2q0') self.e2d0 = ConstService(tex_name="e''_{d0}", v_str='Id0*(xl - xd) - Se0*psi2d0 + vf0') self.e2q0 = ConstService(tex_name="e''_{q0}", v_str='-Iq0*(xl - xq) - Se0*gqd*psi2q0') # begin variables and equations self.psi2q = Algeb( tex_name=r"\psi_{aq}", info='q-axis air gap flux', v_str='u * psi2q0', e_str='gq1*e1d + (1-gq1)*e2q - psi2q', ) self.psi2d = Algeb(tex_name=r"\psi_{ad}", info='d-axis air gap flux', v_str='u * psi2d0', e_str='gd1*e1q + gd2*(xd1-xl)*e2d - psi2d') self.psi2 = Algeb( tex_name=r"\psi_a", info='air gap flux magnitude', v_str='u * abs(psi20_dq)', e_str='psi2d **2 + psi2q ** 2 - psi2 ** 2', diag_eps=True, ) # `LT` is a reserved keyword for SymPy self.SL = LessThan(u=self.psi2, bound=self.SAT_A, equal=False, enable=True, cache=False) self.Se = Algeb( tex_name=r"S_e(|\psi_{a}|)", info='saturation output', v_str='u * Se0', e_str='SL_z0 * (psi2 - SAT_A) ** 2 * SAT_B - psi2 * Se', diag_eps=True, ) # separated `XadIfd` from `e1q` using \dot(e1q) = (vf - XadIfd) / Td10 self.XadIfd.e_str = 'u * (e1q + (xd-xd1) * (gd1*Id - gd2*e2d + gd2*e1q) + Se*psi2d) - XadIfd' # `XadI1q` can also be given in `(xq-xq1)*gq2*(e1d-e2q+(xq1-xl)*Iq) + e1d - Iq*(xq-xq1) + Se*psi2q*gqd` self.XaqI1q =\ Algeb(tex_name='X_{aq}I_{1q}', info='q-axis reaction', unit='p.u (kV)', v_str='0', e_str='e1d + (xq-xq1) * (gq2*e1d - gq2*e2q - gq1*Iq) + Se*psi2q*gqd - XaqI1q' ) self.e1q = State( info='q-axis transient voltage', tex_name=r"e'_q", v_str='u * e1q0', e_str='(-XadIfd + vf)', t_const=self.Td10, ) self.e1d = State( info='d-axis transient voltage', tex_name=r"e'_d", v_str='u * e1d0', e_str='-XaqI1q', t_const=self.Tq10, ) self.e2d = State( info='d-axis sub-transient voltage', tex_name=r"e''_d", v_str='u * e2d0', e_str='(-e2d + e1q - (xd1 - xl) * Id)', t_const=self.Td20, ) self.e2q = State( info='q-axis sub-transient voltage', tex_name=r"e''_q", v_str='u * e2q0', e_str='(-e2q + e1d + (xq1 - xl) * Iq)', t_const=self.Tq20, ) self.Iq.e_str += '+ xq2*Iq + psi2q' self.Id.e_str += '+ xd2*Id - psi2d'
def __init__(self, system, config): Model.__init__(self, system, config) self.flags.pflow = True self.flags.tds = True self.flags.tds_init = False self.group = 'Motor' # services self.wb = ConstService(v_str='2 * pi * fn', tex_name=r'\omega_b', ) self.x0 = ConstService(v_str='xs + xm', tex_name='x_0', ) self.x1 = ConstService(v_str='xs + xr1 * xm / (xr1 + xm)', tex_name="x'", ) self.T10 = ConstService(v_str='(xr1 + xm) / (wb * rr1)', tex_name="T'_0", ) self.M = ConstService(v_str='2 * Hm', tex_name='M', ) self.aa = ConstService(v_str='c1 + c2 + c3', tex_name=r'\alpha', ) self.bb = ConstService(v_str='-c2 - 2 * c3', tex_name=r'\beta', ) # network algebraic variables self.a = ExtAlgeb(model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', info='Bus voltage phase angle', e_str='+p', ename='P', tex_ename='P', ) self.v = ExtAlgeb(model='Bus', src='v', indexer=self.bus, tex_name=r'V', info='Bus voltage magnitude', e_str='+q', ename='Q', tex_ename='Q', ) self.vd = Algeb(info='d-axis voltage', e_str='-u * v * sin(a) - vd', tex_name=r'V_d', ) self.vq = Algeb(info='q-axis voltage', e_str='u * v * cos(a) - vq', tex_name=r'V_q', ) self.slip = State(tex_name=r"\sigma", e_str='u * (tm - te)', t_const=self.M, diag_eps=True, v_str='1.0 * u', ) self.p = Algeb(tex_name='P', e_str='u * (vd * Id + vq * Iq) - p', v_str='u * (vd * Id + vq * Iq)', ) self.q = Algeb(tex_name='Q', e_str='u * (vq * Id - vd * Iq) - q', v_str='u * (vq * Id - vd * Iq)', ) self.e1d = State(info='real part of 1st cage voltage', tex_name="e'_d", v_str='0.05 * u', e_str='u * (wb*slip*e1q - (e1d + (x0 - x1) * Iq)/T10)', diag_eps=True, ) self.e1q = State(info='imaginary part of 1st cage voltage', tex_name="e'_q", v_str='0.9 * u', e_str='u * (-wb*slip*e1d - (e1q - (x0 - x1) * Id)/T10)', diag_eps=True, ) self.Id = Algeb(tex_name='I_d', diag_eps=True, ) self.Iq = Algeb(tex_name='I_q', diag_eps=True, ) self.te = Algeb(tex_name=r'\tau_e', ) self.tm = Algeb(tex_name=r'\tau_m', ) self.tm.v_str = 'u * (aa + bb * slip + c2 * slip * slip)' self.tm.e_str = f'{self.tm.v_str} - tm'
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'SynGen' self.group_param_exception = ['Sn', 'M', 'D'] self.group_var_exception = ['vd', 'vq', 'Id', 'Iq', 'tm', 'te', 'vf', 'XadIfd'] self.flags.tds = True self.subidx = ExtParam(model='StaticGen', src='subidx', indexer=self.gen, export=False, info='Generator idx in plant; only used by PSS/E data' ) self.zs = ConstService('ra + 1j * xs', vtype=complex, info='impedance', ) self.zs2n = ConstService('ra * ra - xs * xs', info='ra^2 - xs^2', ) # get power flow solutions self.p = ExtService(model='StaticGen', src='p', indexer=self.gen, ) self.q = ExtService(model='StaticGen', src='q', indexer=self.gen, ) self.Ec = ConstService('v * exp(1j * a) +' 'conj((p + 1j * q) / (v * exp(1j * a))) * (ra + 1j * xs)', vtype=complex, tex_name='E_c', ) self.E0 = ConstService('abs(Ec)', tex_name='E_0') self.delta0 = ConstService('arg(Ec)', tex_name=r'\delta_0') # Note: `Vts` and `fts` are assigned by TimeSeries before initializing this model. self.Vts = ConstService() self.fts = ConstService() self.ifscale = ConstService('1/fscale', tex_name='1/f_{scale}') self.iVscale = ConstService('1/Vscale', tex_name='1/V_{scale}') self.foffs = ConstService('fts * ifscale - 1', tex_name='f_{offs}') self.Voffs = ConstService('Vts * iVscale - E0', tex_name='V_{offs}') self.Vflt = State(info='filtered voltage', t_const=self.Tv, v_str='(iVscale * Vts - Voffs)', e_str='(iVscale * Vts - Voffs) - Vflt', unit='pu', tex_name='V_{flt}', ) self.omega = State(info='filtered frequency', t_const=self.Tf, v_str='fts * ifscale - foffs', e_str='(ifscale * fts - foffs) - omega', unit='pu', tex_name=r'\omega', ) self.delta = State(info='rotor angle', unit='rad', v_str='delta0', tex_name=r'\delta', e_str='u * (2 * pi * fn) * (omega - 1)', ) # --- Power injections are obtained by sympy --- # >>> from sympy import symbols, sin, cos, conjugate # >>> Vflt, delta, v, a, ra, xs = symbols('Vflt delta v a ra xs', real=True) # >>> S = -v * (cos(a) + 1j*sin(a)) * \ # conjugate((Vflt * (cos(delta)+1j*sin(delta)) - v*(cos(a)+1j*sin(a))) / (ra+1j*xs)) # >>> S.simplify().as_real_imag() self.a = ExtAlgeb(model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', info='Bus voltage phase angle', e_str='Vflt*v*xs*sin(a - delta)/(ra*ra + xs*xs) + ' 'ra*v*(-Vflt*cos(a - delta) + v)/(ra*ra + xs*xs)', ename='P', tex_ename='P', ) self.v = ExtAlgeb(model='Bus', src='v', indexer=self.bus, tex_name=r'V', info='Bus voltage magnitude', ename='Q', e_str='-Vflt*ra*v*sin(a - delta)/(ra*ra + xs*xs) + ' 'v*xs*(-Vflt*cos(a - delta) + v)/(ra*ra + xs*xs)', tex_ename='Q', )
def __init__(self, system, config): TGBase.__init__(self, system, config) self.VELMn = ConstService( v_str='-VELM', tex_name='-VELM', ) self.tr = ConstService( v_str='r * Tr', tex_name='r*Tr', ) self.gr = ConstService( v_str='1/r', tex_name='1/r', ) self.ratel = ConstService( v_str='- VELM - gr', tex_name='rate_l', ) self.rateu = ConstService( v_str='VELM - gr', tex_name='rate_u', ) self.q0 = ConstService( v_str='tm0 / At + qNL', tex_name='q_0', ) self.pref = Algeb( info='Reference power input', tex_name='P_{ref}', v_str='R * q0', e_str='R * q0 - pref', ) self.wd = Algeb( info='Generator speed deviation', unit='p.u.', tex_name=r'\omega_{dev}', v_str='0', e_str='ue * (omega - wref) - wd', ) self.pd = Algeb( info='Pref plus speed deviation times gain', unit='p.u.', tex_name="P_d", v_str='0', e_str='ue * (- wd + pref + paux - R * dg) - pd', ) self.LG = Lag( u=self.pd, K=1, T=self.Tf, info='filter after speed deviation (e)', ) self.gtpos = State(info='State in gate position (c)', unit='rad', v_str='q0', tex_name=r'\delta', e_str='LG_y') self.dgl = VarService( tex_name='dg_{lower}', info='dg lower limit', v_str='- VELM - gr * LG_y', ) self.dgu = VarService( tex_name='dg_{upper}', info='dg upper limit', v_str='VELM - gr * LG_y', ) self.dg_lim = AntiWindupRate( u=self.gtpos, lower=self.GMIN, upper=self.GMAX, rate_lower=self.dgl, rate_upper=self.dgu, tex_name='lim_{dg}', info='gate velocity and position limiter', ) self.dg = Algeb( info='desired gate (c)', unit='p.u.', tex_name="dg", v_str='q0', e_str='gtpos + gr * LG_y - dg', ) self.LAG = Lag( u=self.dg, K=1, T=self.Tg, info='gate opening (g)', ) self.h = Algeb( info='turbine head', unit='p.u.', tex_name="h", e_str='q_y**2 / LAG_y**2 - h', v_str='1', ) self.q = Integrator(u="1 - q_y**2 / LAG_y**2", T=self.Tw, K=1, y0='q0', check_init=False, info="turbine flow (q)") self.pout.e_str = 'ue * (At * h * (q_y - qNL) - Dt * wd * LAG_y) - pout'
def __init__(self, system, config): super().__init__(system, config) self.group = 'SynGen' self.flags.update({ 'tds': True, 'nr_iter': False, }) self.config.add( vf_lower=1.0, vf_upper=5.0, ) self.config.add_extra( "_help", vf_lower="lower limit for vf warning", vf_upper="upper limit for vf warning", ) # state variables self.delta = State(info='rotor angle', unit='rad', v_str='delta0', tex_name=r'\delta', e_str='u * (2 * pi * fn) * (omega - 1)') self.omega = State( info='rotor speed', unit='pu (Hz)', v_str='u', tex_name=r'\omega', e_str='u * (tm - te - D * (omega - 1))', t_const=self.M, ) # network algebraic variables self.a = ExtAlgeb( model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', info='Bus voltage phase angle', e_str='-u * (vd * Id + vq * Iq)', ename='P', tex_ename='P', is_input=True, ) self.v = ExtAlgeb( model='Bus', src='v', indexer=self.bus, tex_name=r'V', info='Bus voltage magnitude', e_str='-u * (vq * Id - vd * Iq)', ename='Q', tex_ename='Q', is_input=True, ) # algebraic variables # Need to be provided by specific generator models self.Id = Algeb(info='d-axis current', v_str='u * Id0', tex_name=r'I_d', e_str='') # to be completed by subclasses self.Iq = Algeb(info='q-axis current', v_str='u * Iq0', tex_name=r'I_q', e_str='') # to be completed self.vd = Algeb( info='d-axis voltage', v_str='u * vd0', e_str='u * v * sin(delta - a) - vd', tex_name=r'V_d', ) self.vq = Algeb( info='q-axis voltage', v_str='u * vq0', e_str='u * v * cos(delta - a) - vq', tex_name=r'V_q', ) self.tm = Algeb(info='mechanical torque', tex_name=r'\tau_m', v_str='tm0', e_str='tm0 - tm') self.te = Algeb( info='electric torque', tex_name=r'\tau_e', v_str='u * tm0', e_str='u * (psid * Iq - psiq * Id) - te', ) self.vf = Algeb(info='excitation voltage', unit='pu', v_str='u * vf0', e_str='u * vf0 - vf', tex_name=r'v_f') self._vfc = InitChecker( u=self.vf, info='(vf range)', lower=self.config.vf_lower, upper=self.config.vf_upper, ) self.XadIfd = Algeb(tex_name='X_{ad}I_{fd}', info='d-axis armature excitation current', unit='p.u (kV)', v_str='u * vf0', e_str='u * vf0 - XadIfd' ) # e_str to be provided. Not available in GENCLS self.subidx = ExtParam( model='StaticGen', src='subidx', indexer=self.gen, export=False, info='Generator idx in plant; only used by PSS/E data', vtype=str, ) # declaring `Vn_bus` as ExtParam will fail for PSS/E parser self.Vn_bus = ExtService( model='Bus', src='Vn', indexer=self.bus, ) self._vnc = InitChecker( u=self.Vn, info='Vn and Bus Vn', equal=self.Vn_bus, ) # ----------service consts for initialization---------- self.p0s = ExtService( model='StaticGen', src='p', indexer=self.gen, tex_name='P_{0s}', info='initial P of the static gen', ) self.q0s = ExtService( model='StaticGen', src='q', indexer=self.gen, tex_name='Q_{0s}', info='initial Q of the static gen', ) self.p0 = ConstService( v_str='p0s * gammap', tex_name='P_0', info='initial P of this gen', ) self.q0 = ConstService( v_str='q0s * gammaq', tex_name='Q_0', info='initial Q of this gen', ) self.Pe = Algeb(tex_name='P_e', info='active power injection', e_str='u * (vd * Id + vq * Iq) - Pe', v_str='u * (vd0 * Id0 + vq0 * Iq0)') self.Qe = Algeb(tex_name='Q_e', info='reactive power injection', e_str='u * (vq * Id - vd * Iq) - Qe', v_str='u * (vq0 * Id0 - vd0 * Iq0)')