def __init__(self, system, config): TGBase.__init__(self, system, config) self.F1 = Lag( u='ue * (omega - wref)', T=self.T1, K=self.K1, ) self.F2 = LeadLag( u=self.F1_y, T1=self.T2, T2=self.T3, K=1.0, ) self.HL = GainLimiter( u='ue * (paux + pref0 - F2_y)', K=1.0, R=1.0, lower=self.PMIN, upper=self.PMAX, ) self.F3 = Lag( u=self.HL_y, T=self.T4, K=1.0, ) self.F4 = Lag( u=self.F3_y, T=self.T5, K=self.K2, ) self.F5 = Lag( u=self.F4_y, T=self.T6, K=self.K3, ) self.pout.e_str = 'ue * ((1-K2)*F3_y + (1-K3)*F4_y + F5_y) - pout'
def __init__(self, system, config): TGBase.__init__(self, system, config) self.gain = ConstService( v_str='ue/R', tex_name='G', ) 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='ue * tm0', e_str='ue*(- wd + pref + paux) * gain - pd') self.LAG = LagAntiWindup( u=self.pd, K=1, T=self.T1, lower=self.VMIN, upper=self.VMAX, ) self.LL = LeadLag( u=self.LAG_y, T1=self.T2, T2=self.T3, ) self.pout.e_str = 'ue * (LL_y - Dt * wd) - pout'
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 = FlagValue( self.syn2, value=None, 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'
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'RenPlant' self.flags.tds = True self.config.add(OrderedDict(( ('kqs', 2), ('ksg', 2), ('freeze', 1), ))) self.config.add_extra( '_help', kqs='Tracking gain for reactive power PI controller', ksg='Tracking gain for active power PI controller', freeze='Voltage dip freeze flag; 1-enable, 0-disable', ) self.config.add_extra('_tex', kqs='K_{qs}', ksg='K_{sg}', freeze='f_{rz}') # --- from RenExciter --- self.reg = ExtParam( model='RenExciter', src='reg', indexer=self.ree, export=False, info='Retrieved RenGen idx', vtype=str, default=None, ) self.Pext = ExtAlgeb( model='RenExciter', src='Pref', indexer=self.ree, info='Pref from RenExciter renamed as Pext', tex_name='P_{ext}', ) self.Qext = ExtAlgeb( model='RenExciter', src='Qref', indexer=self.ree, info='Qref from RenExciter renamed as Qext', tex_name='Q_{ext}', ) # --- from RenGen --- self.bus = ExtParam( model='RenGen', src='bus', indexer=self.reg, export=False, info='Retrieved bus idx', vtype=str, default=None, ) self.buss = DataSelect(self.busr, self.bus, info='selected bus (bus or busr)') self.busfreq = DeviceFinder(self.busf, link=self.buss, idx_name='bus') # from Bus self.v = ExtAlgeb( model='Bus', src='v', indexer=self.buss, tex_name='V', info='Bus (or busr, if given) terminal voltage', ) self.a = ExtAlgeb( model='Bus', src='a', indexer=self.buss, tex_name=r'\theta', info='Bus (or busr, if given) phase angle', ) self.v0 = ExtService( model='Bus', src='v', indexer=self.buss, tex_name="V_0", info='Initial bus voltage', ) # from BusFreq self.f = ExtAlgeb(model='FreqMeasurement', src='f', indexer=self.busfreq, export=False, info='Bus frequency', unit='p.u.') # from Line self.bus1 = ExtParam( model='ACLine', src='bus1', indexer=self.line, export=False, info='Retrieved Line.bus1 idx', vtype=str, default=None, ) self.bus2 = ExtParam( model='ACLine', src='bus2', indexer=self.line, export=False, info='Retrieved Line.bus2 idx', vtype=str, default=None, ) self.r = ExtParam( model='ACLine', src='r', indexer=self.line, export=False, info='Retrieved Line.r', vtype=str, default=None, ) self.x = ExtParam( model='ACLine', src='x', indexer=self.line, export=False, info='Retrieved Line.x', vtype=str, default=None, ) self.v1 = ExtAlgeb( model='ACLine', src='v1', indexer=self.line, tex_name='V_1', info='Voltage at Line.bus1', ) self.v2 = ExtAlgeb( model='ACLine', src='v2', indexer=self.line, tex_name='V_2', info='Voltage at Line.bus2', ) self.a1 = ExtAlgeb( model='ACLine', src='a1', indexer=self.line, tex_name=r'\theta_1', info='Angle at Line.bus1', ) self.a2 = ExtAlgeb( model='ACLine', src='a2', indexer=self.line, tex_name=r'\theta_2', info='Angle at Line.bus2', ) # -- begin services --- self.Isign = CurrentSign(self.bus, self.bus1, self.bus2, tex_name='I_{sign}') Iline = '(Isign * (v1*exp(1j*a1) - v2*exp(1j*a2)) / (r + 1j*x))' self.Iline = VarService( v_str=Iline, vtype=complex, info='Complex current from bus1 to bus2', tex_name='I_{line}', ) self.Iline0 = ConstService( v_str='Iline', vtype=complex, info='Initial complex current from bus1 to bus2', tex_name='I_{line0}', ) Pline = 're(Isign * v1*exp(1j*a1) * conj((v1*exp(1j*a1) - v2*exp(1j*a2)) / (r + 1j*x)))' self.Pline = VarService( v_str=Pline, vtype=float, info='Complex power from bus1 to bus2', tex_name='P_{line}', ) self.Pline0 = ConstService( v_str='Pline', vtype=float, info='Initial vomplex power from bus1 to bus2', tex_name='P_{line0}', ) Qline = 'im(Isign * v1*exp(1j*a1) * conj((v1*exp(1j*a1) - v2*exp(1j*a2)) / (r + 1j*x)))' self.Qline = VarService( v_str=Qline, vtype=float, info='Complex power from bus1 to bus2', tex_name='Q_{line}', ) self.Qline0 = ConstService( v_str='Qline', vtype=float, info='Initial complex power from bus1 to bus2', tex_name='Q_{line0}', ) self.Rcs = NumSelect( self.Rc, self.r, info='Line R (Rc if provided, otherwise line.r)', tex_name='R_{cs}', ) self.Xcs = NumSelect( self.Xc, self.x, info='Line X (Xc if provided, otherwise line.x)', tex_name='X_{cs}', ) self.Vcomp = VarService( v_str='abs(v*exp(1j*a) - (Rcs + 1j * Xcs) * Iline)', info='Voltage after Rc/Xc compensation', tex_name='V_{comp}') self.SWVC = Switcher(u=self.VCFlag, options=(0, 1), tex_name='SW_{VC}', cache=True) self.SWRef = Switcher(u=self.RefFlag, options=(0, 1), tex_name='SW_{Ref}', cache=True) self.SWF = Switcher(u=self.Fflag, options=(0, 1), tex_name='SW_{F}', cache=True) self.SWPL = Switcher(u=self.PLflag, options=(0, 1), tex_name='SW_{PL}', cache=True) VCsel = '(SWVC_s1 * Vcomp + SWVC_s0 * (Qline * Kc + v))' self.Vref0 = ConstService( v_str='(SWVC_s1 * Vcomp + SWVC_s0 * (Qline0 * Kc + v))', tex_name='V_{ref0}', ) self.s0 = Lag( VCsel, T=self.Tfltr, K=1, tex_name='s_0', info='V filter', ) # s0_y is the filter output of voltage deviation self.s1 = Lag(self.Qline, T=self.Tfltr, K=1, tex_name='s_1') self.Vref = Algeb(v_str='Vref0', e_str='Vref0 - Vref', tex_name='Q_{ref}') self.Qlinef = Algeb(v_str='Qline0', e_str='Qline0 - Qlinef', tex_name='Q_{linef}') Refsel = '(SWRef_s0 * (Qlinef - s1_y) + SWRef_s1 * (Vref - s0_y))' self.Refsel = Algeb(v_str=Refsel, e_str=f'{Refsel} - Refsel', tex_name='R_{efsel}') self.dbd = DeadBand1( u=self.Refsel, lower=self.dbd1, upper=self.dbd2, center=0.0, tex_name='d^{bd}', ) # --- e Hardlimit and hold logic --- self.eHL = Limiter( u=self.dbd_y, lower=self.emin, upper=self.emax, tex_name='e_{HL}', info='Hardlimit on deadband output', ) self.zf = VarService( v_str='Indicator(v < Vfrz) * freeze', tex_name='z_f', info='PI Q input freeze signal', ) self.enf = Algeb( tex_name='e_{nf}', info='e Hardlimit output before freeze', v_str='dbd_y*eHL_zi + emax*eHL_zu + emin*eHL_zl', e_str='dbd_y*eHL_zi + emax*eHL_zu + emin*eHL_zl - enf', ) # --- hold of `enf` when v < vfrz self.eHld = VarHold( u=self.enf, hold=self.zf, tex_name='e_{hld}', info='e Hardlimit output after conditional hold', ) self.s2 = PITrackAW( u='eHld', kp=self.Kp, ki=self.Ki, ks=self.config.kqs, lower=self.Qmin, upper=self.Qmax, info='PI controller for eHL output', tex_name='s_2', ) self.s3 = LeadLag( u=self.s2_y, T1=self.Tft, T2=self.Tfv, K=1, tex_name='s_3', ) # s3_y == Qext # Active power part self.s4 = Lag( self.Pline, T=self.Tp, K=1, tex_name='s_4', info='Pline filter', ) self.Freq_ref = ConstService(v_str='1.0', tex_name='f_{ref}', info='Initial Freq_ref') self.ferr = Algeb( tex_name='f_{err}', info='Frequency deviation', unit='p.u. (Hz)', v_str='(Freq_ref - f)', e_str='(Freq_ref - f) - ferr', ) self.fdbd = DeadBand1( u=self.ferr, center=0.0, lower=self.fdbd1, upper=self.fdbd2, tex_name='f^{dbd}', info='frequency error deadband', ) self.fdlt0 = LessThan( self.fdbd_y, 0.0, tex_name='f_{dlt0}', info='frequency deadband output less than zero', ) fdroop = '(fdbd_y * Ddn * fdlt0_z1 + fdbd_y * Dup * fdlt0_z0)' self.Plant_pref = Algeb( tex_name='P_{ref}', info='Plant P ref', v_str='Pline0', e_str='Pline0 - Plant_pref', ) self.Plerr = Algeb( tex_name='P_{lerr}', info='Pline error', v_str='- s4_y + Plant_pref', e_str='- s4_y + Plant_pref - Plerr', ) self.Perr = Algeb( tex_name='P_{err}', info='Power error before fe limits', v_str=f'{fdroop} + Plerr * SWPL_s1', e_str=f'{fdroop} + Plerr * SWPL_s1 - Perr', ) self.feHL = Limiter( self.Perr, lower=self.femin, upper=self.femax, tex_name='f_{eHL}', info='Limiter for power (frequency) error', ) feout = '(Perr * feHL_zi + femin * feHL_zl + femax * feHL_zu)' self.s5 = PITrackAW( u=feout, kp=self.Kpg, ki=self.Kig, ks=self.config.ksg, lower=self.Pmin, upper=self.Pmax, tex_name='s_5', info='PI for fe limiter output', ) self.s6 = Lag( u=self.s5_y, T=self.Tg, K=1, tex_name='s_6', info='Output filter for Pext', ) Qext = '(s3_y)' Pext = '(SWF_s1 * s6_y)' self.Pext.e_str = Pext self.Qext.e_str = Qext
def __init__(self, system, config): TG2Data.__init__(self) TGBase.__init__(self, system, config) self.config.add({'deadband': 0, 'hardlimit': 1}) self.config.add_extra("_help", deadband="enable input dead band", hardlimit="enable output hard limit") self.config.add_extra( "_alt", deadband=(0, 1), hardlimit=(0, 1), ) self.config.add_extra( "_tex", deadband="z_{deadband}", hardlimit="z_{hardlimit}", ) self.gain = ConstService( v_str='u / R', tex_name='G', ) self.w_d = Algeb( info= 'Generator speed deviation before dead band (positive for under speed)', tex_name=r'\omega_{dev}', v_str='0', e_str='u*(wref-omega) - w_d', ) self.w_db = DeadBandRT( u=self.w_d, center=self.dbc, lower=self.dbl, upper=self.dbu, enable=self.config.deadband, ) self.w_dm = Algeb(info='Measured speed deviation after dead band', tex_name=r'\omega_{dm}', v_str='0', e_str='(1 - w_db_zi) * w_d + ' 'w_db_zlr * dbl + ' 'w_db_zur * dbu - ' 'w_dm') self.w_dmg = Algeb( info='Speed deviation after dead band after gain', tex_name=r'\omega_{dmG}', v_str='0', e_str='gain * w_dm - w_dmg', ) self.ll = LeadLag( u=self.w_dmg, T1=self.T1, T2=self.T2, ) self.pnl = Algeb( info='Power output before hard limiter', tex_name='P_{nl}', v_str='tm0', e_str='pref0 + ll_y - pnl', ) self.plim = HardLimiter( u=self.pnl, lower=self.pmin, upper=self.pmax, enable=self.config.hardlimit, ) self.pout.e_str = 'pnl * plim_zi + pmax * plim_zu + pmin * plim_zl - pout'
def __init__(self, system, config): PSSBase.__init__(self, system, config) self.config.add(OrderedDict([('freq_model', 'BusFreq')])) self.config.add_extra( '_help', {'freq_model': 'default freq. measurement model'}) self.config.add_extra('_alt', {'freq_model': ('BusFreq', )}) self.busf.model = self.config.freq_model self.dv = Derivative(self.v, tex_name='dV/dt', info='Finite difference of bus voltage') self.SnSb = ExtService( model='SynGen', src='M', indexer=self.syn, attr='pu_coeff', info='Machine base to sys base factor for power', tex_name='(Sb/Sn)') self.SW = Switcher( u=self.MODE, options=[0, 1, 2, 3, 4, 5, 6], ) self.sig = Algeb( tex_name='S_{ig}', info='Input signal', ) self.sig.v_str = 'SW_s1*(omega-1) + SW_s2*0 + SW_s3*(tm0/SnSb) + ' \ 'SW_s4*(tm-tm0) + SW_s5*v + SW_s6*0' self.sig.e_str = 'SW_s1*(omega-1) + SW_s2*(f-1) + SW_s3*(te/SnSb) + ' \ 'SW_s4*(tm-tm0) + SW_s5*v + SW_s6*dv_v - sig' self.F1 = Lag2ndOrd(u=self.sig, K=1, T1=self.A1, T2=self.A2) self.F2 = LeadLag2ndOrd(u=self.F1_y, T1=self.A3, T2=self.A4, T3=self.A5, T4=self.A6, zero_out=True) self.LL1 = LeadLag(u=self.F2_y, T1=self.T1, T2=self.T2, zero_out=True) self.LL2 = LeadLag(u=self.LL1_y, T1=self.T3, T2=self.T4, zero_out=True) self.Vks = Gain(u=self.LL2_y, K=self.KS) self.WO = WashoutOrLag(u=self.Vks_y, T=self.T6, K=self.T5, name='WO', zero_out=True) # WO_y == Vss self.VLIM = Limiter(u=self.WO_y, lower=self.LSMIN, upper=self.LSMAX, info='Vss limiter') self.Vss = Algeb( tex_name='V_{ss}', info='Voltage output before output limiter', e_str='VLIM_zi * WO_y + VLIM_zu * LSMAX + VLIM_zl * LSMIN - Vss') self.OLIM = Limiter(u=self.v, lower=self.VCLr, upper=self.VCUr, info='output limiter') self.vsout.e_str = 'OLIM_zi * Vss - vsout'
def __init__(self, system, config): PSSBase.__init__(self, system, config) # ALL THE FOLLOWING IS FOR INPUT 2 # retrieve indices of bus and bus freq self.buss2 = DataSelect(self.busr2, self.bus, info='selected bus (bus or busr)') self.busfreq2 = DeviceFinder(self.busf2, link=self.buss2, idx_name='bus', default_model='BusFreq', info='bus frequency idx') # from Bus self.v2 = ExtAlgeb( model='Bus', src='v', indexer=self.buss2, tex_name=r'V', info='Bus (or busr2, if given) terminal voltage', ) # from BusFreq 2 self.f2 = ExtAlgeb(model='FreqMeasurement', src='f', indexer=self.busfreq2, export=False, info='Bus frequency 2') # Config self.config.add(OrderedDict([('freq_model', 'BusFreq')])) self.config.add_extra( '_help', {'freq_model': 'default freq. measurement model'}) self.config.add_extra('_alt', {'freq_model': ('BusFreq', )}) self.busf.model = self.config.freq_model self.busf2.model = self.config.freq_model # input signal switch self.dv = Derivative(self.v) self.dv2 = Derivative(self.v2) self.SnSb = ExtService( model='SynGen', src='M', indexer=self.syn, attr='pu_coeff', info='Machine base to sys base factor for power', tex_name='(Sb/Sn)') self.SW = Switcher( u=self.MODE, options=[0, 1, 2, 3, 4, 5, 6, np.nan], ) self.SW2 = Switcher( u=self.MODE2, options=[0, 1, 2, 3, 4, 5, 6, np.nan], ) # Input signals self.sig = Algeb( tex_name='S_{ig}', info='Input signal', ) self.sig.v_str = 'SW_s1*(omega-1) + SW_s2*0 + SW_s3*(tm0/SnSb) + ' \ 'SW_s4*(tm-tm0) + SW_s5*v + SW_s6*0' self.sig.e_str = 'SW_s1*(omega-1) + SW_s2*(f-1) + SW_s3*(te/SnSb) + ' \ 'SW_s4*(tm-tm0) + SW_s5*v + SW_s6*dv_v - sig' self.sig2 = Algeb( tex_name='S_{ig2}', info='Input signal 2', ) self.sig2.v_str = 'SW2_s1*(omega-1) + SW2_s2*0 + SW2_s3*(tm0/SnSb) + ' \ 'SW2_s4*(tm-tm0) + SW2_s5*v2 + SW2_s6*0' self.sig2.e_str = 'SW2_s1*(omega-1) + SW2_s2*(f2-1) + SW2_s3*(te/SnSb) + ' \ 'SW2_s4*(tm-tm0) + SW2_s5*v2 + SW2_s6*dv2_v - sig2' self.L1 = Lag( u=self.sig, K=self.K1, T=self.T1, info='Transducer 1', ) self.L2 = Lag( u=self.sig2, K=self.K2, T=self.T2, info='Transducer 2', ) self.IN = Algeb( tex_name='I_N', info='Sum of inputs', v_str='L1_y + L2_y', e_str='L1_y + L2_y - IN', ) self.WO = WashoutOrLag( u=self.IN, K=self.T3, T=self.T4, ) self.LL1 = LeadLag( u=self.WO_y, T1=self.T5, T2=self.T6, zero_out=True, ) self.LL2 = LeadLag( u=self.LL1_y, T1=self.T7, T2=self.T8, zero_out=True, ) self.LL3 = LeadLag( u=self.LL2_y, T1=self.T9, T2=self.T10, zero_out=True, ) self.VSS = GainLimiter(u=self.LL3_y, K=1, R=1, lower=self.LSMIN, upper=self.LSMAX) self.VOU = ConstService(v_str='VCUr + v0') self.VOL = ConstService(v_str='VCLr + v0') self.OLIM = Limiter(u=self.v, lower=self.VOL, upper=self.VOU, info='output limiter') self.vsout.e_str = 'OLIM_zi * VSS_y - vsout'