Example Usage

This guide demonstrate how to use the basic capabilities of this package. Further information about using this package may be found in the Public API.

Loading Airfoil Geometry

Before any analysis can be performed, we need to load our airfoil geometry into XFOIL. This can be accomplished using the set_coordinates function.

using Xfoil, Plots, Printf
pyplot()

# read airfoil coordinates from a file
x, y = open("naca2412.dat", "r") do f
    x = Float64[]
    y = Float64[]
    for line in eachline(f)
        entries = split(chomp(line))
        push!(x, parse(Float64, entries[1]))
        push!(y, parse(Float64, entries[2]))
    end
    x, y
end

# load airfoil coordinates into XFOIL
Xfoil.set_coordinates(x,y)

# plot the airfoil geometry
scatter(x, y, label="", framestyle=:none, aspect_ratio=1.0, show=true)

Refining the Airfoil Geometry

It is often a good idea to refine airfoil discretizations prior to performing analyses using XFOIL. This may be accomplished using the pane function.

# repanel using XFOIL's `PANE` command
xr, yr = Xfoil.pane()

# plot the refined airfoil geometry
scatter(xr, yr, label="", framestyle=:none, aspect_ratio=1.0, show=true)

Defining Operating Conditions

We now need to define some operating conditions for our analysis. For inviscid analyses, only the angle of attack is required. For viscous analyses, the Reynolds number must also be specified.

# set operating conditions
alpha = -9:1:14 # range of angle of attacks, in degrees
re = 1e5 # Reynolds number

Airfoil Analysis

The solve_alpha function may now be used to perform an analysis to obtain the airfoil coefficients $c_l$, $c_d$, $c_{d_p}$, and $c_m$. Note that $c_{d_p}$ is profile drag. Skin friction drag may be obtained by subtracting the profile drag coefficient from the total drag coefficient i.e., $c_{d_f} = c_d - c_{d_p}$.

# initialize outputs
n_a = length(alpha)
c_l = zeros(n_a)
c_d = zeros(n_a)
c_dp = zeros(n_a)
c_m = zeros(n_a)
converged = zeros(Bool, n_a)

# determine airfoil coefficients across a range of angle of attacks
for i = 1:n_a
    c_l[i], c_d[i], c_dp[i], c_m[i], converged[i] = Xfoil.solve_alpha(alpha[i], re; iter=100, reinit=true)
end

# print results
println("Angle\t\tCl\t\tCd\t\tCm\t\tConverged")
for i = 1:n_a
    @printf("%8f\t%8f\t%8f\t%8f\t%d\n",alpha[i],c_l[i],c_d[i],c_m[i],converged[i])
end

# plot results
plot(alpha, c_l, label="", xlabel="Angle of Attack (degrees)", ylabel="Lift Coefficient", show=true)
plot(alpha, c_d, label="", xlabel="Angle of Attack (degrees)", ylabel="Drag Coefficient",
    overwrite_figure=false, show=true)
plot(alpha, c_m, label="", xlabel="Angle of Attack (degrees)", ylabel="Moment Coefficient",
    overwrite_figure=false, show=true)
Angle		Cl		Cd		Cm		Converged
-9.000000	-0.650846	0.064745	-0.049415	1
-8.000000	-0.662429	0.046357	-0.043245	1
-7.000000	-0.635637	0.034118	-0.035047	1
-6.000000	-0.565933	0.026936	-0.029727	1
-5.000000	-0.479794	0.022554	-0.025883	1
-4.000000	-0.390118	0.019767	-0.023503	1
-3.000000	-0.301726	0.018318	-0.021741	1
-2.000000	-0.150388	0.017026	-0.030251	1
-1.000000	0.071766	0.017390	-0.049186	1
0.000000	0.263166	0.016828	-0.065553	1
1.000000	0.404422	0.015968	-0.069629	1
2.000000	0.512138	0.015591	-0.066432	1
3.000000	0.611810	0.015749	-0.061841	1
4.000000	0.708466	0.016397	-0.057255	1
5.000000	0.804453	0.017169	-0.052536	1
6.000000	0.893460	0.018133	-0.046931	1
7.000000	0.969537	0.019458	-0.039801	1
8.000000	1.020497	0.023512	-0.030233	1
9.000000	1.087529	0.029120	-0.023964	1
10.000000	1.173720	0.035266	-0.020845	1
11.000000	1.242721	0.043276	-0.016251	1
12.000000	1.256900	0.054220	-0.007123	1
13.000000	1.197794	0.066990	0.003651	1
14.000000	0.849596	0.148350	-0.038913	1

Note that the order in which viscous analyses are performed matters since XFOIL uses boundary layer parameters corresponding to the last previously converged solution as an initial guess when solving for the current boundary layer parameters. This behavior can be disabled by passing the keyword argument pair reinit=true to solve_alpha

Sensitivity Analysis

Suppose we want to find the derivative of $c_l$, $c_d$, and $c_m$ with respect to the angle of attack. One approach to calculate these derivatives would be to use the finite difference method.

using Xfoil, Printf

# read airfoil into XFOIL
open("naca2412.dat", "r") do f
    x = Float64[]
    y = Float64[]
    for line in eachline(f)
        entries = split(chomp(line))
        push!(x, parse(Float64, entries[1]))
        push!(y, parse(Float64, entries[2]))
    end
    Xfoil.set_coordinates(x,y)
end

# repanel using XFOIL's `PANE` command
Xfoil.pane()

# set operating conditions
alpha = -9:1:14
re = 1e5
mach = 0.0

# set step size
h = 1e-6

# initialize outputs
n_a = length(alpha)
c_l_a = zeros(n_a)
c_d_a = zeros(n_a)
c_dp_a = zeros(n_a)
c_m_a = zeros(n_a)
converged = zeros(Bool, n_a)

for i = 1:n_a
    c_l1, c_d1, c_dp1, c_m1, converged[i] = Xfoil.solve_alpha(alpha[i], re; mach, iter=100, reinit=true)
    c_l2, c_d2, c_dp2, c_m2, converged[i] = Xfoil.solve_alpha(alpha[i]+h, re; mach, iter=100, reinit=true)
    c_l_a[i] = (c_l2 - c_l1)/h * 180/pi
    c_d_a[i] = (c_d2 - c_d1)/h * 180/pi
    c_m_a[i] = (c_m2 - c_m1)/h * 180/pi
end

# print results
println("Angle\t\tdClda\t\tdCdda\t\tdCmda\t\tConverged")
for i = 1:n_a
  @printf("%8f\t%8f\t%8f\t%8f\t%d\n",alpha[i],c_l_a[i],c_d_a[i],c_m_a[i],converged[i])
end
Angle		dClda		dCdda		dCmda		Converged
-9.000000	4.368002	-1.420669	0.050753	1
-8.000000	-0.595924	-0.987825	0.492841	1
-7.000000	3.092874	-0.537248	0.414648	1
-6.000000	4.582598	-0.250074	0.264979	1
-5.000000	5.114411	-0.079249	0.248431	1
-4.000000	5.137603	-0.072158	0.141209	1
-3.000000	5.279715	-0.026029	0.058558	1
-2.000000	8.234179	-0.042702	-0.136227	1
-1.000000	10.039118	-0.015135	-0.885506	1
0.000000	12.644209	-0.082045	-1.124300	1
1.000000	9.212672	-0.093061	-0.324636	1
2.000000	3.641180	0.067090	0.521405	1
3.000000	6.896780	-0.016704	0.126283	1
4.000000	5.982546	0.027492	0.212131	1
5.000000	6.180839	0.018997	0.228296	1
6.000000	5.383315	0.055856	0.295899	1
7.000000	3.761211	0.103208	0.470835	1
8.000000	2.940949	0.295101	0.506869	1
9.000000	4.686078	0.293471	0.276193	1
10.000000	5.637746	0.418566	0.028201	1
11.000000	3.889931	0.448186	0.215136	1
12.000000	-1.889114	0.640222	0.760204	1
13.000000	-5.154260	0.958681	0.299039	1
14.000000	1.558546	1.051198	-0.058802	1

A better approach might be to use the complex step method. To use this approach, however, we must use the complex-step enabled version of XFOIL provided by this package.

The complex-step version of each function is denoted by appending _cs to each function name. Note that there is no interaction between the two versions of XFOIL wrapped by this package, so if you wish to use the complex step version of the code you must append _cs to all function names.

For the complex step method to work, we also need to ensure that the imaginary portion of variables from previous iterations does not affect the solution for the current iteration. This may be achieved by setting reinit=true when calling solve_alpha.

using Xfoil, Printf

# read airfoil into XFOIL
open("naca2412.dat", "r") do f
    x = Float64[]
    y = Float64[]
    for line in eachline(f)
        entries = split(chomp(line))
        push!(x, parse(Float64, entries[1]))
        push!(y, parse(Float64, entries[2]))
    end
    Xfoil.set_coordinates_cs(x,y)
end

# repanel using XFOIL's `PANE` command
Xfoil.pane_cs()

# set operating conditions
alpha = -9:1:14
re = 1e5
mach = 0.0

# set step size
h = 1e-12im

# initialize outputs
n_a = length(alpha)
c_l_a = zeros(n_a)
c_d_a = zeros(n_a)
c_dp_a = zeros(n_a)
c_m_a = zeros(n_a)
converged = zeros(Bool, n_a)

for i = 1:n_a
    c_l, c_d, c_dp, c_m, converged[i] = Xfoil.solve_alpha_cs(alpha[i]+h, re; mach, iter=100, reinit=true)
    c_l_a[i] = imag(c_l)/imag(h) * 180/pi
    c_d_a[i] = imag(c_d)/imag(h) * 180/pi
    c_m_a[i] = imag(c_m)/imag(h) * 180/pi
end

# print results
println("Angle\t\tdClda\t\tdCdda\t\tdCmda\t\tConverged")
for i = 1:n_a
  @printf("%8f\t%8f\t%8f\t%8f\t%d\n",alpha[i],c_l_a[i],c_d_a[i],c_m_a[i],converged[i])
end
Angle		dClda		dCdda		dCmda		Converged
-9.000000	4.368078	-1.420675	0.050750	1
-8.000000	-0.595926	-0.987825	0.492841	1
-7.000000	3.094020	-0.537099	0.414477	1
-6.000000	4.583204	-0.250262	0.264980	1
-5.000000	5.114413	-0.079250	0.248431	1
-4.000000	5.137602	-0.072158	0.141209	1
-3.000000	5.279711	-0.026030	0.058559	1
-2.000000	8.234162	-0.042703	-0.136221	1
-1.000000	10.039099	-0.015135	-0.885503	1
0.000000	12.644207	-0.082045	-1.124300	1
1.000000	9.212668	-0.093061	-0.324636	1
2.000000	3.641152	0.067091	0.521409	1
3.000000	6.896776	-0.016704	0.126284	1
4.000000	5.982540	0.027492	0.212131	1
5.000000	6.180833	0.018997	0.228296	1
6.000000	5.383310	0.055856	0.295900	1
7.000000	3.761214	0.103208	0.470834	1
8.000000	2.940954	0.295101	0.506868	1
9.000000	4.686077	0.293470	0.276194	1
10.000000	5.637742	0.418565	0.028201	1
11.000000	3.889928	0.448186	0.215137	1
12.000000	-1.889107	0.640223	0.760202	1
13.000000	-5.154275	0.958683	0.299040	1
14.000000	1.555321	1.049194	-0.058519	1

Note that since XFOIL was not originally designed for sensitivity analysis, there is a distinct possibility that sensitivities may be non-physical. We therefore always recommend checking that computed sensitivities are realistic. Sometimes adjusting the number of panels or the step size will fix the computed sensitivies, but if all else fails a coarse step size (e.g. 1.0 degree) may be used with central finite differencing to artificially smooth the computed sensitivities.

Automated Angle of Attack Sweep

For performing angle of attack sweeps, the function alpha_sweep may also be used.

using Xfoil, Printf

# extract geometry
x = Float64[]
y = Float64[]

f = open("naca2412.dat", "r")

for line in eachline(f)
    entries = split(chomp(line))
    push!(x, parse(Float64, entries[1]))
    push!(y, parse(Float64, entries[2]))
end

close(f)

# set operating conditions
alpha = -9:1:14
re = 1e5

c_l, c_d, c_dp, c_m, converged = Xfoil.alpha_sweep(x, y, alpha, re, iter=100, zeroinit=false, printdata=true, reinit=true)

Angle		Cl		Cd		Cm		Converged
-9.000000	-0.650846	0.064745	-0.049415	1
-8.000000	-0.662429	0.046357	-0.043245	1
-7.000000	-0.635637	0.034118	-0.035047	1
-6.000000	-0.565933	0.026936	-0.029727	1
-5.000000	-0.479794	0.022554	-0.025883	1
-4.000000	-0.390118	0.019767	-0.023503	1
-3.000000	-0.301726	0.018318	-0.021741	1
-2.000000	-0.150388	0.017026	-0.030251	1
-1.000000	0.071766	0.017390	-0.049186	1
0.000000	0.263166	0.016828	-0.065553	1
1.000000	0.404422	0.015968	-0.069629	1
2.000000	0.512138	0.015591	-0.066432	1
3.000000	0.611810	0.015749	-0.061841	1
4.000000	0.708466	0.016397	-0.057255	1
5.000000	0.804453	0.017169	-0.052536	1
6.000000	0.893460	0.018133	-0.046931	1
7.000000	0.969537	0.019458	-0.039801	1
8.000000	1.020497	0.023512	-0.030233	1
9.000000	1.087529	0.029120	-0.023964	1
10.000000	1.173720	0.035266	-0.020845	1
11.000000	1.242721	0.043276	-0.016251	1
12.000000	1.256900	0.054220	-0.007123	1
13.000000	1.197794	0.066990	0.003651	1
14.000000	0.849596	0.148350	-0.038913	1

A version of alpha_sweep has also been implemented for use with the complex step version of XFOIL.

using Xfoil, Printf

# extract geometry
x = Float64[]
y = Float64[]

f = open("naca2412.dat", "r")

for line in eachline(f)
    entries = split(chomp(line))
    push!(x, parse(Float64, entries[1]))
    push!(y, parse(Float64, entries[2]))
end

close(f)

# set operating conditions
alpha = -9:1:14
re = 1e5
mach = 0.0

# set step size
h = 1e-20im

c_l, c_d, c_dp, c_m, converged = Xfoil.alpha_sweep_cs(x, y, alpha .+ h,
    re, mach=mach, iter=100, zeroinit=false, printdata=true, reinit=true)

println("Angle\t\tdClda\t\tdCdda\t\tdCmda\t\tConverged")
for i = 1:length(alpha)
    @printf("%8f\t%8f\t%8f\t%8f\t%d\n", alpha[i], imag(c_l[i])/imag(h)*180/pi, imag(c_d[i])/imag(h)*180/pi, imag(c_m[i])/imag(h)*180/pi, converged[i])
end

Angle		Cl		Cd		Cm		Converged
-9.000000	-0.650846	0.064745	-0.049415	1
-8.000000	-0.662429	0.046357	-0.043245	1
-7.000000	-0.635637	0.034118	-0.035047	1
-6.000000	-0.565933	0.026936	-0.029727	1
-5.000000	-0.479794	0.022554	-0.025883	1
-4.000000	-0.390118	0.019767	-0.023503	1
-3.000000	-0.301726	0.018318	-0.021741	1
-2.000000	-0.150388	0.017026	-0.030251	1
-1.000000	0.071766	0.017390	-0.049186	1
0.000000	0.263166	0.016828	-0.065553	1
1.000000	0.404422	0.015968	-0.069629	1
2.000000	0.512138	0.015591	-0.066432	1
3.000000	0.611810	0.015749	-0.061841	1
4.000000	0.708466	0.016397	-0.057255	1
5.000000	0.804453	0.017169	-0.052536	1
6.000000	0.893460	0.018133	-0.046931	1
7.000000	0.969537	0.019458	-0.039801	1
8.000000	1.020497	0.023512	-0.030233	1
9.000000	1.087529	0.029120	-0.023964	1
10.000000	1.173720	0.035266	-0.020845	1
11.000000	1.242721	0.043276	-0.016251	1
12.000000	1.256900	0.054220	-0.007123	1
13.000000	1.197794	0.066990	0.003651	1
14.000000	0.849596	0.148350	-0.038913	1
Angle		dClda		dCdda		dCmda		Converged
-9.000000	4.368078	-1.420675	0.050750	1
-8.000000	-0.595926	-0.987825	0.492841	1
-7.000000	3.094020	-0.537099	0.414477	1
-6.000000	4.583204	-0.250262	0.264980	1
-5.000000	5.114413	-0.079250	0.248431	1
-4.000000	5.137602	-0.072158	0.141209	1
-3.000000	5.279711	-0.026030	0.058559	1
-2.000000	8.234162	-0.042703	-0.136221	1
-1.000000	10.039099	-0.015135	-0.885503	1
0.000000	12.644207	-0.082045	-1.124300	1
1.000000	9.212668	-0.093061	-0.324636	1
2.000000	3.641152	0.067091	0.521409	1
3.000000	6.896776	-0.016704	0.126284	1
4.000000	5.982540	0.027492	0.212131	1
5.000000	6.180833	0.018997	0.228296	1
6.000000	5.383310	0.055856	0.295900	1
7.000000	3.761214	0.103208	0.470834	1
8.000000	2.940954	0.295101	0.506868	1
9.000000	4.686077	0.293470	0.276194	1
10.000000	5.637742	0.418565	0.028201	1
11.000000	3.889928	0.448186	0.215137	1
12.000000	-1.889107	0.640223	0.760202	1
13.000000	-5.154275	0.958683	0.299040	1
14.000000	1.555321	1.049194	-0.058519	1