Generating components
Rotor
FLOWunsteady uses a database of airfoil and rotor geometries to automate the generation of rotors. A prepopulated database is found in the directory under FLOWUnsteady.default_database
. Alternatively, users can define their own database with custom rotors and airfoils.
The following slides describe the structure of the database, using the DJI 9443 rotor as an example:
Rotors can then be generated calling any of following functions:
FLOWUnsteady.generate_rotor
— Methodgenerate_rotor(rotor_file::String;
data_path=FLOWUnsteady.default_database, optargs...)
Generates a FLOWVLM.Rotor
reading the full rotor geometry from the rotor file rotor_file
found in the database data_path
.
FLOWUnsteady.generate_rotor
— Methodgenerate_rotor(Rtip, Rhub, B, blade_file::String;
data_path=FLOWUnsteady.default_database, optargs...)
Generates a FLOWVLM.Rotor
reading the blade geometry from the blade file blade_file
found in the database data_path
.
FLOWUnsteady.generate_rotor
— Methodgenerate_rotor(Rtip, Rhub, B,
chorddist, twistdist, sweepdist, heightdist,
airfoil_contours;
# MORE ROTOR PARAMETERS
pitch=0.0,
CW=true,
# DISCRETIZATION SETTINGS
n=10, blade_r=1.0,
rediscretize_airfoils=true,
rfl_n_lower=15, rfl_n_upper=15,
spline_k=5, spline_s=0.001, spline_bc="extrapolate",
# AIRFOIL PROCESSING
data_path=FLOWUnsteady.default_database,
read_polar=vlm.ap.read_polar,
xfoil=false,
alphas=[i for i in -20:1.0:20], ncrit=9,
ReD=5e5, altReD=nothing, Matip=0.0,
# OUTPUT OPTIONS
verbose=false, verbose_xfoil=false, v_lvl=1,
plot_disc=true, save_polars=nothing)
Generates a FLOWVLM.Rotor
from direct inputs.
ARGUMENTS
Rtip::Real
: (m) rotor radius (from centerline to blade tip)Rhub::Real
: (m) hub radius (from centerline to blade root)B::Int
: Number of bladeschorddist::Matrix
: Chord distribution (chorddist[:, 1] = r/R
,chorddist[:, 2] = c/R
twistdist::Matrix
: Twist distribution (twistdist[:, 1] = r/R
,twistdist[:, 2] = degs
sweepdist::Matrix
: LE sweep distribution (sweepdist[:, 1] = r/R
,sweepdist[:, 2] = x/R
heightdist::Matrix
: LE height distribution (heightdist[:, 1] = r/R
,heightdist[:, 2] = z/R
airfoil_contours::Array{ Tuple{Float64, Array{Float64, 2}, String} }
: Airfoil contours along the span. It must follow the pattern(pos, contour, polarfile) = airfoil_contours[i]
, wherepos = (r-Rhub)/(Rtip-Rhub)
is the spanwise position (with root=0, tip=1),contour
is the airfoil profile (contour[:, 1] = x/c
,contour[:, 2] = y/c
), andpolarfile
is any file from airfoiltools.com with the airfoil lookup table (airfoil polar).
The function allows airfoil_contours::Array{ Tuple{Float64, String, String} }
, following the pattern (pos, contourfile, polarfile) = airfoil_contours[i]
where contourfile
is a CSV file with columns x/c
and y/c
.
KEYWORD ARGUMENTS
Extra rotor parameters
pitch::Real
: (deg) rotor collective pitchCW::Bool
: Whether the rotor rotates in the clockwise (true
) or counter-clockwise (false
)
Discretization
n::Int
: Number of blade elements per blade.r::Real
: Spacing between elements at the tip, divided by the spacing between elements at the root.spline_k::Int
,spline_s::Real
,spline_bc::String
: To discretize the blade, the blade distributions are splined and re-discretize inton
elements. These splines are done through the Dierckx package, withspline_k
the order of the spline,spline_s
the smoothing parameter, andspline_bc
the boundary condition.rediscretize_airfoils
: If true, it will spline the airfoil contours and re-discretize them. It will discretize the lower side of the contour intorfl_n_lower
panels, and the upper side intorfl_n_upper
panels. This is necessary unless all the airfoil contours already have the same number of points.
Airfoil processing
data_path::String
: Path to database where to read the airfoil polars from.read_polar::Function
: Function used for parsing the airfoil files. Usevlm.ap.read_polar
for files that are direct outputs from XFOIL. Usevlm.ap.read_polar2
for CSV files.xfoil::Bool
: If true, the polar files will be ignored and XFOIL will be used to generate the polars instead. This will be done sweeping AOA as inalphas
(in degrees) andncrit
for inflow turbulence parameter.ReD::Real
,Matip::Real
,altReD::Tuple{Real, Real, Real}
ReD
is the diameter Reynolds number based on rotational speed calculated as ReD = (omega*R)*(2*R)/nu
, and Matip
is the rotational+freestream Mach number at the tip. These number will be used for running XFOIL to compute airfoil polars, and will be ignored if airfoil polars are prescribed.
Give it altReD = (RPM, J, nu)
, and it will calculate the chord-based Reynolds accounting for the effective velocity at every station, ignoring ReD
(this is more accurate, but not needed).
NOTE: If Matip
is different than zero while running XFOIL, you must deactive compressibility corrections in run_simulation
by using sound_spd=nothing
. Otherwise, compressibility effects will be double accounted for.
Outputs
verbose::Bool
: Whether to verbose while generating the rotorverbose_xfoil::Bool
: Whether to verbose while running XFOILv_lvl::Int
: Indentation level for printing the verboseplot_disc
: If true, it will plot the discretization process of the blade, which is useful for verification and debugging. It will also plot the airfoil polars.save_polars::String
: Give it a path to a directory and it will save the polars plot in that directory
FLOWVLM.rotate
— Functionrotate(rotor::Rotor, degs::Real)
Rotates the rotor by degs
degrees in the direction of rotation (rotor.CW
).
VLM Wing
FLOWVLM.simpleWing
— FunctionsimpleWing(b, ar, tr, twist, lambda, gamma; twist_tip=twist, n=20, r=2.0)
Generates a simple wing with constant twist, sweep, dihedral, and taper ratio.
Arguments
b
: (float) spanar
: (float) aspect ratio (span / tip chord)tr
: (float) taper ratio (tip chord / root chord)twist
: (float) twist of the root in degreeslambda
: (float) sweep in degreesgamma
: (float) dihedral in degreestwist_tip
: (float) twist of the tip (if different than root)n
: (int) number of horseshoes per semi-spanr
: (float) horseshoes' expansion ratio
FLOWVLM.complexWing
— FunctioncomplexWing(b, AR, tr, n, pos, twist, sweep, dihed; symmetric=true)
Generates a wing with an abritrary distribution of twist, sweep, dihedral, and chord length.
Arguments
b::Float64
: SpanAR::Float64
: Reference aspect ratio (span / tip chord)n::Int64
: Number of horseshoes per semi-spanpos::Array{Float64,1}
: Position of stations along the semi-spanclen::Array{Float64,1}
: Chord length at each station (normalized by tip chord)twist::Array{Float64,1}
: (deg) twist at each stationsweep::Array{Float64,1}
: (deg) sweep between each stationdihed::Array{Float64,1}
: (deg) dihedral between each station
Optional Arguments
symmetric::Bool=true
: If false, generates only a half-spanchordalign::Float64=0.0
: Indicate position along chord length to align chords. Give it 0 for alignment about leading edge, 0.25 for alignment about quarter chord, and 1.0 for alignment about trailing edge.
Example
# Wing dimensions
b = 1.0 # (m) span
AR = 12.0 # Span over tip chord
n = 50 # Horseshoes per semi-span
# Define chord, twist, sweep, and dihedral distributions
pos = [0, 0.25, 0.75, 1] # Position of stations along semi-span
clen = [2.0, 1.5, 1.5, 1] # Normalized chord length at each station
twist = [0.0, 0.0, -2.0, -4.0] # (deg) twist at each station
sweep = [10.0, 15.0, 35.0] # (deg) sweep between stations
dihed = [2.0, 5.0, 7.5] # (deg) dihedral between stations
# Generate the wing
wing = vlm.complexWing(b, AR, n, pos, clen, twist, sweep, dihed)
FLOWVLM Systems
FLOWVLM.WingSystem
— TypeWingSystem(wings::Array{Union{Wing, WingSystem}}, wing_names::Array{String})
Initiates a system of wings. All methods applicable to a Wing object are applicable to a WingSystem. When solved, it will calculate the interaction between wings.
FLOWVLM.addwing
— Functionaddwing(self::WingSystem, wing_name::String, wing::Union{Wing, Rotor})
Adds a wing (or rotor) to the system. The local reference frame of the wing then is then in relation to the local reference frame of the System.
FLOWVLM.get_wing
— Functionget_wing(self::WingSystem, wing_name::String)
Returns the wing of name wing_name
.
get_wing(self::WingSystem, wing_i::Int)
Returns the i-th wing.
FLOWVLM.setcoordsystem
— Functionsetcoordsystem(wing::Wing, O::Vector, Oaxis::Matrix)
Redefines the local coordinate system of the wing, where O
is the new origin and Oaxis
is the matrix of unit vectors
setcoordsystem(system::WingSystem, O::Vector, Oaxis::Matrix; wings=String[])
Redefines the local coordinate system of the system, where O
is the new origin and Oaxis
is the matrix of unit vectors. It transforms the coordinates of all wings in the system accordingly.
To change the local coordinate system of a specific wing relative to the system's coordinate system, give the name of the wing in an array under argument wings
.
setcoordsystem(rotor::Rotor, O::Vector, Oaxis::Matrix; user=false)
Redefines the local coordinate system of the rotor, where O
is the new origin and Oaxis
is the matrix of unit vectors. If the user is calling this function, give user=true
, otherwise it will not do the automatic translation to blade coordinate system.
FLOWVLM.get_m
— Functionget_m(wing::Wing)
Returns the number of horseshoes in the wing
get_m(system::WingSystem)
Returns the total number of horseshoes in the system
get_m(rotor::Rotor)
Returns the total number of horseshoes in the rotor
FLOWVLM.get_mBlade
— Functionget_mBlade(rotor::Rotor)
Returns the number of horseshoes per blade