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 — Functiongenerate_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/Rtwistdist::Matrix: Twist distribution (twistdist[:, 1] = r/R,twistdist[:, 2] = degssweepdist::Matrix: LE sweep distribution (sweepdist[:, 1] = r/R,sweepdist[:, 2] = x/Rheightdist::Matrix: LE height distribution (heightdist[:, 1] = r/R,heightdist[:, 2] = z/Rairfoil_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),contouris the airfoil profile (contour[:, 1] = x/c,contour[:, 2] = y/c), andpolarfileis 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 intonelements. These splines are done through the Dierckx package, withspline_kthe order of the spline,spline_sthe smoothing parameter, andspline_bcthe 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_lowerpanels, and the upper side intorfl_n_upperpanels. 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_polarfor files that are direct outputs from XFOIL. Usevlm.ap.read_polar2for 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) andncritfor 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
generate_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.
generate_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/Rtwistdist::Matrix: Twist distribution (twistdist[:, 1] = r/R,twistdist[:, 2] = degssweepdist::Matrix: LE sweep distribution (sweepdist[:, 1] = r/R,sweepdist[:, 2] = x/Rheightdist::Matrix: LE height distribution (heightdist[:, 1] = r/R,heightdist[:, 2] = z/Rairfoil_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),contouris the airfoil profile (contour[:, 1] = x/c,contour[:, 2] = y/c), andpolarfileis 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 intonelements. These splines are done through the Dierckx package, withspline_kthe order of the spline,spline_sthe smoothing parameter, andspline_bcthe 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_lowerpanels, and the upper side intorfl_n_upperpanels. 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_polarfor files that are direct outputs from XFOIL. Usevlm.ap.read_polar2for 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) andncritfor 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