def find_fuse_u_coordinate(x_target, fuse_id, fuel_tank_tag): """Determines the u coordinate of an OpenVSP fuselage that matches an x coordinate Assumptions: Fuselage is aligned with the x axis Source: N/A Inputs: x_target [m] fuse_id <str> fuel_tank_tag <str> Outputs: u_current [-] u coordinate for the requests x position Properties Used: N/A """ tol = 1e-3 diff = 1000 u_min = 0 u_max = 1 while np.abs(diff) > tol: u_current = (u_max + u_min) / 2 probe_id = vsp.AddProbe(fuse_id, 0, u_current, 0, fuel_tank_tag + '_probe') vsp.Update() x_id = vsp.FindParm(probe_id, 'X', 'Measure') x_pos = vsp.GetParmVal(x_id) diff = x_target - x_pos if diff > 0: u_min = u_current else: u_max = u_current vsp.DelProbe(probe_id) return u_current
def addVariable(self, component, group, parm, value=None, lower=None, upper=None, scale=1.0, scaledStep=True, dh=1e-6): """ Add a design variable definition. Parameters ---------- component : str Name of the VSP component group : str Name of the VSP group parm : str Name of the VSP parameter value : float or None The design variable. If this value is not supplied (None), then the current value in the VSP model will be queried and used lower : float or None Lower bound for the design variable. Use None for no lower bound upper : float or None Upper bound for the design variable. Use None for no upper bound scale : float Scale factor scaledStep : bool Flag to use a scaled step sized based on the initial value of the variable. It will remain constant thereafter. dh : float Step size. When scaledStep is True, the actual step is dh*value. Otherwise this actual step is used. """ container_id = vsp.FindContainer(component, 0) if container_id == "": raise Error('Bad component for DV: %s' % component) parm_id = vsp.FindParm(container_id, parm, group) if parm_id == "": raise Error('Bad group or parm: %s %s' % (group, parm)) # Now we know the parmID is ok. So we just get the value val = vsp.GetParmVal(parm_id) dvName = '%s:%s:%s' % (component, group, parm) if value is None: value = val if scaledStep: dh = dh * value if value == 0: raise Error( 'Initial value is exactly 0. scaledStep option cannot be used' 'Specify an explicit dh with scaledStep=False') self.DVs[dvName] = vspDV(parm_id, component, group, parm, value, lower, upper, scale, dh)
def write_fuselage_conformal_fuel_tank(fuse_id,fuel_tank,fuel_tank_set_ind): """This writes a conformal fuel tank in a fuselage. Assumptions: Fuselage is aligned with the x axis Source: N/A Inputs: fuse_id <str> fuel_tank. inward_offset [m] start_length_percent [-] .1 is 10% end_length_percent [-] fuel_type.density [kg/m^3] fuel_tank_set_ind <int> Outputs: Operates on the active OpenVSP model, no direct output Properties Used: N/A """ #stdout = vsp.cvar.cstdout #errorMgr = vsp.ErrorMgrSingleton_getInstance() #errorMgr.PopErrorAndPrint(stdout) # Unpack try: offset = fuel_tank.inward_offset len_trim_max = fuel_tank.end_length_percent len_trim_min = fuel_tank.start_length_percent density = fuel_tank.fuel_type.density except: print('Fuel tank does not contain parameters needed for OpenVSP geometry. Tag: '+fuel_tank.tag) return tank_id = vsp.AddGeom('CONFORMAL',fuse_id) vsp.SetGeomName(tank_id, fuel_tank.tag) # Search for proper x position # Get min x probe_id = vsp.AddProbe(fuse_id,0,0,0,fuel_tank.tag+'_probe') vsp.Update() x_id = vsp.FindParm(probe_id,'X','Measure') x_pos = vsp.GetParmVal(x_id) fuse_x_min = x_pos vsp.DelProbe(probe_id) # Get min x probe_id = vsp.AddProbe(fuse_id,0,1,0,fuel_tank.tag+'_probe') vsp.Update() x_id = vsp.FindParm(probe_id,'X','Measure') x_pos = vsp.GetParmVal(x_id) fuse_x_max = x_pos vsp.DelProbe(probe_id) # Search for u values x_target_start = (fuse_x_max-fuse_x_min)*fuel_tank.start_length_percent x_target_end = (fuse_x_max-fuse_x_min)*fuel_tank.end_length_percent u_start = find_fuse_u_coordinate(x_target_start, fuse_id, fuel_tank.tag) u_end = find_fuse_u_coordinate(x_target_end, fuse_id, fuel_tank.tag) # Offset vsp.SetParmVal(tank_id,'Offset','Design',offset) # Fuel tank length bounds vsp.SetParmVal(tank_id,'UTrimFlag','Design',1.) vsp.SetParmVal(tank_id,'UTrimMax','Design',u_end) vsp.SetParmVal(tank_id,'UTrimMin','Design',u_start) # Set density vsp.SetParmVal(tank_id,'Density','Mass_Props',density) # Add to the full fuel tank set vsp.SetSetFlag(tank_id, fuel_tank_set_ind, True) return
def write(vehicle, tag, fuel_tank_set_ind=3, verbose=True, write_file=True, OML_set_ind = 4, write_igs = False): """This writes a SUAVE vehicle to OpenVSP format. It will take wing segments into account if they are specified in the vehicle setup file. Assumptions: Vehicle is composed of conventional shape fuselages, wings, and propulsors. Any propulsor that should be created is tagged as 'turbofan'. Source: N/A Inputs: vehicle. tag [-] wings.*. (* is all keys) 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 [-] propulsors.turbofan. (optional) number_of_engines [-] engine_length [m] nacelle_diameter [m] origin [m] in all three dimension, should have as many origins as engines OpenVSP_simple (optional) <boolean> if False (default) create a flow through nacelle, if True creates a roughly biparabolic shape fuselages.fuselage (optional) 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%) fuel_tank_set_index <int> OpenVSP object set containing the fuel tanks Outputs: <tag>.vsp3 This is the OpenVSP representation of the aircraft Properties Used: N/A """ # Reset OpenVSP to avoid including a previous vehicle if verbose: print('Reseting OpenVSP Model in Memory') try: vsp.ClearVSPModel() except NameError: print('VSP import failed') return -1 area_tags = dict() # for wetted area assignment # ------------- # Wings # ------------- # Default Set_0 in OpenVSP is index 3 vsp.SetSetName(fuel_tank_set_ind, 'fuel_tanks') vsp.SetSetName(OML_set_ind, 'OML') for wing in vehicle.wings: if verbose: print('Writing '+wing.tag+' to OpenVSP Model') area_tags, wing_id = write_vsp_wing(wing,area_tags, fuel_tank_set_ind, OML_set_ind) if wing.tag == 'main_wing': main_wing_id = wing_id # ------------- # Engines # ------------- ## Skeleton code for props and pylons can be found in previous commits (~Dec 2016) if desired ## This was a place to start and may not still be functional if 'turbofan' in vehicle.propulsors: if verbose: print('Writing '+vehicle.propulsors.turbofan.tag+' to OpenVSP Model') turbofan = vehicle.propulsors.turbofan write_vsp_turbofan(turbofan, OML_set_ind) if 'turbojet' in vehicle.propulsors: turbofan = vehicle.propulsors.turbojet write_vsp_turbofan(turbofan, OML_set_ind) # ------------- # Fuselage # ------------- for key, fuselage in vehicle.fuselages.items(): if verbose: print('Writing '+fuselage.tag+' to OpenVSP Model') try: area_tags = write_vsp_fuselage(fuselage, area_tags, vehicle.wings.main_wing, fuel_tank_set_ind, OML_set_ind) except AttributeError: area_tags = write_vsp_fuselage(fuselage, area_tags, None, fuel_tank_set_ind, OML_set_ind) vsp.Update() # Write the vehicle to the file if write_file ==True: cwd = os.getcwd() filename = tag + ".vsp3" if verbose: print('Saving OpenVSP File at '+ cwd + '/' + filename) vsp.WriteVSPFile(filename) elif verbose: print('Not Saving OpenVSP File') if write_igs: if verbose: print('Exporting IGS File') vehicle_id = vsp.FindContainersWithName('Vehicle')[0] parm_id = vsp.FindParm(vehicle_id,'LabelID','IGESSettings') vsp.SetParmVal(parm_id, 0.) vsp.ExportFile(tag + ".igs", OML_set_ind, vsp.EXPORT_IGES) return area_tags
def write_vsp_mesh(geometry, tag, half_mesh_flag, growth_ratio, growth_limiting_flag): """This create an .stl surface mesh based on a vehicle stored in a .vsp3 file. Assumptions: None Source: N/A Inputs: geometry. - Also passed to set_sources wings.main_wing.chords.mean_aerodynamic [m] half_mesh_flag <boolean> determines if a symmetry plane is created growth_ratio [-] growth ratio for the mesh growth_limiting_flag <boolean> determines if 3D growth limiting is used Outputs: <tag>.stl Properties Used: N/A """ # Reset OpenVSP to avoid including a previous vehicle vsp.ClearVSPModel() if 'turbofan' in geometry.networks: print( 'Warning: no meshing sources are currently implemented for the nacelle' ) # Turn on symmetry plane splitting to improve robustness of meshing process if half_mesh_flag == True: f = fileinput.input(tag + '.vsp3', inplace=1) for line in f: if 'SymmetrySplitting' in line: print(line[0:34] + '1' + line[35:-1]) else: print(line) vsp.ReadVSPFile(tag + '.vsp3') # Set output file types and what will be meshed file_type = vsp.CFD_STL_TYPE + vsp.CFD_KEY_TYPE set_int = vsp.SET_ALL vsp.SetComputationFileName(vsp.CFD_STL_TYPE, tag + '.stl') vsp.SetComputationFileName(vsp.CFD_KEY_TYPE, tag + '.key') # Set to create a tagged STL mesh file vehicle_cont = vsp.FindContainer('Vehicle', 0) STL_multi = vsp.FindParm(vehicle_cont, 'MultiSolid', 'STLSettings') vsp.SetParmVal(STL_multi, 1.0) vsp.SetCFDMeshVal(vsp.CFD_FAR_FIELD_FLAG, 1) if half_mesh_flag == True: vsp.SetCFDMeshVal(vsp.CFD_HALF_MESH_FLAG, 1) # Figure out the size of the bounding box vehicle_id = vsp.FindContainersWithName('Vehicle')[0] xlen = vsp.GetParmVal(vsp.FindParm(vehicle_id, "X_Len", "BBox")) ylen = vsp.GetParmVal(vsp.FindParm(vehicle_id, "Y_Len", "BBox")) zlen = vsp.GetParmVal(vsp.FindParm(vehicle_id, "Z_Len", "BBox")) # Max length max_len = np.max([xlen, ylen, zlen]) far_length = 10. * max_len vsp.SetCFDMeshVal(vsp.CFD_FAR_SIZE_ABS_FLAG, 1) vsp.SetCFDMeshVal(vsp.CFD_FAR_LENGTH, far_length) vsp.SetCFDMeshVal(vsp.CFD_FAR_WIDTH, far_length) vsp.SetCFDMeshVal(vsp.CFD_FAR_HEIGHT, far_length) vsp.SetCFDMeshVal(vsp.CFD_FAR_MAX_EDGE_LEN, max_len) vsp.SetCFDMeshVal(vsp.CFD_GROWTH_RATIO, growth_ratio) if growth_limiting_flag == True: vsp.SetCFDMeshVal(vsp.CFD_LIMIT_GROWTH_FLAG, 1.0) # Set the max edge length so we have on average 50 elements per chord length MAC = geometry.wings.main_wing.chords.mean_aerodynamic min_len = MAC / 50. vsp.SetCFDMeshVal(vsp.CFD_MAX_EDGE_LEN, min_len) # vsp.AddDefaultSources() set_sources(geometry) vsp.Update() vsp.WriteVSPFile(tag + '_premesh.vsp3') print('Starting mesh for ' + tag + ' (This may take several minutes)') ti = time.time() vsp.ComputeCFDMesh(set_int, file_type) tf = time.time() dt = tf - ti print('VSP meshing for ' + tag + ' completed in ' + str(dt) + ' s')