Blended Wing Body with CFD

Blended Wing Body using CFD

The purpose of this tutorial is to show a user how to generate geometry, mesh, and run CFD via SU2 from SUAVE. We assume you are familiar with SUAVE and have some CFD experience already. For the design example we an unconventional configuration, a Blended Wing Body (BWB). The BWB used here is most similar to the Boeing BWB-450 by Liebeck.

There are several ways this tutorial could be useful. First it shows how geometry is generated in SUAVE and sent to OpenVSP. Secondly it shows the chain of tools from geometry generation in OpenVSP, to mesh generation using GMSH, to CFD using SU2. Finally, if you already have CFD or other aerodynamic data you will learn how to input it into SUAVE directly.

Other Software You need

For the full tool chain 3 additional pieces of software are needed. These include OpenVSP, GMSH, and SU2. Fortunately for you, these are all open source and freely available online.

First, you will need OpenVSP compiled with Python support. Unfortunately, you cannot use the precompiled binary versions of OpenVSP. You must compile it from scratch. This tutorial was tested with OpenVSP 3.9. If you would like to use 3.11, you will need to add this code at line 30 in trunk/SUAVE/Input_Output/OpenVSP/get_vsp_areas.py. It will be updated in a later release:


if line == '\n':
    break

Second, you must install GMSH. In this case you can use the precompiled binaries. Make sure to add GMSH to your command line path, so it can be called from a terminal.

Finally, you need to install SU2. If you want parallel support then you must also compile the code. Otherwise, you can the precompiled versions.

Vehicle Setup

Here is the first time look at multi-segment wings. With multi-segments we can tune in more complex shapes such as cranks, yehudis, and winglets. We can also specify an airfoil to the wing or a wing segment. However, if you do not specify an airfoil it will default to a symmetrical airfoil profile.

This BWB is broken down into 7 segments. We first specify the top level overal parameters of the wing and then add each segment sequentially as shown below.




    # ------------------------------------------------------------------        
    #   Main Wing
    # ------------------------------------------------------------------        

    wing = SUAVE.Components.Wings.Main_Wing()
    wing.tag = 'main_wing'

    wing.aspect_ratio            = 289.**2 / (7840. * 2)
    wing.thickness_to_chord      = 0.15
    wing.taper                   = 0.0138
    wing.span_efficiency         = 0.95
    
    wing.spans.projected         = 289.0 * Units.feet    

    wing.chords.root             = 145.0 * Units.feet
    wing.chords.tip              = 3.5  * Units.feet
    wing.chords.mean_aerodynamic = 86. * Units.feet

    wing.areas.reference         = 15680. * Units.feet**2   
    wing.sweeps.quarter_chord    = 33. * Units.degrees

    wing.twists.root             = 0.0 * Units.degrees
    wing.twists.tip              = 0.0 * Units.degrees
    wing.dihedral                = 2.5 * Units.degrees

    wing.origin                  = [0.,0.,0]
    wing.aerodynamic_center      = [0,0,0] 

    wing.vertical                = False
    wing.symmetric               = True
    wing.high_lift               = True

    wing.dynamic_pressure_ratio  = 1.0

    segment = SUAVE.Components.Wings.Segment()
    segment.tag                   = 'section_1'
    segment.percent_span_location = 0.0
    segment.twist                 = 0. * Units.deg
    segment.root_chord_percent    = 1.
    segment.dihedral_outboard     = 0. * Units.degrees
    segment.sweeps.quarter_chord  = 30.0 * Units.degrees
    segment.thickness_to_chord    = 0.165 
    wing.Segments.append(segment)    
    
    segment = SUAVE.Components.Wings.Segment()
    segment.tag                   = 'section_2'
    segment.percent_span_location = 0.052
    segment.twist                 = 0. * Units.deg
    segment.root_chord_percent    = 0.921
    segment.dihedral_outboard     = 0.   * Units.degrees
    segment.sweeps.quarter_chord  = 52.5 * Units.degrees
    segment.thickness_to_chord    = 0.167    
    wing.Segments.append(segment)   

    segment = SUAVE.Components.Wings.Segment()
    segment.tag                   = 'section_3'
    segment.percent_span_location = 0.138
    segment.twist                 = 0. * Units.deg
    segment.root_chord_percent    = 0.76
    segment.dihedral_outboard     = 1.85 * Units.degrees
    segment.sweeps.quarter_chord  = 36.9 * Units.degrees  
    segment.thickness_to_chord    = 0.171    
    wing.Segments.append(segment)   
    
    segment = SUAVE.Components.Wings.Segment()
    segment.tag                   = 'section_4'
    segment.percent_span_location = 0.221
    segment.twist                 = 0. * Units.deg
    segment.root_chord_percent    = 0.624
    segment.dihedral_outboard     = 1.85 * Units.degrees
    segment.sweeps.quarter_chord  = 30.4 * Units.degrees    
    segment.thickness_to_chord    = 0.175
    wing.Segments.append(segment)       
    
    segment = SUAVE.Components.Wings.Segment()
    segment.tag                   = 'section_5'
    segment.percent_span_location = 0.457
    segment.twist                 = 0. * Units.deg
    segment.root_chord_percent    = 0.313
    segment.dihedral_outboard     = 1.85  * Units.degrees
    segment.sweeps.quarter_chord  = 30.85 * Units.degrees
    segment.thickness_to_chord    = 0.118
    wing.Segments.append(segment)       
    
    segment = SUAVE.Components.Wings.Segment()
    segment.tag                   = 'section_6'
    segment.percent_span_location = 0.568
    segment.twist                 = 0. * Units.deg
    segment.root_chord_percent    = 0.197
    segment.dihedral_outboard     = 1.85 * Units.degrees
    segment.sweeps.quarter_chord  = 34.3 * Units.degrees
    segment.thickness_to_chord    = 0.10
    wing.Segments.append(segment)     
    
    segment = SUAVE.Components.Wings.Segment()
    segment.tag                   = 'section_7'
    segment.percent_span_location = 0.97
    segment.twist                 = 0. * Units.deg
    segment.root_chord_percent    = 0.086
    segment.dihedral_outboard     = 73. * Units.degrees
    segment.sweeps.quarter_chord  = 55. * Units.degrees
    segment.thickness_to_chord    = 0.10
    wing.Segments.append(segment)      

    # add to vehicle
    vehicle.append_component(wing)

When we specify the configuration then we can write the OpenVSP file. If there are multiple configurations then you will need multiple writes to OpenVSP. We highly suggest you only have one configuration to minimize computational costs.


# ----------------------------------------------------------------------
#   Define the Configurations
# ---------------------------------------------------------------------

def configs_setup(vehicle):

    # ------------------------------------------------------------------
    #   Initialize Configurations
    # ------------------------------------------------------------------

    configs = SUAVE.Components.Configs.Config.Container()

    base_config = SUAVE.Components.Configs.Config(vehicle)
    base_config.tag = 'base'
    configs.append(base_config)
    
    write(vehicle,base_config.tag) 


    # done!
    return configs

Analyses Setup

Here we setup the SU2 analysis. We left commented out the parallel setting if you want to parallelize the computation along with the number of processors to use.

The next setting which is commented out is the input file for pregenerated CFD data. We have included an example of this, even if you don’t have SU2 and GMSH installed you can still run this file to generate the surrogate and run the mission.

After that we specify the points we want generate the surrogate on. Default values are within SUAVE, but we suggest the user specify their own Mach numbers and angles of attack.

Next we use a trick to refine the mesh. This is optional. We change the source parameters at specific segments. Here we make the sources more compact which refines the mesh at the root area.



    # ------------------------------------------------------------------
    #  Aerodynamics Analysis
    aerodynamics = SUAVE.Analyses.Aerodynamics.SU2_Euler()
    aerodynamics.geometry = vehicle
    
    #aerodynamics.process.compute.lift.inviscid.settings.parallel   = True
    #aerodynamics.process.compute.lift.inviscid.settings.processors = 12  
    #aerodynamics.process.compute.lift.inviscid.training_file       = 'base_data.txt'
    aerodynamics.settings.drag_coefficient_increment = 0.0000
    
    aerodynamics.process.compute.lift.inviscid.training.Mach             = np.array([.3, .5, .7, .85]) 
    aerodynamics.process.compute.lift.inviscid.training.angle_of_attack  = np.array([0.,3.,6.]) * Units.deg    
    
    wing_segments = vehicle.wings.main_wing.Segments
    wing_segments.section_1.vsp_mesh = Data()
    wing_segments.section_1.vsp_mesh.inner_radius  = 4.
    wing_segments.section_1.vsp_mesh.outer_radius  = 4.
    wing_segments.section_1.vsp_mesh.inner_length  = .14
    wing_segments.section_1.vsp_mesh.outer_length  = .14
    
    wing_segments.section_2.vsp_mesh = Data()
    wing_segments.section_2.vsp_mesh.inner_radius  = 4.
    wing_segments.section_2.vsp_mesh.outer_radius  = 4.
    wing_segments.section_2.vsp_mesh.inner_length  = .14
    wing_segments.section_2.vsp_mesh.outer_length  = .14
    
    wing_segments.section_3.vsp_mesh = Data()
    wing_segments.section_3.vsp_mesh.inner_radius  = 4.
    wing_segments.section_3.vsp_mesh.outer_radius  = 4.
    wing_segments.section_3.vsp_mesh.inner_length  = .14
    wing_segments.section_3.vsp_mesh.outer_length  = .14
    
    wing_segments.section_4.vsp_mesh = Data()
    wing_segments.section_4.vsp_mesh.inner_radius  = 4.
    wing_segments.section_4.vsp_mesh.outer_radius  = 2.8
    wing_segments.section_4.vsp_mesh.inner_length  = .14
    wing_segments.section_4.vsp_mesh.outer_length  = .14      
    
    analyses.append(aerodynamics)


The rest of the code runs as usual. However, we add in an extra plot which shows the surrogate and the points interpolated.