You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If I am using all functionality correctly, I believe there may be issues with the correction3D method of the openfast_toolbox.airfoils.Polar class, when the self._radians property of an instance is True.
Consider the following example:
importnumpyasnpimportmatplotlib.pyplotaspltfromopenfast_toolbox.airfoils.PolarimportPolar# aoa_rad = ...# cl = ...# cd = ...# cm = ...# may also create an object, with the same outcome, without passing `radians=True` polarRAD=Polar(alpha=aoa_rad, cl=cl, cd=cd, cm=cm, Re=1.0, radians=True)
# for referencepolarDEG=Polar(alpha=np.rad2deg(aoa_rad), cl=cl, cd=cd, cm=cm, Re=1.0, radians=False)
r_R=0.197c_r=0.232tsr=9.153polarRAD3D=polarRAD.correction3D(r_over_R=r_R, chord_over_r=c_r, tsr=tsr)
# for referencepolarDEG3D=polarDEG.correction3D(r_over_R=r_R, chord_over_r=c_r, tsr=tsr)
ax=plt.subplots(1, 1)[1]
ax.plot(np.rad2deg(aoa_rad), cl, label='original')
ax.plot(polarRAD3D.alpha, polarRAD3D.cl, ls='-', label='polarRAD3D')
ax.plot(polarDEG3D.alpha, polarDEG3D.cl, ls='--', label='polarDEG3D')
ax.legend()
ax.set_xlim([-50, 50])
plt.show()
Executing this code results in the following error:
Exception has occurred: UnboundLocalError
cannot access local variable 'alpha' where it is not associated with a value
File "...path...\openfast_toolbox\openfast_toolbox\airfoils\Polar.py", line 360, in correction3D
# rename and convert units for convenienceifself._radians:
alpha=alpha# <- problematic lineelse:
alpha=np.radians(self.alpha)
I believe this line should read:
ifself._radians:
alpha=self.alpha
Making this change the program execution continues, however, the output from the polars generated with input angle in degree/radian are not the same.
As far as I can tell, this is due to the following:
In lines 346-249, a number of variables related to the linear region of the polar are calculated. If self._radians == True, the angular alpha_* variables have radians as unit, and cl_slope 1/radians -- as expected I believe:
If I modify the logic of the relevant routine to the following:
defcorrection3D(
self,
r_over_R,
chord_over_r,
tsr,
lift_method="DuSelig",
drag_method="None",
blending_method="linear_25_45",
max_cl_corr=0.25,
alpha_max_corr=None,
alpha_linear_min=None,
alpha_linear_max=None,
):
"""Applies 3-D corrections for rotating sections from the 2-D data. Parameters ---------- r_over_R : float local radial position / rotor radius chord_over_r : float local chord length / local radial location tsr : float tip-speed ratio lift_method : string, optional flag switching between Du-Selig and Snel corrections drag_method : string, optional flag switching between Eggers correction and None blending_method: string: blending method used to blend from 3D to 2D polar. default 'linear_25_45' max_cl_corr: float, optional maximum correction allowed, default is 0.25. alpha_max_corr : float, optional (deg) maximum angle of attack to apply full correction alpha_linear_min : float, optional (deg) angle of attack where linear portion of lift curve slope begins alpha_linear_max : float, optional (deg) angle of attack where linear portion of lift curve slope ends Returns ------- polar : Polar A new Polar object corrected for 3-D effects Notes ----- The Du-Selig method :cite:`Du1998A-3-D-stall-del` is used to correct lift, and the Eggers method :cite:`Eggers-Jr2003An-assessment-o` is used to correct drag. """ifalpha_max_corr==Noneandalpha_linear_min==Noneandalpha_linear_max==None:
# ASSUMPTIONS:# - No alpha_* are provided# - All are calculated in self's native angular units (DEG or RAD)alpha_linear_region, _, cl_slope, alpha0=self.linear_region()
alpha_linear_min=alpha_linear_region[0]
alpha_linear_max=alpha_linear_region[-1]
_, alpha_max_corr=self.cl_max()
find_linear_region=Falseelifalpha_max_corr*alpha_linear_min*alpha_linear_max==None:
raiseException(
"Define all or none of the keyword arguments alpha_max_corr, alpha_linear_min, and alpha_linear_max"
)
else:
# ASSUMPTIONS:# - All alpha_* are provided# - Given in DEG, since that is indicated in the docstring# - May/may not be the same as self's native angular units (DEG or RAD)find_linear_region=True# rename and convert units for conveniencecl_2d=self.clcd_2d=self.cdifself._radians:
# changed from `alpha = alpha` to `alpha = self.alpha`alpha=self.alphaelse:
alpha=np.radians(self.alpha)
iffind_linear_regionornotself._radians:
# ASSUMPTIONS:# Here because:# - alpha_* values were all provided in DEG (as per docstring), or# - alpha_* values were calculated in self's native angluar units, which were set to DEGalpha_max_corr=np.radians(alpha_max_corr)
alpha_linear_min=np.radians(alpha_linear_min)
alpha_linear_max=np.radians(alpha_linear_max)
# ASSUMPTIONS:# - from this point forward, alpha* quantities (aside from alpha0, which may still be unknown) are in RADIANS# parameters in Du-Selig modela=1b=1d=1lam=tsr/ (1+tsr**2) **0.5# modified tip speed ratioifnp.abs(r_over_R)>1e-4:
expon=d/lam/r_over_Relse:
expon=d/lam/1e-4# find linear region with numpy polyfitiffind_linear_region:
idx=np.logical_and(alpha>=alpha_linear_min, alpha<=alpha_linear_max)
p=np.polyfit(alpha[idx], cl_2d[idx], 1)
cl_slope=p[0]
alpha0=-p[1] /cl_slope# ASSUMPTIONS:# - alpha0 is in rad# - cl_slope is in 1/radelse:
# added test for self._radiansifnotself._radians:
# ASSUMPTION:# - Here because alpha0 and cl_slope were calculated w.r.t. self's native angular unit, which was DEGcl_slope=np.degrees(cl_slope)
alpha0=np.radians(alpha0)
# ASSUMPTIONS:# - all alpha* quantities are in RAD# - cl_slope is in 1/RADiflift_method=="DuSelig":
# Du-Selig correction factorifnp.abs(cl_slope)>1e-4:
fcl= (
1.0/cl_slope* (1.6*chord_over_r/0.1267* (a-chord_over_r**expon) / (b+chord_over_r**expon) -1)
)
# Force fcl to stay non-negativeiffcl<0.:
fcl=0.else:
fcl=0.0eliflift_method=="Snel":
# Snel correctionfcl=3.0*chord_over_r**2.0else:
raiseException("The keyword argument lift_method (3d correction for lift) can only be DuSelig or Snel.")
# 3D correction for liftcl_linear=cl_slope* (alpha-alpha0)
cl_corr=fcl* (cl_linear-cl_2d)
# Bound correction +/- max_cl_corrcl_corr=np.clip(cl_corr, -max_cl_corr, max_cl_corr)
# Blendingifblending_method=="linear_25_45":
# We adjust fully between +/- 25 deg, linearly to +/- 45adj_alpha=np.radians([-180, -45, -25, 25, 45, 180])
adj_value=np.array([0, 0, 1, 1, 0, 0])
adj=np.interp(alpha, adj_alpha, adj_value)
elifblending_method=="heaviside":
# Apply (arbitrary!) smoothing function to smoothen the 3D corrections and zero them out away from alpha_max_corrdelta_corr=10y1=1.0-smooth_heaviside(alpha, k=1, rng=(alpha_max_corr, alpha_max_corr+np.deg2rad(delta_corr)))
y2=smooth_heaviside(alpha, k=1, rng=(0.0, np.deg2rad(delta_corr)))
adj=y1*y2else:
raiseNotImplementedError("blending :", blending_method)
cl_3d=cl_2d+cl_corr*adj# Eggers 2003 correction for dragifdrag_method=="Eggers":
delta_cd=cl_corr* (np.sin(alpha) -0.12*np.cos(alpha)) / (np.cos(alpha) +0.12*np.sin(alpha)) *adjelifdrag_method=="None":
delta_cd=0.0else:
raiseException("The keyword argument darg_method (3d correction for drag) can only be Eggers or None.")
cd_3d=cd_2d+delta_cdreturntype(self)(Re=self.Re, alpha=np.degrees(alpha), cl=cl_3d, cd=cd_3d, cm=self.cm, radians=False)
I get the same result for a 3D corrected airfoil, regardless of whether the initial Polar is created with its angle of attack input in degree/radians (which I assume is the expected behaviour)
Am I doing something wrong, using the toolbox incorrectly, or is this possibly a bug?
The text was updated successfully, but these errors were encountered:
If I am using all functionality correctly, I believe there may be issues with the
correction3D
method of theopenfast_toolbox.airfoils.Polar
class, when theself._radians
property of an instance isTrue
.Consider the following example:
Executing this code results in the following error:
where the relevant line(s) of code in the
openfast_toolbox
is:I believe this line should read:
Making this change the program execution continues, however, the output from the polars generated with input angle in degree/radian are not the same.
As far as I can tell, this is due to the following:
In lines 346-249, a number of variables related to the linear region of the polar are calculated. If self._radians == True, the angular
alpha_*
variables have radians as unit, andcl_slope
1/radians -- as expected I believe:In lines 365-367, these
alpha_*
variables are converted to radians again:Also in lines 386/7,
alpha0
andcl_slope
are converted as if they have units degree and 1/degree, respectively:If I modify the logic of the relevant routine to the following:
I get the same result for a 3D corrected airfoil, regardless of whether the initial Polar is created with its angle of attack input in degree/radians (which I assume is the expected behaviour)
Am I doing something wrong, using the toolbox incorrectly, or is this possibly a bug?
The text was updated successfully, but these errors were encountered: