Source code for RCAIDE.Library.Methods.Geometry.Planform.wing_segmented_planform

# RCAIDE/Library/Methods/Geometry/Two_Dimensional/Planform/wing_segmented_planform.py
# 
# 
# Created:  Jul 2024, M. Clarke 

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------    
from RCAIDE.Framework.Core import Data 

# package imports 
import numpy as np

# ----------------------------------------------------------------------------------------------------------------------
#  Wing Segmented Planform
# ----------------------------------------------------------------------------------------------------------------------    
[docs] def wing_segmented_planform(wing, overwrite_reference = False): """Computes standard wing planform values. Assumptions: Multisegmented wing. There is no unexposed wetted area, ie wing area that intersects inside a fuselage. Aerodynamic center is at 25% mean aerodynamic chord. Source: None Inputs: overwrite_reference <boolean> Determines if reference area, wetted area, and aspect ratio are overwritten based on the segment values. wing. chords.root [m] spans.projected [m] symmetric <boolean> Determines if wing is symmetric Outputs: wing. spans.total [m] chords.tip [m] chords.mean_aerodynamics [m] wing.chords.mean_geometric [m] areas.reference [m^2] taper [-] sweeps.quarter_chord [radians] aspect_ratio [-] thickness_to_chord [-] dihedral [radians] aerodynamic_center [m] x, y, and z location Properties Used: N/A """ # Unpack span = wing.spans.projected RC = wing.chords.root sym = wing.symmetric # Pull all the segment data into array format span_locs = [] twists = [] sweeps = [] dihedrals = [] chords = [] t_cs = [] for key in wing.segments.keys(): seg = wing.segments[key] span_locs.append(seg.percent_span_location) twists.append(seg.twist) chords.append(seg.root_chord_percent) sweeps.append(seg.sweeps.quarter_chord) t_cs.append(seg.thickness_to_chord) dihedrals.append(seg.dihedral_outboard) # Convert to arrays chords = np.array(chords) span_locs = np.array(span_locs) sweeps = np.array(sweeps) t_cs = np.array(t_cs) # Basic calcs: semispan = span/(1+sym) lengths_ndim = span_locs[1:]-span_locs[:-1] lengths_dim = lengths_ndim*semispan chords_dim = RC*chords tapers = chords[1:]/chords[:-1] # Calculate the areas of each segment As = (lengths_dim*chords_dim[:-1]-(chords_dim[:-1]-chords_dim[1:])*(lengths_dim/2)) # Calculate the weighted area, this should not include any unexposed area A_wets = 2*(1+0.2*t_cs[:-1])*As wet_area = np.sum(A_wets) # Calculate the wing area ref_area = np.sum(As)*(1+sym) # Calculate the Aspect Ratio AR = (span**2)/ref_area # Calculate the total span lens = lengths_dim/np.cos(dihedrals[:-1]) total_len = np.sum(np.array(lens))*(1+sym) # Calculate the mean geometric chord mgc = ref_area/span # Calculate the mean aerodynamic chord A = chords_dim[:-1] B = (A-chords_dim[1:])/(-lengths_ndim) C = span_locs[:-1] integral = ((A+B*(span_locs[1:]-C))**3-(A+B*(span_locs[:-1]-C))**3)/(3*B) # For the cases when the wing doesn't taper in a spot integral[np.isnan(integral)] = (A[np.isnan(integral)]**2)*((lengths_ndim)[np.isnan(integral)]) MAC = (semispan*(1+sym)/(ref_area))*np.sum(integral) # Calculate the taper ratio lamda = chords[-1]/chords[0] # the tip chord ct = chords_dim[-1] # Calculate an average t/c weighted by area t_c = np.sum(As*t_cs[:-1])/(ref_area/2) # Calculate the segment leading edge sweeps r_offsets = chords_dim[:-1]/4 t_offsets = chords_dim[1:]/4 le_sweeps = np.arctan((r_offsets+np.tan(sweeps[:-1])*(lengths_dim)-t_offsets)/(lengths_dim)) # Calculate the effective sweeps c_4_sweep = np.arctan(np.sum(lengths_ndim*np.tan(sweeps[:-1]))) le_sweep_total= np.arctan(np.sum(lengths_ndim*np.tan(le_sweeps))) # Calculate the aerodynamic center, but first the centroid dxs = np.cumsum(np.concatenate([np.array([0]),np.tan(le_sweeps[:-1])*lengths_dim[:-1]])) dys = np.cumsum(np.concatenate([np.array([0]),lengths_dim[:-1]])) dzs = np.cumsum(np.concatenate([np.array([0]),np.tan(dihedrals[:-2])*lengths_dim[:-1]])) Cxys = [] for i in range(len(lengths_dim)): Cxys.append(segment_centroid(le_sweeps[i],lengths_dim[i],dxs[i],dys[i],dzs[i], tapers[i], As[i], dihedrals[i], chords_dim[i], chords_dim[i+1])) aerodynamic_center = (np.dot(np.transpose(Cxys),As)/(ref_area/(1+sym))) single_side_aerodynamic_center = (np.array(aerodynamic_center)*1.) single_side_aerodynamic_center[0] = single_side_aerodynamic_center[0] - MAC*.25 if sym== True: aerodynamic_center[1] = 0 aerodynamic_center[0] = single_side_aerodynamic_center[0] # Total length for supersonics total_length = np.tan(le_sweep_total)*semispan + chords[-1]*RC # Pack stuff if overwrite_reference: wing.areas.reference = ref_area wing.areas.wetted = wet_area wing.aspect_ratio = AR wing.spans.total = total_len wing.chords.mean_geometric = mgc wing.chords.mean_aerodynamic = MAC wing.chords.tip = ct wing.taper = lamda wing.sweeps.quarter_chord = c_4_sweep wing.sweeps.leading_edge = le_sweep_total wing.thickness_to_chord = t_c wing.aerodynamic_center = aerodynamic_center wing.single_side_aerodynamic_center = single_side_aerodynamic_center wing.total_length = total_length # update remainder segment properties segment_properties(wing) return wing
[docs] def segment_properties(wing,update_wet_areas=False,update_ref_areas=False): """Computes detailed segment properties. These are currently used for parasite drag calculations. Assumptions: Segments are trapezoids Source: http://aerodesign.stanford.edu/aircraftdesign/aircraftdesign.html (Stanford AA241 A/B Course Notes) Inputs: wing. exposed_root_chord_offset [m] symmetric [-] spans.projected [m] thickness_to_chord [-] areas.wetted [m^2] chords.root [m] Segments. percent_span_location [-] root_chord_percent [-] Outputs: wing.areas.wetted [m^2] wing.areas.reference [m^2] wing.segments. taper [-] chords.mean_aerodynamic [m] areas. reference [m^2] exposed [m^2] wetted [m^2] Properties Used: N/A """ # Unpack wing exposed_root_chord_offset = wing.exposed_root_chord_offset symm = wing.symmetric semispan = wing.spans.projected*0.5 * (2 - symm) t_c_w = wing.thickness_to_chord segments = wing.segments segment_names = list(segments.keys()) num_segments = len(segment_names) total_wetted_area = 0. total_reference_area = 0. root_chord = wing.chords.root for i_segs in range(num_segments): if i_segs == num_segments-1: continue else: span_seg = semispan*(segments[segment_names[i_segs+1]].percent_span_location - segments[segment_names[i_segs]].percent_span_location ) segment = segments[segment_names[i_segs]] if i_segs == 0: chord_root = root_chord*segments[segment_names[i_segs]].root_chord_percent chord_tip = root_chord*segments[segment_names[i_segs+1]].root_chord_percent wing_root = chord_root + exposed_root_chord_offset*((chord_tip - chord_root)/span_seg) taper = chord_tip/wing_root mac_seg = wing_root * 2/3 * (( 1 + taper + taper**2 )/( 1 + taper)) Sref_seg = span_seg*(chord_root+chord_tip)*0.5 S_exposed_seg = (span_seg-exposed_root_chord_offset)*(wing_root+chord_tip)*0.5 else: chord_root = root_chord*segments[segment_names[i_segs]].root_chord_percent chord_tip = root_chord*segments[segment_names[i_segs+1]].root_chord_percent taper = chord_tip/chord_root mac_seg = chord_root * 2/3 * (( 1 + taper + taper**2 )/( 1 + taper)) Sref_seg = span_seg*(chord_root+chord_tip)*0.5 S_exposed_seg = Sref_seg if wing.symmetric: Sref_seg = Sref_seg*2 S_exposed_seg = S_exposed_seg*2 # compute wetted area of segment if t_c_w < 0.05: Swet_seg = 2.003* S_exposed_seg else: Swet_seg = (1.977 + 0.52*t_c_w) * S_exposed_seg segment.taper = taper segment.chords = Data() segment.chords.mean_aerodynamic = mac_seg segment.areas = Data() segment.areas.reference = Sref_seg segment.areas.exposed = S_exposed_seg segment.areas.wetted = Swet_seg total_wetted_area = total_wetted_area + Swet_seg total_reference_area = total_reference_area + Sref_seg if wing.areas.reference==0. or update_ref_areas: wing.areas.reference = total_reference_area if wing.areas.wetted==0. or update_wet_areas: wing.areas.wetted = total_wetted_area return wing
# Segment centroid
[docs] def segment_centroid(le_sweep,seg_span,dx,dy,dz,taper,A,dihedral,root_chord,tip_chord): """Computes the centroid of a trapezoidal segment Assumptions: Polygon Source: None Inputs: le_sweep [rad] seg_span [m] dx [m] dy [m] taper [dimensionless] A [m**2] dihedral [radians] root_chord [m] tip_chord [m] Outputs: cx,cy [m,m] Properties Used: N/A """ a = tip_chord b = root_chord c = np.tan(le_sweep)*seg_span cx = (2*a*c + a**2 + c*b + a*b + b**2) / (3*(a+b)) cy = seg_span / 3. * (( 1. + 2. * taper ) / (1. + taper)) cz = cy * np.tan(dihedral) return np.array([cx+dx,cy+dy,cz+dz])