def setUp(self): ''' Common piece of code to initialise the test ''' # build reference state Mw=4 M=3 alpha=0.*np.pi/180. ainf=15.0*np.pi/180. Uinf=20.*np.array([np.cos(ainf),np.sin(ainf)]) chord=5.0 b=0.5*chord self.S0=uvlm2d_sta.solver(M,Mw,b,Uinf,alpha,rho=1.225) #self.S0.build_flat_plate() self.S0.build_camber_plate(Mcamb=50,Pcamb=4) self.S0.solve_static_Gamma2d() # linear solver self.Slin=lin_uvlm2d_sta.solver(self.S0)
def test_flat_rotation_about_zero(self): ''' Calculate steady case / very short wake can be used. A flat plate at at zero angle of attack is taken as reference condition. The state is perturbed of an angle dalpha. ''' MList = [1, 8, 16] DAlphaList = [0.1, 0.5, 1., 2., 4., 6., 8., 10., 12., 14.] # degs Nm = len(MList) Na = len(DAlphaList) CDlin, CLlin = np.zeros((Na, Nm)), np.zeros((Na, Nm)) CDnnl, CLnnl = np.zeros((Na, Nm)), np.zeros((Na, Nm)) for mm in range(len(MList)): # input: reference condition: flat plate at zero angle Mw = 2 M = MList[mm] alpha = 0. * np.pi / 180. chord = 3. b = 0.5 * chord Uinf = np.array([20., 0.]) rho = 1.225 S0 = uvlm.solver(M, Mw, b, Uinf, alpha, rho=1.225) S0.build_flat_plate() S0.solve_static_Gamma2d() print('Testing steady aerofoil for M=%d...' % M) for aa in range(len(DAlphaList)): # Perturbation dalpha = np.pi / 180. * DAlphaList[aa] # angle [rad] dUinf = 0.0 # velocity [m/s] qinf_tot = 0.5 * rho * (S0.Uabs + dUinf)**2 print('\talpha==%.2f deg' % DAlphaList[aa]) ### Linearised solution # Perturb reference state ZetaRot = geo.rotate_aerofoil(S0.Zeta, dalpha) dZeta = ZetaRot - S0.Zeta Slin = linuvlm.solver(S0) Slin.Zeta = dZeta # solve Slin.solve_static_Gamma2d() # store data CDlin[aa,mm],CLlin[aa,mm]=np.sum(Slin.Faero,0)/\ (qinf_tot*Slin.S0.chord*(alpha+dalpha)) ### Reference nonlinear solution Sref = uvlm.solver(M, Mw, b, Uinf, alpha + dalpha, rho) Sref.build_flat_plate() # solve Sref.solve_static_Gamma2d() # store CDnnl[aa,mm],CLnnl[aa,mm]=np.sum(Sref.Faero,0)/\ (qinf_tot*Sref.chord*(alpha+dalpha)) clist = [ 'k', 'r', 'b', '0.6', ] fig = plt.figure('Aerodynamic coefficients', (12, 4)) ax1 = fig.add_subplot(121) ax2 = fig.add_subplot(122) for mm in range(Nm): # ax1.plot(DAlphaList, CDlin[:, mm], clist[mm], lw=Nm - mm, ls='--', label=r'M=%.2d lin' % MList[mm]) ax1.plot(DAlphaList, CDnnl[:, mm], clist[mm], lw=Nm - mm, label=r'M=%.2d nnl' % MList[mm]) # ax2.plot(DAlphaList, CLlin[:, mm], clist[mm], lw=Nm - mm, ls='--', label=r'M=%.2d lin' % MList[mm]) ax2.plot(DAlphaList, CLnnl[:, mm], clist[mm], lw=Nm - mm, label=r'M=%.2d nnl' % MList[mm]) ax1.set_xlabel(r'\alpha [deg]') ax1.set_title(r'CD') ax1.legend() ax2.set_xlabel(r'\alpha [deg]') ax2.set_title(r'CL') ax2.legend() if self.SHOW_PLOT: plt.show() else: plt.close()
def test_camber_aerofoil_speed(self): ''' Calculate steady case / very short wake can be used. A cambered aerofoil, rotated of a alpha0 angle wrt the horizontal line is taken as a reference. The aerofoil has a nonzero velocity, such that an effective angle of attack, alpha_eff, is achieved. The flow speed magnitude is kept constant. Two linear models are created. Both must be built around the aerofoil at a geometrical angle of alpha0. However, the initial velocity profiles used for the linearisations are different and such to produce effective angles of attack of alpha_eff01 and alpha_eff02. ''' DAlphaList = [ 0.0, 0.1, 0.2, 0.3, 0.5, 1., 2., 4., 6., 8., 9.5, 9.7, 9.8, 9.9, 10., 10.1, 10.2, 10.3, 10.5, 12., 14., 16., 18. ] # degs MList = [20] Nm, mm = 1, 0 Na = len(DAlphaList) CFXlin01, CFZlin01 = np.zeros((Na, Nm)), np.zeros((Na, Nm)) CFXlin02, CFZlin02 = np.zeros((Na, Nm)), np.zeros((Na, Nm)) CFXnnl, CFZnnl = np.zeros((Na, Nm)), np.zeros((Na, Nm)) FZlin01, FXlin01 = np.zeros((Na, Nm)), np.zeros((Na, Nm)) FZlin02, FXlin02 = np.zeros((Na, Nm)), np.zeros((Na, Nm)) FZnnl, FXnnl = np.zeros((Na, Nm)), np.zeros((Na, Nm)) # Reference input: Mw = 2 M = MList[mm] chord = 3. b = 0.5 * chord Uinf0 = np.array([20., 0.]) rho = 1.225 alpha0 = 4.0 * np.pi / 180. # angle of reference aerofoil w.r.t. horizontal line # reference condition 1/2: aerofoil at 0 deg angle S01 = uvlm.solver(M, Mw, b, Uinf0, alpha0, rho=1.225) S01.build_camber_plate(Mcamb=10, Pcamb=4) S02 = copy.deepcopy(S01) alpha_eff01 = 0. * np.pi / 180. alpha_inf = alpha_eff01 - alpha0 Uinf_here = geo.rotate_speed(Uinf0, alpha_inf) S01.dZetadt[:, :] = -(Uinf_here - Uinf0) S01.solve_static_Gamma2d() Fref01 = np.sum(S01.Faero, 0) alpha_eff02 = 10. * np.pi / 180. alpha_inf = alpha_eff02 - alpha0 Uinf_here = geo.rotate_speed(Uinf0, alpha_inf) S02.dZetadt[:, :] = -(Uinf_here - Uinf0) S02.solve_static_Gamma2d() Fref02 = np.sum(S02.Faero, 0) print('Testing steady aerofoil for M=%d...' % M) for aa in range(len(DAlphaList)): # Perturbation alpha_eff = np.pi / 180. * DAlphaList[aa] alpha_inf = alpha_eff - alpha0 Uinf_here = geo.rotate_speed(Uinf0, alpha_inf) qinf_tot = 0.5 * rho * (S01.Uabs)**2 print('\tAlpha effective=%.2f deg' % DAlphaList[aa]) ### Reference nonlinear solution Sref = uvlm.solver(M, Mw, b, Uinf0, alpha0, rho) Sref.build_camber_plate(Mcamb=10, Pcamb=4) Sref.Wzeta[:, :] = Uinf_here - Uinf0 Sref.solve_static_Gamma2d() FXnnl[aa, mm], FZnnl[aa, mm] = np.sum(Sref.Faero, 0) ### Linearised solution 01: Slin01 = linuvlm.solver(S01) Slin01.dZetadt[:, :] = -(Uinf_here - (S01.Uzeta[0, :] - S01.dZetadt[0, :])) Slin01.solve_static_Gamma2d() dFtot01 = np.sum(Slin01.Faero, 0) FXlin01[aa, mm], FZlin01[aa, mm] = dFtot01 + Fref01 ### Linearised solution 02: Slin02 = linuvlm.solver(S02) Slin02.dZetadt[:, :] = -(Uinf_here - (S02.Uzeta[0, :] - S02.dZetadt[0, :])) Slin02.solve_static_Gamma2d() FXlin02[aa, mm], FZlin02[aa, mm] = np.sum(Slin02.Faero, 0) dFtot02 = np.sum(Slin02.Faero, 0) FXlin02[aa, mm], FZlin02[aa, mm] = dFtot02 + Fref02 clist = [ 'k', 'r', 'b', '0.6', ] ### Aerodynamic forces fig1 = plt.figure('Drag', (12, 4)) fig2 = plt.figure('Lift', (12, 4)) ax1 = fig1.add_subplot(111) ax2 = fig2.add_subplot(111) ax1.plot(DAlphaList, FXlin01[:, mm], 'k', lw=2, ls='--', label=r'M=%.2d lin 0 deg' % MList[mm]) ax1.plot(DAlphaList, FXlin02[:, mm], 'b', lw=2, ls=':', label=r'M=%.2d lin 10 deg' % MList[mm]) ax1.plot(DAlphaList, FXnnl[:, mm], 'r', lw=Nm - mm, label=r'M=%.2d nnl' % MList[mm]) # ax2.plot(DAlphaList, FZlin01[:, mm], 'k', lw=2, ls='--', label=r'M=%.2d lin 0 deg' % MList[mm]) ax2.plot(DAlphaList, FZlin02[:, mm], 'b', lw=Nm - mm, ls=':', label=r'M=%.2d lin 10 deg' % MList[mm]) ax2.plot(DAlphaList, FZnnl[:, mm], 'r', lw=2, label=r'M=%.2d nnl' % MList[mm]) ax1.set_xlabel(r'\alpha [deg]') ax1.set_title(r'Horizontal Force [N]') ax1.legend() ax2.set_xlabel(r'\alpha [deg]') ax2.set_title(r'Vertical Force [N]') ax2.legend() if self.SHOW_PLOT: plt.show() else: plt.close()
def test_camber_rotation(self): ''' Calculate steady case / very short wake can be used. A cambered aerofoil is tested at different angles of attack. Two linearisations, about zero and 10deg are used. ''' DAlphaList = [ 0.0, 0.1, 0.2, 0.3, 0.5, 1., 2., 4., 6., 8., 9.5, 9.7, 9.8, 9.9, 10., 10.1, 10.2, 10.3, 10.5, 12., 14., 16., 18. ] # degs MList = [20] Nm, mm = 1, 0 Na = len(DAlphaList) Llin01, Dlin01 = np.zeros((Na, Nm)), np.zeros((Na, Nm)) Llin02, Dlin02 = np.zeros((Na, Nm)), np.zeros((Na, Nm)) Lnnl, Dnnl = np.zeros((Na, Nm)), np.zeros((Na, Nm)) # input: Mw = 2 M = MList[mm] chord = 3. b = 0.5 * chord Uinf = np.array([20., 0.]) rho = 1.225 # reference condition 1: aerofoil at 0 deg angle alpha01 = 0. * np.pi / 180. S01 = uvlm.solver(M, Mw, b, Uinf, alpha01, rho=1.225) S01.build_camber_plate(Mcamb=10, Pcamb=4) S01.solve_static_Gamma2d() Fref01 = np.sum(S01.Faero, 0) # reference condition 2: aerofoil at 10 deg angle alpha02 = 10. * np.pi / 180. S02 = uvlm.solver(M, Mw, b, Uinf, alpha02, rho=1.225) S02.build_camber_plate(Mcamb=10, Pcamb=4) S02.solve_static_Gamma2d() Fref02 = np.sum(S02.Faero, 0) print('Testing steady aerofoil for M=%d...' % M) for aa in range(len(DAlphaList)): # Perturbation alpha_tot = np.pi / 180. * DAlphaList[aa] dUinf = 0.0 # velocity [m/s] qinf_tot = 0.5 * rho * (S01.Uabs + dUinf)**2 print('\talpha==%.2f deg' % DAlphaList[aa]) ### Linearised solution 01: # Perturb reference state dalpha01 = alpha_tot - alpha01 # angle [rad] ZetaRot = geo.rotate_aerofoil(S01.Zeta, dalpha01) dZeta = ZetaRot - S01.Zeta Slin01 = linuvlm.solver(S01) Slin01.Zeta = dZeta # solve Slin01.solve_static_Gamma2d() # store data dFtot01 = np.sum(Slin01.Faero, 0) Dlin01[aa, mm], Llin01[aa, mm] = dFtot01 + Fref01 ### Linearised solution 02: # Perturb reference state dalpha02 = alpha_tot - alpha02 # angle [rad] ZetaRot = geo.rotate_aerofoil(S02.Zeta, dalpha02) dZeta = ZetaRot - S02.Zeta Slin02 = linuvlm.solver(S02) Slin02.Zeta = dZeta # solve Slin02.solve_static_Gamma2d() # store data dFtot02 = np.sum(Slin02.Faero, 0) Dlin02[aa, mm], Llin02[aa, mm] = dFtot02 + Fref02 ### Reference nonlinear solution Sref = uvlm.solver(M, Mw, b, Uinf, alpha_tot, rho) Sref.build_camber_plate(Mcamb=10, Pcamb=4) # solve Sref.solve_static_Gamma2d() # store Dnnl[aa, mm], Lnnl[aa, mm] = np.sum(Sref.Faero, 0) clist = [ 'k', 'r', 'b', '0.6', ] ### Aerodynamic forces fig1 = plt.figure('Drag', (12, 4)) fig2 = plt.figure('Lift', (12, 4)) ax1 = fig1.add_subplot(111) ax2 = fig2.add_subplot(111) ax1.plot(DAlphaList, Dnnl[:, mm], 'r', lw=Nm - mm, label=r'M=%.2d nnl' % MList[mm]) ax1.plot(DAlphaList, Dlin01[:, mm], 'k', lw=2, ls='--', label=r'M=%.2d lin 0 deg' % MList[mm]) ax1.plot(DAlphaList, Dlin02[:, mm], 'b', lw=2, ls=':', label=r'M=%.2d lin 10 deg' % MList[mm]) # ax2.plot(DAlphaList, Lnnl[:, mm], 'r', lw=2, label=r'M=%.2d nnl' % MList[mm]) ax2.plot(DAlphaList, Llin01[:, mm], 'k', lw=2, ls='--', label=r'M=%.2d lin 0 deg' % MList[mm]) ax2.plot(DAlphaList, Llin02[:, mm], 'b', lw=Nm - mm, ls=':', label=r'M=%.2d lin 10 deg' % MList[mm]) ax1.set_xlabel(r'\alpha [deg]') ax1.set_title(r'Drag') ax1.legend() ax2.set_xlabel(r'\alpha [deg]') ax2.set_title(r'Lift') ax2.legend() fig1 = plt.figure('Relative error lift', (12, 4)) ax1 = fig1.add_subplot(111) ax1.plot(DAlphaList, Llin01[:, mm] / Lnnl[:, mm] - 1., 'k', lw=2, ls='--', label=r'M=%.2d lin 0 deg' % MList[mm]) ax1.plot(DAlphaList, Llin02[:, mm] / Lnnl[:, mm] - 1., 'b', lw=2, ls=':', label=r'M=%.2d lin 10 deg' % MList[mm]) ax1.set_xlabel(r'$\alpha$ [deg]') ax1.set_ylabel(r'relative error') ax1.set_title(r'Lift error w.r.t. exact solution') ax1.legend() ax2.legend() if self.SHOW_PLOT: plt.show() else: plt.close()