Dear ccblade users/developers,
For my own education, I have modified the “example.py” file that comes with ccblade docs folder to try and compare the results of ccblade against (what I believe they are) official power curve, Cp, thrust and torque for the NREL 5MW reference turbine.
The data for the power curve/power coefficient for the NREL 5MW reference turbine come from here: github.com/NREL/turbine-models/ … 26_RWT.csv
For thrust, torque, rotor speed and pitch, I have used the data reported here: NREL5MW Rotor Speed Data
Now, by naively running the almost-unmodified code, I was kind of expecting to see the BEM code almost reproduce the reported numbers:
Image link: postimg.cc/Whb340bb
(Seems like I am unable to link images directly in the post for some reason…)
While Cp and the first parts for the power curve and torque look very good, I am a bit puzzled by the behaviour of the thrust curve and the end part of the power/torque curve. I am sure I am misunderstanding something big or I am missing something even bigger here .
I am pasting below my Python script to reproduce my graphs - the NREL data is embedded in it.
I’ll appreciate any suggestion/comment you may have. Thank you in advance.
Andrea.
import numpy as np
from math import pi
import matplotlib.pyplot as plt
from ccblade import CCAirfoil, CCBlade
def Main():
# geometry
Rhub = 1.5
Rtip = 63.0
r = np.array([2.8667, 5.6000, 8.3333, 11.7500, 15.8500, 19.9500, 24.0500,
28.1500, 32.2500, 36.3500, 40.4500, 44.5500, 48.6500, 52.7500,
56.1667, 58.9000, 61.6333])
chord = np.array([3.542, 3.854, 4.167, 4.557, 4.652, 4.458, 4.249, 4.007, 3.748,
3.502, 3.256, 3.010, 2.764, 2.518, 2.313, 2.086, 1.419])
theta = np.array([13.308, 13.308, 13.308, 13.308, 11.480, 10.162, 9.011, 7.795,
6.544, 5.361, 4.188, 3.125, 2.319, 1.526, 0.863, 0.370, 0.106])
B = 3 # number of blades
tilt = 5.0
precone = 2.5
yaw = 0.0
nSector = 18 # azimuthal discretization
# atmosphere
rho = 1.225
mu = 1.81206e-5
# power-law wind shear profile
shearExp = 0.2
hubHt = 90.0
# turbine characteristics
cut_out = 25.0 # m/s
# 2 ----------
afinit = CCAirfoil.initFromAerodynFile # just for shorthand
# load all airfoils
airfoil_types = [0]*8
airfoil_types[0] = afinit('data/Cylinder1.dat')
airfoil_types[1] = afinit('data/Cylinder2.dat')
airfoil_types[2] = afinit('data/DU40_A17.dat')
airfoil_types[3] = afinit('data/DU35_A17.dat')
airfoil_types[4] = afinit('data/DU30_A17.dat')
airfoil_types[5] = afinit('data/DU25_A17.dat')
airfoil_types[6] = afinit('data/DU21_A17.dat')
airfoil_types[7] = afinit('data/NACA64_A17.dat')
# place at appropriate radial stations
af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7]
af = [0]*len(r)
for i in range(len(r)):
af[i] = airfoil_types[af_idx[i]]
# 2 ----------
# 3 ----------
# create CCBlade object
rotor = CCBlade(r, chord, theta, af, Rhub, Rtip, B, rho, mu,
precone, tilt, yaw, shearExp, hubHt, nSector)
# 4 ----------
# Data for NREL 5 MW
Omega = np.array([0, 0, 0, 0, 0, 6.972, 7.0775, 7.183, 7.3445, 7.506, 7.724, 7.942, 8.2055, 8.469, 8.8125,
9.156, 9.726, 10.296, 10.8635, 11.431, 11.6605, 11.89, 11.995, 12.1, 12.1, 12.1, 12.1,
12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1,
12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1, 12.1])
pitch = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9115, 3.823,
5.2125, 6.602, 7.635, 8.668, 9.559, 10.45, 11.2525, 12.055, 12.7955, 13.536,
14.228, 14.92, 15.573, 16.226, 16.8495, 17.473, 18.086, 18.699, 19.32, 19.941,
20.559, 21.177, 21.762, 22.347, 22.908, 23.469])
# Results for NREL 5 MW
# Taken from http://forums.nrel.gov/t/nrel5mw-rotor-speed-data/867/1
ROTOR_POWER = np.array([0, 0, 0, 0, 0, 42.9, 115.55, 188.2, 308.05, 427.9, 604.6, 781.3, 1019.45, 1257.6,
1566.9, 1876.2, 2272.1, 2668, 3160.5, 3653, 4243.1, 4833.2, 5064.9, 5296.6, 5296.6,
5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6,
5296.6, 5296.6, 5296.6, 5296.65, 5296.7, 5296.65, 5296.6, 5296.65, 5296.7, 5296.65,
5296.6, 5296.6, 5296.6, 5296.65, 5296.7])/1e3
POWER_CURVE = np.array([0, 0, 0, 0, 0, 40.5, 109.1, 177.7, 290.8, 403.9, 570.75, 737.6, 962.4, 1187.2,
1479.15, 1771.1, 2144.85, 2518.6, 2983.5, 3448.4, 4005.45, 4562.5, 4781.25, 5000, 5000,
5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000,
5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000])/1e3
THRUST = np.array([0, 0, 0, 0, 0, 171.7, 193.8, 215.9, 242.4, 268.9, 299.6, 330.3, 364.45, 398.6, 438.3, 478,
528.6, 579.2, 635.35, 691.5, 741.05, 790.6, 740.3, 690, 649.2, 608.4, 583.15, 557.9, 539.2,
520.5, 505.85, 491.2, 479.45, 467.7, 458.05, 448.4, 440.35, 432.3, 425.55, 418.8, 412.75,
406.7, 401, 395.3, 390.2, 385.1, 380.9, 376.7, 373, 369.3])
TORQUE = np.array([0, 0, 0, 0, 0, 58.8, 154.5, 250.2, 397.25, 544.3, 741.9, 939.5, 1178.8, 1418.1, 1687.5, 1956.9,
2215.7, 2474.5, 2762.8, 3051.1, 3466.2, 3881.3, 4030.7, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1,
4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1,
4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1, 4180.1])
CP = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.196405748, 0.27998048600000003, 0.363555224, 0.3933196965, 0.423084169,
0.435105171, 0.447126173, 0.4501645485, 0.453202924, 0.45306905350000004, 0.452935183, 0.4526530135,
0.452370844, 0.4519479375, 0.451525031, 0.450679475, 0.44883706, 0.430468281, 0.378869113,
0.3384299655, 0.297990818, 0.26828947400000003, 0.23858813, 0.21628455800000002, 0.193980986,
0.1769081965, 0.159835407, 0.146545612, 0.133255817, 0.122756666, 0.112257515, 0.1038533405,
0.095449166, 0.088642447, 0.081835728, 0.07626425349999999, 0.070692779, 0.06608858649999999,
0.061484394, 0.057646356999999995, 0.05380832, 0.0505834795, 0.047358639, 0.044629266, 0.041899893])
wind_speeds = Uinf = np.arange(0.5, 25.1, 0.5)
outputs, derivs = rotor.evaluate(Uinf, Omega, pitch)
P = outputs['P']
T = outputs['T']
Q = outputs['Q']
outputs, derivs = rotor.evaluate(Uinf, Omega, pitch, coefficients=True)
cp = outputs['CP']
cp[wind_speeds > cut_out] = 0.0
fig = plt.figure()
ax = fig.add_subplot(2, 2, 1)
ax.plot(wind_speeds, CP, lw=2, label='NREL')
ax.plot(wind_speeds, cp, lw=2, label='ccblade')
ax.legend(loc=0, fancybox=True, shadow=True, fontsize=14)
ax.grid()
ax.set_xlabel('Wind Speed $(m/s)$', fontsize=16)
ax.set_ylabel('Power Coefficient $(-)$', fontsize=16)
ax = fig.add_subplot(2, 2, 2)
idx = np.where(wind_speeds >= 25.0)[0]
P[P<0] = 0.0
P2 = P.copy()
P2[P2>5e6] = 5e6
P2[idx] = 0.0
ax.plot(wind_speeds, POWER_CURVE, lw=2, label='NREL')
ax.plot(wind_speeds, P2/1e6, lw=2, label='ccblade')
ax.grid()
ax.set_xlabel('Wind Speed $(m/s)$', fontsize=16)
ax.set_ylabel('Power $(MW)$', fontsize=16)
ax.legend(loc=0, fancybox=True, shadow=True, fontsize=14)
ax.set_ylim(0, 6)
ax = fig.add_subplot(2, 2, 3)
ax.plot(wind_speeds, THRUST, lw=2, label='NREL')
ax.plot(wind_speeds, T/1e3, lw=2, label='ccblade')
ax.grid()
ax.set_xlabel('Wind Speed $(m/s)$', fontsize=16)
ax.set_ylabel('Thrust $(kN)$', fontsize=16)
ax.legend(loc=0, fancybox=True, shadow=True, fontsize=14)
Q[Q<0] = 0.0
ax = fig.add_subplot(2, 2, 4)
ax.plot(wind_speeds, TORQUE, lw=2, label='NREL')
ax.plot(wind_speeds, Q/1e3, lw=2, label='ccblade')
ax.grid()
ax.set_xlabel('Wind Speed $(m/s)$', fontsize=16)
ax.set_ylabel('Torque $(kNm)$', fontsize=16)
ax.legend(loc=0, fancybox=True, shadow=True, fontsize=14)
for ax in fig.axes:
ax.set_xlim(0, 30)
plt.show()
if __name__ == '__main__':
Main()