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
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)
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
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:
# 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.)
# 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]
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.)
# 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'
# 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.)