def __init__(self, u, T1, T2, T3, T4, zero_out=False, name=None, tex_name=None, info=None): super(LeadLag2ndOrd, self).__init__(name=name, tex_name=tex_name, info=info) self.u = dummify(u) self.T1 = dummify(T1) self.T2 = dummify(T2) self.T3 = dummify(T3) self.T4 = dummify(T4) self.zero_out = zero_out self.enforce_tex_name((self.T1, self.T2, self.T3, self.T4)) self.x1 = State(info='State #1 in 2nd order lead-lag', tex_name="x'", t_const=self.T2) self.x2 = State(info='State #2 in 2nd order lead-lag', tex_name="x''") self.y = Algeb(info='Output of 2nd order lead-lag', tex_name='y', diag_eps=1e-6) self.vars = {'x1': self.x1, 'x2': self.x2, 'y': self.y} if self.zero_out is True: self.LT1 = LessThan(T1, dummify(0), equal=True, enable=zero_out, tex_name='LT', cache=True, z0=1, z1=0) self.LT2 = LessThan(T2, dummify(0), equal=True, enable=zero_out, tex_name='LT', cache=True, z0=1, z1=0) self.LT3 = LessThan(T4, dummify(0), equal=True, enable=zero_out, tex_name='LT', cache=True, z0=1, z1=0) self.LT4 = LessThan(T4, dummify(0), equal=True, enable=zero_out, tex_name='LT', cache=True, z0=1, z1=0) self.x2.discrete = (self.LT1, self.LT2, self.LT3, self.LT4) self.vars['LT1'] = self.LT1 self.vars['LT2'] = self.LT2 self.vars['LT3'] = self.LT3 self.vars['LT4'] = self.LT4
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, u, T1, T2, K=1, zero_out=True, name=None, tex_name=None, info=None): super().__init__(name=name, tex_name=tex_name, info=info) self.u = dummify(u) self.T1 = dummify(T1) self.T2 = dummify(T2) self.K = dummify(K) self.zero_out = zero_out self.enforce_tex_name((self.T1, self.T2)) self.x = State(info='State in lead-lag', tex_name="x'", t_const=self.T2) self.y = Algeb(info='Output of lead-lag', tex_name=r'y', diag_eps=1e-6) self.vars = {'x': self.x, 'y': self.y} if self.zero_out is True: self.LT1 = LessThan(T1, dummify(0), equal=True, enable=zero_out, tex_name='LT', cache=True, z0=1, z1=0) self.LT2 = LessThan(T2, dummify(0), equal=True, enable=zero_out, tex_name='LT', cache=True, z0=1, z1=0) self.x.discrete = (self.LT1, self.LT2) self.vars['LT1'] = self.LT1 self.vars['LT2'] = self.LT2
def __init__(self, u, T, K, name=None, zero_out=True, tex_name=None, info=None): super().__init__(u, T, K, name=name, tex_name=tex_name, info=info) self.zero_out = zero_out self.LT = LessThan(K, dummify(0), equal=True, enable=zero_out, tex_name='LT', cache=True, z0=1, z1=0) self.vars.update({'LT': self.LT})
def __init__(self): self.SAT = ExcQuadSat( self.E1, self.SE1, self.E2, self.SE2, info='Field voltage saturation', ) # Input (VR - VFE) self.INT = Integrator( u=self.INTin, T=self.TE, K=1, y0=0, info='V_E, integrator', ) self.INT.y.v_str = 0.1 self.INT.y.v_iter = 'INT_y * FEX_y - vf0' self.SL = LessThan(u=self.INT_y, bound=self.SAT_A, equal=False, enable=True, cache=False) self.Se = Algeb( tex_name=r"V_{out}*S_e(|V_{out}|)", info='saturation output', v_str='Indicator(INT_y > SAT_A) * SAT_B * (INT_y - SAT_A) ** 2', e_str='ue * (SL_z0 * (INT_y - SAT_A) ** 2 * SAT_B - Se)', diag_eps=True, ) self.VFE = Algeb( info='Combined saturation feedback', tex_name='V_{FE}', unit='p.u.', v_str='INT_y * KE + Se + XadIfd * KD', e_str='ue * (INT_y * KE + Se + XadIfd * KD - VFE)', diag_eps=True, )
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 = FlagValue(self.S12, value=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', vtype=np.complex) self._S = ConstService(v_str='p0 - 1j * q0', tex_name='S', info='complex terminal power', vtype=np.complex) self._Zs = ConstService(v_str='ra + 1j * xd2', tex_name='Z_s', info='equivalent impedance', vtype=np.complex) self._It = ConstService(v_str='_S / conj(_V)', tex_name='I_t', info='complex terminal current', vtype=np.complex) self._Is = ConstService(tex_name='I_s', v_str='_It + _V / _Zs', info='equivalent current source', vtype=np.complex) self.psi20 = ConstService( tex_name=r"\psi''_0", v_str='_Is * _Zs', info='sub-transient flux linkage in stator reference', vtype=np.complex) 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= 'Indicator(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)', vtype=np.complex) self.psi20_dq = ConstService(tex_name=r"\psi''_{0,dq}", v_str='psi20 * _Tdq', vtype=np.complex) self.It_dq = ConstService(tex_name=r"I_{t,dq}", v_str='conj(_It * _Tdq)', vtype=np.complex) 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='u * 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='u * abs(psi20_dq)', e_str='psi2d **2 + psi2q ** 2 - psi2 ** 2', diag_eps=True, ) # `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='u * Se0', e_str='SL_z0 * (psi2 - SAT_A) ** 2 * SAT_B - psi2 * Se', diag_eps=True, ) # separated `XadIfd` from `e1q` using \dot(e1q) = (vf - XadIfd) / Td10 self.XadIfd.e_str = 'u * (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='u * e1q0', e_str='(-XadIfd + vf)', t_const=self.Td10, ) self.e1d = State( info='d-axis transient voltage', tex_name=r"e'_d", v_str='e1d0', e_str='-XaqI1q', t_const=self.Tq10, ) self.e2d = State( info='d-axis sub-transient voltage', tex_name=r"e''_d", v_str='u * e2d0', e_str='(-e2d + e1q - (xd1 - xl) * Id)', t_const=self.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)', t_const=self.Tq20, ) self.Iq.e_str += '+ xq2*Iq + psi2q' self.Id.e_str += '+ xd2*Id - psi2d'
def __init__(self): 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}") # Saturation services # when S10 = 0, S12 = 1, Saturation is disabled. Thus, Sat = 0, A = 1, B = 0 self.Sat = ConstService(v_str='sqrt((S10 * 1) / (S12 * 1.2))', tex_name=r"S_{at}") self.SA = ConstService(v_str='1.2 + 0.2 / (Sat - 1)', tex_name='S_A') self.SB = ConstService( v_str= '((Sat < 0) + (Sat > 0)) * 1.2 * S12 * ((Sat - 1) / 0.2) ** 2', tex_name='S_B') # internal voltage and rotor angle calculation # Initialization reference: OpenIPSL at # https://github.com/OpenIPSL/OpenIPSL/blob/master/OpenIPSL/Electrical/Machines/PSSE/GENROU.mo self._V = ConstService(v_str='v * exp(1j * a)', tex_name='V_c', info='complex terminal 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.psia0 = ConstService( tex_name=r"\psi_{a0}", v_str='_Is * _Zs', info='subtransient flux linkage in stator reference') self.psia0_arg = ConstService(tex_name=r"\theta_{\psi a0}", v_str='arg(psia0)') self.psia0_abs = ConstService(tex_name=r"|\psi_{a0}|", v_str='abs(psia0)') self._It_arg = ConstService(tex_name=r"\theta_{It0}", v_str='arg(_It)') self._psia0_It_arg = ConstService(tex_name=r"\theta_{\psi a It}", v_str='psia0_arg - _It_arg') self.Se0 = ConstService( tex_name=r"S_{e0}", v_str='(psia0_abs >= SA) * (psia0_abs - SA) ** 2 * SB / psia0_abs') self._a = ConstService(tex_name=r"a", v_str='psia0_abs + psia0_abs * 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(_psia0_It_arg) / (_b * sin(_psia0_It_arg) - _a)) + ' 'psia0_arg') self._Tdq = ConstService(tex_name=r"T_{dq}", v_str='cos(delta0) - 1j * sin(delta0)') self.psia0_dq = ConstService(tex_name=r"\psi_{a0,dq}", v_str='psia0 * _Tdq') self.It_dq = ConstService(tex_name=r"I_{t,dq}", v_str='conj(_It * _Tdq)') self.psiad0 = ConstService(tex_name=r"\psi_{ad0}", v_str='re(psia0_dq)') self.psiaq0 = ConstService(tex_name=r"\psi_{aq0}", v_str='im(psia0_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='-(psiaq0 - xq2*Iq0) - ra * Id0', tex_name=r'V_{d0}') self.vq0 = ConstService(v_str='psiad0 - 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)') self.vf0 = ConstService(tex_name=r'v_{f0}', v_str='(Se0 + 1)*psiad0 + (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*psiad0 + vf0') self.e1d0 = ConstService(tex_name="e'_{d0}", v_str='Iq0*(xq - xq1) + Se0*gqd*psiaq0') self.e2d0 = ConstService(tex_name="e''_{d0}", v_str='Id0*(xl - xd) - Se0*psiad0 + vf0') self.e2q0 = ConstService(tex_name="e''_{q0}", v_str='Iq0*(xl - xq) - Se0*gqd*psiaq0') # begin variables and equations self.psiaq = Algeb(tex_name=r"\psi_{aq}", info='q-axis air gap flux', v_str='psiaq0', e_str='psiq + xq2 * Iq - psiaq') self.psiad = Algeb(tex_name=r"\psi_{ad}", info='d-axis air gap flux', v_str='psiad0', e_str='-psiad + gd1 * e1q + gd2 * (xd1 - xl) * e2d') self.psia = Algeb(tex_name=r"\psi_{a}", info='air gap flux magnitude', v_str='abs(psia0_dq)', e_str='sqrt(psiad **2 + psiaq ** 2) - psia') self.Slt = LessThan(u=self.psia, bound=self.SA, equal=False, enable=True) self.Se = Algeb(tex_name=r"S_e(|\psi_{a}|)", info='saturation output', v_str='Se0', e_str='Slt_z0 * (psia - SA) ** 2 * SB / psia - Se') self.e1q = State( info='q-axis transient voltage', tex_name=r"e'_q", v_str='e1q0', e_str= '(-e1q - (xd - xd1) * (Id - gd2 * e2d - (1 - gd1) * Id + gd2 * e1q) - ' 'Se * psiad + vf) / Td10') self.e1d = State( info='d-axis transient voltage', tex_name=r"e'_d", v_str='e1d0', e_str= '(-e1d + (xq - xq1) * (Iq - gq2 * e2q - (1 - gq1) * Iq - gq2 * e1d) + ' 'Se * gqd * psiaq) / 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.Id.e_str += '+ xd2 * Id - gd1 * e1q - (1 - gd1) * e2d' self.Iq.e_str += '+ xq2 * Iq + gq1 * e1d - (1 - gq1) * e2q'
def __init__(self, system, config): ExcBase.__init__(self, system, config) self.flags.nr_iter = True self.SAT = ExcQuadSat(self.E1, self.SE1, self.E2, self.SE2, info='Field voltage saturation', ) self.SL = LessThan(u=self.vout, bound=self.SAT_A, equal=False, enable=True, cache=False) self.Se0 = ConstService(info='Initial saturation output', tex_name='S_{e0}', v_str='Indicator(vf0>SAT_A) * SAT_B * (SAT_A - vf0) ** 2 / vf0', ) self.IN = Algeb(tex_name='I_N', info='Input to FEX', v_str='1', v_iter='KC * XadIfd - INT_y * IN', e_str='KC * XadIfd / INT_y - 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.FEX.y.v_iter = '1' self.FEX.y.v_iter = self.FEX.y.e_str self.LG = Lag(self.v, T=self.TR, K=1, info='Voltage transducer', ) self.vi = Algeb(info='Total input voltages', tex_name='V_i', unit='p.u.', e_str='-v + vref - WF_y - vi', v_str='-v + vref', ) self.LL = LeadLag(u=self.vi, T1=self.TC, T2=self.TB, info='Regulator', zero_out=True, ) self.LA = LagAntiWindup(u=self.LL_y, T=self.TA, K=self.KA, lower=self.VRMIN, upper=self.VRMAX, info='Lag AW on VR', ) self.INT = Integrator(u='LA_y - VFE', T=self.TE, K=1, y0=0, info='Integrator', ) self.INT.y.v_str = 0.1 self.INT.y.v_iter = 'INT_y * FEX_y - vf0' 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='INT_y * (KE + Se) + XadIfd * KD', e_str='INT_y * (KE + Se) + XadIfd * KD - VFE' ) self.vref = Algeb(info='Reference voltage input', tex_name='V_{ref}', unit='p.u.', v_str='v + VFE / KA', e_str='vref0 - vref', ) self.vref0 = PostInitService(info='Initial reference voltage input', tex_name='V_{ref0}', v_str='vref', ) self.WF = Washout(u=self.VFE, T=self.TF, K=self.KF, info='Stablizing circuit feedback', ) self.vout.e_str = 'INT_y * FEX_y - vout'