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) # Paste Cross-Section vsp.PasteXSec(fuse_id, 1) vsp.PasteXSec(fuse_id, 2)
num_err = errorMgr.GetNumTotalErrors() for i in range(0, num_err): err = errorMgr.PopLastError() print "error = ", err.m_ErrorString #Analysis #call(["vspaero", "-omp", "8", "-stab", "WingBody_DegenGeom"]) #call(["vspaero", "-omp", "8", "WingBody_DegenGeom"]) #Open files #call(["vspviewer", "WingBody_DegenGeom"]) call(["vsp", "WingBody.vsp3"]) #call(["ls"]) ''' # 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_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_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_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 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