Source code for RCAIDE.Library.Plots.Geometry.plot_3d_nacelle

# RCAIDE/Library/Plots/Geometry/plot_3d_nacelle.py
# 
# 
# Created:  Jul 2023, M. Clarke

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------  
import RCAIDE
from RCAIDE.Framework.Core import Data 
from RCAIDE.Library.Plots.Geometry.Common.contour_surface_slice import contour_surface_slice
from RCAIDE.Library.Methods.Geometry.Airfoil import import_airfoil_geometry
from RCAIDE.Library.Methods.Geometry.Airfoil import compute_naca_4series  
import numpy as np  

# ----------------------------------------------------------------------------------------------------------------------
#  PLOTS
# ----------------------------------------------------------------------------------------------------------------------   
[docs] def plot_3d_nacelle(plot_data, nacelle, tessellation = 24, number_of_airfoil_points = 21, color_map = 'darkmint'): """ Creates a 3D visualization of a nacelle surface using tessellated panels. Parameters ---------- plot_data : list Collection of plot vertices to be rendered nacelle : Nacelle RCAIDE nacelle data structure containing geometry information tessellation : int, optional Number of points in azimuthal discretization (default: 24) number_of_airfoil_points : int, optional Number of points used to discretize airfoil sections (default: 21) color_map : str, optional Color specification for the nacelle surface (default: 'darkmint') Returns ------- plot_data : list Updated collection of plot vertices including nacelle surface Notes ----- Supports three types of nacelles: 1. Stack nacelle (built from stacked segments) 2. Body of Revolution nacelle (built from an airfoil profile) 3. Basic nacelle (built from simple geometric parameters) See Also -------- RCAIDE.Library.Plots.Geometry.generate_3d_stack_nacelle_points : Points generation for stack nacelle RCAIDE.Library.Plots.Geometry.generate_3d_BOR_nacelle_points : Points generation for body of revolution RCAIDE.Library.Plots.Geometry.generate_3d_basic_nacelle_points : Points generation for basic nacelle """ if type(nacelle) == RCAIDE.Library.Components.Nacelles.Stack_Nacelle: G = generate_3d_stack_nacelle_points(nacelle,tessellation,number_of_airfoil_points) elif type(nacelle) == RCAIDE.Library.Components.Nacelles.Body_of_Revolution_Nacelle: G = generate_3d_BOR_nacelle_points(nacelle,tessellation,number_of_airfoil_points) else: G= generate_3d_basic_nacelle_points(nacelle,tessellation,number_of_airfoil_points) num_nac_segs = len(G.PTS[:,0,0]) tesselation = len(G.PTS[0,:,0]) for i_seg in range(num_nac_segs-1): for i_tes in range(tesselation-1): X = np.array([[G.PTS[i_seg ,i_tes ,0],G.PTS[i_seg+1,i_tes ,0]], [G.PTS[i_seg ,i_tes+1,0],G.PTS[i_seg+1,i_tes+1,0]]]) Y = np.array([[G.PTS[i_seg ,i_tes ,1],G.PTS[i_seg+1,i_tes ,1]], [G.PTS[i_seg ,i_tes+1,1],G.PTS[i_seg+1,i_tes+1,1]]]) Z = np.array([[G.PTS[i_seg ,i_tes ,2],G.PTS[i_seg+1,i_tes ,2]], [G.PTS[i_seg ,i_tes+1,2],G.PTS[i_seg+1,i_tes+1,2]]]) values = np.zeros_like(X) verts = contour_surface_slice(X, Y, Z ,values,color_map) plot_data.append(verts) return plot_data
[docs] def generate_3d_stack_nacelle_points(nac, tessellation = 24, number_of_airfoil_points = 21): """ Generates 3D coordinate points for a stack-type nacelle surface. Parameters ---------- nac : Nacelle RCAIDE nacelle data structure containing geometry information tessellation : int, optional Number of points in azimuthal discretization (default: 24) number_of_airfoil_points : int, optional Number of points used to discretize sections (default: 21) Returns ------- G : Data Data structure containing generated points - PTS : ndarray Array of shape (num_segments, tessellation, 3) containing x,y,z coordinates of surface points Notes ----- Creates nacelle from stacked super-elliptical cross-sections with: - Specified width and height - Controllable curvature - Individual segment orientation **Major Assumptions** * Segments are ordered from front to back * Cross-sections are super-elliptical * Orientation defined by Euler angles """ num_nac_segs = len(nac.segments.keys()) theta = np.linspace(0,2*np.pi,tessellation) nac_pts = np.zeros((num_nac_segs,tessellation,3)) for i_seg, segment in enumerate(nac.segments): a = segment.width/2 b = segment.height/2 n = segment.curvature theta = np.linspace(0,2*np.pi,tessellation) section_points = np.zeros((24, 3, 1)) section_points[:, 1, 0] = (abs((np.cos(theta)))**(2/n))*a * ((np.cos(theta)>0)*1 - (np.cos(theta)<0)*1) section_points[:, 2, 0] = (abs((np.sin(theta)))**(2/n))*b * ((np.sin(theta)>0)*1 - (np.sin(theta)<0)*1) x_rotation = np.zeros((tessellation, 3, 3)) x_rotation[:,0,0] = 1 x_rotation[:,1,1] = np.cos(segment.orientation_euler_angles[0]) x_rotation[:,1,2] = -np.sin(segment.orientation_euler_angles[0]) x_rotation[:,2,1] = np.sin(segment.orientation_euler_angles[0]) x_rotation[:,2,2] = np.cos(segment.orientation_euler_angles[0]) y_rotation = np.zeros((tessellation, 3, 3)) y_rotation[:,0,0] = np.cos(segment.orientation_euler_angles[1]) y_rotation[:,0,2] = np.sin(segment.orientation_euler_angles[1]) y_rotation[:,1,1] = 1 y_rotation[:,2,0] = -np.sin(segment.orientation_euler_angles[1]) y_rotation[:,2,2] = np.cos(segment.orientation_euler_angles[1]) z_rotation = np.zeros((tessellation, 3, 3)) z_rotation[:,0,0] = np.cos(segment.orientation_euler_angles[2]) z_rotation[:,0,1] = -np.sin(segment.orientation_euler_angles[2]) z_rotation[:,1,0] = np.sin(segment.orientation_euler_angles[2]) z_rotation[:,1,1] = np.cos(segment.orientation_euler_angles[2]) z_rotation[:,2,2] = 1 rotated_pts = np.matmul(z_rotation,np.matmul(y_rotation,np.matmul(x_rotation,section_points))) nac_pts[i_seg] = rotated_pts[:,:,0] nac_pts[i_seg,:,0] += segment.percent_x_location*nac.length nac_pts[i_seg,:,1] += segment.percent_y_location*nac.length nac_pts[i_seg,:,2] += segment.percent_z_location*nac.length # rotation about y to orient propeller/rotor to thrust angle rot_trans = nac.nac_vel_to_body() rot_trans = np.repeat( np.repeat(rot_trans[ np.newaxis,:,: ],tessellation,axis=0)[ np.newaxis,:,:,: ],num_nac_segs,axis=0) NAC_PTS = np.matmul(rot_trans,nac_pts[...,None]).squeeze() # translate to body NAC_PTS[:,:,0] = NAC_PTS[:,:,0] + nac.origin[0][0] NAC_PTS[:,:,1] = NAC_PTS[:,:,1] + nac.origin[0][1] NAC_PTS[:,:,2] = NAC_PTS[:,:,2] + nac.origin[0][2] G = Data() G.PTS = NAC_PTS return G
[docs] def generate_3d_BOR_nacelle_points(nac, tessellation = 24, number_of_airfoil_points = 21): """ Generates 3D coordinate points for a body-of-revolution nacelle surface. Parameters ---------- nac : Nacelle RCAIDE nacelle data structure containing geometry information tessellation : int, optional Number of points in azimuthal discretization (default: 24) number_of_airfoil_points : int, optional Number of points used to discretize airfoil (default: 21) Returns ------- G : Data Data structure containing generated points - PTS : ndarray Array of shape (num_sections, tessellation, 3) containing x,y,z coordinates of surface points Notes ----- Creates nacelle by revolving an airfoil profile about its chord line. Supports both NACA 4-series and custom airfoil coordinates. **Major Assumptions** * Airfoil profile lies in x-z plane * Profile is rotated about x-axis * Flow-through option raises profile by inlet diameter """ theta = np.linspace(0,2*np.pi,tessellation) num_nac_segs = int(np.ceil(number_of_airfoil_points/2)) nac_pts = np.zeros((num_nac_segs,tessellation,3)) naf = nac.airfoil if type(naf) == RCAIDE.Library.Components.Airfoils.NACA_4_Series_Airfoil: a_geo = compute_naca_4series(naf.NACA_4_Series_code,num_nac_segs) xpts = np.repeat(np.atleast_2d(a_geo.x_coordinates).T,tessellation,axis = 1)*nac.length zpts = np.repeat(np.atleast_2d(a_geo.y_coordinates).T,tessellation,axis = 1)*nac.length elif naf.coordinate_file != None: a_geo = import_airfoil_geometry(naf.coordinate_file,num_nac_segs) xpts = np.repeat(np.atleast_2d(np.take(a_geo.x_coordinates,axis=0)).T,tessellation,axis = 1)*nac.length zpts = np.repeat(np.atleast_2d(np.take(a_geo.y_coordinates,axis=0)).T,tessellation,axis = 1)*nac.length if nac.flow_through: zpts = zpts + nac.diameter/2 # create geometry theta_2d = np.repeat(np.atleast_2d(theta),num_nac_segs,axis =0) nac_pts[:,:,0] = xpts nac_pts[:,:,1] = zpts*np.cos(theta_2d) nac_pts[:,:,2] = zpts*np.sin(theta_2d) # rotation about y to orient propeller/rotor to thrust angle rot_trans = nac.nac_vel_to_body() rot_trans = np.repeat( np.repeat(rot_trans[ np.newaxis,:,: ],tessellation,axis=0)[ np.newaxis,:,:,: ],num_nac_segs,axis=0) NAC_PTS = np.matmul(rot_trans,nac_pts[...,None]).squeeze() # translate to body NAC_PTS[:,:,0] = NAC_PTS[:,:,0] + nac.origin[0][0] NAC_PTS[:,:,1] = NAC_PTS[:,:,1] + nac.origin[0][1] NAC_PTS[:,:,2] = NAC_PTS[:,:,2] + nac.origin[0][2] G = Data() G.PTS = NAC_PTS return G
[docs] def generate_3d_basic_nacelle_points(nac, tessellation, number_of_airfoil_points): """ Generates 3D coordinate points for a basic nacelle surface. Parameters ---------- nac : Nacelle RCAIDE nacelle data structure containing geometry information tessellation : int Number of points in azimuthal discretization number_of_airfoil_points : int Number of points used to discretize sections Returns ------- G : Data Data structure containing generated points - PTS : ndarray Array of shape (num_sections, tessellation, 3) containing x,y,z coordinates of surface points Notes ----- Creates a simple nacelle shape using basic geometric parameters: - Length - Diameter - Inlet diameter (for flow-through nacelles) **Major Assumptions** * Cross-sections are circular * Shape follows super-elliptical profile in x-direction * 10 sections used for discretization """ num_nac_segs = 10 nac_pts = np.zeros((num_nac_segs,tessellation,3)) theta = np.linspace(0,2*np.pi,tessellation) # if no airfoil defined, use super ellipse as default a = nac.length/2 b = nac.diameter/2 xpts = np.repeat(np.atleast_2d(np.linspace(-a,a,num_nac_segs)).T,tessellation,axis = 1) zpts = np.sqrt((b**2)*(1 - (xpts**2)/(a**2) )) xpts = (xpts+a) if nac.flow_through: zpts = zpts + nac.inlet_diameter/2 # create geometry theta_2d = np.repeat(np.atleast_2d(theta),num_nac_segs,axis =0) nac_pts[:,:,0] = xpts nac_pts[:,:,1] = zpts*np.cos(theta_2d) nac_pts[:,:,2] = zpts*np.sin(theta_2d) # rotation about y to orient propeller/rotor to thrust angle rot_trans = nac.nac_vel_to_body() rot_trans = np.repeat( np.repeat(rot_trans[ np.newaxis,:,: ],tessellation,axis=0)[ np.newaxis,:,:,: ],num_nac_segs,axis=0) NAC_PTS = np.matmul(rot_trans,nac_pts[...,None]).squeeze() # translate to body NAC_PTS[:,:,0] = NAC_PTS[:,:,0] + nac.origin[0][0] NAC_PTS[:,:,1] = NAC_PTS[:,:,1] + nac.origin[0][1] NAC_PTS[:,:,2] = NAC_PTS[:,:,2] + nac.origin[0][2] G = Data() G.PTS = NAC_PTS return G