Edit me

The following is an aerodynamic optimization case for the MD 30N30P multi-element Airfoil

Case: Multi-element Airfoil 
Geometry: MD 30N30P 
Objective function: Lift coefficient
Design variables: 
    20 FFD points for the main element shape
    Pitch variables for the slat and flap
    Translation variables for the slat and flap
    Angle of attack
    27 in total 
Constraints: Drag coefficient
Mach number: 0.2
Reynolds number: 4.5 million
Mesh cells: ~13,569
Solver: DARhoSimpleFoam

Fig. 1. Mesh and FFD points for the 30N30P multi-element airfoil

In this tutorial, we set up three FFD blocks (see Fig.1) that cover the slat (vol=0), main airfoil (vol=1), and flap (vol=2). Check the genFFD.py file in the FFD folder. We use the snappyHexMesh utility in OpenFOAM to generate the mesh.

In terms of the design variables setup, we use 20 FFD points to change the shape of the main airfoil element. Then, we use the following functions to rotate and translate the slat and flap:

def twistslat(val, geo):
    for i in range(2):
        geo.rot_z["slatAxis"].coef[i] = -val[0]

def translateslat(val, geo):
    C = geo.extractCoef("slatAxis")
    dx = val[0]
    dy = val[1]
    for i in range(len(C)):
        C[i, 0] = C[i, 0] + dx
    for i in range(len(C)):
        C[i, 1] = C[i, 1] + dy
    geo.restoreCoef(C, "slatAxis")

def twistflap(val, geo):
    for i in range(2):
        geo.rot_z["flapAxis"].coef[i] = -val[0]

def translateflap(val, geo):
    C = geo.extractCoef("flapAxis")
    dx = val[0]
    dy = val[1]
    for i in range(len(C)):
        C[i, 0] = C[i, 0] + dx
    for i in range(len(C)):
        C[i, 1] = C[i, 1] + dy
    geo.restoreCoef(C, "flapAxis")

The rotation and translation are achieved by rotating the reference axis, e.g., geo.rot_z[“slatAxis”].coef[i], or translating the coordinates of the reference axis, e.g., C[i, 0] = C[i, 0] + dx.

To use the above functions, we need to first define the reference axis for slat and flap. The reference axis for the slat is defined near its trailing edge while the reference axis for the flap is define near its leading edge.

# Slat refAxis
xSlat = [-0.027, -0.027]
ySlat = [-0.109, -0.109]
zSlat = [0.0, 0.1]
cSlat = pySpline.Curve(x=xSlat, y=ySlat, z=zSlat, k=2)
DVGeo.addRefAxis("slatAxis", curve=cSlat, axis="z", volumes=[0])
# Flap refAxis
xFlap = [0.875, 0.875]
yFlap = [0.014, 0.014]
zFlap = [0.0, 0.1]
cFlap = pySpline.Curve(x=xFlap, y=yFlap, z=zFlap, k=2)
DVGeo.addRefAxis("flapAxis", curve=cFlap, axis="z", volumes=[2])

For the objective function, we want to maximize the lift while keeping the drag constant.

# Add objective
optProb.addObj("CD", scale=1)
# Add physical constraints
optProb.addCon("CL", lower=CL_target, upper=CL_target, scale=1)

To run this case, first download tutorials and untar it. Then go to tutorials-main/30N30P_MultiElement_Airfoil and run the “preProcessing.sh” script to generate the mesh:

./preProcessing.sh

NOTE: the above command will just download the mesh. If you want to re-generate the mesh or change the mesh density, run preProcessing_reGenMesh.sh instead (the mesh generation process make take up to an hour). Then, use the following command to run the optimization with 4 CPU cores:

mpirun -np 4 python runScript.py 2>&1 | tee logOpt.txt

The case ran for 43 iterations. The initial lift coefficient is 3.416 and the optimized lift coefficient is 3.509 with an increase of 2.7%.

NOTE: By default, this case uses the Jacobian free option in daOptions: “adjJacobianOption”: “JacobianFree”. This means that you need to compile the AD version of OpenFOAM and DAFoam (see here). If you use the Docker image, they have been compiled so no additional action is needed. If you haven’t compiled the AD version, set this: “adjJacobianOption”: “JacobianFD”.

Fig. 2. Evolution of airfoil shape and velocity distribution