def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'TurbineGov' self.flags.update({'tds': True}) self.Sn = ExtParam( src='Sn', model='SynGen', indexer=self.syn, tex_name='S_m', info='Rated power from generator', unit='MVA', export=False, ) self.Vn = ExtParam( src='Vn', model='SynGen', indexer=self.syn, tex_name='V_m', info='Rated voltage from generator', unit='kV', export=False, ) self.tm0 = ExtService(src='tm', model='SynGen', indexer=self.syn, tex_name=r'\tau_{m0}', info='Initial mechanical input') self.omega = ExtState(src='omega', model='SynGen', indexer=self.syn, tex_name=r'\omega', info='Generator speed', unit='p.u.') self.gain = ConstService( v_str='u / R', tex_name='G', ) self.tm = ExtAlgeb( src='tm', model='SynGen', indexer=self.syn, tex_name=r'\tau_m', e_str='u * (pout - tm0)', info='Mechanical power to generator', ) self.pout = Algeb( info='Turbine final output power', tex_name='P_{out}', v_str='tm0', ) self.wref = Algeb( info='Speed reference variable', tex_name=r'\omega_{ref}', v_str='wref0', e_str='wref0 - wref', )
def __init__(self, system, config): ACEData.__init__(self) Model.__init__(self, system, config) self.flags.tds = True 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.area = ExtParam(model='Bus', src='area', indexer=self.bus, export=False) self.busf.model = self.config.freq_model self.busfreq = DeviceFinder(self.busf, link=self.bus, idx_name='bus') self.f = ExtAlgeb( model='FreqMeasurement', src='f', indexer=self.busfreq, export=False, info='Bus frequency', ) self.ace = Algeb( info='area control error', unit='MW (p.u.)', tex_name='ace', e_str='10 * bias * (f - 1) - ace', )
def __init__(self, system, config): AreaData.__init__(self) Model.__init__(self, system, config) self.group = 'Collection' self.flags.pflow = True self.flags.tds = True self.Bus = BackRef() self.ACTopology = BackRef() # --------------------Experiment Zone-------------------- self.Vn = ExtParam(model='Bus', src='Vn', indexer=self.ACTopology, export=False) self.Vn_sum = NumReduce(u=self.Vn, fun=np.sum, ref=self.Bus) self.Vn_sum_rep = NumRepeat(u=self.Vn_sum, ref=self.Bus) self.a = ExtAlgeb(model='ACTopology', src='a', indexer=self.ACTopology, info='Bus voltage angle') self.v = ExtAlgeb(model='ACTopology', src='v', indexer=self.ACTopology, info='Bus voltage magnitude')
def __init__(self, system, config): AreaData.__init__(self) Model.__init__(self, system, config) self.group = 'Collection' self.flags.update({'pflow': True, 'tds': True}) self.Bus = RefParam(export=False) self.ACTopology = RefParam(export=False) # --------------------Experiment Zone-------------------- self.Vn = ExtParam(model='Bus', src='Vn', indexer=self.ACTopology, export=False) self.Vn_sum = ReducerService(u=self.Vn, fun=np.sum, ref=self.Bus) self.Vn_sum_rep = RepeaterService(u=self.Vn_sum, ref=self.Bus) self.a = ExtAlgeb(model='ACTopology', src='a', indexer=self.ACTopology, info='Bus voltage angle') self.v = ExtAlgeb(model='ACTopology', src='v', indexer=self.ACTopology, info='Bus voltage magnitude')
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'DynLoad' self.flags.tds = True self.bus = ExtParam(model='PQ', src='bus', indexer=self.pq) self.p0 = ExtService( model='PQ', src='Ppf', indexer=self.pq, tex_name='P_0', ) self.q0 = ExtService( model='PQ', src='Qpf', indexer=self.pq, tex_name='Q_0', ) self.v0 = ExtService( model='Bus', src='v', indexer=self.bus, tex_name='V_0', ) self.busfreq = DeviceFinder( u=self.busf, link=self.bus, idx_name='bus', info='found idx of BusFreq', ) self.f = ExtAlgeb( model='FreqMeasurement', src='f', indexer=self.busfreq, tex_name='f', ) self.pv0 = ConstService(v_str='u * kp/100 * p0 / (v0) ** ap ') self.qv0 = ConstService(v_str='u * kq/100 * q0 / (v0) ** aq ') self.a = ExtAlgeb( model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', e_str='pv0 * (v ** ap) * (f ** bp)', ) self.v = ExtAlgeb( model='Bus', src='v', indexer=self.bus, tex_name='V', e_str='qv0 * (v ** aq) * (f ** bq)', )
def __init__(self, system, config): ACEData.__init__(self) Model.__init__(self, system, config) self.flags.tds = True self.group = 'Calculation' self.config.add( OrderedDict([ ('freq_model', 'BusFreq'), ('interval', 4.0), ('offset', 0.0), ])) self.config.add_extra( '_help', { 'freq_model': 'default freq. measurement model', 'interval': 'sampling time interval', 'offset': 'sampling time offset' }) self.config.add_extra('_alt', {'freq_model': ('BusFreq', )}) self.area = ExtParam(model='Bus', src='area', indexer=self.bus, export=False) self.busf.model = self.config.freq_model self.busfreq = DeviceFinder(self.busf, link=self.bus, idx_name='bus') self.f = ExtAlgeb( model='FreqMeasurement', src='f', indexer=self.busfreq, export=False, info='Bus frequency', ) self.fs = Sampling( self.f, interval=self.config.interval, offset=self.config.offset, tex_name='f_s', info='Sampled freq.', ) self.ace = Algeb( info='area control error', unit='MW (p.u.)', tex_name='ace', e_str='10 * bias * (fs_v - 1) - ace', )
def __init__(self, system, config): ACEData.__init__(self) Model.__init__(self, system, config) self.flags.tds = True self.group = 'Calculation' 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.area = ExtParam(model='Bus', src='area', indexer=self.bus, export=False) self.busf.model = self.config.freq_model self.busfreq = DeviceFinder(self.busf, link=self.bus, idx_name='bus', default_model='BusFreq') self.imva = ConstService(v_str='1/sys_mva', info='reciprocal of system mva', tex_name='1/S_{b, sys}') self.f = ExtAlgeb(model='FreqMeasurement', src='f', indexer=self.busfreq, export=False, info='Bus frequency', unit='p.u. (Hz)') self.ace = Algeb( info='area control error', unit='p.u. (MW)', tex_name='ace', e_str='10 * (bias * imva) * sys_f * (f - 1) - ace', )
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'Exciter' self.flags.tds = True # from synchronous generators, get Sn, Vn, bus; tm0; omega self.Sn = ExtParam( src='Sn', model='SynGen', indexer=self.syn, tex_name='S_m', info='Rated power from generator', unit='MVA', export=False, ) self.Vn = ExtParam( src='Vn', model='SynGen', indexer=self.syn, tex_name='V_m', info='Rated voltage from generator', unit='kV', export=False, ) self.vf0 = ExtService(src='vf', model='SynGen', indexer=self.syn, tex_name='v_{f0}', info='Steady state excitation voltage') self.bus = ExtParam( src='bus', model='SynGen', indexer=self.syn, tex_name='bus', info='Bus idx of the generators', export=False, vtype=str, ) self.omega = ExtState( src='omega', model='SynGen', indexer=self.syn, tex_name=r'\omega', info='Generator speed', ) self.vf = ExtAlgeb( src='vf', model='SynGen', indexer=self.syn, tex_name=r'v_f', e_str='u * (vout - vf0)', info='Excitation field voltage to generator', ) self.XadIfd = ExtAlgeb( src='XadIfd', model='SynGen', indexer=self.syn, tex_name=r'X_{ad}I_{fd}', info='Armature excitation current', ) # from bus, get a and v 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', ) # output excitation voltage self.vout = Algeb( info='Exciter final output voltage', tex_name='v_{out}', v_str='vf0', )
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 / M) * (tm - te - D * (omega - 1))') # 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)') 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)') # 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') # 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', )
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'Calculation' self.flags.update({'tds': True}) self.SynGen = BackRef(info='Back reference to SynGen idx') self.SynGenIdx = RefFlatten(ref=self.SynGen) self.M = ExtParam( model='SynGen', src='M', indexer=self.SynGenIdx, export=False, info='Linearly stored SynGen.M', ) self.wgen = ExtState( model='SynGen', src='omega', indexer=self.SynGenIdx, tex_name=r'\omega_{gen}', info='Linearly stored SynGen.omega', ) self.agen = ExtState( model='SynGen', src='delta', indexer=self.SynGenIdx, tex_name=r'\delta_{gen}', info='Linearly stored SynGen.delta', ) self.d0 = ExtService( model='SynGen', src='delta', indexer=self.SynGenIdx, tex_name=r'\delta_{gen,0}', info='Linearly stored initial delta', ) self.a0 = ExtService( model='SynGen', src='omega', indexer=self.SynGenIdx, tex_name=r'\omega_{gen,0}', info='Linearly stored initial omega', ) self.Mt = NumReduce( u=self.M, tex_name='M_t', fun=np.sum, ref=self.SynGen, info='Summation of M by COI index', ) self.Mr = NumRepeat( u=self.Mt, tex_name='M_{tr}', ref=self.SynGen, info='Repeated summation of M', ) self.Mw = ConstService(tex_name='M_w', info='Inertia weights', v_str='M/Mr') self.d0w = ConstService(tex_name=r'\delta_{gen,0,w}', v_str='d0 * Mw', info='Linearly stored weighted delta') self.a0w = ConstService(tex_name=r'\omega_{gen,0,w}', v_str='a0 * Mw', info='Linearly stored weighted omega') self.d0a = NumReduce( u=self.d0w, tex_name=r'\delta_{gen,0,avg}', fun=np.sum, ref=self.SynGen, info='Average initial delta', cache=False, ) self.a0a = NumReduce( u=self.a0w, tex_name=r'\omega_{gen,0,avg}', fun=np.sum, ref=self.SynGen, info='Average initial omega', cache=False, ) self.pidx = IdxRepeat(u=self.idx, ref=self.SynGen, info='Repeated COI.idx') # Note: # Even if d(omega) /d (omega) = 1, it is still stored as a lambda function. # When no SynGen is referencing any COI, j_update will not be called, # and Jacobian will become singular. `diag_eps = True` needs to be used. # Note: # Do not assign `v_str=1` for `omega`. Otherwise, COIs with no connected generators will # fail to initialize. self.omega = Algeb( tex_name=r'\omega_{coi}', info='COI speed', v_str='a0a', v_setter=True, e_str='-omega', diag_eps=True, ) self.delta = Algeb( tex_name=r'\delta_{coi}', info='COI rotor angle', v_str='d0a', v_setter=True, e_str='-delta', diag_eps=True, ) # Note: # `omega_sub` or `delta_sub` must not provide `v_str`. # Otherwise, values will be incorrectly summed for `omega` and `delta`. self.omega_sub = ExtAlgeb( model='COI', src='omega', e_str='Mw * wgen', indexer=self.pidx, info='COI frequency contribution of each generator') self.delta_sub = ExtAlgeb( model='COI', src='delta', e_str='Mw * agen', indexer=self.pidx, info='COI angle contribution of each generator')
def __init__(self, system, config): super().__init__(system, config) self.group = 'SynGen' self.flags.update({ 'tds': True, 'nr_iter': False, }) # 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 / M) * (tm - te - D * (omega - 1))') # 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)') 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)') # algebraic variables # Need to be provided by specific generator models self.Id = Algeb(info='d-axis current', v_str='Id0', tex_name=r'I_d', e_str='') # to be completed by subclasses self.Iq = Algeb(info='q-axis current', v_str='Iq0', tex_name=r'I_q', e_str='') # to be completed self.vd = Algeb( info='d-axis voltage', v_str='vd0', e_str='v * sin(delta - a) - vd', tex_name=r'V_d', ) self.vq = Algeb( info='q-axis voltage', v_str='vq0', e_str='v * cos(delta - a) - vq', tex_name=r'V_q', ) self.tm = Algeb(info='mechanical torque', tex_name=r'\tau_m', v_str='tm0', v_setter=True, e_str='tm0 - tm') self.te = Algeb( info='electric torque', tex_name=r'\tau_e', v_str='p0', v_setter=True, e_str='psid * Iq - psiq * Id - te', ) self.vf = Algeb(info='excitation voltage', unit='pu', v_str='vf0', v_setter=True, e_str='vf0 - vf', tex_name=r'v_f') self.subidx = ExtParam( model='StaticGen', src='subidx', indexer=self.gen, tex_name='idx_{sub}', export=False, ) # ----------service consts for initialization---------- self.p0 = ExtService( model='StaticGen', src='p', indexer=self.gen, tex_name='P_0', ) self.q0 = ExtService( model='StaticGen', src='q', indexer=self.gen, tex_name='Q_0', )
def __init__(self, system, config): super().__init__(system, config) self.group = 'PSS' self.flags.update({'tds': True}) self.VCUr = Replace(self.VCU, lambda x: np.equal(x, 0.0), 999) self.VCLr = Replace(self.VCL, lambda x: np.equal(x, 0.0), -999) # retrieve indices of connected generator, bus, and bus freq self.syn = ExtParam(model='Exciter', src='syn', indexer=self.avr, export=False, info='Retrieved generator idx', dtype=str) self.bus = ExtParam( model='SynGen', src='bus', indexer=self.syn, export=False, info='Retrieved bus idx', dtype=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 SynGen self.Sn = ExtParam(model='SynGen', src='Sn', indexer=self.syn, tex_name='S_n', info='Generator power base', export=False) self.omega = ExtState( model='SynGen', src='omega', indexer=self.syn, tex_name=r'\omega', info='Generator speed', unit='p.u.', ) self.tm0 = ExtService( model='SynGen', src='tm', indexer=self.syn, tex_name=r'\tau_{m0}', info='Initial mechanical input', ) self.tm = ExtAlgeb( model='SynGen', src='tm', indexer=self.syn, tex_name=r'\tau_m', info='Generator mechanical input', ) self.te = ExtAlgeb( model='SynGen', src='te', indexer=self.syn, tex_name=r'\tau_e', info='Generator electrical output', ) # from Bus self.v = ExtAlgeb( model='Bus', src='v', indexer=self.buss, tex_name=r'V', info='Bus (or busr, if given) terminal voltage', ) 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') # from Exciter self.vi = ExtAlgeb(model='Exciter', src='vi', indexer=self.avr, tex_name='v_i', info='Exciter input voltage', e_str='u * vsout') self.vsout = Algeb( info='PSS output voltage to exciter', tex_name='v_{sout}', ) # `self.vsout.e_str` to be provided by specific models
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, add_sn=True, add_tm0=True): Model.__init__(self, system, config) self.group = 'TurbineGov' self.flags.update({'tds': True}) self.Sg = ExtParam( src='Sn', model='SynGen', indexer=self.syn, tex_name='S_n', info='Rated power from generator', unit='MVA', export=False, ) if add_sn is True: self.Sn = NumSelect( self.Tn, fallback=self.Sg, tex_name='S_n', info='Turbine or Gen rating', ) self.Vn = ExtParam( src='Vn', model='SynGen', indexer=self.syn, tex_name='V_n', info='Rated voltage from generator', unit='kV', export=False, ) # Note: changing `tm0` is not allowed in any time!! if add_tm0 is True: self.tm0 = ExtService(src='tm', model='SynGen', indexer=self.syn, tex_name=r'\tau_{m0}', info='Initial mechanical input') self.omega = ExtState(src='omega', model='SynGen', indexer=self.syn, tex_name=r'\omega', info='Generator speed', unit='p.u.') # Note: changing `paux0` is allowed. # It is a way how one can input from external programs such as reinforcement learning. self.paux0 = ConstService(v_str='0', tex_name='P_{aux0}', info='const. auxiliary input') self.tm = ExtAlgeb( src='tm', model='SynGen', indexer=self.syn, tex_name=r'\tau_m', e_str='u * (pout - tm0)', info='Mechanical power interface to SynGen', ) # `paux` must be zero upon initialization self.paux = Algeb( info='Auxiliary power input', tex_name='P_{aux}', v_str='paux0', e_str='paux0 - paux', ) self.pout = Algeb( info='Turbine final output power', tex_name='P_{out}', v_str='u*tm0', ) self.wref = Algeb( info='Speed reference variable', tex_name=r'\omega_{ref}', v_str='wref0', e_str='wref0 - wref', )
def __init__(self, system, config): super().__init__(system, config) self.group = 'PSS' self.flags.update({'tds': True}) self.syn = ExtParam(model='Exciter', src='syn', indexer=self.avr, export=False, info='Retrieved generator idx') self.bus = ExtParam(model='SynGen', src='bus', indexer=self.syn, export=False, info='Retrieved bus idx') self.Sn = ExtParam(model='SynGen', src='Sn', indexer=self.syn, tex_name='S_n', info='Generator power base', export=False) # from SynGen self.omega = ExtState( model='SynGen', src='omega', indexer=self.syn, tex_name=r'\omega', info='Generator speed', unit='p.u.', ) self.tm0 = ExtService( model='SynGen', src='tm', indexer=self.syn, tex_name=r'\tau_{m0}', info='Initial mechanical input', ) self.tm = ExtAlgeb( model='SynGen', src='tm', indexer=self.syn, tex_name=r'\tau_m', info='Generator mechanical input', ) self.te = ExtAlgeb( model='SynGen', src='te', indexer=self.syn, tex_name=r'\tau_e', info='Generator electrical output', ) self.vf = ExtAlgeb(model='SynGen', src='vf', indexer=self.syn, tex_name='v_f', info='Generator excitation voltage', e_str='u * vsout') # from Bus #TODO: implement the optional BUSR self.v = ExtAlgeb( model='Bus', src='v', indexer=self.bus, tex_name=r'V', info='Bus (or BUSR, if given) terminal voltage', ) # from Exciter self.vsout = Algeb( info='PSS output voltage to exciter', tex_name='v_{sout}', ) # `e_str` to be provided by specific models
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'Exciter' self.flags.tds = True # Voltage compensator idx-es self.VoltComp = BackRef() # from synchronous generators, get u, Sn, Vn, bus; tm0; omega self.ug = ExtParam( src='u', model='SynGen', indexer=self.syn, tex_name='u_g', info='Generator online status', unit='bool', export=False, ) self.ue = ConstService( v_str='u * ug', info="effective online status", tex_name='u_e', ) self.Sn = ExtParam( src='Sn', model='SynGen', indexer=self.syn, tex_name='S_m', info='Rated power from generator', unit='MVA', export=False, ) self.Vn = ExtParam( src='Vn', model='SynGen', indexer=self.syn, tex_name='V_m', info='Rated voltage from generator', unit='kV', export=False, ) self.vf0 = ExtService(src='vf', model='SynGen', indexer=self.syn, tex_name='v_{f0}', info='Steady state excitation voltage') self.bus = ExtParam( src='bus', model='SynGen', indexer=self.syn, tex_name='bus', info='Bus idx of the generators', export=False, vtype=str, ) self.omega = ExtState( src='omega', model='SynGen', indexer=self.syn, tex_name=r'\omega', info='Generator speed', is_input=True, ) self.vf = ExtAlgeb( src='vf', model='SynGen', indexer=self.syn, tex_name=r'v_f', e_str='ue * (vout - vf0)', info='Excitation field voltage to generator', ename='vf', tex_ename='v_f', is_input=False, ) self.XadIfd = ExtAlgeb( src='XadIfd', model='SynGen', indexer=self.syn, tex_name=r'X_{ad}I_{fd}', info='Armature excitation current', is_input=True, ) # from bus, get a and v self.a = ExtAlgeb( model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', info='Bus voltage phase angle', is_input=True, ) self.vbus = ExtAlgeb(model='Bus', src='v', indexer=self.bus, tex_name='V', info='Bus voltage magnitude', is_input=True) # `self.v` is actually `ETERM` in other software # TODO: # Preferably, its name needs to be changed to `eterm`. # That requires updates in equations of all exciters. self.v = Algeb( info='Input to exciter (bus v or Eterm)', tex_name='E_{term}', v_str='vbus', e_str='vbus - v', v_str_add=True, ) # output excitation voltage self.vout = Algeb( info='Exciter final output voltage', tex_name='v_{out}', v_str='ue * vf0', diag_eps=True, is_output=True, )
def __init__(self, system, config): Model.__init__(self, system, config) self.flags.tds = True self.group = 'DGProtection' self.bus = ExtParam(model='DG', src='bus', indexer=self.dev, export=False) self.fn = ExtParam(model='DG', src='fn', indexer=self.dev, export=False) # -- Frequency protection # Convert frequency deviation range to p.u. self.f = ExtAlgeb(export=False, info='DG frequency read value', unit='p.u.', model='FreqMeasurement', src='f', indexer=self.busfreq, ) self.fHz = Algeb(v_str='fn * f', e_str='fn * f - fHz', info='frequency in Hz', tex_name=r'f_{Hz}', ) # -- Lock DG frequency signal and output power self.ltu = ConstService(v_str='0.8') self.ltl = ConstService(v_str='0.2') # `Ldsum_zu` is `ue` dsum = 'fen * (IAWfl1_lim_zu * Lfl1_zi + IAWfl2_lim_zu * Lfl2_zi + ' \ 'IAWfu1_lim_zu * Lfu1_zi + IAWfu2_lim_zu * Lfu2_zi) + ' \ 'Ven * (IAWVl1_lim_zu * LVl1_zi + IAWVl2_lim_zu * LVl2_zi + ' \ 'IAWVl3_lim_zu * LVl3_zi + ' \ 'IAWVu1_lim_zu * LVu1_zi + IAWVu2_lim_zu * LVu2_zi) - ' \ 'dsum' self.dsum = Algeb(v_str='0', e_str=dsum, info='lock signal summation', tex_name=r'd_{tot}', ) self.Ldsum = Limiter(u=self.dsum, lower=self.ltl, upper=self.ltu, info='lock signal comparer, zu is to act', equal=False, no_warn=True, ) self.ue = Algeb(v_str='0', e_str='Ldsum_zu - ue', info='lock flag', tex_name=r'ue', ) self.zero = ConstService('0') self.res = ExtendedEvent(self.ue, t_ext=self.Tres, trig="rise", extend_only=True) # lock DG frequency signal # fflag option 1: leave source signal online in protection self.fin = ExtAlgeb(model='DG', src='f', indexer=self.dev, info='original f from DG', ) self.fHzl = ExtAlgeb(model='DG', src='fHz', indexer=self.dev, export=False, e_str='- ue * (fn * f)', info='Frequency measure lock', ename='fHzl', tex_ename='f_{Hzl}', ) # TODO: add fflag option 2: block the source signal in protection # lock output power of DG self.Pext = ExtAlgeb(model='DG', src='Pext', indexer=self.dev, info='original Pext from DG', ) self.Pref = ExtAlgeb(model='DG', src='Pref', indexer=self.dev, info='original Pref from DG', ) self.Pdrp = ExtAlgeb(model='DG', src='DB_y', indexer=self.dev, info='original Pdrp from DG', ) self.Psum = ExtAlgeb(model='DG', src='Psum', indexer=self.dev, export=False, e_str='- ue * (Pext + Pref + Pdrp)', info='Active power lock', ename='Pneg', tex_ename='P_{neg}', ) self.Qdrp = ExtAlgeb(model='DG', src='Qdrp', indexer=self.dev, info='original Qdrp from DG', ) self.Qref = ExtAlgeb(model='DG', src='Qref', indexer=self.dev, info='original Qref from DG', ) self.Qsum = ExtAlgeb(model='DG', src='Qsum', indexer=self.dev, export=False, e_str='- ue * (Qdrp + Qref)', info='Reactive power lock', ename='Qneg', tex_ename='Q_{neg}', )
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'DynLoad' self.flags.tds = True self.kps = ConstService( v_str='kpp + kpi + kpz', tex_name='K_{psum}', ) self.kqs = ConstService( v_str='kqp + kqi + kqz', tex_name='K_{qsum}', ) self.kpc = InitChecker( u=self.kps, equal=100.0, tex_name='K_{pc}', info='total `kp` and 100', ) self.kqc = InitChecker( u=self.kqs, equal=100.0, tex_name='K_{qc}', info='total `kq` and 100', ) # convert percentages to decimals self.rpp = ConstService( v_str='u * kpp / 100', tex_name='r_{pp}', ) self.rpi = ConstService( v_str='u * kpi / 100', tex_name='r_{pi}', ) self.rpz = ConstService( v_str='u * kpz / 100', tex_name='r_{pz}', ) self.rqp = ConstService( v_str='u * kqp / 100', tex_name='r_{qp}', ) self.rqi = ConstService( v_str='u * kqi / 100', tex_name='r_{qi}', ) self.rqz = ConstService( v_str='u * kqz / 100', tex_name='r_{qz}', ) self.bus = ExtParam( model='PQ', src='bus', indexer=self.pq, info='retrieved bux idx', export=False, ) self.p0 = ExtService( model='PQ', src='Ppf', indexer=self.pq, tex_name='P_0', ) self.q0 = ExtService( model='PQ', src='Qpf', indexer=self.pq, tex_name='Q_0', ) self.v0 = ExtService( model='Bus', src='v', indexer=self.bus, tex_name='V_0', ) # calculate initial powers, equivalent current, and equivalent z self.pp0 = ConstService( v_str='p0 * rpp', tex_name='P_{p0}', ) self.pi0 = ConstService( v_str='p0 * rpi / v0', tex_name='P_{i0}', ) self.pz0 = ConstService( v_str='p0 * rpz / v0 / v0', tex_name='P_{z0}', ) self.qp0 = ConstService( v_str='q0 * rqp', tex_name='Q_{p0}', ) self.qi0 = ConstService( v_str='q0 * rqi / v0', tex_name='Q_{i0}', ) self.qz0 = ConstService( v_str='q0 * rqz / v0 / v0', tex_name='Q_{z0}', ) self.a = ExtAlgeb( model='Bus', src='a', indexer=self.bus, tex_name=r'\theta', e_str='pp0 + pi0*v + pz0*v*v', ) self.v = ExtAlgeb( model='Bus', src='v', indexer=self.bus, tex_name='V', e_str='qp0 + qi0*v + qz0*v*v', )
def __init__(self, system, config): Model.__init__(self, system, config) self.group = 'VoltComp' self.flags.tds = True # retrieve indices of connected generator, bus, and bus freq self.syn = ExtParam(model='Exciter', src='syn', indexer=self.avr, export=False, info='Retrieved generator idx', vtype=str) self.vf0 = ExtService(src='vf', model='SynGen', indexer=self.syn, tex_name=r'v_{f0}', info='Steady state excitation voltage') # from Bus self.v = ExtAlgeb( model='SynGen', src='v', indexer=self.syn, tex_name=r'V', info='Retrieved bus terminal voltage', ) # 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.vct = VarService( tex_name=r'V_{CT}', v_str='u * Abs((vd + 1j*vq) + (rc + 1j * xc) * (Id + 1j*Iq))', ) # output voltage. # `vcomp` is the additional voltage to be added to bus terminal voltage self.vcomp = Algeb( info='Compensator output voltage to exciter', tex_name=r'v_{comp}', v_str='vct - u * v', e_str='vct - u * v - vcomp', ) # do not need to interface to exciters here. # Let the exciters pick up `vcomp` through back referencing self.Eterm = ExtAlgeb( model='Exciter', src='v', indexer=self.avr, v_str='vcomp', e_str='vcomp', )