def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):

        Cup = -1.0 if discrete_inputs["upwind"] else 1.0
        tilt = float(np.deg2rad(inputs["tilt"]))

        rotor_mass = inputs["blades_mass"] + inputs["hub_system_mass"]
        nac_mass = inputs["nacelle_mass"]

        # rna mass
        outputs["rotor_mass"] = rotor_mass
        outputs["rna_mass"] = rotor_mass + nac_mass

        # rna cm
        shaft0 = inputs["shaft_start"]
        hub_cm = inputs["hub_system_cm"]
        L_drive = inputs["L_drive"]
        hub_cm = shaft0 + (L_drive + hub_cm) * np.array(
            [Cup * np.cos(tilt), 0.0, np.sin(tilt)])
        outputs["rna_cm"] = (rotor_mass * hub_cm + nac_mass *
                             inputs["nacelle_cm"]) / outputs["rna_mass"]

        # rna I
        hub_I = util.assembleI(
            util.rotateI(inputs["hub_system_I"], -Cup * tilt, axis="y"))
        blades_I = util.assembleI(
            util.rotateI(inputs["blades_I"], -Cup * tilt, axis="y"))
        nac_I_TT = util.assembleI(inputs["nacelle_I_TT"])
        rotor_I = blades_I + hub_I

        R = hub_cm
        rotor_I_TT = rotor_I + rotor_mass * (np.dot(R, R) * np.eye(3) -
                                             np.outer(R, R))
        outputs["rna_I_TT"] = util.unassembleI(rotor_I_TT + nac_I_TT)
Exemple #2
0
 def testRotateI(self):
     I = np.arange(6) + 1
     th = np.deg2rad(45)
     Irot = util.rotateI(I, th, axis="z")
     npt.assert_almost_equal(
         Irot,
         np.array([-2.5, 5.5, 3, -0.5, -0.5 * np.sqrt(2),
                   5.5 * np.sqrt(2)]))
Exemple #3
0
    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):

        # Unpack inputs
        L_12 = float(inputs["L_12"])
        L_h1 = float(inputs["L_h1"])
        L_generator = float(inputs["L_generator"])
        L_overhang = float(inputs["overhang"])
        H_drive = float(inputs["drive_height"])
        tilt = float(np.deg2rad(inputs["tilt"]))
        D_access = float(inputs["access_diameter"])
        D_nose = inputs["nose_diameter"]
        D_lss = inputs["lss_diameter"]
        D_top = float(inputs["D_top"])
        D_hub = float(inputs["hub_diameter"])
        t_nose = inputs["nose_wall_thickness"]
        t_lss = inputs["lss_wall_thickness"]
        t_bed = inputs["bedplate_wall_thickness"]
        upwind = discrete_inputs["upwind"]
        lss_rho = float(inputs["lss_rho"])
        bedplate_rho = float(inputs["bedplate_rho"])

        # ------- Discretization ----------------
        L_grs = 0.5 * L_h1
        L_gsn = L_generator - L_grs - L_12
        L_2n = 2.0 * L_gsn

        # Length of lss and nose
        L_lss = L_12 + L_h1
        L_nose = L_12 + L_2n
        outputs["L_lss"] = L_lss
        outputs["L_nose"] = L_nose

        # Total length from bedplate to hub flange
        ds = 0.5 * np.ones(2)
        s_drive = np.cumsum(np.r_[0.0, L_2n * ds, L_12 * ds, L_h1 * ds])
        L_drive = s_drive[-1]
        outputs["L_drive"] = L_drive

        # From Overhang input (dist from center of tower measured in yaw-aligned
        # c.s.-parallel to ground), compute bedplate length and height
        L_bedplate = L_overhang - (L_drive + 0.5 * D_hub) * np.cos(tilt)
        constr_Ldrive = L_bedplate - 0.5 * D_top  # Should be > 0
        if constr_Ldrive < 0:
            L_bedplate = 0.5 * D_top
        H_bedplate = H_drive - (L_drive + 0.5 * D_hub) * np.sin(
            tilt)  # Keep eccentricity under control
        outputs["L_bedplate"] = L_bedplate
        outputs["H_bedplate"] = H_bedplate

        # Discretize the drivetrain from bedplate to hub
        s_mb1 = s_drive[4]
        s_mb2 = s_drive[2]
        s_rotor = s_drive[-2]
        s_stator = s_drive[1]
        s_nose = s_drive[:5]
        s_lss = s_drive[2:]

        # Store outputs
        # outputs['s_drive']     = np.sort(s_drive)
        outputs["s_rotor"] = s_rotor
        outputs["s_stator"] = s_stator
        outputs["s_nose"] = s_nose
        outputs["s_lss"] = s_lss
        outputs["s_generator"] = 0.5 * (s_rotor + s_stator)
        outputs["s_mb1"] = s_mb1
        outputs["s_mb2"] = s_mb2
        # ------------------------------------

        # ------------ Bedplate geometry and coordinates -------------
        # Define reference/centroidal axis
        # Origin currently set like standard ellipse eqns, but will shift below to being at tower top
        # The end point of 90 deg isn't exactly right for non-zero tilt, but leaving that for later
        n_points = 12
        if upwind:
            rad = np.linspace(0.0, 0.5 * np.pi, n_points)
        else:
            rad = np.linspace(np.pi, 0.5 * np.pi, n_points)

        # Make sure we have the right number of bedplate thickness points
        t_bed = np.interp(rad, np.linspace(rad[0], rad[-1], len(t_bed)), t_bed)

        # Centerline
        x_c = L_bedplate * np.cos(rad)
        z_c = H_bedplate * np.sin(rad)

        # Points on the outermost ellipse
        x_outer = (L_bedplate + 0.5 * D_top) * np.cos(rad)
        z_outer = (H_bedplate + 0.5 * D_nose[0]) * np.sin(rad)

        # Points on the innermost ellipse
        x_inner = (L_bedplate - 0.5 * D_top) * np.cos(rad)
        z_inner = (H_bedplate - 0.5 * D_nose[0]) * np.sin(rad)

        # Cross-sectional properties
        D_bed = np.sqrt((z_outer - z_inner)**2 + (x_outer - x_inner)**2)
        r_bed_o = 0.5 * D_bed
        r_bed_i = r_bed_o - t_bed
        A_bed = np.pi * (r_bed_o**2 - r_bed_i**2)

        # This finds the central angle (rad2) given the parametric angle (rad)
        rad2 = np.arctan(L_bedplate / H_bedplate * np.tan(rad))

        # arc length from eccentricity of the centroidal ellipse using incomplete elliptic integral of the second kind
        if L_bedplate >= H_bedplate:
            ecc = np.sqrt(1 - (H_bedplate / L_bedplate)**2)
            arc = L_bedplate * np.diff(ellipeinc(rad2, ecc))
        else:
            ecc = np.sqrt(1 - (L_bedplate / H_bedplate)**2)
            arc = H_bedplate * np.diff(ellipeinc(rad2, ecc))

        # Mass and MoI properties
        x_c_sec = util.nodal2sectional(x_c)[0]
        z_c_sec = util.nodal2sectional(z_c)[0]
        # R_c_sec = np.sqrt( x_c_sec**2 + z_c_sec**2 ) # unnecesary
        mass = util.nodal2sectional(A_bed)[0] * arc * bedplate_rho
        mass_tot = mass.sum()
        cm = np.array([np.sum(mass * x_c_sec), 0.0,
                       np.sum(mass * z_c_sec)]) / mass_tot
        # For I, could do integral over sectional I, rotate axes by rad2, and then parallel axis theorem
        # we simplify by assuming lumped point mass.  TODO: Find a good way to check this?  Torus shell?
        I_bed = util.assembleI(np.zeros(6))
        for k in range(len(mass)):
            r_bed_o_k = 0.5 * (r_bed_o[k] + r_bed_o[k + 1])
            r_bed_i_k = 0.5 * (r_bed_i[k] + r_bed_i[k + 1])
            I_sec = mass[k] * np.array([
                0.5 * (r_bed_o_k**2 + r_bed_i_k**2),
                (1.0 / 12.0) * (3 * (r_bed_o_k**2 + r_bed_i_k**2) + arc[k]**2),
                (1.0 / 12.0) * (3 * (r_bed_o_k**2 + r_bed_i_k**2) + arc[k]**2),
            ])
            I_sec_rot = util.rotateI(I_sec, 0.5 * np.pi - rad2[k], axis="y")
            R_k = np.array([x_c_sec[k] - x_c[0], 0.0, z_c_sec[k]])
            I_bed += util.assembleI(I_sec_rot) + mass[k] * (
                np.dot(R_k, R_k) * np.eye(3) - np.outer(R_k, R_k))

        # Now shift origin to be at tower top
        cm[0] -= x_c[0]
        x_inner -= x_c[0]
        x_outer -= x_c[0]
        x_c -= x_c[0]

        outputs["bedplate_mass"] = mass_tot
        outputs["bedplate_cm"] = cm
        outputs["bedplate_I"] = util.unassembleI(I_bed)

        # Geometry outputs
        outputs["x_bedplate"] = x_c
        outputs["z_bedplate"] = z_c
        outputs["x_bedplate_inner"] = x_inner
        outputs["z_bedplate_inner"] = z_inner
        outputs["x_bedplate_outer"] = x_outer
        outputs["z_bedplate_outer"] = z_outer
        outputs["D_bedplate"] = D_bed
        outputs["t_bedplate"] = t_bed
        # ------------------------------------

        # ------- Constraints ----------------
        outputs["constr_access"] = np.c_[D_lss - 2 * t_lss - D_nose -
                                         0.25 * D_access,
                                         D_nose - 2 * t_nose - D_access]
        outputs["constr_length"] = constr_Ldrive  # Should be > 0
        outputs["constr_height"] = H_bedplate  # Should be > 0
        outputs["constr_ecc"] = L_bedplate - H_bedplate  # Should be > 0
        # ------------------------------------

        # ------- Nose, lss, and bearing properties ----------------
        # Now is a good time to set bearing diameters
        outputs["D_bearing1"] = D_lss[-1] - t_lss[-1] - D_nose[0]
        outputs["D_bearing2"] = D_lss[-1] - t_lss[-1] - D_nose[-1]

        # Compute center of mass based on area
        m_nose, cm_nose, I_nose = rod_prop(s_nose, D_nose, t_nose,
                                           bedplate_rho)
        outputs["nose_mass"] = m_nose
        outputs["nose_cm"] = cm_nose
        outputs["nose_I"] = I_nose

        m_lss, cm_lss, I_lss = rod_prop(s_lss, D_lss, t_lss, lss_rho)
        outputs["lss_mass"] = m_lss
        outputs["lss_cm"] = cm_lss
        outputs["lss_I"] = I_lss
    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):

        Cup = -1.0 if discrete_inputs["upwind"] else 1.0
        tilt = float(np.deg2rad(inputs["tilt"]))

        components = [
            "mb1",
            "mb2",
            "lss",
            "hss",
            "brake",
            "gearbox",
            "generator_rotor",
            "generator_stator",
            "generator",
            "hvac",
            "nose",
            "bedplate",
            "platform",
            "cover",
        ]
        if discrete_inputs["uptower"]:
            components.extend(["transformer", "converter"])

        # Mass and CofM summaries first because will need them for I later
        m_nac = 0.0
        cm_nac = np.zeros(3)
        shaft0 = np.zeros(3)
        shaft0[-1] += inputs["constr_height"]
        if self.options["direct_drive"]:
            shaft0[0] += inputs["x_bedplate"][-1]
        outputs["shaft_start"] = shaft0

        for k in components:
            if k in ["generator_rotor", "generator_stator"]:
                continue
            m_i = inputs[k + "_mass"]
            cm_i = inputs[k + "_cm"]

            # If cm is (x,y,z) then it is already in tower-top c.s.  If it is a scalar, it is in distance from tower and we have to convert
            if len(cm_i) == 1:
                cm_i = shaft0 + cm_i * np.array(
                    [Cup * np.cos(tilt), 0.0,
                     np.sin(tilt)])

            m_nac += m_i
            cm_nac += m_i * cm_i

        # Complete CofM calculation
        cm_nac /= m_nac

        # Now find total I about nacelle CofM
        I_nac = np.zeros(6)
        m_list = np.zeros((len(components) + 3, ))
        cm_list = np.zeros((len(components) + 3, 3))
        I_cm_list = np.zeros((len(components) + 3, 6))
        I_TT_list = np.zeros((len(components) + 3, 6))
        for ic, c in enumerate(components):
            m_i = inputs[c + "_mass"]
            cm_i = inputs["generator_cm"] if c.find(
                "generator") >= 0 else inputs[c + "_cm"]
            I_i = inputs[c + "_I"]

            # Rotate MofI if in hub c.s.
            if len(cm_i) == 1:
                cm_i = shaft0 + cm_i * np.array(
                    [Cup * np.cos(tilt), 0.0,
                     np.sin(tilt)])
                I_i = util.rotateI(I_i, -Cup * tilt, axis="y")
            else:
                I_i = np.r_[I_i, np.zeros(3)]

            r = cm_i - cm_nac
            if not c in ["generator_rotor", "generator_stator"]:
                I_add = util.assembleI(I_i) + m_i * (np.dot(r, r) * np.eye(3) -
                                                     np.outer(r, r))
                I_add = util.unassembleI(I_add)
                I_nac += I_add

            # Record mass, cm, and I for output table
            m_list[ic] = m_i
            cm_list[ic, :] = cm_i
            I_TT_list[ic, :] = util.unassembleI(
                util.assembleI(I_i) + m_i *
                (np.dot(cm_i, cm_i) * np.eye(3) - np.outer(cm_i, cm_i)))
            I_i = inputs[c + "_I"]
            I_cm_list[ic, :] = I_i if I_i.size == 6 else np.r_[I_i,
                                                               np.zeros(3)]

        outputs["above_yaw_mass"] = copy.copy(m_nac)
        outputs["above_yaw_cm"] = R = cm_nac.copy()
        outputs["above_yaw_I"] = I_nac.copy()
        parallel_axis = m_nac * (np.dot(R, R) * np.eye(3) - np.outer(R, R))
        outputs["above_yaw_I_TT"] = util.unassembleI(
            util.assembleI(I_nac) + parallel_axis)

        m_nac += inputs["yaw_mass"]
        cm_nac = (outputs["above_yaw_mass"] * outputs["above_yaw_cm"] +
                  inputs["yaw_cm"] * inputs["yaw_mass"]) / m_nac
        r = inputs["yaw_cm"] - cm_nac
        I_add = util.assembleI(
            np.r_[inputs["yaw_I"], np.zeros(3)]) + inputs["yaw_mass"] * (
                np.dot(r, r) * np.eye(3) - np.outer(r, r))
        I_add = util.unassembleI(I_add)
        I_nac += I_add

        outputs["nacelle_mass"] = m_nac
        outputs["nacelle_cm"] = cm_nac
        outputs["nacelle_I"] = I_nac

        # Find nacelle MoI about tower top
        R = cm_nac
        parallel_axis = m_nac * (np.dot(R, R) * np.eye(3) - np.outer(R, R))
        outputs["nacelle_I_TT"] = util.unassembleI(
            util.assembleI(I_nac) + parallel_axis)

        # Store other misc outputs
        outputs["other_mass"] = (inputs["hvac_mass"] +
                                 inputs["platform_mass"] +
                                 inputs["cover_mass"] + inputs["yaw_mass"] +
                                 inputs["converter_mass"] +
                                 inputs["transformer_mass"])
        outputs["mean_bearing_mass"] = 0.5 * (inputs["mb1_mass"] +
                                              inputs["mb2_mass"])
        outputs["total_bedplate_mass"] = inputs["nose_mass"] + inputs[
            "bedplate_mass"]

        # Wrap up nacelle mass table
        components.append("Above_yaw")
        m_list[-3] = outputs["above_yaw_mass"]
        cm_list[-3, :] = outputs["above_yaw_cm"]
        I_cm_list[-3, :] = outputs["above_yaw_I"]
        I_TT_list[-3, :] = outputs["above_yaw_I_TT"]
        components.append("yaw")
        m_list[-2] = inputs["yaw_mass"]
        cm_list[-2, :] = inputs["yaw_cm"]
        I_cm_list[-2, :] = I_TT_list[-2, :] = np.r_[inputs["yaw_I"],
                                                    np.zeros(3)]
        components.append("nacelle")
        m_list[-1] = m_nac
        cm_list[-1, :] = cm_nac
        I_cm_list[-1, :] = I_nac
        I_TT_list[-1, :] = outputs["nacelle_I_TT"]
        self._mass_table = pd.DataFrame()
        self._mass_table["Component"] = components
        self._mass_table["Mass"] = m_list
        self._mass_table["CoM_TT_x"] = cm_list[:, 0]
        self._mass_table["CoM_TT_y"] = cm_list[:, 1]
        self._mass_table["CoM_TT_z"] = cm_list[:, 2]
        self._mass_table["MoI_CoM_xx"] = I_cm_list[:, 0]
        self._mass_table["MoI_CoM_yy"] = I_cm_list[:, 1]
        self._mass_table["MoI_CoM_zz"] = I_cm_list[:, 2]
        self._mass_table["MoI_CoM_xy"] = I_cm_list[:, 3]
        self._mass_table["MoI_CoM_xz"] = I_cm_list[:, 4]
        self._mass_table["MoI_CoM_yz"] = I_cm_list[:, 5]
        self._mass_table["MoI_TT_xx"] = I_TT_list[:, 0]
        self._mass_table["MoI_TT_yy"] = I_TT_list[:, 1]
        self._mass_table["MoI_TT_zz"] = I_TT_list[:, 2]
        self._mass_table["MoI_TT_xy"] = I_TT_list[:, 3]
        self._mass_table["MoI_TT_xz"] = I_TT_list[:, 4]
        self._mass_table["MoI_TT_yz"] = I_TT_list[:, 5]
        self._mass_table.set_index("Component", inplace=True)
Exemple #5
0
    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):

        Cup = -1.0 if discrete_inputs["upwind"] else 1.0
        tilt = float(np.deg2rad(inputs["tilt"]))

        components = [
            "mb1",
            "mb2",
            "lss",
            "hss",
            "brake",
            "gearbox",
            "generator",
            "hvac",
            "nose",
            "bedplate",
            "platform",
            "cover",
        ]
        if discrete_inputs["uptower"]:
            components.extend(["transformer", "converter"])

        # Mass and CofM summaries first because will need them for I later
        m_nac = 0.0
        cm_nac = np.zeros(3)
        for k in components:
            m_i = inputs[k + "_mass"]
            cm_i = inputs[k + "_cm"]

            # If cm is (x,y,z) then it is already in tower-top c.s.  If it is a scalar, it is in distance from tower and we have to convert
            if len(cm_i) == 1:
                cm_i = cm_i * np.array([Cup * np.cos(tilt), 0.0, np.sin(tilt)])

            m_nac += m_i
            cm_nac += m_i * cm_i

        # Complete CofM calculation
        cm_nac /= m_nac

        # Now find total I about nacelle CofM
        I_nac = np.zeros(6)
        m_list = np.zeros((len(components) + 1, ))
        cm_list = np.zeros((len(components) + 1, 3))
        I_list = np.zeros((len(components) + 1, 6))
        for ic, c in enumerate(components):
            m_i = inputs[c + "_mass"]
            cm_i = inputs[c + "_cm"]
            I_i = inputs[c + "_I"]

            # Rotate MofI if in hub c.s.
            if len(cm_i) == 1:
                cm_i = cm_i * np.array([Cup * np.cos(tilt), 0.0, np.sin(tilt)])
                I_i = util.rotateI(I_i, -Cup * tilt, axis="y")
            else:
                I_i = np.r_[I_i, np.zeros(3)]

            r = cm_i - cm_nac
            I_add = util.assembleI(I_i) + m_i * (np.dot(r, r) * np.eye(3) -
                                                 np.outer(r, r))
            I_add = util.unassembleI(I_add)
            I_nac += I_add

            m_list[ic] = m_i
            cm_list[ic, :] = cm_i
            I_list[ic, :] = I_add

        outputs["above_yaw_mass"] = copy.copy(m_nac)
        outputs["above_yaw_cm"] = copy.copy(cm_nac)
        outputs["above_yaw_I"] = copy.copy(I_nac)

        components.append("yaw")
        m_nac += inputs["yaw_mass"]
        cm_nac = (outputs["above_yaw_mass"] * outputs["above_yaw_cm"] +
                  inputs["yaw_cm"] * inputs["yaw_mass"]) / m_nac
        r = inputs["yaw_cm"] - cm_nac
        I_add = util.assembleI(
            np.r_[inputs["yaw_I"], np.zeros(3)]) + inputs["yaw_mass"] * (
                np.dot(r, r) * np.eye(3) - np.outer(r, r))
        I_add = util.unassembleI(I_add)
        I_nac += I_add

        # Wrap up nacelle mass table
        m_list[-1] = inputs["yaw_mass"]
        cm_list[-1, :] = inputs["yaw_cm"]
        I_list[-1, :] = I_add
        self._mass_table = pd.DataFrame()
        self._mass_table["Component"] = components
        self._mass_table["Mass"] = m_list
        self._mass_table["CoM_x"] = cm_list[:, 0]
        self._mass_table["CoM_y"] = cm_list[:, 1]
        self._mass_table["CoM_z"] = cm_list[:, 2]
        self._mass_table["MoI_xx"] = I_list[:, 0]
        self._mass_table["MoI_yy"] = I_list[:, 1]
        self._mass_table["MoI_zz"] = I_list[:, 2]
        self._mass_table["MoI_xy"] = I_list[:, 3]
        self._mass_table["MoI_xz"] = I_list[:, 4]
        self._mass_table["MoI_yz"] = I_list[:, 5]
        self._mass_table.set_index("Component")
        self._mass_table.loc["Total"] = self._mass_table.sum()

        outputs["nacelle_mass"] = m_nac
        outputs["nacelle_cm"] = cm_nac
        outputs["nacelle_I"] = I_nac
        outputs["other_mass"] = (inputs["hvac_mass"] +
                                 inputs["platform_mass"] +
                                 inputs["cover_mass"] + inputs["yaw_mass"] +
                                 inputs["converter_mass"] +
                                 inputs["transformer_mass"])
        outputs["mean_bearing_mass"] = 0.5 * (inputs["mb1_mass"] +
                                              inputs["mb2_mass"])
        outputs["total_bedplate_mass"] = inputs["nose_mass"] + inputs[
            "bedplate_mass"]