In this tutorial we will perform an analysis of a wing/body configuration.
The model is based on the AGARD-B model1
(The sting is not included, and the aft-body is slightly modified for this tutorial.)
The model will be composed from 12 networks:
The geometry of the model is defined in terms of the body diameter $D$.
For simplicity, it will be set at $D=1$.
In [1]:
from pyPanair.preprocess import wgs_creator
wgs = wgs_creator.LaWGS("agardb_mod")
Next, we will create a Line object that defines the airfoil at the wing root.
The airfoil of the AGARD-B model is a circular-arc airfoil of 4% thickness.
A csv file has been prepared in advance.
The coordinates in circular_arc.csv are normalized, so when using the read_airfoil function, we shall set the expantion_ratio at 2.598.
By doing so, the x,z-coordinates of the resulting airfoil will be multiplied by 2.598.
In [2]:
root_airfoil = wgs_creator.read_airfoil("circular_arc.csv", y_coordinate=0.5, expansion_ratio=2.598)
In [3]:
%matplotlib notebook
import matplotlib.pyplot as plt
plt.plot(root_airfoil[:,0], root_airfoil[:,2], "s", mfc="None", mec="b")
plt.xlabel("x")
plt.ylabel("z")
plt.grid()
plt.show()
Since the apex of the nose is the origin, we'll need to shift the coordinates of the root_airfoil, so that the x-coordinate of its LE becomes 4.5.
This will be done with the shift method.
In [4]:
root_airfoil = root_airfoil.shift((4.5, 0., 0.))
The shift method will create a copy of root_airfoil and add (4.5, 0., 0.) to the xyz-coordinates of each point in the copy.
Next, we define the tip of the wing.
Even though the tip of the wing is a point, when defining it, we need to use the same number of points we used to define the wing root.
This means that we need to define a Line with 61 identical points.
This can be done easily with the replace method. By typing
In [5]:
tip_airfoil = root_airfoil.replace(x=7.098, y=2., z=0.)
a copy of root_airfoil will be created.
The xyz-coordinates of each point in the copy will be replaced by 2.598, 2., and 0., respectively.
The wing Network will be created and registered to wgs in the same way as tutorials 1 and 2.
In [6]:
wing = root_airfoil.linspace(tip_airfoil, num=30)
wgs.append_network("wing", wing, 1)
In [7]:
wing.plot_wireframe()
In [8]:
import numpy as np
n_nose = 8 # number of points in the line
x_nose = np.linspace(0., 3., num=n_nose) # x-coordinates of the line
y_nose = x_nose/3 * (1 - 1/9*(x_nose)**2 + 1/54*(x_nose)**3) # y-coordinates of the line
nose_line = wgs_creator.Line(np.zeros((n_nose, 3))) # create an array of zeros
nose_line[:,0] = x_nose
nose_line[:,1] = y_nose
fbody_p1 = wgs_creator.Point(nose_line[-1]) # point at the end of the nose_line
fbody_p2 = fbody_p1.replace(x=4.5)
fbody_line = fbody_p1.linspace(fbody_p2, num=4) # interpolate fbody_p1 and fbody_p2
nose_fbody_line = nose_line.concat(fbody_line) # concatenate nose_line & fbody_line
Other Lines that define the nose and fore-body will be created by rotating fbody_line.
The upper/lower surfaces can be created by typing
In [9]:
nose_fbody_up = list()
for i in np.linspace(0, 90, 7):
line = nose_fbody_line.rotx(rotcenter=nose_fbody_line[0], angle=i) # create a copy of nose_fbody_line and rotate it around the x-axis
nose_fbody_up.append(line)
nose_fbody_up = wgs_creator.Network(nose_fbody_up) # create a Network object from a list of Lines
nose_fbody_low = list()
for i in np.linspace(-90, 0, 7):
line = nose_fbody_line.rotx(rotcenter=nose_fbody_line[0], angle=i)
nose_fbody_low.append(line)
nose_fbody_low = wgs_creator.Network(nose_fbody_low)
wgs.append_network("n_fb_up", nose_fbody_up, 1)
wgs.append_network("n_fb_low", nose_fbody_low, 1)
In [10]:
nose_fbody_up.plot_wireframe()
In [11]:
nose_fbody_low.plot_wireframe()
In [12]:
wingroot_up, wingroot_low = root_airfoil.split_half()
mbody_line = wingroot_up.replace(z=0.)
mbody_up = list()
for i in np.linspace(90, 0, 7)[:-1]:
line = mbody_line.rotx((0,0,0), angle=i)
mbody_up.append(line)
mbody_up.append(wingroot_up)
mbody_up = wgs_creator.Network(mbody_up)
wgs.append_network("mbody_up", mbody_up, 1)
In [13]:
mbody_up.plot_wireframe()
Next, we define the lower surface.
In [14]:
wingroot_low = wingroot_low.flip()
mbody_low = list()
mbody_low.append(wingroot_low)
for i in np.linspace(0, -90, 7)[1:]:
line = mbody_line.rotx((0,0,0), angle=i)
mbody_low.append(line)
mbody_low = wgs_creator.Network(mbody_low)
wgs.append_network("mbody_low", mbody_low, 1)
In [15]:
mbody_low.plot_wireframe()
In the original AGARD-B model, the aft-body is a cylinder.
However, in this tutorial it will be a circular truncated cone, for the sake of learning how to attach body wakes.
First, we define the Line for the aft-body at z=0..
In [16]:
aft_body_p1 = wgs_creator.Point(root_airfoil[0]) # TE of the root airfoil
aft_body_p2 = aft_body_p1.shift((1.402, -0.05, 0.))
aft_body_line = aft_body_p1.linspace(aft_body_p2, num=6)
After that, we define the Networks for the upper and lower surfaces.
In [17]:
aft_body_up = list()
for i in np.linspace(0, 90, num=7):
line = aft_body_line.rotx((0,0,0), angle=i)
aft_body_up.append(line)
aft_body_up = wgs_creator.Network(aft_body_up)
wgs.append_network("abody_up", aft_body_up, 1)
In [18]:
aft_body_up.plot_wireframe()
In [19]:
aft_body_low = list()
for i in np.linspace(-90, 0, num=7):
line = aft_body_line.rotx((0,0,0), angle=i)
aft_body_low.append(line)
aft_body_low = wgs_creator.Network(aft_body_low)
wgs.append_network("abody_low", aft_body_low, 1)
In [20]:
aft_body_low.plot_wireframe()
The body base is a circle.
The arc is defined by edge3 from the Networks aft_body_up and aft_body_low
We'll use the edge method to create Line objects from these Networks.
In [21]:
body_base_line_up = aft_body_up.edge(3) # create a `Line` that corresponds to edge3 of the `Network` `body_base_up`
body_base_line_up = body_base_line_up.flip() # the order of points are reversed
body_base_line_low = aft_body_low.edge(3)
body_base_line_low = body_base_line_low.flip()
body_base_line = body_base_line_up.concat(body_base_line_low)
body_base_line2 = body_base_line.replace(x=8.5, y=0., z=0.) # create a `Line` with 13 identical points (the center of the circle)
body_base = body_base_line.linspace(body_base_line2, num=3)
wgs.append_network("bodybase", body_base, boun_type=5)
In [22]:
body_base.plot_wireframe()
In [23]:
wake_length = 2.3093 * 50
wing_wake = wing.make_wake(3, wake_length)
wgs.append_network("wingwake", wing_wake, 18)
The body base wake will be attached to edge3 of the Networks aft_body_up and aft_body_low.
The boundary type will be -18 (not 18).
In [24]:
body_base_wake_up = aft_body_up.make_wake(3, wake_length)
wgs.append_network("bbwake_up", body_base_wake_up, -18)
body_base_wake_low = aft_body_low.make_wake(3, wake_length)
wgs.append_network("bbwake_low", body_base_wake_low, -18)
The body-wing wake is a wake that connects the wing wake and body base wake.
The method make_bodywingwake is used to create this special type of wake.
We will attach the body-wing wake to edge4 of aft_body_up.
In [25]:
body_wake = aft_body_up.make_bodywingwake(4, wake_length)
wgs.append_network("bodywake", body_wake, 20)
In [26]:
body_wake.plot_wireframe()
Note that corner 1 of the body base wake must lie at the intersection of the wing TE and the inboard edge of the wing wake.
Also, the boundary type for a body wake is 20.
(Read 3.3.2.5 of Vol.2 of the user manual2 for more information on body wakes.)
Next, we create a stl file and check for errors.
In [27]:
wgs.create_stl()
Finally, we create input files for panin.
In [28]:
wgs.create_wgs()
wgs.create_aux(alpha=5., mach=1.4, cbar=2.3093, span=4., sref=6.928, xref=5.943, zref=0.)
In [29]:
from pyPanair.postprocess import read_ffmf
read_ffmf()
Out[29]:
According to reference 1, the aerodynamic coefficients for an AGARD-B model at an angle of attack of 5.0 degrees, mach number of 5.0, and a Reynolds number of around 10.4 million is,
$$\begin{align}C_L&=0.224 \\
C_D&=0.0435 \\
C_M&=-0.019\end{align}$$
The lift and drag coefficients are fairly close to the results from reference 1, considering the fact that we've slightly tampered with the shape of aft-body.
On the other hand, the pitching moment coefficient does not match with reference 1.
This may be due to multiple factors (e.g. modified aft-body, coarse paneling, etc.).
Visualization can be done in the same way as tutorials 1 and 2.
In [30]:
from pyPanair.postprocess import write_vtk, write_tec
write_vtk(n_wake=4)
write_tec()
In [31]:
import pandas as pd
from pyPanair.postprocess import calc_section_force
calc_section_force(aoa=5., mac=2.3093, rot_center=(5.943,0,0), casenum=1, networknum=1)
section_force = pd.read_csv("section_force.csv")
section_force
Out[31]:
In [32]:
section_force = section_force.dropna()
plt.plot(section_force.pos / 2, section_force.cl * section_force.chord, "s", mfc="None", mec="b")
plt.xlabel("spanwise position [normalized]")
plt.ylabel("$C_l$ * chord")
plt.grid()
plt.show()
This is the end of tutorial 3.
In [ ]: