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)
Example #14
0
    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)
Example #15
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
Example #16
0
    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
Example #18
0
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)
Example #19
0
    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)
Example #21
0
    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
Example #22
0
    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
Example #24
0
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
Example #25
0
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)
Example #27
0
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
Example #28
0
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
Example #29
0
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
Example #30
0
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)
Example #31
0
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
Example #32
0
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