Source code for AeroComBAT.AircraftParts

# AircraftParts.py
# Author: Ben Names
"""
This module contains a library of classes devoted to modeling aircraft parts.

The main purpose of this library is to model various types of aircraft parts.
Currently only wing objects are suported, however in the future it is possible
that fuselages as well as other parts will be added.

:SUMARRY OF THE CLASSES:

- `Wing`: Creates a wing aircraft. This wing is capable of modeling the
    structures of the aircraft wing as well as the aerodynamics. The structures
    are modeled with a combination of beam models currently, however it is
    likely that Ritz method laminates will also incorporated for buckling
    prediction purposes. The aerodynamics are currently modeled with potential
    flow doublet panels.

"""
__docformat__ = 'restructuredtext'
# =============================================================================
# AeroComBAT MODULES
# =============================================================================
from Structures import WingSection, Laminate
from Aerodynamics import CAERO1
# =============================================================================
# IMPORT ANACONDA ASSOCIATED MODULES
# =============================================================================
import numpy as np
import mayavi.mlab as mlab
# =============================================================================
# DEFINE AeroComBAT AIRCRAFT PART CLASSES
# =============================================================================

[docs]class Wing:
[docs] def __init__(self,PID,p1,p2,croot,ctip,x0_spar,xf_spar,Y_rib,n_ply,m_ply,mat_lib,**kwargs): """Creates a wing object. This object represents a wing and contains both structural and aerodynamic models. :Args: - `PID (int)`: The integer ID linked to this part. - `p1 (1x3 np.array[float])`: The initial x,y,z coordinates of the wing. - `p2 (1x3 np.array[float]`: The final x,y,z coordinates of the wing. - `croot (float)`: The root chord length. - `ctip (float)`: The tip chord length. - `x0_spar (float)`: The non-dimensional starting location of the cross section. - `xf_spar (float)`: The non-dimensional ending location of the cross section. - `Y_rib (1xN Array[float])`: The non-dimensional rib locations within the wing. This dimension is primarily used to create wing-sections which primarily define the buckling span's for laminate objects. - `n_ply (1xM Array[int])`: An array of integers specifying the number plies to be used in the model. Each integer refers to the number of plies to be used for at a given orientation. - `m_ply (1xM Array[int])`: An array of integers specifying the material ID to be used for the corresponding number of plies in n_ply at a given orientation. - `th_ply (1xM Array[int])`: An array of floats specifying the degree orientations of the plies used by the lamiantes in the model. - `mat_lib (obj)`: A material library containing all of the material objets to be used in the model. - `name (str)`: The name of the airfoil section to be used for cross section generation. - `wing_SNID (int)`: The first node ID associated with the wing. - `wing_SEID (int)`: The first beam element ID associated with the wing. - `wing_SSBID (int)`: The first superbeam ID associated with the wing. - `SXID (int)`: The starting cross-section ID used by the wing. - `noe (float)`: The number of beam elements to be used in the wing per unit length. - `n_orients (int)`: The number of fiber orientations to be used in each laminate. - `n_lams (int)`: The number of laminates required to mesh the desired cross-section. - `meshSize (float)`: The maximum aspect ratio a 2D element may have in the cross-section. - `ref_ax (str)`: The reference axis to be loaded in the wing. - `chordVec (1x3 np.array[float])`: This numpy array is used to orient the cross-section in 3D space. It corresponds to the local unit x- vector in the cross-section, expressed in the global frame. - `typeXSect (str)`: The type of cross-section to be used by the wing structure. Currently the suported typed are 'boxbeam', 'laminate', and 'rectBoxBeam'. See the meshing class in the structures module for more details. :Returns: - None """ #The type of the object self.type='wing' # Initialize the array holding wing sections self.wingSects = [] # Initialize the wing ID self.PID = PID # Initialize Lifting surface Array self.liftingSurfaces = {} # Name of the airfoil section (used to generate the OML shape of the x-section) name = kwargs.pop('name','NACA0012') # The initial starting node ID for the structural generation of the wing tmp_SB_SNID = kwargs.pop('wing_SNID',0) # The initial beam element EID for the first superbeam ID tmp_SB_SEID = kwargs.pop('wing_SEID',0) # The initial starting superbeam ID tmp_SB_SBID = kwargs.pop('wing_SSBID',0) # The starting cross-section ID SXID = kwargs.pop('SXID',0) # The number of beam elements to be used per unit length noe = kwargs.pop('noe',10) # The number of fiber orientations to be used in each laminate n_orients = kwargs.pop('n_orients',4) # The number of laminates required to mesh the desired cross-section n_lams = kwargs.pop('n_lams',4) # Laminate symmetry lam_sym = kwargs.pop('lam_sym',False) # The maximum aspect ratio a 2D element may have in the cross-section meshSize = kwargs.pop('meshSize',4) # The reference axis to be loaded in the wing ref_ax = kwargs.pop('ref_ax','shearCntr') # Chord vector for wing chordVec = kwargs.pop('chordVec',np.array([1.,0.,0.])) # Orientations of each ply in the lamiante th_ply = kwargs.pop('th_ply',[0]*len(n_ply)) # Type of cross-section typeXSect = kwargs.pop('typeXSect','box') # Calculate the wing span: b_s = np.linalg.norm(p2-p1) # Lambda function to calculate average panel chord length on on the fly. chord = lambda y: (ctip-croot)*y/b_s+croot # Create wing sections between each rib: for i in range(0,len(Y_rib)-1): # Create a wing panel object based on the average chord length # Determine the laminate schedule beam section section_lams = [] for j in range(0,n_lams): # Select vectors of thicknesses and MID's: n_i_tmp = n_ply[i*n_lams+n_orients*j:i*n_lams+n_orients*j+n_orients] m_i_tmp = m_ply[i*n_lams+n_orients*j:i*n_lams+n_orients*j+n_orients] th_i_tmp = th_ply[i*n_lams+n_orients*j:i*n_lams+n_orients*j+n_orients] section_lams += [Laminate(n_i_tmp,m_i_tmp,mat_lib,sym=lam_sym,th=th_i_tmp)] # Compile all information needed to create xsection and beams # Starting coordiante of super beam tmp_x1 = p1+Y_rib[i]*(p2-p1) # Ending coordiante of super beam tmp_x2 = p1+Y_rib[i+1]*(p2-p1) tmpWingSect = WingSection(tmp_x1,tmp_x2,chord,name,x0_spar,xf_spar,\ section_lams,mat_lib,noe,SSBID=tmp_SB_SBID,SNID=tmp_SB_SNID,\ SEID=tmp_SB_SEID,meshSize=meshSize,SXID=SXID,ref_ax=ref_ax,\ chordVec=chordVec,typeXSect=typeXSect) # Prepare ID values for next iteration tmp_SB_SNID = tmpWingSect.SuperBeams[-1].enid tmp_SB_SEID = max(tmpWingSect.SuperBeams[-1].elems.keys())+1 tmp_SB_SBID = tmpWingSect.SuperBeams[-1].SBID+1 self.wingSects += [tmpWingSect] SXID = max(self.wingSects[i].XIDs)+1 #self.model.addElements(tmpWingSect.SuperBeams)
[docs] def addLiftingSurface(self,SID,x1,x2,x3,x4,nspan,nchord): """Adds a potential flow lifting surface to the model. This method adds a potential flow panel aerodynamic model to the wing part. The x1,x2,x3, and x4 points correspond to the root leading edge, root trailing edge, tip trailing edge, and tip leading edge of the wing respectively. Currently the only suported types of panels are doublet- lattice panels to be used for unsteady aerodynamic models. :Args: - `SID (int)`: The lifting surface integer identifier corresponding to the lifting surface. - `x1 (1x3 numpy array)`: The point in 3D space corresponding to the root leading edge point of the lifting surface. - `x2 (1x3 numpy array)`: The point in 3D space corresponding to the root trailing edge point of the lifting surface. - `x3 (1x3 numpy array)`: The point in 3D space corresponding to the tip trailing edge point of the lifting surface. - `x4 (1x3 numpy array)`: The point in 3D space corresponding to the tip leading edge point of the lifting surface. - `nspan (int)`: The number of boxes to be used in the spanwise direction. - `nchord (int)`: The number of boxes to be used in the chordwise direction. :Returns: - None .. Note:: Mutliple surfaces could be added to the wing part. .. Warning:: In order to use the doublet lattice method, the chord lines of the lifting surface must run in the x-direction, and there can be no geometric angles of attack present. The geometry of a general wing can be seen in the figure below: .. image:: images/DoubletLatticeWing.png :align: center """ # Create the lifting surface tmpLiftSurf = CAERO1(SID,x1,x2,x3,x4,nspan,nchord) # Create a temporary dictionary of CQUADA's to iterate through later Dict = tmpLiftSurf.CQUADAs.copy() # Store it in the wing object self.liftingSurfaces[SID]=tmpLiftSurf # CONNECT AERO BOXES TO ELEMENTS # For all elements in the wing for wingSect in self.wingSects: for superBeam in wingSect.SuperBeams: for EID, elem in superBeam.elems.iteritems(): # For all panels in the lifting surface tmpDict = Dict.copy() for PANID, panel in tmpDict.iteritems(): # Determine the y-coord of the recieving point y34pan = panel.y(-0.5,0) # If the panel y-coord is between the nodes of the elem if (y34pan-elem.n1.x[1])*(y34pan-elem.n2.x[1])<0: # The panel's displacement can be described by the # displacements of the nodes used by the element. # Determine the x-coord in the elem corresponding # to the location of the panel recieving point t = (y34pan-elem.n1.x[1])/(elem.n2.x[1]-elem.n1.x[1]) # Determine the nodal contributions of the displacements panel.DOF[elem.n1.NID] = t panel.DOF[elem.n2.NID] = 1-t # Determine the moment arm of the box acting on the # beam x34elem = elem.n1.x[0]+t*(elem.n2.x[0]-elem.n1.x[0]) x34pan = panel.x(-0.5,0) panel.xarm = x34pan-x34elem # Remove this reference from the dictionary so it # is not iterated over again for the next element del Dict[PANID] for NID, node in superBeam.nodes.iteritems(): # For all panels in the lifting surface tmpDict = Dict.copy() for PANID, panel in tmpDict.iteritems(): # Determine the y-coord of the recieving point y34pan = panel.y(-0.5,0) # If the panel y-coord is between the nodes of the elem if abs(y34pan-node.x[1])<1e-6: # The panel's displacement can be described by the # displacements of the nodes used by the element. # Determine the x-coord in the elem corresponding # to the location of the panel recieving point t = 1 # Determine the nodal contributions of the displacements panel.DOF[node.NID] = t # Determine the moment arm of the box acting on the # beam x34node = node.x[0] x34pan = panel.x(-0.5,0) panel.xarm = x34pan-x34node # Remove this reference from the dictionary so it # is not iterated over again for the next element del Dict[PANID] if len(Dict.keys())>0: print('Warning, some elements could not have their displacements'+ ' matched by beam elements. This includes:') for PANID, panel in Dict.iteritems(): #panel.DOF[-1] = None print('CQUADA %d' %(PANID))
[docs] def plotRigidWing(self,**kwargs): """Plots the rigid wing. This method plots the rigid model of the wing. This includes the reference axis of the beams, the cross-sections of the beams, and the lifting surfaces that make up the wing. This is an excellent check to perform before adding the part to a FEM model. :Args: - `figName (str)`: The name of the MayaVi figure. 'Rigid Wing' by default. - `numXSects (int)`: The number of cross-sections that each wing section will display. By default it is 2. - `color (1x3 tuple(int))`: This is a length 3 tuple to be used as the color of the beam reference axes. Black by default. :Returns: - None """ figName = kwargs.pop('figName','Rigid Wing') # Chose the color of the beam, defaults to black, accepts tuple clr = kwargs.pop('color',(0,0,0)) # Chose the number of cross-sections to be plotted. By default this is 2 # One at the beggining and one at the end of the super beam numXSects = kwargs.pop('numXSects',2) mlab.figure(figure=figName) for sects in self.wingSects: sects.plotRigid(figName=figName,clr=clr,numXSects=numXSects) if len(self.liftingSurfaces)>0: for SID, surface in self.liftingSurfaces.iteritems(): surface.plotLiftingSurface(figName=figName)