def __init__(self, E1, SE1, E2, SE2, name=None, tex_name=None, info=None): Block.__init__(self, name=name, tex_name=tex_name, info=info) self._E1 = dummify(E1) self._E2 = dummify(E2) self._SE1 = SE1 self._SE2 = SE2 self.zSE2 = FlagNotNone(self._SE2, to_flag=0., info='Flag non-zeros in SE2', tex_name='z^{SE2}') # data correction for E1, E2, SE1 (TODO) self.E1 = ConstService( tex_name='E^{1c}', info='Corrected E1 data', ) self.E2 = ConstService( tex_name='E^{2c}', info='Corrected E2 data', ) self.SE1 = ConstService( tex_name='SE^{1c}', info='Corrected SE1 data', ) self.SE2 = ConstService( tex_name='SE^{2c}', info='Corrected SE2 data', ) self.a = ConstService( info='Intermediate Sat coeff', tex_name='a', ) self.A = ConstService( info='Saturation start', tex_name='A^q', ) self.B = ConstService( info='Saturation gain', tex_name='B^q', ) self.vars = { 'E1': self.E1, 'E2': self.E2, 'SE1': self.SE1, 'SE2': self.SE2, 'zSE2': self.zSE2, 'a': self.a, 'A': self.A, 'B': self.B, }
def __init__(self, E1, SE1, E2, SE2, name=None, tex_name=None, info=None): Block.__init__(self, name=name, tex_name=tex_name, info=info) self._E1 = E1 self._E2 = E2 self._SE1 = SE1 self._SE2 = SE2 self.zE1 = FlagNotNone( self._E1, to_flag=0., info='Flag non-zeros in E1', tex_name='z^{E1}', ) self.zE2 = FlagNotNone( self._E2, to_flag=0., info='Flag non-zeros in E2', tex_name='z^{E2}', ) self.zSE1 = FlagNotNone( self._SE1, to_flag=0., info='Flag non-zeros in SE1', tex_name='z^{SE1}', ) self.zSE2 = FlagNotNone(self._SE2, to_flag=0., info='Flag non-zeros in SE2', tex_name='z^{SE2}') # disallow E1 = E2 != 0 since the curve fitting will fail self.E12c = InitChecker( self._E1, not_equal=self._E2, info='E1 and E2 after correction', error_out=True, ) # data correction for E1, E2, SE1 self.E1 = ConstService( tex_name='E^{1c}', info='Corrected E1 data', ) self.E2 = ConstService( tex_name='E^{2c}', info='Corrected E2 data', ) self.SE1 = ConstService( tex_name='SE^{1c}', info='Corrected SE1 data', ) self.SE2 = ConstService( tex_name='SE^{2c}', info='Corrected SE2 data', ) self.A = ConstService( info='Saturation gain', tex_name='A^e', ) self.B = ConstService( info='Exponential coef. in saturation', tex_name='B^e', ) self.vars = { 'E1': self.E1, 'E2': self.E2, 'SE1': self.SE1, 'SE2': self.SE2, 'zE1': self.zE1, 'zE2': self.zE2, 'zSE1': self.zSE1, 'zSE2': self.zSE2, 'A': self.A, 'B': self.B, }
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 = FlagNotNone(self.S12, to_flag=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') self._S = ConstService(v_str='p0 - 1j * q0', tex_name='S', info='complex terminal power') self._Zs = ConstService(v_str='ra + 1j * xd2', tex_name='Z_s', info='equivalent impedance') self._It = ConstService(v_str='_S / conj(_V)', tex_name='I_t', info='complex terminal current') self._Is = ConstService(tex_name='I_s', v_str='_It + _V / _Zs', info='equivalent current source') self.psi20 = ConstService(tex_name=r"\psi''_0", v_str='_Is * _Zs', info='sub-transient flux linkage in stator reference') 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='(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)') self.psi20_dq = ConstService(tex_name=r"\psi''_{0,dq}", v_str='psi20 * _Tdq') self.It_dq = ConstService(tex_name=r"I_{t,dq}", v_str='conj(_It * _Tdq)') 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='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='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='abs(psi20_dq)', e_str='sqrt(psi2d **2 + psi2q ** 2) - psi2') # `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='Se0', e_str='SL_z0 * (psi2 - SAT_A) ** 2 * SAT_B / psi2 - Se') # separated `XadIfd` from `e1q` using \dot(e1q) = (vf - XadIfd) / Td10 self.XadIfd.e_str = '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='e1q0', e_str='(-XadIfd + vf) / Td10') self.e1d = State(info='d-axis transient voltage', tex_name=r"e'_d", v_str='e1d0', e_str='-XaqI1q / Tq10') self.e2d = State(info='d-axis sub-transient voltage', tex_name=r"e''_d", v_str='e2d0', e_str='(-e2d + e1q - (xd1 - xl) * Id) / Td20') self.e2q = State(info='q-axis sub-transient voltage', tex_name=r"e''_q", v_str='e2q0', e_str='(-e2q + e1d + (xq1 - xl) * Iq) / Tq20') self.Iq.e_str += '+ xq2*Iq + psi2q' self.Id.e_str += '+ xd2*Id - psi2d'
def __init__(self, system, config): ExcBase.__init__(self, system, config) # Set VRMAX to 999 when VRMAX = 0 self._zVRM = FlagNotNone( self.VRMAX, to_flag=0, tex_name='z_{VRMAX}', ) self.VRMAXc = ConstService( v_str='VRMAX + 999*(1-_zVRM)', info='Set VRMAX=999 when zero', ) self.LG = Lag( u=self.v, T=self.TR, K=1, info='Transducer delay', ) self.SAT = ExcQuadSat( self.E1, self.SE1, self.E2, self.SE2, info='Field voltage saturation', ) self.Se0 = ConstService( tex_name='S_{e0}', v_str='(vf0>SAT_A) * SAT_B*(SAT_A-vf0) ** 2 / vf0', ) self.vfe0 = ConstService( v_str='vf0 * (KE + Se0)', tex_name='V_{FE0}', ) self.vref0 = ConstService( info='Initial reference voltage input', tex_name='V_{ref0}', v_str='v + vfe0 / KA', ) self.vref = Algeb(info='Reference voltage input', tex_name='V_{ref}', unit='p.u.', v_str='vref0', e_str='vref0 - vref') self.vi = Algeb( info='Total input voltages', tex_name='V_i', unit='p.u.', v_str='vref0 - v', e_str='(vref - v - WF_y) - vi', ) self.LL = LeadLag( u=self.vi, T1=self.TC, T2=self.TB, info='Lead-lag compensator', zero_out=True, ) self.UEL = Algeb(info='Interface var for under exc. limiter', tex_name='U_{EL}', v_str='0', e_str='0 - UEL') self.HG = HVGate( u1=self.UEL, u2=self.LL_y, info='HVGate for under excitation', ) self.VRU = VarService( v_str='VRMAXc * v', tex_name='V_T V_{RMAX}', ) self.VRL = VarService( v_str='VRMIN * v', tex_name='V_T V_{RMIN}', ) # TODO: WARNING: HVGate is temporarily skipped self.LA = LagAntiWindup( u=self.LL_y, T=self.TA, K=self.KA, upper=self.VRU, lower=self.VRL, info='Anti-windup lag', ) # LA_y == VR # `LessThan` may be causing memory issue in (SL_z0 * vout) - uncertain yet self.SL = LessThan(u=self.vout, bound=self.SAT_A, equal=False, enable=True, cache=False) self.Se = Algeb( tex_name=r"S_e(|V_{out}|)", info='saturation output', v_str='Se0', e_str='SL_z0 * (INT_y - SAT_A) ** 2 * SAT_B / INT_y - Se', ) self.VFE = Algeb(info='Combined saturation feedback', tex_name='V_{FE}', unit='p.u.', v_str='vfe0', e_str='INT_y * (KE + Se) - VFE') self.INT = Integrator( u='LA_y - VFE', T=self.TE, K=1, y0=self.vf0, info='Integrator', ) self.WF = Washout(u=self.INT_y, T=self.TF1, K=self.KF, info='Feedback to input') self.vout.e_str = 'INT_y - vout'
def __init__(self, system, config): TGBase.__init__(self, system, config, add_sn=False) # check if K1-K8 sums up to 1 self._sumK18 = ConstService(v_str='K1+K2+K3+K4+K5+K6+K7+K8', info='summation of K1-K8', tex_name=r"\sum_{i=1}^8 K_i" ) self._K18c1 = InitChecker(u=self._sumK18, info='summation of K1-K8 and 1.0', equal=1, ) # check if `tm0 * (K2 + k4 + K6 + K8) = tm02 *(K1 + K3 + K5 + K7) self._tm0K2 = PostInitService(info='mul of tm0 and (K2+K4+K6+K8)', v_str='zsyn2*tm0*(K2+K4+K6+K8)', ) self._tm02K1 = PostInitService(info='mul of tm02 and (K1+K3+K5+K6)', v_str='tm02*(K1+K3+K5+K7)', ) self._Pc = InitChecker(u=self._tm0K2, info='proportionality of tm0 and tm02', equal=self._tm02K1, ) self.Sg2 = ExtParam(src='Sn', model='SynGen', indexer=self.syn2, allow_none=True, default=0.0, tex_name='S_{n2}', info='Rated power of Syn2', unit='MVA', export=False, ) self.Sg12 = ParamCalc(self.Sg, self.Sg2, func=np.add, tex_name="S_{g12}", info='Sum of generator power ratings', ) self.Sn = NumSelect(self.Tn, fallback=self.Sg12, tex_name='S_n', info='Turbine or Gen rating', ) self.zsyn2 = FlagNotNone(self.syn2, tex_name='z_{syn2}', info='Exist flags for syn2', ) self.tm02 = ExtService(src='tm', model='SynGen', indexer=self.syn2, tex_name=r'\tau_{m02}', info='Initial mechanical input of syn2', allow_none=True, default=0.0, ) self.tm012 = ConstService(info='total turbine power', v_str='tm0 + tm02', ) self.tm2 = ExtAlgeb(src='tm', model='SynGen', indexer=self.syn2, allow_none=True, tex_name=r'\tau_{m2}', e_str='zsyn2 * u * (PLP - tm02)', info='Mechanical power to syn2', ) self.wd = Algeb(info='Generator under speed', unit='p.u.', tex_name=r'\omega_{dev}', v_str='0', e_str='(wref - omega) - wd', ) self.LL = LeadLag(u=self.wd, T1=self.T2, T2=self.T1, K=self.K, info='Signal conditioning for wd', ) # `P0` == `tm0` self.vs = Algeb(info='Valve speed', tex_name='V_s', v_str='0', e_str='(LL_y + tm012 + paux - IAW_y) / T3 - vs', ) self.HL = HardLimiter(u=self.vs, lower=self.UC, upper=self.UO, info='Limiter on valve acceleration', ) self.vsl = Algeb(info='Valve move speed after limiter', tex_name='V_{sl}', v_str='vs * HL_zi + UC * HL_zl + UO * HL_zu', e_str='vs * HL_zi + UC * HL_zl + UO * HL_zu - vsl', ) self.IAW = IntegratorAntiWindup(u=self.vsl, T=1, K=1, y0=self.tm012, lower=self.PMIN, upper=self.PMAX, info='Valve position integrator', ) self.L4 = Lag(u=self.IAW_y, T=self.T4, K=1, info='first process',) self.L5 = Lag(u=self.L4_y, T=self.T5, K=1, info='second (reheat) process', ) self.L6 = Lag(u=self.L5_y, T=self.T6, K=1, info='third process', ) self.L7 = Lag(u=self.L6_y, T=self.T7, K=1, info='fourth (second reheat) process', ) self.PHP = Algeb(info='HP output', tex_name='P_{HP}', v_str='K1*L4_y + K3*L5_y + K5*L6_y + K7*L7_y', e_str='K1*L4_y + K3*L5_y + K5*L6_y + K7*L7_y - PHP', ) self.PLP = Algeb(info='LP output', tex_name='P_{LP}', v_str='K2*L4_y + K4*L5_y + K6*L6_y + K8*L7_y', e_str='K2*L4_y + K4*L5_y + K6*L6_y + K8*L7_y - PLP', ) self.pout.e_str = 'PHP - pout'