Source code for RCAIDE.Library.Methods.Aerodynamics.Airfoil_Panel_Method.airfoil_analysis

# RCAIDE/Methods/Aerodynamics/Airfoil_Panel_Method/airfoil_analysis.py
# 
# 
# Created:  Dec 2023, M. Clarke
# Modified: Apr 2024, N. Nanjappa

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------
# RCAIDE imports  
from RCAIDE.Framework.Core import Data
from .hess_smith           import hess_smith
from .thwaites_method      import thwaites_method 
from .heads_method         import heads_method 
from .aero_coeff           import aero_coeff
from .chordwise_distribution import chordwise_distribution
from .cf_filter            import cf_filter

# pacakge imports  
import numpy as np  

# ----------------------------------------------------------------------------------------------------------------------
# airfoil_analysis.py
# ----------------------------------------------------------------------------------------------------------------------
[docs] def airfoil_analysis(airfoil_geometry,alpha,Re_L,initial_momentum_thickness=1E-5,tolerance = 1E0,H_wake = 1.05,Ue_wake = 0.99): """This computes the aerodynamic polars as well as the boundary layer properties of an airfoil at a defined set of reynolds numbers and angle of attacks Assumptions: Michel Criteria used for transition Squire-Young relation for total drag (exrapolates theta from end of wake). However, since we do not have a wake we will assume H_wake = 1.05 and Ue_wake = 0.99 The freestream velocity is taken to be 1 m/s. Airfoil has a unit chord length [1 m]. Source: N/A Inputs: airfoil_geometry - airfoil geometry points [unitless] alpha - angle of attacks [radians] Re_L - Reynolds numbers [unitless] batch_analysis - boolean : If True: the specified number of angle of attacks and Reynolds [boolean] numbers are used to create a table of 2-D results for each combination Note: Can only accomodate one airfoil If False:The airfoils specified are run and corresponding angle of attacks and Reynolds numbers Note: The number of airfoils, angle of attacks and reynolds numbers must all the same dimension Outputs: airfoil_properties. AoA - angle of attack [radians Re - Reynolds number [unitless] Cl - lift coefficients [unitless] Cd - drag coefficients [unitless] Cm - moment coefficients [unitless] normals - surface normals of airfoil [unitless] x - x coordinate points on airfoil [unitless] y - y coordinate points on airfoil [unitless] x_bl - x coordinate points on airfoil adjusted to include boundary layer [unitless] y_bl - y coordinate points on airfoil adjusted to include boundary layer [unitless] Cp - pressure coefficient distribution [unitless] Ue_Vinf - ratio of boundary layer edge velocity to freestream [unitless] dVe - derivative of boundary layer velocity [m/s-m] theta - momentum thickness [m] delta_star - displacement thickness [m] delta - boundary layer thickness [m] H - shape factor [unitless] Cf - local skin friction coefficient [unitless] Re_theta_t - Reynolds Number as a function of theta transition location [unitless] tr_crit - critical transition criteria [unitless] Properties Used: N/A """ ncases = len(alpha[0,:]) ncpts = len(Re_L) x_coord = airfoil_geometry.x_coordinates y_coord = airfoil_geometry.y_coordinates npanel = len(x_coord)-1 x_coord_3d = np.tile(x_coord[:,None,None],(1,ncases,ncpts)) # number of points, number of cases, number of control points y_coord_3d = np.tile(y_coord[:,None,None],(1,ncases,ncpts)) # number of points, number of cases, number of control points # Begin by solving for velocity distribution at airfoil surface using inviscid panel simulation # these are the locations (faces) where things are computed , len = n panel # dimension of vt = npanel x ncases x ncpts X,Y,vt,normals = hess_smith(x_coord_3d,y_coord_3d,alpha,Re_L,npanel) # Reynolds number RE_L_VALS = Re_L.T # kinematic coefficient of viscosity (assuming unit chord and unit inlet velocity) nu = 1/RE_L_VALS # --------------------------------------------------------------------- # Bottom surface of airfoil # --------------------------------------------------------------------- VT = np.ma.masked_greater(vt,0 ) VT_mask = np.ma.masked_greater(vt,0 ).mask X_BOT_VALS = np.ma.array(X, mask = VT_mask)[::-1] Y_BOT = np.ma.array(Y, mask = VT_mask)[::-1] X_BOT = np.zeros_like(X_BOT_VALS) X_BOT[1:] = np.cumsum(np.sqrt((X_BOT_VALS[1:] - X_BOT_VALS[:-1])**2 + (Y_BOT[1:] - Y_BOT[:-1])**2),axis = 0) first_idx = np.ma.count_masked(X_BOT,axis = 0) mask_count = np.ma.count(X_BOT,axis = 0) prev_index = first_idx-1 first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) aoas = list(np.repeat(np.arange(ncases),ncpts)) res = list(np.tile(np.arange(ncpts),ncases) ) X_BOT.mask[first_panel,aoas,res] = False # flow velocity and pressure of on botton surface VE_BOT = -VT[::-1] # velocity gradients on bottom surface DVE_BOT = np.ma.zeros(np.shape(X_BOT)) DVE_BOT_TEMP = np.diff(VE_BOT,axis = 0)/np.diff(X_BOT,axis = 0) a = X_BOT[1:-1] - X_BOT[:-2] b = X_BOT[2:] - X_BOT[:-2] DVE_BOT[1:-1] = ((b*DVE_BOT_TEMP[:-1] + a*DVE_BOT_TEMP[1:])/(a+b)).data DVE_BOT.mask = X_BOT.mask DVE_BOT[first_panel,aoas,res] = DVE_BOT_TEMP[first_panel,aoas,res] DVE_BOT[last_panel,aoas,res] = DVE_BOT_TEMP[last_paneldve,aoas,res] # x - location of stagnation point L_BOT = X_BOT[-1,:,:] x_mask = X_BOT.mask # This is to prevent high AOA mask errors ################################################################## VT_mask_count = np.ma.count_masked(VT, axis=0) X_BOT_mask_count = np.ma.count_masked(X_BOT, axis=0) # Find columns where the counts are not equal # wrong_columns = np.where(X_BOT_mask_count != VT_mask_count)[0] # # Set the entire column to True in X_BOT's mask # X_BOT.mask[:, columns_to_mask] = True # VE_BOT.mask[:, columns_to_mask] = True # DVE_BOT.mask[:, columns_to_mask] = True AOA_deg = np.rad2deg(alpha) wrong_columns = np.where(abs(AOA_deg)>80)[1] ############################################################################################################ # laminar boundary layer properties using thwaites method BOT_T_RESULTS = thwaites_method(npanel,ncases,ncpts, nu, L_BOT , RE_L_VALS, X_BOT, VE_BOT, DVE_BOT,tolerance,wrong_columns, THETA_0=initial_momentum_thickness) X_T_BOT = BOT_T_RESULTS.X_T THETA_T_BOT = BOT_T_RESULTS.THETA_T DELTA_STAR_T_BOT = BOT_T_RESULTS.DELTA_STAR_T H_T_BOT = BOT_T_RESULTS.H_T CF_T_BOT = BOT_T_RESULTS.CF_T RE_THETA_T_BOT = BOT_T_RESULTS.RE_THETA_T RE_X_T_BOT = BOT_T_RESULTS.RE_X_T DELTA_T_BOT = BOT_T_RESULTS.DELTA_T # transition location TR_CRIT_BOT = RE_THETA_T_BOT - 1.174*(1 + 22400/RE_X_T_BOT)*RE_X_T_BOT**0.46 CRITERION_BOT = np.ma.masked_greater(TR_CRIT_BOT,0 ) mask_count = np.ma.count(CRITERION_BOT,axis = 0) mask_count[mask_count == npanel] = npanel-1 transition_panel = list(mask_count.flatten()) aoas = list(np.repeat(np.arange(ncases),ncpts)) res = list(np.tile(np.arange(ncpts),ncases)) X_TR_BOT = X_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) DELTA_STAR_TR_BOT = DELTA_STAR_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) THETA_TR_BOT = THETA_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) DELTA_TR_BOT = DELTA_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) CF_TR_BOT = CF_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) H_TR_BOT = H_T_BOT[transition_panel,aoas,res].reshape(ncases,ncpts) # TURBULENT_SURF TURBULENT_SURF = L_BOT.data TURBULENT_COORD = np.ma.masked_less(X_BOT.data - X_TR_BOT,0) turb_mask = TURBULENT_COORD.mask # turbulent boundary layer properties using heads method BOT_H_RESULTS = heads_method(npanel,ncases,ncpts, nu, DELTA_TR_BOT, THETA_TR_BOT , DELTA_STAR_TR_BOT, CF_TR_BOT, H_TR_BOT, RE_L_VALS, TURBULENT_COORD, VE_BOT, DVE_BOT, TURBULENT_SURF, tolerance,wrong_columns) X_H_BOT = BOT_H_RESULTS.X_H THETA_H_BOT = BOT_H_RESULTS.THETA_H DELTA_STAR_H_BOT = BOT_H_RESULTS.DELTA_STAR_H H_H_BOT = BOT_H_RESULTS.H_H CF_H_BOT = BOT_H_RESULTS.CF_H RE_THETA_H_BOT = BOT_H_RESULTS.RE_THETA_H RE_X_H_BOT = BOT_H_RESULTS.RE_X_H DELTA_H_BOT = BOT_H_RESULTS.DELTA_H # Apply Masks to surface vectors X_T_BOT = np.ma.array(X_T_BOT , mask = CRITERION_BOT.mask) THETA_T_BOT = np.ma.array(THETA_T_BOT , mask = CRITERION_BOT.mask) DELTA_STAR_T_BOT = np.ma.array(DELTA_STAR_T_BOT , mask = CRITERION_BOT.mask) H_T_BOT = np.ma.array(H_T_BOT , mask = CRITERION_BOT.mask) CF_T_BOT = np.ma.array(CF_T_BOT , mask = CRITERION_BOT.mask) RE_THETA_T_BOT = np.ma.array(RE_THETA_T_BOT , mask = CRITERION_BOT.mask) RE_X_T_BOT = np.ma.array(RE_X_T_BOT , mask = CRITERION_BOT.mask) DELTA_T_BOT = np.ma.array(DELTA_T_BOT , mask = CRITERION_BOT.mask) X_H_BOT = np.ma.array(X_H_BOT , mask = ~CRITERION_BOT.mask) THETA_H_BOT = np.ma.array(THETA_H_BOT , mask = ~CRITERION_BOT.mask) DELTA_STAR_H_BOT = np.ma.array(DELTA_STAR_H_BOT , mask = ~CRITERION_BOT.mask) H_H_BOT = np.ma.array(H_H_BOT , mask = ~CRITERION_BOT.mask) CF_H_BOT = np.ma.array(CF_H_BOT , mask = ~CRITERION_BOT.mask) RE_THETA_H_BOT = np.ma.array(RE_THETA_H_BOT , mask = ~CRITERION_BOT .mask) RE_X_H_BOT = np.ma.array(RE_X_H_BOT , mask = ~CRITERION_BOT .mask) DELTA_H_BOT = np.ma.array(DELTA_H_BOT , mask = ~CRITERION_BOT.mask) # Concatenate vectors X_H_BOT_MOD = X_H_BOT.data + X_TR_BOT.data X_H_BOT_MOD = np.ma.array(X_H_BOT_MOD, mask = X_H_BOT.mask) X_BOT_SURF = np.ma.concatenate([X_T_BOT, X_H_BOT_MOD], axis = 0 ) THETA_BOT_SURF = np.ma.concatenate([THETA_T_BOT,THETA_H_BOT ], axis = 0) DELTA_STAR_BOT_SURF = np.ma.concatenate([DELTA_STAR_T_BOT,DELTA_STAR_H_BOT], axis = 0) H_BOT_SURF = np.ma.concatenate([H_T_BOT,H_H_BOT], axis = 0) CF_BOT_SURF = np.ma.concatenate([CF_T_BOT,CF_H_BOT], axis = 0) RE_THETA_BOT_SURF = np.ma.concatenate([RE_THETA_T_BOT,RE_THETA_H_BOT], axis = 0) RE_X_BOT_SURF = np.ma.concatenate([RE_X_T_BOT,RE_X_H_BOT], axis = 0) DELTA_BOT_SURF = np.ma.concatenate([DELTA_T_BOT,DELTA_H_BOT], axis = 0) # Flatten array to extract values X_BOT_SURF_1 = X_BOT_SURF.flatten('F') THETA_BOT_SURF_1 = THETA_BOT_SURF.flatten('F') DELTA_STAR_BOT_SURF_1 = DELTA_STAR_BOT_SURF.flatten('F') H_BOT_SURF_1 = H_BOT_SURF.flatten('F') CF_BOT_SURF_1 = CF_BOT_SURF.flatten('F') RE_THETA_BOT_SURF_1 = RE_THETA_BOT_SURF.flatten('F') RE_X_BOT_SURF_1 = RE_X_BOT_SURF.flatten('F') DELTA_BOT_SURF_1 = DELTA_BOT_SURF.flatten('F') # Extract values that are not masked X_BOT_SURF_2 = X_BOT_SURF_1.data[~X_BOT_SURF_1.mask] THETA_BOT_SURF_2 = THETA_BOT_SURF_1.data[~THETA_BOT_SURF_1.mask] DELTA_STAR_BOT_SURF_2 = DELTA_STAR_BOT_SURF_1.data[~DELTA_STAR_BOT_SURF_1.mask] H_BOT_SURF_2 = H_BOT_SURF_1.data[~H_BOT_SURF_1.mask] CF_BOT_SURF_2 = CF_BOT_SURF_1.data[~CF_BOT_SURF_1.mask] RE_THETA_BOT_SURF_2 = RE_THETA_BOT_SURF_1.data[~RE_THETA_BOT_SURF_1.mask] RE_X_BOT_SURF_2 = RE_X_BOT_SURF_1.data[~RE_X_BOT_SURF_1.mask] DELTA_BOT_SURF_2 = DELTA_BOT_SURF_1.data[~DELTA_BOT_SURF_1.mask] X_BOT_SURF = X_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') THETA_BOT_SURF = THETA_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') DELTA_STAR_BOT_SURF = DELTA_STAR_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') H_BOT_SURF = H_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') CF_BOT_SURF = CF_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') RE_THETA_BOT_SURF = RE_THETA_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') RE_X_BOT_SURF = RE_X_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') DELTA_BOT_SURF = DELTA_BOT_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') # ------------------------------------------------------------------------------------------------------ # Top surface of airfoil # ------------------------------------------------------------------------------------------------------ VT = np.ma.masked_less(vt,0 ) CP = 1 - vt**2 VT_mask = np.ma.masked_less(vt,0 ).mask X_TOP_VALS = np.ma.array(X, mask = VT_mask) Y_TOP = np.ma.array(Y, mask = VT_mask) X_TOP = np.zeros_like(X_TOP_VALS) X_TOP[1:] = np.cumsum(np.sqrt((X_TOP_VALS[1:] - X_TOP_VALS[:-1])**2 + (Y_TOP[1:] - Y_TOP[:-1])**2),axis = 0) first_idx = np.ma.count_masked(X_TOP,axis = 0) mask_count = np.ma.count(X_TOP,axis = 0) prev_index = first_idx-1 first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) aoas = list(np.repeat(np.arange(ncases),ncpts)) res = list(np.tile(np.arange(ncpts),ncases) ) X_TOP.mask[first_panel,aoas,res] = False # flow velocity and pressure of on botton surface VE_TOP = VT # velocity gradients on bottom surface DVE_TOP = np.ma.zeros(np.shape(X_TOP)) DVE_TOP_TEMP = np.diff(VE_TOP,axis = 0)/np.diff(X_TOP,axis = 0) a = X_TOP[1:-1] - X_TOP[:-2] b = X_TOP[2:] - X_TOP[:-2] DVE_TOP[1:-1] = ((b*DVE_TOP_TEMP[:-1] + a*DVE_TOP_TEMP[1:])/(a+b)).data DVE_TOP.mask = X_TOP.mask DVE_TOP[first_panel,aoas,res] = DVE_TOP_TEMP[first_panel,aoas,res] DVE_TOP[last_panel,aoas,res] = DVE_TOP_TEMP[last_paneldve,aoas,res] # x - location of stagnation point L_TOP = X_TOP[-1,:,:] # laminar boundary layer properties using thwaites method TOP_T_RESULTS = thwaites_method(npanel,ncases,ncpts, nu, L_TOP , RE_L_VALS,X_TOP,VE_TOP, DVE_TOP,tolerance,wrong_columns, THETA_0=initial_momentum_thickness) X_T_TOP = TOP_T_RESULTS.X_T THETA_T_TOP = TOP_T_RESULTS.THETA_T DELTA_STAR_T_TOP = TOP_T_RESULTS.DELTA_STAR_T H_T_TOP = TOP_T_RESULTS.H_T CF_T_TOP = TOP_T_RESULTS.CF_T RE_THETA_T_TOP = TOP_T_RESULTS.RE_THETA_T RE_X_T_TOP = TOP_T_RESULTS.RE_X_T DELTA_T_TOP = TOP_T_RESULTS.DELTA_T # transition location TR_CRIT_TOP = RE_THETA_T_TOP - 1.174*(1 + 22400/RE_X_T_TOP)*(RE_X_T_TOP**0.46) CRITERION_TOP = np.ma.masked_greater(TR_CRIT_TOP,0 ) mask_count = np.ma.count(CRITERION_TOP,axis = 0) mask_count[mask_count == npanel] = npanel-1 transition_panel = list(mask_count.flatten()) aoas = list(np.repeat(np.arange(ncases),ncpts)) res = list(np.tile(np.arange(ncpts),ncases) ) X_TR_TOP = X_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) DELTA_STAR_TR_TOP = DELTA_STAR_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) THETA_TR_TOP = THETA_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) DELTA_TR_TOP = DELTA_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) CF_TR_TOP = CF_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) H_TR_TOP = H_T_TOP[transition_panel,aoas,res].reshape(ncases,ncpts) TURBULENT_SURF = L_TOP.data TURBULENT_COORD = np.ma.masked_less( X_TOP.data - X_TR_TOP,0) # turbulent boundary layer properties using heads method TOP_H_RESULTS = heads_method(npanel,ncases,ncpts, nu, DELTA_TR_TOP, THETA_TR_TOP , DELTA_STAR_TR_TOP, CF_TR_TOP, H_TR_TOP, RE_L_VALS, TURBULENT_COORD, VE_TOP, DVE_TOP, TURBULENT_SURF, tolerance,wrong_columns) X_H_TOP = TOP_H_RESULTS.X_H THETA_H_TOP = TOP_H_RESULTS.THETA_H DELTA_STAR_H_TOP = TOP_H_RESULTS.DELTA_STAR_H H_H_TOP = TOP_H_RESULTS.H_H CF_H_TOP = TOP_H_RESULTS.CF_H RE_THETA_H_TOP = TOP_H_RESULTS.RE_THETA_H RE_X_H_TOP = TOP_H_RESULTS.RE_X_H DELTA_H_TOP = TOP_H_RESULTS.DELTA_H # Apply Masks to surface vectors X_T_TOP = np.ma.array(X_T_TOP , mask = CRITERION_TOP.mask) THETA_T_TOP = np.ma.array(THETA_T_TOP , mask = CRITERION_TOP.mask) DELTA_STAR_T_TOP = np.ma.array(DELTA_STAR_T_TOP , mask = CRITERION_TOP.mask) H_T_TOP = np.ma.array(H_T_TOP , mask = CRITERION_TOP.mask) CF_T_TOP = np.ma.array(CF_T_TOP , mask = CRITERION_TOP.mask) RE_THETA_T_TOP = np.ma.array(RE_THETA_T_TOP , mask = CRITERION_TOP.mask) RE_X_T_TOP = np.ma.array(RE_X_T_TOP , mask = CRITERION_TOP.mask) DELTA_T_TOP = np.ma.array(DELTA_T_TOP , mask = CRITERION_TOP.mask) X_H_TOP = np.ma.array(X_H_TOP , mask = ~CRITERION_TOP.mask) THETA_H_TOP = np.ma.array(THETA_H_TOP , mask = ~CRITERION_TOP.mask) DELTA_STAR_H_TOP = np.ma.array(DELTA_STAR_H_TOP , mask = ~CRITERION_TOP.mask) H_H_TOP = np.ma.array(H_H_TOP , mask = ~CRITERION_TOP.mask) CF_H_TOP = np.ma.array(CF_H_TOP , mask = ~CRITERION_TOP.mask) RE_THETA_H_TOP = np.ma.array(RE_THETA_H_TOP , mask = ~CRITERION_TOP.mask) RE_X_H_TOP = np.ma.array(RE_X_H_TOP , mask = ~CRITERION_TOP.mask) DELTA_H_TOP = np.ma.array(DELTA_H_TOP , mask = ~CRITERION_TOP.mask) # Concatenate laminar and turbulent vectors X_H_TOP_MOD = X_H_TOP.data + X_TR_TOP.data X_H_TOP_MOD = np.ma.array(X_H_TOP_MOD, mask = X_H_TOP.mask) X_TOP_SURF = np.ma.concatenate([X_T_TOP, X_H_TOP_MOD], axis = 0 ) THETA_TOP_SURF = np.ma.concatenate([THETA_T_TOP,THETA_H_TOP ], axis = 0) DELTA_STAR_TOP_SURF = np.ma.concatenate([DELTA_STAR_T_TOP,DELTA_STAR_H_TOP], axis = 0) H_TOP_SURF = np.ma.concatenate([H_T_TOP,H_H_TOP], axis = 0) CF_TOP_SURF = np.ma.concatenate([CF_T_TOP,CF_H_TOP], axis = 0) RE_THETA_TOP_SURF = np.ma.concatenate([RE_THETA_T_TOP,RE_THETA_H_TOP], axis = 0) RE_X_TOP_SURF = np.ma.concatenate([RE_X_T_TOP,RE_X_H_TOP], axis = 0) DELTA_TOP_SURF = np.ma.concatenate([DELTA_T_TOP,DELTA_H_TOP], axis = 0) # Flatten array to extract values X_TOP_SURF_1 = X_TOP_SURF.flatten('F') THETA_TOP_SURF_1 = THETA_TOP_SURF.flatten('F') DELTA_STAR_TOP_SURF_1 = DELTA_STAR_TOP_SURF.flatten('F') H_TOP_SURF_1 = H_TOP_SURF.flatten('F') CF_TOP_SURF_1 = CF_TOP_SURF.flatten('F') RE_THETA_TOP_SURF_1 = RE_THETA_TOP_SURF.flatten('F') RE_X_TOP_SURF_1 = RE_X_TOP_SURF.flatten('F') DELTA_TOP_SURF_1 = DELTA_TOP_SURF.flatten('F') # Extract values that are not masked X_TOP_SURF_2 = X_TOP_SURF_1.data[~X_TOP_SURF_1.mask] THETA_TOP_SURF_2 = THETA_TOP_SURF_1.data[~THETA_TOP_SURF_1.mask] DELTA_STAR_TOP_SURF_2 = DELTA_STAR_TOP_SURF_1.data[~DELTA_STAR_TOP_SURF_1.mask] H_TOP_SURF_2 = H_TOP_SURF_1.data[~H_TOP_SURF_1.mask] CF_TOP_SURF_2 = CF_TOP_SURF_1.data[~CF_TOP_SURF_1.mask] RE_THETA_TOP_SURF_2 = RE_THETA_TOP_SURF_1.data[~RE_THETA_TOP_SURF_1.mask] RE_X_TOP_SURF_2 = RE_X_TOP_SURF_1.data[~RE_X_TOP_SURF_1.mask] DELTA_TOP_SURF_2 = DELTA_TOP_SURF_1.data[~DELTA_TOP_SURF_1.mask] X_TOP_SURF = X_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') THETA_TOP_SURF = THETA_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') DELTA_STAR_TOP_SURF = DELTA_STAR_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') H_TOP_SURF = H_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') CF_TOP_SURF = CF_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') RE_THETA_TOP_SURF = RE_THETA_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') RE_X_TOP_SURF = RE_X_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') DELTA_TOP_SURF = DELTA_TOP_SURF_2.reshape((npanel,ncases,ncpts),order = 'F') # ------------------------------------------------------------------------------------------------------ # concatenate lower and upper surfaces # ------------------------------------------------------------------------------------------------------ THETA = concatenate_surfaces(X_BOT,X_TOP,THETA_BOT_SURF,THETA_TOP_SURF,npanel,ncases,ncpts,wrong_columns) # The error is here now, will be fixed soon DELTA_STAR = concatenate_surfaces(X_BOT,X_TOP,DELTA_STAR_BOT_SURF,DELTA_STAR_TOP_SURF,npanel,ncases,ncpts,wrong_columns) H = concatenate_surfaces(X_BOT,X_TOP,H_BOT_SURF,H_TOP_SURF,npanel,ncases,ncpts,wrong_columns) CF = concatenate_surfaces(X_BOT,X_TOP,CF_BOT_SURF,CF_TOP_SURF,npanel,ncases,ncpts,wrong_columns) RE_THETA = concatenate_surfaces(X_BOT,X_TOP,RE_THETA_BOT_SURF,RE_THETA_TOP_SURF,npanel,ncases,ncpts,wrong_columns) RE_X = concatenate_surfaces(X_BOT,X_TOP,RE_X_BOT_SURF,RE_X_TOP_SURF,npanel,ncases,ncpts,wrong_columns) DELTA = concatenate_surfaces(X_BOT,X_TOP,DELTA_BOT_SURF,DELTA_TOP_SURF,npanel,ncases,ncpts,wrong_columns) VE_VALS = np.ma.concatenate([np.flip(VE_BOT,axis = 0),VE_TOP ], axis = 0) DVE_VALS = np.ma.concatenate([np.flip(DVE_BOT,axis = 0),DVE_TOP], axis = 0) DVE_VALS.mask[:,wrong_columns,:] = VE_VALS.mask[:,wrong_columns,:] # DEBUG FIX FOR ENSURING WRONG COLUMNS FIT VE_VALS_1 = VE_VALS.flatten('F') DVE_VALS_1 = DVE_VALS.flatten('F') VE_VALS_2 = VE_VALS_1.data[~VE_VALS_1.mask] DVE_VALS_2 = DVE_VALS_1.data[~DVE_VALS_1.mask] VE = VE_VALS_2.reshape((npanel,ncases,ncpts),order = 'F') DVE = DVE_VALS_2.reshape((npanel,ncases,ncpts),order = 'F') # ------------------------------------------------------------------------------------------------------ # Filter Skin Friction Coeffiecient # ------------------------------------------------------------------------------------------------------ CF = cf_filter(ncpts, ncases, npanel, CF) # ------------------------------------------------------------------------------------------------------ # Compute effective surface of airfoil with boundary layer and recompute aerodynamic properties # ------------------------------------------------------------------------------------------------------ DELTA = np.nan_to_num(DELTA) # make sure no nans y_coord_3d_bl = Y + DELTA*normals[:,1,:,:] x_coord_3d_bl = X + DELTA*normals[:,0,:,:] npanel_mod = npanel-1 X_BL,Y_BL,vt_bl,normals_bl = hess_smith(x_coord_3d_bl,y_coord_3d_bl,alpha,Re_L,npanel_mod) # --------------------------------------------------------------------- # Bottom surface of airfoil with boundary layer # --------------------------------------------------------------------- VT_BL = np.ma.masked_greater(vt_bl,0 ) VT_BL_mask = np.ma.masked_greater(vt_bl,0 ).mask X_BL_BOT_VALS = np.ma.array(X_BL, mask = VT_BL_mask)[::-1] Y_BL_BOT = np.ma.array(Y_BL, mask = VT_BL_mask)[::-1] X_BL_BOT = np.zeros_like(X_BL_BOT_VALS) X_BL_BOT[1:] = np.cumsum(np.sqrt((X_BL_BOT_VALS[1:] - X_BL_BOT_VALS[:-1])**2 + (Y_BL_BOT[1:] - Y_BL_BOT[:-1])**2),axis = 0) first_idx = np.ma.count_masked(X_BL_BOT,axis = 0) mask_count = np.ma.count(X_BL_BOT,axis = 0) prev_index = first_idx-1 first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) aoas = list(np.repeat(np.arange(ncases),ncpts)) res = list(np.tile(np.arange(ncpts),ncases) ) X_BL_BOT.mask[first_panel,aoas,res] = False # flow velocity and pressure of on bottom surface VE_BL_BOT = -VT_BL[::-1] CP_BL_BOT = 1 - VE_BL_BOT**2 # --------------------------------------------------------------------- # Top surface of airfoil with boundary layer # --------------------------------------------------------------------- VT_BL = np.ma.masked_less(vt_bl,0 ) VT_BL_mask = np.ma.masked_less(vt_bl,0 ).mask X_BL_TOP_VALS = np.ma.array(X_BL, mask = VT_BL_mask) Y_BL_TOP = np.ma.array(Y_BL, mask = VT_BL_mask) X_BL_TOP = np.zeros_like(X_BL_TOP_VALS) X_BL_TOP[1:] = np.cumsum(np.sqrt((X_BL_TOP_VALS[1:] - X_BL_TOP_VALS[:-1])**2 + (Y_BL_TOP[1:] - Y_BL_TOP[:-1])**2),axis = 0) first_idx = np.ma.count_masked(X_BL_TOP,axis = 0) mask_count = np.ma.count(X_BL_TOP,axis = 0) prev_index = first_idx-1 first_panel = list(prev_index.flatten()) last_panel = list((first_idx-1 + mask_count).flatten()) last_paneldve = list((first_idx-2 + mask_count).flatten()) aoas = list(np.repeat(np.arange(ncases),ncpts)) res = list(np.tile(np.arange(ncpts),ncases) ) X_BL_TOP.mask[first_panel,aoas,res] = False # flow velocity and pressure of on botton surface VE_BL_TOP = VT_BL CP_BL_TOP = 1 - VE_BL_TOP**2 CP_BL_VALS = np.ma.concatenate([np.flip(CP_BL_BOT,axis = 0),CP_BL_TOP], axis = 0 ) CP_BL_VALS_1 = CP_BL_VALS.flatten('F') CP_BL_VALS_2 = CP_BL_VALS_1.data[~CP_BL_VALS_1.mask] CP_BL = CP_BL_VALS_2.reshape((npanel_mod,ncases,ncpts),order = 'F') DCP_DX = np.diff(CP_BL,axis=0)/ np.diff(X_BL,axis=0) AERO_RES = aero_coeff(x_coord_3d,y_coord_3d,CP,alpha,npanel) # Squire-Young relation for total drag del2_inf_l = THETA[0,:,:]*VE[0,:,:]**((5+H[0,:,:])/2) del2_inf_u = THETA[-1,:,:]*VE[-1,:,:]**((5+H[-1,:,:])/2) del2_inf = del2_inf_u + del2_inf_l cd_sqy = 2*del2_inf.T # chordwise distribution of lift and drag fL, fD = chordwise_distribution(x_coord_3d,y_coord_3d,CP,alpha,npanel,CF,vt) # FINAL DEBUG FIX FOR WRONG COLUMNS ################################################### AERO_RES.cl[:,wrong_columns] = 0 AERO_RES.cdpi[:,wrong_columns] = 0 AERO_RES.cm[:,wrong_columns] = 0 cd_sqy[:,wrong_columns] = 0 CP_BL[:,wrong_columns,:] = 0 DCP_DX[:,wrong_columns,:] = 0 VE[:,wrong_columns,:] = 0 DVE[:,wrong_columns,:] = 0 THETA[:,wrong_columns,:] = 0 DELTA_STAR[:,wrong_columns,:] = 0 DELTA[:,wrong_columns,:] = 0 RE_THETA[:,wrong_columns,:] = 0 RE_X[:,wrong_columns,:] = 0 H[:,wrong_columns,:] = 0 CF[:,wrong_columns,:] = 0 fL[:,wrong_columns,:] = 0 fD[:,wrong_columns,:] = 0 ####################################################################################### airfoil_properties_old = Data( AoA = alpha, Re = Re_L, cl_invisc = AERO_RES.cl, cd_invisc = AERO_RES.cdpi, cm_invisc = AERO_RES.cm, cd_visc = cd_sqy, normals = np.transpose(normals,(3,2,0,1)), x = np.transpose(X,(2,1,0)), y = np.transpose(Y,(2,1,0)), x_bl = np.transpose(X_BL ,(2,1,0)), y_bl = np.transpose(Y_BL ,(2,1,0)), cp = np.transpose(CP_BL,(2,1,0)), dcp_dx = np.transpose(DCP_DX,(2,1,0)), Ue_Vinf = np.transpose(VE ,(2,1,0)), dVe = np.transpose(DVE ,(2,1,0)), theta = np.transpose(THETA,(2,1,0)), delta_star = np.transpose(DELTA_STAR,(2,1,0)), delta = np.transpose(DELTA,(2,1,0)), Re_theta = np.transpose(RE_THETA,(2,1,0)), Re_x = np.transpose(RE_X,(2,1,0)), H = np.transpose(H,(2,1,0)), cf = np.transpose(CF,(2,1,0)), fL = fL, fD = fD ) return airfoil_properties_old
[docs] def concatenate_surfaces(X_BOT,X_TOP,FUNC_BOT_SURF,FUNC_TOP_SURF,npanel,ncases,ncpts,wrong_columns): '''Interpolation of airfoil properties Assumptions: None Source: None Inputs: X_BOT - bottom surface of airfoil [unitless] X_TOP - top surface of airfoil [unitless] FUNC_BOT_SURF - airfoil property computation discretization on bottom surface [multiple units] FUNC_TOP_SURF - airfoil property computation discretization on top surface [multiple units] npanel - number of panels [unitless] ncases - number of angle of attacks [unitless] ncpts - number of Reynolds numbers [unitless] Outputs: FUNC - airfoil property in user specified discretization on entire surface of airfoil [multiple units] Properties Used: N/A ''' FUNC = np.zeros((npanel,ncases,ncpts)) for case in range(ncases): for cpt in range(ncpts): if case in wrong_columns: continue top_func = FUNC_TOP_SURF[:,case,cpt][X_TOP[:,case,cpt].mask == False] bot_func = FUNC_BOT_SURF[:,case,cpt][X_BOT[:,case,cpt].mask == False] FUNC[:,case,cpt] = np.concatenate([bot_func[::-1],top_func]) return FUNC