Ejemplo n.º 1
0
    def __init__(self, E1, SE1, E2, SE2, name=None, tex_name=None, info=None):
        Block.__init__(self, name=name, tex_name=tex_name, info=info)

        self._E1 = dummify(E1)
        self._E2 = dummify(E2)
        self._SE1 = SE1
        self._SE2 = SE2

        self.zSE2 = FlagValue(self._SE2,
                              value=0.,
                              info='Flag non-zeros in SE2',
                              tex_name='z^{SE2}')

        # data correction for E1, E2, SE1 (TODO)
        self.E1 = ConstService(
            tex_name='E^{1c}',
            info='Corrected E1 data',
        )
        self.E2 = ConstService(
            tex_name='E^{2c}',
            info='Corrected E2 data',
        )
        self.SE1 = ConstService(
            tex_name='SE^{1c}',
            info='Corrected SE1 data',
        )
        self.SE2 = ConstService(
            tex_name='SE^{2c}',
            info='Corrected SE2 data',
        )
        self.a = ConstService(
            info='Intermediate Sat coeff',
            tex_name='a',
        )
        self.A = ConstService(
            info='Saturation start',
            tex_name='A^q',
        )
        self.B = ConstService(
            info='Saturation gain',
            tex_name='B^q',
        )
        self.vars = {
            'E1': self.E1,
            'E2': self.E2,
            'SE1': self.SE1,
            'SE2': self.SE2,
            'zSE2': self.zSE2,
            'a': self.a,
            'A': self.A,
            'B': self.B,
        }
Ejemplo n.º 2
0
    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'
Ejemplo n.º 3
0
    def __init__(self, system, config):
        ExcBase.__init__(self, system, config)

        # 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',
        )
        # Saturation
        self.SAT = ExcQuadSat(
            self.E1,
            self.SE1,
            self.E2,
            self.SE2,
            info='Field voltage saturation',
        )

        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.vr0 = ConstService(info='Initial vr',
                                tex_name='V_{r0}',
                                v_str='(KE + Se0) * vf0')
        self.vb0 = ConstService(info='Initial vb',
                                tex_name='V_{b0}',
                                v_str='vr0 / KA')
        self.vref0 = ConstService(
            info='Initial reference voltage input',
            tex_name='V_{ref0}',
            v_str='v + vb0',
        )
        self.vfe0 = ConstService(
            v_str='vf0 * (KE + Se0)',
            tex_name='V_{FE0}',
        )

        self.vref = Algeb(info='Reference voltage input',
                          tex_name='V_{ref}',
                          unit='p.u.',
                          v_str='vref0',
                          e_str='vref0 - vref')

        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.',
            e_str='-LG_y + vref - vi',
            v_str='-v + vref',
        )
        self.LA = LagAntiWindup(
            u='vi + WF_y',
            T=self.TA,
            K=self.KA,
            upper=self.VRMAXc,
            lower=self.VRMIN,
            info='Anti-windup lag',
        )
        self.VFE = Algeb(info='Combined saturation feedback',
                         tex_name='V_{FE}',
                         unit='p.u.',
                         v_str='vfe0',
                         e_str='INT_y * (KE + Se) - VFE')

        self.INT = Integrator(
            u='LA_y - VFE',
            T=self.TE,
            K=1,
            y0=self.vf0,
            info='Integrator',
        )

        self.SL = LessThan(u=self.vout,
                           bound=self.SAT_A,
                           equal=False,
                           enable=True,
                           cache=False)

        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.WF = Washout(u=self.vout,
                          T=self.TF,
                          K=self.KF,
                          info='Stablizing circuit feedback')

        self.vout.e_str = 'INT_y - vout'
Ejemplo n.º 4
0
    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'
Ejemplo n.º 5
0
    def __init__(self, E1, SE1, E2, SE2, name=None, tex_name=None, info=None):
        Block.__init__(self, name=name, tex_name=tex_name, info=info)

        self._E1 = E1
        self._E2 = E2
        self._SE1 = SE1
        self._SE2 = SE2

        self.zE1 = FlagValue(
            self._E1,
            value=0.,
            info='Flag non-zeros in E1',
            tex_name='z^{E1}',
        )
        self.zE2 = FlagValue(
            self._E2,
            value=0.,
            info='Flag non-zeros in E2',
            tex_name='z^{E2}',
        )
        self.zSE1 = FlagValue(
            self._SE1,
            value=0.,
            info='Flag non-zeros in SE1',
            tex_name='z^{SE1}',
        )
        self.zSE2 = FlagValue(self._SE2,
                              value=0.,
                              info='Flag non-zeros in SE2',
                              tex_name='z^{SE2}')

        # disallow E1 = E2 != 0 since the curve fitting will fail
        self.E12c = InitChecker(
            self._E1,
            not_equal=self._E2,
            info='E1 and E2 after correction',
            error_out=True,
        )

        # data correction for E1, E2, SE1
        self.E1 = ConstService(
            tex_name='E^{1c}',
            info='Corrected E1 data',
        )
        self.E2 = ConstService(
            tex_name='E^{2c}',
            info='Corrected E2 data',
        )
        self.SE1 = ConstService(
            tex_name='SE^{1c}',
            info='Corrected SE1 data',
        )
        self.SE2 = ConstService(
            tex_name='SE^{2c}',
            info='Corrected SE2 data',
        )
        self.A = ConstService(
            info='Saturation gain',
            tex_name='A^e',
        )
        self.B = ConstService(
            info='Exponential coef. in saturation',
            tex_name='B^e',
        )
        self.vars = {
            'E1': self.E1,
            'E2': self.E2,
            'SE1': self.SE1,
            'SE2': self.SE2,
            'zE1': self.zE1,
            'zE2': self.zE2,
            'zSE1': self.zSE1,
            'zSE2': self.zSE2,
            'A': self.A,
            'B': self.B,
        }
Ejemplo n.º 6
0
    def __init__(self, system, config):
        ExcBase.__init__(self, system, config)

        # 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='Transducer delay',
        )

        self.SAT = ExcQuadSat(
            self.E1,
            self.SE1,
            self.E2,
            self.SE2,
            info='Field voltage saturation',
        )

        self.Se0 = ConstService(
            tex_name='S_{e0}',
            v_str='(vf0>SAT_A) * SAT_B*(SAT_A-vf0) ** 2 / vf0',
        )

        self.vfe0 = ConstService(
            v_str='vf0 * (KE + Se0)',
            tex_name='V_{FE0}',
        )
        self.vref0 = ConstService(
            info='Initial reference voltage input',
            tex_name='V_{ref0}',
            v_str='v + vfe0 / KA',
        )

        self.vref = Algeb(info='Reference voltage input',
                          tex_name='V_{ref}',
                          unit='p.u.',
                          v_str='vref0',
                          e_str='vref0 - vref')

        self.vi = Algeb(
            info='Total input voltages',
            tex_name='V_i',
            unit='p.u.',
            v_str='vref0 - v',
            e_str='(vref - v - WF_y) - vi',
        )

        self.LL = LeadLag(
            u=self.vi,
            T1=self.TC,
            T2=self.TB,
            info='Lead-lag compensator',
            zero_out=True,
        )

        self.UEL = Algeb(info='Interface var for under exc. limiter',
                         tex_name='U_{EL}',
                         v_str='0',
                         e_str='0 - UEL')

        self.HG = HVGate(
            u1=self.UEL,
            u2=self.LL_y,
            info='HVGate for under excitation',
        )

        self.VRU = VarService(
            v_str='VRMAXc * v',
            tex_name='V_T V_{RMAX}',
        )
        self.VRL = VarService(
            v_str='VRMIN * v',
            tex_name='V_T V_{RMIN}',
        )

        # TODO: WARNING: HVGate is temporarily skipped
        self.LA = LagAntiWindup(
            u=self.LL_y,
            T=self.TA,
            K=self.KA,
            upper=self.VRU,
            lower=self.VRL,
            info='Anti-windup lag',
        )  # LA_y == VR

        # `LessThan` may be causing memory issue in (SL_z0 * vout) - uncertain yet
        self.SL = LessThan(u=self.vout,
                           bound=self.SAT_A,
                           equal=False,
                           enable=True,
                           cache=False)

        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='vfe0',
                         e_str='INT_y * (KE + Se) - VFE')

        self.INT = Integrator(
            u='LA_y - VFE',
            T=self.TE,
            K=1,
            y0=self.vf0,
            info='Integrator',
        )

        self.WF = Washout(u=self.INT_y,
                          T=self.TF1,
                          K=self.KF,
                          info='Feedback to input')

        self.vout.e_str = 'INT_y - vout'
Ejemplo n.º 7
0
    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)'
Ejemplo n.º 8
0
    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='-v + vref',
            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

        self.zero = ConstService(v_str='0.0')
        self.one = ConstService(v_str='1.0')

        self.LA1 = LagAntiWindup(
            u='ue * (LA3_y + V4)',
            T=self.TE,
            K=self.one,
            D=self.KE,
            upper=self.VBMAX,
            lower=self.zero,
            info=r'E_{FD}, vout, Lag Anti-Windup',
        )  # LA1_y is final output

        self.WF = Washout(u=self.LA1_y,
                          T=self.TF,
                          K=self.KF,
                          info='V_F, stablizing circuit feedback, washout')

        self.SQE = VarService(
            tex_name=r'SQE',
            info=r'Square Error',
            v_str='VE ** 2 - (0.78 * XadIfd) ** 2',
        )

        self.SL = LessThan(u=self.zero,
                           bound=self.SQE,
                           equal=False,
                           enable=True,
                           cache=False)

        self.V4 = VarService(
            tex_name='V_4',
            v_str='SL_z1 * sqrt(SQE)',
        )

        self.vout.e_str = 'ue * (LA1_y - vout)'