def __init__(self, system, config): Model.__init__(self, system, config) # Notes: # TimeSeries model is not used in power flow for now self.flags.tds = True self.config.add(OrderedDict((('silent', 1), ))) self.config.add_extra( "_help", silent="suppress output messages if is not zero", ) self.config.add_extra( "_alt", silent=(0, 1), ) self.SW = Switcher( self.mode, options=(0, 1, 2), info='mode switcher', ) self._data = OrderedDict( ) # keys are the idx, and values are the dataframe
def __init__(self, system, config): super(IEEESTModel, self).__init__(system, config) self.KST5 = ConstService(v_str='KS * T5', tex_name='KS*T5') self.SW = Switcher( u=self.MODE, options=[1, 2, 3, 4, 5, 6], ) self.signal = Algeb( tex_name='S_{in}', info='Input signal', ) # input signals: # 1 (s0) - Rotor speed deviation (p.u.) # 2 (s1) - Bus frequency deviation (p.u.) # TODO: calculate freq without reimpl. # 3 (s2) - Generator electrical power in Gen MVABase (p.u.) # TODO: allow using system.config.mva # 4 (s3) - Generator accelerating power (p.u.) # 5 (s4) - Bus voltage (p.u.) # 6 (s5) - Derivative of p.u. bus voltage # TODO: memory block for calc. of derivative self.signal.e_str = 'SW_s0 * (1-omega) + SW_s1 * 0 + SW_s2 * te + ' \ 'SW_s3 * (tm-tm0) + SW_s4 *v + SW_s5 * 0 - signal' self.F1 = Lag2ndOrd(u=self.signal, 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) self.LL1 = LeadLag(u=self.F2_y, T1=self.T1, T2=self.T2) self.LL2 = LeadLag(u=self.LL1_y, T1=self.T3, T2=self.T4) self.WO = Washout(u=self.LL2_y, T=self.T6, K=self.KST5) # 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.VCL, upper=self.VCU, info='output limiter') # TODO: allow ignoring VCU or VCL when zero self.vsout.e_str = 'OLIM_zi * Vss - vsout'
def __init__(self, system, config): Model.__init__(self, system, config) self.flags.tds = True self.group = 'TimedEvent' self.SW = Switcher( u=self.method, options=('+', '-', '*', '/', '='), info='Switcher for alteration method', ) self.t.callback = self._alter_field
def test_switcher(self): p = NumParam() p.v = np.array([0, 1, 2, 2, 1, 3, 1]) switcher = Switcher(u=p, options=[0, 1, 2, 3, 4]) switcher.list2array(len(p.v)) switcher.check_var() self.assertSequenceEqual(switcher.s0.tolist(), [1, 0, 0, 0, 0, 0, 0]) self.assertSequenceEqual(switcher.s1.tolist(), [0, 1, 0, 0, 1, 0, 1]) self.assertSequenceEqual(switcher.s2.tolist(), [0, 0, 1, 1, 0, 0, 0]) self.assertSequenceEqual(switcher.s3.tolist(), [0, 0, 0, 0, 0, 1, 0]) self.assertSequenceEqual(switcher.s4.tolist(), [0, 0, 0, 0, 0, 0, 0])
def __init__(self, system, config): Model.__init__(self, system, config) self.flags.tds = True self.group = 'DG' self.config.add(OrderedDict((('plim', 0), ))) self.config.add_extra('_help', plim='enable input power limit check bound by [0, pmx]', ) self.config.add_extra('_tex', plim='P_{lim}', ) self.config.add_extra('_alt', plim=(0, 1), ) self.SWPQ = Switcher(u=self.pqflag, options=(0, 1), tex_name='SW_{PQ}', cache=True) self.buss = DataSelect(self.igreg, self.bus, info='selected bus (bus or igreg)', ) self.busfreq = DeviceFinder(self.busf, link=self.buss, idx_name='bus') # --- initial values from power flow --- # a : bus voltage angle # v : bus voltage magnitude # p0s : active power from connected static PV generator # q0s : reactive power from connected static PV generator # pref0 : initial active power set point for the PVD1 device # qref0 : initial reactive power set point for the PVD1 device self.a = ExtAlgeb(model='Bus', src='a', indexer=self.buss, tex_name=r'\theta', info='bus (or igreg) phase angle', unit='rad.', e_str='-Ipout_y * v * u', ename='P', tex_ename='P', ) self.v = ExtAlgeb(model='Bus', src='v', indexer=self.buss, tex_name='V', info='bus (or igreg) terminal voltage', unit='p.u.', e_str='-Iqout_y * v * u', ename='Q', tex_ename='Q', ) self.p0s = ExtService(model='StaticGen', src='p', indexer=self.gen, tex_name='P_{0s}', info='Initial P from static gen', ) self.q0s = ExtService(model='StaticGen', src='q', indexer=self.gen, tex_name='Q_{0s}', info='Initial Q from static gen', ) # --- calculate the initial P and Q for this distributed device --- self.pref0 = ConstService(v_str='gammap * p0s', tex_name='P_{ref0}', info='Initial P for the PVD1 device', ) self.qref0 = ConstService(v_str='gammaq * q0s', tex_name='Q_{ref0}', info='Initial Q for the PVD1 device', ) # frequency measurement variable `f` self.f = ExtAlgeb(model='FreqMeasurement', src='f', indexer=self.busfreq, export=False, info='Bus frequency', unit='p.u.', ) self.fHz = Algeb(info='frequency in Hz', v_str='fn * f', e_str='fn * f - fHz', unit='Hz', tex_name='f_{Hz}', ) # --- frequency branch --- self.FL1 = Limiter(u=self.fHz, lower=self.ft0, upper=self.ft1, info='Under frequency comparer', no_warn=True, ) self.FL2 = Limiter(u=self.fHz, lower=self.ft2, upper=self.ft3, info='Over frequency comparer', no_warn=True, ) self.Kft01 = ConstService(v_str='1/(ft1 - ft0)', tex_name='K_{ft01}') self.Ffl = Algeb(info='Coeff. for under frequency', v_str='FL1_zi * Kft01 * (fHz - ft0) + FL1_zu', e_str='FL1_zi * Kft01 * (fHz - ft0) + FL1_zu - Ffl', tex_name='F_{fl}', discrete=self.FL1, ) self.Kft23 = ConstService(v_str='1/(ft3 - ft2)', tex_name='K_{ft23}') self.Ffh = Algeb(info='Coeff. for over frequency', v_str='FL2_zl + FL2_zi * (1 + Kft23 * (ft2 - fHz))', e_str='FL2_zl + FL2_zi * (1 + Kft23 * (ft2 - fHz)) - Ffh', tex_name='F_{fh}', discrete=self.FL2, ) self.Fdev = Algeb(info='Frequency deviation', v_str='fn - fHz', e_str='fn - fHz - Fdev', unit='Hz', tex_name='f_{dev}', ) self.DB = DeadBand1(u=self.Fdev, center=0.0, lower=self.fdbd, upper=0.0, gain=self.ddn, info='frequency deviation deadband with gain', ) # outputs `Pdrp` self.DB.db.no_warn = True # --- Voltage flags --- self.VL1 = Limiter(u=self.v, lower=self.vt0, upper=self.vt1, info='Under voltage comparer', no_warn=True, ) self.VL2 = Limiter(u=self.v, lower=self.vt2, upper=self.vt3, info='Over voltage comparer', no_warn=True, ) self.Kvt01 = ConstService(v_str='1/(vt1 - vt0)', tex_name='K_{vt01}') self.Fvl = Algeb(info='Coeff. for under voltage', v_str='VL1_zi * Kvt01 * (v - vt0) + VL1_zu', e_str='VL1_zi * Kvt01 * (v - vt0) + VL1_zu - Fvl', tex_name='F_{vl}', discrete=self.VL1, ) self.Kvt23 = ConstService(v_str='1/(vt3 - vt2)', tex_name='K_{vt23}') self.Fvh = Algeb(info='Coeff. for over voltage', v_str='VL2_zl + VL2_zi * (1 + Kvt23 * (vt2 - v))', e_str='VL2_zl + VL2_zi * (1 + Kvt23 * (vt2 - v)) - Fvh', tex_name='F_{vh}', discrete=self.VL2, ) # --- sensed voltage with lower limit of 0.01 --- self.VLo = Limiter(u=self.v, lower=0.01, upper=999, no_upper=True, info='Voltage lower limit (0.01) flag', ) self.vp = Algeb(tex_name='V_p', info='Sensed positive voltage', v_str='v * VLo_zi + 0.01 * VLo_zl', e_str='v * VLo_zi + 0.01 * VLo_zl - vp', ) self.Pext0 = ConstService(info='External additional signal added to Pext', tex_name='P_{ext0}', v_str='0', ) self.Pext = Algeb(tex_name='P_{ext}', info='External power signal (for AGC)', v_str='u * Pext0', e_str='u * Pext0 - Pext' ) self.Pref = Algeb(tex_name='P_{ref}', info='Reference power signal (for scheduling setpoint)', v_str='u * pref0', e_str='u * pref0 - Pref' ) self.Psum = Algeb(tex_name='P_{tot}', info='Sum of P signals', v_str='u * (Pext + Pref + DB_y)', e_str='u * (Pext + Pref + DB_y) - Psum', ) # `DB_y` is `Pdrp` (f droop) self.PHL = Limiter(u=self.Psum, lower=0.0, upper=self.pmx, enable=self.config.plim, info='limiter for Psum in [0, pmx]', ) self.Vcomp = VarService(v_str='abs(v*exp(1j*a) + (1j * xc) * (Ipout_y + 1j * Iqout_y))', info='Voltage before Xc compensation', tex_name='V_{comp}' ) self.Vqu = ConstService(v_str='v1 - (qref0 - qmn) / dqdv', info='Upper voltage bound => qmx', tex_name='V_{qu}', ) self.Vql = ConstService(v_str='v0 + (qmx - qref0) / dqdv', info='Lower voltage bound => qmn', tex_name='V_{ql}', ) self.VQ1 = Limiter(u=self.Vcomp, lower=self.Vql, upper=self.v0, info='Under voltage comparer for Q droop', no_warn=True, ) self.VQ2 = Limiter(u=self.Vcomp, lower=self.v1, upper=self.Vqu, info='Over voltage comparer for Q droop', no_warn=True, ) Qdrp = 'u * VQ1_zl * qmx + VQ2_zu * qmn + ' \ 'u * VQ1_zi * (qmx + dqdv *(Vqu - Vcomp)) + ' \ 'u * VQ2_zi * (dqdv * (v1 - Vcomp)) ' self.Qdrp = Algeb(tex_name='Q_{drp}', info='External power signal (for AGC)', v_str=Qdrp, e_str=f'{Qdrp} - Qdrp', discrete=(self.VQ1, self.VQ2), ) self.Qref = Algeb(tex_name=r'Q_{ref}', info='Reference power signal (for scheduling setpoint)', v_str='u * qref0', e_str='u * qref0 - Qref' ) self.Qsum = Algeb(tex_name=r'Q_{tot}', info='Sum of Q signals', v_str=f'u * (qref0 + {Qdrp})', e_str='u * (Qref + Qdrp) - Qsum', discrete=(self.VQ1, self.VQ2), ) self.Ipul = Algeb(info='Ipcmd before Ip hard limit', v_str='(Psum * PHL_zi + pmx * PHL_zu) / vp', e_str='(Psum * PHL_zi + pmx * PHL_zu) / vp - Ipul', tex_name='I_{p,ul}', ) self.Iqul = Algeb(info='Iqcmd before Iq hard limit', v_str='Qsum / vp', e_str='Qsum / vp - Iqul', tex_name='I_{q,ul}', ) # --- Ipmax, Iqmax and Iqmin --- Ipmaxsq = "(Piecewise((0, Le(ialim**2 - Iqcmd_y**2, 0)), ((ialim**2 - Iqcmd_y ** 2), True)))" Ipmaxsq0 = "(Piecewise((0, Le(ialim**2 - (u*qref0/v)**2, 0)), ((ialim**2 - (u*qref0/v) ** 2), True)))" self.Ipmaxsq = VarService(v_str=Ipmaxsq, tex_name='I_{pmax}^2') self.Ipmaxsq0 = ConstService(v_str=Ipmaxsq0, tex_name='I_{pmax0}^2') self.Ipmax = Algeb(v_str='(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq0))', e_str='(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq)) - Ipmax', tex_name='I_{pmax}', ) Iqmaxsq = "(Piecewise((0, Le(ialim**2 - Ipcmd_y**2, 0)), ((ialim**2 - Ipcmd_y ** 2), True)))" Iqmaxsq0 = "(Piecewise((0, Le(ialim**2 - (u*pref0/v)**2, 0)), ((ialim**2 - (u*pref0/v) ** 2), True)))" self.Iqmaxsq = VarService(v_str=Iqmaxsq, tex_name='I_{qmax}^2') self.Iqmaxsq0 = ConstService(v_str=Iqmaxsq0, tex_name='I_{qmax0}^2') self.Iqmax = Algeb(v_str='SWPQ_s0 * ialim + SWPQ_s1 * sqrt(Iqmaxsq0)', e_str='SWPQ_s0 * ialim + SWPQ_s1 * sqrt(Iqmaxsq) - Iqmax', tex_name='I_{qmax}', ) # TODO: set option whether to use degrading gain # --- `Ipcmd` and `Iqcmd` --- self.Ipcmd = GainLimiter(u=self.Ipul, K=1, R='Fvl * Fvh * Ffl * Ffh * recflag + 1 * (1 - recflag)', lower=0, upper=self.Ipmax, info='Ip with limiter and coeff.', tex_name='I^{pcmd}', ) self.Iqcmd = GainLimiter(u=self.Iqul, K=1, R='Fvl * Fvh * Ffl * Ffh * recflag + 1 * (1 - recflag)', lower=self.Iqmax, sign_lower=-1, upper=self.Iqmax, info='Iq with limiter and coeff.', tex_name='I^{qcmd}', ) self.Ipout = Lag(u=self.Ipcmd_y, T=self.tip, K=1.0, info='Output Ip filter', ) self.Iqout = Lag(u=self.Iqcmd_y, T=self.tiq, K=1.0, info='Output Iq filter', )
def __init__(self, system, config): ACDC2Term.__init__(self, system, config) self.rsh = NumParam(default=0.0025, info="AC interface resistance", unit="ohm", z=True, tex_name='r_{sh}') self.xsh = NumParam(default=0.06, info="AC interface reactance", unit="ohm", z=True, tex_name='x_{sh}') self.control = NumParam( info="Control method: 0-PQ, 1-PV, 2-vQ or 3-vV", mandatory=True) self.v0 = NumParam( default=1.0, info="AC voltage setting (PV or vV) or initial guess (PQ or vQ)") self.p0 = NumParam(default=0.0, info="AC active power setting", unit="pu") self.q0 = NumParam(default=0.0, info="AC reactive power setting", unit="pu") self.vdc0 = NumParam(default=1.0, info="DC voltage setting", unit="pu", tex_name='v_{dc0}') self.k0 = NumParam(default=0.0, info="Loss coefficient - constant") self.k1 = NumParam(default=0.0, info="Loss coefficient - linear") self.k2 = NumParam(default=0.0, info="Loss coefficient - quadratic") self.droop = NumParam(default=0.0, info="Enable dc voltage droop control", unit="boolean") self.K = NumParam(default=0.0, info="Droop coefficient") self.vhigh = NumParam(default=9999, info="Upper voltage threshold in droop control", unit="pu") self.vlow = NumParam(default=0.0, info="Lower voltage threshold in droop control", unit="pu") self.vshmax = NumParam(default=1.1, info="Maximum ac interface voltage", unit="pu") self.vshmin = NumParam(default=0.9, info="Minimum ac interface voltage", unit="pu") self.Ishmax = NumParam(default=2, info="Maximum ac current", unit="pu") # define variables and equations self.flags.update({'pflow': True}) self.group = 'StaticACDC' self.gsh = ConstService(tex_name='g_{sh}', v_str='re(1/(rsh + 1j * xsh))', vtype=np.complex) self.bsh = ConstService(tex_name='b_{sh}', v_str='im(1/(rsh + 1j * xsh))', vtype=np.complex) self.mode = Switcher(u=self.control, options=(0, 1, 2, 3)) self.ash = Algeb( info='voltage phase behind the transformer', unit='rad', tex_name=r'\theta_{sh}', v_str='a', e_str='u * (gsh * v**2 - gsh * v * vsh * cos(a - ash) - ' 'bsh * v * vsh * sin(a - ash)) - psh', diag_eps=True, ) self.vsh = Algeb( info='voltage magnitude behind transformer', tex_name="V_{sh}", unit='p.u.', v_str='v0', e_str='u * (-bsh * v**2 - gsh * v * vsh * sin(a - ash) + ' 'bsh * v * vsh * cos(a - ash)) - qsh', diag_eps=True, ) self.psh = Algeb( info='active power injection into VSC', tex_name="P_{sh}", unit='p.u.', v_str='p0 * (mode_s0 + mode_s1)', e_str='u * (mode_s0 + mode_s1) * (p0 - psh) + ' 'u * (mode_s2 + mode_s3) * (v1 - v2 - vdc0)', diag_eps=True, ) self.qsh = Algeb( info='reactive power injection into VSC', tex_name="Q_{sh}", v_str='q0 * (mode_s0 + mode_s2)', e_str='u * (mode_s0 + mode_s2) * (q0 - qsh) + ' 'u * (mode_s1 + mode_s3) * (v0 - v)', diag_eps=True, ) self.pdc = Algeb( info='DC power injection', tex_name="P_{dc}", v_str='0', e_str='u * (gsh * vsh * vsh - gsh * v * vsh * cos(a - ash) + ' 'bsh * v * vsh * sin(a - ash)) + pdc', ) self.a.e_str = '-psh' self.v.e_str = '-qsh' self.v1.e_str = '-pdc / (v1 - v2)' self.v2.e_str = 'pdc / (v1 - v2)'
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') # 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, 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'
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) 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=[1, 2, 3, 4, 5, 6], ) self.sig = Algeb( tex_name='S_{ig}', info='Input signal', ) self.sig.v_str = 'SW_s0*(omega-1) + SW_s1*0 + SW_s2*(tm0/SnSb) + ' \ 'SW_s3*(tm-tm0) + SW_s4*v + SW_s5*0' self.sig.e_str = 'SW_s0*(omega-1) + SW_s1*(f-1) + SW_s2*(te/SnSb) + ' \ 'SW_s3*(tm-tm0) + SW_s4*v + SW_s5*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): ExcBase.__init__(self, system, config) self.flags.nr_iter = True ExcVsum.__init__(self) self.UEL0.v_str = '-999' self.OEL0.v_str = '999' self.ulim = ConstService('9999') self.llim = ConstService('-9999') self.SWUEL = Switcher(u=self.UELc, options=[0, 1, 2, 3], tex_name='SW_{UEL}', cache=True) self.SWVOS = Switcher(u=self.VOSc, options=[0, 1, 2], tex_name='SW_{VOS}', cache=True) # control block begin self.LG = Lag( self.v, T=self.TR, K=1, info='Voltage transducer', ) self.SG0 = ConstService(v_str='0', info='SG initial value.') self.SG = Algeb( tex_name='SG', info='SG', v_str='SG0', e_str='SG0 - SG', ) self.zero = ConstService('0') self.LR = GainLimiter( u='XadIfd - ILR', K=self.KLR, R=1, upper=self.ulim, lower=self.zero, no_upper=True, info='Exciter output current gain limiter', ) self.VA0 = PostInitService(tex_name='V_{A0}', v_str='vf0 - SWVOS_s2 * SG + LR_y', info='VA (LA_y) initial value') self.vref.v_str = 'ue * (v + (vf0 - SWVOS_s2 * SG + LR_y) / KA - SWVOS_s1 * SG - SWUEL_s1 * UEL)' self.vref.v_iter = 'ue * (v + (vf0 - SWVOS_s2 * SG + LR_y) / KA - SWVOS_s1 * SG - SWUEL_s1 * UEL)' self.vref0 = PostInitService( info='Initial reference voltage input', tex_name='V_{ref0}', v_str='vref', ) self.vi = Algeb( info='Total input voltages', tex_name='V_i', unit='p.u.', e_str= 'ue * (-LG_y + vref - WF_y + SWUEL_s1 * UEL + SWVOS_s1 * SG + Vs) - vi', v_iter= 'ue * (-LG_y + vref - WF_y + SWUEL_s1 * UEL + SWVOS_s1 * SG + Vs)', v_str= 'ue * (-LG_y + vref - WF_y + SWUEL_s1 * UEL + SWVOS_s1 * SG + Vs)', ) self.vil = GainLimiter( u=self.vi, K=1, R=1, upper=self.VIMAX, lower=self.VIMIN, info='Exciter voltage input limiter', ) self.UEL2 = Algeb( tex_name='UEL_2', info='UEL_2 as HVG1 u1', v_str='ue * (SWUEL_s2 * UEL + (1 - SWUEL_s2) * llim)', e_str='ue * (SWUEL_s2 * UEL + (1 - SWUEL_s2) * llim) - UEL2', ) self.HVG1 = HVGate( u1=self.UEL2, u2=self.vil_y, info='HVGate after V_I', ) self.LL = LeadLag( u=self.HVG1_y, T1=self.TC, T2=self.TB, info='Lead-lag compensator', zero_out=True, ) self.LL1 = LeadLag( u=self.LL_y, T1=self.TC1, T2=self.TB1, info='Lead-lag compensator 1', zero_out=True, ) self.LA = LagAntiWindup( u=self.LL1_y, T=self.TA, K=self.KA, upper=self.VAMAX, lower=self.VAMIN, info='V_A, Anti-windup lag', ) # LA_y is VA self.vas = Algeb( tex_name=r'V_{As}', info='V_A after subtraction, as HVG u2', v_str='ue * (SWVOS_s2 * SG + LA_y - LR_y)', v_iter='ue * (SWVOS_s2 * SG + LA_y - LR_y)', e_str='ue * (SWVOS_s2 * SG + LA_y - LR_y) - vas', ) self.UEL3 = Algeb( tex_name='UEL_3', info='UEL_3 as HVG u1', v_str='ue * (SWUEL_s3 * UEL + (1 - SWUEL_s3) * llim)', e_str='ue * (SWUEL_s3 * UEL + (1 - SWUEL_s3) * llim) - UEL3', ) self.HVG = HVGate( u1=self.UEL3, u2=self.vas, info='HVGate for under excitation', ) self.LVG = LVGate( u1=self.HVG_y, u2=self.OEL, info='HVGate for over excitation', ) # vd, vq, Id, Iq from SynGen self.vd = ExtAlgeb( src='vd', model='SynGen', indexer=self.syn, tex_name=r'V_d', info='d-axis machine voltage', ) self.vq = ExtAlgeb( src='vq', model='SynGen', indexer=self.syn, tex_name=r'V_q', info='q-axis machine voltage', ) self.efdu = VarService( info='Output exciter voltage upper bound', tex_name=r'efd_{u}', v_str='Abs(vd + 1j*vq) * VRMAX - KC * XadIfd', ) self.efdl = VarService(info='Output exciter voltage lower bound', tex_name=r'efd_{l}', v_str='Abs(vd + 1j*vq) * VRMIN') self.vol = GainLimiter( u=self.LVG_y, K=1, R=1, upper=self.efdu, lower=self.efdl, info='Exciter output limiter', ) self.WF = Washout( u=self.LVG_y, T=self.TF, K=self.KF, info='V_F, Stablizing circuit feedback', ) self.vout.e_str = 'ue * vol_y - vout'