def run_wagner(self, Nsurf, integr_ord, RemovePred, UseSparse, RollNodes): """ see test_wagner """ ### ----- set reference solution self.setUp_from_params(Nsurf, integr_ord, RemovePred, UseSparse, RollNodes) tsaero0 = self.tsaero0 ws = self.ws M = self.M N = self.N Mstar_fact = self.Mstar_fact Uinf0 = self.Uinf0 ### ----- linearisation uvlm = linuvlm.Dynamic(tsaero0, dynamic_settings=ws.config['LinearUvlm']) uvlm.assemble_ss() zeta_pole = np.array([0., 0., 0.]) uvlm.get_total_forces_gain(zeta_pole=zeta_pole) uvlm.get_rigid_motion_gains(zeta_rotation=zeta_pole) uvlm.get_sect_forces_gain() ### ----- Scale gains Fref_tot = self.Fref_tot Fref_span = self.Fref_span uvlm.Kftot = uvlm.Kftot / Fref_tot uvlm.Kmtot = uvlm.Kmtot / Fref_tot / ws.c_ref uvlm.Kfsec /= Fref_span uvlm.Kmsec /= (Fref_span * ws.c_ref) ### ----- step input # rotate incoming flow, wing lattice and wing lattice speed about # the (rolled) wing elastic axis to create an effective angle of attack. # Rotation is expressed through a CRV. delta_AlphaEffDeg = 1e-2 delta_AlphaEffRad = 1e-2 * np.pi / 180. Roll0Rad = self.Roll0Deg / 180. * np.pi dcrv = -delta_AlphaEffRad * np.array( [0., np.cos(Roll0Rad), np.sin(Roll0Rad)]) uvec0 = np.array([Uinf0, 0, 0]) uvec = np.dot(algebra.crv2rotation(dcrv), uvec0) duvec = uvec - uvec0 dzeta = np.zeros((Nsurf, 3, M + 1, N // Nsurf + 1)) dzeta_dot = np.zeros((Nsurf, 3, M + 1, N // Nsurf + 1)) du_ext = np.zeros((Nsurf, 3, M + 1, N // Nsurf + 1)) for ss in range(Nsurf): for mm in range(M + 1): for nn in range(N // Nsurf + 1): dzeta_dot[ss, :, mm, nn] = -1. / 3 * duvec du_ext[ss, :, mm, nn] = +1. / 3 * duvec dzeta = 1. / 3 * np.dot(uvlm.Krot, dcrv) Uaero = np.concatenate( (dzeta.reshape(-1), dzeta_dot.reshape(-1), du_ext.reshape(-1))) ### ----- Steady state solution xste, yste = uvlm.solve_steady(Uaero) Ftot_ste = np.dot(uvlm.Kftot, yste) Mtot_ste = np.dot(uvlm.Kmtot, yste) # first check of gain matrices... Ftot_ste_ref = np.zeros((3, )) Mtot_ste_ref = np.zeros((3, )) fnodes = yste.reshape((Nsurf, 3, M + 1, N // Nsurf + 1)) for ss in range(Nsurf): for nn in range(N // Nsurf + 1): for mm in range(M + 1): Ftot_ste_ref += fnodes[ss, :, mm, nn] Mtot_ste_ref += np.cross(uvlm.MS.Surfs[ss].zeta[:, mm, nn], fnodes[ss, :, mm, nn]) Ftot_ste_ref /= Fref_tot Mtot_ste_ref /= (Fref_tot * ws.c_ref) Fmag = np.linalg.norm(Ftot_ste_ref) er_f = np.max(np.abs(Ftot_ste - Ftot_ste_ref)) / Fmag er_m = np.max(np.abs(Mtot_ste - Mtot_ste_ref)) / Fmag / ws.c_ref assert (er_f < 1e-8 and er_m < 1e-8), \ 'Error of total forces (%.2e) and moment (%.2e) too large!' % (er_f, er_m) + \ 'Verify gains produced by linuvlm.Dynamic.get_total_forces_gain.' # then compare against analytical ... Cl_inf = delta_AlphaEffRad * np.pi * 2. Cfvec_inf = Cl_inf * np.array( [0., -np.sin(Roll0Rad), np.cos(Roll0Rad)]) er_f = np.abs(np.linalg.norm(Ftot_ste) / Cl_inf - 1.) assert (er_f < 1e-2), \ 'Error of total lift coefficient (%.2e) too large!' % (er_f,) + \ 'Verify linuvlm.Dynamic.' er_f = np.abs(np.linalg.norm(Ftot_ste - Cfvec_inf) / Cl_inf) assert (er_f < 1e-2), \ 'Error of total aero force (%.2e) too large!' % (er_f,) + \ 'Verify linuvlm.Dynamic.' # ... and finally compare against non-linear UVLM # ps: here we roll the wing and rotate the incoming flow to generate an effective # angle of attack case_pert = 'wagner_r%.4daeff%.2d_rnodes%s_Nsurf%.2dM%.2dN%.2dwk%.2d' \ % (int(np.round(100 * self.Roll0Deg)), int(np.round(100 * delta_AlphaEffDeg)), RollNodes, Nsurf, M, N, Mstar_fact) ws_pert = flying_wings.QuasiInfinite(M=M, N=N, Mstar_fact=Mstar_fact, n_surfaces=Nsurf, u_inf=Uinf0, alpha=self.Alpha0Deg, roll=self.Roll0Deg, aspect_ratio=1e5, route=self.route_main, case_name=case_pert, RollNodes=RollNodes) ws_pert.u_inf_direction = uvec / Uinf0 ws_pert.main_ea = ws.main_ea ws_pert.clean_test_files() ws_pert.update_derived_params() ws_pert.generate_fem_file() ws_pert.generate_aero_file() # solution flow ws_pert.set_default_config_dict() ws_pert.config['SHARPy']['flow'] = ws.config['SHARPy']['flow'] ws_pert.config['SHARPy']['write_screen'] = 'off' ws_pert.config['SHARPy']['write_log'] = 'off' ws_pert.config['SHARPy'][ 'log_folder'] = self.route_test_dir + '/output/' + self.case_code + '/' ws_pert.config.write() # solve at perturbed point data_pert = sharpy.sharpy_main.main( ['...', self.route_main + case_pert + '.sharpy']) tsaero = data_pert.aero.timestep_info[0] self.start_writer() # get total forces Ftot_ste_pert = np.zeros((3, )) Mtot_ste_pert = np.zeros((3, )) for ss in range(Nsurf): for nn in range(N // Nsurf + 1): for mm in range(M + 1): Ftot_ste_pert += tsaero.forces[ss][:3, mm, nn] Mtot_ste_pert += np.cross( uvlm.MS.Surfs[ss].zeta[:, mm, nn], tsaero.forces[ss][:3, mm, nn]) Ftot_ste_pert /= Fref_tot Mtot_ste_pert /= (Fref_tot * ws.c_ref) Fmag = np.linalg.norm(Ftot_ste_pert) er_f = np.max(np.abs(Ftot_ste - Ftot_ste_pert)) / Fmag er_m = np.max(np.abs(Mtot_ste - Mtot_ste_pert)) / Fmag / ws.c_ref assert (er_f < 2e-4 and er_m < 2e-4), \ 'Error of total forces (%.2e) and moment (%.2e) ' % (er_f, er_m) + \ 'with respect to geometrically-exact UVLM too large!' # and check non-linear uvlm against analytical solution er_f = np.abs(np.linalg.norm(Ftot_ste_pert - Cfvec_inf) / Cl_inf) assert (er_f <= 1.5e-2), \ 'Error of total aero force components (%.2e) too large!' % (er_f,) + \ 'Verify StaticUvlm' ### ----- Analytical step response (Wagner solution) NT = 251 tv = np.linspace(0., uvlm.dt * (NT - 1), NT) Clv_an = an.wagner_imp_start(delta_AlphaEffRad, Uinf0, ws.c_ref, tv) assert np.abs(Clv_an[-1] / Cl_inf - 1.) < 1e-2, \ 'Did someone modify this test case?! The time should be enough to reach ' \ 'the steady-state CL with a 1 perc. tolerance...' Cfvec_an = np.zeros((NT, 3)) Cfvec_an[:, 1] = -np.sin(Roll0Rad) * Clv_an Cfvec_an[:, 2] = np.cos(Roll0Rad) * Clv_an ### ----- Dynamic step response Fsect = np.zeros((NT, Nsurf, 3, N // Nsurf + 1)) # Fbeam=np.zeros((NT,6,N//Nsurf+1)) Ftot = np.zeros((NT, 3)) Er_f_tot = np.zeros((NT, )) # Ybeam=[] gamma = np.zeros((NT, Nsurf, M, N // Nsurf)) gamma_dot = np.zeros((NT, Nsurf, M, N // Nsurf)) gamma_star = np.zeros((NT, Nsurf, int(M * Mstar_fact), N // Nsurf)) xold = np.zeros((uvlm.SS.A.shape[0], )) for tt in range(1, NT): xnew, ynew = uvlm.solve_step(xold, Uaero) change = np.linalg.norm(xnew - xold) xold = xnew # record state ? if uvlm.remove_predictor is False: gv, gvstar, gvdot = uvlm.unpack_state(xnew) gamma[tt, :, :, :] = gv.reshape((Nsurf, M, N // Nsurf)) gamma_dot[tt, :, :, :] = gvdot.reshape((Nsurf, M, N // Nsurf)) gamma_star[tt, :, :, :] = gvstar.reshape( (Nsurf, int(M * Mstar_fact), N // Nsurf)) # calculate forces (and error) Ftot[tt, :3] = np.dot(uvlm.Kftot, ynew) Er_f_tot[tt] = np.linalg.norm(Ftot[tt, :] - Cfvec_an[tt, :]) / Clv_an[tt] Fsect[tt, :, :, :] = np.dot(uvlm.Kfsec, ynew).reshape( (Nsurf, 3, N // Nsurf + 1)) # ### beam forces # Ybeam.append(np.dot(Sol.Kforces[:-10,:],ynew)) # Fdummy=Ybeam[-1].reshape((N//Nsurf,6)).T # Fbeam[tt,:,:N//Nsurf]=Fdummy[:,:N//Nsurf] # Fbeam[tt,:,N//Nsurf+1:]=Fdummy[:,N//Nsurf:] if RemovePred: ts2perc, ts1perc = 6, 6 else: ts2perc, ts1perc = 16, 36 er_th_2perc = np.max(Er_f_tot[ts2perc:]) er_th_1perc = np.max(Er_f_tot[ts1perc:]) ### ----- generate plot if self.ProducePlots: # sections to plot if Nsurf == 1: Nplot = [0, N // 2, N] labs = [r'tip', r'root', r'tip'] elif Nsurf == 2: Nplot = [0, N // 2 - 1] labs = [r'tip', r'near root', r'tip', r'near root'] axtitle = [r'$C_{F_y}$', r'$C_{F_z}$'] # non-dimensional time sv = 2.0 * Uinf0 * tv / ws.c_ref # generate figure clist = ['#003366', '#CC3333', '#336633', '#FF6600'] * 4 fontlabel = 12 std_params = { 'legend.fontsize': 10, 'font.size': fontlabel, 'xtick.labelsize': fontlabel - 2, 'ytick.labelsize': fontlabel - 2, 'figure.autolayout': True, 'legend.numpoints': 1 } # plt.rcParams.update(std_params) # fig = plt.figure('Lift time-history', (12, 6)) # axvec = fig.subplots(1, 2) # for aa in [0, 1]: # comp = aa + 1 # axvec[aa].set_title(axtitle[aa]) # axvec[aa].plot(sv, Cfvec_an[:, comp] / Cl_inf, lw=4, ls='-', # alpha=0.5, color='r', label=r'Wagner') # axvec[aa].plot(sv, Ftot[:, comp] / Cl_inf, lw=5, ls=':', # alpha=0.7, color='k', label=r'Total') # cc = 0 # for ss in range(Nsurf): # for nn in Nplot: # axvec[aa].plot(sv, Fsect[:, ss, comp, nn] / Cl_inf, # lw=4 - cc, ls='--', alpha=0.7, color=clist[cc], # label=r'Surf. %.1d, n=%.2d (%s)' % (ss, nn, labs[cc])) # cc += 1 # axvec[aa].grid(color='0.8', ls='-') # axvec[aa].grid(color='0.85', ls='-', which='minor') # axvec[aa].set_xlabel(r'normalised time $t=2 U_\infty \tilde{t}/c$') # axvec[aa].set_ylabel(axtitle[aa] + r'$/C_{l_\infty}$') # axvec[aa].set_xlim(0, sv[-1]) # if Cfvec_inf[comp] > 0.: # axvec[aa].set_ylim(0, 1.1) # else: # axvec[aa].set_ylim(-1.1, 0) # plt.legend(ncol=1) # # plt.show() # fig.savefig(self.figfold + self.case_main + '.png') # fig.savefig(self.figfold + self.case_main + '.pdf') # plt.close() assert er_th_2perc < 2e-2 and er_th_1perc < 1e-2, \ 'Error of dynamic step response at time-steps 16 and 36 ' + \ '(%.2e and %.2e) too large. Verify Linear UVLM.' % (er_th_2perc, er_th_1perc)
def __init__(self, data, custom_settings_linear=None, uvlm_block=False, chosen_ts=None): self.data = data if custom_settings_linear is None: settings_here = data.settings['LinearUvlm'] else: settings_here = custom_settings_linear sharpy.utils.settings.to_custom_types(settings_here, linuvlm.settings_types_dynamic, linuvlm.settings_default_dynamic) if chosen_ts is None: self.chosen_ts = self.data.ts else: self.chosen_ts = chosen_ts ## TEMPORARY - NEED TO INCLUDE PROPER INTEGRATION OF SETTINGS try: self.rigid_body_motions = settings_here['rigid_body_motion'] except KeyError: self.rigid_body_motions = False try: self.use_euler = settings_here['use_euler'] except KeyError: self.use_euler = False if self.rigid_body_motions and settings_here['track_body']: self.track_body = True else: self.track_body = False ## ------- ### extract aeroelastic info self.dt = settings_here['dt'].value ### reference to timestep_info # aero aero = data.aero self.tsaero = aero.timestep_info[self.chosen_ts] # structure structure = data.structure self.tsstr = structure.timestep_info[self.chosen_ts] # --- backward compatibility try: rho = settings_here['density'].value except KeyError: warnings.warn( "Key 'density' not found in 'LinearUvlm' solver settings. '\ 'Trying to read it from 'StaticCoupled'." ) rho = data.settings['StaticCoupled']['aero_solver_settings']['rho'] if type(rho) == str: rho = np.float(rho) if hasattr(rho, 'value'): rho = rho.value self.tsaero.rho = rho # --- backward compatibility ### gebm if self.use_euler: self.num_dof_rig = 9 else: self.num_dof_rig = 10 self.num_dof_flex = np.sum(self.data.structure.vdof >= 0) * 6 self.num_dof_str = self.num_dof_flex + self.num_dof_rig self.reshape_struct_input() try: beam_settings = settings_here['beam_settings'] except KeyError: beam_settings = dict() self.lingebm_str = lingebm.FlexDynamic(self.tsstr, structure, beam_settings) cga = algebra.quat2rotation(self.tsstr.quat) ### uvlm if uvlm_block: self.linuvlm = linuvlm.DynamicBlock( self.tsaero, dt=settings_here['dt'].value, dynamic_settings=settings_here, RemovePredictor=settings_here['remove_predictor'].value, UseSparse=settings_here['use_sparse'].value, integr_order=settings_here['integr_order'].value, ScalingDict=settings_here['ScalingDict'], for_vel=np.hstack((cga.dot(self.tsstr.for_vel[:3]), cga.dot(self.tsstr.for_vel[3:])))) else: self.linuvlm = linuvlm.Dynamic( self.tsaero, dt=settings_here['dt'].value, dynamic_settings=settings_here, RemovePredictor=settings_here['remove_predictor'].value, UseSparse=settings_here['use_sparse'].value, integr_order=settings_here['integr_order'].value, ScalingDict=settings_here['ScalingDict'], for_vel=np.hstack((cga.dot(self.tsstr.for_vel[:3]), cga.dot(self.tsstr.for_vel[3:]))))
def initialise(self, data, custom_settings=None): if custom_settings: self.settings = custom_settings else: try: self.settings = data.settings['LinearAssembler'][ 'linear_system_settings'] # Load settings, the settings should be stored in data.linear.settings except KeyError: pass settings.to_custom_types(self.settings, self.settings_types, self.settings_default, self.settings_options, no_ctype=True) settings.to_custom_types(self.settings['ScalingDict'], self.scaling_settings_types, self.scaling_settings_default, no_ctype=True) data.linear.tsaero0.rho = float(self.settings['density']) self.scaled = not all( scale == 1.0 for scale in self.settings['ScalingDict'].values()) for_vel = data.linear.tsstruct0.for_vel cga = data.linear.tsstruct0.cga() uvlm = linuvlm.Dynamic(data.linear.tsaero0, dt=None, dynamic_settings=self.settings, for_vel=np.hstack((cga.dot(for_vel[:3]), cga.dot(for_vel[3:])))) self.tsaero0 = data.linear.tsaero0 self.sys = uvlm input_variables_database = { 'zeta': [0, 3 * self.sys.Kzeta], 'zeta_dot': [3 * self.sys.Kzeta, 6 * self.sys.Kzeta], 'u_gust': [6 * self.sys.Kzeta, 9 * self.sys.Kzeta] } state_variables_database = { 'gamma': [0, self.sys.K], 'gamma_w': [self.sys.K, self.sys.K_star], 'dtgamma_dot': [self.sys.K + self.sys.K_star, 2 * self.sys.K + self.sys.K_star], 'gamma_m1': [ 2 * self.sys.K + self.sys.K_star, 3 * self.sys.K + self.sys.K_star ] } self.linearisation_vectors['zeta'] = np.concatenate([ self.tsaero0.zeta[i_surf].reshape(-1, order='C') for i_surf in range(self.tsaero0.n_surf) ]) self.linearisation_vectors['zeta_dot'] = np.concatenate([ self.tsaero0.zeta_dot[i_surf].reshape(-1, order='C') for i_surf in range(self.tsaero0.n_surf) ]) self.linearisation_vectors['u_ext'] = np.concatenate([ self.tsaero0.u_ext[i_surf].reshape(-1, order='C') for i_surf in range(self.tsaero0.n_surf) ]) self.linearisation_vectors['forces_aero'] = np.concatenate([ self.tsaero0.forces[i_surf][:3].reshape(-1, order='C') for i_surf in range(self.tsaero0.n_surf) ]) self.input_variables = ss_interface.LinearVector( input_variables_database, self.sys_id) self.state_variables = ss_interface.LinearVector( state_variables_database, self.sys_id) if data.aero.n_control_surfaces >= 1: import sharpy.linear.assembler.lincontrolsurfacedeflector as lincontrolsurfacedeflector self.control_surface = lincontrolsurfacedeflector.LinControlSurfaceDeflector( ) self.control_surface.initialise(data, uvlm) if self.settings['rom_method'] != '': # Initialise ROM self.rom = dict() for rom_name in self.settings['rom_method']: self.rom[rom_name] = rom_interface.initialise_rom(rom_name) self.rom[rom_name].initialise( self.settings['rom_method_settings'][rom_name]) if 'u_gust' not in self.settings['remove_inputs'] and self.settings[ 'gust_assembler'] == 'leading_edge': import sharpy.linear.assembler.lineargustassembler as lineargust self.gust_assembler = lineargust.LinearGustGenerator() self.gust_assembler.initialise(data.aero)