def test_dtheta3(self): dCT_dtheta = self.dCT_dv[0, 2, :] dCQ_dtheta = self.dCQ_dv[0, 2, :] dCP_dtheta = self.dCP_dv[0, 2, :] dCT_dtheta_fd = np.zeros(self.n) dCQ_dtheta_fd = np.zeros(self.n) dCP_dtheta_fd = np.zeros(self.n) for i in range(self.n): theta = np.array(self.theta) delta = 1e-6*theta[i] theta[i] += delta rotor = CCBlade(self.r, self.chord, theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dtheta_fd[i] = (CTd - self.CT) / delta dCQ_dtheta_fd[i] = (CQd - self.CQ) / delta dCP_dtheta_fd[i] = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dtheta_fd, dCT_dtheta, rtol=5e-6, atol=1e-8) np.testing.assert_allclose(dCQ_dtheta_fd, dCQ_dtheta, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dCP_dtheta_fd, dCP_dtheta, rtol=7e-5, atol=1e-8)
def test_dpresweepTip3(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) CP, CT, CQ, dCP_ds, dCT_ds, dCQ_ds, dCP_dv, dCT_dv, dCQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dpresweepTip = dCT_ds[0, 6] dCQ_dpresweepTip = dCQ_ds[0, 6] dCP_dpresweepTip = dCP_ds[0, 6] pst = float(presweepTip) delta = 1e-6*pst pst += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=presweep, presweepTip=pst) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dpresweepTip_fd = (CTd - CT) / delta dCQ_dpresweepTip_fd = (CQd - CQ) / delta dCP_dpresweepTip_fd = (CPd - CP) / delta np.testing.assert_allclose(dCT_dpresweepTip_fd, dCT_dpresweepTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dCQ_dpresweepTip_fd, dCQ_dpresweepTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dCP_dpresweepTip_fd, dCP_dpresweepTip, rtol=1e-4, atol=1e-8)
def test_dpresweep1(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve = \ rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dpresweep = dNp_dX[5, :] dTp_dpresweep = dTp_dX[5, :] dNp_dpresweep_fd = np.zeros(self.n) dTp_dpresweep_fd = np.zeros(self.n) for i in range(self.n): ps = np.array(presweep) delta = 1e-6*ps[i] ps[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=ps, presweepTip=presweepTip) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dpresweep_fd[i] = (Npd[i] - Np[i]) / delta dTp_dpresweep_fd[i] = (Tpd[i] - Tp[i]) / delta np.testing.assert_allclose(dNp_dpresweep_fd, dNp_dpresweep, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dTp_dpresweep_fd, dTp_dpresweep, rtol=1e-5, atol=1e-8)
def test_dprecurveTip1(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve = \ rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) pct = float(precurveTip) delta = 1e-6*pct pct += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=precurve, precurveTip=pct) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dprecurveTip_fd = (Npd - Np) / delta dTp_dprecurveTip_fd = (Tpd - Tp) / delta np.testing.assert_allclose(dNp_dprecurveTip_fd, 0.0, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dTp_dprecurveTip_fd, 0.0, rtol=1e-4, atol=1e-8)
def test_dprecurveTip2(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) P, T, Q, dP_ds, dT_ds, dQ_ds, dP_dv, dT_dv, dQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dprecurveTip = dT_ds[0, 5] dQ_dprecurveTip = dQ_ds[0, 5] dP_dprecurveTip = dP_ds[0, 5] pct = float(precurveTip) delta = 1e-6*pct pct += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=precurve, precurveTip=pct) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dprecurveTip_fd = (Td - T) / delta dQ_dprecurveTip_fd = (Qd - Q) / delta dP_dprecurveTip_fd = (Pd - P) / delta np.testing.assert_allclose(dT_dprecurveTip_fd, dT_dprecurveTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dQ_dprecurveTip_fd, dQ_dprecurveTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dP_dprecurveTip_fd, dP_dprecurveTip, rtol=1e-4, atol=1e-8)
def test_dtheta2(self): dT_dtheta = self.dT_dv[0, 2, :] dQ_dtheta = self.dQ_dv[0, 2, :] dP_dtheta = self.dP_dv[0, 2, :] dT_dtheta_fd = np.zeros(self.n) dQ_dtheta_fd = np.zeros(self.n) dP_dtheta_fd = np.zeros(self.n) for i in range(self.n): theta = np.array(self.theta) delta = 1e-6*theta[i] theta[i] += delta rotor = CCBlade(self.r, self.chord, theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dtheta_fd[i] = (Td - self.T) / delta dQ_dtheta_fd[i] = (Qd - self.Q) / delta dP_dtheta_fd[i] = (Pd - self.P) / delta np.testing.assert_allclose(dT_dtheta_fd, dT_dtheta, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dQ_dtheta_fd, dQ_dtheta, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dP_dtheta_fd, dP_dtheta, rtol=7e-5, atol=1e-8)
def test_dprecurve1(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve = \ rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dprecurve_fd = np.zeros((self.n, self.n)) dTp_dprecurve_fd = np.zeros((self.n, self.n)) for i in range(self.n): pc = np.array(precurve) delta = 1e-6*pc[i] pc[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=pc, precurveTip=precurveTip) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dprecurve_fd[i, :] = (Npd - Np) / delta dTp_dprecurve_fd[i, :] = (Tpd - Tp) / delta np.testing.assert_allclose(dNp_dprecurve_fd, dNp_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dTp_dprecurve_fd, dTp_dprecurve, rtol=3e-4, atol=1e-8)
def test_dhubht1(self): dNp_dhubht = self.dNp_dX[8, :] dTp_dhubht = self.dTp_dX[8, :] hubht = float(self.hubHt) delta = 1e-6*hubht hubht += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, hubht, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dhubht_fd = (Npd - self.Np) / delta dTp_dhubht_fd = (Tpd - self.Tp) / delta np.testing.assert_allclose(dNp_dhubht_fd, dNp_dhubht, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dTp_dhubht_fd, dTp_dhubht, rtol=1e-5, atol=1e-8)
def test_dpresweep2(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) P, T, Q, dP_ds, dT_ds, dQ_ds, dP_dv, dT_dv, dQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dpresweep = dT_dv[0, 4, :] dQ_dpresweep = dQ_dv[0, 4, :] dP_dpresweep = dP_dv[0, 4, :] dT_dpresweep_fd = np.zeros(self.n) dQ_dpresweep_fd = np.zeros(self.n) dP_dpresweep_fd = np.zeros(self.n) for i in range(self.n): ps = np.array(presweep) delta = 1e-6*ps[i] ps[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=ps, presweepTip=presweepTip) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dpresweep_fd[i] = (Td - T) / delta dQ_dpresweep_fd[i] = (Qd - Q) / delta dP_dpresweep_fd[i] = (Pd - P) / delta np.testing.assert_allclose(dT_dpresweep_fd, dT_dpresweep, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dQ_dpresweep_fd, dQ_dpresweep, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dP_dpresweep_fd, dP_dpresweep, rtol=3e-4, atol=1e-8)
def test_dprecurve3(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) CP, CT, CQ, dCP_ds, dCT_ds, dCQ_ds, dCP_dv, dCT_dv, dCQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dprecurve = dCT_dv[0, 3, :] dCQ_dprecurve = dCQ_dv[0, 3, :] dCP_dprecurve = dCP_dv[0, 3, :] dCT_dprecurve_fd = np.zeros(self.n) dCQ_dprecurve_fd = np.zeros(self.n) dCP_dprecurve_fd = np.zeros(self.n) for i in range(self.n): pc = np.array(precurve) delta = 1e-6*pc[i] pc[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=pc, precurveTip=precurveTip) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dprecurve_fd[i] = (CTd - CT) / delta dCQ_dprecurve_fd[i] = (CQd - CQ) / delta dCP_dprecurve_fd[i] = (CPd - CP) / delta np.testing.assert_allclose(dCT_dprecurve_fd, dCT_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dCQ_dprecurve_fd, dCQ_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dCP_dprecurve_fd, dCP_dprecurve, rtol=3e-4, atol=1e-8)
def test_dhubht3(self): dCT_dhubht = self.dCT_ds[0, 2] dCQ_dhubht = self.dCQ_ds[0, 2] dCP_dhubht = self.dCP_ds[0, 2] hubht = float(self.hubHt) delta = 1e-6*hubht hubht += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, hubht, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dhubht_fd = (CTd - self.CT) / delta dCQ_dhubht_fd = (CQd - self.CQ) / delta dCP_dhubht_fd = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dhubht_fd, dCT_dhubht, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dCQ_dhubht_fd, dCQ_dhubht, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dCP_dhubht_fd, dCP_dhubht, rtol=5e-5, atol=1e-8)
def test_dhubht2(self): dT_dhubht = self.dT_ds[0, 2] dQ_dhubht = self.dQ_ds[0, 2] dP_dhubht = self.dP_ds[0, 2] hubht = float(self.hubHt) delta = 1e-6*hubht hubht += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, hubht, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dhubht_fd = (Td - self.T) / delta dQ_dhubht_fd = (Qd - self.Q) / delta dP_dhubht_fd = (Pd - self.P) / delta np.testing.assert_allclose(dT_dhubht_fd, dT_dhubht, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dQ_dhubht_fd, dQ_dhubht, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dP_dhubht_fd, dP_dhubht, rtol=5e-5, atol=1e-8)
def test_dtheta1(self): dNp_dtheta = self.dNp_dX[2, :] dTp_dtheta = self.dTp_dX[2, :] dNp_dtheta_fd = np.zeros(self.n) dTp_dtheta_fd = np.zeros(self.n) for i in range(self.n): theta = np.array(self.theta) delta = 1e-6*theta[i] theta[i] += delta rotor = CCBlade(self.r, self.chord, theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dtheta_fd[i] = (Npd[i] - self.Np[i]) / delta dTp_dtheta_fd[i] = (Tpd[i] - self.Tp[i]) / delta np.testing.assert_allclose(dNp_dtheta_fd, dNp_dtheta, rtol=1e-6, atol=1e-8) np.testing.assert_allclose(dTp_dtheta_fd, dTp_dtheta, rtol=1e-4, atol=1e-8)
def setUp(self): # 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 # atmosphere rho = 1.225 mu = 1.81206e-5 afinit = CCAirfoil.initFromAerodynFile # just for shorthand basepath = path.join(path.dirname(path.realpath(__file__)), '5MW_AFFiles') # load all airfoils airfoil_types = [0]*8 airfoil_types[0] = afinit(path.join(basepath, 'Cylinder1.dat')) airfoil_types[1] = afinit(path.join(basepath, 'Cylinder2.dat')) airfoil_types[2] = afinit(path.join(basepath, 'DU40_A17.dat')) airfoil_types[3] = afinit(path.join(basepath, 'DU35_A17.dat')) airfoil_types[4] = afinit(path.join(basepath, 'DU30_A17.dat')) airfoil_types[5] = afinit(path.join(basepath, 'DU25_A17.dat')) airfoil_types[6] = afinit(path.join(basepath, 'DU21_A17.dat')) airfoil_types[7] = afinit(path.join(basepath, '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]] tilt = -5.0 precone = 2.5 yaw = 0.0 # create CCBlade object self.rotor = CCBlade(r, chord, theta, af, Rhub, Rtip, B, rho, mu, precone, tilt, yaw, shearExp=0.2, hubHt=90.0)
def execute(self): if len(self.precurve) == 0: self.precurve = np.zeros_like(self.r) # airfoil files n = len(self.airfoil_files) af = [0]*n afinit = CCAirfoil.initFromAerodynFile for i in range(n): af[i] = afinit(self.airfoil_files[i]) self.ccblade = CCBlade_PY(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) if self.run_case == 'power': # power, thrust, torque self.P, self.T, self.Q, self.dP, self.dT, self.dQ \ = self.ccblade.evaluate(self.Uhub, self.Omega, self.pitch, coefficient=False) elif self.run_case == 'loads': # distributed loads Np, Tp, self.dNp, self.dTp \ = self.ccblade.distributedAeroLoads(self.V_load, self.Omega_load, self.pitch_load, self.azimuth_load) # concatenate loads at root/tip self.loads.r = np.concatenate([[self.Rhub], self.r, [self.Rtip]]) Np = np.concatenate([[0.0], Np, [0.0]]) Tp = np.concatenate([[0.0], Tp, [0.0]]) # conform to blade-aligned coordinate system self.loads.Px = Np self.loads.Py = -Tp self.loads.Pz = 0*Np # return other outputs needed self.loads.V = self.V_load self.loads.Omega = self.Omega_load self.loads.pitch = self.pitch_load self.loads.azimuth = self.azimuth_load
def solve_nonlinear(self, params, unknowns, resids): self.r = params['r'] self.chord = params['chord'] self.theta = params['theta'] self.Rhub = params['Rhub'] self.Rtip = params['Rtip'] self.hubHt = params['hubHt'] self.precone = params['precone'] self.tilt = params['tilt'] self.yaw = params['yaw'] self.precurve = params['precurve'] self.precurveTip = params['precurveTip'] self.airfoils = params['airfoils'] self.B = params['B'] self.rho = params['rho'] self.mu = params['mu'] self.shearExp = params['shearExp'] self.nSector = params['nSector'] self.tiploss = params['tiploss'] self.hubloss = params['hubloss'] self.wakerotation = params['wakerotation'] self.usecd = params['usecd'] self.Uhub = params['Uhub'] self.Omega = params['Omega'] self.pitch = params['pitch'] self.ccblade = CCBlade(self.r, self.chord, self.theta, self.airfoils, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # power, thrust, torque self.P, self.T, self.Q, self.M, self.dP, self.dT, self.dQ \ = self.ccblade.evaluate(self.Uhub, self.Omega, self.pitch, coefficients=False) unknowns['T'] = self.T unknowns['Q'] = self.Q unknowns['P'] = self.P
# The parameters tiploss and hubloss toggle Prandtl tip and hub losses repsectively. # The parameter wakerotation toggles wake swirl (i.e., a′=0). # The parameter usecd can be used to disable the inclusion of drag in the # calculation of the induction factors (it is always used in calculations of the # distributed loads). However, doing so may cause potential failure in the # solution methodology (see [4]). In practice, it should work fine, but special care # for that particular case has not yet been examined, and the default implementation # allows for the possibility of convergence failure. # All four of these parameters are True by default. # The parameter iterRe is for advanced usage. # Referring to [4], this parameter controls the number of internal iterations on the Reynolds number. # One iteration is almost always sufficient, but for high accuracy in the Reynolds number # iterRe could be set at 2. Anything larger than that is unnecessary. m1_rotor = CCBlade(m1_r, m1_chord, m1_theta, m1_af, m1_Rhub, m1_Rtip, B, m1_rho, m1_mu, precone, tilt, yaw, shearExp, m1_hubHt, nSector) m4_rotor = CCBlade(m4_r, m4_chord, m4_theta, m4_af, m4_Rhub, m4_Rtip, B, m4_rho, m4_mu, precone, tilt, yaw, shearExp, m4_hubHt, nSector) m6_rotor = CCBlade(m6_r, m6_chord, m6_theta, m6_af, m6_Rhub, m6_Rtip, B, m6_rho, m6_mu, precone, tilt, yaw, shearExp, m6_hubHt, nSector) m66_rotor = CCBlade(m66_r, m66_chord, m66_theta, m66_af, m66_Rhub, m66_Rtip, B, m66_rho, m66_mu, precone, tilt, yaw, shearExp, m66_hubHt, nSector) m66yaw20_rotor = CCBlade(m66_r, m66_chord, m66_theta, m66_af, m66_Rhub, m66_Rtip, B, m66_rho, m66_mu, precone, tilt, 20.0, shearExp, m66_hubHt, nSector) m67_rotor = CCBlade(m67_r, m67_chord, m67_theta, m67_af, m67_Rhub, m67_Rtip, B, m67_rho, m67_mu, precone, tilt, yaw, shearExp, m67_hubHt, nSector) # convert to RPM m1_Omega = m1_Uinf*tsr/m1_Rtip * 30.0/pi m4_Omega = m4_Uinf*tsr/m4_Rtip * 30.0/pi m6_Omega = m6_Uinf*tsr/m6_Rtip * 30.0/pi
class TestNREL5MW(unittest.TestCase): def setUp(self): # 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 # atmosphere rho = 1.225 mu = 1.81206e-5 afinit = CCAirfoil.initFromAerodynFile # just for shorthand basepath = path.join(path.dirname(path.realpath(__file__)), '5MW_AFFiles') # load all airfoils airfoil_types = [0]*8 airfoil_types[0] = afinit(path.join(basepath, 'Cylinder1.dat')) airfoil_types[1] = afinit(path.join(basepath, 'Cylinder2.dat')) airfoil_types[2] = afinit(path.join(basepath, 'DU40_A17.dat')) airfoil_types[3] = afinit(path.join(basepath, 'DU35_A17.dat')) airfoil_types[4] = afinit(path.join(basepath, 'DU30_A17.dat')) airfoil_types[5] = afinit(path.join(basepath, 'DU25_A17.dat')) airfoil_types[6] = afinit(path.join(basepath, 'DU21_A17.dat')) airfoil_types[7] = afinit(path.join(basepath, '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]] tilt = -5.0 precone = 2.5 yaw = 0.0 # create CCBlade object self.rotor = CCBlade(r, chord, theta, af, Rhub, Rtip, B, rho, mu, precone, tilt, yaw, shearExp=0.2, hubHt=90.0) def test_thrust_torque(self): Uinf = np.array([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]) Omega = np.array([6.972, 7.183, 7.506, 7.942, 8.469, 9.156, 10.296, 11.431, 11.890, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100, 12.100]) pitch = np.array([0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 3.823, 6.602, 8.668, 10.450, 12.055, 13.536, 14.920, 16.226, 17.473, 18.699, 19.941, 21.177, 22.347, 23.469]) Pref = np.array([42.9, 188.2, 427.9, 781.3, 1257.6, 1876.2, 2668.0, 3653.0, 4833.2, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.6, 5296.7, 5296.6, 5296.7, 5296.6, 5296.6, 5296.7]) Tref = np.array([171.7, 215.9, 268.9, 330.3, 398.6, 478.0, 579.2, 691.5, 790.6, 690.0, 608.4, 557.9, 520.5, 491.2, 467.7, 448.4, 432.3, 418.8, 406.7, 395.3, 385.1, 376.7, 369.3]) Qref = np.array([58.8, 250.2, 544.3, 939.5, 1418.1, 1956.9, 2474.5, 3051.1, 3881.3, 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]) m_rotor = 110.0 # kg g = 9.81 tilt = 5*math.pi/180.0 Tref -= m_rotor*g*math.sin(tilt) # remove weight of rotor that is included in reported results P, T, Q, M = self.rotor.evaluate(Uinf, Omega, pitch) # import matplotlib.pyplot as plt # plt.plot(Uinf, P/1e6) # plt.plot(Uinf, Pref/1e3) # plt.figure() # plt.plot(Uinf, T/1e6) # plt.plot(Uinf, Tref/1e3) # plt.show() idx = (Uinf < 15) np.testing.assert_allclose(Q[idx]/1e6, Qref[idx]/1e3, atol=0.15) np.testing.assert_allclose(P[idx]/1e6, Pref[idx]/1e3, atol=0.2) # within 0.2 of 1MW np.testing.assert_allclose(T[idx]/1e6, Tref[idx]/1e3, atol=0.15)
def solve_nonlinear(self, params, unknowns, resids): self.r = params['r'] self.chord = params['chord'] self.theta = params['theta'] self.Rhub = params['Rhub'] self.Rtip = params['Rtip'] self.hubHt = params['hubHt'] self.precone = params['precone'] self.tilt = params['tilt'] self.yaw = params['yaw'] self.precurve = params['precurve'] self.precurveTip = params['precurveTip'] self.airfoils = params['airfoils'] self.B = params['B'] self.rho = params['rho'] self.mu = params['mu'] self.shearExp = params['shearExp'] self.nSector = params['nSector'] self.tiploss = params['tiploss'] self.hubloss = params['hubloss'] self.wakerotation = params['wakerotation'] self.usecd = params['usecd'] self.V_load = params['V_load'] self.Omega_load = params['Omega_load'] self.pitch_load = params['pitch_load'] self.azimuth_load = params['azimuth_load'] if len(self.precurve) == 0: self.precurve = np.zeros_like(self.r) # airfoil files n = len(self.airfoils) af = self.airfoils # af = [0]*n # for i in range(n): # af[i] = CCAirfoil.initFromAerodynFile(self.airfoil_files[i]) self.ccblade = CCBlade(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # distributed loads Np, Tp, self.dNp, self.dTp \ = self.ccblade.distributedAeroLoads(self.V_load, self.Omega_load, self.pitch_load, self.azimuth_load) # concatenate loads at root/tip unknowns['loads_r'] = self.r # conform to blade-aligned coordinate system unknowns['loads_Px'] = Np unknowns['loads_Py'] = -Tp unknowns['loads_Pz'] = 0 * Np # return other outputs needed unknowns['loads_V'] = self.V_load unknowns['loads_Omega'] = self.Omega_load unknowns['loads_pitch'] = self.pitch_load unknowns['loads_azimuth'] = self.azimuth_load
def setUp(self): # geometry self.Rhub = 1.5 self.Rtip = 63.0 self.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]) self.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]) self.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]) self.B = 3 # number of blades # atmosphere self.rho = 1.225 self.mu = 1.81206e-5 afinit = CCAirfoil.initFromAerodynFile # just for shorthand basepath = path.join(path.dirname(path.realpath(__file__)), '5MW_AFFiles') + path.sep # load all airfoils airfoil_types = [0]*8 airfoil_types[0] = afinit(basepath + 'Cylinder1.dat') airfoil_types[1] = afinit(basepath + 'Cylinder2.dat') airfoil_types[2] = afinit(basepath + 'DU40_A17.dat') airfoil_types[3] = afinit(basepath + 'DU35_A17.dat') airfoil_types[4] = afinit(basepath + 'DU30_A17.dat') airfoil_types[5] = afinit(basepath + 'DU25_A17.dat') airfoil_types[6] = afinit(basepath + 'DU21_A17.dat') airfoil_types[7] = afinit(basepath + '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] self.af = [0]*len(self.r) for i in range(len(self.r)): self.af[i] = airfoil_types[af_idx[i]] self.tilt = -5.0 self.precone = 2.5 self.yaw = 0.0 self.shearExp = 0.2 self.hubHt = 80.0 self.nSector = 8 # create CCBlade object self.rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True) # set conditions self.Uinf = 10.0 tsr = 7.55 self.pitch = 0.0 self.Omega = self.Uinf*tsr/self.Rtip * 30.0/pi # convert to RPM self.azimuth = 90 self.Np, self.Tp, self.dNp_dX, self.dTp_dX, self.dNp_dprecurve, self.dTp_dprecurve = \ self.rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) self.P, self.T, self.Q, self.dP_ds, self.dT_ds, self.dQ_ds, self.dP_dv, self.dT_dv, \ self.dQ_dv = self.rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) self.CP, self.CT, self.CQ, self.dCP_ds, self.dCT_ds, self.dCQ_ds, self.dCP_dv, self.dCT_dv, \ self.dCQ_dv = self.rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) self.rotor.derivatives = False self.n = len(self.r)
def solve_nonlinear(self, params, unknowns, resids): self.r = params['r'] self.chord = params['chord'] self.theta = params['theta'] self.Rhub = params['Rhub'] self.Rtip = params['Rtip'] self.hubHt = params['hubHt'] self.precone = params['precone'] self.tilt = params['tilt'] self.yaw = params['yaw'] self.precurve = params['precurve'] self.precurveTip = params['precurveTip'] self.airfoils = params['airfoils'] self.B = params['B'] self.rho = params['rho'] self.mu = params['mu'] self.shearExp = params['shearExp'] self.nSector = params['nSector'] self.tiploss = params['tiploss'] self.hubloss = params['hubloss'] self.wakerotation = params['wakerotation'] self.usecd = params['usecd'] self.V_load = params['V_load'] self.Omega_load = params['Omega_load'] self.pitch_load = params['pitch_load'] self.azimuth_load = params['azimuth_load'] if len(self.precurve) == 0: self.precurve = np.zeros_like(self.r) # airfoil files n = len(self.airfoils) af = self.airfoils # af = [0]*n # for i in range(n): # af[i] = CCAirfoil.initFromAerodynFile(self.airfoil_files[i]) self.ccblade = CCBlade(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # distributed loads Np, Tp, self.dNp, self.dTp \ = self.ccblade.distributedAeroLoads(self.V_load, self.Omega_load, self.pitch_load, self.azimuth_load) # concatenate loads at root/tip unknowns['loads_r'] = self.r # conform to blade-aligned coordinate system unknowns['loads_Px'] = Np unknowns['loads_Py'] = -Tp unknowns['loads_Pz'] = 0*Np # return other outputs needed unknowns['loads_V'] = self.V_load unknowns['loads_Omega'] = self.Omega_load unknowns['loads_pitch'] = self.pitch_load unknowns['loads_azimuth'] = self.azimuth_load
def solve_nonlinear(self, params, unknowns, resids): self.ccblade = CCBlade(params['r'], params['chord'], params['theta'], params['airfoils'], params['Rhub'], params['Rtip'], params['B'], params['rho'], params['mu'], params['precone'], params['tilt'], params['yaw'], params['shearExp'], params['hubHt'], params['nSector']) Uhub = np.linspace(params['control_Vin'],params['control_Vout'], self.n_pc) P_aero = np.zeros_like(Uhub) Cp_aero = np.zeros_like(Uhub) P = np.zeros_like(Uhub) Cp = np.zeros_like(Uhub) T = np.zeros_like(Uhub) Q = np.zeros_like(Uhub) M = np.zeros_like(Uhub) Omega = np.zeros_like(Uhub) pitch = np.zeros_like(Uhub) + params['control_pitch'] Omega_max = min([params['control_maxTS'] / params['Rtip'], params['control_maxOmega']*np.pi/30.]) # Region II for i in range(len(Uhub)): Omega[i] = Uhub[i] * params['control_tsr'] / params['Rtip'] P_aero, T, Q, M, Cp_aero, _, _, _ = self.ccblade.evaluate(Uhub, Omega * 30. / np.pi, pitch, coefficients=True) P, eff = CSMDrivetrain(P_aero, params['control_ratedPower'], params['drivetrainType']) Cp = Cp_aero*eff # search for Region 2.5 bounds for i in range(len(Uhub)): if Omega[i] > Omega_max and P[i] < params['control_ratedPower']: Omega[i] = Omega_max Uhub[i] = Omega[i] * params['Rtip'] / params['control_tsr'] P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch[i]], coefficients=True) P[i], eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff regionIIhalf = True i_IIhalf_start = i unknowns['V_R25'] = Uhub[i] break if P[i] > params['control_ratedPower']: regionIIhalf = False break def maxPregionIIhalf(pitch, Uhub, Omega): Uhub_i = Uhub Omega_i = Omega pitch = pitch P, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch], coefficients=False) return -P # Solve for regoin 2.5 pitch options = {} options['disp'] = False options['xatol'] = 1.e-2 if regionIIhalf == True: for i in range(i_IIhalf_start + 1, len(Uhub)): Omega[i] = Omega_max pitch0 = pitch[i-1] bnds = [pitch0 - 10., pitch0 + 10.] pitch_regionIIhalf = minimize_scalar(lambda x: maxPregionIIhalf(x, Uhub[i], Omega[i]), bounds=bnds, method='bounded', options=options)['x'] pitch[i] = pitch_regionIIhalf P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch[i]], coefficients=True) P[i], eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff if P[i] > params['control_ratedPower']: break def constantPregionIII(pitch, Uhub, Omega): Uhub_i = Uhub Omega_i = Omega pitch = pitch P_aero, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch], coefficients=False) P, eff = CSMDrivetrain(P_aero, params['control_ratedPower'], params['drivetrainType']) return abs(P - params['control_ratedPower']) if regionIIhalf == True: # Rated conditions def min_Uhub_rated_II12(min_params): return min_params[1] def get_Uhub_rated_II12(min_params): Uhub_i = min_params[1] Omega_i = Omega_max pitch = min_params[0] P_aero_i, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch], coefficients=False) P_i,eff = CSMDrivetrain(P_aero_i, params['control_ratedPower'], params['drivetrainType']) return abs(P_i - params['control_ratedPower']) x0 = [pitch[i] + 2. , Uhub[i]] bnds = [(pitch0, pitch0 + 10.),(Uhub[i-1],Uhub[i+1])] const = {} const['type'] = 'eq' const['fun'] = get_Uhub_rated_II12 params_rated = minimize(min_Uhub_rated_II12, x0, method='SLSQP', tol = 1.e-2, bounds=bnds, constraints=const) U_rated = params_rated.x[1] if not np.isnan(U_rated): Uhub[i] = U_rated pitch[i] = params_rated.x[0] else: print('Regulation trajectory is struggling to find a solution for rated wind speed. Check rotor_aeropower.py') Omega[i] = Omega_max P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch0], coefficients=True) P_i, eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff P[i] = params['control_ratedPower'] else: # Rated conditions def get_Uhub_rated_noII12(pitch, Uhub): Uhub_i = Uhub Omega_i = min([Uhub_i * params['control_tsr'] / params['Rtip'], Omega_max]) pitch_i = pitch P_aero_i, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch_i], coefficients=False) P_i, eff = CSMDrivetrain(P_aero_i, params['control_ratedPower'], params['drivetrainType']) return abs(P_i - params['control_ratedPower']) bnds = [Uhub[i-1], Uhub[i+1]] U_rated = minimize_scalar(lambda x: get_Uhub_rated_noII12(pitch[i], x), bounds=bnds, method='bounded', options=options)['x'] if not np.isnan(U_rated): Uhub[i] = U_rated else: print('Regulation trajectory is struggling to find a solution for rated wind speed. Check rotor_aeropower.py') Omega[i] = min([Uhub[i] * params['control_tsr'] / params['Rtip'], Omega_max]) pitch0 = pitch[i] P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch0], coefficients=True) P[i], eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff for j in range(i + 1,len(Uhub)): Omega[j] = Omega[i] if self.regulation_reg_III == True: pitch0 = pitch[j-1] bnds = [pitch0, pitch0 + 15.] pitch_regionIII = minimize_scalar(lambda x: constantPregionIII(x, Uhub[j], Omega[j]), bounds=bnds, method='bounded', options=options)['x'] pitch[j] = pitch_regionIII P_aero[j], T[j], Q[j], M[j], Cp_aero[j], _, _, _ = self.ccblade.evaluate([Uhub[j]], [Omega[j] * 30. / np.pi], [pitch[j]], coefficients=True) P[j], eff = CSMDrivetrain(P_aero[j], params['control_ratedPower'], params['drivetrainType']) Cp[j] = Cp_aero[j]*eff if abs(P[j] - params['control_ratedPower']) > 1e+4: print('The pitch in region III is not being determined correctly at wind speed ' + str(Uhub[j]) + ' m/s') P[j] = params['control_ratedPower'] T[j] = T[j-1] Q[j] = P[j] / Omega[j] M[j] = M[j-1] pitch[j] = pitch[j-1] Cp[j] = P[j] / (0.5 * params['rho'] * np.pi * params['Rtip']**2 * Uhub[i]**3) P[j] = params['control_ratedPower'] else: P[j] = params['control_ratedPower'] T[j] = 0 Q[j] = Q[i] M[j] = 0 pitch[j] = 0 Cp[j] = P[j] / (0.5 * params['rho'] * np.pi * params['Rtip']**2 * Uhub[i]**3) unknowns['T'] = T unknowns['Q'] = Q unknowns['Omega'] = Omega * 30. / np.pi unknowns['P'] = P unknowns['Cp'] = Cp unknowns['V'] = Uhub unknowns['M'] = M unknowns['pitch'] = pitch self.ccblade.induction_inflow = True a_regII, ap_regII, alpha_regII = self.ccblade.distributedAeroLoads(Uhub[0], Omega[0] * 30. / np.pi, pitch[0], 0.0) # Fit spline to powercurve for higher grid density spline = PchipInterpolator(Uhub, P) V_spline = np.linspace(params['control_Vin'],params['control_Vout'], num=self.n_pc_spline) P_spline = spline(V_spline) # outputs idx_rated = list(Uhub).index(U_rated) unknowns['rated_V'] = U_rated unknowns['rated_Omega'] = Omega[idx_rated] * 30. / np.pi unknowns['rated_pitch'] = pitch[idx_rated] unknowns['rated_T'] = T[idx_rated] unknowns['rated_Q'] = Q[idx_rated] unknowns['V_spline'] = V_spline unknowns['P_spline'] = P_spline unknowns['ax_induct_cutin'] = a_regII unknowns['tang_induct_cutin'] = ap_regII unknowns['aoa_cutin'] = alpha_regII
af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7] af = [0]*len(rad) for i in range(len(rad)): af[i] = airfoil_types[af_idx[i]] # Aerodynamic properties of NREL turbine tilt = -5.0 precone = 2.5 yaw = 0.0 shearExp = 0.2 hubHt = high nSector = 8 # create CCBlade object aeroanalysis = CCBlade(rad, c, alpha, af, Rhub, Rtip, blade, rho, mu, precone, tilt, yaw, shearExp, hubHt, nSector) tsr = np.linspace(0,20,100) # tip-speed ratio Uinf = Vinf*np.ones_like(tsr) # free stream wind speed Omega = ((Uinf*tsr)/Rtip)*(30./np.pi) # rotation rate (rad/s) pitch = np.ones_like(tsr)*0. # pitch (deg) # Calculating power coefficients at each tip-speed ratio CP,_,_ = aeroanalysis.evaluate(Uinf, Omega, pitch, coefficient=True) # Creating a power curve for the turbine (tip-speed ratio vs. power coefficient) powercurve = Akima1DInterpolator(tsr,CP) # Adjusting geometry for SPL calculations r_nrel = 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, 63.0]) # radial positions (m) rad = r_nrel*r_ratio
class CCBladeLoads(Component): def __init__(self, naero, npower): super(CCBladeLoads, self).__init__() """blade element momentum code""" # inputs self.add_param('V_load', val=0.0, units='m/s', desc='hub height wind speed') self.add_param('Omega_load', val=0.0, units='rpm', desc='rotor rotation speed') self.add_param('pitch_load', val=0.0, units='deg', desc='blade pitch setting') self.add_param('azimuth_load', val=0.0, units='deg', desc='blade azimuthal location') # outputs self.add_output('loads_r', val=np.zeros(naero), units='m', desc='radial positions along blade going toward tip') self.add_output('loads_Px', val=np.zeros(naero), units='N/m', desc='distributed loads in blade-aligned x-direction') self.add_output('loads_Py', val=np.zeros(naero), units='N/m', desc='distributed loads in blade-aligned y-direction') self.add_output('loads_Pz', val=np.zeros(naero), units='N/m', desc='distributed loads in blade-aligned z-direction') # corresponding setting for loads self.add_output('loads_V', val=0.0, units='m/s', desc='hub height wind speed') self.add_output('loads_Omega', val=0.0, units='rpm', desc='rotor rotation speed') self.add_output('loads_pitch', val=0.0, units='deg', desc='pitch angle') self.add_output('loads_azimuth', val=0.0, units='deg', desc='azimuthal angle') # (potential) variables self.add_param( 'r', val=np.zeros(naero), units='m', desc= 'radial locations where blade is defined (should be increasing and not go all the way to hub or tip)' ) self.add_param('chord', val=np.zeros(naero), units='m', desc='chord length at each section') self.add_param( 'theta', val=np.zeros(naero), units='deg', desc= 'twist angle at each section (positive decreases angle of attack)') self.add_param('Rhub', val=0.0, units='m', desc='hub radius') self.add_param('Rtip', val=0.0, units='m', desc='tip radius') self.add_param('hubHt', val=0.0, units='m', desc='hub height') self.add_param('precone', val=0.0, desc='precone angle', units='deg') self.add_param('tilt', val=0.0, desc='shaft tilt', units='deg') self.add_param('yaw', val=0.0, desc='yaw error', units='deg') # TODO: I've not hooked up the gradients for these ones yet. self.add_param('precurve', val=np.zeros(naero), units='m', desc='precurve at each section') self.add_param('precurveTip', val=0.0, units='m', desc='precurve at tip') # parameters self.add_param('airfoils', val=[0] * naero, desc='CCAirfoil instances', pass_by_obj=True) self.add_param('B', val=0, desc='number of blades', pass_by_obj=True) self.add_param('rho', val=0.0, units='kg/m**3', desc='density of air') self.add_param('mu', val=0.0, units='kg/(m*s)', desc='dynamic viscosity of air') self.add_param('shearExp', val=0.0, desc='shear exponent') self.add_param( 'nSector', val=4, desc= 'number of sectors to divide rotor face into in computing thrust and power', pass_by_obj=True) self.add_param('tiploss', val=True, desc='include Prandtl tip loss model', pass_by_obj=True) self.add_param('hubloss', val=True, desc='include Prandtl hub loss model', pass_by_obj=True) self.add_param( 'wakerotation', val=True, desc= 'include effect of wake rotation (i.e., tangential induction factor is nonzero)', pass_by_obj=True) self.add_param( 'usecd', val=True, desc='use drag coefficient in computing induction factors', pass_by_obj=True) self.naero = naero self.deriv_options['form'] = 'central' self.deriv_options['step_calc'] = 'relative' def solve_nonlinear(self, params, unknowns, resids): self.r = params['r'] self.chord = params['chord'] self.theta = params['theta'] self.Rhub = params['Rhub'] self.Rtip = params['Rtip'] self.hubHt = params['hubHt'] self.precone = params['precone'] self.tilt = params['tilt'] self.yaw = params['yaw'] self.precurve = params['precurve'] self.precurveTip = params['precurveTip'] self.airfoils = params['airfoils'] self.B = params['B'] self.rho = params['rho'] self.mu = params['mu'] self.shearExp = params['shearExp'] self.nSector = params['nSector'] self.tiploss = params['tiploss'] self.hubloss = params['hubloss'] self.wakerotation = params['wakerotation'] self.usecd = params['usecd'] self.V_load = params['V_load'] self.Omega_load = params['Omega_load'] self.pitch_load = params['pitch_load'] self.azimuth_load = params['azimuth_load'] if len(self.precurve) == 0: self.precurve = np.zeros_like(self.r) # airfoil files n = len(self.airfoils) af = self.airfoils # af = [0]*n # for i in range(n): # af[i] = CCAirfoil.initFromAerodynFile(self.airfoil_files[i]) self.ccblade = CCBlade(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # distributed loads Np, Tp, self.dNp, self.dTp \ = self.ccblade.distributedAeroLoads(self.V_load, self.Omega_load, self.pitch_load, self.azimuth_load) # concatenate loads at root/tip unknowns['loads_r'] = self.r # conform to blade-aligned coordinate system unknowns['loads_Px'] = Np unknowns['loads_Py'] = -Tp unknowns['loads_Pz'] = 0 * Np # return other outputs needed unknowns['loads_V'] = self.V_load unknowns['loads_Omega'] = self.Omega_load unknowns['loads_pitch'] = self.pitch_load unknowns['loads_azimuth'] = self.azimuth_load def list_deriv_vars(self): inputs = ('r', 'chord', 'theta', 'Rhub', 'Rtip', 'hubHt', 'precone', 'tilt', 'yaw', 'V_load', 'Omega_load', 'pitch_load', 'azimuth_load', 'precurve') outputs = ('loads_r', 'loads_Px', 'loads_Py', 'loads_Pz', 'loads_V', 'loads_Omega', 'loads_pitch', 'loads_azimuth') return inputs, outputs def linearize(self, params, unknowns, resids): dNp = self.dNp dTp = self.dTp n = len(self.r) dr_dr = np.vstack([np.zeros(n), np.eye(n), np.zeros(n)]) dr_dRhub = np.zeros(n + 2) dr_dRtip = np.zeros(n + 2) dr_dRhub[0] = 1.0 dr_dRtip[-1] = 1.0 dV = np.zeros(4 * n + 10) dV[3 * n + 6] = 1.0 dOmega = np.zeros(4 * n + 10) dOmega[3 * n + 7] = 1.0 dpitch = np.zeros(4 * n + 10) dpitch[3 * n + 8] = 1.0 dazimuth = np.zeros(4 * n + 10) dazimuth[3 * n + 9] = 1.0 J = {} zero = np.zeros(self.naero) J['loads_r', 'r'] = dr_dr J['loads_r', 'Rhub'] = dr_dRhub J['loads_r', 'Rtip'] = dr_dRtip J['loads_Px', 'r'] = np.vstack([zero, dNp['dr'], zero]) J['loads_Px', 'chord'] = np.vstack([zero, dNp['dchord'], zero]) J['loads_Px', 'theta'] = np.vstack([zero, dNp['dtheta'], zero]) J['loads_Px', 'Rhub'] = np.concatenate([[0.0], np.squeeze(dNp['dRhub']), [0.0]]) J['loads_Px', 'Rtip'] = np.concatenate([[0.0], np.squeeze(dNp['dRtip']), [0.0]]) J['loads_Px', 'hubHt'] = np.concatenate([[0.0], np.squeeze(dNp['dhubHt']), [0.0]]) J['loads_Px', 'precone'] = np.concatenate([[0.0], np.squeeze(dNp['dprecone']), [0.0]]) J['loads_Px', 'tilt'] = np.concatenate([[0.0], np.squeeze(dNp['dtilt']), [0.0]]) J['loads_Px', 'yaw'] = np.concatenate([[0.0], np.squeeze(dNp['dyaw']), [0.0]]) J['loads_Px', 'V_load'] = np.concatenate([[0.0], np.squeeze(dNp['dUinf']), [0.0]]) J['loads_Px', 'Omega_load'] = np.concatenate([[0.0], np.squeeze(dNp['dOmega']), [0.0]]) J['loads_Px', 'pitch_load'] = np.concatenate([[0.0], np.squeeze(dNp['dpitch']), [0.0]]) J['loads_Px', 'azimuth_load'] = np.concatenate([[0.0], np.squeeze(dNp['dazimuth']), [0.0]]) J['loads_Px', 'precurve'] = np.vstack([zero, dNp['dprecurve'], zero]) J['loads_Py', 'r'] = np.vstack([zero, -dTp['dr'], zero]) J['loads_Py', 'chord'] = np.vstack([zero, -dTp['dchord'], zero]) J['loads_Py', 'theta'] = np.vstack([zero, -dTp['dtheta'], zero]) J['loads_Py', 'Rhub'] = np.concatenate([[0.0], -np.squeeze(dTp['dRhub']), [0.0]]) J['loads_Py', 'Rtip'] = np.concatenate([[0.0], -np.squeeze(dTp['dRtip']), [0.0]]) J['loads_Py', 'hubHt'] = np.concatenate([[0.0], -np.squeeze(dTp['dhubHt']), [0.0]]) J['loads_Py', 'precone'] = np.concatenate([[0.0], -np.squeeze(dTp['dprecone']), [0.0]]) J['loads_Py', 'tilt'] = np.concatenate([[0.0], -np.squeeze(dTp['dtilt']), [0.0]]) J['loads_Py', 'yaw'] = np.concatenate([[0.0], -np.squeeze(dTp['dyaw']), [0.0]]) J['loads_Py', 'V_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dUinf']), [0.0]]) J['loads_Py', 'Omega_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dOmega']), [0.0]]) J['loads_Py', 'pitch_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dpitch']), [0.0]]) J['loads_Py', 'azimuth_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dazimuth']), [0.0]]) J['loads_Py', 'precurve'] = np.vstack([zero, -dTp['dprecurve'], zero]) J['loads_V', 'V_load'] = 1.0 J['loads_Omega', 'Omega_load'] = 1.0 J['loads_pitch', 'pitch_load'] = 1.0 J['loads_azimuth', 'azimuth_load'] = 1.0 return J
airfoil_types[6] = afinit('DU21_A17.dat') airfoil_types[7] = afinit('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, derivatives=True) # 3 ---------- # 4 ---------- # set conditions Uinf = 10.0 tsr = 7.55 pitch = 0.0 Omega = Uinf*tsr/Rtip * 30.0/pi # convert to RPM azimuth = 0.0 # # evaluate distributed loads # Np, Tp = rotor.distributedAeroLoads(Uinf, Omega, pitch, azimuth) # 4 ----------
class TestGradients(unittest.TestCase): def setUp(self): # geometry self.Rhub = 1.5 self.Rtip = 63.0 self.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]) self.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]) self.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]) self.B = 3 # number of blades # atmosphere self.rho = 1.225 self.mu = 1.81206e-5 afinit = CCAirfoil.initFromAerodynFile # just for shorthand basepath = path.join(path.dirname(path.realpath(__file__)), '5MW_AFFiles') + path.sep # load all airfoils airfoil_types = [0]*8 airfoil_types[0] = afinit(basepath + 'Cylinder1.dat') airfoil_types[1] = afinit(basepath + 'Cylinder2.dat') airfoil_types[2] = afinit(basepath + 'DU40_A17.dat') airfoil_types[3] = afinit(basepath + 'DU35_A17.dat') airfoil_types[4] = afinit(basepath + 'DU30_A17.dat') airfoil_types[5] = afinit(basepath + 'DU25_A17.dat') airfoil_types[6] = afinit(basepath + 'DU21_A17.dat') airfoil_types[7] = afinit(basepath + '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] self.af = [0]*len(self.r) for i in range(len(self.r)): self.af[i] = airfoil_types[af_idx[i]] self.tilt = -5.0 self.precone = 2.5 self.yaw = 0.0 self.shearExp = 0.2 self.hubHt = 80.0 self.nSector = 8 # create CCBlade object self.rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True) # set conditions self.Uinf = 10.0 tsr = 7.55 self.pitch = 0.0 self.Omega = self.Uinf*tsr/self.Rtip * 30.0/pi # convert to RPM self.azimuth = 90 self.Np, self.Tp, self.dNp_dX, self.dTp_dX, self.dNp_dprecurve, self.dTp_dprecurve = \ self.rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) self.P, self.T, self.Q, self.dP_ds, self.dT_ds, self.dQ_ds, self.dP_dv, self.dT_dv, \ self.dQ_dv = self.rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) self.CP, self.CT, self.CQ, self.dCP_ds, self.dCT_ds, self.dCQ_ds, self.dCP_dv, self.dCT_dv, \ self.dCQ_dv = self.rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) self.rotor.derivatives = False self.n = len(self.r) # X = [r, chord, theta, Rhub, Rtip, presweep, precone, tilt, hubHt] # scalars = [precone, tilt, hubHt, Rhub, Rtip, precurvetip, presweeptip] # vectors = [r, chord, theta, precurve, presweep] def test_dr1(self): dNp_dr = self.dNp_dX[0, :] dTp_dr = self.dTp_dX[0, :] dNp_dr_fd = np.zeros(self.n) dTp_dr_fd = np.zeros(self.n) for i in range(self.n): r = np.array(self.r) delta = 1e-6*r[i] r[i] += delta rotor = CCBlade(r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dr_fd[i] = (Npd[i] - self.Np[i]) / delta dTp_dr_fd[i] = (Tpd[i] - self.Tp[i]) / delta np.testing.assert_allclose(dNp_dr_fd, dNp_dr, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dTp_dr_fd, dTp_dr, rtol=1e-4, atol=1e-8) def test_dr2(self): dT_dr = self.dT_dv[0, 0, :] dQ_dr = self.dQ_dv[0, 0, :] dP_dr = self.dP_dv[0, 0, :] dT_dr_fd = np.zeros(self.n) dQ_dr_fd = np.zeros(self.n) dP_dr_fd = np.zeros(self.n) for i in range(self.n): r = np.array(self.r) delta = 1e-6*r[i] r[i] += delta rotor = CCBlade(r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dr_fd[i] = (Td - self.T) / delta dQ_dr_fd[i] = (Qd - self.Q) / delta dP_dr_fd[i] = (Pd - self.P) / delta np.testing.assert_allclose(dT_dr_fd, dT_dr, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dQ_dr_fd, dQ_dr, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dP_dr_fd, dP_dr, rtol=3e-4, atol=1e-8) def test_dr3(self): dCT_dr = self.dCT_dv[0, 0, :] dCQ_dr = self.dCQ_dv[0, 0, :] dCP_dr = self.dCP_dv[0, 0, :] dCT_dr_fd = np.zeros(self.n) dCQ_dr_fd = np.zeros(self.n) dCP_dr_fd = np.zeros(self.n) for i in range(self.n): r = np.array(self.r) delta = 1e-6*r[i] r[i] += delta rotor = CCBlade(r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dr_fd[i] = (CTd - self.CT) / delta dCQ_dr_fd[i] = (CQd - self.CQ) / delta dCP_dr_fd[i] = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dr_fd, dCT_dr, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dCQ_dr_fd, dCQ_dr, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dCP_dr_fd, dCP_dr, rtol=3e-4, atol=1e-8) def test_dchord1(self): dNp_dchord = self.dNp_dX[1, :] dTp_dchord = self.dTp_dX[1, :] dNp_dchord_fd = np.zeros(self.n) dTp_dchord_fd = np.zeros(self.n) for i in range(self.n): chord = np.array(self.chord) delta = 1e-6*chord[i] chord[i] += delta rotor = CCBlade(self.r, chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dchord_fd[i] = (Npd[i] - self.Np[i]) / delta dTp_dchord_fd[i] = (Tpd[i] - self.Tp[i]) / delta np.testing.assert_allclose(dNp_dchord_fd, dNp_dchord, rtol=1e-6, atol=1e-8) np.testing.assert_allclose(dTp_dchord_fd, dTp_dchord, rtol=5e-5, atol=1e-8) def test_dchord2(self): dT_dchord = self.dT_dv[0, 1, :] dQ_dchord = self.dQ_dv[0, 1, :] dP_dchord = self.dP_dv[0, 1, :] dT_dchord_fd = np.zeros(self.n) dQ_dchord_fd = np.zeros(self.n) dP_dchord_fd = np.zeros(self.n) for i in range(self.n): chord = np.array(self.chord) delta = 1e-6*chord[i] chord[i] += delta rotor = CCBlade(self.r, chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dchord_fd[i] = (Td - self.T) / delta dQ_dchord_fd[i] = (Qd - self.Q) / delta dP_dchord_fd[i] = (Pd - self.P) / delta np.testing.assert_allclose(dT_dchord_fd, dT_dchord, rtol=5e-6, atol=1e-8) np.testing.assert_allclose(dQ_dchord_fd, dQ_dchord, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dP_dchord_fd, dP_dchord, rtol=7e-5, atol=1e-8) def test_dchord3(self): dCT_dchord = self.dCT_dv[0, 1, :] dCQ_dchord = self.dCQ_dv[0, 1, :] dCP_dchord = self.dCP_dv[0, 1, :] dCT_dchord_fd = np.zeros(self.n) dCQ_dchord_fd = np.zeros(self.n) dCP_dchord_fd = np.zeros(self.n) for i in range(self.n): chord = np.array(self.chord) delta = 1e-6*chord[i] chord[i] += delta rotor = CCBlade(self.r, chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dchord_fd[i] = (CTd - self.CT) / delta dCQ_dchord_fd[i] = (CQd - self.CQ) / delta dCP_dchord_fd[i] = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dchord_fd, dCT_dchord, rtol=5e-6, atol=1e-8) np.testing.assert_allclose(dCQ_dchord_fd, dCQ_dchord, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dCP_dchord_fd, dCP_dchord, rtol=7e-5, atol=1e-8) def test_dtheta1(self): dNp_dtheta = self.dNp_dX[2, :] dTp_dtheta = self.dTp_dX[2, :] dNp_dtheta_fd = np.zeros(self.n) dTp_dtheta_fd = np.zeros(self.n) for i in range(self.n): theta = np.array(self.theta) delta = 1e-6*theta[i] theta[i] += delta rotor = CCBlade(self.r, self.chord, theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dtheta_fd[i] = (Npd[i] - self.Np[i]) / delta dTp_dtheta_fd[i] = (Tpd[i] - self.Tp[i]) / delta np.testing.assert_allclose(dNp_dtheta_fd, dNp_dtheta, rtol=1e-6, atol=1e-8) np.testing.assert_allclose(dTp_dtheta_fd, dTp_dtheta, rtol=1e-4, atol=1e-8) def test_dtheta2(self): dT_dtheta = self.dT_dv[0, 2, :] dQ_dtheta = self.dQ_dv[0, 2, :] dP_dtheta = self.dP_dv[0, 2, :] dT_dtheta_fd = np.zeros(self.n) dQ_dtheta_fd = np.zeros(self.n) dP_dtheta_fd = np.zeros(self.n) for i in range(self.n): theta = np.array(self.theta) delta = 1e-6*theta[i] theta[i] += delta rotor = CCBlade(self.r, self.chord, theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dtheta_fd[i] = (Td - self.T) / delta dQ_dtheta_fd[i] = (Qd - self.Q) / delta dP_dtheta_fd[i] = (Pd - self.P) / delta np.testing.assert_allclose(dT_dtheta_fd, dT_dtheta, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dQ_dtheta_fd, dQ_dtheta, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dP_dtheta_fd, dP_dtheta, rtol=7e-5, atol=1e-8) def test_dtheta3(self): dCT_dtheta = self.dCT_dv[0, 2, :] dCQ_dtheta = self.dCQ_dv[0, 2, :] dCP_dtheta = self.dCP_dv[0, 2, :] dCT_dtheta_fd = np.zeros(self.n) dCQ_dtheta_fd = np.zeros(self.n) dCP_dtheta_fd = np.zeros(self.n) for i in range(self.n): theta = np.array(self.theta) delta = 1e-6*theta[i] theta[i] += delta rotor = CCBlade(self.r, self.chord, theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dtheta_fd[i] = (CTd - self.CT) / delta dCQ_dtheta_fd[i] = (CQd - self.CQ) / delta dCP_dtheta_fd[i] = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dtheta_fd, dCT_dtheta, rtol=5e-6, atol=1e-8) np.testing.assert_allclose(dCQ_dtheta_fd, dCQ_dtheta, rtol=7e-5, atol=1e-8) np.testing.assert_allclose(dCP_dtheta_fd, dCP_dtheta, rtol=7e-5, atol=1e-8) def test_dRhub1(self): dNp_dRhub = self.dNp_dX[3, :] dTp_dRhub = self.dTp_dX[3, :] Rhub = float(self.Rhub) delta = 1e-6*Rhub Rhub += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dRhub_fd = (Npd - self.Np) / delta dTp_dRhub_fd = (Tpd - self.Tp) / delta np.testing.assert_allclose(dNp_dRhub_fd, dNp_dRhub, rtol=1e-5, atol=1e-7) np.testing.assert_allclose(dTp_dRhub_fd, dTp_dRhub, rtol=1e-4, atol=1e-7) def test_dRhub2(self): dT_dRhub = self.dT_ds[0, 3] dQ_dRhub = self.dQ_ds[0, 3] dP_dRhub = self.dP_ds[0, 3] Rhub = float(self.Rhub) delta = 1e-6*Rhub Rhub += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dRhub_fd = (Td - self.T) / delta dQ_dRhub_fd = (Qd - self.Q) / delta dP_dRhub_fd = (Pd - self.P) / delta np.testing.assert_allclose(dT_dRhub_fd, dT_dRhub, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dQ_dRhub_fd, dQ_dRhub, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dP_dRhub_fd, dP_dRhub, rtol=5e-5, atol=1e-8) def test_dRhub3(self): dCT_dRhub = self.dCT_ds[0, 3] dCQ_dRhub = self.dCQ_ds[0, 3] dCP_dRhub = self.dCP_ds[0, 3] Rhub = float(self.Rhub) delta = 1e-6*Rhub Rhub += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dRhub_fd = (CTd - self.CT) / delta dCQ_dRhub_fd = (CQd - self.CQ) / delta dCP_dRhub_fd = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dRhub_fd, dCT_dRhub, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dCQ_dRhub_fd, dCQ_dRhub, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dCP_dRhub_fd, dCP_dRhub, rtol=5e-5, atol=1e-8) def test_dRtip1(self): dNp_dRtip = self.dNp_dX[4, :] dTp_dRtip = self.dTp_dX[4, :] Rtip = float(self.Rtip) delta = 1e-6*Rtip Rtip += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dRtip_fd = (Npd - self.Np) / delta dTp_dRtip_fd = (Tpd - self.Tp) / delta np.testing.assert_allclose(dNp_dRtip_fd, dNp_dRtip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dTp_dRtip_fd, dTp_dRtip, rtol=1e-4, atol=1e-8) def test_dRtip2(self): dT_dRtip = self.dT_ds[0, 4] dQ_dRtip = self.dQ_ds[0, 4] dP_dRtip = self.dP_ds[0, 4] Rtip = float(self.Rtip) delta = 1e-6*Rtip Rtip += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dRtip_fd = (Td - self.T) / delta dQ_dRtip_fd = (Qd - self.Q) / delta dP_dRtip_fd = (Pd - self.P) / delta np.testing.assert_allclose(dT_dRtip_fd, dT_dRtip, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dQ_dRtip_fd, dQ_dRtip, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dP_dRtip_fd, dP_dRtip, rtol=5e-5, atol=1e-8) def test_dRtip3(self): dCT_dRtip = self.dCT_ds[0, 4] dCQ_dRtip = self.dCQ_ds[0, 4] dCP_dRtip = self.dCP_ds[0, 4] Rtip = float(self.Rtip) delta = 1e-6*Rtip Rtip += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dRtip_fd = (CTd - self.CT) / delta dCQ_dRtip_fd = (CQd - self.CQ) / delta dCP_dRtip_fd = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dRtip_fd, dCT_dRtip, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dCQ_dRtip_fd, dCQ_dRtip, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dCP_dRtip_fd, dCP_dRtip, rtol=5e-5, atol=1e-8) def test_dprecone1(self): dNp_dprecone = self.dNp_dX[6, :] dTp_dprecone = self.dTp_dX[6, :] precone = float(self.precone) delta = 1e-6*precone precone += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dprecone_fd = (Npd - self.Np) / delta dTp_dprecone_fd = (Tpd - self.Tp) / delta np.testing.assert_allclose(dNp_dprecone_fd, dNp_dprecone, rtol=1e-6, atol=1e-8) np.testing.assert_allclose(dTp_dprecone_fd, dTp_dprecone, rtol=1e-6, atol=1e-8) def test_dprecone2(self): dT_dprecone = self.dT_ds[0, 0] dQ_dprecone = self.dQ_ds[0, 0] dP_dprecone = self.dP_ds[0, 0] precone = float(self.precone) delta = 1e-6*precone precone += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dprecone_fd = (Td - self.T) / delta dQ_dprecone_fd = (Qd - self.Q) / delta dP_dprecone_fd = (Pd - self.P) / delta np.testing.assert_allclose(dT_dprecone_fd, dT_dprecone, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dQ_dprecone_fd, dQ_dprecone, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dP_dprecone_fd, dP_dprecone, rtol=5e-5, atol=1e-8) def test_dprecone3(self): dCT_dprecone = self.dCT_ds[0, 0] dCQ_dprecone = self.dCQ_ds[0, 0] dCP_dprecone = self.dCP_ds[0, 0] precone = float(self.precone) delta = 1e-6*precone precone += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dprecone_fd = (CTd - self.CT) / delta dCQ_dprecone_fd = (CQd - self.CQ) / delta dCP_dprecone_fd = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dprecone_fd, dCT_dprecone, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dCQ_dprecone_fd, dCQ_dprecone, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dCP_dprecone_fd, dCP_dprecone, rtol=5e-5, atol=1e-8) def test_dtilt1(self): dNp_dtilt = self.dNp_dX[7, :] dTp_dtilt = self.dTp_dX[7, :] tilt = float(self.tilt) delta = 1e-6*tilt tilt += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dtilt_fd = (Npd - self.Np) / delta dTp_dtilt_fd = (Tpd - self.Tp) / delta np.testing.assert_allclose(dNp_dtilt_fd, dNp_dtilt, rtol=1e-6, atol=1e-8) np.testing.assert_allclose(dTp_dtilt_fd, dTp_dtilt, rtol=1e-5, atol=1e-8) def test_dtilt2(self): dT_dtilt = self.dT_ds[0, 1] dQ_dtilt = self.dQ_ds[0, 1] dP_dtilt = self.dP_ds[0, 1] tilt = float(self.tilt) delta = 1e-6*tilt tilt += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dtilt_fd = (Td - self.T) / delta dQ_dtilt_fd = (Qd - self.Q) / delta dP_dtilt_fd = (Pd - self.P) / delta np.testing.assert_allclose(dT_dtilt_fd, dT_dtilt, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dQ_dtilt_fd, dQ_dtilt, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dP_dtilt_fd, dP_dtilt, rtol=5e-5, atol=1e-8) def test_dtilt3(self): dCT_dtilt = self.dCT_ds[0, 1] dCQ_dtilt = self.dCQ_ds[0, 1] dCP_dtilt = self.dCP_ds[0, 1] tilt = float(self.tilt) delta = 1e-6*tilt tilt += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dtilt_fd = (CTd - self.CT) / delta dCQ_dtilt_fd = (CQd - self.CQ) / delta dCP_dtilt_fd = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dtilt_fd, dCT_dtilt, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dCQ_dtilt_fd, dCQ_dtilt, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dCP_dtilt_fd, dCP_dtilt, rtol=5e-5, atol=1e-8) def test_dhubht1(self): dNp_dhubht = self.dNp_dX[8, :] dTp_dhubht = self.dTp_dX[8, :] hubht = float(self.hubHt) delta = 1e-6*hubht hubht += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, hubht, self.nSector, derivatives=False) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dhubht_fd = (Npd - self.Np) / delta dTp_dhubht_fd = (Tpd - self.Tp) / delta np.testing.assert_allclose(dNp_dhubht_fd, dNp_dhubht, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dTp_dhubht_fd, dTp_dhubht, rtol=1e-5, atol=1e-8) def test_dhubht2(self): dT_dhubht = self.dT_ds[0, 2] dQ_dhubht = self.dQ_ds[0, 2] dP_dhubht = self.dP_ds[0, 2] hubht = float(self.hubHt) delta = 1e-6*hubht hubht += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, hubht, self.nSector, derivatives=False) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dhubht_fd = (Td - self.T) / delta dQ_dhubht_fd = (Qd - self.Q) / delta dP_dhubht_fd = (Pd - self.P) / delta np.testing.assert_allclose(dT_dhubht_fd, dT_dhubht, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dQ_dhubht_fd, dQ_dhubht, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dP_dhubht_fd, dP_dhubht, rtol=5e-5, atol=1e-8) def test_dhubht3(self): dCT_dhubht = self.dCT_ds[0, 2] dCQ_dhubht = self.dCQ_ds[0, 2] dCP_dhubht = self.dCP_ds[0, 2] hubht = float(self.hubHt) delta = 1e-6*hubht hubht += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, hubht, self.nSector, derivatives=False) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dhubht_fd = (CTd - self.CT) / delta dCQ_dhubht_fd = (CQd - self.CQ) / delta dCP_dhubht_fd = (CPd - self.CP) / delta np.testing.assert_allclose(dCT_dhubht_fd, dCT_dhubht, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dCQ_dhubht_fd, dCQ_dhubht, rtol=5e-5, atol=1e-8) np.testing.assert_allclose(dCP_dhubht_fd, dCP_dhubht, rtol=5e-5, atol=1e-8) def test_dprecurve1(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve = \ rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dprecurve_fd = np.zeros((self.n, self.n)) dTp_dprecurve_fd = np.zeros((self.n, self.n)) for i in range(self.n): pc = np.array(precurve) delta = 1e-6*pc[i] pc[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=pc, precurveTip=precurveTip) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dprecurve_fd[i, :] = (Npd - Np) / delta dTp_dprecurve_fd[i, :] = (Tpd - Tp) / delta np.testing.assert_allclose(dNp_dprecurve_fd, dNp_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dTp_dprecurve_fd, dTp_dprecurve, rtol=3e-4, atol=1e-8) def test_dprecurve2(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) P, T, Q, dP_ds, dT_ds, dQ_ds, dP_dv, dT_dv, dQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dprecurve = dT_dv[0, 3, :] dQ_dprecurve = dQ_dv[0, 3, :] dP_dprecurve = dP_dv[0, 3, :] dT_dprecurve_fd = np.zeros(self.n) dQ_dprecurve_fd = np.zeros(self.n) dP_dprecurve_fd = np.zeros(self.n) for i in range(self.n): pc = np.array(precurve) delta = 1e-6*pc[i] pc[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=pc, precurveTip=precurveTip) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dprecurve_fd[i] = (Td - T) / delta dQ_dprecurve_fd[i] = (Qd - Q) / delta dP_dprecurve_fd[i] = (Pd - P) / delta np.testing.assert_allclose(dT_dprecurve_fd, dT_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dQ_dprecurve_fd, dQ_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dP_dprecurve_fd, dP_dprecurve, rtol=3e-4, atol=1e-8) def test_dprecurve3(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) CP, CT, CQ, dCP_ds, dCT_ds, dCQ_ds, dCP_dv, dCT_dv, dCQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dprecurve = dCT_dv[0, 3, :] dCQ_dprecurve = dCQ_dv[0, 3, :] dCP_dprecurve = dCP_dv[0, 3, :] dCT_dprecurve_fd = np.zeros(self.n) dCQ_dprecurve_fd = np.zeros(self.n) dCP_dprecurve_fd = np.zeros(self.n) for i in range(self.n): pc = np.array(precurve) delta = 1e-6*pc[i] pc[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=pc, precurveTip=precurveTip) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dprecurve_fd[i] = (CTd - CT) / delta dCQ_dprecurve_fd[i] = (CQd - CQ) / delta dCP_dprecurve_fd[i] = (CPd - CP) / delta np.testing.assert_allclose(dCT_dprecurve_fd, dCT_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dCQ_dprecurve_fd, dCQ_dprecurve, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dCP_dprecurve_fd, dCP_dprecurve, rtol=3e-4, atol=1e-8) def test_dpresweep1(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve = \ rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dpresweep = dNp_dX[5, :] dTp_dpresweep = dTp_dX[5, :] dNp_dpresweep_fd = np.zeros(self.n) dTp_dpresweep_fd = np.zeros(self.n) for i in range(self.n): ps = np.array(presweep) delta = 1e-6*ps[i] ps[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=ps, presweepTip=presweepTip) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dpresweep_fd[i] = (Npd[i] - Np[i]) / delta dTp_dpresweep_fd[i] = (Tpd[i] - Tp[i]) / delta np.testing.assert_allclose(dNp_dpresweep_fd, dNp_dpresweep, rtol=1e-5, atol=1e-8) np.testing.assert_allclose(dTp_dpresweep_fd, dTp_dpresweep, rtol=1e-5, atol=1e-8) def test_dpresweep2(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) P, T, Q, dP_ds, dT_ds, dQ_ds, dP_dv, dT_dv, dQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dpresweep = dT_dv[0, 4, :] dQ_dpresweep = dQ_dv[0, 4, :] dP_dpresweep = dP_dv[0, 4, :] dT_dpresweep_fd = np.zeros(self.n) dQ_dpresweep_fd = np.zeros(self.n) dP_dpresweep_fd = np.zeros(self.n) for i in range(self.n): ps = np.array(presweep) delta = 1e-6*ps[i] ps[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=ps, presweepTip=presweepTip) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dpresweep_fd[i] = (Td - T) / delta dQ_dpresweep_fd[i] = (Qd - Q) / delta dP_dpresweep_fd[i] = (Pd - P) / delta np.testing.assert_allclose(dT_dpresweep_fd, dT_dpresweep, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dQ_dpresweep_fd, dQ_dpresweep, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dP_dpresweep_fd, dP_dpresweep, rtol=3e-4, atol=1e-8) def test_dpresweep3(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) CP, CT, CQ, dCP_ds, dCT_ds, dCQ_ds, dCP_dv, dCT_dv, dCQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dpresweep = dCT_dv[0, 4, :] dCQ_dpresweep = dCQ_dv[0, 4, :] dCP_dpresweep = dCP_dv[0, 4, :] dCT_dpresweep_fd = np.zeros(self.n) dCQ_dpresweep_fd = np.zeros(self.n) dCP_dpresweep_fd = np.zeros(self.n) for i in range(self.n): ps = np.array(presweep) delta = 1e-6*ps[i] ps[i] += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=ps, presweepTip=presweepTip) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dpresweep_fd[i] = (CTd - CT) / delta dCQ_dpresweep_fd[i] = (CQd - CQ) / delta dCP_dpresweep_fd[i] = (CPd - CP) / delta np.testing.assert_allclose(dCT_dpresweep_fd, dCT_dpresweep, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dCQ_dpresweep_fd, dCQ_dpresweep, rtol=3e-4, atol=1e-8) np.testing.assert_allclose(dCP_dpresweep_fd, dCP_dpresweep, rtol=3e-4, atol=1e-8) def test_dprecurveTip1(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve = \ rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) pct = float(precurveTip) delta = 1e-6*pct pct += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=precurve, precurveTip=pct) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dprecurveTip_fd = (Npd - Np) / delta dTp_dprecurveTip_fd = (Tpd - Tp) / delta np.testing.assert_allclose(dNp_dprecurveTip_fd, 0.0, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dTp_dprecurveTip_fd, 0.0, rtol=1e-4, atol=1e-8) def test_dprecurveTip2(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) P, T, Q, dP_ds, dT_ds, dQ_ds, dP_dv, dT_dv, dQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dprecurveTip = dT_ds[0, 5] dQ_dprecurveTip = dQ_ds[0, 5] dP_dprecurveTip = dP_ds[0, 5] pct = float(precurveTip) delta = 1e-6*pct pct += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=precurve, precurveTip=pct) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dprecurveTip_fd = (Td - T) / delta dQ_dprecurveTip_fd = (Qd - Q) / delta dP_dprecurveTip_fd = (Pd - P) / delta np.testing.assert_allclose(dT_dprecurveTip_fd, dT_dprecurveTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dQ_dprecurveTip_fd, dQ_dprecurveTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dP_dprecurveTip_fd, dP_dprecurveTip, rtol=1e-4, atol=1e-8) def test_dprecurveTip3(self): precurve = np.linspace(1, 10, self.n) precurveTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, precurve=precurve, precurveTip=precurveTip) CP, CT, CQ, dCP_ds, dCT_ds, dCQ_ds, dCP_dv, dCT_dv, dCQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dprecurveTip = dCT_ds[0, 5] dCQ_dprecurveTip = dCQ_ds[0, 5] dCP_dprecurveTip = dCP_ds[0, 5] pct = float(precurveTip) delta = 1e-6*pct pct += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, precurve=precurve, precurveTip=pct) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dprecurveTip_fd = (CTd - CT) / delta dCQ_dprecurveTip_fd = (CQd - CQ) / delta dCP_dprecurveTip_fd = (CPd - CP) / delta np.testing.assert_allclose(dCT_dprecurveTip_fd, dCT_dprecurveTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dCQ_dprecurveTip_fd, dCQ_dprecurveTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dCP_dprecurveTip_fd, dCP_dprecurveTip, rtol=1e-4, atol=1e-8) def test_dpresweepTip1(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) Np, Tp, dNp_dX, dTp_dX, dNp_dpresweep, dTp_dpresweep = \ rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) pst = float(presweepTip) delta = 1e-6*pst pst += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=presweep, presweepTip=pst) Npd, Tpd = rotor.distributedAeroLoads(self.Uinf, self.Omega, self.pitch, self.azimuth) dNp_dpresweepTip_fd = (Npd - Np) / delta dTp_dpresweepTip_fd = (Tpd - Tp) / delta np.testing.assert_allclose(dNp_dpresweepTip_fd, 0.0, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dTp_dpresweepTip_fd, 0.0, rtol=1e-4, atol=1e-8) def test_dpresweepTip2(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) P, T, Q, dP_ds, dT_ds, dQ_ds, dP_dv, dT_dv, dQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dpresweepTip = dT_ds[0, 6] dQ_dpresweepTip = dQ_ds[0, 6] dP_dpresweepTip = dP_ds[0, 6] pst = float(presweepTip) delta = 1e-6*pst pst += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=presweep, presweepTip=pst) Pd, Td, Qd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=False) dT_dpresweepTip_fd = (Td - T) / delta dQ_dpresweepTip_fd = (Qd - Q) / delta dP_dpresweepTip_fd = (Pd - P) / delta np.testing.assert_allclose(dT_dpresweepTip_fd, dT_dpresweepTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dQ_dpresweepTip_fd, dQ_dpresweepTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dP_dpresweepTip_fd, dP_dpresweepTip, rtol=1e-4, atol=1e-8) def test_dpresweepTip3(self): presweep = np.linspace(1, 10, self.n) presweepTip = 10.1 precone = 0.0 rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=True, presweep=presweep, presweepTip=presweepTip) CP, CT, CQ, dCP_ds, dCT_ds, dCQ_ds, dCP_dv, dCT_dv, dCQ_dv = \ rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dpresweepTip = dCT_ds[0, 6] dCQ_dpresweepTip = dCQ_ds[0, 6] dCP_dpresweepTip = dCP_ds[0, 6] pst = float(presweepTip) delta = 1e-6*pst pst += delta rotor = CCBlade(self.r, self.chord, self.theta, self.af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, derivatives=False, presweep=presweep, presweepTip=pst) CPd, CTd, CQd = rotor.evaluate([self.Uinf], [self.Omega], [self.pitch], coefficient=True) dCT_dpresweepTip_fd = (CTd - CT) / delta dCQ_dpresweepTip_fd = (CQd - CQ) / delta dCP_dpresweepTip_fd = (CPd - CP) / delta np.testing.assert_allclose(dCT_dpresweepTip_fd, dCT_dpresweepTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dCQ_dpresweepTip_fd, dCQ_dpresweepTip, rtol=1e-4, atol=1e-8) np.testing.assert_allclose(dCP_dpresweepTip_fd, dCP_dpresweepTip, rtol=1e-4, atol=1e-8)
class CCBladeLoads(Component): def __init__(self, naero, npower): super(CCBladeLoads, self).__init__() """blade element momentum code""" # inputs self.add_param('V_load', val=0.0, units='m/s', desc='hub height wind speed') self.add_param('Omega_load', val=0.0, units='rpm', desc='rotor rotation speed') self.add_param('pitch_load', val=0.0, units='deg', desc='blade pitch setting') self.add_param('azimuth_load', val=0.0, units='deg', desc='blade azimuthal location') # outputs self.add_output('loads_r', val=np.zeros(naero), units='m', desc='radial positions along blade going toward tip') self.add_output('loads_Px', val=np.zeros(naero), units='N/m', desc='distributed loads in blade-aligned x-direction') self.add_output('loads_Py', val=np.zeros(naero), units='N/m', desc='distributed loads in blade-aligned y-direction') self.add_output('loads_Pz', val=np.zeros(naero), units='N/m', desc='distributed loads in blade-aligned z-direction') # corresponding setting for loads self.add_output('loads_V', val=0.0, units='m/s', desc='hub height wind speed') self.add_output('loads_Omega', val=0.0, units='rpm', desc='rotor rotation speed') self.add_output('loads_pitch', val=0.0, units='deg', desc='pitch angle') self.add_output('loads_azimuth', val=0.0, units='deg', desc='azimuthal angle') # (potential) variables self.add_param('r', val=np.zeros(naero), units='m', desc='radial locations where blade is defined (should be increasing and not go all the way to hub or tip)') self.add_param('chord', val=np.zeros(naero), units='m', desc='chord length at each section') self.add_param('theta', val=np.zeros(naero), units='deg', desc='twist angle at each section (positive decreases angle of attack)') self.add_param('Rhub', val=0.0, units='m', desc='hub radius') self.add_param('Rtip', val=0.0, units='m', desc='tip radius') self.add_param('hubHt', val=0.0, units='m', desc='hub height') self.add_param('precone', val=0.0, desc='precone angle', units='deg') self.add_param('tilt', val=0.0, desc='shaft tilt', units='deg') self.add_param('yaw', val=0.0, desc='yaw error', units='deg') # TODO: I've not hooked up the gradients for these ones yet. self.add_param('precurve', val=np.zeros(naero), units='m', desc='precurve at each section') self.add_param('precurveTip', val=0.0, units='m', desc='precurve at tip') # parameters self.add_param('airfoils', val=[0]*naero, desc='CCAirfoil instances', pass_by_obj=True) self.add_param('B', val=0, desc='number of blades', pass_by_obj=True) self.add_param('rho', val=0.0, units='kg/m**3', desc='density of air') self.add_param('mu', val=0.0, units='kg/(m*s)', desc='dynamic viscosity of air') self.add_param('shearExp', val=0.0, desc='shear exponent') self.add_param('nSector', val=4, desc='number of sectors to divide rotor face into in computing thrust and power', pass_by_obj=True) self.add_param('tiploss', val=True, desc='include Prandtl tip loss model', pass_by_obj=True) self.add_param('hubloss', val=True, desc='include Prandtl hub loss model', pass_by_obj=True) self.add_param('wakerotation', val=True, desc='include effect of wake rotation (i.e., tangential induction factor is nonzero)', pass_by_obj=True) self.add_param('usecd', val=True, desc='use drag coefficient in computing induction factors', pass_by_obj=True) self.naero = naero self.deriv_options['form'] = 'central' self.deriv_options['step_calc'] = 'relative' def solve_nonlinear(self, params, unknowns, resids): self.r = params['r'] self.chord = params['chord'] self.theta = params['theta'] self.Rhub = params['Rhub'] self.Rtip = params['Rtip'] self.hubHt = params['hubHt'] self.precone = params['precone'] self.tilt = params['tilt'] self.yaw = params['yaw'] self.precurve = params['precurve'] self.precurveTip = params['precurveTip'] self.airfoils = params['airfoils'] self.B = params['B'] self.rho = params['rho'] self.mu = params['mu'] self.shearExp = params['shearExp'] self.nSector = params['nSector'] self.tiploss = params['tiploss'] self.hubloss = params['hubloss'] self.wakerotation = params['wakerotation'] self.usecd = params['usecd'] self.V_load = params['V_load'] self.Omega_load = params['Omega_load'] self.pitch_load = params['pitch_load'] self.azimuth_load = params['azimuth_load'] if len(self.precurve) == 0: self.precurve = np.zeros_like(self.r) # airfoil files n = len(self.airfoils) af = self.airfoils # af = [0]*n # for i in range(n): # af[i] = CCAirfoil.initFromAerodynFile(self.airfoil_files[i]) self.ccblade = CCBlade(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # distributed loads Np, Tp, self.dNp, self.dTp \ = self.ccblade.distributedAeroLoads(self.V_load, self.Omega_load, self.pitch_load, self.azimuth_load) # concatenate loads at root/tip unknowns['loads_r'] = self.r # conform to blade-aligned coordinate system unknowns['loads_Px'] = Np unknowns['loads_Py'] = -Tp unknowns['loads_Pz'] = 0*Np # return other outputs needed unknowns['loads_V'] = self.V_load unknowns['loads_Omega'] = self.Omega_load unknowns['loads_pitch'] = self.pitch_load unknowns['loads_azimuth'] = self.azimuth_load def list_deriv_vars(self): inputs = ('r', 'chord', 'theta', 'Rhub', 'Rtip', 'hubHt', 'precone', 'tilt', 'yaw', 'V_load', 'Omega_load', 'pitch_load', 'azimuth_load', 'precurve') outputs = ('loads_r', 'loads_Px', 'loads_Py', 'loads_Pz', 'loads_V', 'loads_Omega', 'loads_pitch', 'loads_azimuth') return inputs, outputs def linearize(self, params, unknowns, resids): dNp = self.dNp dTp = self.dTp n = len(self.r) dr_dr = np.vstack([np.zeros(n), np.eye(n), np.zeros(n)]) dr_dRhub = np.zeros(n+2) dr_dRtip = np.zeros(n+2) dr_dRhub[0] = 1.0 dr_dRtip[-1] = 1.0 dV = np.zeros(4*n+10) dV[3*n+6] = 1.0 dOmega = np.zeros(4*n+10) dOmega[3*n+7] = 1.0 dpitch = np.zeros(4*n+10) dpitch[3*n+8] = 1.0 dazimuth = np.zeros(4*n+10) dazimuth[3*n+9] = 1.0 J = {} zero = np.zeros(self.naero) J['loads_r', 'r'] = dr_dr J['loads_r', 'Rhub'] = dr_dRhub J['loads_r', 'Rtip'] = dr_dRtip J['loads_Px', 'r'] = np.vstack([zero, dNp['dr'], zero]) J['loads_Px', 'chord'] = np.vstack([zero, dNp['dchord'], zero]) J['loads_Px', 'theta'] = np.vstack([zero, dNp['dtheta'], zero]) J['loads_Px', 'Rhub'] = np.concatenate([[0.0], np.squeeze(dNp['dRhub']), [0.0]]) J['loads_Px', 'Rtip'] = np.concatenate([[0.0], np.squeeze(dNp['dRtip']), [0.0]]) J['loads_Px', 'hubHt'] = np.concatenate([[0.0], np.squeeze(dNp['dhubHt']), [0.0]]) J['loads_Px', 'precone'] = np.concatenate([[0.0], np.squeeze(dNp['dprecone']), [0.0]]) J['loads_Px', 'tilt'] = np.concatenate([[0.0], np.squeeze(dNp['dtilt']), [0.0]]) J['loads_Px', 'yaw'] = np.concatenate([[0.0], np.squeeze(dNp['dyaw']), [0.0]]) J['loads_Px', 'V_load'] = np.concatenate([[0.0], np.squeeze(dNp['dUinf']), [0.0]]) J['loads_Px', 'Omega_load'] = np.concatenate([[0.0], np.squeeze(dNp['dOmega']), [0.0]]) J['loads_Px', 'pitch_load'] = np.concatenate([[0.0], np.squeeze(dNp['dpitch']), [0.0]]) J['loads_Px', 'azimuth_load'] = np.concatenate([[0.0], np.squeeze(dNp['dazimuth']), [0.0]]) J['loads_Px', 'precurve'] = np.vstack([zero, dNp['dprecurve'], zero]) J['loads_Py', 'r'] = np.vstack([zero, -dTp['dr'], zero]) J['loads_Py', 'chord'] = np.vstack([zero, -dTp['dchord'], zero]) J['loads_Py', 'theta'] = np.vstack([zero, -dTp['dtheta'], zero]) J['loads_Py', 'Rhub'] = np.concatenate([[0.0], -np.squeeze(dTp['dRhub']), [0.0]]) J['loads_Py', 'Rtip'] = np.concatenate([[0.0], -np.squeeze(dTp['dRtip']), [0.0]]) J['loads_Py', 'hubHt'] = np.concatenate([[0.0], -np.squeeze(dTp['dhubHt']), [0.0]]) J['loads_Py', 'precone'] = np.concatenate([[0.0], -np.squeeze(dTp['dprecone']), [0.0]]) J['loads_Py', 'tilt'] = np.concatenate([[0.0], -np.squeeze(dTp['dtilt']), [0.0]]) J['loads_Py', 'yaw'] = np.concatenate([[0.0], -np.squeeze(dTp['dyaw']), [0.0]]) J['loads_Py', 'V_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dUinf']), [0.0]]) J['loads_Py', 'Omega_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dOmega']), [0.0]]) J['loads_Py', 'pitch_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dpitch']), [0.0]]) J['loads_Py', 'azimuth_load'] = np.concatenate([[0.0], -np.squeeze(dTp['dazimuth']), [0.0]]) J['loads_Py', 'precurve'] = np.vstack([zero, -dTp['dprecurve'], zero]) J['loads_V', 'V_load'] = 1.0 J['loads_Omega', 'Omega_load'] = 1.0 J['loads_pitch', 'pitch_load'] = 1.0 J['loads_azimuth', 'azimuth_load'] = 1.0 return J
class RegulatedPowerCurve(Component): # Implicit COMPONENT def __init__(self, naero, n_pc, n_pc_spline, regulation_reg_II5 = True, regulation_reg_III = False): super(RegulatedPowerCurve, self).__init__() # parameters self.add_param('control_Vin', val=0.0, units='m/s', desc='cut-in wind speed') self.add_param('control_Vout', val=0.0, units='m/s', desc='cut-out wind speed') self.add_param('control_ratedPower', val=0.0, units='W', desc='electrical rated power') self.add_param('control_minOmega', val=0.0, units='rpm', desc='minimum allowed rotor rotation speed') self.add_param('control_maxOmega', val=0.0, units='rpm', desc='maximum allowed rotor rotation speed') self.add_param('control_maxTS', val=0.0, units='m/s', desc='maximum allowed blade tip speed') self.add_param('control_tsr', val=0.0, desc='tip-speed ratio in Region 2 (should be optimized externally)') self.add_param('control_pitch', val=0.0, units='deg', desc='pitch angle in region 2 (and region 3 for fixed pitch machines)') self.add_param('drivetrainType', val=DRIVETRAIN_TYPE['GEARED'], pass_by_obj=True) self.add_param('drivetrainEff', val=0.0, desc='overwrite drivetrain model with a given efficiency, used for FAST analysis') self.add_param('r', val=np.zeros(naero), units='m', desc='radial locations where blade is defined (should be increasing and not go all the way to hub or tip)') self.add_param('chord', val=np.zeros(naero), units='m', desc='chord length at each section') self.add_param('theta', val=np.zeros(naero), units='deg', desc='twist angle at each section (positive decreases angle of attack)') self.add_param('Rhub', val=0.0, units='m', desc='hub radius') self.add_param('Rtip', val=0.0, units='m', desc='tip radius') self.add_param('hubHt', val=0.0, units='m', desc='hub height') self.add_param('precone', val=0.0, units='deg', desc='precone angle', ) self.add_param('tilt', val=0.0, units='deg', desc='shaft tilt', ) self.add_param('yaw', val=0.0, units='deg', desc='yaw error', ) self.add_param('precurve', val=np.zeros(naero), units='m', desc='precurve at each section') self.add_param('precurveTip', val=0.0, units='m', desc='precurve at tip') self.add_param('airfoils', val=[0]*naero, desc='CCAirfoil instances', pass_by_obj=True) self.add_param('B', val=0, desc='number of blades', pass_by_obj=True) self.add_param('rho', val=0.0, units='kg/m**3', desc='density of air') self.add_param('mu', val=0.0, units='kg/(m*s)', desc='dynamic viscosity of air') self.add_param('shearExp', val=0.0, desc='shear exponent') self.add_param('nSector', val=4, desc='number of sectors to divide rotor face into in computing thrust and power', pass_by_obj=True) self.add_param('tiploss', val=True, desc='include Prandtl tip loss model', pass_by_obj=True) self.add_param('hubloss', val=True, desc='include Prandtl hub loss model', pass_by_obj=True) self.add_param('wakerotation', val=True, desc='include effect of wake rotation (i.e., tangential induction factor is nonzero)', pass_by_obj=True) self.add_param('usecd', val=True, desc='use drag coefficient in computing induction factors', pass_by_obj=True) # outputs self.add_output('V', val=np.zeros(n_pc), units='m/s', desc='wind vector') self.add_output('Omega', val=np.zeros(n_pc), units='rpm', desc='rotor rotational speed') self.add_output('pitch', val=np.zeros(n_pc), units='deg', desc='rotor pitch schedule') self.add_output('P', val=np.zeros(n_pc), units='W', desc='rotor electrical power') self.add_output('T', val=np.zeros(n_pc), units='N', desc='rotor aerodynamic thrust') self.add_output('Q', val=np.zeros(n_pc), units='N*m', desc='rotor aerodynamic torque') self.add_output('M', val=np.zeros(n_pc), units='N*m', desc='blade root moment') self.add_output('Cp', val=np.zeros(n_pc), desc='rotor electrical power coefficient') self.add_output('V_spline', val=np.zeros(n_pc_spline), units='m/s', desc='wind vector') self.add_output('P_spline', val=np.zeros(n_pc_spline), units='W', desc='rotor electrical power') self.add_output('V_R25', val=0.0, units='m/s', desc='region 2.5 transition wind speed') self.add_output('rated_V', val=0.0, units='m/s', desc='rated wind speed') self.add_output('rated_Omega', val=0.0, units='rpm', desc='rotor rotation speed at rated') self.add_output('rated_pitch', val=0.0, units='deg', desc='pitch setting at rated') self.add_output('rated_T', val=0.0, units='N', desc='rotor aerodynamic thrust at rated') self.add_output('rated_Q', val=0.0, units='N*m', desc='rotor aerodynamic torque at rated') self.add_output('ax_induct_cutin', val=np.zeros(naero), desc='rotor axial induction at cut-in wind speed along blade span') self.add_output('tang_induct_cutin', val=np.zeros(naero), desc='rotor tangential induction at cut-in wind speed along blade span') self.add_output('aoa_cutin', val=np.zeros(naero), desc='angle of attack distribution along blade span at cut-in wind speed') self.naero = naero self.n_pc = n_pc self.n_pc_spline = n_pc_spline self.lock_pitchII = False self.regulation_reg_II5 = regulation_reg_II5 self.regulation_reg_III = regulation_reg_III self.deriv_options['form'] = 'central' self.deriv_options['step_calc'] = 'relative' def solve_nonlinear(self, params, unknowns, resids): self.ccblade = CCBlade(params['r'], params['chord'], params['theta'], params['airfoils'], params['Rhub'], params['Rtip'], params['B'], params['rho'], params['mu'], params['precone'], params['tilt'], params['yaw'], params['shearExp'], params['hubHt'], params['nSector']) Uhub = np.linspace(params['control_Vin'],params['control_Vout'], self.n_pc) P_aero = np.zeros_like(Uhub) Cp_aero = np.zeros_like(Uhub) P = np.zeros_like(Uhub) Cp = np.zeros_like(Uhub) T = np.zeros_like(Uhub) Q = np.zeros_like(Uhub) M = np.zeros_like(Uhub) Omega = np.zeros_like(Uhub) pitch = np.zeros_like(Uhub) + params['control_pitch'] Omega_max = min([params['control_maxTS'] / params['Rtip'], params['control_maxOmega']*np.pi/30.]) # Region II for i in range(len(Uhub)): Omega[i] = Uhub[i] * params['control_tsr'] / params['Rtip'] P_aero, T, Q, M, Cp_aero, _, _, _ = self.ccblade.evaluate(Uhub, Omega * 30. / np.pi, pitch, coefficients=True) P, eff = CSMDrivetrain(P_aero, params['control_ratedPower'], params['drivetrainType']) Cp = Cp_aero*eff # search for Region 2.5 bounds for i in range(len(Uhub)): if Omega[i] > Omega_max and P[i] < params['control_ratedPower']: Omega[i] = Omega_max Uhub[i] = Omega[i] * params['Rtip'] / params['control_tsr'] P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch[i]], coefficients=True) P[i], eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff regionIIhalf = True i_IIhalf_start = i unknowns['V_R25'] = Uhub[i] break if P[i] > params['control_ratedPower']: regionIIhalf = False break def maxPregionIIhalf(pitch, Uhub, Omega): Uhub_i = Uhub Omega_i = Omega pitch = pitch P, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch], coefficients=False) return -P # Solve for regoin 2.5 pitch options = {} options['disp'] = False options['xatol'] = 1.e-2 if regionIIhalf == True: for i in range(i_IIhalf_start + 1, len(Uhub)): Omega[i] = Omega_max pitch0 = pitch[i-1] bnds = [pitch0 - 10., pitch0 + 10.] pitch_regionIIhalf = minimize_scalar(lambda x: maxPregionIIhalf(x, Uhub[i], Omega[i]), bounds=bnds, method='bounded', options=options)['x'] pitch[i] = pitch_regionIIhalf P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch[i]], coefficients=True) P[i], eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff if P[i] > params['control_ratedPower']: break def constantPregionIII(pitch, Uhub, Omega): Uhub_i = Uhub Omega_i = Omega pitch = pitch P_aero, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch], coefficients=False) P, eff = CSMDrivetrain(P_aero, params['control_ratedPower'], params['drivetrainType']) return abs(P - params['control_ratedPower']) if regionIIhalf == True: # Rated conditions def min_Uhub_rated_II12(min_params): return min_params[1] def get_Uhub_rated_II12(min_params): Uhub_i = min_params[1] Omega_i = Omega_max pitch = min_params[0] P_aero_i, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch], coefficients=False) P_i,eff = CSMDrivetrain(P_aero_i, params['control_ratedPower'], params['drivetrainType']) return abs(P_i - params['control_ratedPower']) x0 = [pitch[i] + 2. , Uhub[i]] bnds = [(pitch0, pitch0 + 10.),(Uhub[i-1],Uhub[i+1])] const = {} const['type'] = 'eq' const['fun'] = get_Uhub_rated_II12 params_rated = minimize(min_Uhub_rated_II12, x0, method='SLSQP', tol = 1.e-2, bounds=bnds, constraints=const) U_rated = params_rated.x[1] if not np.isnan(U_rated): Uhub[i] = U_rated pitch[i] = params_rated.x[0] else: print('Regulation trajectory is struggling to find a solution for rated wind speed. Check rotor_aeropower.py') Omega[i] = Omega_max P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch0], coefficients=True) P_i, eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff P[i] = params['control_ratedPower'] else: # Rated conditions def get_Uhub_rated_noII12(pitch, Uhub): Uhub_i = Uhub Omega_i = min([Uhub_i * params['control_tsr'] / params['Rtip'], Omega_max]) pitch_i = pitch P_aero_i, _, _, _ = self.ccblade.evaluate([Uhub_i], [Omega_i * 30. / np.pi], [pitch_i], coefficients=False) P_i, eff = CSMDrivetrain(P_aero_i, params['control_ratedPower'], params['drivetrainType']) return abs(P_i - params['control_ratedPower']) bnds = [Uhub[i-1], Uhub[i+1]] U_rated = minimize_scalar(lambda x: get_Uhub_rated_noII12(pitch[i], x), bounds=bnds, method='bounded', options=options)['x'] if not np.isnan(U_rated): Uhub[i] = U_rated else: print('Regulation trajectory is struggling to find a solution for rated wind speed. Check rotor_aeropower.py') Omega[i] = min([Uhub[i] * params['control_tsr'] / params['Rtip'], Omega_max]) pitch0 = pitch[i] P_aero[i], T[i], Q[i], M[i], Cp_aero[i], _, _, _ = self.ccblade.evaluate([Uhub[i]], [Omega[i] * 30. / np.pi], [pitch0], coefficients=True) P[i], eff = CSMDrivetrain(P_aero[i], params['control_ratedPower'], params['drivetrainType']) Cp[i] = Cp_aero[i]*eff for j in range(i + 1,len(Uhub)): Omega[j] = Omega[i] if self.regulation_reg_III == True: pitch0 = pitch[j-1] bnds = [pitch0, pitch0 + 15.] pitch_regionIII = minimize_scalar(lambda x: constantPregionIII(x, Uhub[j], Omega[j]), bounds=bnds, method='bounded', options=options)['x'] pitch[j] = pitch_regionIII P_aero[j], T[j], Q[j], M[j], Cp_aero[j], _, _, _ = self.ccblade.evaluate([Uhub[j]], [Omega[j] * 30. / np.pi], [pitch[j]], coefficients=True) P[j], eff = CSMDrivetrain(P_aero[j], params['control_ratedPower'], params['drivetrainType']) Cp[j] = Cp_aero[j]*eff if abs(P[j] - params['control_ratedPower']) > 1e+4: print('The pitch in region III is not being determined correctly at wind speed ' + str(Uhub[j]) + ' m/s') P[j] = params['control_ratedPower'] T[j] = T[j-1] Q[j] = P[j] / Omega[j] M[j] = M[j-1] pitch[j] = pitch[j-1] Cp[j] = P[j] / (0.5 * params['rho'] * np.pi * params['Rtip']**2 * Uhub[i]**3) P[j] = params['control_ratedPower'] else: P[j] = params['control_ratedPower'] T[j] = 0 Q[j] = Q[i] M[j] = 0 pitch[j] = 0 Cp[j] = P[j] / (0.5 * params['rho'] * np.pi * params['Rtip']**2 * Uhub[i]**3) unknowns['T'] = T unknowns['Q'] = Q unknowns['Omega'] = Omega * 30. / np.pi unknowns['P'] = P unknowns['Cp'] = Cp unknowns['V'] = Uhub unknowns['M'] = M unknowns['pitch'] = pitch self.ccblade.induction_inflow = True a_regII, ap_regII, alpha_regII = self.ccblade.distributedAeroLoads(Uhub[0], Omega[0] * 30. / np.pi, pitch[0], 0.0) # Fit spline to powercurve for higher grid density spline = PchipInterpolator(Uhub, P) V_spline = np.linspace(params['control_Vin'],params['control_Vout'], num=self.n_pc_spline) P_spline = spline(V_spline) # outputs idx_rated = list(Uhub).index(U_rated) unknowns['rated_V'] = U_rated unknowns['rated_Omega'] = Omega[idx_rated] * 30. / np.pi unknowns['rated_pitch'] = pitch[idx_rated] unknowns['rated_T'] = T[idx_rated] unknowns['rated_Q'] = Q[idx_rated] unknowns['V_spline'] = V_spline unknowns['P_spline'] = P_spline unknowns['ax_induct_cutin'] = a_regII unknowns['tang_induct_cutin'] = ap_regII unknowns['aoa_cutin'] = alpha_regII
class CCBladePower(Component): def __init__(self, naero, npower): super(CCBladePower, self).__init__() """blade element momentum code""" # inputs self.add_param('Uhub', val=np.zeros(npower), units='m/s', desc='hub height wind speed') self.add_param('Omega', val=np.zeros(npower), units='rpm', desc='rotor rotation speed') self.add_param('pitch', val=np.zeros(npower), units='deg', desc='blade pitch setting') # outputs self.add_output('T', val=np.zeros(npower), units='N', desc='rotor aerodynamic thrust') self.add_output('Q', val=np.zeros(npower), units='N*m', desc='rotor aerodynamic torque') self.add_output('P', val=np.zeros(npower), units='W', desc='rotor aerodynamic power') # (potential) variables self.add_param('r', val=np.zeros(naero), units='m', desc='radial locations where blade is defined (should be increasing and not go all the way to hub or tip)') self.add_param('chord', val=np.zeros(naero), units='m', desc='chord length at each section') self.add_param('theta', val=np.zeros(naero), units='deg', desc='twist angle at each section (positive decreases angle of attack)') self.add_param('Rhub', val=0.0, units='m', desc='hub radius') self.add_param('Rtip', val=0.0, units='m', desc='tip radius') self.add_param('hubHt', val=0.0, units='m', desc='hub height') self.add_param('precone', val=0.0, desc='precone angle', units='deg') self.add_param('tilt', val=0.0, desc='shaft tilt', units='deg') self.add_param('yaw', val=0.0, desc='yaw error', units='deg') # TODO: I've not hooked up the gradients for these ones yet. self.add_param('precurve', val=np.zeros(naero), units='m', desc='precurve at each section') self.add_param('precurveTip', val=0.0, units='m', desc='precurve at tip') # parameters self.add_param('airfoils', val=[0]*naero, desc='CCAirfoil instances', pass_by_obj=True) self.add_param('B', val=0, desc='number of blades', pass_by_obj=True) self.add_param('rho', val=0.0, units='kg/m**3', desc='density of air') self.add_param('mu', val=0.0, units='kg/(m*s)', desc='dynamic viscosity of air') self.add_param('shearExp', val=0.0, desc='shear exponent') self.add_param('nSector', val=4, desc='number of sectors to divide rotor face into in computing thrust and power', pass_by_obj=True) self.add_param('tiploss', val=True, desc='include Prandtl tip loss model', pass_by_obj=True) self.add_param('hubloss', val=True, desc='include Prandtl hub loss model', pass_by_obj=True) self.add_param('wakerotation', val=True, desc='include effect of wake rotation (i.e., tangential induction factor is nonzero)', pass_by_obj=True) self.add_param('usecd', val=True, desc='use drag coefficient in computing induction factors', pass_by_obj=True) self.naero = naero self.deriv_options['form'] = 'central' self.deriv_options['step_calc'] = 'relative' def solve_nonlinear(self, params, unknowns, resids): self.r = params['r'] self.chord = params['chord'] self.theta = params['theta'] self.Rhub = params['Rhub'] self.Rtip = params['Rtip'] self.hubHt = params['hubHt'] self.precone = params['precone'] self.tilt = params['tilt'] self.yaw = params['yaw'] self.precurve = params['precurve'] self.precurveTip = params['precurveTip'] self.airfoils = params['airfoils'] self.B = params['B'] self.rho = params['rho'] self.mu = params['mu'] self.shearExp = params['shearExp'] self.nSector = params['nSector'] self.tiploss = params['tiploss'] self.hubloss = params['hubloss'] self.wakerotation = params['wakerotation'] self.usecd = params['usecd'] self.Uhub = params['Uhub'] self.Omega = params['Omega'] self.pitch = params['pitch'] self.ccblade = CCBlade(self.r, self.chord, self.theta, self.airfoils, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) # power, thrust, torque self.P, self.T, self.Q, self.M, self.dP, self.dT, self.dQ \ = self.ccblade.evaluate(self.Uhub, self.Omega, self.pitch, coefficients=False) unknowns['T'] = self.T unknowns['Q'] = self.Q unknowns['P'] = self.P def list_deriv_vars(self): inputs = ('precone', 'tilt', 'hubHt', 'Rhub', 'Rtip', 'yaw', 'Uhub', 'Omega', 'pitch', 'r', 'chord', 'theta', 'precurve', 'precurveTip') outputs = ('P', 'T', 'Q') return inputs, outputs def linearize(self, params, unknowns, resids): dP = self.dP dT = self.dT dQ = self.dQ J = {} J['P', 'precone'] = dP['dprecone'] J['P', 'tilt'] = dP['dtilt'] J['P', 'hubHt'] = dP['dhubHt'] J['P', 'Rhub'] = dP['dRhub'] J['P', 'Rtip'] = dP['dRtip'] J['P', 'yaw'] = dP['dyaw'] J['P', 'Uhub'] = dP['dUinf'] J['P', 'Omega'] = dP['dOmega'] J['P', 'pitch'] = dP['dpitch'] J['P', 'r'] = dP['dr'] J['P', 'chord'] = dP['dchord'] J['P', 'theta'] = dP['dtheta'] J['P', 'precurve'] = dP['dprecurve'] J['P', 'precurveTip'] = dP['dprecurveTip'] J['T', 'precone'] = dT['dprecone'] J['T', 'tilt'] = dT['dtilt'] J['T', 'hubHt'] = dT['dhubHt'] J['T', 'Rhub'] = dT['dRhub'] J['T', 'Rtip'] = dT['dRtip'] J['T', 'yaw'] = dT['dyaw'] J['T', 'Uhub'] = dT['dUinf'] J['T', 'Omega'] = dT['dOmega'] J['T', 'pitch'] = dT['dpitch'] J['T', 'r'] = dT['dr'] J['T', 'chord'] = dT['dchord'] J['T', 'theta'] = dT['dtheta'] J['T', 'precurve'] = dT['dprecurve'] J['T', 'precurveTip'] = dT['dprecurveTip'] J['Q', 'precone'] = dQ['dprecone'] J['Q', 'tilt'] = dQ['dtilt'] J['Q', 'hubHt'] = dQ['dhubHt'] J['Q', 'Rhub'] = dQ['dRhub'] J['Q', 'Rtip'] = dQ['dRtip'] J['Q', 'yaw'] = dQ['dyaw'] J['Q', 'Uhub'] = dQ['dUinf'] J['Q', 'Omega'] = dQ['dOmega'] J['Q', 'pitch'] = dQ['dpitch'] J['Q', 'r'] = dQ['dr'] J['Q', 'chord'] = dQ['dchord'] J['Q', 'theta'] = dQ['dtheta'] J['Q', 'precurve'] = dQ['dprecurve'] J['Q', 'precurveTip'] = dQ['dprecurveTip'] return J
airfoil_types[6] = afinit('DU21_A17.dat') airfoil_types[7] = afinit('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) # 3 ---------- # 4 ---------- # set conditions Uinf = 10.0 tsr = 7.55 pitch = 0.0 Omega = Uinf*tsr/Rtip * 30.0/pi # convert to RPM azimuth = 0.0 # evaluate distributed loads Np, Tp = rotor.distributedAeroLoads(Uinf, Omega, pitch, azimuth)
class CCBlade(AeroBase): """blade element momentum code""" # (potential) variables r = Array(iotype='in', units='m', desc='radial locations where blade is defined (should be increasing and not go all the way to hub or tip)') chord = Array(iotype='in', units='m', desc='chord length at each section') theta = Array(iotype='in', units='deg', desc='twist angle at each section (positive decreases angle of attack)') Rhub = Float(iotype='in', units='m', desc='hub radius') Rtip = Float(iotype='in', units='m', desc='tip radius') hubHt = Float(iotype='in', units='m', desc='hub height') precone = Float(0.0, iotype='in', desc='precone angle', units='deg') tilt = Float(0.0, iotype='in', desc='shaft tilt', units='deg') yaw = Float(0.0, iotype='in', desc='yaw error', units='deg') # TODO: I've not hooked up the gradients for these ones yet. precurve = Array(iotype='in', units='m', desc='precurve at each section') precurveTip = Float(0.0, iotype='in', units='m', desc='precurve at tip') # parameters airfoil_files = List(Str, iotype='in', desc='names of airfoil file') coordinate_files = List(Str, iotype='in', desc='names of airfoil file') B = Int(3, iotype='in', desc='number of blades') rho = Float(1.225, iotype='in', units='kg/m**3', desc='density of air') mu = Float(1.81206e-5, iotype='in', units='kg/(m*s)', desc='dynamic viscosity of air') shearExp = Float(0.2, iotype='in', desc='shear exponent') nSector = Int(4, iotype='in', desc='number of sectors to divide rotor face into in computing thrust and power') tiploss = Bool(True, iotype='in', desc='include Prandtl tip loss model') hubloss = Bool(True, iotype='in', desc='include Prandtl hub loss model') wakerotation = Bool(True, iotype='in', desc='include effect of wake rotation (i.e., tangential induction factor is nonzero)') usecd = Bool(True, iotype='in', desc='use drag coefficient in computing induction factors') af = Array(iotype='in', desc='CCBlade objects') missing_deriv_policy = 'assume_zero' def execute(self): if len(self.precurve) == 0: self.precurve = np.zeros_like(self.r) af = self.af self.ccblade = CCBlade_PY(self.r, self.chord, self.theta, af, self.Rhub, self.Rtip, self.B, self.rho, self.mu, self.precone, self.tilt, self.yaw, self.shearExp, self.hubHt, self.nSector, self.precurve, self.precurveTip, tiploss=self.tiploss, hubloss=self.hubloss, wakerotation=self.wakerotation, usecd=self.usecd, derivatives=True) if self.run_case == 'power': # power, thrust, torque self.P, self.T, self.Q, self.dP, self.dT, self.dQ \ = self.ccblade.evaluate(self.Uhub, self.Omega, self.pitch, coefficient=False) elif self.run_case == 'loads': # distributed loads Np, Tp, self.dNp, self.dTp \ = self.ccblade.distributedAeroLoads(self.V_load, self.Omega_load, self.pitch_load, self.azimuth_load) # concatenate loads at root/tip self.loads.r = np.concatenate([[self.Rhub], self.r, [self.Rtip]]) Np = np.concatenate([[0.0], Np, [0.0]]) Tp = np.concatenate([[0.0], Tp, [0.0]]) # conform to blade-aligned coordinate system self.loads.Px = Np self.loads.Py = -Tp self.loads.Pz = 0*Np # return other outputs needed self.loads.V = self.V_load self.loads.Omega = self.Omega_load self.loads.pitch = self.pitch_load self.loads.azimuth = self.azimuth_load def list_deriv_vars(self): if self.run_case == 'power': inputs = ('precone', 'tilt', 'hubHt', 'Rhub', 'Rtip', 'yaw', 'Uhub', 'Omega', 'pitch', 'r', 'chord', 'theta', 'precurve', 'precurveTip') outputs = ('P', 'T', 'Q') elif self.run_case == 'loads': inputs = ('r', 'chord', 'theta', 'Rhub', 'Rtip', 'hubHt', 'precone', 'tilt', 'yaw', 'V_load', 'Omega_load', 'pitch_load', 'azimuth_load', 'precurve') outputs = ('loads.r', 'loads.Px', 'loads.Py', 'loads.Pz', 'loads.V', 'loads.Omega', 'loads.pitch', 'loads.azimuth') return inputs, outputs def provideJ(self): if self.run_case == 'power': dP = self.dP dT = self.dT dQ = self.dQ jP = hstack([dP['dprecone'], dP['dtilt'], dP['dhubHt'], dP['dRhub'], dP['dRtip'], dP['dyaw'], dP['dUinf'], dP['dOmega'], dP['dpitch'], dP['dr'], dP['dchord'], dP['dtheta'], dP['dprecurve'], dP['dprecurveTip']]) jT = hstack([dT['dprecone'], dT['dtilt'], dT['dhubHt'], dT['dRhub'], dT['dRtip'], dT['dyaw'], dT['dUinf'], dT['dOmega'], dT['dpitch'], dT['dr'], dT['dchord'], dT['dtheta'], dT['dprecurve'], dT['dprecurveTip']]) jQ = hstack([dQ['dprecone'], dQ['dtilt'], dQ['dhubHt'], dQ['dRhub'], dQ['dRtip'], dQ['dyaw'], dQ['dUinf'], dQ['dOmega'], dQ['dpitch'], dQ['dr'], dQ['dchord'], dQ['dtheta'], dQ['dprecurve'], dQ['dprecurveTip']]) J = vstack([jP, jT, jQ]) elif self.run_case == 'loads': dNp = self.dNp dTp = self.dTp n = len(self.r) dr_dr = vstack([np.zeros(n), np.eye(n), np.zeros(n)]) dr_dRhub = np.zeros(n+2) dr_dRtip = np.zeros(n+2) dr_dRhub[0] = 1.0 dr_dRtip[-1] = 1.0 dr = hstack([dr_dr, np.zeros((n+2, 2*n)), dr_dRhub, dr_dRtip, np.zeros((n+2, 8+n))]) jNp = hstack([dNp['dr'], dNp['dchord'], dNp['dtheta'], dNp['dRhub'], dNp['dRtip'], dNp['dhubHt'], dNp['dprecone'], dNp['dtilt'], dNp['dyaw'], dNp['dUinf'], dNp['dOmega'], dNp['dpitch'], dNp['dazimuth'], dNp['dprecurve']]) jTp = hstack([dTp['dr'], dTp['dchord'], dTp['dtheta'], dTp['dRhub'], dTp['dRtip'], dTp['dhubHt'], dTp['dprecone'], dTp['dtilt'], dTp['dyaw'], dTp['dUinf'], dTp['dOmega'], dTp['dpitch'], dTp['dazimuth'], dTp['dprecurve']]) dPx = vstack([np.zeros(4*n+10), jNp, np.zeros(4*n+10)]) dPy = vstack([np.zeros(4*n+10), -jTp, np.zeros(4*n+10)]) dPz = np.zeros((n+2, 4*n+10)) dV = np.zeros(4*n+10) dV[3*n+6] = 1.0 dOmega = np.zeros(4*n+10) dOmega[3*n+7] = 1.0 dpitch = np.zeros(4*n+10) dpitch[3*n+8] = 1.0 dazimuth = np.zeros(4*n+10) dazimuth[3*n+9] = 1.0 J = vstack([dr, dPx, dPy, dPz, dV, dOmega, dpitch, dazimuth]) return J
def CCrotor( Rtip=base_R, Rhub=1.5, hubHt=90.0, shearExp=0.2, rho=1.225, mu=1.81206e-5, path_to_af="5MW_AFFiles", ): r = (Rtip / base_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 = (Rtip / base_R) * 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 # In this initial version, hard-code to be NREL 5MW afinit = CCAirfoil.initFromAerodynFile # just for shorthand basepath = path_to_af # load all airfoils airfoil_types = [0] * 8 airfoil_types[0] = afinit(path.join(basepath, "Cylinder1.dat")) airfoil_types[1] = afinit(path.join(basepath, "Cylinder2.dat")) airfoil_types[2] = afinit(path.join(basepath, "DU40_A17.dat")) airfoil_types[3] = afinit(path.join(basepath, "DU35_A17.dat")) airfoil_types[4] = afinit(path.join(basepath, "DU30_A17.dat")) airfoil_types[5] = afinit(path.join(basepath, "DU25_A17.dat")) airfoil_types[6] = afinit(path.join(basepath, "DU21_A17.dat")) airfoil_types[7] = afinit(path.join(basepath, "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]] tilt = -5.0 precone = 2.5 yaw = 0.0 nSector = 8 # azimuthal discretization rotor = CCBlade( r, chord, theta, af, Rhub, Rtip, B, rho, mu, precone, tilt, yaw, shearExp, hubHt, nSector, ) return rotor