Airfoil Geometry Manipulation Tools
Here we include the variety of methods for manipulating airfoil geometries in useful ways implemented in AirfoilTools.
Deconstruction
It is often convenient to deconstruct an airfoil into its upper and lower halves. The split_upper_lower
function makes this process straightforward.
using FLOWFoil.AirfoilTools
using Plots
using LaTeXStrings
x, y = naca4()
xl, xu, yl, yu = split_upper_lower(x, y)
plot(xl, yl; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Lower Side")
plot!(xu, yu; label="Upper Side")
FLOWFoil.AirfoilTools.split_upper_lower
— Functionsplit_upper_lower(x, y; idx::Integer=nothing)
Split the upper and lower halves of the airfoil coordinates.
Assumes leading edge point is at first minimum x value if idx
is not provided. Returns the upper and lower coordinates each with the leading edge point. Assumes airfoil is defined clockwise starting at the trailing edge.
Arguments
x::AbstractArray{Float}
: Vector of x coordinatesy::AbstractArray{Float}
: Vector of y coordinates
Keyword Arguments
idx::Integer
: optional index at which to split the coordinates
Returns
xl::AbstractArray{Float}
: Vector of lower half of x coordinatesxu::AbstractArray{Float}
: Vector of upper half of x coordinatesyl::AbstractArray{Float}
: Vector of lower half of y coordinatesyu::AbstractArray{Float}
: Vector of upper half of y coordinates
split_upper_lower(coordaintes; idx::Integer=nothing)
Split the upper and lower halves of the airfoil coordinates.
Assumes leading edge point is at first minimum x value if idx
is not provided. Returns the upper and lower coordinates each with the leading edge point. Assumes airfoil is defined clockwise starting at the trailing edge.
Arguments
coordinates::Matrix{Float}
: Matrix of [x y] coordinates
Keyword Arguments
idx::Integer
: optional index at which to split the coordinates
Returns
xl::AbstractArray{Float}
: View of lower half of x coordinatesxu::AbstractArray{Float}
: View of upper half of x coordinatesyl::AbstractArray{Float}
: View of lower half of y coordinatesyu::AbstractArray{Float}
: View of upper half of y coordinates
Translation
There are various transformation functions that can also be helpful in various situations.
We can flip an airfoil.
using FLOWFoil.AirfoilTools
using Plots
using LaTeXStrings
x, y = naca4()
plot(x, y; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Nominal")
plot!(flip!(copy(x)), y; label="x Flipped")
Note that this function both flips and translates. It's primarily useful for flipping the x coordinates. If you want to flip the y coordinate, applying a simple negative would be best
plot(x, y; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Nominal")
plot!(x, flip!(copy(y)); label="y Flipped")
plot!(x, -y; label="y Negated")
Another thing we could do is translate the airfoil so that the trailing edge is at y=0.
using FLOWFoil.AirfoilTools
using Plots
using LaTeXStrings
x, y = naca4()
y .+= 0.2
plot(x, y; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Nominal")
xy = [x y]
zero_y_te!(xy)
plot!(xy[:,1], xy[:,2]; label="TE zeroed")
This only vertically translates the geometry, it does not rotate things to put the leading edge on the axis as well if the geometry is rotated.
FLOWFoil.AirfoilTools.zero_y_te!
— Functionzero_y_te!(coordinates)
Places trailing edge on the x-axis.
Arguments
coordinates::Array{Float}
: Array of [x y] coordinates to be updated in place.
Rotation
We can also rotate airfoils about arbitrary points.
using FLOWFoil.AirfoilTools
using Plots
using LaTeXStrings
x, y = naca4()
plot(x, y; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Nominal")
xy = [x y]
angle = 10.0 # degrees
# rotate by angle about default point: (0,0)
rotate_coordinates!(xy, angle)
plot!(xy[:,1], xy[:,2], label="rotated about (0,0)")
# rotate again but about the trailing edge
xy2 = [x y]
rotate_coordinates!(xy2, angle; rotation_point=[1.0,0.0])
plot!(xy2[:,1], xy2[:,2], label="rotated about (1,0)")
FLOWFoil.AirfoilTools.rotate_coordinates!
— Functionrotate_coordinates!(coordinates, angle; rotation_point=[0.0; 0.0])
Rotate coordinates clockwise about rotation_point
by angle
in degrees.
Arguments
coordinates::Array{Float}
: Array of [x y] coordinates to be updated in place.angle::Float=0.0
: Angles, in degrees, by which to rotate the coordinates clockwise (positive angle will pitch airfoil up).
Keyword Arguments
rotation_point::AbstractArray{Float}=[0.0; 0.0]
: Array of [x y] position of point about which to perform rotation.
Normalization
It can also be convenient to normalize coordinates to have a chord length of one.
using FLOWFoil.AirfoilTools
using Plots
using LaTeXStrings
x, y = 2.0.*naca4()
plot(x, y; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Nominal")
normalize_coordinates!(x, y)
plot!(x, y; label="Normalized")
This function is designed to go from a nominal airfoil (length 1, leading edge and trailing edges on the axis, etc.) to something else. The operations are in the order: scale, rotate, then translate.
FLOWFoil.AirfoilTools.normalize_coordinates!
— Functionnormalize_coordinates!(coordinates)
Normalize airfoil to unit chord and shift leading edge to zero. Adjusts coordinates in place.
Arguments
coordinates::AbstractArray{Float}
: Array of [x y] coordinates
normalize_coordinates!(x, y)
Normalize airfoil to unit chord and shift leading edge to zero. Adjusts coordinates in place.
Arguments
x::Array{Float}
: Array of x coordinatesy::Array{Float}
: Array of y coordinates
Scale, Rotate, and Translate
Sometimes, we might want do several operations together, which we can with the position_coordinates!
function.
using FLOWFoil.AirfoilTools
using Plots
using LaTeXStrings
x, y = naca4()
plot(x, y; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Unscaled")
xy = [x y]
position_coordinates!(
xy; scale=0.8, angle=3.0, location=[-0.2; 0.1], rotation_point=[0.0; 0.0], flipped=true
)
plot!(xy[:, 1], xy[:, 2]; label="Re-positioned")
FLOWFoil.AirfoilTools.position_coordinates!
— Functionposition_coordinates!(coordinates, scale, angle, location)
Scale, Rotate, and Transform (in that order) airfoil coordinates.
Arguments
coordinates::Array{Float}
: Array of [x y] coordinates to be updated in place.
Keyword Arguments
scale::Float=1.0
: Value by which to scale coordinates.angle::Float=0.0
: Angles, in degrees, by which to rotate the coordinates clockwise (positive angle will pitch airfoil up).location::AbstractArray{Float}=[0.0; 0.0]
: Array of [x y] position of leading edge location.rotation_point::AbstractArray{Float}=[0.0; 0.0]
: Array of [x y] position of point about which to perform rotation.flipped::Bool
: flag whether to flip airfoil upside down.
Returns
x::Array{Float}
: array of x-coordinatesy::Array{Float}
: array of y-coordinates
position_coordinates!(
coordinates::Vector{AbstractArray{TF}};
scales=[1.0],
angles=[0.0],
locations=[[0.0; 0.0],],
rotation_points=[[0.0; 0.0],],
flipped=[false],
) where {TF}
Multi-airfoil version of position_coordinates!.
If keyword arguments are give as single valued vectors, the same values are used for all coordinate sets. If keyword arguments are provided as vectors of length greater than 1, they must have the same length as the set of coordinates. For example, if scaling 3 airfoils, there will be a vector of 3 airfoil coordinate sets input and scales
must either be a one element vector or a vector of length 3.
Re-definition
Sometimes, we may want to "re-panel" an airfoil, (a good example is the PANE command in Xfoil). The repanel_airfoil
method splines the provided geometry and then resamples it using cosine spacing to give a higher density of coordinates at the leading and trailing edges.
using FLOWFoil.AirfoilTools
using Plots
using LaTeXStrings
x, y = naca4(; x = range(0.0,1.0,30))
plot(x, y; aspectratio=1, xlabel=L"x", ylabel=L"y", label="Linear Spaced", marker=true, markersize=4)
x_rp, y_rp = repanel_airfoil(x,y; N=161)
scatter!(x_rp, y_rp; label="Cosine Spaced", markersize=1.5)
Note that if you simply have too few coordinates to begin with, repaneling the airfoil isn't going to smooth out an inaccurate leading edge. In such a case you should consider fitting the geometry with one of the airfoil parameterizazation methods, which will ensure a round leading edge.
FLOWFoil.AirfoilTools.repanel_airfoil
— Functionrepanel_airfoil(x, y; N=160)
Repanels airfoil coordinates using Akima splines with N
coordinate points.
Arguments
x::AbstractArray{Float}
: vector containing the x coordinates of the airfoily::AbstractArray{Float}
: vector containing the y coordinates of the airfoil
Keyword Arguements
N::Int
: Number of data points to be returned after repaneling. Will only return odd numbers, if N is even, N+1 points will be returned.
Returns
repaneled_x::AbstractArray{Float}
: Repaneled, cosine spaced x corrdinates of the airfoilrepaneled_y::AbstractArray{Float}
: y coordinates of the repaneled airfoil obtained using an akima spline
repanel_airfoil(coordinates; N=160)
Repanels airfoil coordinates using Akima splines with N
coordinate points.
Arguments
coordinates::Array{Float}
: Array of [x y] coordinates
Keyword Arguements
N::Int=160
: Number of data points to be returned after repaneling. Will only return odd numbers, if N is even, N+1 points will be returned.
Returns
repaneled_coordinates::Array{Float}
: new coordinate array.
FLOWFoil.AirfoilTools.repanel_revolution
— Functionrepanel_revolution(coordinates; N=160)
Repanels body of revolution coordinates using Akima splines with N
coordinate points.
Arguments
coordinates::Array{Float}
: Array of [x y] coordinates
Keyword Arguements
N::Int=160
: Number of data points to be returned after repaneling. Will only return odd numbers, if N is even, N+1 points will be returned.
Returns
repaneled_coordinates::Array{Float}
: new coordinate array.
Contributing
We welcome the addition of more convenience functions for airfoil geometry manipulation.