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
# ==== Use Case 2 ====# vsp.VSPRenew() errorMgr.PopErrorAndPrint(stdout) geoms = vsp.FindGeoms() print("All geoms in Vehicle.") print(geoms) # Add Fuse fuse_id = vsp.AddGeom("FUSELAGE") # Get XSec Surf ID xsurf_id = vsp.GetXSecSurf(fuse_id, 0) # Change Type of First XSec vsp.ChangeXSecShape(xsurf_id, 0, vsp.XS_SUPER_ELLIPSE) errorMgr.PopErrorAndPrint(stdout) # Change Type First XSec Properties xsec_id = vsp.GetXSec(xsurf_id, 0) width_id = vsp.GetXSecParm(xsec_id, "Super_Width") height_id = vsp.GetXSecParm(xsec_id, "Super_Height") vsp.SetParmVal(width_id, 4.0) vsp.SetParmVal(height_id, 2.0) # Copy Cross-Section to Clipboard vsp.CopyXSec(fuse_id, 0)
def write_vsp_turbofan(turbofan, OML_set_ind): """This converts turbofans into OpenVSP format. Assumptions: None Source: N/A Inputs: turbofan. number_of_engines [-] engine_length [m] nacelle_diameter [m] origin [m] in all three dimension, should have as many origins as engines OpenVSP_flow_through <boolean> if True create a flow through nacelle, if False create a cylinder Outputs: Operates on the active OpenVSP model, no direct output Properties Used: N/A """ n_engines = turbofan.number_of_engines length = turbofan.engine_length width = turbofan.nacelle_diameter origins = turbofan.origin inlet_width = turbofan.inlet_diameter tf_tag = turbofan.tag # True will create a flow-through subsonic nacelle (which may have dimensional errors) # False will create a cylindrical stack (essentially a cylinder) ft_flag = turbofan.OpenVSP_flow_through import operator # import here since engines are not always needed # sort engines per left to right convention origins_sorted = sorted(origins, key=operator.itemgetter(1)) for ii in range(0,int(n_engines)): origin = origins_sorted[ii] x = origin[0] y = origin[1] z = origin[2] if ft_flag == True: nac_id = vsp.AddGeom( "BODYOFREVOLUTION") vsp.SetGeomName(nac_id, tf_tag+'_'+str(ii+1)) # Origin vsp.SetParmVal(nac_id,'X_Location','XForm',x) vsp.SetParmVal(nac_id,'Y_Location','XForm',y) vsp.SetParmVal(nac_id,'Z_Location','XForm',z) vsp.SetParmVal(nac_id,'Abs_Or_Relitive_flag','XForm',vsp.ABS) # misspelling from OpenVSP # Length and overall diameter vsp.SetParmVal(nac_id,"Diameter","Design",inlet_width) vsp.ChangeBORXSecShape(nac_id ,vsp.XS_SUPER_ELLIPSE) vsp.Update() vsp.SetParmVal(nac_id, "Super_Height", "XSecCurve", (width-inlet_width)/2) vsp.SetParmVal(nac_id, "Super_Width", "XSecCurve", length) vsp.SetParmVal(nac_id, "Super_MaxWidthLoc", "XSecCurve", -1.) vsp.SetParmVal(nac_id, "Super_M", "XSecCurve", 2.) vsp.SetParmVal(nac_id, "Super_N", "XSecCurve", 1.) else: nac_id = vsp.AddGeom("STACK") vsp.SetGeomName(nac_id, tf_tag+'_'+str(ii+1)) # Origin vsp.SetParmVal(nac_id,'X_Location','XForm',x) vsp.SetParmVal(nac_id,'Y_Location','XForm',y) vsp.SetParmVal(nac_id,'Z_Location','XForm',z) vsp.SetParmVal(nac_id,'Abs_Or_Relitive_flag','XForm',vsp.ABS) # misspelling from OpenVSP vsp.SetParmVal(nac_id,'Origin','XForm',0.5) vsp.CutXSec(nac_id,2) # remove extra default subsurface xsecsurf = vsp.GetXSecSurf(nac_id,0) vsp.ChangeXSecShape(xsecsurf,1,vsp.XS_CIRCLE) vsp.ChangeXSecShape(xsecsurf,2,vsp.XS_CIRCLE) vsp.Update() vsp.SetParmVal(nac_id, "Circle_Diameter", "XSecCurve_1", width) vsp.SetParmVal(nac_id, "Circle_Diameter", "XSecCurve_2", width) vsp.SetParmVal(nac_id, "XDelta", "XSec_1", 0) vsp.SetParmVal(nac_id, "XDelta", "XSec_2", length) vsp.SetParmVal(nac_id, "XDelta", "XSec_3", 0) vsp.SetSetFlag(nac_id, OML_set_ind, True) vsp.Update()
def write_vsp_fuselage(fuselage,area_tags, main_wing, fuel_tank_set_ind, OML_set_ind): """This writes a fuselage into OpenVSP format. Assumptions: None Source: N/A Inputs: fuselage width [m] lengths.total [m] heights. maximum [m] at_quarter_length [m] at_wing_root_quarter_chord [m] at_three_quarters_length [m] effective_diameter [m] fineness.nose [-] ratio of nose section length to fuselage width fineness.tail [-] ratio of tail section length to fuselage width tag <string> OpenVSP_values. (optional) nose.top.angle [degrees] nose.top.strength [-] this determines how much the specified angle influences that shape nose.side.angle [degrees] nose.side.strength [-] nose.TB_Sym <boolean> determines if top angle is mirrored on bottom nose.z_pos [-] z position of the nose as a percentage of fuselage length (.1 is 10%) tail.top.angle [degrees] tail.top.strength [-] tail.z_pos (optional, 0.02 default) [-] z position of the tail as a percentage of fuselage length (.1 is 10%) Segments. (optional) width [m] height [m] percent_x_location [-] .1 is 10% length percent_z_location [-] .1 is 10% length area_tags <dict> used to keep track of all tags needed in wetted area computation main_wing.origin [m] main_wing.chords.root [m] fuel_tank_set_index <int> OpenVSP object set containing the fuel tanks Outputs: Operates on the active OpenVSP model, no direct output Properties Used: N/A """ num_segs = len(fuselage.Segments) length = fuselage.lengths.total fuse_x = fuselage.origin[0][0] fuse_y = fuselage.origin[0][1] fuse_z = fuselage.origin[0][2] fuse_x_rotation = fuselage.x_rotation fuse_y_rotation = fuselage.y_rotation fuse_z_rotation = fuselage.z_rotation if num_segs==0: # SUAVE default fuselage shaping width = fuselage.width hmax = fuselage.heights.maximum height1 = fuselage.heights.at_quarter_length height2 = fuselage.heights.at_wing_root_quarter_chord height3 = fuselage.heights.at_three_quarters_length effdia = fuselage.effective_diameter n_fine = fuselage.fineness.nose t_fine = fuselage.fineness.tail try: if main_wing != None: w_origin = main_wing.origin w_c_4 = main_wing.chords.root/4. else: w_origin = 0.5*length w_c_4 = 0.5*length except AttributeError: raise AttributeError('Main wing not detected. Fuselage must have specified sections in this configuration.') # Figure out the location x location of each section, 3 sections, end of nose, wing origin, and start of tail x1 = n_fine*width/length x2 = (w_origin[0][0]+w_c_4)/length x3 = 1-t_fine*width/length end_ind = 4 else: # Fuselage shaping based on sections widths = [] heights = [] x_poses = [] z_poses = [] segs = fuselage.Segments for seg in segs: widths.append(seg.width) heights.append(seg.height) x_poses.append(seg.percent_x_location) z_poses.append(seg.percent_z_location) end_ind = num_segs-1 fuse_id = vsp.AddGeom("FUSELAGE") vsp.SetGeomName(fuse_id, fuselage.tag) area_tags[fuselage.tag] = ['fuselages',fuselage.tag] tail_z_pos = 0.02 # default value # set fuselage relative location and rotation vsp.SetParmVal( fuse_id,'X_Rel_Rotation','XForm',fuse_x_rotation) vsp.SetParmVal( fuse_id,'Y_Rel_Rotation','XForm',fuse_y_rotation) vsp.SetParmVal( fuse_id,'Z_Rel_Rotation','XForm',fuse_z_rotation) vsp.SetParmVal( fuse_id,'X_Rel_Location','XForm',fuse_x) vsp.SetParmVal( fuse_id,'Y_Rel_Location','XForm',fuse_y) vsp.SetParmVal( fuse_id,'Z_Rel_Location','XForm',fuse_z) if 'OpenVSP_values' in fuselage: vals = fuselage.OpenVSP_values # for wave drag testing fuselage.OpenVSP_ID = fuse_id # Nose vsp.SetParmVal(fuse_id,"TopLAngle","XSec_0",vals.nose.top.angle) vsp.SetParmVal(fuse_id,"TopLStrength","XSec_0",vals.nose.top.strength) vsp.SetParmVal(fuse_id,"RightLAngle","XSec_0",vals.nose.side.angle) vsp.SetParmVal(fuse_id,"RightLStrength","XSec_0",vals.nose.side.strength) vsp.SetParmVal(fuse_id,"TBSym","XSec_0",vals.nose.TB_Sym) vsp.SetParmVal(fuse_id,"ZLocPercent","XSec_0",vals.nose.z_pos) if not vals.nose.TB_Sym: vsp.SetParmVal(fuse_id,"BottomLAngle","XSec_0",vals.nose.bottom.angle) vsp.SetParmVal(fuse_id,"BottomLStrength","XSec_0",vals.nose.bottom.strength) # Tail # Below can be enabled if AllSym (below) is removed #vsp.SetParmVal(fuse_id,"RightLAngle","XSec_4",vals.tail.side.angle) #vsp.SetParmVal(fuse_id,"RightLStrength","XSec_4",vals.tail.side.strength) #vsp.SetParmVal(fuse_id,"TBSym","XSec_4",vals.tail.TB_Sym) #vsp.SetParmVal(fuse_id,"BottomLAngle","XSec_4",vals.tail.bottom.angle) #vsp.SetParmVal(fuse_id,"BottomLStrength","XSec_4",vals.tail.bottom.strength) if 'z_pos' in vals.tail: tail_z_pos = vals.tail.z_pos else: pass # use above default if num_segs == 0: vsp.SetParmVal(fuse_id,"Length","Design",length) vsp.SetParmVal(fuse_id,"Diameter","Design",width) vsp.SetParmVal(fuse_id,"XLocPercent","XSec_1",x1) vsp.SetParmVal(fuse_id,"XLocPercent","XSec_2",x2) vsp.SetParmVal(fuse_id,"XLocPercent","XSec_3",x3) vsp.SetParmVal(fuse_id,"ZLocPercent","XSec_4",tail_z_pos) vsp.SetParmVal(fuse_id, "Ellipse_Width", "XSecCurve_1", width) vsp.SetParmVal(fuse_id, "Ellipse_Width", "XSecCurve_2", width) vsp.SetParmVal(fuse_id, "Ellipse_Width", "XSecCurve_3", width) vsp.SetParmVal(fuse_id, "Ellipse_Height", "XSecCurve_1", height1); vsp.SetParmVal(fuse_id, "Ellipse_Height", "XSecCurve_2", height2); vsp.SetParmVal(fuse_id, "Ellipse_Height", "XSecCurve_3", height3); else: # OpenVSP vals do not exist: vals = Data() vals.nose = Data() vals.tail = Data() vals.tail.top = Data() vals.nose.z_pos = 0.0 vals.tail.top.angle = 0.0 vals.tail.top.strength = 0.0 if len(np.unique(x_poses)) != len(x_poses): raise ValueError('Duplicate fuselage section positions detected.') vsp.SetParmVal(fuse_id,"Length","Design",length) if num_segs != 5: # reduce to only nose and tail vsp.CutXSec(fuse_id,1) # remove extra default section vsp.CutXSec(fuse_id,1) # remove extra default section vsp.CutXSec(fuse_id,1) # remove extra default section for i in range(num_segs-2): # add back the required number of sections vsp.InsertXSec(fuse_id, 0, vsp.XS_ELLIPSE) vsp.Update() for i in range(num_segs-2): # Bunch sections to allow proper length settings in the next step # This is necessary because OpenVSP will not move a section past an adjacent section vsp.SetParmVal(fuse_id, "XLocPercent", "XSec_"+str(i+1),1e-6*(i+1)) vsp.Update() if x_poses[1] < (num_segs-2)*1e-6: print('Warning: Second fuselage section is too close to the nose. OpenVSP model may not be accurate.') for i in reversed(range(num_segs-2)): # order is reversed because sections are initially bunched in the front and cannot be extended passed the next vsp.SetParmVal(fuse_id, "XLocPercent", "XSec_"+str(i+1),x_poses[i+1]) vsp.SetParmVal(fuse_id, "ZLocPercent", "XSec_"+str(i+1),z_poses[i+1]) vsp.SetParmVal(fuse_id, "Ellipse_Width", "XSecCurve_"+str(i+1), widths[i+1]) vsp.SetParmVal(fuse_id, "Ellipse_Height", "XSecCurve_"+str(i+1), heights[i+1]) vsp.Update() set_section_angles(i, vals.nose.z_pos, tail_z_pos, x_poses, z_poses, heights, widths,length,end_ind,fuse_id) vsp.SetParmVal(fuse_id, "XLocPercent", "XSec_"+str(0),x_poses[0]) vsp.SetParmVal(fuse_id, "ZLocPercent", "XSec_"+str(0),z_poses[0]) vsp.SetParmVal(fuse_id, "XLocPercent", "XSec_"+str(end_ind),x_poses[-1]) vsp.SetParmVal(fuse_id, "ZLocPercent", "XSec_"+str(end_ind),z_poses[-1]) # Tail if heights[-1] > 0.: stdout = vsp.cvar.cstdout errorMgr = vsp.ErrorMgrSingleton_getInstance() errorMgr.PopErrorAndPrint(stdout) pos = len(heights)-1 vsp.InsertXSec(fuse_id, pos-1, vsp.XS_ELLIPSE) vsp.Update() vsp.SetParmVal(fuse_id, "Ellipse_Width", "XSecCurve_"+str(pos), widths[-1]) vsp.SetParmVal(fuse_id, "Ellipse_Height", "XSecCurve_"+str(pos), heights[-1]) vsp.SetParmVal(fuse_id, "XLocPercent", "XSec_"+str(pos),x_poses[-1]) vsp.SetParmVal(fuse_id, "ZLocPercent", "XSec_"+str(pos),z_poses[-1]) xsecsurf = vsp.GetXSecSurf(fuse_id,0) vsp.ChangeXSecShape(xsecsurf,pos+1,vsp.XS_POINT) vsp.Update() vsp.SetParmVal(fuse_id, "XLocPercent", "XSec_"+str(pos+1),x_poses[-1]) vsp.SetParmVal(fuse_id, "ZLocPercent", "XSec_"+str(pos+1),z_poses[-1]) # update strengths to make end flat vsp.SetParmVal(fuse_id,"TopRStrength","XSec_"+str(pos), 0.) vsp.SetParmVal(fuse_id,"RightRStrength","XSec_"+str(pos), 0.) vsp.SetParmVal(fuse_id,"BottomRStrength","XSec_"+str(pos), 0.) vsp.SetParmVal(fuse_id,"TopLStrength","XSec_"+str(pos+1), 0.) vsp.SetParmVal(fuse_id,"RightLStrength","XSec_"+str(pos+1), 0.) else: vsp.SetParmVal(fuse_id,"TopLAngle","XSec_"+str(end_ind),vals.tail.top.angle) vsp.SetParmVal(fuse_id,"TopLStrength","XSec_"+str(end_ind),vals.tail.top.strength) vsp.SetParmVal(fuse_id,"AllSym","XSec_"+str(end_ind),1) vsp.Update() if 'z_pos' in vals.tail: tail_z_pos = vals.tail.z_pos else: pass # use above default if 'Fuel_Tanks' in fuselage: for tank in fuselage.Fuel_Tanks: write_fuselage_conformal_fuel_tank(fuse_id, tank, fuel_tank_set_ind) vsp.SetSetFlag(fuse_id, OML_set_ind, True) return area_tags
def write_vsp_wing(wing, area_tags, fuel_tank_set_ind, OML_set_ind): """This write a given wing into OpenVSP format Assumptions: If wing segments are defined, they must cover the full span. (may work in some other cases, but functionality will not be maintained) Source: N/A Inputs: wing. origin [m] in all three dimensions spans.projected [m] chords.root [m] chords.tip [m] sweeps.quarter_chord [radians] twists.root [radians] twists.tip [radians] thickness_to_chord [-] dihedral [radians] tag <string> Segments.*. (optional) twist [radians] percent_span_location [-] .1 is 10% root_chord_percent [-] .1 is 10% dihedral_outboard [radians] sweeps.quarter_chord [radians] thickness_to_chord [-] area_tags <dict> used to keep track of all tags needed in wetted area computation fuel_tank_set_index <int> OpenVSP object set containing the fuel tanks Outputs: area_tags <dict> used to keep track of all tags needed in wetted area computation wing_id <str> OpenVSP ID for given wing Properties Used: N/A """ wing_x = wing.origin[0][0] wing_y = wing.origin[0][1] wing_z = wing.origin[0][2] if wing.symmetric == True: span = wing.spans.projected/2. # span of one side else: span = wing.spans.projected root_chord = wing.chords.root tip_chord = wing.chords.tip sweep = wing.sweeps.quarter_chord / Units.deg sweep_loc = 0.25 root_twist = wing.twists.root / Units.deg tip_twist = wing.twists.tip / Units.deg root_tc = wing.thickness_to_chord tip_tc = wing.thickness_to_chord dihedral = wing.dihedral / Units.deg # Check to see if segments are defined. Get count if len(wing.Segments.keys())>0: n_segments = len(wing.Segments.keys()) else: n_segments = 0 # Create the wing wing_id = vsp.AddGeom( "WING" ) vsp.SetGeomName(wing_id, wing.tag) area_tags[wing.tag] = ['wings',wing.tag] # Make names for each section and insert them into the wing if necessary x_secs = [] x_sec_curves = [] # n_segments + 2 will create an extra segment if the root segment is # included in the list of segments. This is not used and the tag is # removed when the segments are checked for this case. for i_segs in range(0,n_segments+2): x_secs.append('XSec_' + str(i_segs)) x_sec_curves.append('XSecCurve_' + str(i_segs)) # Apply the basic characteristics of the wing to root and tip if wing.symmetric == False: vsp.SetParmVal( wing_id,'Sym_Planar_Flag','Sym',0) if wing.vertical == True: vsp.SetParmVal( wing_id,'X_Rel_Rotation','XForm',90) dihedral = -dihedral # check for vertical tail, direction reverses from SUAVE/AVL vsp.SetParmVal( wing_id,'X_Rel_Location','XForm',wing_x) vsp.SetParmVal( wing_id,'Y_Rel_Location','XForm',wing_y) vsp.SetParmVal( wing_id,'Z_Rel_Location','XForm',wing_z) # This ensures that the other VSP parameters are driven properly vsp.SetDriverGroup( wing_id, 1, vsp.SPAN_WSECT_DRIVER, vsp.ROOTC_WSECT_DRIVER, vsp.TIPC_WSECT_DRIVER ) # Root chord vsp.SetParmVal( wing_id,'Root_Chord',x_secs[1],root_chord) # Sweep of the first section vsp.SetParmVal( wing_id,'Sweep',x_secs[1],sweep) vsp.SetParmVal( wing_id,'Sweep_Location',x_secs[1],sweep_loc) # Twists if n_segments != 0: if np.isclose(wing.Segments[0].percent_span_location,0.): vsp.SetParmVal( wing_id,'Twist',x_secs[0],wing.Segments[0].twist / Units.deg) # root else: vsp.SetParmVal( wing_id,'Twist',x_secs[0],root_twist) # root # The tips should write themselves else: vsp.SetParmVal( wing_id,'Twist',x_secs[0],root_twist) # root vsp.SetParmVal( wing_id,'Twist',x_secs[1],tip_twist) # tip # Figure out if there is an airfoil provided # Airfoils should be in Lednicer format # i.e. : # #EXAMPLE AIRFOIL # 3. 3. # # 0.0 0.0 # 0.5 0.1 # 1.0 0.0 # # 0.0 0.0 # 0.5 -0.1 # 1.0 0.0 # Note this will fail silently if airfoil is not in correct format # check geometry output airfoil_vsp_types = [] if n_segments > 0: for i in range(n_segments): if 'airfoil_type' in wing.Segments[i].keys(): if wing.Segments[i].airfoil_type == 'biconvex': airfoil_vsp_types.append(vsp.XS_BICONVEX) else: airfoil_vsp_types.append(vsp.XS_FILE_AIRFOIL) else: airfoil_vsp_types.append(vsp.XS_FILE_AIRFOIL) elif 'airfoil_type' in wing.keys(): if wing.airfoil_type == 'biconvex': airfoil_vsp_types.append(vsp.XS_BICONVEX) else: airfoil_vsp_types.append(vsp.XS_FILE_AIRFOIL) else: airfoil_vsp_types = [vsp.XS_FILE_AIRFOIL] if n_segments==0: if len(wing.Airfoil) != 0 or 'airfoil_type' in wing.keys(): xsecsurf = vsp.GetXSecSurf(wing_id,0) vsp.ChangeXSecShape(xsecsurf,0,airfoil_vsp_types[0]) vsp.ChangeXSecShape(xsecsurf,1,airfoil_vsp_types[0]) if len(wing.Airfoil) != 0: xsec1 = vsp.GetXSec(xsecsurf,0) xsec2 = vsp.GetXSec(xsecsurf,1) vsp.ReadFileAirfoil(xsec1,wing.Airfoil['airfoil'].coordinate_file) vsp.ReadFileAirfoil(xsec2,wing.Airfoil['airfoil'].coordinate_file) vsp.Update() else: if len(wing.Segments[0].Airfoil) != 0 or 'airfoil_type' in wing.Segments[0].keys(): xsecsurf = vsp.GetXSecSurf(wing_id,0) vsp.ChangeXSecShape(xsecsurf,0,airfoil_vsp_types[0]) vsp.ChangeXSecShape(xsecsurf,1,airfoil_vsp_types[0]) if len(wing.Segments[0].Airfoil) != 0: xsec1 = vsp.GetXSec(xsecsurf,0) xsec2 = vsp.GetXSec(xsecsurf,1) vsp.ReadFileAirfoil(xsec1,wing.Segments[0].Airfoil['airfoil'].coordinate_file) vsp.ReadFileAirfoil(xsec2,wing.Segments[0].Airfoil['airfoil'].coordinate_file) vsp.Update() # Thickness to chords vsp.SetParmVal( wing_id,'ThickChord','XSecCurve_0',root_tc) vsp.SetParmVal( wing_id,'ThickChord','XSecCurve_1',tip_tc) # Dihedral vsp.SetParmVal( wing_id,'Dihedral',x_secs[1],dihedral) # Span and tip of the section if n_segments>1: local_span = span*wing.Segments[0].percent_span_location sec_tip_chord = root_chord*wing.Segments[0].root_chord_percent vsp.SetParmVal( wing_id,'Span',x_secs[1],local_span) vsp.SetParmVal( wing_id,'Tip_Chord',x_secs[1],sec_tip_chord) else: vsp.SetParmVal( wing_id,'Span',x_secs[1],span/np.cos(dihedral*Units.degrees)) vsp.Update() if n_segments>0: if wing.Segments[0].percent_span_location==0.: x_secs[-1] = [] # remove extra section tag (for clarity) segment_0_is_root_flag = True adjust = 0 # used for indexing else: segment_0_is_root_flag = False adjust = 1 else: adjust = 1 # Loop for the number of segments left over for i_segs in range(1,n_segments+1): if (wing.Segments[i_segs-1] == wing.Segments[-1]) and (wing.Segments[-1].percent_span_location == 1.): break # Unpack dihedral_i = wing.Segments[i_segs-1].dihedral_outboard / Units.deg chord_i = root_chord*wing.Segments[i_segs-1].root_chord_percent try: twist_i = wing.Segments[i_segs].twist / Units.deg no_twist_flag = False except: no_twist_flag = True sweep_i = wing.Segments[i_segs-1].sweeps.quarter_chord / Units.deg tc_i = wing.Segments[i_segs-1].thickness_to_chord # Calculate the local span if i_segs == n_segments: span_i = span*(1 - wing.Segments[i_segs-1].percent_span_location)/np.cos(dihedral_i*Units.deg) else: span_i = span*(wing.Segments[i_segs].percent_span_location-wing.Segments[i_segs-1].percent_span_location)/np.cos(dihedral_i*Units.deg) # Insert the new wing section with specified airfoil if available if len(wing.Segments[i_segs-1].Airfoil) != 0 or 'airfoil_type' in wing.Segments[i_segs-1].keys(): vsp.InsertXSec(wing_id,i_segs-1+adjust,airfoil_vsp_types[i_segs-1]) if len(wing.Segments[i_segs-1].Airfoil) != 0: xsecsurf = vsp.GetXSecSurf(wing_id,0) xsec = vsp.GetXSec(xsecsurf,i_segs+adjust) vsp.ReadFileAirfoil(xsec, wing.Segments[i_segs-1].Airfoil['airfoil'].coordinate_file) else: vsp.InsertXSec(wing_id,i_segs-1+adjust,vsp.XS_FOUR_SERIES) # Set the parms vsp.SetParmVal( wing_id,'Span',x_secs[i_segs+adjust],span_i) vsp.SetParmVal( wing_id,'Dihedral',x_secs[i_segs+adjust],dihedral_i) vsp.SetParmVal( wing_id,'Sweep',x_secs[i_segs+adjust],sweep_i) vsp.SetParmVal( wing_id,'Sweep_Location',x_secs[i_segs+adjust],sweep_loc) vsp.SetParmVal( wing_id,'Root_Chord',x_secs[i_segs+adjust],chord_i) if not no_twist_flag: vsp.SetParmVal( wing_id,'Twist',x_secs[i_segs+adjust],twist_i) vsp.SetParmVal( wing_id,'ThickChord',x_sec_curves[i_segs+adjust],tc_i) if adjust and (i_segs == 1): vsp.Update() vsp.SetParmVal( wing_id,'Twist',x_secs[1],wing.Segments[i_segs-1].twist / Units.deg) vsp.Update() if (n_segments != 0) and (wing.Segments[-1].percent_span_location == 1.): tip_chord = root_chord*wing.Segments[-1].root_chord_percent vsp.SetParmVal( wing_id,'Tip_Chord',x_secs[n_segments-1+adjust],tip_chord) vsp.SetParmVal( wing_id,'ThickChord',x_sec_curves[n_segments-1+adjust],wing.Segments[-1].thickness_to_chord) # twist is set in the normal loop else: vsp.SetParmVal( wing_id,'Tip_Chord',x_secs[-1-(1-adjust)],tip_chord) vsp.SetParmVal( wing_id,'Twist',x_secs[-1-(1-adjust)],tip_twist) # a single trapezoidal wing is assumed to have constant thickness to chord vsp.Update() vsp.SetParmVal(wing_id,'CapUMaxOption','EndCap',2.) vsp.SetParmVal(wing_id,'CapUMaxStrength','EndCap',1.) vsp.Update() # to fix problems with chords not matching up if 'Fuel_Tanks' in wing: for tank in wing.Fuel_Tanks: write_wing_conformal_fuel_tank(wing, wing_id, tank, fuel_tank_set_ind) vsp.SetSetFlag(wing_id, OML_set_ind, True) return area_tags, wing_id
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
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
def write_vsp_nacelle(nacelle, OML_set_ind): """This converts nacelles into OpenVSP format. Assumptions: 1. If nacelle segments are defined, geometry written to OpenVSP is of type "StackGeom". 1.a This type of nacelle can be either set as flow through or not flow through. 1.b Segments are defined in a similar manner to fuselage segments. See geometric documentation in SUAVE-Components-Nacelles-Nacelle 2. If nacelle segments are not defined, geometry written to OpenVSP is of type "BodyofRevolution". 2.a This type of nacelle can be either set as flow through or not flow through. 2.b BodyofRevolution can be either be a 4 digit airfoil (type string) or super ellipse (default) Source: N/A Inputs: nacelle. origin [m] in all three dimension, should have as many origins as engines length [m] diameter [m] flow_through <boolean> if True create a flow through nacelle, if False create a cylinder segment(optional). width [m] height [m] lenght [m] percent_x_location [m] percent_y_location [m] percent_z_location [m] Outputs: Operates on the active OpenVSP model, no direct output Properties Used: N/A """ # default tesselation radial_tesselation = 21 axial_tesselation = 25 # True will create a flow-through subsonic nacelle (which may have dimensional errors) # False will create a cylindrical stack (essentially a cylinder) ft_flag = nacelle.flow_through length = nacelle.length height = nacelle.diameter - nacelle.inlet_diameter diameter = nacelle.diameter - height / 2 nac_tag = nacelle.tag nac_x = nacelle.origin[0][0] nac_y = nacelle.origin[0][1] nac_z = nacelle.origin[0][2] nac_x_rotation = nacelle.orientation_euler_angles[0] / Units.degrees nac_y_rotation = nacelle.orientation_euler_angles[1] / Units.degrees nac_z_rotation = nacelle.orientation_euler_angles[2] / Units.degrees num_segs = len(nacelle.Segments) if num_segs > 0: if nacelle.Airfoil.naca_4_series_airfoil != None: raise AssertionError( 'Nacelle segments defined. Airfoil section will not be used.') nac_id = vsp.AddGeom("STACK") vsp.SetGeomName(nac_id, nac_tag) # set nacelle relative location and rotation vsp.SetParmVal(nac_id, 'Abs_Or_Relitive_flag', 'XForm', vsp.ABS) vsp.SetParmVal(nac_id, 'X_Rotation', 'XForm', nac_x_rotation) vsp.SetParmVal(nac_id, 'Y_Rotation', 'XForm', nac_y_rotation) vsp.SetParmVal(nac_id, 'Z_Rotation', 'XForm', nac_z_rotation) vsp.SetParmVal(nac_id, 'X_Location', 'XForm', nac_x) vsp.SetParmVal(nac_id, 'Y_Location', 'XForm', nac_y) vsp.SetParmVal(nac_id, 'Z_Location', 'XForm', nac_z) vsp.SetParmVal(nac_id, 'Tess_U', 'Shape', radial_tesselation) vsp.SetParmVal(nac_id, 'Tess_W', 'Shape', axial_tesselation) widths = [] heights = [] x_delta = [] x_poses = [] z_delta = [] segs = nacelle.Segments for seg in range(num_segs): widths.append(segs[seg].width) heights.append(segs[seg].height) x_poses.append(segs[seg].percent_x_location) if seg == 0: x_delta.append(0) z_delta.append(0) else: x_delta.append(length * (segs[seg].percent_x_location - segs[seg - 1].percent_x_location)) z_delta.append(length * (segs[seg].percent_z_location - segs[seg - 1].percent_z_location)) vsp.CutXSec(nac_id, 4) # remove point section at end vsp.CutXSec(nac_id, 0) # remove point section at beginning vsp.CutXSec(nac_id, 1) # remove point section at beginning for _ in range(num_segs - 2): # add back the required number of sections vsp.InsertXSec(nac_id, 1, vsp.XS_ELLIPSE) vsp.Update() xsec_surf = vsp.GetXSecSurf(nac_id, 0) for i3 in reversed(range(num_segs)): xsec = vsp.GetXSec(xsec_surf, i3) if i3 == 0: pass else: vsp.SetParmVal(nac_id, "XDelta", "XSec_" + str(i3), x_delta[i3]) vsp.SetParmVal(nac_id, "ZDelta", "XSec_" + str(i3), z_delta[i3]) vsp.SetXSecWidthHeight(xsec, widths[i3], heights[i3]) vsp.SetXSecTanAngles(xsec, vsp.XSEC_BOTH_SIDES, 0, 0, 0, 0) vsp.SetXSecTanSlews(xsec, vsp.XSEC_BOTH_SIDES, 0, 0, 0, 0) vsp.SetXSecTanStrengths(xsec, vsp.XSEC_BOTH_SIDES, 0, 0, 0, 0) vsp.Update() if ft_flag: pass else: # append front point xsecsurf = vsp.GetXSecSurf(nac_id, 0) vsp.ChangeXSecShape(xsecsurf, 0, vsp.XS_POINT) vsp.Update() xsecsurf = vsp.GetXSecSurf(nac_id, 0) vsp.ChangeXSecShape(xsecsurf, num_segs - 1, vsp.XS_POINT) vsp.Update() else: nac_id = vsp.AddGeom("BODYOFREVOLUTION") vsp.SetGeomName(nac_id, nac_tag) # Origin vsp.SetParmVal(nac_id, 'Abs_Or_Relitive_flag', 'XForm', vsp.ABS) vsp.SetParmVal(nac_id, 'X_Rotation', 'XForm', nac_x_rotation) vsp.SetParmVal(nac_id, 'Y_Rotation', 'XForm', nac_y_rotation) vsp.SetParmVal(nac_id, 'Z_Rotation', 'XForm', nac_z_rotation) vsp.SetParmVal(nac_id, 'X_Location', 'XForm', nac_x) vsp.SetParmVal(nac_id, 'Y_Location', 'XForm', nac_y) vsp.SetParmVal(nac_id, 'Z_Location', 'XForm', nac_z) vsp.SetParmVal(nac_id, 'Tess_U', 'Shape', radial_tesselation) vsp.SetParmVal(nac_id, 'Tess_W', 'Shape', axial_tesselation) # Length and overall diameter vsp.SetParmVal(nac_id, "Diameter", "Design", diameter) if ft_flag: vsp.SetParmVal(nac_id, "Mode", "Design", 0.0) else: vsp.SetParmVal(nac_id, "Mode", "Design", 1.0) if nacelle.Airfoil.naca_4_series_airfoil != None: if isinstance( nacelle.Airfoil.naca_4_series_airfoil, str) and len(nacelle.Airfoil.naca_4_series_airfoil) != 4: raise AssertionError( 'Nacelle cowling airfoil must be of type < string > and length < 4 >' ) else: angle = nacelle.cowling_airfoil_angle / Units.degrees camber = float(nacelle.Airfoil.naca_4_series_airfoil[0]) / 100 camber_loc = float( nacelle.Airfoil.naca_4_series_airfoil[1]) / 10 thickness = float( nacelle.Airfoil.naca_4_series_airfoil[2:]) / 100 vsp.ChangeBORXSecShape(nac_id, vsp.XS_FOUR_SERIES) vsp.Update() vsp.SetParmVal(nac_id, "Diameter", "Design", diameter) vsp.SetParmVal(nac_id, "Angle", "Design", angle) vsp.SetParmVal(nac_id, "Chord", "XSecCurve", length) vsp.SetParmVal(nac_id, "ThickChord", "XSecCurve", thickness) vsp.SetParmVal(nac_id, "Camber", "XSecCurve", camber) vsp.SetParmVal(nac_id, "CamberLoc", "XSecCurve", camber_loc) vsp.Update() else: vsp.ChangeBORXSecShape(nac_id, vsp.XS_SUPER_ELLIPSE) vsp.Update() if ft_flag: vsp.SetParmVal(nac_id, "Super_Height", "XSecCurve", height) vsp.SetParmVal(nac_id, "Diameter", "Design", diameter) else: vsp.SetParmVal(nac_id, "Super_Height", "XSecCurve", diameter) vsp.SetParmVal(nac_id, "Super_Width", "XSecCurve", length) vsp.SetParmVal(nac_id, "Super_MaxWidthLoc", "XSecCurve", 0.) vsp.SetParmVal(nac_id, "Super_M", "XSecCurve", 2.) vsp.SetParmVal(nac_id, "Super_N", "XSecCurve", 1.) vsp.SetSetFlag(nac_id, OML_set_ind, True) vsp.Update() return
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