Ejemplo n.º 1
0
    def run(self, ss):
        A, B, C, D = ss.get_mats()

        try:
            if ss.dt is not None:
                dtsystem = True
            else:
                dtsystem = False
        except AttributeError:
            dtsystem = False

        S, T, Tinv = librom.balreal_direct_py(A, B, C, DLTI=dtsystem)

        Ar = T.dot(A.dot(Tinv))
        Br = T.dot(B)
        Cr = C.dot(Tinv)

        if self.dtsystem:
            ss_bal = libss.ss(Ar, Br, Cr, self.ss.D, dt=self.ss.dt)
        else:
            ss_bal = libss.ss(Ar, Br, Cr, self.ss.D)

        if self.settings['tune']:
            kv = np.linspace(self.settings['rom_tune_freq_range'][0],
                             self.settings['rom_tune_freq_range'][1])
            ssrom = librom.tune_rom(ss_bal,
                                    kv=kv,
                                    tol=self.settings['rom_tolerance'],
                                    gv=S,
                                    convergence=self.settings['convergence'],
                                    method=self.settings['reduction_method'])

            return ssrom
        else:
            return ss_bal
Ejemplo n.º 2
0
    def run(self, ss):
        if self.print_info:
            cout.cout_wrap(
                'Reducing system using a Direct balancing method...')
        t0 = time.time()
        A, B, C, D = ss.get_mats()

        try:
            if ss.dt is not None:
                dtsystem = True
            else:
                dtsystem = False
        except AttributeError:
            dtsystem = False

        S, T, Tinv = librom.balreal_direct_py(A,
                                              B,
                                              C,
                                              DLTI=dtsystem,
                                              Schur=self.settings['use_schur'])

        Ar = T.dot(A.dot(Tinv))
        Br = T.dot(B)
        Cr = C.dot(Tinv)

        if dtsystem:
            ss_bal = libss.ss(Ar, Br, Cr, D, dt=ss.dt)
        else:
            ss_bal = libss.ss(Ar, Br, Cr, D)

        t1 = time.time()
        if self.print_info:
            cout.cout_wrap('\t...completed balancing in %.2fs' % (t1 - t0), 1)

        if self.settings['tune']:
            cout.cout_wrap('\t\tTuning ROM to specified tolerance...', 2)
            kv = np.linspace(self.settings['rom_tune_freq_range'][0],
                             self.settings['rom_tune_freq_range'][1])
            ssrom = librom.tune_rom(ss_bal,
                                    kv=kv,
                                    tol=self.settings['rom_tolerance'],
                                    gv=S,
                                    convergence=self.settings['convergence'],
                                    method=self.settings['reduction_method'])
            if librom.check_stability(ssrom.A, dt=True):
                if self.settings['print_info']:
                    cout.cout_wrap('ROM by direct balancing is stable')
            t2 = time.time()
            cout.cout_wrap('\t...completed reduction in %.2fs' % (t2 - t0), 1)
            return ssrom
        else:
            return ss_bal
Ejemplo n.º 3
0
    def assemble(self, track_body=False):
        r"""
        Assembles the linearised UVLM system, removes the desired inputs and adds linearised control surfaces
        (if present).

        With all possible inputs present, these are ordered as

        .. math:: \mathbf{u} = [\boldsymbol{\zeta},\,\dot{\boldsymbol{\zeta}},\,\mathbf{w},\,\delta]

        Control surface inputs are ordered last as:

        .. math:: [\delta_1, \delta_2, \dots, \dot{\delta}_1, \dot{\delta_2}]
        """

        self.sys.assemble_ss()

        if self.scaled:
            self.sys.nondimss()
        self.ss = self.sys.SS
        self.C_to_vertex_forces = self.ss.C.copy()

        nzeta = 3 * self.sys.Kzeta

        if self.settings['remove_inputs']:
            self.remove_inputs(self.settings['remove_inputs'])

        if self.gust_assembler is not None:
            A, B, C, D = self.gust_assembler.generate(self.sys, aero=None)
            ss_gust = libss.ss(A, B, C, D, dt=self.ss.dt)
            self.gust_assembler.ss_gust = ss_gust
            self.ss = libss.series(ss_gust, self.ss)

        if self.control_surface is not None:
            Kzeta_delta, Kdzeta_ddelta = self.control_surface.generate()
            n_zeta, n_ctrl_sfc = Kzeta_delta.shape

            # Modify the state space system with a gain at the input side
            # such that the control surface deflections are last
            if self.sys.use_sparse:
                gain_cs = sp.eye(self.ss.inputs,
                                 self.ss.inputs +
                                 2 * self.control_surface.n_control_surfaces,
                                 format='lil')
                gain_cs[:n_zeta, self.ss.inputs:self.ss.inputs +
                        n_ctrl_sfc] = Kzeta_delta
                gain_cs[n_zeta:2 * n_zeta,
                        self.ss.inputs + n_ctrl_sfc:self.ss.inputs +
                        2 * n_ctrl_sfc] = Kdzeta_ddelta
                gain_cs = libsp.csc_matrix(gain_cs)
            else:
                gain_cs = np.eye(
                    self.ss.inputs, self.ss.inputs +
                    2 * self.control_surface.n_control_surfaces)
                gain_cs[:n_zeta, self.ss.inputs:self.ss.inputs +
                        n_ctrl_sfc] = Kzeta_delta
                gain_cs[n_zeta:2 * n_zeta,
                        self.ss.inputs + n_ctrl_sfc:self.ss.inputs +
                        2 * n_ctrl_sfc] = Kdzeta_ddelta
            self.ss.addGain(gain_cs, where='in')
            self.gain_cs = gain_cs
Ejemplo n.º 4
0
 def load_uvlm(filename):
     import sharpy.utils.h5utils as h5
     cout.cout_wrap('Loading UVLM state space system projected onto structural DOFs from file')
     read_data = h5.readh5(filename).ss
     # uvlm_ss_read = read_data.linear.linear_system.uvlm.ss
     uvlm_ss_read = read_data
     return libss.ss(uvlm_ss_read.A, uvlm_ss_read.B, uvlm_ss_read.C, uvlm_ss_read.D, dt=uvlm_ss_read.dt)
Ejemplo n.º 5
0
    def assemble(self):

        qinf = 0.5 * self.rho * self.u_inf ** 2
        e = 0.8

        L0 = 318*9.81 * np.cos(2*np.pi / 180) # qinf * self.S * self.CLa * self.alpha0
        CL0 = self.CLa * self.alpha0
        D0 = qinf * self.S * (0.005 + CL0 ** 2 / e / np.pi / self.AR)

        D = np.zeros((3, 3))

        D[0, 0] = 2 * L0 / self.u_inf
        D[0, 1] = qinf * self.S / self.u_inf * self.CLa
        D[0, 2] = qinf * self.St * self.Clat * self.lt / self.u_inf

        D[1, 0] = 2 * D0 / self.u_inf
        D[1, 1] = qinf * self.S * 2 * self.S*CL0*self.CLa/np.pi/self.b**2/e/self.u_inf

        D[2, 1] = qinf * self.S * self.c * (self.lw/self.c*self.CLa - self.St/self.S*self.lt/self.c*self.Clat)/self.u_inf
        D[2, 2] = qinf * self.S * self.c * self.St * self.lt / self.S / self.c * self.Clat * self.lt / self.u_inf

        self.ss = libss.ss(np.zeros((3, 3)),
                           np.zeros((3, 3)),
                           np.zeros((3, 3)),
                           D)
Ejemplo n.º 6
0
 def setUp(self):
     # allocate some state-space model (dense and sparse)
     dt = 0.3
     Ny, Nx, Nu = 4, 3, 2
     A = np.random.rand(Nx, Nx)
     B = np.random.rand(Nx, Nu)
     C = np.random.rand(Ny, Nx)
     D = np.random.rand(Ny, Nu)
     self.SS = libss.ss(A, B, C, D, dt=dt)
Ejemplo n.º 7
0
def modred(SSb, N, method='residualisation'):
    """
    Produces a reduced order model with N states from balanced or modal system
    SSb.
    Both "truncation" and "residualisation" methods are employed.

    Note:
    - this method is designed for small size systems, i.e. a deep copy of SSb is
    produced by default.
    """

    assert method in ['residualisation', 'realisation', 'truncation'], \
        "method must be equal to 'residualisation' or 'truncation'!"
    assert SSb.dt is not None, 'SSb is not a DLTI!'

    Nb = SSb.A.shape[0]
    if Nb == N:
        SSrom = libss.ss(SSb.A, SSb.B, SSb.C, SSb.D, dt=SSb.dt)
        return SSrom

    A11 = SSb.A[:N, :N]
    B11 = SSb.B[:N, :]
    C11 = SSb.C[:, :N]
    D = SSb.D

    if method is 'truncation':
        SSrom = libss.ss(A11, B11, C11, D, dt=SSb.dt)
    else:
        Nb = SSb.A.shape[0]
        IA22inv = -SSb.A[N:, N:].copy()
        eevec = range(Nb - N)
        IA22inv[eevec, eevec] += 1.
        IA22inv = scalg.inv(IA22inv, overwrite_a=True)

        SSrom = libss.ss(
            A11 + np.dot(SSb.A[:N, N:], np.dot(IA22inv, SSb.A[N:, :N])),
            B11 + np.dot(SSb.A[:N, N:], np.dot(IA22inv, SSb.B[N:, :])),
            C11 + np.dot(SSb.C[:, N:], np.dot(IA22inv, SSb.A[N:, :N])),
            D + np.dot(SSb.C[:, N:], np.dot(IA22inv, SSb.B[N:, :])),
            dt=SSb.dt)

    return SSrom
Ejemplo n.º 8
0
    def setUp(self):

        # This particular system is a good test as it requires a few iterations of the Hinf
        # solver. In addition it is known that its SVD peak occurs between 0.1 and 1.0 rad/s
        # allowing us to increase the resolution in the graphical method around that vicinity.
        a = np.load(self.test_dir + '/src/a.npy')
        b = np.load(self.test_dir + '/src/b.npy')
        c = np.load(self.test_dir + '/src/c.npy')
        d = np.load(self.test_dir + '/src/d.npy')

        self.sys = libss.ss(a, b, c, d, dt=None)
Ejemplo n.º 9
0
    def run(self, ss):

        A, B, C, D = ss.get_mats()

        s, T, Tinv, rcmax, romax = librom.balreal_iter(A, B, C,
                                                       lowrank=self.settings['lowrank'],
                                                       tolSmith=self.settings['smith_tol'].value,
                                                       tolSVD=self.settings['tolSVD'].value)

        Ar = Tinv.dot(A.dot(T))
        Br = Tinv.dot(B)
        Cr = C.dot(T)

        ssrom = libss.ss(Ar, Br, Cr, D, dt=ss.dt)
        return ssrom
Ejemplo n.º 10
0
    def run(self, ss):

        self.ss = ss

        A, B, C, D = self.ss.get_mats()

        if self.ss.dt:
            self.dtsystem = True
        else:
            self.dtsystem = False

        out = self.algorithm.run(ss)

        if type(out) == libss.ss:
            self.ssrom = out

        else:
            Ar, Br, Cr = out
            if self.dtsystem:
                self.ssrom = libss.ss(Ar, Br, Cr, D, dt=self.ss.dt)
            else:
                self.ssrom = libss.ss(Ar, Br, Cr, D)

        return self.ssrom
Ejemplo n.º 11
0
    def setUp(self):
        cout.cout_wrap.initialise(False, False)
        A = scio.loadmat(TestKrylov.test_dir + '/src/' + 'A.mat')
        B = scio.loadmat(TestKrylov.test_dir + '/src/' + 'B.mat')
        C = scio.loadmat(TestKrylov.test_dir + '/src/' + 'C.mat')
        A = libsp.csc_matrix(A['A'])
        B = B['B']
        C = C['C']
        D = np.zeros((B.shape[1], C.shape[0]))

        A = A.todense()

        self.ss = libss.ss(A, B, C, D)

        self.rom = krylov.Krylov()

        if not os.path.exists(self.test_dir + '/figs/'):
            os.makedirs(self.test_dir + '/figs/')
Ejemplo n.º 12
0
    def __call__(self, wv):
        """
        Evaluate interpolated model using weights wv.
        """

        assert self.Projected, (
            'You must project the state-space models over' +
            ' a common basis before interpolating')

        Aint = np.zeros_like(self.AA[0])
        Bint = np.zeros_like(self.BB[0])
        Cint = np.zeros_like(self.CC[0])
        Dint = np.zeros_like(self.DD[0])

        for ii in range(len(self.AA)):
            Aint += wv[ii] * self.AA[ii]
            Bint += wv[ii] * self.BB[ii]
            Cint += wv[ii] * self.CC[ii]
            Dint += wv[ii] * self.DD[ii]

        return libss.ss(Aint, Bint, Cint, Dint, self.SS[0].dt)
Ejemplo n.º 13
0
def balfreq(SS, DictBalFreq):
    """
    Method for frequency limited balancing.

    The Observability and controllability Gramians over the frequencies kv
    are solved in factorised form. Balanced modes are then obtained with a
    square-root method.

    Details:

        * Observability and controllability Gramians are solved in factorised form
          through explicit integration. The number of integration points determines
          both the accuracy and the maximum size of the balanced model.

        * Stability over all (Nb) balanced states is achieved if:

            a. one of the Gramian is integrated through the full Nyquist range
            b. the integration points are enough.


    Input:

    - DictBalFreq: dictionary specifying integration method with keys:

        - ``frequency``: defines limit frequencies for balancing. The balanced
           model will be accurate in the range ``[0,F]``, where ``F`` is the value of
           this key. Note that ``F`` units must be consistent with the units specified
           in the ``self.ScalingFacts`` dictionary.

        - ``method_low``: ``['gauss','trapz']`` specifies whether to use gauss
          quadrature or trapezoidal rule in the low-frequency range ``[0,F]``.

        - ``options_low``: options to use for integration in the low-frequencies.
          These depend on the integration scheme (See below).

        - ``method_high``: method to use for integration in the range [F,F_N],
          where F_N is the Nyquist frequency. See 'method_low'.

        - ``options_high``: options to use for integration in the high-frequencies.

        - ``check_stability``: if True, the balanced model is truncated to
          eliminate unstable modes - if any is found. Note that very accurate
          balanced model can still be obtained, even if high order modes are
          unstable. Note that this option is overridden if ""

        - ``get_frequency_response``: if True, the function also returns the
          frequency response evaluated at the low-frequency range integration
          points. If True, this option also allows to automatically tune the
          balanced model.


    Future options:
        - Ncpu: for parallel run


    The following integration schemes are available:
        - ``trapz``: performs integration over equally spaced points using
          trapezoidal rule. It accepts options dictionaries with keys:

             - ``points``: number of integration points to use (including
               domain boundary)

        - ``gauss`` performs gauss-lobotto quadrature. The domain can be
          partitioned in Npart sub-domain in which the gauss-lobotto quadrature
          of order Ord can be applied. A total number of Npart*Ord points is
          required. It accepts options dictionaries of the form:

             - ``partitions``: number of partitions

             - ``order``: quadrature order.


    Examples:

        The following dictionary

        >>>   DictBalFreq={'frequency': 1.2,
        >>>                'method_low': 'trapz',
        >>>                'options_low': {'points': 12},
        >>>                'method_high': 'gauss',
        >>>                'options_high': {'partitions': 2, 'order': 8},
        >>>                'check_stability': True }


        balances the state-space model in the frequency range [0, 1.2]
        using:

            a. 12 equally-spaced points integration of the Gramians in
               the low-frequency range [0,1.2] and

            b. A 2 Gauss-Lobotto 8-th order quadratures of the controllability
               Gramian in the high-frequency range.


        A total number of 28 integration points will be required, which will
        result into a balanced model with number of states

        >>>    min{ 2*28* number_inputs, 2*28* number_outputs }


        The model is finally truncated so as to retain only the first Ns stable
        modes.
    """

    ### check input dictionary
    if 'frequency' not in DictBalFreq:
        raise NameError('Solution dictionary must include the "frequency" key')

    if 'method_low' not in DictBalFreq:
        warnings.warn('Setting default options for low-frequency integration')
        DictBalFreq['method_low'] = 'trapz'
        DictBalFreq['options_low'] = {'points': 12}

    if 'method_high' not in DictBalFreq:
        warnings.warn('Setting default options for high-frequency integration')
        DictBalFreq['method_high'] = 'gauss'
        DictBalFreq['options_high'] = {'partitions': 2, 'order': 8}

    if 'check_stability' not in DictBalFreq:
        DictBalFreq['check_stability'] = True

    if 'output_modes' not in DictBalFreq:
        DictBalFreq['output_modes'] = True

    if 'get_frequency_response' not in DictBalFreq:
        DictBalFreq['get_frequency_response'] = False

    ### get integration points and weights

    # Nyquist frequency
    kn = np.pi / SS.dt

    Opt = DictBalFreq['options_low']
    if DictBalFreq['method_low'] == 'trapz':
        kv_low, wv_low = get_trapz_weights(0., DictBalFreq['frequency'],
                                           Opt['points'], False)
    elif DictBalFreq['method_low'] == 'gauss':
        kv_low, wv_low = get_gauss_weights(0., DictBalFreq['frequency'],
                                           Opt['partitions'], Opt['order'])
    else:
        raise NameError('Invalid value %s for key "method_low"' %
                        DictBalFreq['method_low'])

    Opt = DictBalFreq['options_high']
    if DictBalFreq['method_high'] == 'trapz':
        if Opt['points'] == 0:
            warnings.warn('You have chosen no points in high frequency range!')
            kv_high, wv_high = [], []
        else:
            kv_high, wv_high = get_trapz_weights(DictBalFreq['frequency'], kn,
                                                 Opt['points'], True)
    elif DictBalFreq['method_high'] == 'gauss':
        if Opt['order'] * Opt['partitions'] == 0:
            warnings.warn('You have chosen no points in high frequency range!')
            kv_high, wv_high = [], []
        else:
            kv_high, wv_high = get_gauss_weights(DictBalFreq['frequency'], kn,
                                                 Opt['partitions'],
                                                 Opt['order'])
    else:
        raise NameError('Invalid value %s for key "method_high"' %
                        DictBalFreq['method_high'])

    ### -------------------------------------------------- loop frequencies

    ### merge vectors
    Nk_low = len(kv_low)
    kvdt = np.concatenate((kv_low, kv_high)) * SS.dt
    wv = np.concatenate((wv_low, wv_high)) * SS.dt
    zv = np.cos(kvdt) + 1.j * np.sin(kvdt)

    Eye = libsp.eye_as(SS.A)
    Zc = np.zeros((SS.states, 2 * SS.inputs * len(kvdt)), )
    Zo = np.zeros((SS.states, 2 * SS.outputs * Nk_low), )

    if DictBalFreq['get_frequency_response']:
        Yfreq = np.empty((
            SS.outputs,
            SS.inputs,
            Nk_low,
        ), dtype=np.complex_)
        kv = kv_low

    for kk in range(len(kvdt)):

        zval = zv[kk]
        Intfact = wv[kk]  # integration factor

        Qctrl = Intfact * libsp.solve(zval * Eye - SS.A, SS.B)
        kkvec = range(2 * kk * SS.inputs, 2 * (kk + 1) * SS.inputs)
        Zc[:, kkvec[:SS.inputs]] = Qctrl.real
        Zc[:, kkvec[SS.inputs:]] = Qctrl.imag

        ### ----- frequency response
        if DictBalFreq['get_frequency_response'] and kk < Nk_low:
            Yfreq[:, :, kk] = (1. / Intfact) * \
                              libsp.dot(SS.C, Qctrl, type_out=np.ndarray) + SS.D

        ### ----- observability
        if kk >= Nk_low:
            continue

        Qobs = Intfact * libsp.solve(np.conj(zval) * Eye - SS.A.T, SS.C.T)

        kkvec = range(2 * kk * SS.outputs, 2 * (kk + 1) * SS.outputs)
        Zo[:, kkvec[:SS.outputs]] = Intfact * Qobs.real
        Zo[:, kkvec[SS.outputs:]] = Intfact * Qobs.imag

    # delete full matrices
    Kernel = None
    Qctrl = None
    Qobs = None

    # LRSQM (optimised)
    U, hsv, Vh = scalg.svd(np.dot(Zo.T, Zc), full_matrices=False)
    sinv = hsv**(-0.5)
    T = np.dot(Zc, Vh.T * sinv)
    Ti = np.dot((U * sinv).T, Zo.T)
    # Zc,Zo=None,None

    ### build frequency balanced model
    Ab = libsp.dot(Ti, libsp.dot(SS.A, T))
    Bb = libsp.dot(Ti, SS.B)
    Cb = libsp.dot(SS.C, T)
    SSb = libss.ss(Ab, Bb, Cb, SS.D, dt=SS.dt)

    ### Eliminate unstable modes - if any:
    if DictBalFreq['check_stability']:
        for nn in range(1, len(hsv) + 1):
            eigs_trunc = scalg.eigvals(SSb.A[:nn, :nn])
            eigs_trunc_max = np.max(np.abs(eigs_trunc))
            if eigs_trunc_max > 1. - 1e-16:
                SSb.truncate(nn - 1)
                hsv = hsv[:nn - 1]
                T = T[:, :nn - 1]
                Ti = Ti[:nn - 1, :]
                break

    outs = (SSb, hsv)
    if DictBalFreq['output_modes']:
        outs += (T, Ti, Zc, Zo, U, Vh)
    return outs
Ejemplo n.º 14
0
cpubal=time.time()
kmin= 512#(2*gebm.Nmodes)*(2**6) 	# speed-up and better accuracy
tolSVD=1e-8
print('Balanced realisation started (with A sparsity)...')
print('kmin: %s tolSVD: %s'%(kmin,tolSVD))
gv,T,Ti,rc,ro=librom.balreal_iter(Sol.linuvlm.SS.A,Sol.linuvlm.SS.B,Sol.linuvlm.SS.C,
				lowrank=True,tolSmith=1e-10,tolSVD=tolSVD,kmin=kmin,tolAbs=False,Print=True)
cpubal=time.time()-cpubal
print('\t\tdone in %.2f sec'%cpubal )


### define balaned system
Ab=libsp.dot(Ti,libsp.dot(Sol.linuvlm.SS.A,T))
Bb=libsp.dot(Ti,Sol.linuvlm.SS.B)
Cb=libsp.dot(Sol.linuvlm.SS.C,T)
SSb=libss.ss(Ab,Bb,Cb,Sol.linuvlm.SS.D,dt=Sol.linuvlm.SS.dt)
Nxbal=Ab.shape[0]


### tune ROM
# ROM is tuned under the assumption that the balanced system freq. response
# is exact.
ds=Sol.linuvlm.SS.dt
fs=1./ds
fn=fs/2.
ks=2.*np.pi*fs
kn=2.*np.pi*fn
# build freq. range
kmin,kmax,Nk=0.001,.5,30
kv=np.linspace(kmin,kmax,Nk)
Ejemplo n.º 15
0
A = scio.loadmat('A.mat')
B = scio.loadmat('B.mat')
C = scio.loadmat('C.mat')
A = A['A']
B = B['B']
C = C['C']
D = np.zeros((B.shape[1], C.shape[0]))

# Convert A to dense
As = sc.sparse.csc_matrix(A)
Ad = As.todense()

A = libsp.csc_matrix(As)

# Assemble continuous time system
fom_ss = libss.ss(A, B, C, D)
fom_sc = sig.lti(Ad, B, C, D)

# Compare frequency response
wv = np.logspace(-1, 3, 1000)
wvsc, mag_fom_sc, ph_fom_sc = fom_sc.bode(wv)
Y_fom_ss = fom_ss.freqresp(wv)[0, 0, :]
mag_fom_ss = 20 * np.log10(np.abs(Y_fom_ss))
ph_fom_ss = np.angle(Y_fom_ss) * 180 / np.pi

print(np.max(np.abs(mag_fom_sc - mag_fom_ss)))

# Build rom
rom = ROM.KrylovReducedOrderModel()
rom.initialise(data=None, ss=fom_ss)
algorithm = "dual_rational_arnoldi"
Ejemplo n.º 16
0
else:
    b = np.zeros((2 * N, ))
    b[-1] = 1.
    c = np.zeros((1, 2 * N))
    c[0, N - 1] = 1
    d = np.zeros(1)

# Plant matrix
Minv = np.linalg.inv(m)
MinvK = Minv.dot(k)
A = np.zeros((2 * N, 2 * N))
A[:N, N:] = np.eye(N)
A[N:, :N] = -MinvK
A[N:, N:] = -Minv.dot(C)

system_CT = libss.ss(A, b, c, d, dt=None)

evals_ss = np.linalg.eigvals(system_CT.A)

# Discrete time system
dt = 1e-2
Adt, Bdt, Cdt, Ddt = lingebm.newmark_ss(Minv, C, k, dt=dt, num_damp=0)

system_DT = libss.ss(Adt, Bdt, Cdt, Ddt, dt=dt)

# SISO Gains for DT system
if system_type == 'SISO':
    b_dt = np.zeros((N))
    b_dt[-1] = 1
    system_DT.addGain(b_dt, 'in')
Ejemplo n.º 17
0
# Create system to transmit a vertical gust across the chord in time
A_gust = np.zeros((M + 1, M + 1))
A_gust[1:, :-1] = np.eye(M)
B_gust = np.zeros((M + 1, 1))
B_gust[0] = 1
C_gust = np.eye(M + 1)
D_gust = np.zeros((C_gust.shape[0], 1))
print(D_gust.shape)
if use_sparse:
    A_gust = libsp.csc_matrix(A_gust)
    B_gust = libsp.csc_matrix(B_gust)
    C_gust = libsp.csc_matrix(C_gust)
    D_gust = libsp.csc_matrix(D_gust)
print(D_gust.shape)
ss_gust = libss.ss(A_gust, B_gust, C_gust, D_gust, dt=ws.dt)
# print(ss_gust_airfoil.A.shape)
# print(ss_gust_airfoil.B.shape)
# print(ss_gust_airfoil.C.shape)
# print(ss_gust_airfoil.D.shape)
# B_temp = B_gust.copy()
# B_temp.shape = (M+1, 1)
# D_temp = D_gust.copy()
# D_temp.shape = (M+1, 1)
# sc_gust_airfoil = sc.signal.dlti(A_gust, B_temp, C_gust, D_temp, dt=dt)

# Gain to get uz at single chordwise position across entire span
K_lattice_gust = np.zeros((uvlm.SS.inputs, ss_gust.outputs))
for i in range(M + 1):
    K_lattice_gust[i * (N + 1):(i + 1) * (N + 1), i] = np.ones((N + 1, ))
Ejemplo n.º 18
0
    def build_system(self, system_inputs, system_time):
        N = 5  # Number of masses/springs/dampers

        k_db = np.linspace(1, 10, N)  # Stiffness database
        m_db = np.logspace(2, 0, N)  # Mass database
        C_db = np.ones(N) * 1e-1  # Damping database

        # Build mass matrix
        m = np.zeros((N, N))
        k = np.zeros((N, N))
        C = np.zeros((N, N))
        m[0, 0] = m_db[0]

        k[0, 0:2] = [k_db[0] + k_db[1], -k_db[1]]
        C[0, 0:2] = [C_db[0] + C_db[1], -C_db[1]]
        for i in range(1, N - 1):
            k[i, i - 1:i +
              2] = [-k_db[i - 1], k_db[i] + k_db[i + 1], -k_db[i + 1]]
            C[i, i - 1:i +
              2] = [-C_db[i - 1], C_db[i] + C_db[i + 1], -C_db[i + 1]]
            m[i, i] = m_db[i]
        m[-1, -1] = m_db[-1]
        k[-1, -2:] = [-k_db[-1], k_db[-1]]
        C[-1, -2:] = [-C_db[-1], C_db[-1]]

        # Input: Forces, Output: Displacements
        if system_inputs == 'MIMO':
            b = np.zeros((2 * N, N))
            b[N:, :] = np.eye(N)
            # Output rn
            c = np.zeros((N, 2 * N))
            c[:, :N] = np.eye(N)
            d = np.zeros((N, N))
        else:
            b = np.zeros((2 * N, ))
            b[-1] = 1.
            c = np.zeros((1, 2 * N))
            c[0, N - 1] = 1
            d = np.zeros(1)

        # Plant matrix
        Minv = np.linalg.inv(m)
        MinvK = Minv.dot(k)
        A = np.zeros((2 * N, 2 * N))
        A[:N, N:] = np.eye(N)
        A[N:, :N] = -MinvK
        A[N:, N:] = -Minv.dot(C)

        # Build State Space
        if system_time == 'ct':
            system = libss.ss(A, b, c, d, dt=None)

        else:
            # Discrete time system
            dt = 1e-2
            Adt, Bdt, Cdt, Ddt = lingebm.newmark_ss(Minv,
                                                    C,
                                                    k,
                                                    dt=dt,
                                                    num_damp=0)

            system = libss.ss(Adt, Bdt, Cdt, Ddt, dt=dt)

            # SISO Gains for DT system
            if system_inputs == 'SISO':
                b_dt = np.zeros((N))
                b_dt[-1] = 1
                system.addGain(b_dt, 'in')

                system.addGain(c, where='out')

        return system
Ejemplo n.º 19
0
    def run(self, ss):
        """
        Performs Model Order Reduction employing Krylov space projection methods.

        Supported methods include:

        =========================  ====================  ==========================================================
        Algorithm                  Interpolation Points  Systems
        =========================  ====================  ==========================================================
        ``one_sided_arnoldi``      1                     SISO Systems
        ``two_sided_arnoldi``      1                     SISO Systems
        ``dual_rational_arnoldi``  K                     SISO systems and Tangential interpolation for MIMO systems
        ``mimo_rational_arnoldi``  K                     MIMO systems. Uses vector-wise construction (more robust)
        ``mimo_block_arnoldi``     K                     MIMO systems. Uses block Arnoldi methods (more efficient)
        =========================  ====================  ==========================================================

        Args:
            ss (sharpy.linear.src.libss.ss): State space to reduce

        Returns:
            (libss.ss): Reduced state space system
        """
        self.ss = ss

        if self.settings['print_info']:
            cout.cout_wrap('Model Order Reduction in progress...')
            self.print_header()

        if self.ss.dt is None:
            self.sstype = 'ct'
        else:
            self.sstype = 'dt'
            self.frequency = np.exp(self.frequency * ss.dt)

        t0 = time.time()

        Ar, Br, Cr = self.__getattribute__(self.algorithm)(self.frequency,
                                                           self.r)

        self.ssrom = libss.ss(Ar, Br, Cr, self.ss.D, self.ss.dt)

        self.stable = self.check_stability(
            restart_arnoldi=self.restart_arnoldi)

        if not self.stable:
            pass
            warn.warn('Reduced Order Model Unstable')
            # Under development
            # TL, TR = self.restart()
            # Wtr, Vr = self.restart()
            # TL, TR = self.stable_realisation()
            # self.ssrom = libss.ss(TL.T.dot(Ar.dot(TR)), TL.T.dot(Br), Cr.dot(TR), self.ss.D, self.ss.dt)
            # self.ssrom = libss.ss(Wtr.dot(self.ssrom.A.dot(Vr)), Wtr.dot(self.ssrom.B), self.ssrom.C.dot(Vr), self.ss.D, self.ss.dt)
            # self.stable = self.check_stability(restart_arnoldi=self.restart_arnoldi)

        t_rom = time.time() - t0
        self.cpu_summary['run'] = t_rom
        if self.settings['print_info']:
            cout.cout_wrap('System reduced from order %d to ' % self.ss.states)
            cout.cout_wrap('\tn = %d states' % self.ssrom.states, 1)
            cout.cout_wrap('...Completed Model Order Reduction in %.2f s' %
                           t_rom)

        return self.ssrom
Ejemplo n.º 20
0
def FLB_transfer_function(SS_list,
                          wv,
                          U_list,
                          VT_list,
                          hsv_list=None,
                          M_list=None):
    r"""
    Returns an interpolatory state-space model based on the transfer function 
    method [1]. This method is applicable to frequency limited balanced 
    state-space models only.


    Features:

        - stability preserved
        - the interpolated state-space model has the same size than the tabulated ones
        - all state-space models, need to have the same size and the same numbers of
          hankel singular values.
        - suitable for any ROM


    Args:
        SS_list (list): List of state-space models instances of :class:`sharpy.linear.src.libss.ss` class.
        wv (list): list of interpolatory weights.
        U_list (list): small size, thin SVD factors of Gramians square roots of each state space model (:math:`\mathbf{U}`).
        VT_list (list): small size, thin SVD factors of Gramians square roots of each state space model (:math:`\mathbf{V}^\top`).
        hsv_list (list): small size, thin SVD factors of Gramians square roots of each state space model. If ``None``,
          it is assumed that
                        ``U_list = [ U_i sqrt(hsv_i) ]``
                        ``VT_list = [ sqrt(hsv_i) V_i.T ]``
          where ``U_i`` and ``V_i.T`` are square matrices and hsv is an array.

        M_list (list): for fast on-line evaluation. Small size product of Gramians
          factors of each state-space model. Each element of this list is equal to:
          ``M_i = U_i hsv_i V_i.T``

    Notes:
        Message for future generations:

            - the implementation is divided into an offline and online part.

    References:

    Maraniello S. and Palacios R., Frequency-limited balanced truncation for
    parametric reduced-order modelling of the UVLM. Only in the best theaters.

    See Also:

        Frequency-Limited Balanced ROMs may be obtained from SHARPy using :class:`sharpy.rom.balanced.FrequencyLimited`.
    """

    # ----------------------------------------------------------------- offline

    ### checks sizes
    N_interp = len(SS_list)
    states = SS_list[0].states
    inputs = SS_list[0].inputs
    outputs = SS_list[0].outputs
    for ss_here in SS_list:
        assert ss_here.states == states, \
            'State-space models must have the same number of states!'
        assert ss_here.inputs == inputs, \
            'State-space models must have the same number of states!'
        assert ss_here.outputs == outputs, \
            'State-space models must have the same number of states!'

    ### case of unbalanced state-space models
    # in this case, U_list and VT_list contain the full-rank Gramians factors
    # of each ROM
    if U_list is None and VT_list is None:
        raise NameError('apply FLB before calling this routine')
        # hsv_list = None
        # M_list, U_list, VT_list = [], [], []
        # for ii in range(N_interp):

        #     # # avoid direct
        #     # hsv,U,Vh,Zc,Zo = librom.balreal_direct_py(
        #     #                         SS_list[ii].A, SS_list[ii].B, SS_list[ii].C,
        #     #                         DLTI=True,full_outputs=True)

        #     # iterative also fails
        #     hsv,Zc,Zo = librom.balreal_iter(SS_list[ii].A, SS_list[ii].B, SS_list[ii].C,
        #                     lowrank=True,tolSmith=1e-10,tolSVD=1e-10,
        #                     kmin=None, tolAbs=False, Print=True, outFacts=True)

        #     # M_list.append( np.dot( np.dot(U,np.diag(hsv)), Vh) )
        #     M_list.append( np.dot( Zo.T,Zc ) )
        #     U_list.append(Zo.T)
        #     VT_list.append(Zc)

    # calculate small size product of Gramians factors
    elif M_list is None:
        if hsv_list is None:
            M_list = [np.dot(U, VT) for U, VT in zip(U_list, VT_list)]
        else:
            M_list = [
                np.dot(U * hsv, VT)
                for U, hsv, VT in zip(U_list, hsv_list, VT_list)
            ]

    # ------------------------------------------------------------------ online

    ### balance interpolated model
    M_int = np.zeros_like(M_list[0])
    for ii in range(N_interp):
        M_int += wv[ii] * M_list[ii]

    U_int, hsv_int, Vh_int = scalg.svd(M_int, full_matrices=False)
    sinv_int = hsv_int**(-0.5)

    ### build projection matrices
    sinvUT_int = (U_int * sinv_int).T
    Vsinv_int = Vh_int.T * sinv_int

    if hsv_list is None:
        Ti_int_list = [np.dot(sinvUT_int, U) for U in U_list]
        T_int_list = [np.dot(VT, Vsinv_int) for VT in VT_list]
    else:
        Ti_int_list = [np.dot(sinvUT_int, U * np.sqrt(hsv)) \
                       for U, hsv in zip(U_list, hsv_list)]
        T_int_list = [np.dot(np.dot(np.diag(np.sqrt(hsv)), VT),
                             Vsinv_int) \
                      for hsv, VT in zip(hsv_list, VT_list)]

    ### assemble interp state-space model
    A_int = np.zeros((states, states))
    B_int = np.zeros((states, inputs))
    C_int = np.zeros((outputs, states))
    D_int = np.zeros((outputs, inputs))

    for ii in range(N_interp):
        # in A and B the weigths come from Ti
        A_int += wv[ii] * np.dot(Ti_int_list[ii],
                                 np.dot(SS_list[ii].A, T_int_list[ii]))
        B_int += wv[ii] * np.dot(Ti_int_list[ii], SS_list[ii].B)
        # in C and D the weights come from the interp system expression
        C_int += wv[ii] * np.dot(SS_list[ii].C, T_int_list[ii])
        D_int += wv[ii] * SS_list[ii].D

    return libss.ss(A_int, B_int, C_int, D_int, dt=SS_list[0].dt), hsv_int