In the previous chapter, we have learned how to conduct optimization for 2D airfoils. This chapter will extend the optimization to a 3D wing called ADODG3. The following is a summary of the optimization.
Case: 3D Wing aerodynamic optimization Geometry: ADODG3 Wing Objective function: Drag coefficient (CD) Lift coefficient (CL): 0.375 Design variables: 120 FFD points, twist, and angle of attack (total: 126). Constraints: volume, thickness, LE/TE, and the lift coefficient (total number: 764) Mach number: 0.3 Reynolds number: 0.22 million Mesh cells: ~435,000 Solver: DARhoSimpleFoam
Fig. 1. Mesh and FFD points for the ADODG3 wing.
The “runScript.py” is based on the one used in the NACA0012-Subsonic case with the following modifications:
-
In “meshOptions”, we set only one symmetry plane at z=0, instead of two symmetry planes used in the 2D airfoil case.
-
We add a reference axis for the twist variable, and get the number of spanwise FFD point sections as
nRefAxPts
. In other words, if we have 10 FFD points in the spanwise direction, we will have 10 twist variables. Here we need to give a name to this axis"wingAxis"
, and this name will be used later when we define the twist function.xFraction=0.25
means the axis is placed at the 25% chordwise (x) direction in the FFD box.alignIndex="k"
means the axis rotate with respect to the k index (z axis) in the FFD box. Here we assume the FFD’s i, j, and k indices aligns with the x, y, and z directions.
nRefAxPts = self.geometry.nom_addRefAxis(name="wingAxis", xFraction=0.25, alignIndex="k")
- We define a
twist
function to change the twist at these spanwise FFD sections. Hereval
, andgeo
are the two inputs.val
is an array that has all the twist values the optimizer want to use, and we need to set these values to thegeo
object.geo
is a pyGeo class, andgeo.rot_z["wingAxis"]
means the rotation (twist) angle for thewingAxis
defined above. Herecoef[i]
is the ith spanwise twist angle. Note that we do NOT change the root twist (we already had angle of attack as design variable), so the first element inval
is the twist at the 2nd spanwise location. That is why we assignval[i-1]
tocoef[i]
. Also note that we use a minus sign forval
because we want to use nose-up as positive twist, while pyGeo uses the right-hand rule to determine the rotation direction (which is nose down).
def twist(val, geo):
for i in range(1, nRefAxPts):
geo.rot_z["wingAxis"].coef[i] = -val[i - 1]
- We call the following functions to add the twist design variable to the
geometry
component. Again, we have nRefAxPts-1 twists because we do not change the root twist.
self.geometry.nom_addGlobalDV(dvName="twist", value=np.array([0] * (nRefAxPts - 1)), func=twist)
- The leList and teList are similar to the NACA0012 case. They should be close to the leading and trailing edges but completely within the wing surface. We use them to define the volume and thickness constraints. Note that we need more than 3 point in the spanwise direction
nSpan=25
because the case is 3D. The rule of thumb is that we should use a only a slightly larger number of sample points (in this case is 25 by 30 in the span and chord wise directions) as the FFD points. Here we also add leading and trailing edge constraints, e.g.,nom_add_LETEConstraint
to fix the leading and trailing edges.volID
is the volume index for the FFD block. In this case we have only one block, sovolID=0
.faceID="iLow"
means we make link displacements for the first layer of FFDs in the ith (x) direction. The top and bottom FFD move in the opposite directions for i=0, such that the leading edge does not move. Similarly,faceID="iHigh"
means the last layer of FFDs in the ith direction.
leList = [[0.02, 0.0, 1e-3], [0.02, 0.0, 2.9]]
teList = [[0.95, 0.0, 1e-3], [0.95, 0.0, 2.9]]
self.geometry.nom_addThicknessConstraints2D("thickcon", leList, teList, nSpan=25, nChord=30)
self.geometry.nom_addVolumeConstraint("volcon", leList, teList, nSpan=25, nChord=30)
self.geometry.nom_add_LETEConstraint("lecon", volID=0, faceID="iLow")
self.geometry.nom_add_LETEConstraint("tecon", volID=0, faceID="iHigh")