User-Provided Mesh Example

Here is an example script with a custom mesh provided as an array of coordinates. This should help you understand how meshes are defined in OpenAeroStruct and how to create them for your own custom planform shapes. This is an alternative to the helper-function approach described in Aerodynamic Optimization.

The following shows the portion of the example script in which the user provides the coordinates for the mesh. This example is for a wing with a kink and two distinct trapezoidal segments.

# -----------------------------------------------------------------------------
# CUSTOM MESH: Example mesh for a 2-segment wing with sweep
# -----------------------------------------------------------------------------

# Planform specifications
half_span = 12.0  # wing half-span in m
kink_location = 4.0  # spanwise location of the kink in m

root_chord = 6.0  # root chord in m
kink_chord = 3.0  # kink chord in m
tip_chord = 2.0  # tip chord in m

inboard_LE_sweep = 10.0  # inboard leading-edge sweep angle in deg
outboard_LE_sweep = -10.0  # outboard leading-edge sweep angle in deg

# Mesh specifications
nx = 5  # number of chordwise nodal points (should be odd)
ny_outboard = 9  # number of spanwise nodal points for the outboard segment
ny_inboard = 7  # number of spanwise nodal points for the inboard segment

# Initialize the 3-D mesh object. Indexing: Chordwise, spanwise, then the 3-D coordinates.
# We use ny_inboard+ny_outboard-1 because the 2 segments share the nodes where they connect.
mesh = np.zeros((nx, ny_inboard + ny_outboard - 1, 3))

# The form of this 3-D array can be confusing initially.
# For each node, we are providing the x, y, and z coordinates.
# x is streamwise, y is spanwise, and z is up.
# For example, the node for the leading edge at the tip would be specified as mesh[0, 0, :] = np.array([x, y, z]).
# And the node at the trailing edge at the root would be mesh[nx-1, ny-1, :] = np.array([x, y, z]).
# We only provide the right half of the wing here because we use symmetry.
# Print elements of the mesh to better understand the form.

####### THE Z-COORDINATES ######
# Assume no dihedral, so set the z-coordinate for all the points to 0.
mesh[:, :, 2] = 0.0

####### THE Y-COORDINATES ######
# Using uniform spacing for the spanwise locations of all the nodes within each of the two trapezoidal segments:
# Outboard
mesh[:, :ny_outboard, 1] = np.linspace(half_span, kink_location, ny_outboard)
# Inboard
mesh[:, ny_outboard : ny_outboard + ny_inboard, 1] = np.linspace(kink_location, 0, ny_inboard)[1:]

###### THE X-COORDINATES ######
# Start with the leading edge and create some intermediate arrays that we will use
x_LE = np.zeros(ny_inboard + ny_outboard - 1)

array_for_inboard_leading_edge_x_coord = np.linspace(0, kink_location, ny_inboard) * np.tan(
    inboard_LE_sweep / 180.0 * np.pi
)

array_for_outboard_leading_edge_x_coord = (
    np.linspace(0, half_span - kink_location, ny_outboard) * np.tan(outboard_LE_sweep / 180.0 * np.pi)
    + np.ones(ny_outboard) * array_for_inboard_leading_edge_x_coord[-1]
)

x_LE[:ny_inboard] = array_for_inboard_leading_edge_x_coord
x_LE[ny_inboard : ny_inboard + ny_outboard] = array_for_outboard_leading_edge_x_coord[1:]

# Then the trailing edge
x_TE = np.zeros(ny_inboard + ny_outboard - 1)

array_for_inboard_trailing_edge_x_coord = np.linspace(
    array_for_inboard_leading_edge_x_coord[0] + root_chord,
    array_for_inboard_leading_edge_x_coord[-1] + kink_chord,
    ny_inboard,
)

array_for_outboard_trailing_edge_x_coord = np.linspace(
    array_for_outboard_leading_edge_x_coord[0] + kink_chord,
    array_for_outboard_leading_edge_x_coord[-1] + tip_chord,
    ny_outboard,
)

x_TE[:ny_inboard] = array_for_inboard_trailing_edge_x_coord
x_TE[ny_inboard : ny_inboard + ny_outboard] = array_for_outboard_trailing_edge_x_coord[1:]

# # Quick plot to check leading and trailing edge x-coords
# plt.plot(x_LE, np.arange(0, ny_inboard+ny_outboard-1), marker='*')
# plt.plot(x_TE, np.arange(0, ny_inboard+ny_outboard-1), marker='*')
# plt.show()
# exit()

for i in range(0, ny_inboard + ny_outboard - 1):
    mesh[:, i, 0] = np.linspace(np.flip(x_LE)[i], np.flip(x_TE)[i], nx)

# -----------------------------------------------------------------------------
# END MESH
# -----------------------------------------------------------------------------

The following shows a visualization of the mesh.

../_images/two_part_mesh.png

The complete script for the optimization is as follows. Make sure you go through the Aerostructural Optimization with Wingbox before trying to understand this setup.

There is plenty of room for improvement. A finer mesh and a tighter optimization tolerance should be used.