def __init__(self, system, config): ExcBase.__init__(self, system, config) self.LG = Lag( u=self.v, T=self.TR, K=1, info='Sensing delay', ) self.vi = Algeb( info='Total input voltages', tex_name='V_i', unit='p.u.', ) self.vi.v_str = 'vf0 / KA' self.vi.e_str = '(vref0 - LG_y) - vi' self.vref0 = PostInitService( info='Const reference voltage', tex_name='V_{ref0}', v_str='v + vf0 / KA', ) self.HLI = HardLimiter( u=self.vi, lower=self.VIMIN, upper=self.VIMAX, info='Hard limiter on input', ) self.LL = LeadLag( u='vi * HLI_zi + VIMIN * HLI_zl + VIMAX * HLI_zu', T1=self.TC, T2=self.TB, info='Lead-lag compensator', zero_out=True, ) self.LR = Lag(u=self.LL_y, T=self.TA, K=self.KA, info='Regulator') # the following uses `XadIfd` for `IIFD` in the PSS/E manual self.vfmax = Algeb( info='Upper bound of output limiter', tex_name='V_{fmax}', v_str='VRMAX - KC * XadIfd', e_str='VRMAX - KC * XadIfd - vfmax', ) self.vfmin = Algeb( info='Lower bound of output limiter', tex_name='V_{fmin}', v_str='VRMIN - KC * XadIfd', e_str='VRMIN - KC * XadIfd - vfmin', ) self.HLR = HardLimiter(u=self.LR_y, lower=self.vfmin, upper=self.vfmax, info='Hard limiter on regulator output') self.vout.e_str = 'ue * (LR_y*HLR_zi + vfmin*HLR_zl + vfmax*HLR_zu) - vout'
def __init__(self, system, config): ExcBase.__init__(self, system, config) self.vref = Algeb(info='Reference voltage input', tex_name='V_{ref}', unit='p.u.', v_str='v + vf0 / KA', e_str='vref0 - vref' ) self.vref0 = PostInitService(info='constant vref', v_str='vref', tex_name='V_{ref0}', ) # input excitation voltages; PSS outputs summed at vi self.vi = Algeb(info='Total input voltages', tex_name='V_i', unit='p.u.', ) self.vi.v_str = 'vf0 / KA' self.vi.e_str = '(vref - LG_y - WF_y) - vi' self.LG = Lag(u=self.v, T=self.TR, K=1, info='Sensing delay', ) self.HLI = HardLimiter(u=self.vi, lower=self.VIMIN, upper=self.VIMAX, info='Hard limiter on input', ) self.vl = Algeb(info='Input after limiter', tex_name='V_l', v_str='HLI_zi*vi + HLI_zu*VIMAX + HLI_zl*VIMIN', e_str='HLI_zi*vi + HLI_zu*VIMAX + HLI_zl*VIMIN - vl', ) self.LL = LeadLag(u=self.vl, T1=self.TC, T2=self.TB, info='Lead-lag compensator', zero_out=True) self.LR = Lag(u=self.LL_y, T=self.TA, K=self.KA, info='Regulator') self.WF = Washout(u=self.LR_y, T=self.TF, K=self.KF, info='Stablizing circuit feedback') # the following uses `XadIfd` for `IIFD` in the PSS/E manual self.vfmax = Algeb(info='Upper bound of output limiter', tex_name='V_{fmax}', v_str='VRMAX - KC * XadIfd', e_str='VRMAX - KC * XadIfd - vfmax', ) self.vfmin = Algeb(info='Lower bound of output limiter', tex_name='V_{fmin}', v_str='VRMIN - KC * XadIfd', e_str='VRMIN - KC * XadIfd - vfmin', ) self.HLR = HardLimiter(u=self.WF_y, lower=self.vfmin, upper=self.vfmax, info='Hard limiter on regulator output') self.vout.e_str = 'LR_y*HLR_zi + vfmin*HLR_zl + vfmax*HLR_zu - vout'
def __init__(self, u, K, upper, lower, no_upper=False, no_lower=False, name=None, tex_name=None, info=None): Block.__init__(self, name=name, tex_name=tex_name, info=info) self.u = dummify(u) self.K = dummify(K) self.upper = upper self.lower = lower if (no_upper and no_lower) is True: raise ValueError("no_upper or no_lower cannot both be True") self.no_lower = no_lower self.no_upper = no_upper self.x = Algeb(info='Gain output before limiter', tex_name='x') self.y = Algeb(info='Gain output after limiter', tex_name='y') self.lim = HardLimiter(u=self.x, lower=self.lower, upper=self.upper, no_upper=no_upper, no_lower=no_lower, tex_name='lim') self.vars = {'lim': self.lim, 'x': self.x, 'y': self.y}
def __init__(self, u, T1, T2, lower, upper, name=None, info='Lead-lag transfer function'): super().__init__(name=name, info=info) self.T1 = T1 self.T2 = T2 self.u = u self.lower = lower self.upper = upper self.x = State(info='State in lead-lag transfer function', tex_name="x'") self.ynl = Algeb( info='Output of lead-lag transfer function before limiter', tex_name=r'y_{nl}') self.y = Algeb( info='Output of lead-lag transfer function after limiter', tex_name=r'y') self.lim = HardLimiter(u=self.ynl, lower=self.lower, upper=self.upper) self.vars = { 'x': self.x, 'ynl': self.ynl, 'y': self.y, 'lim': self.lim }
def __init__(self, system, config): PVD1Model.__init__(self, system, config) # --- Determine whether the energy storage is in charging or discharging mode --- self.LTN = LessThan(self.Ipout_y, 0.0) # --- Add integrator. Assume that state-of-charge is the initial condition --- self.pIG = Integrator( u='-LTN_z1*(v * Ipout_y)*EtaC - LTN_z0*(v * Ipout_y)/EtaD', T=self.Tf, K='sys_mva / 3600 / En', y0=self.SOCinit, check_init=False, ) # --- Add hard limiter for SOC --- self.SOClim = HardLimiter(u=self.pIG_y, lower=self.SOCmin, upper=self.SOCmax) # --- Add Ipmax, Ipmin, and Ipcmd --- self.Ipmax.v_str = '(1-SOClim_zl)*(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq0))' self.Ipmax.e_str = '(1-SOClim_zl)*(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq)) - Ipmax' self.Ipmin = Algeb( info='Minimum value of Ip', v_str= '-(1-SOClim_zu) * (SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq0))', e_str= '-(1-SOClim_zu) * (SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq)) - Ipmin', ) self.Ipcmd.lim.lower = self.Ipmin self.Ipcmd.y.deps = ['Ipmin']
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.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 = DeadBand(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='tm0 + 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): 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))', ) 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, u, kp, ki, ks, lower, upper, no_lower=False, no_upper=False, ref=0.0, x0=0.0, name=None, tex_name=None, info=None): Block.__init__(self, name=name, tex_name=tex_name, info=info) self.u = dummify(u) self.kp = dummify(kp) self.ki = dummify(ki) self.ks = dummify(ks) self.lower = dummify(lower) self.upper = dummify(upper) self.ref = dummify(ref) self.x0 = dummify(x0) self.xi = State(info="Integrator output", tex_name='xi') self.ys = Algeb(info="PI summation before limit", tex_name='ys') self.lim = HardLimiter(u=self.ys, lower=self.lower, upper=self.upper, no_lower=no_lower, no_upper=no_upper, tex_name='lim') self.y = Algeb(info="PI output", discrete=self.lim, tex_name='y') self.vars = {'xi': self.xi, 'ys': self.ys, 'lim': self.lim, 'y': self.y}
def __init__(self, system, config): PVD1Model.__init__(self, system, config) self.pgen = Algeb(info='Real power output', v_str='v * Ipout_y', e_str='v*Ipout_y - pgen', tex_name='P_{gen}' ) # --- Add integrator. Assume that state-of-charge is the initial condition --- self.pIG = Integrator(u=self.pgen, T=self.Tf, K=1.0, y0=self.SOCinit) # --- Add hard limiter for SOC --- self.SOC = HardLimiter(u=self.pIG_y, lower=self.SOCmin, upper=self.SOCmax) # --- Add Ipmax and Ipcmd --- self.Ipmax.v_str = '(1-SOC_zl)*(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq0))' self.Ipmax.e_str = '(1-SOC_zl)*(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq)) - Ipmax' self.Ipcmd.lim.sign_lower = dummify(-1) self.Ipcmd.lim.lower = self.Ipmax
def __init__(self, system, config): PVD1Model.__init__(self, system, config) # --- Add integrator. Assume that state-of-charge is the initial condition --- self.pIG = Integrator( u='v * Ipout_y', T=self.Tf, K=1.0, y0=self.SOCinit, check_init=False, ) # --- Add hard limiter for SOC --- self.SOC = HardLimiter(u=self.pIG_y, lower=self.SOCmin, upper=self.SOCmax) # --- Add Ipmax and Ipcmd --- self.Ipmax.v_str = '(1-SOC_zl)*(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq0))' self.Ipmax.e_str = '(1-SOC_zl)*(SWPQ_s1 * ialim + SWPQ_s0 * sqrt(Ipmaxsq)) - Ipmax' self.Ipcmd.lim.sign_lower = dummify(-1) self.Ipcmd.lim.lower = self.Ipmax
def __init__(self, system, config): ExcBase.__init__(self, system, config) self.KPC = ConstService(v_str='KP * exp(1j * radians(THETAP))', tex_name='K_{PC}', info='KP polar THETAP', vtype=np.complex) # 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.Id = ExtAlgeb( src='Id', model='SynGen', indexer=self.syn, tex_name=r'I_d', info='d-axis machine current', ) self.Iq = ExtAlgeb( src='Iq', model='SynGen', indexer=self.syn, tex_name=r'I_q', info='q-axis machine current', ) # control block begin self.LG = Lag( self.v, T=self.TR, K=1, info='Voltage transducer', ) self.UEL = Algeb(info='Interface var for under exc. limiter', tex_name='U_{EL}', v_str='0', e_str='0 - UEL') self.VE = VarService( tex_name='V_E', info='VE', v_str='Abs(KPC*(vd + 1j*vq) + 1j*(KI + KPC*XL)*(Id + 1j*Iq))', ) self.IN = Algeb( tex_name='I_N', info='Input to FEX', v_str='KC * XadIfd / VE', e_str='KC * XadIfd / VE - IN', ) self.FEX = Piecewise( u=self.IN, points=(0, 0.433, 0.75, 1), funs=('1', '1 - 0.577*IN', 'sqrt(0.75 - IN ** 2)', '1.732*(1 - IN)', 0), info='Piecewise function FEX', ) self.VBMIN = dummify(-9999) self.VGMIN = dummify(-9999) self.VB = GainLimiter( u='VE*FEX_y', K=1, upper=self.VBMAX, lower=self.VBMIN, no_lower=True, info='VB with limiter', ) self.VG = GainLimiter( u=self.vout, K=self.KG, upper=self.VGMAX, lower=self.VGMIN, no_lower=True, info='Feedback gain with HL', ) self.vrs = Algeb( tex_name='V_{RS}', info='VR subtract feedback VG', v_str='vf0 / VB_y / KM', e_str='LAW1_y - VG_y - vrs', ) self.vref = Algeb( info='Reference voltage input', tex_name='V_{ref}', unit='p.u.', v_str='(vrs + VG_y) / KA + v', e_str='vref0 - vref', ) self.vref0 = PostInitService( info='Initial reference voltage input', tex_name='V_{ref0}', v_str='vref', ) # input excitation voltages; PSS outputs summed at vi self.vi = Algeb( info='Total input voltages', tex_name='V_i', unit='p.u.', e_str='-LG_y + vref - vi', v_str='-v + vref', ) self.vil = Algeb(info='Input voltage after limit', tex_name='V_{il}', v_str='HLI_zi*vi + HLI_zl*VIMIN + HLI_zu*VIMAX', e_str='HLI_zi*vi + HLI_zl*VIMIN + HLI_zu*VIMAX - vil') self.HG = HVGate( u1=self.UEL, u2=self.vil, info='HVGate for under excitation', ) self.LL = LeadLag( u=self.HG_y, T1=self.TC, T2=self.TB, info='Regulator', zero_out=True, ) # LL_y == VA self.LAW1 = LagAntiWindup( u=self.LL_y, T=self.TA, K=self.KA, lower=self.VRMIN, upper=self.VRMAX, info='Lag AW on VR', ) # LAW1_y == VR self.HLI = HardLimiter( u=self.vi, lower=self.VIMIN, upper=self.VIMAX, info='Input limiter', ) self.LAW2 = LagAntiWindup( u=self.vrs, T=self.TM, K=self.KM, lower=self.VMMIN, upper=self.VMMAX, info='Lag AW on VM', ) # LAW2_y == VM self.vout.e_str = 'VB_y * LAW2_y - vout'
def __init__(self, system, config): ExcBase.__init__(self, system, config) # 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.Id = ExtAlgeb( src='Id', model='SynGen', indexer=self.syn, tex_name=r'I_d', info='d-axis machine current', ) self.Iq = ExtAlgeb( src='Iq', model='SynGen', indexer=self.syn, tex_name=r'I_q', info='q-axis machine current', ) self.VE = VarService( tex_name=r'V_{E}', info=r'V_{E}', v_str='Abs(KP * (vd + 1j*vq) + 1j*KI*(Id + 1j*Iq))', ) self.V40 = ConstService('sqrt(VE ** 2 - (0.78 * XadIfd) ** 2)') self.VR0 = ConstService(info='Initial VR', tex_name='V_{R0}', v_str='vf0 * KE - V40') self.vb0 = ConstService(info='Initial vb', tex_name='V_{b0}', v_str='VR0 / KA') # Set VRMAX to 999 when VRMAX = 0 self._zVRM = FlagValue( self.VRMAX, value=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='Sensing delay') ExcVsum.__init__(self) self.vref.v_str = 'v + vb0' self.vref0 = PostInitService(info='Constant vref', tex_name='V_{ref0}', v_str='vref') # NOTE: for offline exciters, `vi` equation ignores ext. voltage changes self.vi = Algeb( info='Total input voltages', tex_name='V_i', unit='p.u.', e_str='ue * (-LG_y + vref + UEL + OEL + Vs - vi)', v_str='vref - v', diag_eps=True, ) self.LA3 = LagAntiWindup( u='ue * (vi - WF_y)', T=self.TA, K=self.KA, upper=self.VRMAXc, lower=self.VRMIN, info=r'V_{R}, Lag Anti-Windup', ) # LA3_y is V_R # FIXME: antiwindup out of limit is not warned of in initialization self.zeros = ConstService(v_str='0.0') self.LA1 = Lag( 'ue * (VB_y * HL_zi + VBMAX * HL_zu)', T=self.TE, K=1, D=self.KE, ) self.WF = Washout(u=self.LA1_y, T=self.TF, K=self.KF, info='V_F, stablizing circuit feedback, washout') self.SQE = Algeb( tex_name=r'SQE', info=r'Square of error after mul', v_str='VE ** 2 - (0.78 * XadIfd) ** 2', e_str='VE ** 2 - (0.78 * XadIfd) ** 2 - SQE', ) self.SL = LessThan(u=self.zeros, bound=self.SQE, equal=False, enable=True, cache=False) self.VB = Piecewise(self.SQE, points=(0, ), funs=('ue * LA3_y', 'ue * (sqrt(SQE) + LA3_y)')) self.HL = HardLimiter( u=self.VB_y, lower=self.zeros, upper=self.VBMAX, info='Hard limiter for VB', ) self.vout.e_str = 'ue * (LA1_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 = 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'