Ejemplo n.º 1
0
    def _load_from_file(self, filename):
        vsp.ClearVSPModel()
        vsp.ReadVSPFile(filename)

        for geom_id in vsp.FindGeoms():
            geom_name_raw = vsp.GetGeomName(geom_id)
            geom_name, geom_idx = regex_listname.findall(geom_name_raw)[0]

            if geom_name not in self:
                if geom_idx:
                    self[geom_name] = []
                else:
                    self[geom_name] = VspElement()

            if geom_idx != '':
                geom = self._update_list(self[geom_name], geom_idx)
            else:
                geom = self[geom_name]

            geom._id = geom_id

            for param_id in vsp.GetGeomParmIDs(geom_id):
                group_name_raw = vsp.GetParmDisplayGroupName(param_id)
                group_name, group_idx = regex_listname.findall(
                    group_name_raw)[0]
                if group_name not in EXCLUDE_GROUPS:
                    if group_name not in geom:
                        if group_idx:
                            geom[group_name] = []
                        else:
                            geom[group_name] = VspElement()

                    if group_idx != '':
                        geom[group_name], group = self._update_list(
                            geom[group_name], group_idx)
                    else:
                        group = geom[group_name]

                    param = self._make_parameter(param_id)

                    if param['name'] in group:
                        raise ValueError("{} already in <{}:{}>".format(
                            param.name, geom_name, group_name))

                    group[param['name']] = param
Ejemplo n.º 2
0
def vsp_read(tag, units_type='SI',specified_network=None): 
    """This reads an OpenVSP vehicle geometry and writes it into a SUAVE vehicle format.
    Includes wings, fuselages, and propellers.

    Assumptions:
    1. OpenVSP vehicle is composed of conventionally shaped fuselages, wings, and propellers. 
    1a. OpenVSP fuselage: generally narrow at nose and tail, wider in center). 
    1b. Fuselage is designed in VSP as it appears in real life. That is, the VSP model does not rely on
       superficial elements such as canopies, stacks, or additional fuselages to cover up internal lofting oddities.
    1c. This program will NOT account for multiple geometries comprising the fuselage. For example: a wingbox mounted beneath
       is a separate geometry and will NOT be processed.
    2. Fuselage origin is located at nose. VSP file origin can be located anywhere, preferably at the forward tip
       of the vehicle or in front (to make all X-coordinates of vehicle positive).
    3. Written for OpenVSP 3.21.1

    Source:
    N/A

    Inputs:
    1. A tag for an XML file in format .vsp3.
    2. Units_type set to 'SI' (default) or 'Imperial'
    3. User-specified network

    Outputs:
    Writes SUAVE vehicle with these geometries from VSP:    (All values default to SI. Any other 2nd argument outputs Imperial.)
    	Wings.Wing.    (* is all keys)
    		origin                                  [m] in all three dimensions
    		spans.projected                         [m]
    		chords.root                             [m]
    		chords.tip                              [m]
    		aspect_ratio                            [-]
    		sweeps.quarter_chord                    [radians]
    		twists.root                             [radians]
    		twists.tip                              [radians]
    		thickness_to_chord                      [-]
    		dihedral                                [radians]
    		symmetric                               <boolean>
    		tag                                     <string>
    		areas.reference                         [m^2]
    		areas.wetted                            [m^2]
    		Segments.
    		  tag                                   <string>
    		  twist                                 [radians]
    		  percent_span_location                 [-]  .1 is 10%
    		  root_chord_percent                    [-]  .1 is 10%
    		  dihedral_outboard                     [radians]
    		  sweeps.quarter_chord                  [radians]
    		  thickness_to_chord                    [-]
    		  airfoil                               <NACA 4-series, 6 series, or airfoil file>

    	Fuselages.Fuselage.			
    		origin                                  [m] in all three dimensions
    		width                                   [m]
    		lengths.
    		  total                                 [m]
    		  nose                                  [m]
    		  tail                                  [m]
    		heights.
    		  maximum                               [m]
    		  at_quarter_length                     [m]
    		  at_three_quarters_length              [m]
    		effective_diameter                      [m]
    		fineness.nose                           [-] ratio of nose section length to fuselage effective diameter
    		fineness.tail                           [-] ratio of tail section length to fuselage effective diameter
    		areas.wetted                            [m^2]
    		tag                                     <string>
    		segment[].   (segments are in ordered container and callable by number)
    		  vsp.shape                               [point,circle,round_rect,general_fuse,fuse_file]
    		  vsp.xsec_id                             <10 digit string>
    		  percent_x_location
    		  percent_z_location
    		  height
    		  width
    		  length
    		  effective_diameter
    		  tag
    		vsp.xsec_num                              <integer of fuselage segment quantity>
    		vsp.xsec_surf_id                          <10 digit string>

    	Propellers.Propeller.
    		location[X,Y,Z]                            [radians]
    		rotation[X,Y,Z]                            [radians]
    		tip_radius                                 [m]
    	        hub_radius                                 [m]
    		thrust_angle                               [radians]

    Properties Used:
    N/A
    """  	

    vsp.ClearVSPModel() 
    vsp.ReadVSPFile(tag)	

    vsp_fuselages     = []
    vsp_wings         = []	
    vsp_props         = [] 
    vsp_nacelles      = [] 
    vsp_nacelle_type  = []
    
    vsp_geoms         = vsp.FindGeoms()
    geom_names        = []

    vehicle           = SUAVE.Vehicle()
    vehicle.tag       = tag 

    if units_type == 'SI':
        units_type = 'SI' 
    elif units_type == 'inches':
        units_type = 'inches'	
    else:
        units_type = 'imperial'	

    # The two for-loops below are in anticipation of an OpenVSP API update with a call for GETGEOMTYPE.
    # This print function allows user to enter VSP GeomID manually as first argument in vsp_read functions.

    print("VSP geometry IDs: ")	

    # Label each geom type by storing its VSP geom ID. 

    for geom in vsp_geoms: 
        geom_name = vsp.GetGeomName(geom)
        geom_names.append(geom_name)
        print(str(geom_name) + ': ' + geom)

    # --------------------------------
    # AUTOMATIC VSP ENTRY & PROCESSING
    # --------------------------------		

    for geom in vsp_geoms:
        geom_name = vsp.GetGeomName(geom)
        geom_type = vsp.GetGeomTypeName(str(geom))

        if geom_type == 'Fuselage':
            vsp_fuselages.append(geom)
        if geom_type == 'Wing':
            vsp_wings.append(geom)
        if geom_type == 'Propeller':
            vsp_props.append(geom) 
        if (geom_type == 'Stack') or (geom_type == 'BodyOfRevolution'):
            vsp_nacelle_type.append(geom_type)
            vsp_nacelles.append(geom) 
        
    # --------------------------------------------------			
    # Read Fuselages 
    # --------------------------------------------------			    
    for fuselage_id in vsp_fuselages:
        sym_planar = vsp.GetParmVal(fuselage_id, 'Sym_Planar_Flag', 'Sym') # Check for symmetry
        sym_origin = vsp.GetParmVal(fuselage_id, 'Sym_Ancestor_Origin_Flag', 'Sym') 
        if sym_planar == 2. and sym_origin == 1.:  
            num_fus  = 2 
            sym_flag = [1,-1]
        else: 
            num_fus  = 1 
            sym_flag = [1] 
        for fux_idx in range(num_fus):	# loop through fuselages on aircraft 
            fuselage = read_vsp_fuselage(fuselage_id,fux_idx,sym_flag[fux_idx],units_type)
            vehicle.append_component(fuselage)
        
    # --------------------------------------------------			    
    # Read Wings 
    # --------------------------------------------------			
    for wing_id in vsp_wings:
        wing = read_vsp_wing(wing_id, units_type)
        vehicle.append_component(wing)		 
        
    # --------------------------------------------------			    
    # Read Nacelles 
    # --------------------------------------------------			
    for nac_id, nacelle_id in enumerate(vsp_nacelles):
        nacelle = read_vsp_nacelle(nacelle_id,vsp_nacelle_type[nac_id], units_type)
        vehicle.append_component(nacelle)	  
    
    # --------------------------------------------------			    
    # Read Propellers/Rotors and assign to a network
    # --------------------------------------------------			
    # Initialize rotor network elements
    number_of_lift_rotor_engines = 0
    number_of_propeller_engines  = 0
    lift_rotors = Data()
    propellers  = Data() 
    for prop_id in vsp_props:
        prop = read_vsp_propeller(prop_id,units_type)
        prop.tag = vsp.GetGeomName(prop_id)
        if prop.orientation_euler_angles[1] >= 70 * Units.degrees:
            lift_rotors.append(prop)
            number_of_lift_rotor_engines += 1 
        else:
            propellers.append(prop)
            number_of_propeller_engines += 1  

    if specified_network == None:
        # If no network specified, assign a network
        if number_of_lift_rotor_engines>0 and number_of_propeller_engines>0:
            net = Lift_Cruise()
        else:
            net = Battery_Propeller() 
    else:
        net = specified_network

    # Create the rotor network
    if net.tag == "Lift_Cruise":
        # Lift + Cruise network
        for i in range(number_of_lift_rotor_engines):
            net.lift_rotors.append(lift_rotors[list(lift_rotors.keys())[i]])
        net.number_of_lift_rotor_engines = number_of_lift_rotor_engines	

        for i in range(number_of_propeller_engines):
            net.propellers.append(propellers[list(propellers.keys())[i]])
        net.number_of_propeller_engines = number_of_propeller_engines		

    elif net.tag == "Battery_Propeller":
        # Append all rotors as propellers for the battery propeller network
        for i in range(number_of_lift_rotor_engines):
            # Accounts for multicopter configurations
            net.propellers.append(lift_rotors[list(lift_rotors.keys())[i]])

        for i in range(number_of_propeller_engines):
            net.propellers.append(propellers[list(propellers.keys())[i]])

        net.number_of_propeller_engines = number_of_lift_rotor_engines + number_of_propeller_engines	

    vehicle.networks.append(net)

    return vehicle
Ejemplo n.º 3
0
def vsp_read_wing(wing_id, units_type='SI'):
    """This reads an OpenVSP wing vehicle geometry and writes it into a SUAVE wing format.

	Assumptions:
	1. OpenVSP wing is divided into segments ("XSecs" in VSP).
	2. Written for OpenVSP 3.21.1

	Source:
	N/A

	Inputs:
	0. Pre-loaded VSP vehicle in memory, via vsp_read.
	1. VSP 10-digit geom ID for wing.
	2. units_type set to 'SI' (default) or 'Imperial'.

	Outputs:
	Writes SUAVE wing object, with these geometries, from VSP:
		Wings.Wing.    (* is all keys)
			origin                                  [m] in all three dimensions
			spans.projected                         [m]
			chords.root                             [m]
			chords.tip                              [m]
			aspect_ratio                            [-]
			sweeps.quarter_chord                    [radians]
			twists.root                             [radians]
			twists.tip                              [radians]
			thickness_to_chord                      [-]
			dihedral                                [radians]
			symmetric                               <boolean>
			tag                                     <string>
			areas.exposed                           [m^2]
			areas.reference                         [m^2]
			areas.wetted                            [m^2]
			Segments.
			  tag                                   <string>
			  twist                                 [radians]
			  percent_span_location                 [-]  .1 is 10%
			  root_chord_percent                    [-]  .1 is 10%
			  dihedral_outboard                     [radians]
			  sweeps.quarter_chord                  [radians]
			  thickness_to_chord                    [-]
			  airfoil                               <NACA 4-series, 6 series, or airfoil file>

	Properties Used:
	N/A
	"""

    # Check if this is vertical tail, this seems like a weird first step but it's necessary
    # Get the initial rotation to get the dihedral angles
    x_rot = vsp.GetParmVal(wing_id, 'X_Rotation', 'XForm')
    if x_rot >= 70:
        wing = SUAVE.Components.Wings.Vertical_Tail()
        wing.vertical = True
        x_rot = (90 - x_rot) * Units.deg
    else:
        # Instantiate a wing
        wing = SUAVE.Components.Wings.Wing()

    # Set the units
    if units_type == 'SI':
        units_factor = Units.meter * 1.
    else:
        units_factor = Units.foot * 1.

    # Apply a tag to the wing
    if vsp.GetGeomName(wing_id):
        tag = vsp.GetGeomName(wing_id)
        tag = tag.translate(t_table)
        wing.tag = tag
    else:
        wing.tag = 'winggeom'

    # Top level wing parameters
    # Wing origin
    wing.origin[0][0] = vsp.GetParmVal(wing_id, 'X_Location',
                                       'XForm') * units_factor
    wing.origin[0][1] = vsp.GetParmVal(wing_id, 'Y_Location',
                                       'XForm') * units_factor
    wing.origin[0][2] = vsp.GetParmVal(wing_id, 'Z_Location',
                                       'XForm') * units_factor

    # Wing Symmetry
    sym_planar = vsp.GetParmVal(wing_id, 'Sym_Planar_Flag', 'Sym')
    sym_origin = vsp.GetParmVal(wing_id, 'Sym_Ancestor', 'Sym')

    # Check for symmetry
    if sym_planar == 2. and sym_origin == 1.:  #origin at wing, not vehicle
        wing.symmetric = True
    else:
        wing.symmetric = False

    #More top level parameters
    total_proj_span = vsp.GetParmVal(wing_id, 'TotalProjectedSpan',
                                     'WingGeom') * units_factor
    wing.aspect_ratio = vsp.GetParmVal(wing_id, 'TotalAR', 'WingGeom')
    wing.areas.reference = vsp.GetParmVal(wing_id, 'TotalArea',
                                          'WingGeom') * units_factor**2
    wing.spans.projected = total_proj_span

    # Check if this is a single segment wing
    xsec_surf_id = vsp.GetXSecSurf(wing_id,
                                   0)  # This is how VSP stores surfaces.
    x_sec_1 = vsp.GetXSec(xsec_surf_id, 1)
    x_sec_1_span_parm = vsp.GetXSecParm(x_sec_1, 'Span')
    x_sec_1_span = vsp.GetParmVal(x_sec_1_span_parm) * (
        1 + wing.symmetric) * units_factor

    if x_sec_1_span == wing.spans.projected:
        single_seg = True
    else:
        single_seg = False

    segment_num = vsp.GetNumXSec(
        xsec_surf_id
    )  # Get number of wing segments (is one more than the VSP GUI shows).
    x_sec = vsp.GetXSec(xsec_surf_id, 0)
    chord_parm = vsp.GetXSecParm(x_sec, 'Root_Chord')

    total_chord = vsp.GetParmVal(chord_parm)

    span_sum = 0.  # Non-projected.
    proj_span_sum = 0.  # Projected.
    segment_spans = [None] * (segment_num)  # Non-projected.
    segment_dihedral = [None] * (segment_num)
    segment_sweeps_quarter_chord = [None] * (segment_num)

    # Check for wing segment *inside* fuselage, then skip XSec_0 to start at first exposed segment.
    if total_chord == 1.:
        start = 1
        xsec_surf_id = vsp.GetXSecSurf(wing_id, 1)
        x_sec = vsp.GetXSec(xsec_surf_id, 0)
        chord_parm = vsp.GetXSecParm(x_sec, 'Tip_Chord')
        root_chord = vsp.GetParmVal(chord_parm) * units_factor
    else:
        start = 0
        root_chord = total_chord * units_factor

    # -------------
    # Wing segments
    # -------------

    if single_seg == False:

        # Convert VSP XSecs to SUAVE segments. (Wing segments are defined by outboard sections in VSP, but inboard sections in SUAVE.)
        for i in range(start, segment_num + 1):
            segment = SUAVE.Components.Wings.Segment()
            segment.tag = 'Section_' + str(i)
            thick_cord = vsp.GetParmVal(wing_id, 'ThickChord',
                                        'XSecCurve_' + str(i - 1))
            segment.thickness_to_chord = thick_cord  # Thick_cord stored for use in airfoil, below.
            segment_root_chord = vsp.GetParmVal(
                wing_id, 'Root_Chord', 'XSec_' + str(i)) * units_factor
            segment.root_chord_percent = segment_root_chord / root_chord
            segment.percent_span_location = proj_span_sum / (total_proj_span /
                                                             2)
            segment.twist = vsp.GetParmVal(wing_id, 'Twist',
                                           'XSec_' + str(i - 1)) * Units.deg

            if i == start:
                wing.thickness_to_chord = thick_cord

            if i < segment_num:  # This excludes the tip xsec, but we need a segment in SUAVE to store airfoil.
                sweep = vsp.GetParmVal(wing_id, 'Sweep',
                                       'XSec_' + str(i)) * Units.deg
                sweep_loc = vsp.GetParmVal(wing_id, 'Sweep_Location',
                                           'XSec_' + str(i))
                AR = vsp.GetParmVal(wing_id, 'Aspect', 'XSec_' + str(i))
                taper = vsp.GetParmVal(wing_id, 'Taper', 'XSec_' + str(i))

                segment_sweeps_quarter_chord[i] = convert_sweep(
                    sweep, sweep_loc, 0.25, AR, taper)
                segment.sweeps.quarter_chord = segment_sweeps_quarter_chord[
                    i]  # Used again, below

                # Used for dihedral computation, below.
                segment_dihedral[i] = vsp.GetParmVal(
                    wing_id, 'Dihedral', 'XSec_' + str(i)) * Units.deg + x_rot
                segment.dihedral_outboard = segment_dihedral[i]

                segment_spans[i] = vsp.GetParmVal(
                    wing_id, 'Span', 'XSec_' + str(i)) * units_factor
                proj_span_sum += segment_spans[i] * np.cos(segment_dihedral[i])
                span_sum += segment_spans[i]
            else:
                segment.root_chord_percent = (vsp.GetParmVal(
                    wing_id, 'Tip_Chord',
                    'XSec_' + str(i - 1))) * units_factor / total_chord

            # XSec airfoil
            jj = i - 1  # Airfoil index i-1 because VSP airfoils and sections are one index off relative to SUAVE.
            xsec_id = str(vsp.GetXSec(xsec_surf_id, jj))
            airfoil = Airfoil()
            if vsp.GetXSecShape(
                    xsec_id
            ) == vsp.XS_FOUR_SERIES:  # XSec shape: NACA 4-series
                camber = vsp.GetParmVal(wing_id, 'Camber',
                                        'XSecCurve_' + str(jj))

                if camber == 0.:
                    camber_loc = 0.
                else:
                    camber_loc = vsp.GetParmVal(wing_id, 'CamberLoc',
                                                'XSecCurve_' + str(jj))

                airfoil.thickness_to_chord = thick_cord
                camber_round = int(np.around(camber * 100))
                camber_loc_round = int(np.around(camber_loc * 10))
                thick_cord_round = int(np.around(thick_cord * 100))
                airfoil.tag = 'NACA ' + str(camber_round) + str(
                    camber_loc_round) + str(thick_cord_round)

            elif vsp.GetXSecShape(
                    xsec_id) == vsp.XS_SIX_SERIES:  # XSec shape: NACA 6-series
                thick_cord_round = int(np.around(thick_cord * 100))
                a_value = vsp.GetParmVal(wing_id, 'A', 'XSecCurve_' + str(jj))
                ideal_CL = int(
                    np.around(
                        vsp.GetParmVal(wing_id, 'IdealCl',
                                       'XSecCurve_' + str(jj)) * 10))
                series_vsp = int(
                    vsp.GetParmVal(wing_id, 'Series', 'XSecCurve_' + str(jj)))
                series_dict = {
                    0: '63',
                    1: '64',
                    2: '65',
                    3: '66',
                    4: '67',
                    5: '63A',
                    6: '64A',
                    7: '65A'
                }  # VSP series values.
                series = series_dict[series_vsp]
                airfoil.tag = 'NACA ' + series + str(ideal_CL) + str(
                    thick_cord_round) + ' a=' + str(np.around(a_value, 1))

            elif vsp.GetXSecShape(
                    xsec_id
            ) == vsp.XS_FILE_AIRFOIL:  # XSec shape: 12 is type AF_FILE
                airfoil.thickness_to_chord = thick_cord
                airfoil.points = vsp.GetAirfoilCoordinates(
                    wing_id, float(jj / segment_num))
                # VSP airfoil API calls get coordinates and write files with the final argument being the fraction of segment position, regardless of relative spans.
                # (Write the root airfoil with final arg = 0. Write 4th airfoil of 5 segments with final arg = .8)
                vsp.WriteSeligAirfoil(
                    str(wing.tag) + '_airfoil_XSec_' + str(jj) + '.dat',
                    wing_id, float(jj / segment_num))
                airfoil.coordinate_file = 'str(wing.tag)' + '_airfoil_XSec_' + str(
                    jj) + '.dat'
                airfoil.tag = 'AF_file'

                segment.append_airfoil(airfoil)

            wing.Segments.append(segment)

        # Wing dihedral
        proj_span_sum_alt = 0.
        span_sum_alt = 0.
        sweeps_sum = 0.

        for ii in range(start, segment_num):
            span_sum_alt += segment_spans[ii]
            proj_span_sum_alt += segment_spans[ii] * np.cos(
                segment_dihedral[ii]
            )  # Use projected span to find total wing dihedral.
            sweeps_sum += segment_spans[ii] * np.tan(
                segment_sweeps_quarter_chord[ii])

        wing.dihedral = np.arccos(proj_span_sum_alt / span_sum_alt)
        wing.sweeps.quarter_chord = -np.arctan(
            sweeps_sum / span_sum_alt)  # Minus sign makes it positive sweep.

        # Add a tip segment, all values are zero except the tip chord
        tc = vsp.GetParmVal(wing_id, 'Tip_Chord',
                            'XSec_' + str(segment_num - 1)) * units_factor
        segment = SUAVE.Components.Wings.Segment()
        segment.percent_span_location = 1.0
        segment.root_chord_percent = tc / root_chord

        # Chords
        wing.chords.root = vsp.GetParmVal(wing_id, 'Tip_Chord',
                                          'XSec_0') * units_factor
        wing.chords.tip = tc
        wing.chords.mean_geometric = wing.areas.reference / wing.spans.projected

        # Just double calculate and fix things:
        wing = wing_segmented_planform(wing)

    else:
        # Single segment

        # Get ID's
        x_sec_1_dih_parm = vsp.GetXSecParm(x_sec_1, 'Dihedral')
        x_sec_1_sweep_parm = vsp.GetXSecParm(x_sec_1, 'Sweep')
        x_sec_1_sweep_loc_parm = vsp.GetXSecParm(x_sec_1, 'Sweep_Location')
        x_sec_1_taper_parm = vsp.GetXSecParm(x_sec_1, 'Taper')
        x_sec_1_rc_parm = vsp.GetXSecParm(x_sec_1, 'Root_Chord')
        x_sec_1_tc_parm = vsp.GetXSecParm(x_sec_1, 'Tip_Chord')

        # Calcs
        sweep = vsp.GetParmVal(x_sec_1_sweep_parm) * Units.deg
        sweep_loc = vsp.GetParmVal(x_sec_1_sweep_loc_parm)
        taper = vsp.GetParmVal(x_sec_1_taper_parm)
        c_4_sweep = convert_sweep(sweep, sweep_loc, 0.25, wing.aspect_ratio,
                                  taper)

        # Pull and pack
        wing.sweeps.quarter_chord = c_4_sweep
        wing.taper = taper
        wing.dihedral = vsp.GetParmVal(x_sec_1_dih_parm) * Units.deg + x_rot
        wing.chords.root = vsp.GetParmVal(x_sec_1_rc_parm) * units_factor
        wing.chords.tip = vsp.GetParmVal(x_sec_1_tc_parm) * units_factor
        wing.chords.mean_geometric = wing.areas.reference / wing.spans.projected

        # Just double calculate and fix things:
        wing = wing_planform(wing)

    # Twists
    wing.twists.root = vsp.GetParmVal(wing_id, 'Twist', 'XSec_0') * Units.deg
    wing.twists.tip = vsp.GetParmVal(
        wing_id, 'Twist', 'XSec_' + str(segment_num - 1)) * Units.deg

    return wing
Ejemplo n.º 4
0
def vsp_read_fuselage(fuselage_id, units_type='SI', fineness=True):
    """This reads an OpenVSP fuselage geometry and writes it to a SUAVE fuselage format.

	Assumptions:
	1. OpenVSP fuselage is "conventionally shaped" (generally narrow at nose and tail, wider in center). 
	2. Fuselage is designed in VSP as it appears in real life. That is, the VSP model does not rely on
	   superficial elements such as canopies, stacks, or additional fuselages to cover up internal lofting oddities.
	3. This program will NOT account for multiple geometries comprising the fuselage. For example: a wingbox mounted beneath
	   is a separate geometry and will NOT be processed.
	4. Fuselage origin is located at nose. VSP file origin can be located anywhere, preferably at the forward tip
	   of the vehicle or in front (to make all X-coordinates of vehicle positive).
	5. Written for OpenVSP 3.16.1
	
	Source:
	N/A

	Inputs:
	0. Pre-loaded VSP vehicle in memory, via vsp_read.
	1. VSP 10-digit geom ID for fuselage.
	2. Units_type set to 'SI' (default) or 'Imperial'.
	3. Boolean for whether or not to compute fuselage finenesses (default = True).
	4. Uses exterior function get_vsp_areas, in SUAVE/trunk/SUAVE/Input_Output/OpenVSP.
	
	Outputs:
	Writes SUAVE fuselage, with these geometries:           (all defaults are SI, but user may specify Imperial)

		Fuselages.Fuselage.			
			origin                                  [m] in all three dimensions
			width                                   [m]
			lengths.
			  total                                 [m]
			  nose                                  [m]
			  tail                                  [m]
			heights.
			  maximum                               [m]
			  at_quarter_length                     [m]
			  at_three_quarters_length              [m]
			effective_diameter                      [m]
			fineness.nose                           [-] ratio of nose section length to fuselage effective diameter
			fineness.tail                           [-] ratio of tail section length to fuselage effective diameter
			areas.wetted                            [m^2]
			tag                                     <string>
			segment[].   (segments are in ordered container and callable by number)
			  vsp.shape                               [point,circle,round_rect,general_fuse,fuse_file]
			  vsp.xsec_id                             <10 digit string>
			  percent_x_location
			  percent_z_location
			  height
			  width
			  length
			  effective_diameter
			  tag
			vsp.xsec_num                              <integer of fuselage segment quantity>
			vsp.xsec_surf_id                          <10 digit string>

	Properties Used:
	N/A
	"""
    fuselage = SUAVE.Components.Fuselages.Fuselage()

    if units_type == 'SI':
        units_factor = Units.meter * 1.
    else:
        units_factor = Units.foot * 1.

    if vsp.GetGeomName(fuselage_id):
        fuselage.tag = vsp.GetGeomName(fuselage_id)
    else:
        fuselage.tag = 'FuselageGeom'

    fuselage.origin[0][0] = vsp.GetParmVal(fuselage_id, 'X_Location',
                                           'XForm') * units_factor
    fuselage.origin[0][1] = vsp.GetParmVal(fuselage_id, 'Y_Location',
                                           'XForm') * units_factor
    fuselage.origin[0][2] = vsp.GetParmVal(fuselage_id, 'Z_Location',
                                           'XForm') * units_factor

    fuselage.lengths.total = vsp.GetParmVal(fuselage_id, 'Length',
                                            'Design') * units_factor
    fuselage.vsp_data.xsec_surf_id = vsp.GetXSecSurf(
        fuselage_id, 0)  # There is only one XSecSurf in geom.
    fuselage.vsp_data.xsec_num = vsp.GetNumXSec(
        fuselage.vsp_data.xsec_surf_id)  # Number of xsecs in fuselage.

    x_locs = []
    heights = []
    widths = []
    eff_diams = []
    lengths = []

    # -----------------
    # Fuselage segments
    # -----------------

    for ii in range(0, fuselage.vsp_data.xsec_num):
        segment = SUAVE.Components.Fuselages.Segment()
        segment.vsp_data.xsec_id = vsp.GetXSec(fuselage.vsp_data.xsec_surf_id,
                                               ii)  # VSP XSec ID.
        segment.tag = 'segment_' + str(ii)
        segment.percent_x_location = vsp.GetParmVal(
            fuselage_id, 'XLocPercent',
            'XSec_' + str(ii))  # Along fuselage length.
        segment.percent_z_location = vsp.GetParmVal(
            fuselage_id, 'ZLocPercent',
            'XSec_' + str(ii))  # Vertical deviation of fuselage center.
        segment.height = vsp.GetXSecHeight(
            segment.vsp_data.xsec_id) * units_factor
        segment.width = vsp.GetXSecWidth(
            segment.vsp_data.xsec_id) * units_factor
        segment.effective_diameter = (segment.height + segment.width) / 2.

        x_locs.append(segment.percent_x_location
                      )  # Save into arrays for later computation.
        heights.append(segment.height)
        widths.append(segment.width)
        eff_diams.append(segment.effective_diameter)

        if ii != (
                fuselage.vsp_data.xsec_num - 1
        ):  # Segment length: stored as length since previous segment. (First segment will have length 0.0.)
            segment.length = fuselage.lengths.total * (
                fuselage.Segments[ii + 1].percent_x_location -
                segment.percent_x_location) * units_factor
        else:
            segment.length = 0.0
        lengths.append(segment.length)

        shape = vsp.GetXSecShape(segment.vsp_data.xsec_id)
        shape_dict = {
            0: 'point',
            1: 'circle',
            2: 'ellipse',
            3: 'super ellipse',
            4: 'rounded rectangle',
            5: 'general fuse',
            6: 'fuse file'
        }
        segment.vsp_data.shape = shape_dict[shape]

        fuselage.Segments.append(segment)

    fuselage.heights.at_quarter_length = get_fuselage_height(
        fuselage, .25)  # Calls get_fuselage_height function (below).
    fuselage.heights.at_three_quarters_length = get_fuselage_height(
        fuselage, .75)
    fuselage.heights.at_wing_root_quarter_chord = get_fuselage_height(
        fuselage, .4)

    fuselage.heights.maximum = max(heights)  # Max segment height.
    fuselage.width = max(widths)  # Max segment width.
    fuselage.effective_diameter = max(eff_diams)  # Max segment effective diam.

    fuselage.areas.front_projected = np.pi * (
        (fuselage.effective_diameter) / 2)**2

    eff_diam_gradients_fwd = np.array(eff_diams[1:]) - np.array(
        eff_diams[:-1])  # Compute gradients of segment effective diameters.
    eff_diam_gradients_fwd = np.multiply(eff_diam_gradients_fwd, lengths[:-1])

    fuselage = compute_fuselage_fineness(fuselage, x_locs, eff_diams,
                                         eff_diam_gradients_fwd)

    return fuselage
Ejemplo n.º 5
0
def set_sources(geometry):
    """This sets meshing sources in a way similar to the OpenVSP default. Some source values can
    also be optionally specified as below.
    
    Assumptions:
    None

    Source:
    https://github.com/OpenVSP/OpenVSP (with some modifications)

    Inputs:
    geometry.
      wings.*.                              (passed to add_segment_sources())
        tag                                 <string>
        Segments.*.percent_span_location    [-] (.1 is 10%)
        Segments.*.root_chord_percent       [-] (.1 is 10%)
        chords.root                         [m]
        chords.tip                          [m]
        vsp_mesh                            (optional) - This holds settings that are used in add_segment_sources
      fuselages.*.
        tag                                 <string>
        vsp_mesh.                           (optional)
          length                            [m]
          radius                            [m]
        lengths.total                       (only used if vsp_mesh is not defined for the fuselage)

    Outputs:
    <tag>.stl                               

    Properties Used:
    N/A
    """
    # Extract information on geometry type (for some reason it seems VSP doesn't have a simple
    # way to do this)
    comp_type_dict = dict()
    comp_dict = dict()
    for wing in geometry.wings:
        comp_type_dict[wing.tag] = 'wing'
        comp_dict[wing.tag] = wing
    for fuselage in geometry.fuselages:
        comp_type_dict[fuselage.tag] = 'fuselage'
        comp_dict[fuselage.tag] = fuselage
    # network sources have not been implemented
    #for network in geometry.networks:
    #comp_type_dict[network.tag] = 'turbojet'
    #comp_dict[network.tag] = network

    components = vsp.FindGeoms()

    # The default source values are (mostly) based on the OpenVSP scripts, wing for example:
    # https://github.com/OpenVSP/OpenVSP/blob/a5ac5302b320e8e318830663bb50ba0d4f2d6f64/src/geom_core/WingGeom.cpp

    for comp in components:
        comp_name = vsp.GetGeomName(comp)
        if comp_name not in comp_dict:
            continue
        comp_type = comp_type_dict[comp_name]
        # Nacelle sources are not implemented
        #if comp_name[0:8] == 'turbofan':
        #comp_type = comp_type_dict[comp_name[0:8]]
        #else:
        #comp_type = comp_type_dict[comp_name]
        if comp_type == 'wing':
            wing = comp_dict[comp_name]
            if len(wing.Segments) == 0:  # check if segments exist
                num_secs = 1
                use_base = True
            else:
                if wing.Segments[
                        0].percent_span_location == 0.:  # check if first segment starts at the root
                    num_secs = len(wing.Segments)
                    use_base = False
                else:
                    num_secs = len(wing.Segments) + 1
                    use_base = True

            u_start = 0.
            base_root = wing.chords.root
            base_tip = wing.chords.tip
            for ii in range(0, num_secs):
                if (ii == 0) and (use_base
                                  == True):  # create sources on root segment
                    cr = base_root
                    if len(wing.Segments) > 0:
                        ct = base_root * wing.Segments[0].root_chord_percent
                        seg = wing.Segments[ii]
                    else:
                        if 'vsp_mesh' in wing:
                            custom_flag = True
                        else:
                            custom_flag = False
                        ct = base_tip
                        seg = wing
                    # extract CFD source parameters
                    if len(wing.Segments) == 0:
                        wingtip_flag = True
                    else:
                        wingtip_flag = False
                    add_segment_sources(comp, cr, ct, ii, u_start, num_secs,
                                        custom_flag, wingtip_flag, seg)
                elif (ii == 0) and (use_base == False):
                    cr = base_root * wing.Segments[0].root_chord_percent
                    if num_secs > 1:
                        ct = base_root * wing.Segments[1].root_chord_percent
                    else:
                        ct = base_tip
                    # extract CFD source parameters
                    seg = wing.Segments[ii]
                    if 'vsp_mesh' in wing.Segments[ii]:
                        custom_flag = True
                    else:
                        custom_flag = False
                    wingtip_flag = False
                    add_segment_sources(comp, cr, ct, ii, u_start, num_secs,
                                        custom_flag, wingtip_flag, seg)
                elif ii < num_secs - 1:
                    if use_base == True:
                        jj = 1
                    else:
                        jj = 0
                    cr = base_root * wing.Segments[ii - jj].root_chord_percent
                    ct = base_root * wing.Segments[ii + 1 -
                                                   jj].root_chord_percent
                    seg = wing.Segments[ii - jj]
                    if 'vsp_mesh' in wing.Segments[ii - jj]:
                        custom_flag = True
                    else:
                        custom_flag = False
                    wingtip_flag = False
                    add_segment_sources(comp, cr, ct, ii, u_start, num_secs,
                                        custom_flag, wingtip_flag, seg)
                else:
                    if use_base == True:
                        jj = 1
                    else:
                        jj = 0
                    cr = base_root * wing.Segments[ii - jj].root_chord_percent
                    ct = base_tip
                    seg = wing.Segments[ii - jj]
                    if 'vsp_mesh' in wing.Segments[ii - jj]:
                        custom_flag = True
                    else:
                        custom_flag = False
                    wingtip_flag = True
                    add_segment_sources(comp, cr, ct, ii, u_start, num_secs,
                                        custom_flag, wingtip_flag, seg)
                pass

        elif comp_type == 'fuselage':
            fuselage = comp_dict[comp_name]
            if 'vsp_mesh' in fuselage:
                len1 = fuselage.vsp_mesh.length
                rad1 = fuselage.vsp_mesh.radius
            else:
                len1 = 0.1 * 0.5  # not sure where VSP is getting this value
                rad1 = 0.2 * fuselage.lengths.total
            uloc = 0.0
            wloc = 0.0
            vsp.AddCFDSource(vsp.POINT_SOURCE, comp, 0, len1, rad1, uloc, wloc)
            uloc = 1.0
            vsp.AddCFDSource(vsp.POINT_SOURCE, comp, 0, len1, rad1, uloc, wloc)
            pass
Ejemplo n.º 6
0
def vsp_read(tag, units_type='SI'):
    """This reads an OpenVSP vehicle geometry and writes it into a SUAVE vehicle format.
	Includes wings, fuselages, and propellers.

	Assumptions:
	1. OpenVSP vehicle is composed of conventionally shaped fuselages, wings, and propellers. 
	1a. OpenVSP fuselage: generally narrow at nose and tail, wider in center). 
	1b. Fuselage is designed in VSP as it appears in real life. That is, the VSP model does not rely on
	   superficial elements such as canopies, stacks, or additional fuselages to cover up internal lofting oddities.
	1c. This program will NOT account for multiple geometries comprising the fuselage. For example: a wingbox mounted beneath
	   is a separate geometry and will NOT be processed.
	2. Fuselage origin is located at nose. VSP file origin can be located anywhere, preferably at the forward tip
	   of the vehicle or in front (to make all X-coordinates of vehicle positive).
	3. Written for OpenVSP 3.16.1
	
	Source:
	N/A

	Inputs:
	1. A tag for an XML file in format .vsp3.
	2. Units_type set to 'SI' (default) or 'Imperial'

	Outputs:
	Writes SUAVE vehicle with these geometries from VSP:    (All values default to SI. Any other 2nd argument outputs Imperial.)
		Wings.Wing.    (* is all keys)
			origin                                  [m] in all three dimensions
			spans.projected                         [m]
			chords.root                             [m]
			chords.tip                              [m]
			aspect_ratio                            [-]
			sweeps.quarter_chord                    [radians]
			twists.root                             [radians]
			twists.tip                              [radians]
			thickness_to_chord                      [-]
			dihedral                                [radians]
			symmetric                               <boolean>
			tag                                     <string>
			areas.exposed                           [m^2]
			areas.reference                         [m^2]
			areas.wetted                            [m^2]
			Segments.
			  tag                                   <string>
			  twist                                 [radians]
			  percent_span_location                 [-]  .1 is 10%
			  root_chord_percent                    [-]  .1 is 10%
			  dihedral_outboard                     [radians]
			  sweeps.quarter_chord                  [radians]
			  thickness_to_chord                    [-]
			  airfoil                               <NACA 4-series, 6 series, or airfoil file>
			
		Fuselages.Fuselage.			
			origin                                  [m] in all three dimensions
			width                                   [m]
			lengths.
			  total                                 [m]
			  nose                                  [m]
			  tail                                  [m]
			heights.
			  maximum                               [m]
			  at_quarter_length                     [m]
			  at_three_quarters_length              [m]
			effective_diameter                      [m]
			fineness.nose                           [-] ratio of nose section length to fuselage effective diameter
			fineness.tail                           [-] ratio of tail section length to fuselage effective diameter
			areas.wetted                            [m^2]
			tag                                     <string>
			segment[].   (segments are in ordered container and callable by number)
			  vsp.shape                               [point,circle,round_rect,general_fuse,fuse_file]
			  vsp.xsec_id                             <10 digit string>
			  percent_x_location
			  percent_z_location
			  height
			  width
			  length
			  effective_diameter
			  tag
			vsp.xsec_num                              <integer of fuselage segment quantity>
			vsp.xsec_surf_id                          <10 digit string>
	
		Propellers.Propeller.
			location[X,Y,Z]                            [radians]
			rotation[X,Y,Z]                            [radians]
			tip_radius                                 [m]
		        hub_radius                                 [m]
			thrust_angle                               [radians]
	
	Properties Used:
	N/A
	"""

    vsp.ClearVSPModel()
    vsp.ReadVSPFile(tag)

    vsp_fuselages = []
    vsp_wings = []
    vsp_props = []

    vsp_geoms = vsp.FindGeoms()
    geom_names = []

    vehicle = SUAVE.Vehicle()
    vehicle.tag = tag

    if units_type == 'SI':
        units_type = 'SI'
    else:
        units_type = 'Imperial'

    # The two for-loops below are in anticipation of an OpenVSP API update with a call for GETGEOMTYPE.
    # This print function allows user to enter VSP GeomID manually as first argument in vsp_read functions.

    print("VSP geometry IDs: ")

    # Label each geom type by storing its VSP geom ID. (The API call for GETGEOMTYPE was not released as of 8/9/18, v 3.16.1)

    for geom in vsp_geoms:
        geom_name = vsp.GetGeomName(geom)
        geom_names.append(geom_name)
        print(str(geom_name) + ': ' + geom)

    # -----------------------------
    # MANUAL VSP ENTRY & PROCESSING
    # -----------------------------

    #fuselage = read_vsp_fuselage(fuselage_id, units_type=units_type) # Replace fuselage_id manually.
    #vehicle.append_component(fuselage)

    #wing = read_vsp_wing(wing_id, units_type=units_type)		# Replace wing_id manually.
    #vehicle.append_component(wing)

    #prop = read_vsp_prop(prop_id, units_type=units_type)		# Replace prop_id manually.
    #vehicle.append_component(prop)

    # --------------------------------
    # AUTOMATIC VSP ENTRY & PROCESSING
    # --------------------------------

    #for geom in vsp_geoms:
    #if vsp.GETGEOMTYPE(str(geom)) == 'FUSELAGE':
    #vsp_fuselages.append(geom)
    #if vsp.GETGEOMTYPE(str(geom)) == 'WING':
    #vsp_wings.append(geom)
    #if vsp.GETGEOMTYPE(str(geom)) == 'PROP':
    #vsp_props.append(geom)

    # Read VSP geoms and store in SUAVE components.

    #for vsp_fuselage in vsp_fuselages:
    #fuselage_id = vsp_fuselages[vsp_fuselage]
    #fuselage = read_vsp_fuselage(fuselage_id, units_type)
    #vehicle.append_component(fuselage)

    #for vsp_wing in vsp_wings:
    #wing_id = vsp_wings[vsp_wing]
    #wing = read_vsp_wing(wing_id, units_type)
    #vehicle.append_component(wing)

    #for vsp_prop in vsp_props:
    #prop_id = vsp_props[vsp_prop]
    #prop = read_vsp_prop(prop_id, units_type)
    #vehicle.append_component(prop)

    return vehicle
Ejemplo n.º 7
0
def read_vsp_wing(wing_id, units_type='SI',write_airfoil_file=True): 	
    """This reads an OpenVSP wing vehicle geometry and writes it into a SUAVE wing format.

    Assumptions:
    1. OpenVSP wing is divided into segments ("XSecs" in VSP).
    2. Written for OpenVSP 3.21.1

    Source:
    N/A

    Inputs:
    1. VSP 10-digit geom ID for wing.
    2. units_type set to 'SI' (default) or 'Imperial'.

    Outputs:
    Writes SUAVE wing object, with these geometries, from VSP:
    	Wings.Wing.    (* is all keys)
    		origin                                  [m] in all three dimensions
    		spans.projected                         [m]
    		chords.root                             [m]
    		chords.tip                              [m]
    		aspect_ratio                            [-]
    		sweeps.quarter_chord                    [radians]
    		twists.root                             [radians]
    		twists.tip                              [radians]
    		thickness_to_chord                      [-]
    		dihedral                                [radians]
    		symmetric                               <boolean>
    		tag                                     <string>
    		areas.reference                         [m^2]
    		areas.wetted                            [m^2]
    		Segments.
    		  tag                                   <string>
    		  twist                                 [radians]
    		  percent_span_location                 [-]  .1 is 10%
    		  root_chord_percent                    [-]  .1 is 10%
    		  dihedral_outboard                     [radians]
    		  sweeps.quarter_chord                  [radians]
    		  thickness_to_chord                    [-]
    		  airfoil                               <NACA 4-series, 6 series, or airfoil file>

    Properties Used:
    N/A
    """  

    # Check if this is vertical tail, this seems like a weird first step but it's necessary
    # Get the initial rotation to get the dihedral angles
    x_rot = vsp.GetParmVal( wing_id,'X_Rotation','XForm')		
    if  x_rot >=70:
        wing = SUAVE.Components.Wings.Vertical_Tail()
        wing.vertical = True
        x_rot = (90-x_rot) * Units.deg
    else:
        # Instantiate a wing
        wing = SUAVE.Components.Wings.Wing()	
        x_rot =  x_rot  * Units.deg	

    # Set the units
    if units_type == 'SI':
        units_factor = Units.meter * 1.
    elif units_type == 'imperial':
        units_factor = Units.foot * 1.
    elif units_type == 'inches':
        units_factor = Units.inch * 1.		

    # Apply a tag to the wing
    if vsp.GetGeomName(wing_id):
        tag = vsp.GetGeomName(wing_id)
        tag = tag.translate(t_table)
        wing.tag = tag
    else: 
        wing.tag = 'winggeom'
    
    scaling           = vsp.GetParmVal(wing_id, 'Scale', 'XForm')  
    units_factor      = units_factor*scaling
        
    # Top level wing parameters
    # Wing origin
    wing.origin[0][0] = vsp.GetParmVal(wing_id, 'X_Location', 'XForm') * units_factor 
    wing.origin[0][1] = vsp.GetParmVal(wing_id, 'Y_Location', 'XForm') * units_factor 
    wing.origin[0][2] = vsp.GetParmVal(wing_id, 'Z_Location', 'XForm') * units_factor 

    # Wing Symmetry
    sym_planar = vsp.GetParmVal(wing_id, 'Sym_Planar_Flag', 'Sym')
    sym_origin = vsp.GetParmVal(wing_id, 'Sym_Ancestor_Origin_Flag', 'Sym')

    # Check for symmetry
    if sym_planar == 2. and sym_origin == 1.: #origin at wing, not vehicle
        wing.symmetric = True	
    else:
        wing.symmetric = False 

    #More top level parameters
    total_proj_span      = vsp.GetParmVal(wing_id, 'TotalProjectedSpan', 'WingGeom') * units_factor
    wing.aspect_ratio    = vsp.GetParmVal(wing_id, 'TotalAR', 'WingGeom')
    wing.areas.reference = vsp.GetParmVal(wing_id, 'TotalArea', 'WingGeom') * units_factor**2 
    wing.spans.projected = total_proj_span 

    # Check if this is a single segment wing
    xsec_surf_id      = vsp.GetXSecSurf(wing_id, 0)   # This is how VSP stores surfaces.
    x_sec_1           = vsp.GetXSec(xsec_surf_id, 1) 

    if vsp.GetNumXSec(xsec_surf_id) == 2:
        single_seg = True
    else:
        single_seg = False
    
    segment_num = vsp.GetNumXSec(xsec_surf_id) # Get number of segments

    span_sum         = 0.				# Non-projected.
    proj_span_sum    = 0.				# Projected.
    segment_spans    = [None] * (segment_num) 	        # Non-projected.
    segment_dihedral = [None] * (segment_num)
    segment_sweeps_quarter_chord = [None] * (segment_num) 

    # Necessary wing segment definitions start at XSec_1 (XSec_0 exists mainly to hold the root airfoil)
    xsec_surf_id = vsp.GetXSecSurf(wing_id, 0)
    x_sec = vsp.GetXSec(xsec_surf_id, 1)
    chord_parm = vsp.GetXSecParm(x_sec,'Root_Chord')
    root_chord = vsp.GetParmVal(chord_parm) * units_factor

    # -------------
    # Wing segments
    # -------------

    if single_seg == False:

        # Convert VSP XSecs to SUAVE segments. (Wing segments are defined by outboard sections in VSP, but inboard sections in SUAVE.) 
        for i in range(1, segment_num+1):	
            # XSec airfoil
            jj = i-1  # Airfoil index i-1 because VSP airfoils and sections are one index off relative to SUAVE.
		
            segment = SUAVE.Components.Wings.Segment()
            segment.tag                   = 'Section_' + str(i)
            thick_cord                    = vsp.GetParmVal(wing_id, 'ThickChord', 'XSecCurve_' + str(jj))
            segment.thickness_to_chord    = thick_cord	# Thick_cord stored for use in airfoil, below.		
            if i!=segment_num:
                segment_root_chord    = vsp.GetParmVal(wing_id, 'Root_Chord', 'XSec_' + str(i)) * units_factor
            else:
                segment_root_chord    = 0.0
            segment.root_chord_percent    = segment_root_chord / root_chord		
            segment.percent_span_location = proj_span_sum / (total_proj_span/(1+wing.symmetric))
            segment.twist                 = vsp.GetParmVal(wing_id, 'Twist', 'XSec_' + str(jj)) * Units.deg

            if i==1:
                wing.thickness_to_chord = thick_cord

            if i < segment_num:      # This excludes the tip xsec, but we need a segment in SUAVE to store airfoil.
                sweep     = vsp.GetParmVal(wing_id, 'Sweep', 'XSec_' + str(i)) * Units.deg
                sweep_loc = vsp.GetParmVal(wing_id, 'Sweep_Location', 'XSec_' + str(i))
                AR        = 2*vsp.GetParmVal(wing_id, 'Aspect', 'XSec_' + str(i))
                taper     = vsp.GetParmVal(wing_id, 'Taper', 'XSec_' + str(i))

                segment_sweeps_quarter_chord[i] = convert_sweep(sweep,sweep_loc,0.25,AR,taper)
                segment.sweeps.quarter_chord    = segment_sweeps_quarter_chord[i]  # Used again, below

                # Used for dihedral computation, below.
                segment_dihedral[i]	      = vsp.GetParmVal(wing_id, 'Dihedral', 'XSec_' + str(i)) * Units.deg  + x_rot
                segment.dihedral_outboard     = segment_dihedral[i]

                segment_spans[i] 	      = vsp.GetParmVal(wing_id, 'Span', 'XSec_' + str(i)) * units_factor
                proj_span_sum += segment_spans[i] * np.cos(segment_dihedral[i])	
                span_sum      += segment_spans[i]
            else:
                segment.root_chord_percent    = (vsp.GetParmVal(wing_id, 'Tip_Chord', 'XSec_' + str(i-1))) * units_factor /root_chord


            xsec_id = str(vsp.GetXSec(xsec_surf_id, jj))
            airfoil = Airfoil()
            if vsp.GetXSecShape(xsec_id) == vsp.XS_FOUR_SERIES: 	# XSec shape: NACA 4-series
                camber = vsp.GetParmVal(wing_id, 'Camber', 'XSecCurve_' + str(jj)) 

                if camber == 0.:
                    camber_loc = 0.
                else:
                    camber_loc = vsp.GetParmVal(wing_id, 'CamberLoc', 'XSecCurve_' + str(jj))

                airfoil.thickness_to_chord = thick_cord
                camber_round               = int(np.around(camber*100))
                camber_loc_round           = int(np.around(camber_loc*10)) 
                thick_cord_round           = int(np.around(thick_cord*100))
                airfoil.tag                = 'NACA ' + str(camber_round) + str(camber_loc_round) + str(thick_cord_round)	

            elif vsp.GetXSecShape(xsec_id) == vsp.XS_SIX_SERIES: 	# XSec shape: NACA 6-series
                thick_cord_round = int(np.around(thick_cord*100))
                a_value          = vsp.GetParmVal(wing_id, 'A', 'XSecCurve_' + str(jj))
                ideal_CL         = int(np.around(vsp.GetParmVal(wing_id, 'IdealCl', 'XSecCurve_' + str(jj))*10))
                series_vsp       = int(vsp.GetParmVal(wing_id, 'Series', 'XSecCurve_' + str(jj)))
                series_dict      = {0:'63',1:'64',2:'65',3:'66',4:'67',5:'63A',6:'64A',7:'65A'} # VSP series values.
                series           = series_dict[series_vsp]
                airfoil.tag      = 'NACA ' + series + str(ideal_CL) + str(thick_cord_round) + ' a=' + str(np.around(a_value,1))			


            elif vsp.GetXSecShape(xsec_id) == vsp.XS_FILE_AIRFOIL:	# XSec shape: 12 is type AF_FILE
                airfoil.thickness_to_chord = thick_cord
                # VSP airfoil API calls get coordinates and write files with the final argument being the fraction of segment position, regardless of relative spans. 
                # (Write the root airfoil with final arg = 0. Write 4th airfoil of 5 segments with final arg = .8)

            if write_airfoil_file==True:
                vsp.WriteSeligAirfoil(str(wing.tag) + '_airfoil_XSec_' + str(jj) +'.dat', wing_id, float(jj/segment_num))
                airfoil.coordinate_file    = str(wing.tag) + '_airfoil_XSec_' + str(jj) +'.dat'
                airfoil.tag                = 'airfoil'	

                segment.append_airfoil(airfoil)

            wing.Segments.append(segment)

        # Wing dihedral 
        proj_span_sum_alt = 0.
        span_sum_alt      = 0.
        sweeps_sum        = 0.			

        for ii in range(1, segment_num):
            span_sum_alt += segment_spans[ii]
            proj_span_sum_alt += segment_spans[ii] * np.cos(segment_dihedral[ii])  # Use projected span to find total wing dihedral.
            sweeps_sum += segment_spans[ii] * np.tan(segment_sweeps_quarter_chord[ii])	

        wing.dihedral              = np.arccos(proj_span_sum_alt / span_sum_alt) 
        wing.sweeps.quarter_chord  = -np.arctan(sweeps_sum / span_sum_alt)  # Minus sign makes it positive sweep.

        # Add a tip segment, all values are zero except the tip chord
        tc = vsp.GetParmVal(wing_id, 'Tip_Chord', 'XSec_' + str(segment_num-1)) * units_factor

        # Chords
        wing.chords.root              = vsp.GetParmVal(wing_id, 'Tip_Chord', 'XSec_0') * units_factor
        wing.chords.tip               = tc
        wing.chords.mean_geometric    = wing.areas.reference / wing.spans.projected

        # Just double calculate and fix things:
        wing = wing_segmented_planform(wing)


    else:
        # Single segment

        # Get ID's
        x_sec_1_dih_parm       = vsp.GetXSecParm(x_sec_1,'Dihedral')
        x_sec_1_sweep_parm     = vsp.GetXSecParm(x_sec_1,'Sweep')
        x_sec_1_sweep_loc_parm = vsp.GetXSecParm(x_sec_1,'Sweep_Location')
        x_sec_1_taper_parm     = vsp.GetXSecParm(x_sec_1,'Taper')
        x_sec_1_rc_parm        = vsp.GetXSecParm(x_sec_1,'Root_Chord')
        x_sec_1_tc_parm        = vsp.GetXSecParm(x_sec_1,'Tip_Chord')
        x_sec_1_t_parm        = vsp.GetXSecParm(x_sec_1,'ThickChord')
     
        # Calcs
        sweep     = vsp.GetParmVal(x_sec_1_sweep_parm) * Units.deg
        sweep_loc = vsp.GetParmVal(x_sec_1_sweep_loc_parm)
        taper     = vsp.GetParmVal(x_sec_1_taper_parm)
        c_4_sweep = convert_sweep(sweep,sweep_loc,0.25,wing.aspect_ratio,taper)		

        # Pull and pack
        wing.sweeps.quarter_chord  = c_4_sweep
        wing.taper                 = taper
        wing.dihedral              = vsp.GetParmVal(x_sec_1_dih_parm) * Units.deg + x_rot
        wing.chords.root           = vsp.GetParmVal(x_sec_1_rc_parm)* units_factor
        wing.chords.tip            = vsp.GetParmVal(x_sec_1_tc_parm) * units_factor	
        wing.chords.mean_geometric = wing.areas.reference / wing.spans.projected
        wing.thickness_to_chord    = vsp.GetParmVal(x_sec_1_t_parm) 

        # Just double calculate and fix things:
        wing = wing_planform(wing)		


    # Twists
    wing.twists.root      = vsp.GetParmVal(wing_id, 'Twist', 'XSec_0') * Units.deg
    wing.twists.tip       = vsp.GetParmVal(wing_id, 'Twist', 'XSec_' + str(segment_num-1)) * Units.deg

    # check if control surface (sub surfaces) are defined
    tags                 = []
    LE_flags             = []
    span_fraction_starts = []
    span_fraction_ends   = []
    chord_fractions      = []
    
    num_cs = vsp.GetNumSubSurf(wing_id)
    
    # loop through wing and get all control surface parameters 
    for cs_idx in range(num_cs):
        cs_id   = vsp.GetSubSurf(wing_id,cs_idx)
        param_names = vsp.GetSubSurfParmIDs(cs_id)
        tags.append(vsp.GetSubSurfName(cs_id))
        for p_idx in range(len(param_names)):
            if 'LE_Flag' == vsp.GetParmName(param_names[p_idx]):
                LE_flags.append(vsp.GetParmVal(param_names[p_idx]))
            if 'UStart' == vsp.GetParmName(param_names[p_idx]):
                span_fraction_starts.append(vsp.GetParmVal(param_names[p_idx]))
            if 'UEnd' == vsp.GetParmName(param_names[p_idx]):
                span_fraction_ends.append(vsp.GetParmVal(param_names[p_idx]))
            if 'Length_C_Start' == vsp.GetParmName(param_names[p_idx]):
                chord_fractions.append(vsp.GetParmVal(param_names[p_idx]))
                
    # assign control surface parameters to wings. Outer most control surface on main/horizontal wing is assigned a aileron
    for cs_idx in range(num_cs):   
        aileron_present = False
        if num_cs > 1:
            aileron_loc = np.argmax(np.array(span_fraction_starts))   
            if cs_idx == aileron_loc: 
                aileron_present = True
        if LE_flags[cs_idx] == 1.0:
            CS = SUAVE.Components.Wings.Control_Surfaces.Slat()
        else:
            if wing.vertical == True:
                CS = SUAVE.Components.Wings.Control_Surfaces.Rudder()
            else:
                if aileron_present:
                    CS = SUAVE.Components.Wings.Control_Surfaces.Aileron()
                else: 
                    CS = SUAVE.Components.Wings.Control_Surfaces.Flap()
        CS.tag                 = tags[cs_idx]
        CS.span_fraction_start = span_fraction_starts[cs_idx]*3 - 1
        CS.span_fraction_end   = span_fraction_ends[cs_idx]*3 - 1
        CS.chord_fraction      = chord_fractions[cs_idx]
        CS.span                = (CS.span_fraction_end - CS.span_fraction_start)*wing.spans.projected
        wing.append_control_surface(CS)
    
    return wing
Ejemplo n.º 8
0
def read_vsp_nacelle(nacelle_id, vsp_nacelle_type, units_type='SI'):
    """This reads an OpenVSP stack geometry or body of revolution and writes it to a SUAVE nacelle format.
    If an airfoil is defined in body-of-revolution, its coordinates are not read in due to absence of
    API functions in VSP.

    Assumptions: 
    
    Source:
    N/A

    Inputs:
    0. Pre-loaded VSP vehicle in memory, via vsp_read.
    1. VSP 10-digit geom ID for nacelle.
    2. Units_type set to 'SI' (default) or 'Imperial'. 

    Outputs:
    Writes SUAVE nacelle, with these geometries:           (all defaults are SI, but user may specify Imperial)

        Nacelles.Nacelle.	
            origin                  [m] in all three dimensions
            width                   [m]
            lengths                 [m]
            heights                 [m]
            tag                     <string>
            segment[].   (segments are in ordered container and callable by number) 
              percent_x_location    [unitless]
              percent_z_location    [unitless]
              height                [m]
              width                 [m]

    Properties Used:
    N/A
    """
    nacelle = SUAVE.Components.Nacelles.Nacelle()

    if units_type == 'SI':
        units_factor = Units.meter * 1.
    elif units_type == 'imperial':
        units_factor = Units.foot * 1.
    elif units_type == 'inches':
        units_factor = Units.inch * 1.

    if vsp.GetGeomName(nacelle_id):
        nacelle.tag = vsp.GetGeomName(nacelle_id)
    else:
        nacelle.tag = 'NacelleGeom'

    nacelle.origin[0][0] = vsp.GetParmVal(nacelle_id, 'X_Location',
                                          'XForm') * units_factor
    nacelle.origin[0][1] = vsp.GetParmVal(nacelle_id, 'Y_Location',
                                          'XForm') * units_factor
    nacelle.origin[0][2] = vsp.GetParmVal(nacelle_id, 'Z_Location',
                                          'XForm') * units_factor
    nacelle.x_rotation = vsp.GetParmVal(nacelle_id, 'X_Rotation',
                                        'XForm') * units_factor
    nacelle.y_rotation = vsp.GetParmVal(nacelle_id, 'Y_Rotation',
                                        'XForm') * units_factor
    nacelle.z_rotation = vsp.GetParmVal(nacelle_id, 'Z_Rotation',
                                        'XForm') * units_factor

    if vsp_nacelle_type == 'Stack':

        xsec_surf_id = vsp.GetXSecSurf(
            nacelle_id, 0)  # There is only one XSecSurf in geom.
        num_segs = vsp.GetNumXSec(xsec_surf_id)  # Number of xsecs in nacelle.
        abs_x_location = 0
        abs_y_location = 0
        abs_z_location = 0
        abs_x_location_vec = []
        abs_y_location_vec = []
        abs_z_location_vec = []

        for i in range(num_segs):
            # Create the segment
            xsec_id = vsp.GetXSec(xsec_surf_id, i)  # VSP XSec ID.
            segment = SUAVE.Components.Lofted_Body_Segment.Segment()
            segment.tag = 'segment_' + str(i)

            # Pull out Parms that will be needed
            X_Loc_P = vsp.GetXSecParm(xsec_id, 'XDelta')
            Y_Loc_P = vsp.GetXSecParm(xsec_id, 'YDelta')
            Z_Loc_P = vsp.GetXSecParm(xsec_id, 'XDelta')

            del_x = vsp.GetParmVal(X_Loc_P)
            del_y = vsp.GetParmVal(Y_Loc_P)
            del_z = vsp.GetParmVal(Z_Loc_P)

            abs_x_location = abs_x_location + del_x
            abs_y_location = abs_y_location + del_y
            abs_z_location = abs_z_location + del_z

            abs_x_location_vec.append(abs_x_location)
            abs_y_location_vec.append(abs_y_location)
            abs_z_location_vec.append(abs_z_location)

            shape = vsp.GetXSecShape(xsec_id)
            shape_dict = {
                0: 'point',
                1: 'circle',
                2: 'ellipse',
                3: 'super ellipse',
                4: 'rounded rectangle',
                5: 'general fuse',
                6: 'fuse file'
            }

            if shape_dict[shape] == 'point':
                segment.height = 0.0
                segment.width = 0.0
                if i == 0:
                    nacelle.flow_through = False
            else:
                segment.height = vsp.GetXSecHeight(xsec_id) * units_factor
                segment.width = vsp.GetXSecWidth(xsec_id) * units_factor
                if i == 0:
                    nacelle.flow_through = True

            nacelle.Segments.append(segment)

        nacelle.length = abs_x_location_vec[-1]
        segs = nacelle.Segments
        for seg in range(num_segs):
            segs[seg].percent_x_location = np.array(
                abs_x_location_vec) / abs_x_location_vec[-1]
            segs[seg].percent_y_location = np.array(
                abs_y_location_vec) / abs_x_location_vec[-1]
            segs[seg].percent_z_location = np.array(
                abs_z_location_vec) / abs_x_location_vec[-1]

    elif vsp_nacelle_type == 'BodyOfRevolution':
        diameter = vsp.GetParmVal(nacelle_id, "Diameter",
                                  "Design") * units_factor
        angle = vsp.GetParmVal(nacelle_id, "Diameter",
                               "Design") * Units.degrees
        ft_flag_idx = vsp.GetParmVal(nacelle_id, "Mode", "Design")
        if ft_flag_idx == 0.0:
            ft_flag = True
        else:
            ft_flag = False
        nacelle.flow_through = ft_flag

        shape = vsp.GetBORXSecShape(nacelle_id)
        shape_dict = {0:'point',1:'circle',2:'ellipse',3:'super ellipse',4:'rounded rectangle',5:'general fuse',6:'fuse file',\
                      7:'four series',8:'six series',9:'biconvex',10:'wedge',11:'editcurve',12:'file airfoil'}
        if shape_dict[shape] == 'four series':
            naf = SUAVE.Components.Airfoils.Airfoil()
            length = vsp.GetParmVal(nacelle_id, "Chord", "XSecCurve")
            thickness = int(
                round(
                    vsp.GetParmVal(nacelle_id, "ThickChord", "XSecCurve") * 10,
                    0))
            camber = int(
                round(
                    vsp.GetParmVal(nacelle_id, "Camber", "XSecCurve") * 100,
                    0))
            camber_loc = int(
                round(
                    vsp.GetParmVal(nacelle_id, "CamberLoc", "XSecCurve") * 10,
                    0))

            airfoil = str(camber) + str(camber_loc) + str(thickness)
            height = thickness
            naf.naca_4_series_airfoil = str(airfoil)
            naf.thickness_to_chord = thickness
            nacelle.append_airfoil(naf)

        elif shape_dict[shape] == 'super ellipse':
            if ft_flag:
                height = vsp.GetParmVal(nacelle_id, "Super_Height",
                                        "XSecCurve")
                diameter = vsp.GetParmVal(nacelle_id, "Diameter", "Design")
                length = vsp.GetParmVal(nacelle_id, "Super_Width", "XSecCurve")
            else:
                diameter = vsp.GetParmVal(nacelle_id, "Super_Height",
                                          "XSecCurve")
                length = vsp.GetParmVal(nacelle_id, "Super_Width", "XSecCurve")
                height = diameter / 2

        elif shape_dict[shape] == 'file airfoil':
            naf = SUAVE.Components.Airfoils.Airfoil()
            thickness_to_chord = vsp.GetParmVal(nacelle_id, "ThickChord",
                                                "XSecCurve") * units_factor
            length = vsp.GetParmVal(nacelle_id, "Chord",
                                    "XSecCurve") * units_factor
            height = thickness_to_chord * length * units_factor
            if ft_flag:
                diameter = vsp.GetParmVal(nacelle_id, "Diameter",
                                          "Design") * units_factor
            else:
                diameter = 0
            naf.thickness_to_chord = thickness_to_chord
            nacelle.append_airfoil(naf)

        nacelle.length = length
        nacelle.diameter = diameter + height / 2
        nacelle.inlet_diameter = nacelle.diameter - height
        nacelle.cowling_airfoil_angle = angle

    return nacelle
Ejemplo n.º 9
0
def read_vsp_propeller(prop_id, units_type='SI',write_airfoil_file=True):
    """This reads an OpenVSP propeller geometry and writes it into a SUAVE propeller format.

    Assumptions:
    1. Written for OpenVSP 3.24.0

    Source:
    N/A

    Inputs:
    1. VSP 10-digit geom ID for prop.
    2. units_type set to 'SI' (default) or 'Imperial'.
    3. write_airfoil_file set to True (default) or False
    4. number_of_radial_stations is the radial discretization used to extract the propeller design from OpenVSP

    Outputs:
    Writes SUAVE propeller/rotor object, with these geometries, from VSP:
    	prop.
    		origin                                  [m] in all three dimensions
    		orientation				[deg] in all three dimensions
    		number_of_blades			[-]
    		tip_radius				[m]
    		hub_radius				[m]
    		twist_distribution			[deg]
    		chord_distribution			[m]
    		radius_distribution			[m]
    		sweep_distribution			[deg]
    		mid_chord_alignment			[m]
    		max_thickness_distribution		[m]
    		thickness_to_chord			[-]
    		blade_solidity				[-]
    		rotation			        [-]  
    		thickness_to_chord                      [-] 
                beta34                                  [radians]
                pre_cone                                [radians]
                rake                                    [radians]
                skew                                    [radians]
                axial                                   [radians]
                tangential                              [radians]
    		dihedral                                [radians]
    		symmetric                               <boolean>
    		tag                                     <string>
    		Segments.
    		  tag                                   <string>
    		  twist                                 [radians]
    		  percent_span_location                 [-]  .1 is 10%
    		  root_chord_percent                    [-]  .1 is 10%
    		  dihedral_outboard                     [radians]
    		  sweeps.quarter_chord                  [radians]
    		  thickness_to_chord                    [-]
    		  airfoil                               <NACA 4-series, 6 series, or airfoil file>

    Properties Used:
    N/A
    """


    # Check if this is a propeller or a lift rotor
    # Check if the thrust angle	is > 70 deg in pitch
    if vsp.GetParmVal( prop_id,'Y_Rotation','XForm') >= 70:
        # Assume lift rotor
        prop 	= SUAVE.Components.Energy.Converters.Lift_Rotor()
    else:
        # Instantiate a propeller
        prop 	= SUAVE.Components.Energy.Converters.Propeller()

    # Set the units
    if units_type == 'SI':
        units_factor = Units.meter * 1.
    elif units_type == 'imperial':
        units_factor = Units.foot * 1.
    elif units_type == 'inches':
        units_factor = Units.inch * 1.

    # Apply a tag to the prop
    if vsp.GetGeomName(prop_id):
        tag = vsp.GetGeomName(prop_id)
        tag = tag.translate(t_table)
        prop.tag = tag
    else: 
        prop.tag = 'propgeom'

    
    scaling           = vsp.GetParmVal(prop_id, 'Scale', 'XForm')  
    units_factor      = units_factor*scaling
        
    # Propeller location (absolute)
    prop.origin 	= [[0.0,0.0,0.0]]
    prop.origin[0][0] 	= vsp.GetParmVal(prop_id, 'X_Location', 'XForm') * units_factor
    prop.origin[0][1] 	= vsp.GetParmVal(prop_id, 'Y_Location', 'XForm') * units_factor
    prop.origin[0][2] 	= vsp.GetParmVal(prop_id, 'Z_Location', 'XForm') * units_factor

    # Propeller orientation
    prop.orientation_euler_angles 	= [0.0,0.0,0.0]
    prop.orientation_euler_angles[0] 	= vsp.GetParmVal(prop_id, 'X_Rotation', 'XForm') * Units.degrees
    prop.orientation_euler_angles[1] 	= vsp.GetParmVal(prop_id, 'Y_Rotation', 'XForm') * Units.degrees
    prop.orientation_euler_angles[2] 	= vsp.GetParmVal(prop_id, 'Z_Rotation', 'XForm') * Units.degrees

    # Get the propeller parameter IDs
    parm_id    = vsp.GetGeomParmIDs(prop_id)
    parm_names = []
    for i in range(len(parm_id)):
        parm_name = vsp.GetParmName(parm_id[i])
        parm_names.append(parm_name)

    # Run the vsp Blade Element analysis
    vsp.SetStringAnalysisInput( "BladeElement" , "PropID" , (prop_id,) )
    rid = vsp.ExecAnalysis( "BladeElement" )
    Nc  = len(vsp.GetDoubleResults(rid,"YSection_000"))

    prop.number_points_around_airfoil = 2*Nc
    prop.CLi                          = vsp.GetParmVal(parm_id[parm_names.index('CLi')])
    prop.blade_solidity               = vsp.GetParmVal(parm_id[parm_names.index('Solidity')])
    prop.number_of_blades             = int(vsp.GetParmVal(parm_id[parm_names.index('NumBlade')]))

    prop.tip_radius                   = vsp.GetDoubleResults(rid, "Diameter" )[0] / 2 * units_factor
    prop.radius_distribution          = np.array(vsp.GetDoubleResults(rid, "Radius" )) * prop.tip_radius
    prop.radius_distribution[-1]      = 0.99 * prop.tip_radius # BEMT requires max nondimensional radius to be less than 1.0
    prop.hub_radius                   = prop.radius_distribution[0]

    prop.chord_distribution           = np.array(vsp.GetDoubleResults(rid, "Chord" ))  * prop.tip_radius # vsp gives c/R
    prop.twist_distribution           = np.array(vsp.GetDoubleResults(rid, "Twist" ))  * Units.degrees
    prop.sweep_distribution           = np.array(vsp.GetDoubleResults(rid, "Sweep" ))
    prop.mid_chord_alignment          = np.tan(prop.sweep_distribution*Units.degrees)  * prop.radius_distribution
    prop.thickness_to_chord           = np.array(vsp.GetDoubleResults(rid, "Thick" ))
    prop.max_thickness_distribution   = prop.thickness_to_chord*prop.chord_distribution * units_factor
    prop.Cl_distribution              = np.array(vsp.GetDoubleResults(rid, "CLi" )) 

    # Extra data from VSP BEM for future use in BEMT
    prop.beta34                       = vsp.GetDoubleResults(rid, "Beta34" )[0]  # pitch at 3/4 radius
    prop.pre_cone                     = vsp.GetDoubleResults(rid, "Pre_Cone")[0]
    prop.rake                         = np.array(vsp.GetDoubleResults(rid, "Rake"))
    prop.skew                         = np.array(vsp.GetDoubleResults(rid, "Skew"))
    prop.axial                        = np.array(vsp.GetDoubleResults(rid, "Axial"))
    prop.tangential                   = np.array(vsp.GetDoubleResults(rid, "Tangential"))

    # Set prop rotation
    prop.rotation = 1

    # ---------------------------------------------
    # Rotor Airfoil
    # ---------------------------------------------
    if write_airfoil_file:
        print("Airfoil write not yet implemented. Defaulting to NACA 4412 airfoil for propeller cross section.") 

    return prop