예제 #1
0
    def update(self, u_infty):
        """
        Updates the aeroelastic scaled system with the new reference velocity.

        Only the beam equations need updating since the only dependency in the forward flight velocity resides there.

        Args:
              u_infty (float): New reference velocity

        Returns:
            sharpy.linear.src.libss.ss: Updated aeroelastic state-space system

        """
        t_ref = self.uvlm.sys.ScalingFacts['length'] / u_infty

        self.beam.sys.update_matrices_time_scale(t_ref)
        self.beam.sys.assemble()
        self.beam.ss = self.beam.sys.SSdisc

        self.ss = libss.couple(ss01=self.uvlm.ss,
                               ss02=self.beam.ss,
                               K12=self.couplings['Tas'],
                               K21=self.couplings['Tsa'])

        return self.ss
예제 #2
0
    def assemble_ss(self, beam_num_modes=None):
        """Assemble State Space formulation"""
        data = self.data

        aero = self.data.aero
        structure = self.data.structure  # data.aero.beam
        tsaero = self.tsaero
        tsstr = self.tsstr

        ### assemble linear uvlm
        self.linuvlm.assemble_ss()
        SSaero = self.linuvlm.SS

        ### assemble gains and stiffening term due to non-zero forces
        # only flexible dof accounted for
        self.get_gebm2uvlm_gains()

        ### assemble linear gebm
        # structural part only
        self.lingebm_str.assemble(beam_num_modes)
        SSstr_flex = self.lingebm_str.SSdisc
        SSstr = SSstr_flex
        # # rigid-body (fake)
        # ZeroMat=np.zeros((self.num_dof_rig,self.num_dof_rig))
        # EyeMat=np.eye(self.num_dof_rig)
        # Astr=np.zeros((2*self.num_dof_rig,2*self.num_dof_rig))
        # Bstr=np.zeros((2*self.num_dof_rig,2*self.num_dof_rig))
        # Cstr=np.eye(2*self.num_dof_rig)
        # Dstr=np.zeros((2*self.num_dof_rig,2*self.num_dof_rig))
        # Astr[:self.num_dof_flex,:self.num_dof_flex]=SSstr.A[]
        # SSstr_rig=scsig.dlti()

        # str -> aero
        Zblock = np.zeros((3 * self.linuvlm.Kzeta, SSstr.outputs // 2))
        if self.rigid_body_motions:
            Kas = np.block([[self.Kdisp, Zblock],
                            [self.Kvel_disp, self.Kvel_vel], [Zblock, Zblock]])
        else:
            Kas = np.block([[self.Kdisp[:, :-self.num_dof_rig], Zblock],
                            [
                                self.Kvel_disp[:, :-self.num_dof_rig],
                                self.Kvel_vel[:, :-self.num_dof_rig]
                            ], [Zblock, Zblock]])

        # aero -> str
        if self.rigid_body_motions:
            Ksa = self.Kforces  # aero --> str

        else:
            Ksa = self.Kforces[:-10, :]  # aero --> str

        ### feedback connection
        self.SS = libss.couple(ss01=self.linuvlm.SS,
                               ss02=SSstr,
                               K12=Kas,
                               K21=Ksa)
예제 #3
0
    def assemble(self):
        r"""
        Assembly of the linearised aeroelastic system.

        The UVLM state-space system has already been assembled. Prior to assembling the beam's first order state-space,
        the damping and stiffness matrices have to be modified to include the damping and stiffenning terms that arise
        from the linearisation of the aeordynamic forces with respect to the A frame of reference. See
        :func:`sharpy.linear.src.lin_aeroela.get_gebm2uvlm_gains()` for details on the linearisation.

        Then the beam is assembled as per the given settings in normalised time if the aerodynamic system has been
        scaled. The discrete time systems of the UVLM and the beam must have the same time step.

        The UVLM inputs and outputs are then projected onto the structural degrees of freedom (obviously with the
        exception of external gusts and control surfaces). Hence, the gains :math:`\mathbf{K}_{sa}` and
        :math:`\mathbf{K}_{as}` are added to the output and input of the UVLM system, respectively. These gains perform
        the following relation:

        .. math:: \begin{bmatrix}\zeta \\ \zeta' \\ u_g \\ \delta \end{bmatrix} = \mathbf{K}_{as}
            \begin{bmatrix} \eta \\ \eta' \\ u_g \\ \delta \end{bmatrix} =

        .. math:: \mathbf{N}_{nodes} = \mathbf{K}_{sa} \mathbf{f}_{vertices}

        If the beam is expressed in modal form, the UVLM is further projected onto the beam's modes to have the
        following input/output structure:


        Returns:

        """
        uvlm = self.uvlm
        beam = self.beam

        # Linearisation of the aerodynamic forces introduces stiffenning and damping terms into the beam matrices
        flex_nodes = self.beam.sys.num_dof_flex

        stiff_aero = np.zeros_like(beam.sys.Kstr)
        damping_aero = np.zeros_like(beam.sys.Cstr)
        stiff_aero[:flex_nodes, :flex_nodes] = self.Kss

        rigid_dof = beam.sys.Kstr.shape[0] - flex_nodes
        total_dof = flex_nodes + rigid_dof

        if rigid_dof > 0:
            rigid_dof = beam.sys.Kstr.shape[0] - self.Kss.shape[0]
            stiff_aero[flex_nodes:, :flex_nodes] = self.Krs

            damping_aero[:flex_nodes, flex_nodes:] = self.Csr
            damping_aero[flex_nodes:, flex_nodes:] = self.Crr
            damping_aero[flex_nodes:, :flex_nodes] = self.Crs

        beam.sys.Cstr += damping_aero
        beam.sys.Kstr += stiff_aero

        if uvlm.scaled:
            beam.assemble(t_ref=uvlm.sys.ScalingFacts['time'])
        else:
            beam.assemble()

        if not self.load_uvlm_from_file:
            # Projecting the UVLM inputs and outputs onto the structural degrees of freedom
            Ksa = self.Kforces[:beam.sys.
                               num_dof, :]  # maps aerodynamic grid forces to nodal forces

            # Map the nodal displacement and velocities onto the grid displacements and velocities
            Kas = np.zeros((uvlm.ss.inputs, 2 * beam.sys.num_dof +
                            (uvlm.ss.inputs - 2 * self.Kdisp.shape[0])))
            Kas[:2*self.Kdisp.shape[0], :2*beam.sys.num_dof] = \
                np.block([[self.Kdisp[:, :beam.sys.num_dof], self.Kdisp_vel[:, :beam.sys.num_dof]],
                [self.Kvel_disp[:, :beam.sys.num_dof], self.Kvel_vel[:, :beam.sys.num_dof]]])

            # Retain other inputs
            Kas[2 * self.Kdisp.shape[0]:,
                2 * beam.sys.num_dof:] = np.eye(uvlm.ss.inputs -
                                                2 * self.Kdisp.shape[0])

            # Scaling
            if uvlm.scaled:
                Kas /= uvlm.sys.ScalingFacts['length']

            uvlm.ss.addGain(Ksa, where='out')
            uvlm.ss.addGain(Kas, where='in')

            self.couplings['Ksa'] = Ksa
            self.couplings['Kas'] = Kas

            if self.settings['beam_settings']['modal_projection'] == True and \
                    self.settings['beam_settings']['inout_coords'] == 'modes':
                # Project UVLM onto modal space
                phi = beam.sys.U
                in_mode_matrix = np.zeros(
                    (uvlm.ss.inputs, beam.ss.outputs +
                     (uvlm.ss.inputs - 2 * beam.sys.num_dof)))
                in_mode_matrix[:2 * beam.sys.num_dof, :2 *
                               beam.sys.num_modes] = sclalg.block_diag(
                                   phi, phi)
                in_mode_matrix[2 * beam.sys.num_dof:, 2 *
                               beam.sys.num_modes:] = np.eye(uvlm.ss.inputs -
                                                             2 *
                                                             beam.sys.num_dof)
                out_mode_matrix = phi.T

                uvlm.ss.addGain(in_mode_matrix, where='in')
                uvlm.ss.addGain(out_mode_matrix, where='out')

            # Reduce uvlm projected onto structural coordinates
            if uvlm.rom:
                if rigid_dof != 0:
                    self.runrom_rbm(uvlm)
                else:
                    for k, rom in uvlm.rom.items():
                        uvlm.ss = rom.run(uvlm.ss)

        else:
            uvlm.ss = self.load_uvlm(self.settings['uvlm_filename'])

        # Coupling matrices
        Tas = np.eye(uvlm.ss.inputs, beam.ss.outputs)
        Tsa = np.eye(beam.ss.inputs, uvlm.ss.outputs)

        # Scale coupling matrices
        if uvlm.scaled:
            Tsa *= uvlm.sys.ScalingFacts['force'] * uvlm.sys.ScalingFacts[
                'time']**2
            if rigid_dof > 0:
                warnings.warn(
                    'Time scaling for problems with rigid body motion under development.'
                )
                Tas[:flex_nodes + 6, :flex_nodes +
                    6] /= uvlm.sys.ScalingFacts['length']
                Tas[total_dof:total_dof + flex_nodes +
                    6] /= uvlm.sys.ScalingFacts['length']
            else:
                if not self.settings['beam_settings']['modal_projection']:
                    Tas /= uvlm.sys.ScalingFacts['length']

        ss = libss.couple(ss01=uvlm.ss, ss02=beam.ss, K12=Tas, K21=Tsa)
        # Conditioning of A matrix
        # cond_a = np.linalg.cond(ss.A)
        # if type(uvlm.ss.A) != np.ndarray:
        #     cond_a_uvlm = np.linalg.cond(uvlm.ss.A.todense())
        # else:
        #     cond_a_uvlm = np.linalg.cond(uvlm.ss.A)
        # cond_a_beam = np.linalg.cond(beam.ss.A)
        # cout.cout_wrap('Matrix A condition = %e' % cond_a)
        # cout.cout_wrap('Matrix A_uvlm condition = %e' % cond_a_uvlm)
        # cout.cout_wrap('Matrix A_beam condition = %e' % cond_a_beam)

        self.couplings['Tas'] = Tas
        self.couplings['Tsa'] = Tsa
        self.state_variables = {'aero': uvlm.ss.states, 'beam': beam.ss.states}

        # Save zero force reference
        self.linearisation_vectors['forces_aero_beam_dof'] = Ksa.dot(
            self.linearisation_vectors['forces_aero'])

        if self.settings['beam_settings']['modal_projection'] == True and \
                self.settings['beam_settings']['inout_coords'] == 'modes':
            self.linearisation_vectors[
                'forces_aero_beam_dof'] = out_mode_matrix.dot(
                    self.linearisation_vectors['forces_aero_beam_dof'])

        cout.cout_wrap('Aeroelastic system assembled:')
        cout.cout_wrap('\tAerodynamic states: %g' % uvlm.ss.states, 1)
        cout.cout_wrap('\tStructural states: %g' % beam.ss.states, 1)
        cout.cout_wrap('\tTotal states: %g' % ss.states, 1)
        cout.cout_wrap('\tInputs: %g' % ss.inputs, 1)
        cout.cout_wrap('\tOutputs: %g' % ss.outputs, 1)
        return ss
예제 #4
0
rot_matrix[0, :] = np.array([-np.cos(theta), -np.sin(theta), 0])
rot_matrix[1, :] = np.array([np.cos(theta), -np.sin(theta), 0])
rot_matrix[2, 2] = 1

aero.addGain(rot_matrix, where='out')

# Couple UVLM and BEAM
# AERO TO BEAM
Tsa = np.zeros((beam.SScont.inputs, aero.ss.outputs))
Tsa[0:3, :] = np.eye(3)

# BEAM to UVLM
Tas = np.zeros((aero.ss.inputs, beam.SScont.outputs))
Tas[:, -4:-1] = np.eye(3)

ss_coupled = libss.couple(uvlm.SS, beam.SScont, Tas, Tsa)

eigs_ct, fd_modes = np.linalg.eig(ss_coupled.A)
# eigs_ct = np.log(eigs_dt) / ws.dt

# Sort eigvals in real mag
order = np.argsort(eigs_ct.real)[::-1]
eigs_ct = eigs_ct[order]
fd_modes = fd_modes[:, order]

for eig in range(len(eigs_ct)):
    if np.argmax(np.abs(fd_modes[:, eig]), axis=0) < uvlm.SS.states:
        C = 'b'
        S = 2
        m = 's'
    else:
예제 #5
0
# beam.Cstr = beam.Cstr * t_ref
beam.inout_coords = 'modes'
beam.assemble()
beam_ss = beam.SSdisc
#
# Update BEAM -> UVLM gains due to scaling
# Tas = np.eye(2*num_modes) / length_ref
Tas = np.eye(2 * beam.Nmodes)

# Update UVLM -> BEAM gains with scaling
# Tsa = np.diag((force_c*t_ref**2) * np.ones(beam.Nmodes))
Tsa = np.diag(1. * np.ones(beam.Nmodes))
# Tsa = np.ones(beam.Nmodes)
#
#     # Assemble new aeroelastic systems
ss_aeroelastic = libss.couple(ss01=uvlm.SS, ss02=beam_ss, K12=Tas, K21=Tsa)
#     ss_aeroelastic = libss.couple(ss01=rom.ssrom, ss02=beam_ss, K12=Tas, K21=Tsa)
#
dt_new = dt  # ws.c_root / M / u_ref
# assert np.abs(dt_new - t_ref * aeroelastic_system.linuvlm.SS.dt) < 1e-14, 'dimensional time-scaling not correct!'
# #
#     # Asymptotic stability of the system
eigs, eigvecs = sclalg.eig(ss_aeroelastic.A)
eigs_mag = np.abs(eigs)
order = np.argsort(eigs_mag)[::-1]
eigs = eigs[order]
eigvecs = eigvecs[:, order]
eigs_mag = eigs_mag[order]
# #     frequency_dt_evals = 0.5*np.angle(eigs)/np.pi/dt_new
# #
# #     # Nunst = np.sum(eigs_mag>1.)
예제 #6
0
# Aero to modal and retain nodal forces
phi = beam.U
out_mat = np.vstack((np.eye(uvlm.SS.outputs),
                     np.hstack((phi.T, np.zeros((phi.T.shape[0], uvlm.K))))))
# out_mat = sclalg.block_diag(out_mat, np.eye(uvlm.K))

mod_mat = sclalg.block_diag(phi, phi)
uvlm.SS.addGain(mod_mat, where='in')
uvlm.SS.addGain(out_mat, where='out')

# Couple
Tsa = np.zeros((beam.SSdisc.inputs, uvlm.SS.outputs))
Tsa[-1, -1] = 1
Tas = np.eye(2)
ss_coupled = libss.couple(uvlm.SS, beam.SSdisc, Tas,
                          Tsa)  # Coupled system with modal i/o
nodal_gain_out = +1 * np.eye(3) * beam.U[-9 + mode]
ss_coupled.addGain(nodal_gain_out, where='in')

A = ss_coupled.A
B = ss_coupled.B
C = ss_coupled.C
D = ss_coupled.D

dlti = sc.signal.dlti(A, B, C, D, dt=ws.dt)
T = 30  #s
out = sc.signal.dstep(dlti, n=np.ceil(T / ws.dt))
tout = out[0]
phi_out = out[1][0]
Vx_out = out[1][1]
Q_out = out[1][2]
예제 #7
0
    beam.dt = aeroelastic_system.linuvlm.SS.dt
    beam.freq_natural = natural_frequency_ref * t_ref
    beam.Kstr = K_ref * t_ref ** 2
    # beam.Mstr = M_ref * t_ref *0
    beam.inout_coords = 'modes'
    beam.assemble()
    beam_ss = beam.SSdisc

    # Update BEAM -> UVLM gains due to scaling
    Tas = np.eye(beam.SSdisc.outputs) / length_ref

    # Update UVLM -> BEAM gains with scaling
    Tsa = np.diag((force_c*t_ref**2) * np.ones(aeroelastic_system.linuvlm.SS.outputs))

    # Assemble new aeroelastic systems
    ss_aeroelastic = libss.couple(ss01=rom.ssrom, ss02=beam_ss, K12=Tas, K21=Tsa)

    dt_new = ws.c_ref / M / u_ref
    assert np.abs(dt_new - t_ref * aeroelastic_system.linuvlm.SS.dt) < 1e-14, 'dimensional time-scaling not correct!'

    # Asymptotic stability of the system
    eigs, eigvecs = sclalg.eig(ss_aeroelastic.A)
    eigs_mag = np.abs(eigs)
    order = np.argsort(eigs_mag)[::-1]
    eigs = eigs[order]
    eigvecs = eigvecs[:, order]
    eigs_mag = eigs_mag[order]
    frequency_dt_evals = 0.5*np.angle(eigs)/np.pi/dt_new

    # Nunst = np.sum(eigs_mag>1.)
예제 #8
0
# out_mat[0, :] *= 1
# out_mat[2, :] *= 1
# in_mat = sclalg.block_diag(theta, mode_integr)
uvlm.SS.addGain(uvlm_in, where='in')
uvlm.SS.addGain(uvlm_out, where='out')

# Couple UVLM and BEAM
Tsa = np.eye(beam.SSdisc.inputs, uvlm.SS.outputs)
Tas = np.eye(uvlm.SS.inputs, beam.SSdisc.outputs)
# Tas[0, 0] = -1
# Tas[2, 2] = -1
# Tas[4, 4] = -1
# Tas[6, 6] = -1
# Tsa = Tas[:4, :4]
# Remove the couple between orientation and aerodynamics
ss_coupled = libss.couple(uvlm.SS, beam.SSdisc, Tas, Tsa)

eigs_dt, fd_modes = np.linalg.eig(ss_coupled.A)
eigs_ct = np.log(eigs_dt) / ws.dt

# Sort eigvals in real mag
order = np.argsort(eigs_ct.real)[::-1]
eigs_ct = eigs_ct[order]
fd_modes = fd_modes[:, order]

plt.figure()
for eig in range(len(eigs_ct)):
    if np.argmax(np.abs(fd_modes[:, eig]), axis=0) < uvlm.SS.states:
        C = 'b'
        S = 2
        m = 's'
예제 #9
0
	# scale_out=qinf*Lref0**2

	### update gebm
	gebm.dt=Sol.linuvlm.SS.dt # normalised time
	gebm.freq_natural=freq0.copy() * tref
	gebm.inout_coords='modes'
	gebm.assemble()
	SSstr=gebm.SSdisc

	# gebm modal -> uvlm modal
	Tas=np.eye(2*gebm.Nmodes)/Lref0
	# uvlm modal -> gebm modal
	Tsa=np.diag( (fref*tref**2) * np.ones(gebm.Nmodes,) )

	### feedback connection
	SS=libss.couple( ss01=SSrom,ss02=SSstr,K12=Tas,K21=Tsa )

	### only for eigenvalue conversion!
	dt_new=ws.c_ref/M/u_inf
	assert np.abs(dt_new - tref*Sol.linuvlm.SS.dt)<1e-14, 'dimensional time-scaling not correct!'


	### aeroelastic eigenvalues
	eigs,eigvecs=np.linalg.eig(SS.A)
	eigmag=np.abs(eigs)
	order=np.argsort(eigmag)[::-1]
	eigs=eigs[order]
	eigvecs=eigvecs[:,order]
	eigmag=eigmag[order]
	eigmax=np.max(eigmag)
	Nunst=np.sum(eigmag>1.)