Example #1
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.flags.tds = True
        self.group = 'RenGovernor'

        self.reg = ExtParam(model='RenExciter', src='reg', indexer=self.ree,
                            export=False,
                            )

        self.Sn = ExtParam(model='RenGen', src='Sn', indexer=self.reg,
                           tex_name='S_n', export=False,
                           )

        self.wge = ExtAlgeb(model='RenExciter', src='wg', indexer=self.ree,
                            export=False,
                            e_str='-1.0 + s1_y',
                            ename='wg',
                            tex_ename=r'\omega_g',
                            )

        self.Pe = ExtAlgeb(model='RenGen', src='Pe', indexer=self.reg, export=False,
                           info='Retrieved Pe of RenGen')

        self.Pe0 = ExtService(model='RenGen', src='Pe', indexer=self.reg, tex_name='P_{e0}',
                              )

        self.H2 = ConstService(v_str='2 * H', tex_name='2H')

        self.Pm = Algeb(tex_name='P_m',
                        info='Mechanical power',
                        e_str='Pe0 - Pm',
                        v_str='Pe0',
                        )

        self.wr0 = Algeb(tex_name=r'\omega_{r0}',
                         unit='p.u.',
                         v_str='w0',
                         e_str='w0 - wr0',
                         info='speed set point',
                         )

        # `s1_y` is `w_m`
        self.s1 = Integrator(u='(Pm - Pe) / wge - D * (s1_y - wr0)',
                             T=self.H2,
                             K=1.0,
                             y0='wr0',
                             )

        # make two alias states, `wt` and `wg`, pointing to `s1_y`
        self.wt = AliasState(self.s1_y, tex_name=r'\omega_t')

        self.wg = AliasState(self.s1_y, tex_name=r'\omega_g')

        self.s3_y = State(info='Unused state variable',
                          tex_name='y_{s3}',
                          )

        self.Kshaft = ConstService(v_str='1.0', tex_name='K_{shaft}',
                                   info='Dummy Kshaft',
                                   )
Example #2
0
    def __init__(self):
        self.udref0 = ConstService(tex_name=r'u_{dref0}',
                                   v_str='vd0 + ra*Id0 - xs*Iq0')
        self.uqref0 = ConstService(
            tex_name=r'u_{qref0}',
            v_str='vq0 + ra*Iq0 + xs*Id0',
        )

        # PIvd_y, PIvq_y are Idref, Iqref
        self.PIId = PIController(
            u='Id - PIvd_y',
            kp=self.KpId,
            ki=self.KiId,
        )
        self.PIIq = PIController(
            u='Iq - PIvq_y',
            kp=self.KpIq,
            ki=self.KiIq,
        )

        # udLag_y, uqLag_y are ud, uq
        self.Id.e_str = 'vd + ra*Id - xs*Iq - udLag_y'
        self.Iq.e_str = 'vq + ra*Iq + xs*Id - uqLag_y'

        self.udref = Algeb(
            tex_name=r'u_{dref}',
            info='ud reference',
            v_str='udref0',
            e_str='PIId_y + vd - Iqref * xs - udref',
        )
        self.uqref = Algeb(
            tex_name=r'u_{qref}',
            info='uq reference',
            v_str='uqref0',
            e_str='PIIq_y + vq + Idref * xs - uqref',
        )

        self.udLag = Lag(
            u='udref',
            T=self.Tc,
            K=1,
        )
        self.uqLag = Lag(
            u='uqref',
            T=self.Tc,
            K=1,
        )

        self.ud = AliasState(self.udLag_y)
        self.uq = AliasState(self.uqLag_y)
Example #3
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.flags.tds = True
        self.group = 'RenAerodynamics'

        self.theta0r = ConstService(
            v_str='rad(theta0)',
            tex_name=r'\theta_{0r}',
            info='Initial pitch angle in radian',
        )

        self.theta = Algeb(
            tex_name=r'\theta',
            info='Pitch angle',
            unit='rad',
            v_str='theta0r',
            e_str='theta0r - theta',
        )

        self.Pe0 = ExtService(
            model='RenGovernor',
            src='Pe0',
            indexer=self.rego,
            tex_name='P_{e0}',
        )

        self.Pmg = ExtAlgeb(model='RenGovernor',
                            src='Pm',
                            indexer=self.rego,
                            e_str='-Pe0 - (theta - theta0) * theta + Pe0')
Example #4
0
    def __init__(self, system, config):
        MotorBaseModel.__init__(self, system, config)

        self.x2 = ConstService(
            v_str='xs + xr1*xr2*xm / (xr1*xr2 + xr1*xm + xr2*xm)',
            tex_name="x''",
        )
        self.T20 = ConstService(
            v_str='(xr2 + xr1*xm / (xr1 + xm) ) / (wb * rr2)',
            tex_name="T''_0",
        )

        self.e2d = State(
            info='real part of 2nd cage voltage',
            e_str='u * '
            '(-wb*slip*(e1q - e2q) + '
            '(wb*slip*e1q - (e1d + (x0 - x1) * Iq)/T10) + '
            '(e1d - e2d - (x1 - x2) * Iq)/T20)',
            v_str='0.05 * u',
            tex_name="e''_d",
            diag_eps=True,
        )

        self.e2q = State(
            info='imag part of 2nd cage voltage',
            e_str='u * '
            '(wb*slip*(e1d - e2d) + '
            '(-wb*slip*e1d - (e1q - (x0 - x1) * Id)/T10) + '
            '(e1q - e2q + (x1 - x2) * Id) / T20)',
            v_str='0.9 * u',
            tex_name="e''_q",
            diag_eps=True,
        )

        self.Id.e_str = 'u * (vd - e2d - rs * Id + x2 * Iq)'

        self.Id.v_str = '0.9 * u'

        self.Iq.e_str = 'u * (vq - e2q - rs * Iq - x2 * Id)'

        self.Iq.v_str = '0.1 * u'

        self.te.v_str = 'u * (e2d * Id + e2q * Iq)'

        self.te.e_str = f'{self.te.v_str} - te'
Example #5
0
    def __init__(self, system, config):
        ShuntModel.__init__(self, system, config)

        self.config.add(OrderedDict((
            ('min_iter', 2),
            ('err_tol', 0.01),
        )))
        self.config.add_extra(
            "_help",
            min_iter="iteration number starting from which to enable switching",
            err_tol="iteration error below which to enable switching",
        )
        self.config.add_extra(
            "_alt",
            min_iter='int',
            err_tol='float',
        )
        self.config.add_extra(
            "_tex",
            min_iter="sw_{iter}",
            err_tol=r"\epsilon_{tol}",
        )

        self.beff = SwBlock(init=self.b, ns=self.ns, blocks=self.bs)
        self.geff = SwBlock(init=self.g,
                            ns=self.ns,
                            blocks=self.gs,
                            ext_sel=self.beff)

        self.vlo = ConstService(v_str='vref - dv', tex_name='v_{lo}')
        self.vup = ConstService(v_str='vref + dv', tex_name='v_{up}')

        self.adj = ShuntAdjust(v=self.v,
                               lower=self.vlo,
                               upper=self.vup,
                               bsw=self.beff,
                               gsw=self.geff,
                               dt=self.dt,
                               u=self.u,
                               min_iter=self.config.min_iter,
                               err_tol=self.config.err_tol,
                               info='shunt adjuster')

        self.a.e_str = 'u * v**2 * geff'
        self.v.e_str = '-u * v**2 * beff'
Example #6
0
    def __init__(self, system, config):
        TGBase.__init__(self, system, config)

        self.gain = ConstService(
            v_str='ue/R',
            tex_name='G',
        )

        self.pref = Algeb(
            info='Reference power input',
            tex_name='P_{ref}',
            v_str='tm0 * R',
            e_str='pref0 * R - pref',
        )

        self.wd = Algeb(
            info='Generator under speed',
            unit='p.u.',
            tex_name=r'\omega_{dev}',
            v_str='0',
            e_str='ue * (omega - wref) - wd',
        )
        self.pd = Algeb(info='Pref plus under speed times gain',
                        unit='p.u.',
                        tex_name="P_d",
                        v_str='ue * tm0',
                        e_str='ue*(- wd + pref + paux) * gain - pd')

        self.v9 = Algeb(
            tex_name=r'V_{9}',
            info='V_9 for LVGate input',
            v_str='ue * (AT + KT * (AT - tm0))',
            e_str='ue * (AT + KT * (AT - LG3_y)) - v9',
        )

        self.LVG = LVGate(
            u1=self.pd,
            u2=self.v9,
            info='LVGate',
        )

        self.LAG = LagAntiWindup(
            u=self.LVG_y,
            K=1,
            T=self.T1,
            lower=self.VMIN,
            upper=self.VMAX,
        )

        self.LG2 = Lag(u=self.LAG_y, T=self.T2, K=1, info='Lag T2')

        self.LG3 = Lag(u=self.LG2_y, T=self.T3, K=1, info='Lag T3')

        self.pout.e_str = 'ue * (LG2_y - Dt * wd) - pout'
Example #7
0
    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)',
                          ename='P',
                          tex_ename='P',
                          )

        self.v = ExtAlgeb(model='Bus', src='v', indexer=self.bus,
                          tex_name='V',
                          e_str='qv0 * (v ** aq) * (f ** bq)',
                          ename='Q',
                          tex_ename='Q',
                          )
Example #8
0
    def __init__(self, system, config):
        TGBase.__init__(self, system, config)

        self.gain = ConstService(
            v_str='ue/R',
            tex_name='G',
        )

        self.pref = Algeb(
            info='Reference power input',
            tex_name='P_{ref}',
            v_str='tm0 * R',
            e_str='pref0 * R - pref',
        )

        self.wd = Algeb(
            info='Generator speed deviation',
            unit='p.u.',
            tex_name=r'\omega_{dev}',
            v_str='0',
            e_str='ue * (omega - wref) - wd',
        )
        self.pd = Algeb(info='Pref plus speed deviation times gain',
                        unit='p.u.',
                        tex_name="P_d",
                        v_str='ue * tm0',
                        e_str='ue*(- wd + pref + paux) * gain - pd')

        self.LAG = LagAntiWindup(
            u=self.pd,
            K=1,
            T=self.T1,
            lower=self.VMIN,
            upper=self.VMAX,
        )
        self.LL = LeadLag(
            u=self.LAG_y,
            T1=self.T2,
            T2=self.T3,
        )
        self.pout.e_str = 'ue * (LL_y - Dt * wd) - pout'
Example #9
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.flags.tds = True
        self.group = 'RenTorque'

        self.kp1 = ConstService(v_str='(sp2 - sp1) / (p2 - p1)',
                                tex_name='k_{p1}',
                                )
        self.kp2 = ConstService(v_str='(sp3 - sp2) / (p3 - p2)',
                                tex_name='k_{p2}',
                                )
        self.kp3 = ConstService(v_str='(sp4 - sp3) / (p4 - p3)',
                                tex_name='k_{p3}',
                                )

        self.rea = ExtParam(model='RenPitch', src='rea', indexer=self.rep, export=False,
                            )

        self.rego = ExtParam(model='RenAerodynamics', src='rego', indexer=self.rea,
                             export=False,
                             )

        self.ree = ExtParam(model='RenGovernor', src='ree', indexer=self.rego,
                            export=False,
                            )

        self.reg = ExtParam(model='RenExciter', src='reg', indexer=self.ree,
                            export=False,)

        self.Sngo = ExtParam(model='RenGovernor', src='Sn', indexer=self.rego,
                             tex_name='S_{n,go}', export=False,
                             )
        self.Sn = NumSelect(self.Tn,
                            fallback=self.Sngo,
                            tex_name='S_n',
                            info='Turbine or RenGovernor rating',
                            )

        self.Pe = ExtAlgeb(model='RenGen', src='Pe', indexer=self.reg,
                           tex_name='P_e', export=False,
                           )

        self.s1 = Lag(u=self.Pe, T=self.Tp, K=1.0, tex_name='s_1',
                      info='Pe filter',
                      )

        self.fPe = Piecewise(u=self.s1_y,
                             points=('p1', 'p2', 'p3', 'p4'),
                             funs=('sp1',
                                   f'sp1 + ({self.s1_y.name} - p1) * kp1',
                                   f'sp2 + ({self.s1_y.name} - p2) * kp2',
                                   f'sp3 + ({self.s1_y.name} - p3) * kp3',
                                   'sp4'),
                             tex_name='f_{Pe}',
                             info='Piecewise Pe to wref mapping',
                             )

        # Overwrite `wg` and `wt` initial values in turbine governors
        self.wg = ExtState(model='RenGovernor', src='wg', indexer=self.rego,
                           tex_name=r'\omega_g', export=False,
                           v_str='fPe_y',
                           v_setter=True,
                           )

        self.wt = ExtState(model='RenGovernor', src='wt', indexer=self.rego,
                           tex_name=r'\omega_t', export=False,
                           v_str='fPe_y',
                           v_setter=True,
                           )

        self.s3_y = ExtState(model='RenGovernor', src='s3_y', indexer=self.rego,
                             tex_name='y_{s3}', export=False,
                             v_str='Pref0 / wg / Kshaft',
                             v_setter=True,
                             )

        self.w0 = ExtParam(model='RenGovernor', src='w0', indexer=self.rego,
                           tex_name=r'\omega_0', export=False,
                           )

        self.Kshaft = ExtService(model='RenGovernor', src='Kshaft', indexer=self.rego,
                                 tex_name='K_{shaft}',
                                 )

        self.wr0 = ExtAlgeb(model='RenGovernor', src='wr0', indexer=self.rego,
                            tex_name=r'\omega_{r0}', export=False,
                            info='Retrieved initial w0 from RenGovernor',
                            v_str='fPe_y',
                            e_str='-w0 + fPe_y',
                            v_setter=True,
                            ename='dwr',
                            tex_ename=r'\Delta \omega_r',
                            )

        self.s2 = Lag(u=self.fPe_y, T=self.Twref, K=1.0,
                      tex_name='s_2', info='speed filter',
                      )

        self.SWT = Switcher(u=self.Tflag, options=(0, 1),
                            tex_name='SW_{T}',
                            cache=True,
                            )

        self.Tsel = Algeb(tex_name='T_{sel}',
                          info='Output after Tflag selector',
                          discrete=self.SWT
                          )
        self.Tsel.v_str = 'SWT_s1 * (Pe - Pref0) / wg +' \
                          'SWT_s0 * (s2_y - wg)'
        self.Tsel.e_str = f'{self.Tsel.v_str} - Tsel'

        self.PI = PIAWHardLimit(u=self.Tsel, kp=self.Kpp, ki=self.Kip,
                                aw_lower=self.Temin, aw_upper=self.Temax,
                                lower=self.Temin, upper=self.Temax,
                                tex_name='PI',
                                info='PI controller',
                                x0='Pref0 / fPe_y',
                                )

        # Note:
        #   Reset `wg` of REECA1 to 1.0 becase `wg` has already been multiplied
        #   in the toeque model.
        #   This effectively sets `PFLAG` to 0 if the torque model is connected.

        self.wge = ExtAlgeb(model='RenExciter', src='wg', indexer=self.ree,
                            tex_name=r'\omega_{ge}', export=False,
                            v_str='1.0',
                            e_str='-fPe_y + 1',
                            v_setter=True,
                            ename='dwg',
                            tex_ename=r'\Delta \omega_g',
                            )

        self.Pref0 = ExtService(model='RenExciter', src='p0', indexer=self.ree,
                                tex_name='P_{ref0}',
                                )

        self.Pref = ExtAlgeb(model='RenExciter', src='Pref', indexer=self.ree,
                             tex_name='P_{ref}', export=False,
                             e_str='-Pref0 / wge + PI_y * wg',
                             v_str='PI_y * wg',
                             v_setter=True,
                             ename='Pref',
                             tex_ename='P_{ref}',
                             )
Example #10
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.group = 'RenPlant'
        self.flags.tds = True

        self.config.add(OrderedDict((
            ('kqs', 2),
            ('ksg', 2),
            ('freeze', 1),
        )))

        self.config.add_extra(
            '_help',
            kqs='Tracking gain for reactive power PI controller',
            ksg='Tracking gain for active power PI controller',
            freeze='Voltage dip freeze flag; 1-enable, 0-disable',
        )
        self.config.add_extra('_tex',
                              kqs='K_{qs}',
                              ksg='K_{sg}',
                              freeze='f_{rz}')

        # --- from RenExciter ---
        self.reg = ExtParam(
            model='RenExciter',
            src='reg',
            indexer=self.ree,
            export=False,
            info='Retrieved RenGen idx',
            vtype=str,
            default=None,
        )
        self.Pext = ExtAlgeb(
            model='RenExciter',
            src='Pref',
            indexer=self.ree,
            info='Pref from RenExciter renamed as Pext',
            tex_name='P_{ext}',
        )

        self.Qext = ExtAlgeb(
            model='RenExciter',
            src='Qref',
            indexer=self.ree,
            info='Qref from RenExciter renamed as Qext',
            tex_name='Q_{ext}',
        )

        # --- from RenGen ---
        self.bus = ExtParam(
            model='RenGen',
            src='bus',
            indexer=self.reg,
            export=False,
            info='Retrieved bus idx',
            vtype=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 Bus
        self.v = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.buss,
            tex_name='V',
            info='Bus (or busr, if given) terminal voltage',
        )

        self.a = ExtAlgeb(
            model='Bus',
            src='a',
            indexer=self.buss,
            tex_name=r'\theta',
            info='Bus (or busr, if given) phase angle',
        )

        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',
                          unit='p.u.')

        # from Line
        self.bus1 = ExtParam(
            model='ACLine',
            src='bus1',
            indexer=self.line,
            export=False,
            info='Retrieved Line.bus1 idx',
            vtype=str,
            default=None,
        )

        self.bus2 = ExtParam(
            model='ACLine',
            src='bus2',
            indexer=self.line,
            export=False,
            info='Retrieved Line.bus2 idx',
            vtype=str,
            default=None,
        )
        self.r = ExtParam(
            model='ACLine',
            src='r',
            indexer=self.line,
            export=False,
            info='Retrieved Line.r',
            vtype=str,
            default=None,
        )

        self.x = ExtParam(
            model='ACLine',
            src='x',
            indexer=self.line,
            export=False,
            info='Retrieved Line.x',
            vtype=str,
            default=None,
        )

        self.v1 = ExtAlgeb(
            model='ACLine',
            src='v1',
            indexer=self.line,
            tex_name='V_1',
            info='Voltage at Line.bus1',
        )

        self.v2 = ExtAlgeb(
            model='ACLine',
            src='v2',
            indexer=self.line,
            tex_name='V_2',
            info='Voltage at Line.bus2',
        )

        self.a1 = ExtAlgeb(
            model='ACLine',
            src='a1',
            indexer=self.line,
            tex_name=r'\theta_1',
            info='Angle at Line.bus1',
        )

        self.a2 = ExtAlgeb(
            model='ACLine',
            src='a2',
            indexer=self.line,
            tex_name=r'\theta_2',
            info='Angle at Line.bus2',
        )

        # -- begin services ---

        self.Isign = CurrentSign(self.bus,
                                 self.bus1,
                                 self.bus2,
                                 tex_name='I_{sign}')

        Iline = '(Isign * (v1*exp(1j*a1) - v2*exp(1j*a2)) / (r + 1j*x))'

        self.Iline = VarService(
            v_str=Iline,
            vtype=complex,
            info='Complex current from bus1 to bus2',
            tex_name='I_{line}',
        )

        self.Iline0 = ConstService(
            v_str='Iline',
            vtype=complex,
            info='Initial complex current from bus1 to bus2',
            tex_name='I_{line0}',
        )

        Pline = 're(Isign * v1*exp(1j*a1) * conj((v1*exp(1j*a1) - v2*exp(1j*a2)) / (r + 1j*x)))'

        self.Pline = VarService(
            v_str=Pline,
            vtype=float,
            info='Complex power from bus1 to bus2',
            tex_name='P_{line}',
        )

        self.Pline0 = ConstService(
            v_str='Pline',
            vtype=float,
            info='Initial vomplex power from bus1 to bus2',
            tex_name='P_{line0}',
        )

        Qline = 'im(Isign * v1*exp(1j*a1) * conj((v1*exp(1j*a1) - v2*exp(1j*a2)) / (r + 1j*x)))'

        self.Qline = VarService(
            v_str=Qline,
            vtype=float,
            info='Complex power from bus1 to bus2',
            tex_name='Q_{line}',
        )

        self.Qline0 = ConstService(
            v_str='Qline',
            vtype=float,
            info='Initial complex power from bus1 to bus2',
            tex_name='Q_{line0}',
        )

        self.Rcs = NumSelect(
            self.Rc,
            self.r,
            info='Line R (Rc if provided, otherwise line.r)',
            tex_name='R_{cs}',
        )

        self.Xcs = NumSelect(
            self.Xc,
            self.x,
            info='Line X (Xc if provided, otherwise line.x)',
            tex_name='X_{cs}',
        )

        self.Vcomp = VarService(
            v_str='abs(v*exp(1j*a) - (Rcs + 1j * Xcs) * Iline)',
            info='Voltage after Rc/Xc compensation',
            tex_name='V_{comp}')

        self.SWVC = Switcher(u=self.VCFlag,
                             options=(0, 1),
                             tex_name='SW_{VC}',
                             cache=True)

        self.SWRef = Switcher(u=self.RefFlag,
                              options=(0, 1),
                              tex_name='SW_{Ref}',
                              cache=True)

        self.SWF = Switcher(u=self.Fflag,
                            options=(0, 1),
                            tex_name='SW_{F}',
                            cache=True)

        self.SWPL = Switcher(u=self.PLflag,
                             options=(0, 1),
                             tex_name='SW_{PL}',
                             cache=True)

        VCsel = '(SWVC_s1 * Vcomp + SWVC_s0 * (Qline * Kc + v))'

        self.Vref0 = ConstService(
            v_str='(SWVC_s1 * Vcomp + SWVC_s0 * (Qline0 * Kc + v))',
            tex_name='V_{ref0}',
        )

        self.s0 = Lag(
            VCsel,
            T=self.Tfltr,
            K=1,
            tex_name='s_0',
            info='V filter',
        )  # s0_y is the filter output of voltage deviation

        self.s1 = Lag(self.Qline, T=self.Tfltr, K=1, tex_name='s_1')

        self.Vref = Algeb(v_str='Vref0',
                          e_str='Vref0 - Vref',
                          tex_name='Q_{ref}')

        self.Qlinef = Algeb(v_str='Qline0',
                            e_str='Qline0 - Qlinef',
                            tex_name='Q_{linef}')

        Refsel = '(SWRef_s0 * (Qlinef - s1_y) + SWRef_s1 * (Vref - s0_y))'

        self.Refsel = Algeb(v_str=Refsel,
                            e_str=f'{Refsel} - Refsel',
                            tex_name='R_{efsel}')

        self.dbd = DeadBand1(
            u=self.Refsel,
            lower=self.dbd1,
            upper=self.dbd2,
            center=0.0,
            tex_name='d^{bd}',
        )

        # --- e Hardlimit and hold logic ---
        self.eHL = Limiter(
            u=self.dbd_y,
            lower=self.emin,
            upper=self.emax,
            tex_name='e_{HL}',
            info='Hardlimit on deadband output',
        )

        self.zf = VarService(
            v_str='Indicator(v < Vfrz) * freeze',
            tex_name='z_f',
            info='PI Q input freeze signal',
        )

        self.enf = Algeb(
            tex_name='e_{nf}',
            info='e Hardlimit output before freeze',
            v_str='dbd_y*eHL_zi + emax*eHL_zu + emin*eHL_zl',
            e_str='dbd_y*eHL_zi + emax*eHL_zu + emin*eHL_zl - enf',
        )

        # --- hold of `enf` when v < vfrz

        self.eHld = VarHold(
            u=self.enf,
            hold=self.zf,
            tex_name='e_{hld}',
            info='e Hardlimit output after conditional hold',
        )

        self.s2 = PITrackAW(
            u='eHld',
            kp=self.Kp,
            ki=self.Ki,
            ks=self.config.kqs,
            lower=self.Qmin,
            upper=self.Qmax,
            info='PI controller for eHL output',
            tex_name='s_2',
        )

        self.s3 = LeadLag(
            u=self.s2_y,
            T1=self.Tft,
            T2=self.Tfv,
            K=1,
            tex_name='s_3',
        )  # s3_y == Qext

        # Active power part

        self.s4 = Lag(
            self.Pline,
            T=self.Tp,
            K=1,
            tex_name='s_4',
            info='Pline filter',
        )

        self.Freq_ref = ConstService(v_str='1.0',
                                     tex_name='f_{ref}',
                                     info='Initial Freq_ref')
        self.ferr = Algeb(
            tex_name='f_{err}',
            info='Frequency deviation',
            unit='p.u. (Hz)',
            v_str='(Freq_ref - f)',
            e_str='(Freq_ref - f) - ferr',
        )

        self.fdbd = DeadBand1(
            u=self.ferr,
            center=0.0,
            lower=self.fdbd1,
            upper=self.fdbd2,
            tex_name='f^{dbd}',
            info='frequency error deadband',
        )

        self.fdlt0 = LessThan(
            self.fdbd_y,
            0.0,
            tex_name='f_{dlt0}',
            info='frequency deadband output less than zero',
        )

        fdroop = '(fdbd_y * Ddn * fdlt0_z1 + fdbd_y * Dup * fdlt0_z0)'

        self.Plant_pref = Algeb(
            tex_name='P_{ref}',
            info='Plant P ref',
            v_str='Pline0',
            e_str='Pline0 - Plant_pref',
        )

        self.Plerr = Algeb(
            tex_name='P_{lerr}',
            info='Pline error',
            v_str='- s4_y + Plant_pref',
            e_str='- s4_y + Plant_pref - Plerr',
        )

        self.Perr = Algeb(
            tex_name='P_{err}',
            info='Power error before fe limits',
            v_str=f'{fdroop} + Plerr * SWPL_s1',
            e_str=f'{fdroop} + Plerr * SWPL_s1 - Perr',
        )

        self.feHL = Limiter(
            self.Perr,
            lower=self.femin,
            upper=self.femax,
            tex_name='f_{eHL}',
            info='Limiter for power (frequency) error',
        )

        feout = '(Perr * feHL_zi + femin * feHL_zl + femax * feHL_zu)'
        self.s5 = PITrackAW(
            u=feout,
            kp=self.Kpg,
            ki=self.Kig,
            ks=self.config.ksg,
            lower=self.Pmin,
            upper=self.Pmax,
            tex_name='s_5',
            info='PI for fe limiter output',
        )

        self.s6 = Lag(
            u=self.s5_y,
            T=self.Tg,
            K=1,
            tex_name='s_6',
            info='Output filter for Pext',
        )

        Qext = '(s3_y)'

        Pext = '(SWF_s1 * s6_y)'

        self.Pext.e_str = Pext

        self.Qext.e_str = Qext
Example #11
0
    def __init__(self, system=None, config=None):
        LineData.__init__(self)
        Model.__init__(self, system, config)
        self.group = 'ACLine'
        self.flags.pflow = True
        self.flags.tds = True

        self.a1 = ExtAlgeb(
            model='Bus',
            src='a',
            indexer=self.bus1,
            tex_name='a_1',
            info='phase angle of the from bus',
            ename='Pij',
            tex_ename='P_{ij}',
        )
        self.a2 = ExtAlgeb(
            model='Bus',
            src='a',
            indexer=self.bus2,
            tex_name='a_2',
            info='phase angle of the to bus',
            ename='Pji',
            tex_ename='P_{ji}',
        )
        self.v1 = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.bus1,
            tex_name='v_1',
            info='voltage magnitude of the from bus',
            ename='Qij',
            tex_ename='Q_{ij}',
        )
        self.v2 = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.bus2,
            tex_name='v_2',
            info='voltage magnitude of the to bus',
            ename='Qji',
            tex_ename='Q_{ji}',
        )

        self.gh = ConstService(tex_name='g_h')
        self.bh = ConstService(tex_name='b_h')
        self.gk = ConstService(tex_name='g_k')
        self.bk = ConstService(tex_name='b_k')

        self.yh = ConstService(tex_name='y_h', vtype=complex)
        self.yk = ConstService(tex_name='y_k', vtype=complex)
        self.yhk = ConstService(tex_name='y_{hk}', vtype=complex)

        self.ghk = ConstService(tex_name='g_{hk}')
        self.bhk = ConstService(tex_name='b_{hk}')

        self.itap = ConstService(tex_name='1/t_{ap}')
        self.itap2 = ConstService(tex_name='1/t_{ap}^2')

        self.gh.v_str = 'g1 + 0.5 * g'
        self.bh.v_str = 'b1 + 0.5 * b'
        self.gk.v_str = 'g2 + 0.5 * g'
        self.bk.v_str = 'b2 + 0.5 * b'

        self.yh.v_str = 'u * (gh + 1j * bh)'
        self.yk.v_str = 'u * (gk + 1j * bk)'
        self.yhk.v_str = 'u/((r+1e-8) + 1j*(x+1e-8))'

        self.ghk.v_str = 're(yhk)'
        self.bhk.v_str = 'im(yhk)'

        self.itap.v_str = '1/tap'
        self.itap2.v_str = '1/tap/tap'

        self.a1.e_str = 'u * (v1 ** 2 * (gh + ghk) * itap2  - \
                              v1 * v2 * (ghk * cos(a1 - a2 - phi) + \
                                         bhk * sin(a1 - a2 - phi)) * itap)'

        self.v1.e_str = 'u * (-v1 ** 2 * (bh + bhk) * itap2 - \
                              v1 * v2 * (ghk * sin(a1 - a2 - phi) - \
                                         bhk * cos(a1 - a2 - phi)) * itap)'

        self.a2.e_str = 'u * (v2 ** 2 * (gh + ghk) - \
                              v1 * v2 * (ghk * cos(a1 - a2 - phi) - \
                                         bhk * sin(a1 - a2 - phi)) * itap)'

        self.v2.e_str = 'u * (-v2 ** 2 * (bh + bhk) + \
Example #12
0
File: pq.py Project: CURENT/andes
    def __init__(self, system=None, config=None):
        PQData.__init__(self)
        Model.__init__(self, system, config)
        self.group = 'StaticLoad'
        # ``tds`` flag is needed to retrieve initial voltage (for constant Z/I conversion)
        self.flags.update({'pflow': True,
                           'tds': True,
                           })
        self.config.add(OrderedDict((('pq2z', 1),
                                     ('p2p', 0.0),
                                     ('p2i', 0.0),
                                     ('p2z', 1.0),
                                     ('q2q', 0.0),
                                     ('q2i', 0.0),
                                     ('q2z', 1.0),
                                     )))
        self.config.add_extra("_help",
                              pq2z="pq2z conversion if out of voltage limits",
                              p2p="P constant power percentage for TDS. Must have (p2p+p2i+p2z)=1",
                              p2i="P constant current percentage",
                              p2z="P constant impedance percentage",
                              q2q="Q constant power percentage for TDS. Must have (q2q+q2i+q2z)=1",
                              q2i="Q constant current percentage",
                              q2z="Q constant impedance percentage",
                              )
        self.config.add_extra("_alt",
                              pq2z="(0, 1)",
                              p2p="float",
                              p2i="float",
                              p2z="float",
                              q2q="float",
                              q2i="float",
                              q2z="float",
                              )
        self.config.add_extra("_tex",
                              pq2z="z_{pq2z}",
                              p2p=r"\gamma_{p2p}",
                              p2i=r"\gamma_{p2i}",
                              p2z=r"\gamma_{p2z}",
                              q2q=r"\gamma_{q2q}",
                              q2i=r"\gamma_{q2i}",
                              q2z=r"\gamma_{q2z}",
                              )

        self.a = ExtAlgeb(model='Bus',
                          src='a',
                          indexer=self.bus,
                          tex_name=r'\theta',
                          ename='P',
                          tex_ename='P',
                          is_input=True,
                          )
        self.v = ExtAlgeb(model='Bus',
                          src='v',
                          indexer=self.bus,
                          tex_name=r'V',
                          ename='Q',
                          tex_ename='Q',
                          is_input=True,
                          )

        self.v0 = ExtService(src='v',
                             model='Bus',
                             indexer=self.bus,
                             tex_name='V_0',
                             info='Initial voltage magnitude from power flow'
                             )
        self.a0 = ExtService(src='a',
                             model='Bus',
                             indexer=self.bus,
                             tex_name=r'\theta_0',
                             info='Initial voltage angle from power flow'
                             )

        # Rub, Xub, Rlb, Xlb are constant for both PF and TDS.
        self.Rub = ConstService(info='Equivalent resistance at voltage upper bound',
                                v_str='p0 / vmax**2',
                                tex_name='R_{ub}'
                                )
        self.Xub = ConstService(info='Equivalent reactance at voltage upper bound',
                                v_str='q0 / vmax**2',
                                tex_name='X_{ub}'
                                )
        self.Rlb = ConstService(info='Equivalent resistance at voltage lower bound',
                                v_str='p0 / vmin**2',
                                tex_name='R_{lb}'
                                )
        self.Xlb = ConstService(info='Equivalent reactance at voltage lower bound',
                                v_str='q0 / vmin**2',
                                tex_name='X_{lb}'
                                )

        # Ppf, Qpf, Req, Xeq, Ipeq, Iqeq are only meaningful after initializing TDS
        self.Ppf = ConstService(info='Actual P in power flow',
                                v_str='(p0 * vcmp_zi + Rlb * vcmp_zl * v0**2 + Rub * vcmp_zu * v0**2)',
                                tex_name='P_{pf}')
        self.Qpf = ConstService(info='Actual Q in power flow',
                                v_str='(q0 * vcmp_zi + Xlb * vcmp_zl * v0**2 + Xub * vcmp_zu * v0**2)',
                                tex_name='Q_{pf}')
        self.Req = ConstService(info='Equivalent resistance at steady state',
                                v_str='Ppf / v0**2',
                                tex_name='R_{eq}'
                                )
        self.Xeq = ConstService(info='Equivalent reactance at steady state',
                                v_str='Qpf / v0**2',
                                tex_name='X_{eq}'
                                )
        self.Ipeq = ConstService(info='Equivalent active current source at steady state',
                                 v_str='Ppf / v0',
                                 tex_name='I_{peq}'
                                 )
        self.Iqeq = ConstService(info='Equivalent reactive current source at steady state',
                                 v_str='Qpf / v0',
                                 tex_name='I_{qeq}'
                                 )

        self.vcmp = Limiter(u=self.v,
                            lower=self.vmin,
                            upper=self.vmax,
                            enable=self.config.pq2z,
                            )

        # Note: the "or" condition "|" is not supported in sympy equation strings.
        # They will simply be ignored.

        # To modify P and Q during TDS, use `alter` to set values to `Ppf` and `Qpf`
        # after, before simulation, setting `config.p2p=1` and `config.q2q=1`.

        self.a.e_str = "u * Indicator(dae_t <= 0) * " \
                       "(p0 * vcmp_zi + Rlb * vcmp_zl * v**2 + Rub * vcmp_zu * v**2) + " \
                       "u * Indicator(dae_t > 0) * " \
                       "(p2p * Ppf + p2i * Ipeq * v + p2z * Req * v**2)"

        self.v.e_str = "u * Indicator(dae_t <= 0) * " \
                       "(q0 * vcmp_zi + Xlb * vcmp_zl * v**2 + Xub * vcmp_zu * v**2) + " \
                       "u * Indicator(dae_t > 0) * " \
                       "(q2q * Qpf + q2i * Iqeq * v + q2z * Xeq * v**2)"
Example #13
0
    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 the values of `tm0` is not allowed at 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.pref0 = ConstService(
                v_str='tm0',
                info='initial pref',
                tex_name='P_{ref0}',
            )

        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',
        )
Example #14
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.flags.tds = True
        self.group = 'RenGovernor'

        self.reg = ExtParam(
            model='RenExciter',
            src='reg',
            indexer=self.ree,
            vtype=str,
            export=False,
        )
        self.Sn = ExtParam(
            model='RenGen',
            src='Sn',
            indexer=self.reg,
            tex_name='S_n',
            export=False,
        )

        self.wge = ExtAlgeb(
            model='RenExciter',
            src='wg',
            indexer=self.ree,
            export=False,
            e_str='-1.0 + s2_y',
            ename='wg',
            tex_ename=r'\omega_g',
        )

        self.Pe = ExtAlgeb(model='RenGen',
                           src='Pe',
                           indexer=self.reg,
                           export=False,
                           info='Retrieved Pe of RenGen')

        self.Pe0 = ExtService(
            model='RenGen',
            src='Pe',
            indexer=self.reg,
            tex_name='P_{e0}',
        )

        self.Ht2 = ConstService(v_str='2 * (Htfrac * H)', tex_name='2H_t')

        self.Hg2 = ConstService(v_str='2 * H * (1 - Htfrac)', tex_name='2H_g')

        # (2*pi*Freq1)**2 is considered in p.u., which is Freq1**2 here
        self.Kshaft = ConstService(v_str='Ht2 * Hg2 * 0.5 * Freq1 * Freq1 / H',
                                   tex_name='K_{shaft}')

        self.wr0 = Algeb(
            tex_name=r'\omega_{r0}',
            unit='p.u.',
            v_str='w0',
            e_str='w0 - wr0',
            info='speed set point',
        )

        self.Pm = Algeb(
            tex_name='P_m',
            info='Mechanical power',
            e_str='Pe0 - Pm',
            v_str='Pe0',
        )

        # `s1_y` is `wt`
        self.s1 = Integrator(
            u='(Pm / s1_y) - s3_y - pd',
            T=self.Ht2,
            K=1.0,
            y0='wr0',
        )

        self.wt = AliasState(self.s1_y, tex_name=r'\omega_t')

        # `s2_y` is `wg`
        self.s2 = Integrator(
            u='-(Pe / s2_y) + s3_y - DAMP * (s2_y - w0) + pd',
            T=self.Hg2,
            K=1.0,
            y0='wr0',
        )

        self.wg = AliasState(self.s2_y, tex_name=r'\omega_g')

        # `s3_y` gets reinitialized in `WTTQA1`
        self.s3 = Integrator(
            u='s1_y - s2_y',
            T=1.0,
            K=self.Kshaft,
            y0='Pe0 / wr0',
        )

        self.pd = Algeb(
            tex_name='P_d',
            info='Output after damping',
            e_str='Dshaft * (s1_y - s2_y) - pd',
            v_str='0',
        )
Example #15
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.flags.tds = True
        self.group = 'RenExciter'

        self.config.add(OrderedDict((('kqs', 2),
                                     ('kvs', 2),
                                     ('tpfilt', 0.02),
                                     )))
        self.config.add_extra('_help',
                              kqs='Q PI controller tracking gain',
                              kvs='Voltage PI controller tracking gain',
                              tpfilt='Time const. for Pref filter',
                              )
        self.config.add_extra('_tex',
                              kqs='K_{qs}',
                              kvs='K_{vs}',
                              tpfilt='T_{pfilt}',
                              )

        # --- Sanitize inputs ---
        self.Imaxr = Replace(self.Imax, flt=lambda x: np.less_equal(x, 0), new_val=1e8,
                             tex_name='I_{maxr}')

        # --- Flag switchers ---
        self.SWPF = Switcher(u=self.PFFLAG, options=(0, 1), tex_name='SW_{PF}', cache=True)

        self.SWV = Switcher(u=self.VFLAG, options=(0, 1), tex_name='SW_{V}', cache=True)

        self.SWQ = Switcher(u=self.QFLAG, options=(0, 1), tex_name='SW_{V}', cache=True)

        self.SWP = Switcher(u=self.PFLAG, options=(0, 1), tex_name='SW_{P}', cache=True)

        self.SWPQ = Switcher(u=self.PQFLAG, options=(0, 1), tex_name='SW_{PQ}', cache=True)

        # --- External parameters ---
        self.bus = ExtParam(model='RenGen', src='bus', indexer=self.reg, export=False,
                            info='Retrieved bus idx', vtype=str, default=None,
                            )

        self.buss = DataSelect(self.busr, self.bus, info='selected bus (bus or busr)')

        self.gen = ExtParam(model='RenGen', src='gen', indexer=self.reg, export=False,
                            info='Retrieved StaticGen idx', vtype=str, default=None,
                            )

        self.Sn = ExtParam(model='RenGen', src='Sn', indexer=self.reg,
                           tex_name='S_n', export=False,
                           )

        # --- External variables ---
        self.a = ExtAlgeb(model='Bus',
                          src='a',
                          indexer=self.bus,
                          tex_name=r'\theta',
                          info='Bus voltage angle',
                          )

        self.v = ExtAlgeb(model='Bus',
                          src='v',
                          indexer=self.bus,
                          tex_name=r'V',
                          info='Bus voltage magnitude',
                          )  # check whether to use `bus` or `buss`

        self.Pe = ExtAlgeb(model='RenGen', src='Pe', indexer=self.reg, export=False,
                           info='Retrieved Pe of RenGen')

        self.Qe = ExtAlgeb(model='RenGen', src='Qe', indexer=self.reg, export=False,
                           info='Retrieved Qe of RenGen')

        self.Ipcmd = ExtAlgeb(model='RenGen', src='Ipcmd', indexer=self.reg, export=False,
                              info='Retrieved Ipcmd of RenGen',
                              e_str='-Ipcmd0 + IpHL_y',
                              )

        self.Iqcmd = ExtAlgeb(model='RenGen', src='Iqcmd', indexer=self.reg, export=False,
                              info='Retrieved Iqcmd of RenGen',
                              e_str='-Iqcmd0 - IqHL_y',
                              )

        self.p0 = ExtService(model='RenGen',
                             src='p0',
                             indexer=self.reg,
                             tex_name='P_0',
                             )
        self.q0 = ExtService(model='RenGen',
                             src='q0',
                             indexer=self.reg,
                             tex_name='Q_0',
                             )

        # Initial current commands
        self.Ipcmd0 = ConstService('p0 / v', info='initial Ipcmd')

        self.Iqcmd0 = ConstService('-q0 / v', info='initial Iqcmd')

        # --- Initial power factor angle ---
        # NOTE: if `p0` = 0, `pfaref0` = pi/2, `tan(pfaref0)` = inf
        self.pfaref0 = ConstService(v_str='atan2(q0, p0)', tex_name=r'\Phi_{ref0}',
                                    info='Initial power factor angle',
                                    )
        # flag devices with `p0`=0, which causes `tan(PF) = +inf`
        self.zp0 = ConstService(v_str='Eq(p0, 0)',
                                vtype=float,
                                tex_name='z_{p0}',
                                )

        # --- Discrete components ---
        self.Vcmp = Limiter(u=self.v, lower=self.Vdip, upper=self.Vup, tex_name='V_{cmp}',
                            info='Voltage dip comparator', equal=False,
                            )
        self.Volt_dip = VarService(v_str='1 - Vcmp_zi',
                                   info='Voltage dip flag; 1-dip, 0-normal',
                                   tex_name='z_{Vdip}',
                                   )

        # --- Equations begin ---
        self.s0 = Lag(u=self.v, T=self.Trv, K=1,
                      info='Voltage filter',
                      )
        self.VLower = Limiter(u=self.v, lower=0.01, upper=999, no_upper=True,
                              info='Limiter for lower voltage cap',
                              )
        self.vp = Algeb(tex_name='V_p',
                        info='Sensed lower-capped voltage',
                        v_str='v * VLower_zi + 0.01 * VLower_zl',
                        e_str='v * VLower_zi + 0.01 * VLower_zl - vp',
                        )

        self.pfaref = Algeb(tex_name=r'\Phi_{ref}',
                            info='power factor angle ref',
                            unit='rad',
                            v_str='pfaref0',
                            e_str='pfaref0 - pfaref',
                            )

        self.S1 = Lag(u='Pe', T=self.Tp, K=1, tex_name='S_1', info='Pe filter',
                      )

        # ignore `Qcpf` if `pfaref` is pi/2 by multiplying (1-zp0)
        self.Qcpf = Algeb(tex_name='Q_{cpf}',
                          info='Q calculated from P and power factor',
                          v_str='q0',
                          e_str='(1-zp0) * (S1_y * tan(pfaref) - Qcpf)',
                          diag_eps=True,
                          unit='p.u.',
                          )

        self.Qref = Algeb(tex_name='Q_{ref}',
                          info='external Q ref',
                          v_str='q0',
                          e_str='q0 - Qref',
                          unit='p.u.',
                          )

        self.PFsel = Algeb(v_str='SWPF_s0*Qref + SWPF_s1*Qcpf',
                           e_str='SWPF_s0*Qref + SWPF_s1*Qcpf - PFsel',
                           info='Output of PFFLAG selector',
                           )

        self.PFlim = Limiter(u=self.PFsel, lower=self.QMin, upper=self.QMax)

        self.Qerr = Algeb(tex_name='Q_{err}',
                          info='Reactive power error',
                          v_str='(PFsel*PFlim_zi + QMin*PFlim_zl + QMax*PFlim_zu) - Qe',
                          e_str='(PFsel*PFlim_zi + QMin*PFlim_zl + QMax*PFlim_zu) - Qe - Qerr',
                          )

        self.PIQ = PITrackAWFreeze(u=self.Qerr,
                                   kp=self.Kqp, ki=self.Kqi, ks=self.config.kqs,
                                   lower=self.VMIN, upper=self.VMAX,
                                   freeze=self.Volt_dip,
                                   )

        # If `VFLAG=0`, set the input as `Vref1` (see the NREL report)
        self.Vsel = GainLimiter(u='SWV_s0 * Vref1 + SWV_s1 * PIQ_y',
                                K=1, R=1,
                                lower=self.VMIN, upper=self.VMAX,
                                info='Selection output of VFLAG',
                                )

        # --- Placeholders for `Iqmin` and `Iqmax` ---

        self.s4 = LagFreeze(u='PFsel / vp', T=self.Tiq, K=1,
                            freeze=self.Volt_dip,
                            tex_name='s_4',
                            info='Filter for calculated voltage with freeze',
                            )

        # --- Upper portion - Iqinj calculation ---

        self.Verr = Algeb(info='Voltage error (Vref0)',
                          v_str='Vref0 - s0_y',
                          e_str='Vref0 - s0_y - Verr',
                          tex_name='V_{err}',
                          )
        self.dbV = DeadBand1(u=self.Verr, lower=self.dbd1, upper=self.dbd2,
                             center=0.0,
                             enable='DB_{V}',
                             info='Deadband for voltage error (ref0)'
                             )

        self.pThld = ConstService(v_str='Indicator(Thld > 0)', tex_name='p_{Thld}')

        self.nThld = ConstService(v_str='Indicator(Thld < 0)', tex_name='n_{Thld}')

        self.Thld_abs = ConstService(v_str='abs(Thld)', tex_name='|Thld|')

        self.fThld = ExtendedEvent(self.Volt_dip,
                                   t_ext=self.Thld_abs,
                                   )

        # Gain after dbB
        Iqv = "(dbV_y * Kqv)"
        Iqinj = f'{Iqv} * Volt_dip + ' \
                f'(1 - Volt_dip) * fThld * ({Iqv} * nThld + Iqfrz * pThld)'

        # state transition, output of Iqinj
        self.Iqinj = Algeb(v_str=Iqinj,
                           e_str=Iqinj + ' - Iqinj',
                           tex_name='I_{qinj}',
                           info='Additional Iq signal during under- or over-voltage',
                           )

        # --- Lower portion - active power ---
        self.wg = Algeb(tex_name=r'\omega_g',
                        info='Drive train generator speed',
                        v_str='1.0',
                        e_str='1.0 - wg',
                        )

        self.Pref = Algeb(tex_name='P_{ref}',
                          info='external P ref',
                          v_str='p0 / wg',
                          e_str='p0 / wg - Pref',
                          unit='p.u.',
                          )

        self.pfilt = LagRate(u=self.Pref, T=self.config.tpfilt, K=1,
                             rate_lower=self.dPmin, rate_upper=self.dPmax,
                             info='Active power filter with rate limits',
                             tex_name='P_{filt}',
                             )

        self.Psel = Algeb(tex_name='P_{sel}',
                          info='Output selection of PFLAG',
                          v_str='SWP_s1*wg*pfilt_y + SWP_s0*pfilt_y',
                          e_str='SWP_s1*wg*pfilt_y + SWP_s0*pfilt_y - Psel',
                          )

        # `s5_y` is `Pord`
        self.s5 = LagAWFreeze(u=self.Psel, T=self.Tpord, K=1,
                              lower=self.PMIN, upper=self.PMAX,
                              freeze=self.Volt_dip,
                              tex_name='s5',
                              )

        self.Pord = AliasState(self.s5_y)

        # --- Current limit logic ---

        self.kVq12 = ConstService(v_str='(Iq2 - Iq1) / (Vq2 - Vq1)',
                                  tex_name='k_{Vq12}',
                                  )
        self.kVq23 = ConstService(v_str='(Iq3 - Iq2) / (Vq3 - Vq2)',
                                  tex_name='k_{Vq23}',
                                  )
        self.kVq34 = ConstService(v_str='(Iq4 - Iq3) / (Vq4 - Vq3)',
                                  tex_name='k_{Vq34}',
                                  )

        self.zVDL1 = ConstService(v_str='(Vq1 <= Vq2) & (Vq2 <= Vq3) & (Vq3 <= Vq4) & '
                                        '(Iq1 <= Iq2) & (Iq2 <= Iq3) & (Iq3 <= Iq4)',
                                  tex_name='z_{VDL1}',
                                  info='True if VDL1 is in service',
                                  )

        self.VDL1 = Piecewise(u=self.s0_y,
                              points=('Vq1', 'Vq2', 'Vq3', 'Vq4'),
                              funs=('Iq1',
                                    f'({self.s0_y.name} - Vq1) * kVq12 + Iq1',
                                    f'({self.s0_y.name} - Vq2) * kVq23 + Iq2',
                                    f'({self.s0_y.name} - Vq3) * kVq34 + Iq3',
                                    'Iq4'),
                              tex_name='V_{DL1}',
                              info='Piecewise linear characteristics of Vq-Iq',
                              )

        self.kVp12 = ConstService(v_str='(Ip2 - Ip1) / (Vp2 - Vp1)',
                                  tex_name='k_{Vp12}',
                                  )
        self.kVp23 = ConstService(v_str='(Ip3 - Ip2) / (Vp3 - Vp2)',
                                  tex_name='k_{Vp23}',
                                  )
        self.kVp34 = ConstService(v_str='(Ip4 - Ip3) / (Vp4 - Vp3)',
                                  tex_name='k_{Vp34}',
                                  )

        self.zVDL2 = ConstService(v_str='(Vp1 <= Vp2) & (Vp2 <= Vp3) & (Vp3 <= Vp4) & '
                                        '(Ip1 <= Ip2) & (Ip2 <= Ip3) & (Ip3 <= Ip4)',
                                  tex_name='z_{VDL2}',
                                  info='True if VDL2 is in service',
                                  )

        self.VDL2 = Piecewise(u=self.s0_y,
                              points=('Vp1', 'Vp2', 'Vp3', 'Vp4'),
                              funs=('Ip1',
                                    f'({self.s0_y.name} - Vp1) * kVp12 + Ip1',
                                    f'({self.s0_y.name} - Vp2) * kVp23 + Ip2',
                                    f'({self.s0_y.name} - Vp3) * kVp34 + Ip3',
                                    'Ip4'),
                              tex_name='V_{DL2}',
                              info='Piecewise linear characteristics of Vp-Ip',
                              )

        self.fThld2 = ExtendedEvent(self.Volt_dip,
                                    t_ext=self.Thld2,
                                    extend_only=True,
                                    )

        self.VDL1c = VarService(v_str='Lt(VDL1_y, Imaxr)')

        self.VDL2c = VarService(v_str='Lt(VDL2_y, Imaxr)')

        # `Iqmax` not considering mode or `Thld2`
        Iqmax1 = '(zVDL1*(VDL1c*VDL1_y + (1-VDL1c)*Imaxr) + 1e8*(1-zVDL1))'

        # `Ipmax` not considering mode or `Thld2`
        Ipmax1 = '(zVDL2*(VDL2c*VDL2_y + (1-VDL2c)*Imaxr) + 1e8*(1-zVDL2))'

        Ipmax2sq0 = '(Imax**2 - Iqcmd0**2)'

        Ipmax2sq = '(Imax**2 - IqHL_y**2)'

        # `Ipmax20`-squared (non-negative)
        self.Ipmax2sq0 = ConstService(v_str=f'Piecewise((0, Le({Ipmax2sq0}, 0.0)), ({Ipmax2sq0}, True), \
                                              evaluate=False)',
                                      tex_name='I_{pmax20,nn}^2',
                                      )

        self.Ipmax2sq = VarService(v_str=f'Piecewise((0, Le({Ipmax2sq}, 0.0)), ({Ipmax2sq}, True), \
                                           evaluate=False)',
                                   tex_name='I_{pmax2}^2',
                                   )

        Ipmax = f'((1-fThld2) * (SWPQ_s0*sqrt(Ipmax2sq) + SWPQ_s1*{Ipmax1}))'

        Ipmax0 = f'((1-fThld2) * (SWPQ_s0*sqrt(Ipmax2sq0) + SWPQ_s1*{Ipmax1}))'

        self.Ipmax = Algeb(v_str=f'{Ipmax0}',
                           e_str=f'{Ipmax} + (fThld2 * Ipmaxh) - Ipmax',
                           tex_name='I_{pmax}',
                           diag_eps=True,
                           info='Upper limit on Ipcmd',
                           )

        self.Ipmaxh = VarHold(self.Ipmax, hold=self.fThld2)

        Iqmax2sq = '(Imax**2 - IpHL_y**2)'

        Iqmax2sq0 = '(Imax**2 - Ipcmd0**2)'  # initialization equation by using `Ipcmd0`

        self.Iqmax2sq0 = ConstService(v_str=f'Piecewise((0, Le({Iqmax2sq0}, 0.0)), ({Iqmax2sq0}, True), \
                                              evaluate=False)',
                                      tex_name='I_{qmax,nn}^2',
                                      )

        self.Iqmax2sq = VarService(v_str=f'Piecewise((0, Le({Iqmax2sq}, 0.0)), ({Iqmax2sq}, True), \
                                           evaluate=False)',
                                   tex_name='I_{qmax2}^2')

        self.Iqmax = Algeb(v_str=f'(SWPQ_s0*{Iqmax1} + SWPQ_s1*sqrt(Iqmax2sq0))',
                           e_str=f'(SWPQ_s0*{Iqmax1} + SWPQ_s1*sqrt(Iqmax2sq)) - Iqmax',
                           tex_name='I_{qmax}',
                           info='Upper limit on Iqcmd',
                           )

        self.Iqmin = ApplyFunc(self.Iqmax, lambda x: -x, cache=False,
                               tex_name='I_{qmin}',
                               info='Lower limit on Iqcmd',
                               )

        self.Ipmin = ConstService(v_str='0.0', tex_name='I_{pmin}',
                                  info='Lower limit on Ipcmd',
                                  )

        self.PIV = PITrackAWFreeze(u='Vsel_y - s0_y * SWV_s0',
                                   x0='-SWQ_s1 * Iqcmd0',
                                   kp=self.Kvp, ki=self.Kvi, ks=self.config.kvs,
                                   lower=self.Iqmin, upper=self.Iqmax,
                                   freeze=self.Volt_dip,
                                   )

        self.Qsel = Algeb(info='Selection output of QFLAG',
                          v_str='SWQ_s1 * PIV_y + SWQ_s0 * s4_y',
                          e_str='SWQ_s1 * PIV_y + SWQ_s0 * s4_y - Qsel',
                          tex_name='Q_{sel}',
                          )

        # `IpHL_y` is `Ipcmd`
        self.IpHL = GainLimiter(u='s5_y / vp',
                                K=1, R=1,
                                lower=self.Ipmin, upper=self.Ipmax,
                                )

        # `IqHL_y` is `Iqcmd`
        self.IqHL = GainLimiter(u='Qsel + Iqinj',
                                K=1, R=1,
                                lower=self.Iqmin, upper=self.Iqmax)
Example #16
0
    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.gain = ConstService(
            v_str='u / R',
            tex_name='G',
        )

        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 = DeadBandRT(
            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='pref0 + 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'
Example #17
0
    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 * (tm - te - D * (omega - 1))',
            t_const=self.M,
        )

        # 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)',
            ename='P',
            tex_ename='P',
            is_input=True,
        )
        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)',
            ename='Q',
            tex_ename='Q',
            is_input=True,
        )

        # 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',
            vtype=str,
        )

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

        self.Pe = Algeb(tex_name='P_e',
                        info='active power injection',
                        e_str='u * (vd * Id + vq * Iq) - Pe',
                        v_str='u * (vd0 * Id0 + vq0 * Iq0)')
        self.Qe = Algeb(tex_name='Q_e',
                        info='reactive power injection',
                        e_str='u * (vq * Id - vd * Iq) - Qe',
                        v_str='u * (vq0 * Id0 - vd0 * Iq0)')
Example #18
0
    def __init__(self, system, config):
        ACDC2Term.__init__(self, system, config)
        self.rsh = NumParam(default=0.0025,
                            info="AC interface resistance",
                            unit="ohm",
                            z=True,
                            tex_name='r_{sh}')
        self.xsh = NumParam(default=0.06,
                            info="AC interface reactance",
                            unit="ohm",
                            z=True,
                            tex_name='x_{sh}')

        self.control = NumParam(
            info="Control method: 0-PQ, 1-PV, 2-vQ or 3-vV", mandatory=True)
        self.v0 = NumParam(
            default=1.0,
            info="AC voltage setting (PV or vV) or initial guess (PQ or vQ)")
        self.p0 = NumParam(default=0.0,
                           info="AC active power setting",
                           unit="pu")
        self.q0 = NumParam(default=0.0,
                           info="AC reactive power setting",
                           unit="pu")
        self.vdc0 = NumParam(default=1.0,
                             info="DC voltage setting",
                             unit="pu",
                             tex_name='v_{dc0}')

        self.k0 = NumParam(default=0.0, info="Loss coefficient - constant")
        self.k1 = NumParam(default=0.0, info="Loss coefficient - linear")
        self.k2 = NumParam(default=0.0, info="Loss coefficient - quadratic")

        self.droop = NumParam(default=0.0,
                              info="Enable dc voltage droop control",
                              unit="boolean")
        self.K = NumParam(default=0.0, info="Droop coefficient")
        self.vhigh = NumParam(default=9999,
                              info="Upper voltage threshold in droop control",
                              unit="pu")
        self.vlow = NumParam(default=0.0,
                             info="Lower voltage threshold in droop control",
                             unit="pu")

        self.vshmax = NumParam(default=1.1,
                               info="Maximum ac interface voltage",
                               unit="pu")
        self.vshmin = NumParam(default=0.9,
                               info="Minimum ac interface voltage",
                               unit="pu")
        self.Ishmax = NumParam(default=2, info="Maximum ac current", unit="pu")

        # define variables and equations
        self.flags.update({'pflow': True})
        self.group = 'StaticACDC'

        self.gsh = ConstService(
            tex_name='g_{sh}',
            v_str='re(1/(rsh + 1j * xsh))',
        )
        self.bsh = ConstService(
            tex_name='b_{sh}',
            v_str='im(1/(rsh + 1j * xsh))',
        )

        self.mode = Switcher(u=self.control, options=(0, 1, 2, 3))

        self.ash = Algeb(
            info='voltage phase behind the transformer',
            unit='rad',
            tex_name=r'\theta_{sh}',
            v_str='a',
            e_str='u * (gsh * v**2 - gsh * v * vsh * cos(a - ash) - '
            'bsh * v * vsh * sin(a - ash)) - psh',
            diag_eps=True,
        )
        self.vsh = Algeb(
            info='voltage magnitude behind transformer',
            tex_name="V_{sh}",
            unit='p.u.',
            v_str='v0',
            e_str='u * (-bsh * v**2 - gsh * v * vsh * sin(a - ash) + '
            'bsh * v * vsh * cos(a - ash)) - qsh',
            diag_eps=True,
        )
        self.psh = Algeb(
            info='active power injection into VSC',
            tex_name="P_{sh}",
            unit='p.u.',
            v_str='p0 * (mode_s0 + mode_s1)',
            e_str='u * (mode_s0 + mode_s1) * (p0 - psh) + '
            'u * (mode_s2 + mode_s3) * (v1 - v2 - vdc0)',
            diag_eps=True,
        )
        self.qsh = Algeb(
            info='reactive power injection into VSC',
            tex_name="Q_{sh}",
            v_str='q0 * (mode_s0 + mode_s2)',
            e_str='u * (mode_s0 + mode_s2) * (q0 - qsh) + '
            'u * (mode_s1 + mode_s3) * (v0 - v)',
            diag_eps=True,
        )
        self.pdc = Algeb(
            info='DC power injection',
            tex_name="P_{dc}",
            v_str='0',
            e_str='u * (gsh * vsh * vsh - gsh * v * vsh * cos(a - ash) + '
            'bsh * v * vsh * sin(a - ash)) + pdc',
        )
        self.a.e_str = '-psh'
        self.v.e_str = '-qsh'
        self.v1.e_str = '-pdc / (v1 - v2)'
        self.v2.e_str = 'pdc / (v1 - v2)'
Example #19
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.flags.pflow = True
        self.flags.tds = True
        self.flags.tds_init = False
        self.group = 'Motor'

        # services
        self.wb = ConstService(v_str='2 * pi * fn',
                               tex_name=r'\omega_b',
                               )

        self.x0 = ConstService(v_str='xs + xm',
                               tex_name='x_0',
                               )

        self.x1 = ConstService(v_str='xs + xr1 * xm / (xr1 + xm)',
                               tex_name="x'",
                               )

        self.T10 = ConstService(v_str='(xr1 + xm) / (wb * rr1)',
                                tex_name="T'_0",
                                )

        self.M = ConstService(v_str='2 * Hm',
                              tex_name='M',
                              )

        self.aa = ConstService(v_str='c1 + c2 + c3',
                               tex_name=r'\alpha',
                               )

        self.bb = ConstService(v_str='-c2 - 2 * c3',
                               tex_name=r'\beta',
                               )

        # network algebraic variables
        self.a = ExtAlgeb(model='Bus',
                          src='a',
                          indexer=self.bus,
                          tex_name=r'\theta',
                          info='Bus voltage phase angle',
                          e_str='+p',
                          ename='P',
                          tex_ename='P',
                          )

        self.v = ExtAlgeb(model='Bus',
                          src='v',
                          indexer=self.bus,
                          tex_name=r'V',
                          info='Bus voltage magnitude',
                          e_str='+q',
                          ename='Q',
                          tex_ename='Q',
                          )

        self.vd = Algeb(info='d-axis voltage',
                        e_str='-u * v * sin(a) - vd',
                        tex_name=r'V_d',
                        )

        self.vq = Algeb(info='q-axis voltage',
                        e_str='u * v * cos(a) - vq',
                        tex_name=r'V_q',
                        )

        self.slip = State(tex_name=r"\sigma",
                          e_str='u * (tm - te)',
                          t_const=self.M,
                          diag_eps=True,
                          v_str='1.0 * u',
                          )

        self.p = Algeb(tex_name='P',
                       e_str='u * (vd * Id + vq * Iq) - p',
                       v_str='u * (vd * Id + vq * Iq)',
                       )

        self.q = Algeb(tex_name='Q',
                       e_str='u * (vq * Id - vd * Iq) - q',
                       v_str='u * (vq * Id - vd * Iq)',
                       )

        self.e1d = State(info='real part of 1st cage voltage',
                         tex_name="e'_d",
                         v_str='0.05 * u',
                         e_str='u * (wb*slip*e1q - (e1d + (x0 - x1) * Iq)/T10)',
                         diag_eps=True,
                         )

        self.e1q = State(info='imaginary part of 1st cage voltage',
                         tex_name="e'_q",
                         v_str='0.9 * u',
                         e_str='u * (-wb*slip*e1d - (e1q - (x0 - x1) * Id)/T10)',
                         diag_eps=True,
                         )

        self.Id = Algeb(tex_name='I_d',
                        diag_eps=True,
                        )

        self.Iq = Algeb(tex_name='I_q',
                        diag_eps=True,
                        )

        self.te = Algeb(tex_name=r'\tau_e',
                        )

        self.tm = Algeb(tex_name=r'\tau_m',
                        )

        self.tm.v_str = 'u * (aa + bb * slip + c2 * slip * slip)'
        self.tm.e_str = f'{self.tm.v_str} - tm'
Example #20
0
    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',
            ename='P',
            tex_ename='P',
        )

        self.v = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.bus,
            tex_name='V',
            e_str='qp0 + qi0*v + qz0*v*v',
            ename='Q',
            tex_ename='Q',
        )
Example #21
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.flags.tds = True
        self.group = 'RenGen'

        self.a = ExtAlgeb(model='Bus',
                          src='a',
                          indexer=self.bus,
                          tex_name=r'\theta',
                          info='Bus voltage angle',
                          e_str='-Pe',
                          )

        self.v = ExtAlgeb(model='Bus',
                          src='v',
                          indexer=self.bus,
                          tex_name=r'V',
                          info='Bus voltage magnitude',
                          e_str='-Qe',
                          )

        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',
                               )
        self.ra = ExtParam(model='StaticGen',
                           src='ra',
                           indexer=self.gen,
                           tex_name='r_a',
                           export=False,
                           )
        self.xs = ExtParam(model='StaticGen',
                           src='xs',
                           indexer=self.gen,
                           tex_name='x_s',
                           export=False,
                           )

        # --- INITIALIZATION ---
        self.q0gt0 = ConstService('Indicator(q0> 0)', tex_name='z_{q0>0}',
                                  info='flags for q0 below zero',
                                  )
        self.q0lt0 = ConstService('Indicator(q0< 0)', tex_name='z_{q0<0}',
                                  info='flags for q0 below zero',
                                  )

        self.Ipcmd0 = ConstService('p0 / v', info='initial Ipcmd',
                                   tex_name='I_{pcmd0}',
                                   )

        self.Iqcmd0 = ConstService('-q0 / v', info='initial Iqcmd',
                                   tex_name='I_{qcmd0}',
                                   )

        self.Ipcmd = Algeb(tex_name='I_{pcmd}',
                           info='current component for active power',
                           e_str='Ipcmd0 - Ipcmd', v_str='Ipcmd0')

        self.Iqcmd = Algeb(tex_name='I_{qcmd}',
                           info='current component for reactive power',
                           e_str='Iqcmd0 - Iqcmd', v_str='Iqcmd0')

        # reactive power management

        # rate limiting logic (for fault recovery, although it does not detect any recovery)
        #   - activate upper limit when q0 > 0 (self.q0gt0)
        #   - activate lower limit when q0 < 0 (self.q0lt0)

        self.S1 = LagAntiWindupRate(u=self.Iqcmd,
                                    T=self.Tg, K=-1,
                                    lower=-9999, upper=9999, no_lower=True, no_upper=True,
                                    rate_lower=self.Iqrmin, rate_upper=self.Iqrmax,
                                    rate_lower_cond=self.q0lt0, rate_upper_cond=self.q0gt0,
                                    tex_name='S_1',
                                    info='Iqcmd delay',
                                    )  # output `S1_y` == `Iq`

        # piece-wise gain for low voltage active current mgnt.
        self.kLVG = ConstService(v_str='1 / (Lvpnt1 - Lvpnt0)',
                                 tex_name='k_{LVG}',
                                 )

        self.LVG = Piecewise(u=self.v, points=('Lvpnt0', 'Lvpnt1'),
                             funs=('0', '(v - Lvpnt0) * kLVG', '1'),
                             info='Ip gain during low voltage',
                             tex_name='L_{VG}',
                             )

        # piece-wise gain for LVPL
        self.kLVPL = ConstService(v_str='Lvplsw * Lvpl1 / (Brkpt - Zerox)',
                                  tex_name='k_{LVPL}',
                                  )

        self.S2 = Lag(u=self.v, T=self.Tfltr, K=1.0,
                      info='Voltage filter with no anti-windup',
                      tex_name='S_2',
                      )
        self.LVPL = Piecewise(u=self.S2_y,
                              points=('Zerox', 'Brkpt'),
                              funs=('0 + 9999*(1-Lvplsw)',
                                    '(S2_y - Zerox) * kLVPL + 9999 * (1-Lvplsw)',
                                    '9999'),
                              info='Low voltage Ipcmd upper limit',
                              tex_name='L_{VPL}',
                              )

        self.S0 = LagAntiWindupRate(u=self.Ipcmd, T=self.Tg, K=1,
                                    upper=self.LVPL_y, rate_upper=self.Rrpwr,
                                    lower=-9999, rate_lower=-9999,
                                    no_lower=True, rate_no_lower=True,
                                    tex_name='S_0',
                                    )  # `S0_y` is the output `Ip` in the block diagram

        self.Ipout = Algeb(e_str='S0_y * LVG_y -Ipout',
                           v_str='Ipcmd * LVG_y',
                           info='Output Ip current',
                           tex_name='I_{pout}',
                           )

        # high voltage part
        self.HVG = GainLimiter(u='v - Volim', K=self.Khv, info='High voltage gain block',
                               lower=0, upper=999, no_upper=True,
                               tex_name='H_{VG}'
                               )
        self.HVG.lim.no_warn = True

        self.Iqout = GainLimiter(u='S1_y- HVG_y', K=1, lower=self.Iolim, upper=9999,
                                 no_upper=True, info='Iq output block',
                                 tex_name='I^{qout}',
                                 )  # `Iqout_y` is the final Iq output

        self.Pe = Algeb(tex_name='P_e', info='Active power output',
                        v_str='p0', e_str='Ipout * v - Pe')
        self.Qe = Algeb(tex_name='Q_e', info='Reactive power output',
                        v_str='q0', e_str='Iqout_y * v - Qe')
Example #22
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.group = 'SynGen'

        self.group_param_exception = ['Sn', 'M', 'D']
        self.group_var_exception = ['vd', 'vq', 'Id', 'Iq', 'tm', 'te', 'vf', 'XadIfd']
        self.flags.tds = True

        self.subidx = ExtParam(model='StaticGen',
                               src='subidx',
                               indexer=self.gen,
                               export=False,
                               info='Generator idx in plant; only used by PSS/E data'
                               )

        self.zs = ConstService('ra + 1j * xs', vtype=complex,
                               info='impedance',
                               )
        self.zs2n = ConstService('ra * ra - xs * xs',
                                 info='ra^2 - xs^2',
                                 )

        # get power flow solutions

        self.p = ExtService(model='StaticGen', src='p',
                            indexer=self.gen,
                            )
        self.q = ExtService(model='StaticGen', src='q',
                            indexer=self.gen,
                            )
        self.Ec = ConstService('v * exp(1j * a) +'
                               'conj((p + 1j * q) / (v * exp(1j * a))) * (ra + 1j * xs)',
                               vtype=complex,
                               tex_name='E_c',
                               )

        self.E0 = ConstService('abs(Ec)', tex_name='E_0')
        self.delta0 = ConstService('arg(Ec)', tex_name=r'\delta_0')

        # Note: `Vts` and `fts` are assigned by TimeSeries before initializing this model.
        self.Vts = ConstService()
        self.fts = ConstService()

        self.ifscale = ConstService('1/fscale', tex_name='1/f_{scale}')
        self.iVscale = ConstService('1/Vscale', tex_name='1/V_{scale}')

        self.foffs = ConstService('fts * ifscale - 1', tex_name='f_{offs}')
        self.Voffs = ConstService('Vts * iVscale - E0', tex_name='V_{offs}')

        self.Vflt = State(info='filtered voltage',
                          t_const=self.Tv,
                          v_str='(iVscale * Vts - Voffs)',
                          e_str='(iVscale * Vts - Voffs) - Vflt',
                          unit='pu',
                          tex_name='V_{flt}',
                          )

        self.omega = State(info='filtered frequency',
                           t_const=self.Tf,
                           v_str='fts * ifscale - foffs',
                           e_str='(ifscale * fts - foffs) - omega',
                           unit='pu',
                           tex_name=r'\omega',
                           )

        self.delta = State(info='rotor angle',
                           unit='rad',
                           v_str='delta0',
                           tex_name=r'\delta',
                           e_str='u * (2 * pi * fn) * (omega - 1)',
                           )

        # --- Power injections are obtained by sympy ---

        # >>> from sympy import symbols, sin, cos, conjugate
        # >>> Vflt, delta, v, a, ra, xs = symbols('Vflt delta v a ra xs', real=True)

        # >>> S = -v * (cos(a) + 1j*sin(a)) * \
        #         conjugate((Vflt * (cos(delta)+1j*sin(delta)) - v*(cos(a)+1j*sin(a))) / (ra+1j*xs))
        # >>> S.simplify().as_real_imag()

        self.a = ExtAlgeb(model='Bus',
                          src='a',
                          indexer=self.bus,
                          tex_name=r'\theta',
                          info='Bus voltage phase angle',
                          e_str='Vflt*v*xs*sin(a - delta)/(ra*ra + xs*xs) + '
                                'ra*v*(-Vflt*cos(a - delta) + v)/(ra*ra + xs*xs)',
                          ename='P',
                          tex_ename='P',
                          )
        self.v = ExtAlgeb(model='Bus',
                          src='v',
                          indexer=self.bus,
                          tex_name=r'V',
                          info='Bus voltage magnitude',
                          ename='Q',
                          e_str='-Vflt*ra*v*sin(a - delta)/(ra*ra + xs*xs) + '
                                'v*xs*(-Vflt*cos(a - delta) + v)/(ra*ra + xs*xs)',
                          tex_ename='Q',
                          )
Example #23
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)

        self.flags.tds = True
        self.group = 'RenGovernor'

        self.reg = ExtParam(
            model='RenExciter',
            src='reg',
            indexer=self.ree,
            export=False,
        )
        self.Sn = ExtParam(
            model='RenGen',
            src='Sn',
            indexer=self.reg,
            tex_name='S_n',
            export=False,
        )

        self.wge = ExtAlgeb(
            model='RenExciter',
            src='wg',
            indexer=self.ree,
            export=False,
            e_str='-1.0 + s2_y',
            ename='wg',
            tex_ename=r'\omega_g',
        )

        self.Pe = ExtAlgeb(model='RenGen',
                           src='Pe',
                           indexer=self.reg,
                           export=False,
                           info='Retrieved Pe of RenGen')

        self.Pe0 = ExtService(
            model='RenGen',
            src='Pe',
            indexer=self.reg,
            tex_name='P_{e0}',
        )

        self.Ht2 = ConstService(v_str='2 * Ht', tex_name='2H_t')

        self.Hg2 = ConstService(v_str='2 * Hg', tex_name='2H_g')

        self.wr0 = Algeb(
            tex_name=r'\omega_{r0}',
            unit='p.u.',
            v_str='w0',
            e_str='w0 - wr0',
            info='speed set point',
        )

        self.Pm = Algeb(
            tex_name='P_m',
            info='Mechanical power',
            e_str='Pe0 - Pm',
            v_str='Pe0',
        )

        # `s1_y` is `wt`
        self.s1 = Integrator(
            u='(Pm / s1_y) - (Kshaft * s3_y ) - pd',
            T=self.Ht2,
            K=1.0,
            y0='wr0',
        )

        self.wt = AliasState(self.s1_y, tex_name=r'\omega_t')

        # `s2_y` is `wg`
        self.s2 = Integrator(
            u='-(Pe / s2_y) + (Kshaft * s3_y ) + pd',
            T=self.Hg2,
            K=1.0,
            y0='wr0',
        )

        self.wg = AliasState(self.s2_y, tex_name=r'\omega_g')

        # TODO: `s3_y` needs to be properly reinitialized with the new `wr0`
        self.s3 = Integrator(
            u='s1_y - s2_y',
            T=1.0,
            K=1.0,
            y0='Pe0 / wr0 / Kshaft',
        )

        self.pd = Algeb(
            tex_name='P_d',
            info='Output after damping',
            v_str='0.0',
            e_str='Dshaft * (s1_y - s2_y) - pd',
        )
Example #24
0
    def __init__(self, system, config):
        PSSBase.__init__(self, system, config)

        # ALL THE FOLLOWING IS FOR INPUT 2
        # retrieve indices of bus and bus freq
        self.buss2 = DataSelect(self.busr2,
                                self.bus,
                                info='selected bus (bus or busr)')

        self.busfreq2 = DeviceFinder(self.busf2,
                                     link=self.buss2,
                                     idx_name='bus',
                                     default_model='BusFreq',
                                     info='bus frequency idx')

        # from Bus
        self.v2 = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.buss2,
            tex_name=r'V',
            info='Bus (or busr2, if given) terminal voltage',
        )

        # from BusFreq 2
        self.f2 = ExtAlgeb(model='FreqMeasurement',
                           src='f',
                           indexer=self.busfreq2,
                           export=False,
                           info='Bus frequency 2')

        # Config
        self.config.add(OrderedDict([('freq_model', 'BusFreq')]))
        self.config.add_extra(
            '_help', {'freq_model': 'default freq. measurement model'})
        self.config.add_extra('_alt', {'freq_model': ('BusFreq', )})

        self.busf.model = self.config.freq_model
        self.busf2.model = self.config.freq_model

        # input signal switch
        self.dv = Derivative(self.v)
        self.dv2 = Derivative(self.v2)

        self.SnSb = ExtService(
            model='SynGen',
            src='M',
            indexer=self.syn,
            attr='pu_coeff',
            info='Machine base to sys base factor for power',
            tex_name='(Sb/Sn)')

        self.SW = Switcher(
            u=self.MODE,
            options=[0, 1, 2, 3, 4, 5, 6, np.nan],
        )
        self.SW2 = Switcher(
            u=self.MODE2,
            options=[0, 1, 2, 3, 4, 5, 6, np.nan],
        )

        # Input signals
        self.sig = Algeb(
            tex_name='S_{ig}',
            info='Input signal',
        )
        self.sig.v_str = 'SW_s1*(omega-1) + SW_s2*0 + SW_s3*(tm0/SnSb) + ' \
                         'SW_s4*(tm-tm0) + SW_s5*v + SW_s6*0'
        self.sig.e_str = 'SW_s1*(omega-1) + SW_s2*(f-1) + SW_s3*(te/SnSb) + ' \
                         'SW_s4*(tm-tm0) + SW_s5*v + SW_s6*dv_v - sig'

        self.sig2 = Algeb(
            tex_name='S_{ig2}',
            info='Input signal 2',
        )
        self.sig2.v_str = 'SW2_s1*(omega-1) + SW2_s2*0 + SW2_s3*(tm0/SnSb) + ' \
                          'SW2_s4*(tm-tm0) + SW2_s5*v2 + SW2_s6*0'
        self.sig2.e_str = 'SW2_s1*(omega-1) + SW2_s2*(f2-1) + SW2_s3*(te/SnSb) + ' \
                          'SW2_s4*(tm-tm0) + SW2_s5*v2 + SW2_s6*dv2_v - sig2'

        self.L1 = Lag(
            u=self.sig,
            K=self.K1,
            T=self.T1,
            info='Transducer 1',
        )
        self.L2 = Lag(
            u=self.sig2,
            K=self.K2,
            T=self.T2,
            info='Transducer 2',
        )
        self.IN = Algeb(
            tex_name='I_N',
            info='Sum of inputs',
            v_str='L1_y + L2_y',
            e_str='L1_y + L2_y - IN',
        )

        self.WO = WashoutOrLag(
            u=self.IN,
            K=self.T3,
            T=self.T4,
        )

        self.LL1 = LeadLag(
            u=self.WO_y,
            T1=self.T5,
            T2=self.T6,
            zero_out=True,
        )

        self.LL2 = LeadLag(
            u=self.LL1_y,
            T1=self.T7,
            T2=self.T8,
            zero_out=True,
        )

        self.LL3 = LeadLag(
            u=self.LL2_y,
            T1=self.T9,
            T2=self.T10,
            zero_out=True,
        )

        self.VSS = GainLimiter(u=self.LL3_y,
                               K=1,
                               R=1,
                               lower=self.LSMIN,
                               upper=self.LSMAX)

        self.VOU = ConstService(v_str='VCUr + v0')
        self.VOL = ConstService(v_str='VCLr + v0')

        self.OLIM = Limiter(u=self.v,
                            lower=self.VOL,
                            upper=self.VOU,
                            info='output limiter')

        self.vsout.e_str = 'OLIM_zi * VSS_y - vsout'
Example #25
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=complex)
        self._S = ConstService(v_str='p0 - 1j * q0',
                               tex_name='S',
                               info='complex terminal power',
                               vtype=complex)
        self._Zs = ConstService(v_str='ra + 1j * xd2',
                                tex_name='Z_s',
                                info='equivalent impedance',
                                vtype=complex)
        self._It = ConstService(v_str='_S / conj(_V)',
                                tex_name='I_t',
                                info='complex terminal current',
                                vtype=complex)
        self._Is = ConstService(tex_name='I_s',
                                v_str='_It + _V / _Zs',
                                info='equivalent current source',
                                vtype=complex)

        self.psi20 = ConstService(
            tex_name=r"\psi''_0",
            v_str='_Is * _Zs',
            info='sub-transient flux linkage in stator reference',
            vtype=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=complex)
        self.psi20_dq = ConstService(tex_name=r"\psi''_{0,dq}",
                                     v_str='psi20 * _Tdq',
                                     vtype=complex)
        self.It_dq = ConstService(tex_name=r"I_{t,dq}",
                                  v_str='conj(_It * _Tdq)',
                                  vtype=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='u * 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='u * 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='u * 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'
Example #26
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.flags.tds = True
        self.group = 'RenGen'

        self.a = ExtAlgeb(
            model='Bus',
            src='a',
            indexer=self.bus,
            tex_name=r'\theta',
            info='Bus voltage angle',
            e_str='-u * Pe',
        )
        self.v = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.bus,
            tex_name='V',
            info='Bus voltage magnitude',
            e_str='-u * Qe',
        )

        self.p0s = ExtService(
            model='StaticGen',
            src='p',
            indexer=self.gen,
            tex_name=r'P_{0s}',
            info='total P of the static gen',
        )
        self.q0s = ExtService(
            model='StaticGen',
            src='q',
            indexer=self.gen,
            tex_name=r'Q_{0s}',
            info='total Q of the static gen',
        )
        self.Pref = ConstService(
            v_str='gammap * p0s',
            tex_name='P_{ref}',
            info='Initial P for the REGCV1 device',
        )
        self.Qref = ConstService(
            v_str='gammaq * q0s',
            tex_name='Q_{ref}',
            info='Initial Q for the REGCV1 device',
        )

        self.vref = ExtService(
            model='StaticGen',
            src='v',
            indexer=self.gen,
            tex_name=r'V_{ref}',
            info='initial v of the static gen',
        )

        # --- INITIALIZATION ---
        self.ixs = ConstService(
            v_str='1/xs',
            tex_name=r'1/xs',
        )
        self.Id0 = ConstService(
            tex_name=r'I_{d0}',
            v_str='u * Pref / v',
        )
        self.Iq0 = ConstService(
            tex_name=r'I_{q0}',
            v_str='- u * Qref / v',
        )

        self.vd0 = ConstService(
            tex_name=r'v_{d0}',
            v_str='u * v',
        )
        self.vq0 = ConstService(
            tex_name=r'v_{q0}',
            v_str='0',
        )

        self.Pref2 = Algeb(
            tex_name=r'P_{ref2}',
            info='active power reference after adjusting by frequency',
            e_str='u * Pref - dw * kw - Pref2',
            v_str='u * Pref')

        self.vref2 = Algeb(
            tex_name=r'v_{ref2}',
            info='voltage reference after adjusted by reactive power',
            e_str='(u * Qref - Qe) * kv + vref - vref2',
            v_str='u * vref')

        self.dw = State(info='delta virtual rotor speed',
                        unit='pu (Hz)',
                        v_str='0',
                        tex_name=r'\Delta\omega',
                        e_str='Pref2 - Pe - D * dw',
                        t_const=self.M)

        self.omega = Algeb(info='virtual rotor speed',
                           unit='pu (Hz)',
                           v_str='u',
                           tex_name=r'\omega',
                           e_str='1 + dw - omega')

        self.delta = State(info='virtual delta',
                           unit='rad',
                           v_str='a',
                           tex_name=r'\delta',
                           e_str='2 * pi * fn * dw')

        self.vd = Algeb(tex_name='V_d',
                        info='d-axis voltage',
                        e_str='u * v * cos(delta - a) - vd',
                        v_str='vd0')
        self.vq = Algeb(tex_name='V_q',
                        info='q-axis voltage',
                        e_str='- u * v * sin(delta - a) - vq',
                        v_str='vq0')

        self.Pe = Algeb(tex_name='P_e',
                        info='active power injection from VSC',
                        e_str='vd * Id + vq * Iq - Pe',
                        v_str='Pref')
        self.Qe = Algeb(tex_name='Q_e',
                        info='reactive power injection from VSC',
                        e_str='- vd * Iq + vq * Id - Qe',
                        v_str='Qref')

        self.Id = Algeb(
            tex_name='I_d',
            info='d-axis current',
            v_str='Id0',
            diag_eps=True,
        )
        self.Iq = Algeb(
            tex_name='I_q',
            info='q-axis current',
            v_str='Iq0',
            diag_eps=True,
        )
Example #27
0
    def __init__(self):
        # internal voltage and rotor angle calculation
        self.xq = ExtService(
            model='GENCLS',
            src='xd1',
            indexer=self.idx,
        )
        self._V = ConstService(
            v_str='v * exp(1j * a)',
            tex_name='V_c',
            vtype=complex,
        )
        self._S = ConstService(
            v_str='p0 - 1j * q0',
            tex_name='S',
            vtype=complex,
        )
        self._I = ConstService(
            v_str='_S / conj(_V)',
            tex_name='I_c',
            vtype=complex,
        )
        self._E = ConstService(tex_name='E', vtype=complex)
        self._deltac = ConstService(tex_name=r'\delta_c', vtype=complex)
        self.delta0 = ConstService(tex_name=r'\delta_0')

        self.vdq = ConstService(
            v_str='u * (_V * exp(1j * 0.5 * pi - _deltac))',
            tex_name='V_{dq}',
            vtype=complex)
        self.Idq = ConstService(
            v_str='u * (_I * exp(1j * 0.5 * pi - _deltac))',
            tex_name='I_{dq}',
            vtype=complex)

        self.Id0 = ConstService(v_str='re(Idq)', tex_name=r'I_{d0}')
        self.Iq0 = ConstService(v_str='im(Idq)', tex_name=r'I_{q0}')
        self.vd0 = ConstService(v_str='re(vdq)', tex_name=r'V_{d0}')
        self.vq0 = ConstService(v_str='im(vdq)', 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.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')
        self.vf0 = ConstService(tex_name=r'v_{f0}')

        # initialization of internal voltage and delta
        self._E.v_str = '_V + _I * (ra + 1j * xq)'
        self._deltac.v_str = 'log(_E / abs(_E))'
        self.delta0.v_str = 'u * im(_deltac)'

        self.Id.e_str += '+ xq * Id - vf'
        self.Iq.e_str += '+ xq * Iq'
        self.vf0.v_str = '(vq0 + ra * Iq0) + xq * Id0'
Example #28
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'
Example #29
0
    def __init__(self, system, config):
        TGBase.__init__(self, system, config)

        self.VELMn = ConstService(
            v_str='-VELM',
            tex_name='-VELM',
        )
        self.tr = ConstService(
            v_str='r * Tr',
            tex_name='r*Tr',
        )
        self.gr = ConstService(
            v_str='1/r',
            tex_name='1/r',
        )
        self.ratel = ConstService(
            v_str='- VELM - gr',
            tex_name='rate_l',
        )
        self.rateu = ConstService(
            v_str='VELM - gr',
            tex_name='rate_u',
        )
        self.q0 = ConstService(
            v_str='tm0 / At + qNL',
            tex_name='q_0',
        )
        self.pref = Algeb(
            info='Reference power input',
            tex_name='P_{ref}',
            v_str='R * q0',
            e_str='R * q0 - pref',
        )

        self.wd = Algeb(
            info='Generator speed deviation',
            unit='p.u.',
            tex_name=r'\omega_{dev}',
            v_str='0',
            e_str='ue * (omega - wref) - wd',
        )
        self.pd = Algeb(
            info='Pref plus speed deviation times gain',
            unit='p.u.',
            tex_name="P_d",
            v_str='0',
            e_str='ue * (- wd + pref + paux - R * dg) - pd',
        )

        self.LG = Lag(
            u=self.pd,
            K=1,
            T=self.Tf,
            info='filter after speed deviation (e)',
        )

        self.gtpos = State(info='State in gate position (c)',
                           unit='rad',
                           v_str='q0',
                           tex_name=r'\delta',
                           e_str='LG_y')

        self.dgl = VarService(
            tex_name='dg_{lower}',
            info='dg lower limit',
            v_str='- VELM - gr * LG_y',
        )
        self.dgu = VarService(
            tex_name='dg_{upper}',
            info='dg upper limit',
            v_str='VELM - gr * LG_y',
        )
        self.dg_lim = AntiWindupRate(
            u=self.gtpos,
            lower=self.GMIN,
            upper=self.GMAX,
            rate_lower=self.dgl,
            rate_upper=self.dgu,
            tex_name='lim_{dg}',
            info='gate velocity and position limiter',
        )

        self.dg = Algeb(
            info='desired gate (c)',
            unit='p.u.',
            tex_name="dg",
            v_str='q0',
            e_str='gtpos + gr * LG_y - dg',
        )

        self.LAG = Lag(
            u=self.dg,
            K=1,
            T=self.Tg,
            info='gate opening (g)',
        )
        self.h = Algeb(
            info='turbine head',
            unit='p.u.',
            tex_name="h",
            e_str='q_y**2 / LAG_y**2 - h',
            v_str='1',
        )
        self.q = Integrator(u="1 - q_y**2 / LAG_y**2",
                            T=self.Tw,
                            K=1,
                            y0='q0',
                            check_init=False,
                            info="turbine flow (q)")

        self.pout.e_str = 'ue * (At * h * (q_y - qNL) - Dt * wd * LAG_y) - pout'