Example #1
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.group = 'TurbineGov'
        self.flags.update({'tds': True})
        self.Sn = ExtParam(
            src='Sn',
            model='SynGen',
            indexer=self.syn,
            tex_name='S_m',
            info='Rated power from generator',
            unit='MVA',
            export=False,
        )
        self.Vn = ExtParam(
            src='Vn',
            model='SynGen',
            indexer=self.syn,
            tex_name='V_m',
            info='Rated voltage from generator',
            unit='kV',
            export=False,
        )
        self.tm0 = ExtService(src='tm',
                              model='SynGen',
                              indexer=self.syn,
                              tex_name=r'\tau_{m0}',
                              info='Initial mechanical input')
        self.omega = ExtState(src='omega',
                              model='SynGen',
                              indexer=self.syn,
                              tex_name=r'\omega',
                              info='Generator speed',
                              unit='p.u.')

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

        self.tm = ExtAlgeb(
            src='tm',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'\tau_m',
            e_str='u * (pout - tm0)',
            info='Mechanical power to generator',
        )
        self.pout = Algeb(
            info='Turbine final output power',
            tex_name='P_{out}',
            v_str='tm0',
        )
        self.wref = Algeb(
            info='Speed reference variable',
            tex_name=r'\omega_{ref}',
            v_str='wref0',
            e_str='wref0 - wref',
        )
Example #2
0
    def __init__(self, system, config):
        ACEData.__init__(self)
        Model.__init__(self, system, config)

        self.flags.tds = True

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

        self.area = ExtParam(model='Bus',
                             src='area',
                             indexer=self.bus,
                             export=False)

        self.busf.model = self.config.freq_model
        self.busfreq = DeviceFinder(self.busf, link=self.bus, idx_name='bus')

        self.f = ExtAlgeb(
            model='FreqMeasurement',
            src='f',
            indexer=self.busfreq,
            export=False,
            info='Bus frequency',
        )

        self.ace = Algeb(
            info='area control error',
            unit='MW (p.u.)',
            tex_name='ace',
            e_str='10 * bias * (f - 1) - ace',
        )
Example #3
0
    def __init__(self, system, config):
        AreaData.__init__(self)
        Model.__init__(self, system, config)
        self.group = 'Collection'
        self.flags.pflow = True
        self.flags.tds = True

        self.Bus = BackRef()
        self.ACTopology = BackRef()

        # --------------------Experiment Zone--------------------
        self.Vn = ExtParam(model='Bus',
                           src='Vn',
                           indexer=self.ACTopology,
                           export=False)
        self.Vn_sum = NumReduce(u=self.Vn, fun=np.sum, ref=self.Bus)
        self.Vn_sum_rep = NumRepeat(u=self.Vn_sum, ref=self.Bus)

        self.a = ExtAlgeb(model='ACTopology',
                          src='a',
                          indexer=self.ACTopology,
                          info='Bus voltage angle')
        self.v = ExtAlgeb(model='ACTopology',
                          src='v',
                          indexer=self.ACTopology,
                          info='Bus voltage magnitude')
Example #4
0
    def __init__(self, system, config):
        AreaData.__init__(self)
        Model.__init__(self, system, config)
        self.group = 'Collection'
        self.flags.update({'pflow': True, 'tds': True})

        self.Bus = RefParam(export=False)
        self.ACTopology = RefParam(export=False)

        # --------------------Experiment Zone--------------------
        self.Vn = ExtParam(model='Bus',
                           src='Vn',
                           indexer=self.ACTopology,
                           export=False)
        self.Vn_sum = ReducerService(u=self.Vn, fun=np.sum, ref=self.Bus)
        self.Vn_sum_rep = RepeaterService(u=self.Vn_sum, ref=self.Bus)

        self.a = ExtAlgeb(model='ACTopology',
                          src='a',
                          indexer=self.ACTopology,
                          info='Bus voltage angle')
        self.v = ExtAlgeb(model='ACTopology',
                          src='v',
                          indexer=self.ACTopology,
                          info='Bus voltage magnitude')
Example #5
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)',
        )

        self.v = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.bus,
            tex_name='V',
            e_str='qv0 * (v ** aq) * (f ** bq)',
        )
Example #6
0
    def __init__(self, system, config):
        ACEData.__init__(self)
        Model.__init__(self, system, config)

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

        self.config.add(
            OrderedDict([
                ('freq_model', 'BusFreq'),
                ('interval', 4.0),
                ('offset', 0.0),
            ]))
        self.config.add_extra(
            '_help', {
                'freq_model': 'default freq. measurement model',
                'interval': 'sampling time interval',
                'offset': 'sampling time offset'
            })

        self.config.add_extra('_alt', {'freq_model': ('BusFreq', )})

        self.area = ExtParam(model='Bus',
                             src='area',
                             indexer=self.bus,
                             export=False)

        self.busf.model = self.config.freq_model
        self.busfreq = DeviceFinder(self.busf, link=self.bus, idx_name='bus')

        self.f = ExtAlgeb(
            model='FreqMeasurement',
            src='f',
            indexer=self.busfreq,
            export=False,
            info='Bus frequency',
        )

        self.fs = Sampling(
            self.f,
            interval=self.config.interval,
            offset=self.config.offset,
            tex_name='f_s',
            info='Sampled freq.',
        )

        self.ace = Algeb(
            info='area control error',
            unit='MW (p.u.)',
            tex_name='ace',
            e_str='10 * bias * (fs_v - 1) - ace',
        )
Example #7
0
    def __init__(self, system, config):
        ACEData.__init__(self)
        Model.__init__(self, system, config)

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

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

        self.area = ExtParam(model='Bus',
                             src='area',
                             indexer=self.bus,
                             export=False)

        self.busf.model = self.config.freq_model
        self.busfreq = DeviceFinder(self.busf,
                                    link=self.bus,
                                    idx_name='bus',
                                    default_model='BusFreq')

        self.imva = ConstService(v_str='1/sys_mva',
                                 info='reciprocal of system mva',
                                 tex_name='1/S_{b, sys}')

        self.f = ExtAlgeb(model='FreqMeasurement',
                          src='f',
                          indexer=self.busfreq,
                          export=False,
                          info='Bus frequency',
                          unit='p.u. (Hz)')
        self.ace = Algeb(
            info='area control error',
            unit='p.u. (MW)',
            tex_name='ace',
            e_str='10 * (bias * imva) * sys_f * (f - 1) - ace',
        )
Example #8
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.group = 'Exciter'
        self.flags.tds = True

        # from synchronous generators, get Sn, Vn, bus; tm0; omega
        self.Sn = ExtParam(
            src='Sn',
            model='SynGen',
            indexer=self.syn,
            tex_name='S_m',
            info='Rated power from generator',
            unit='MVA',
            export=False,
        )
        self.Vn = ExtParam(
            src='Vn',
            model='SynGen',
            indexer=self.syn,
            tex_name='V_m',
            info='Rated voltage from generator',
            unit='kV',
            export=False,
        )
        self.vf0 = ExtService(src='vf',
                              model='SynGen',
                              indexer=self.syn,
                              tex_name='v_{f0}',
                              info='Steady state excitation voltage')
        self.bus = ExtParam(
            src='bus',
            model='SynGen',
            indexer=self.syn,
            tex_name='bus',
            info='Bus idx of the generators',
            export=False,
            vtype=str,
        )
        self.omega = ExtState(
            src='omega',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'\omega',
            info='Generator speed',
        )
        self.vf = ExtAlgeb(
            src='vf',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'v_f',
            e_str='u * (vout - vf0)',
            info='Excitation field voltage to generator',
        )
        self.XadIfd = ExtAlgeb(
            src='XadIfd',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'X_{ad}I_{fd}',
            info='Armature excitation current',
        )
        # from bus, get a and v
        self.a = ExtAlgeb(
            model='Bus',
            src='a',
            indexer=self.bus,
            tex_name=r'\theta',
            info='Bus voltage phase angle',
        )
        self.v = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.bus,
            tex_name=r'V',
            info='Bus voltage magnitude',
        )

        # output excitation voltage
        self.vout = Algeb(
            info='Exciter final output voltage',
            tex_name='v_{out}',
            v_str='vf0',
        )
Example #9
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 / M) * (tm - te - D * (omega - 1))')

        # network algebraic variables
        self.a = ExtAlgeb(model='Bus',
                          src='a',
                          indexer=self.bus,
                          tex_name=r'\theta',
                          info='Bus voltage phase angle',
                          e_str='-u * (vd * Id + vq * Iq)')
        self.v = ExtAlgeb(model='Bus',
                          src='v',
                          indexer=self.bus,
                          tex_name=r'V',
                          info='Bus voltage magnitude',
                          e_str='-u * (vq * Id - vd * Iq)')

        # algebraic variables
        # Need to be provided by specific generator models
        self.Id = Algeb(info='d-axis current',
                        v_str='u * Id0',
                        tex_name=r'I_d',
                        e_str='')  # to be completed by subclasses
        self.Iq = Algeb(info='q-axis current',
                        v_str='u * Iq0',
                        tex_name=r'I_q',
                        e_str='')  # to be completed

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

        self.tm = Algeb(info='mechanical torque',
                        tex_name=r'\tau_m',
                        v_str='tm0',
                        e_str='tm0 - tm')
        self.te = Algeb(
            info='electric torque',
            tex_name=r'\tau_e',
            v_str='u * tm0',
            e_str='u * (psid * Iq - psiq * Id) - te',
        )
        self.vf = Algeb(info='excitation voltage',
                        unit='pu',
                        v_str='u * vf0',
                        e_str='u * vf0 - vf',
                        tex_name=r'v_f')

        self._vfc = InitChecker(
            u=self.vf,
            info='(vf range)',
            lower=self.config.vf_lower,
            upper=self.config.vf_upper,
        )

        self.XadIfd = Algeb(tex_name='X_{ad}I_{fd}',
                            info='d-axis armature excitation current',
                            unit='p.u (kV)',
                            v_str='u * vf0',
                            e_str='u * vf0 - XadIfd'
                            )  # e_str to be provided. Not available in GENCLS

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

        # declaring `Vn_bus` as ExtParam will fail for PSS/E parser
        self.Vn_bus = ExtService(
            model='Bus',
            src='Vn',
            indexer=self.bus,
        )
        self._vnc = InitChecker(
            u=self.Vn,
            info='Vn and Bus Vn',
            equal=self.Vn_bus,
        )

        # ----------service consts for initialization----------
        self.p0s = ExtService(
            model='StaticGen',
            src='p',
            indexer=self.gen,
            tex_name='P_{0s}',
            info='initial P of the static gen',
        )
        self.q0s = ExtService(
            model='StaticGen',
            src='q',
            indexer=self.gen,
            tex_name='Q_{0s}',
            info='initial Q of the static gen',
        )
        self.p0 = ConstService(
            v_str='p0s * gammap',
            tex_name='P_0',
            info='initial P of this gen',
        )
        self.q0 = ConstService(
            v_str='q0s * gammaq',
            tex_name='Q_0',
            info='initial Q of this gen',
        )
Example #10
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.group = 'Calculation'
        self.flags.update({'tds': True})

        self.SynGen = BackRef(info='Back reference to SynGen idx')

        self.SynGenIdx = RefFlatten(ref=self.SynGen)

        self.M = ExtParam(
            model='SynGen',
            src='M',
            indexer=self.SynGenIdx,
            export=False,
            info='Linearly stored SynGen.M',
        )

        self.wgen = ExtState(
            model='SynGen',
            src='omega',
            indexer=self.SynGenIdx,
            tex_name=r'\omega_{gen}',
            info='Linearly stored SynGen.omega',
        )
        self.agen = ExtState(
            model='SynGen',
            src='delta',
            indexer=self.SynGenIdx,
            tex_name=r'\delta_{gen}',
            info='Linearly stored SynGen.delta',
        )
        self.d0 = ExtService(
            model='SynGen',
            src='delta',
            indexer=self.SynGenIdx,
            tex_name=r'\delta_{gen,0}',
            info='Linearly stored initial delta',
        )

        self.a0 = ExtService(
            model='SynGen',
            src='omega',
            indexer=self.SynGenIdx,
            tex_name=r'\omega_{gen,0}',
            info='Linearly stored initial omega',
        )

        self.Mt = NumReduce(
            u=self.M,
            tex_name='M_t',
            fun=np.sum,
            ref=self.SynGen,
            info='Summation of M by COI index',
        )

        self.Mr = NumRepeat(
            u=self.Mt,
            tex_name='M_{tr}',
            ref=self.SynGen,
            info='Repeated summation of M',
        )

        self.Mw = ConstService(tex_name='M_w',
                               info='Inertia weights',
                               v_str='M/Mr')

        self.d0w = ConstService(tex_name=r'\delta_{gen,0,w}',
                                v_str='d0 * Mw',
                                info='Linearly stored weighted delta')

        self.a0w = ConstService(tex_name=r'\omega_{gen,0,w}',
                                v_str='a0 * Mw',
                                info='Linearly stored weighted omega')

        self.d0a = NumReduce(
            u=self.d0w,
            tex_name=r'\delta_{gen,0,avg}',
            fun=np.sum,
            ref=self.SynGen,
            info='Average initial delta',
            cache=False,
        )

        self.a0a = NumReduce(
            u=self.a0w,
            tex_name=r'\omega_{gen,0,avg}',
            fun=np.sum,
            ref=self.SynGen,
            info='Average initial omega',
            cache=False,
        )

        self.pidx = IdxRepeat(u=self.idx,
                              ref=self.SynGen,
                              info='Repeated COI.idx')

        # Note:
        # Even if d(omega) /d (omega) = 1, it is still stored as a lambda function.
        # When no SynGen is referencing any COI, j_update will not be called,
        # and Jacobian will become singular. `diag_eps = True` needs to be used.

        # Note:
        # Do not assign `v_str=1` for `omega`. Otherwise, COIs with no connected generators will
        # fail to initialize.
        self.omega = Algeb(
            tex_name=r'\omega_{coi}',
            info='COI speed',
            v_str='a0a',
            v_setter=True,
            e_str='-omega',
            diag_eps=True,
        )
        self.delta = Algeb(
            tex_name=r'\delta_{coi}',
            info='COI rotor angle',
            v_str='d0a',
            v_setter=True,
            e_str='-delta',
            diag_eps=True,
        )

        # Note:
        # `omega_sub` or `delta_sub` must not provide `v_str`.
        # Otherwise, values will be incorrectly summed for `omega` and `delta`.
        self.omega_sub = ExtAlgeb(
            model='COI',
            src='omega',
            e_str='Mw * wgen',
            indexer=self.pidx,
            info='COI frequency contribution of each generator')
        self.delta_sub = ExtAlgeb(
            model='COI',
            src='delta',
            e_str='Mw * agen',
            indexer=self.pidx,
            info='COI angle contribution of each generator')
Example #11
0
    def __init__(self, system, config):
        super().__init__(system, config)
        self.group = 'SynGen'
        self.flags.update({
            'tds': True,
            'nr_iter': False,
        })

        # state variables
        self.delta = State(info='rotor angle',
                           unit='rad',
                           v_str='delta0',
                           tex_name=r'\delta',
                           e_str='u * (2 * pi * fn) * (omega - 1)')
        self.omega = State(info='rotor speed',
                           unit='pu (Hz)',
                           v_str='u',
                           tex_name=r'\omega',
                           e_str='(u / M) * (tm - te - D * (omega - 1))')

        # network algebraic variables
        self.a = ExtAlgeb(model='Bus',
                          src='a',
                          indexer=self.bus,
                          tex_name=r'\theta',
                          info='Bus voltage phase angle',
                          e_str='-u * (vd * Id + vq * Iq)')
        self.v = ExtAlgeb(model='Bus',
                          src='v',
                          indexer=self.bus,
                          tex_name=r'V',
                          info='Bus voltage magnitude',
                          e_str='-u * (vq * Id - vd * Iq)')

        # algebraic variables
        # Need to be provided by specific generator models
        self.Id = Algeb(info='d-axis current',
                        v_str='Id0',
                        tex_name=r'I_d',
                        e_str='')  # to be completed by subclasses
        self.Iq = Algeb(info='q-axis current',
                        v_str='Iq0',
                        tex_name=r'I_q',
                        e_str='')  # to be completed

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

        self.tm = Algeb(info='mechanical torque',
                        tex_name=r'\tau_m',
                        v_str='tm0',
                        v_setter=True,
                        e_str='tm0 - tm')
        self.te = Algeb(
            info='electric torque',
            tex_name=r'\tau_e',
            v_str='p0',
            v_setter=True,
            e_str='psid * Iq - psiq * Id - te',
        )
        self.vf = Algeb(info='excitation voltage',
                        unit='pu',
                        v_str='vf0',
                        v_setter=True,
                        e_str='vf0 - vf',
                        tex_name=r'v_f')

        self.subidx = ExtParam(
            model='StaticGen',
            src='subidx',
            indexer=self.gen,
            tex_name='idx_{sub}',
            export=False,
        )
        # ----------service consts for initialization----------
        self.p0 = ExtService(
            model='StaticGen',
            src='p',
            indexer=self.gen,
            tex_name='P_0',
        )
        self.q0 = ExtService(
            model='StaticGen',
            src='q',
            indexer=self.gen,
            tex_name='Q_0',
        )
Example #12
0
    def __init__(self, system, config):
        super().__init__(system, config)
        self.group = 'PSS'
        self.flags.update({'tds': True})

        self.VCUr = Replace(self.VCU, lambda x: np.equal(x, 0.0), 999)
        self.VCLr = Replace(self.VCL, lambda x: np.equal(x, 0.0), -999)

        # retrieve indices of connected generator, bus, and bus freq
        self.syn = ExtParam(model='Exciter',
                            src='syn',
                            indexer=self.avr,
                            export=False,
                            info='Retrieved generator idx',
                            dtype=str)

        self.bus = ExtParam(
            model='SynGen',
            src='bus',
            indexer=self.syn,
            export=False,
            info='Retrieved bus idx',
            dtype=str,
            default=None,
        )

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

        self.busfreq = DeviceFinder(self.busf, link=self.buss, idx_name='bus')

        # from SynGen
        self.Sn = ExtParam(model='SynGen',
                           src='Sn',
                           indexer=self.syn,
                           tex_name='S_n',
                           info='Generator power base',
                           export=False)

        self.omega = ExtState(
            model='SynGen',
            src='omega',
            indexer=self.syn,
            tex_name=r'\omega',
            info='Generator speed',
            unit='p.u.',
        )

        self.tm0 = ExtService(
            model='SynGen',
            src='tm',
            indexer=self.syn,
            tex_name=r'\tau_{m0}',
            info='Initial mechanical input',
        )
        self.tm = ExtAlgeb(
            model='SynGen',
            src='tm',
            indexer=self.syn,
            tex_name=r'\tau_m',
            info='Generator mechanical input',
        )
        self.te = ExtAlgeb(
            model='SynGen',
            src='te',
            indexer=self.syn,
            tex_name=r'\tau_e',
            info='Generator electrical output',
        )
        # from Bus
        self.v = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.buss,
            tex_name=r'V',
            info='Bus (or busr, if given) terminal voltage',
        )
        self.v0 = ExtService(
            model='Bus',
            src='v',
            indexer=self.buss,
            tex_name="V_0",
            info='Initial bus voltage',
        )

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

        # from Exciter
        self.vi = ExtAlgeb(model='Exciter',
                           src='vi',
                           indexer=self.avr,
                           tex_name='v_i',
                           info='Exciter input voltage',
                           e_str='u * vsout')

        self.vsout = Algeb(
            info='PSS output voltage to exciter',
            tex_name='v_{sout}',
        )  # `self.vsout.e_str` to be provided by specific models
Example #13
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 #14
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 `tm0` is not allowed in any time!!
        if add_tm0 is True:
            self.tm0 = ExtService(src='tm',
                                  model='SynGen',
                                  indexer=self.syn,
                                  tex_name=r'\tau_{m0}',
                                  info='Initial mechanical input')

        self.omega = ExtState(src='omega',
                              model='SynGen',
                              indexer=self.syn,
                              tex_name=r'\omega',
                              info='Generator speed',
                              unit='p.u.')

        # Note: changing `paux0` is allowed.
        # It is a way how one can input from external programs such as reinforcement learning.
        self.paux0 = ConstService(v_str='0',
                                  tex_name='P_{aux0}',
                                  info='const. auxiliary input')

        self.tm = ExtAlgeb(
            src='tm',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'\tau_m',
            e_str='u * (pout - tm0)',
            info='Mechanical power interface to SynGen',
        )
        # `paux` must be zero upon initialization
        self.paux = Algeb(
            info='Auxiliary power input',
            tex_name='P_{aux}',
            v_str='paux0',
            e_str='paux0 - paux',
        )
        self.pout = Algeb(
            info='Turbine final output power',
            tex_name='P_{out}',
            v_str='u*tm0',
        )
        self.wref = Algeb(
            info='Speed reference variable',
            tex_name=r'\omega_{ref}',
            v_str='wref0',
            e_str='wref0 - wref',
        )
Example #15
0
    def __init__(self, system, config):
        super().__init__(system, config)
        self.group = 'PSS'
        self.flags.update({'tds': True})

        self.syn = ExtParam(model='Exciter',
                            src='syn',
                            indexer=self.avr,
                            export=False,
                            info='Retrieved generator idx')
        self.bus = ExtParam(model='SynGen',
                            src='bus',
                            indexer=self.syn,
                            export=False,
                            info='Retrieved bus idx')
        self.Sn = ExtParam(model='SynGen',
                           src='Sn',
                           indexer=self.syn,
                           tex_name='S_n',
                           info='Generator power base',
                           export=False)

        # from SynGen
        self.omega = ExtState(
            model='SynGen',
            src='omega',
            indexer=self.syn,
            tex_name=r'\omega',
            info='Generator speed',
            unit='p.u.',
        )

        self.tm0 = ExtService(
            model='SynGen',
            src='tm',
            indexer=self.syn,
            tex_name=r'\tau_{m0}',
            info='Initial mechanical input',
        )
        self.tm = ExtAlgeb(
            model='SynGen',
            src='tm',
            indexer=self.syn,
            tex_name=r'\tau_m',
            info='Generator mechanical input',
        )
        self.te = ExtAlgeb(
            model='SynGen',
            src='te',
            indexer=self.syn,
            tex_name=r'\tau_e',
            info='Generator electrical output',
        )
        self.vf = ExtAlgeb(model='SynGen',
                           src='vf',
                           indexer=self.syn,
                           tex_name='v_f',
                           info='Generator excitation voltage',
                           e_str='u * vsout')

        # from Bus  #TODO: implement the optional BUSR
        self.v = ExtAlgeb(
            model='Bus',
            src='v',
            indexer=self.bus,
            tex_name=r'V',
            info='Bus (or BUSR, if given) terminal voltage',
        )

        # from Exciter
        self.vsout = Algeb(
            info='PSS output voltage to exciter',
            tex_name='v_{sout}',
        )  # `e_str` to be provided by specific models
Example #16
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.group = 'Exciter'
        self.flags.tds = True

        # Voltage compensator idx-es
        self.VoltComp = BackRef()

        # from synchronous generators, get u, Sn, Vn, bus; tm0; omega
        self.ug = ExtParam(
            src='u',
            model='SynGen',
            indexer=self.syn,
            tex_name='u_g',
            info='Generator online status',
            unit='bool',
            export=False,
        )
        self.ue = ConstService(
            v_str='u * ug',
            info="effective online status",
            tex_name='u_e',
        )
        self.Sn = ExtParam(
            src='Sn',
            model='SynGen',
            indexer=self.syn,
            tex_name='S_m',
            info='Rated power from generator',
            unit='MVA',
            export=False,
        )
        self.Vn = ExtParam(
            src='Vn',
            model='SynGen',
            indexer=self.syn,
            tex_name='V_m',
            info='Rated voltage from generator',
            unit='kV',
            export=False,
        )
        self.vf0 = ExtService(src='vf',
                              model='SynGen',
                              indexer=self.syn,
                              tex_name='v_{f0}',
                              info='Steady state excitation voltage')
        self.bus = ExtParam(
            src='bus',
            model='SynGen',
            indexer=self.syn,
            tex_name='bus',
            info='Bus idx of the generators',
            export=False,
            vtype=str,
        )
        self.omega = ExtState(
            src='omega',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'\omega',
            info='Generator speed',
            is_input=True,
        )
        self.vf = ExtAlgeb(
            src='vf',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'v_f',
            e_str='ue * (vout - vf0)',
            info='Excitation field voltage to generator',
            ename='vf',
            tex_ename='v_f',
            is_input=False,
        )
        self.XadIfd = ExtAlgeb(
            src='XadIfd',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'X_{ad}I_{fd}',
            info='Armature excitation current',
            is_input=True,
        )
        # from bus, get a and v
        self.a = ExtAlgeb(
            model='Bus',
            src='a',
            indexer=self.bus,
            tex_name=r'\theta',
            info='Bus voltage phase angle',
            is_input=True,
        )
        self.vbus = ExtAlgeb(model='Bus',
                             src='v',
                             indexer=self.bus,
                             tex_name='V',
                             info='Bus voltage magnitude',
                             is_input=True)

        # `self.v` is actually `ETERM` in other software
        # TODO:
        # Preferably, its name needs to be changed to `eterm`.
        # That requires updates in equations of all exciters.
        self.v = Algeb(
            info='Input to exciter (bus v or Eterm)',
            tex_name='E_{term}',
            v_str='vbus',
            e_str='vbus - v',
            v_str_add=True,
        )

        # output excitation voltage
        self.vout = Algeb(
            info='Exciter final output voltage',
            tex_name='v_{out}',
            v_str='ue * vf0',
            diag_eps=True,
            is_output=True,
        )
Example #17
0
    def __init__(self, system, config):
        Model.__init__(self, system, config)
        self.flags.tds = True
        self.group = 'DGProtection'

        self.bus = ExtParam(model='DG', src='bus',
                            indexer=self.dev,
                            export=False)

        self.fn = ExtParam(model='DG', src='fn',
                           indexer=self.dev,
                           export=False)

        # -- Frequency protection
        # Convert frequency deviation range to p.u.
        self.f = ExtAlgeb(export=False,
                          info='DG frequency read value', unit='p.u.',
                          model='FreqMeasurement', src='f',
                          indexer=self.busfreq,
                          )

        self.fHz = Algeb(v_str='fn * f',
                         e_str='fn * f - fHz',
                         info='frequency in Hz',
                         tex_name=r'f_{Hz}',
                         )

        # -- Lock DG frequency signal and output power

        self.ltu = ConstService(v_str='0.8')
        self.ltl = ConstService(v_str='0.2')

        # `Ldsum_zu` is `ue`
        dsum = 'fen * (IAWfl1_lim_zu * Lfl1_zi + IAWfl2_lim_zu * Lfl2_zi + ' \
               'IAWfu1_lim_zu * Lfu1_zi + IAWfu2_lim_zu * Lfu2_zi) + ' \
               'Ven * (IAWVl1_lim_zu * LVl1_zi + IAWVl2_lim_zu * LVl2_zi + ' \
               'IAWVl3_lim_zu * LVl3_zi + ' \
               'IAWVu1_lim_zu * LVu1_zi + IAWVu2_lim_zu * LVu2_zi) - ' \
               'dsum'

        self.dsum = Algeb(v_str='0',
                          e_str=dsum,
                          info='lock signal summation',
                          tex_name=r'd_{tot}',
                          )

        self.Ldsum = Limiter(u=self.dsum,
                             lower=self.ltl,
                             upper=self.ltu,
                             info='lock signal comparer, zu is to act',
                             equal=False, no_warn=True,
                             )

        self.ue = Algeb(v_str='0',
                        e_str='Ldsum_zu - ue',
                        info='lock flag',
                        tex_name=r'ue',
                        )

        self.zero = ConstService('0')

        self.res = ExtendedEvent(self.ue, t_ext=self.Tres,
                                 trig="rise",
                                 extend_only=True)

        # lock DG frequency signal

        # fflag option 1: leave source signal online in protection
        self.fin = ExtAlgeb(model='DG', src='f',
                            indexer=self.dev,
                            info='original f from DG',
                            )

        self.fHzl = ExtAlgeb(model='DG', src='fHz',
                             indexer=self.dev,
                             export=False,
                             e_str='- ue * (fn * f)',
                             info='Frequency measure lock',
                             ename='fHzl',
                             tex_ename='f_{Hzl}',
                             )
        # TODO: add fflag option 2: block the source signal in protection

        # lock output power of DG

        self.Pext = ExtAlgeb(model='DG', src='Pext',
                             indexer=self.dev,
                             info='original Pext from DG',
                             )

        self.Pref = ExtAlgeb(model='DG', src='Pref',
                             indexer=self.dev,
                             info='original Pref from DG',
                             )

        self.Pdrp = ExtAlgeb(model='DG', src='DB_y',
                             indexer=self.dev,
                             info='original Pdrp from DG',
                             )

        self.Psum = ExtAlgeb(model='DG', src='Psum',
                             indexer=self.dev,
                             export=False,
                             e_str='- ue * (Pext + Pref + Pdrp)',
                             info='Active power lock',
                             ename='Pneg',
                             tex_ename='P_{neg}',
                             )

        self.Qdrp = ExtAlgeb(model='DG', src='Qdrp',
                             indexer=self.dev,
                             info='original Qdrp from DG',
                             )

        self.Qref = ExtAlgeb(model='DG', src='Qref',
                             indexer=self.dev,
                             info='original Qref from DG',
                             )

        self.Qsum = ExtAlgeb(model='DG', src='Qsum',
                             indexer=self.dev,
                             export=False,
                             e_str='- ue * (Qdrp + Qref)',
                             info='Reactive power lock',
                             ename='Qneg',
                             tex_ename='Q_{neg}',
                             )
Example #18
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',
        )

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

        # retrieve indices of connected generator, bus, and bus freq
        self.syn = ExtParam(model='Exciter',
                            src='syn',
                            indexer=self.avr,
                            export=False,
                            info='Retrieved generator idx',
                            vtype=str)

        self.vf0 = ExtService(src='vf',
                              model='SynGen',
                              indexer=self.syn,
                              tex_name=r'v_{f0}',
                              info='Steady state excitation voltage')
        # from Bus
        self.v = ExtAlgeb(
            model='SynGen',
            src='v',
            indexer=self.syn,
            tex_name=r'V',
            info='Retrieved bus terminal voltage',
        )
        # vd, vq, Id, Iq from SynGen
        self.vd = ExtAlgeb(
            src='vd',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'V_d',
            info='d-axis machine voltage',
        )
        self.vq = ExtAlgeb(
            src='vq',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'V_q',
            info='q-axis machine voltage',
        )
        self.Id = ExtAlgeb(
            src='Id',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'I_d',
            info='d-axis machine current',
        )
        self.Iq = ExtAlgeb(
            src='Iq',
            model='SynGen',
            indexer=self.syn,
            tex_name=r'I_q',
            info='q-axis machine current',
        )

        self.vct = VarService(
            tex_name=r'V_{CT}',
            v_str='u * Abs((vd + 1j*vq) + (rc + 1j * xc) * (Id + 1j*Iq))',
        )

        # output voltage.
        # `vcomp` is the additional voltage to be added to bus terminal voltage
        self.vcomp = Algeb(
            info='Compensator output voltage to exciter',
            tex_name=r'v_{comp}',
            v_str='vct - u * v',
            e_str='vct - u * v - vcomp',
        )

        # do not need to interface to exciters here.
        # Let the exciters pick up `vcomp` through back referencing

        self.Eterm = ExtAlgeb(
            model='Exciter',
            src='v',
            indexer=self.avr,
            v_str='vcomp',
            e_str='vcomp',
        )