# RCAIDE/Library/Plots/Geometry/generate_3d_nacelle_points.py
#
#
# Created: Jul 2023, M. Clarke
# ----------------------------------------------------------------------------------------------------------------------
# IMPORT
# ----------------------------------------------------------------------------------------------------------------------
import RCAIDE
from RCAIDE.Framework.Core import Data
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 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((tessellation, 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