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_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