Esempio n. 1
0
    def evaluate(self, Uinf, Omega, pitch, coefficient=False):
        """Run the aerodynamic analysis at the specified conditions.

        Parameters
        ----------
        Uinf : array_like (m/s)
            hub height wind speed
        Omega : array_like (RPM)
            rotor rotation speed
        pitch : array_like (deg)
            blade pitch setting
        coefficient : bool, optional
            if True, results are returned in nondimensional form

        Returns
        -------
        P or CP : ndarray (W)
            power or power coefficient
        T or CT : ndarray (N)
            thrust or thrust coefficient (magnitude)
        Q or CQ : ndarray (N*m)
            torque or torque coefficient (magnitude)
        dP_ds or dCP_ds : ndarray, shape=(npts, 7)
            derivative of power or power coefficient w.r.t. scalar quantities
            thus dP_ds[i, 4] = dP_dRtip for input condition i
        dT_ds or dCT_ds : ndarray, shape=(npts, 7)
            derivative of thrust or thrust coefficient w.r.t. scalar quantities
            thus dT_ds[i, 4] = dT_dRtip for input condition i
        dQ_ds or dCQ_ds : ndarray, shape=(npts, 7)
            derivative of torque or torque coefficient w.r.t. scalar quantities
            thus dQ_ds[i, 4] = dQ_dRtip for input condition i
        dP_dv or dCP_dv : ndarray, shape=(npts, 5, n)
            derivative of power or power coefficient w.r.t. vector quantities
            thus dP_dv[i, 1, j] = dP_dchord_j for input condition i
        dT_dv or dCT_dv : ndarray, shape=(npts, 5, n)
            derivative of thrust or thrust coefficient w.r.t. vector quantities
            thus dT_dv[i, 1, j] = dT_dchord_j for input condition i
        dQ_dv or dCQ_dv : ndarray, shape=(npts, 5, n)
            derivative of torque or torque coefficient w.r.t. vector quantities
            thus dQ_dv[i, 1, j] = dQ_dchord_j for input condition i

        Notes
        -----

        CP = P / (q * Uinf * A)

        CT = T / (q * A)

        CQ = Q / (q * A * R)

        The rotor radius R, may not actually be Rtip if precone and precurve are both nonzero
        ``R = Rtip*cos(precone) + precurveTip*sin(precone)``

        order of scalar quantities is: s = [precone, tilt, hubHt, Rhub, Rtip, precurvetip, presweeptip]

        order of vector quantities is: v = [r, chord, theta, precurve, presweep]


        """

        # rename
        args = (self.r, self.precurve, self.presweep, self.precone,
            self.Rhub, self.Rtip, self.precurveTip, self.presweepTip)
        nsec = self.nSector

        # initialize
        Uinf = np.array(Uinf)
        Omega = np.array(Omega)
        pitch = np.array(pitch)

        npts = len(Uinf)
        T = np.zeros(npts)
        Q = np.zeros(npts)
        P = np.zeros(npts)

        if self.derivatives:
            dT_ds = np.zeros((npts, 7))
            dQ_ds = np.zeros((npts, 7))
            dT_dv = np.zeros((npts, 5, len(self.r)))
            dQ_dv = np.zeros((npts, 5, len(self.r)))

        for i in range(npts):  # iterate across conditions

            for j in range(nsec):  # integrate across azimuth
                azimuth = 360.0*float(j)/nsec

                if not self.derivatives:
                    # contribution from this azimuthal location
                    Np, Tp = self.distributedAeroLoads(Uinf[i], Omega[i], pitch[i], azimuth)

                else:

                    Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve = \
                        self.distributedAeroLoads(Uinf[i], Omega[i], pitch[i], azimuth)

                    dT_ds_sub, dQ_ds_sub, dT_dv_sub, dQ_dv_sub = \
                        self.__thrustTorqueDeriv(Np, Tp, dNp_dX, dTp_dX, dNp_dprecurve, dTp_dprecurve, *args)

                    dT_ds[i, :] += self.B * dT_ds_sub / nsec
                    dQ_ds[i, :] += self.B * dQ_ds_sub / nsec
                    dT_dv[i, :, :] += self.B * dT_dv_sub / nsec
                    dQ_dv[i, :, :] += self.B * dQ_dv_sub / nsec


                Tsub, Qsub = _bem.thrusttorque(Np, Tp, *args)

                T[i] += self.B * Tsub / nsec
                Q[i] += self.B * Qsub / nsec


        # Power
        P = Q * Omega*pi/30.0  # RPM to rad/s

        # normalize if necessary
        if coefficient:
            q = 0.5 * self.rho * Uinf**2
            A = pi * self.rotorR**2
            CP = P / (q * A * Uinf)
            CT = T / (q * A)
            CQ = Q / (q * self.rotorR * A)

            if self.derivatives:

                dR_ds = np.array([-self.Rtip*sin(self.precone)*pi/180.0 + self.precurveTip*cos(self.precone)*pi/180.0,
                    0.0, 0.0, 0.0, cos(self.precone), sin(self.precone), 0.0])

                dCT_ds = dT_ds / (q*A) - dR_ds * 2.0*CT/self.rotorR
                dCT_dv = dT_dv / (q*A)

                dCQ_ds = dQ_ds / (q*self.rotorR*A) - dR_ds * 3.0*CQ/self.rotorR
                dCQ_dv = dQ_dv / (q*self.rotorR*A)

                dCP_ds = dQ_ds * CP/Q - dR_ds * 2.0*CP/self.rotorR
                dCP_dv = dQ_dv * CP/Q

                return CP, CT, CQ, dCP_ds, dCT_ds, dCQ_ds, dCP_dv, dCT_dv, dCQ_dv

            else:
                return CP, CT, CQ


        if self.derivatives:
            # scalars = [precone, tilt, hubHt, Rhub, Rtip, precurvetip, presweeptip]
            # vectors = [r, chord, theta, precurve, presweep]

            dP_ds = dQ_ds * Omega*pi/30.0
            dP_dv = dQ_dv * Omega*pi/30.0

            return P, T, Q, dP_ds, dT_ds, dQ_ds, dP_dv, dT_dv, dQ_dv

        else:
            return P, T, Q
Esempio n. 2
0
    def evaluate(self, Uinf, Omega, pitch, coefficient=False):
        """Run the aerodynamic analysis at the specified conditions.

        Parameters
        ----------
        Uinf : array_like (m/s)
            hub height wind speed
        Omega : array_like (RPM)
            rotor rotation speed
        pitch : array_like (deg)
            blade pitch setting
        coefficient : bool, optional
            if True, results are returned in nondimensional form

        Returns
        -------
        P or CP : ndarray (W)
            power or power coefficient
        T or CT : ndarray (N)
            thrust or thrust coefficient (magnitude)
        Q or CQ : ndarray (N*m)
            torque or torque coefficient (magnitude)
        dP or dCP : dictionary of arrays (present only if derivatives==True)
            derivatives of power or power coefficient.  Each item in dictionary is a Jacobian.
            The array sizes and keys are below
            npts is the number of conditions (len(Uinf)),
            n = number of stations along blade (len(r))

            npts x 1: 'dprecone', 'dtilt', 'dhubHt', 'dRhub', 'dRtip', 'dprecurveTip', 'dpresweepTip', 'dyaw'

            npts x npts: 'dUinf', 'dOmega', 'dpitch'

            npts x n: 'dr', 'dchord', 'dtheta', 'dprecurve', 'dpresweep'

            for example dP_dr = dP['dr']  (where dP_dr is an npts x n array)
            and dP_dr[i, j] = dP_i / dr_j
        dT or dCT : dictionary of arrays (present only if derivatives==True)
            derivative of thrust or thrust coefficient.  Same format as dP and dCP
        dQ or dCQ : dictionary of arrays (present only if derivatives==True)
            derivative of torque or torque coefficient.  Same format as dP and dCP


        Notes
        -----

        CP = P / (q * Uinf * A)

        CT = T / (q * A)

        CQ = Q / (q * A * R)

        The rotor radius R, may not actually be Rtip if precone and precurve are both nonzero
        ``R = Rtip*cos(precone) + precurveTip*sin(precone)``

        """

        # rename
        args = (self.r, self.precurve, self.presweep, self.precone, self.Rhub,
                self.Rtip, self.precurveTip, self.presweepTip)
        nsec = self.nSector

        # initialize
        Uinf = np.array(Uinf)
        Omega = np.array(Omega)
        pitch = np.array(pitch)

        npts = len(Uinf)
        T = np.zeros(npts)
        Q = np.zeros(npts)
        P = np.zeros(npts)

        if self.derivatives:
            dT_ds = np.zeros((npts, 11))
            dQ_ds = np.zeros((npts, 11))
            dT_dv = np.zeros((npts, 5, len(self.r)))
            dQ_dv = np.zeros((npts, 5, len(self.r)))

        for i in range(npts):  # iterate across conditions

            for j in range(nsec):  # integrate across azimuth
                azimuth = 360.0 * float(j) / nsec

                if not self.derivatives:
                    # contribution from this azimuthal location
                    Np, Tp = self.distributedAeroLoads(Uinf[i], Omega[i],
                                                       pitch[i], azimuth)

                else:

                    Np, Tp, dNp, dTp = self.distributedAeroLoads(
                        Uinf[i], Omega[i], pitch[i], azimuth)

                    dT_ds_sub, dQ_ds_sub, dT_dv_sub, dQ_dv_sub = self.__thrustTorqueDeriv(
                        Np, Tp, self._dNp_dX, self._dTp_dX,
                        self._dNp_dprecurve, self._dTp_dprecurve, *args)

                    dT_ds[i, :] += self.B * dT_ds_sub / nsec
                    dQ_ds[i, :] += self.B * dQ_ds_sub / nsec
                    dT_dv[i, :, :] += self.B * dT_dv_sub / nsec
                    dQ_dv[i, :, :] += self.B * dQ_dv_sub / nsec

                Tsub, Qsub = _bem.thrusttorque(Np, Tp, *args)

                T[i] += self.B * Tsub / nsec
                Q[i] += self.B * Qsub / nsec

        # Power
        P = Q * Omega * pi / 30.0  # RPM to rad/s

        # normalize if necessary
        if coefficient:
            q = 0.5 * self.rho * Uinf**2
            A = pi * self.rotorR**2
            CP = P / (q * A * Uinf)
            CT = T / (q * A)
            CQ = Q / (q * self.rotorR * A)

            if self.derivatives:

                # s = [precone, tilt, hubHt, Rhub, Rtip, precurvetip, presweeptip, yaw, Uinf, Omega, pitch]

                dR_ds = np.array([
                    -self.Rtip * sin(self.precone) * pi / 180.0 +
                    self.precurveTip * cos(self.precone) * pi / 180.0, 0.0,
                    0.0, 0.0,
                    cos(self.precone),
                    sin(self.precone), 0.0, 0.0, 0.0, 0.0, 0.0
                ])
                dR_ds = np.dot(np.ones(
                    (npts,
                     1)), np.array([dR_ds
                                    ]))  # same for each operating condition

                dA_ds = 2 * pi * self.rotorR * dR_ds

                dU_ds = np.zeros((npts, 11))
                dU_ds[:, 8] = 1.0

                dOmega_ds = np.zeros((npts, 11))
                dOmega_ds[:, 9] = 1.0

                dq_ds = (self.rho * Uinf * dU_ds.T).T

                dCT_ds = (CT * (dT_ds.T / T - dA_ds.T / A - dq_ds.T / q)).T
                dCT_dv = (dT_dv.T / (q * A)).T

                dCQ_ds = (CQ * (dQ_ds.T / Q - dA_ds.T / A - dq_ds.T / q -
                                dR_ds.T / self.rotorR)).T
                dCQ_dv = (dQ_dv.T / (q * self.rotorR * A)).T

                dCP_ds = (CP * (dQ_ds.T / Q + dOmega_ds.T / Omega -
                                dA_ds.T / A - dq_ds.T / q - dU_ds.T / Uinf)).T
                dCP_dv = (dQ_dv.T * CP / Q).T

                # pack derivatives into dictionary
                dCT, dCQ, dCP = self.__thrustTorqueDictionary(
                    dCT_ds, dCQ_ds, dCP_ds, dCT_dv, dCQ_dv, dCP_dv, npts)

                return CP, CT, CQ, dCP, dCT, dCQ

            else:
                return CP, CT, CQ

        if self.derivatives:
            # scalars = [precone, tilt, hubHt, Rhub, Rtip, precurvetip, presweeptip, yaw, Uinf, Omega, pitch]
            # vectors = [r, chord, theta, precurve, presweep]

            dP_ds = (dQ_ds.T * Omega * pi / 30.0).T
            dP_ds[:, 9] += Q * pi / 30.0
            dP_dv = (dQ_dv.T * Omega * pi / 30.0).T

            # pack derivatives into dictionary
            dT, dQ, dP = self.__thrustTorqueDictionary(dT_ds, dQ_ds, dP_ds,
                                                       dT_dv, dQ_dv, dP_dv,
                                                       npts)

            return P, T, Q, dP, dT, dQ

        else:
            return P, T, Q
Esempio n. 3
0
    def evaluate(self, Uinf, Omega, pitch, coefficient=False):
        """Run the aerodynamic analysis at the specified conditions.

        Parameters
        ----------
        Uinf : array_like (m/s)
            hub height wind speed
        Omega : array_like (RPM)
            rotor rotation speed
        pitch : array_like (deg)
            blade pitch setting
        coefficient : bool, optional
            if True, results are returned in nondimensional form

        Returns
        -------
        P or CP : ndarray (W)
            power or power coefficient
        T or CT : ndarray (N)
            thrust or thrust coefficient (magnitude)
        Q or CQ : ndarray (N*m)
            torque or torque coefficient (magnitude)
        dP or dCP : dictionary of arrays (present only if derivatives==True)
            derivatives of power or power coefficient.  Each item in dictionary is a Jacobian.
            The array sizes and keys are below
            npts is the number of conditions (len(Uinf)),
            n = number of stations along blade (len(r))

            npts x 1: 'dprecone', 'dtilt', 'dhubHt', 'dRhub', 'dRtip', 'dprecurveTip', 'dpresweepTip', 'dyaw'

            npts x npts: 'dUinf', 'dOmega', 'dpitch'

            npts x n: 'dr', 'dchord', 'dtheta', 'dprecurve', 'dpresweep'

            for example dP_dr = dP['dr']  (where dP_dr is an npts x n array)
            and dP_dr[i, j] = dP_i / dr_j
        dT or dCT : dictionary of arrays (present only if derivatives==True)
            derivative of thrust or thrust coefficient.  Same format as dP and dCP
        dQ or dCQ : dictionary of arrays (present only if derivatives==True)
            derivative of torque or torque coefficient.  Same format as dP and dCP


        Notes
        -----

        CP = P / (q * Uinf * A)

        CT = T / (q * A)

        CQ = Q / (q * A * R)

        The rotor radius R, may not actually be Rtip if precone and precurve are both nonzero
        ``R = Rtip*cos(precone) + precurveTip*sin(precone)``

        """

        # rename
        args = (
            self.r,
            self.precurve,
            self.presweep,
            self.precone,
            self.Rhub,
            self.Rtip,
            self.precurveTip,
            self.presweepTip,
        )
        nsec = self.nSector

        # initialize
        Uinf = np.array(Uinf)
        Omega = np.array(Omega)
        pitch = np.array(pitch)

        npts = len(Uinf)
        T = np.zeros(npts)
        Q = np.zeros(npts)
        P = np.zeros(npts)

        if self.derivatives:
            dT_ds = np.zeros((npts, 11))
            dQ_ds = np.zeros((npts, 11))
            dT_dv = np.zeros((npts, 5, len(self.r)))
            dQ_dv = np.zeros((npts, 5, len(self.r)))

        for i in range(npts):  # iterate across conditions

            for j in range(nsec):  # integrate across azimuth
                azimuth = 360.0 * float(j) / nsec

                if not self.derivatives:
                    # contribution from this azimuthal location
                    Np, Tp = self.distributedAeroLoads(Uinf[i], Omega[i], pitch[i], azimuth)

                else:

                    Np, Tp, dNp, dTp = self.distributedAeroLoads(Uinf[i], Omega[i], pitch[i], azimuth)

                    dT_ds_sub, dQ_ds_sub, dT_dv_sub, dQ_dv_sub = self.__thrustTorqueDeriv(
                        Np, Tp, self._dNp_dX, self._dTp_dX, self._dNp_dprecurve, self._dTp_dprecurve, *args
                    )

                    dT_ds[i, :] += self.B * dT_ds_sub / nsec
                    dQ_ds[i, :] += self.B * dQ_ds_sub / nsec
                    dT_dv[i, :, :] += self.B * dT_dv_sub / nsec
                    dQ_dv[i, :, :] += self.B * dQ_dv_sub / nsec

                Tsub, Qsub = _bem.thrusttorque(Np, Tp, *args)

                T[i] += self.B * Tsub / nsec
                Q[i] += self.B * Qsub / nsec

        # Power
        P = Q * Omega * pi / 30.0  # RPM to rad/s

        # normalize if necessary
        if coefficient:
            q = 0.5 * self.rho * Uinf ** 2
            A = pi * self.rotorR ** 2
            CP = P / (q * A * Uinf)
            CT = T / (q * A)
            CQ = Q / (q * self.rotorR * A)

            if self.derivatives:

                # s = [precone, tilt, hubHt, Rhub, Rtip, precurvetip, presweeptip, yaw, Uinf, Omega, pitch]

                dR_ds = np.array(
                    [
                        -self.Rtip * sin(self.precone) * pi / 180.0 + self.precurveTip * cos(self.precone) * pi / 180.0,
                        0.0,
                        0.0,
                        0.0,
                        cos(self.precone),
                        sin(self.precone),
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ]
                )
                dR_ds = np.dot(np.ones((npts, 1)), np.array([dR_ds]))  # same for each operating condition

                dA_ds = 2 * pi * self.rotorR * dR_ds

                dU_ds = np.zeros((npts, 11))
                dU_ds[:, 8] = 1.0

                dOmega_ds = np.zeros((npts, 11))
                dOmega_ds[:, 9] = 1.0

                dq_ds = (self.rho * Uinf * dU_ds.T).T

                dCT_ds = (CT * (dT_ds.T / T - dA_ds.T / A - dq_ds.T / q)).T
                dCT_dv = (dT_dv.T / (q * A)).T

                dCQ_ds = (CQ * (dQ_ds.T / Q - dA_ds.T / A - dq_ds.T / q - dR_ds.T / self.rotorR)).T
                dCQ_dv = (dQ_dv.T / (q * self.rotorR * A)).T

                dCP_ds = (CP * (dQ_ds.T / Q + dOmega_ds.T / Omega - dA_ds.T / A - dq_ds.T / q - dU_ds.T / Uinf)).T
                dCP_dv = (dQ_dv.T * CP / Q).T

                # pack derivatives into dictionary
                dCT, dCQ, dCP = self.__thrustTorqueDictionary(dCT_ds, dCQ_ds, dCP_ds, dCT_dv, dCQ_dv, dCP_dv, npts)

                return CP, CT, CQ, dCP, dCT, dCQ

            else:
                return CP, CT, CQ

        if self.derivatives:
            # scalars = [precone, tilt, hubHt, Rhub, Rtip, precurvetip, presweeptip, yaw, Uinf, Omega, pitch]
            # vectors = [r, chord, theta, precurve, presweep]

            dP_ds = (dQ_ds.T * Omega * pi / 30.0).T
            dP_ds[:, 9] += Q * pi / 30.0
            dP_dv = (dQ_dv.T * Omega * pi / 30.0).T

            # pack derivatives into dictionary
            dT, dQ, dP = self.__thrustTorqueDictionary(dT_ds, dQ_ds, dP_ds, dT_dv, dQ_dv, dP_dv, npts)

            return P, T, Q, dP, dT, dQ

        else:
            return P, T, Q