Beispiel #1
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"]))

        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)
Beispiel #2
0
    def compute(self, inputs, outputs):
        # Unpack inputs
        m_pitch = float(inputs["pitch_mass"])
        m_hub = float(inputs["hub_mass"])
        m_spin = float(inputs["spinner_mass"])
        cm_hub = float(inputs["hub_cm"])
        cm_spin = float(inputs["spinner_cm"])

        # Mass and cost totals
        m_total = m_pitch + m_hub + m_spin
        c_total = inputs["pitch_cost"] + inputs["hub_cost"] + inputs[
            "spinner_cost"]

        # CofM total
        cm_total = ((m_pitch + m_hub) * cm_hub + m_spin * cm_spin) / m_total

        # I total
        I_total = util.assembleI(np.zeros(6))
        r = np.array([cm_hub - cm_total, 0.0, 0.0])
        I_total += util.assembleI(inputs["hub_I"]) + m_hub * (
            np.dot(r, r) * np.eye(3) - np.outer(r, r))
        I_total += util.assembleI(inputs["pitch_I"]) + m_pitch * (
            np.dot(r, r) * np.eye(3) - np.outer(r, r))
        r = np.array([cm_spin - cm_total, 0.0, 0.0])
        I_total += util.assembleI(inputs["spinner_I"]) + m_spin * (
            np.dot(r, r) * np.eye(3) - np.outer(r, r))

        # Outputs
        outputs["hub_system_mass"] = m_total
        outputs["hub_system_cost"] = c_total
        outputs["hub_system_cm"] = cm_total
        outputs["hub_system_I"] = util.unassembleI(I_total)
Beispiel #3
0
    def compute(self, inputs, outputs):
        outputs['turbine_mass'] = inputs['rna_mass'] + inputs['tower_mass']
        
        cg_rna   = inputs['rna_cg'] + np.array([0.0, 0.0, inputs['hub_height']])
        cg_tower = np.array([0.0, 0.0, inputs['tower_center_of_mass']])
        outputs['turbine_center_of_mass'] = (inputs['rna_mass']*cg_rna + inputs['tower_mass']*cg_tower) / outputs['turbine_mass']

        R = cg_rna
        I_tower = assembleI(inputs['tower_I_base'])
        I_rna   = assembleI(inputs['rna_I']) + inputs['rna_mass']*(np.dot(R, R)*np.eye(3) - np.outer(R, R))
        outputs['turbine_I_base'] = unassembleI(I_tower + I_rna)
Beispiel #4
0
    def compute(self, inputs, outputs):
        outputs["turbine_mass"] = inputs["rna_mass"] + inputs["tower_mass"] + inputs["monopile_mass"]

        cg_rna = inputs["rna_cg"] + np.r_[0.0, 0.0, inputs["hub_height"]]
        cg_tower = np.r_[0.0, 0.0, inputs["tower_center_of_mass"]]
        outputs["turbine_center_of_mass"] = (inputs["rna_mass"] * cg_rna + inputs["tower_mass"] * cg_tower) / outputs[
            "turbine_mass"
        ]

        R = cg_rna
        I_tower = util.assembleI(inputs["tower_I_base"])
        I_rna = util.assembleI(inputs["rna_I"]) + inputs["rna_mass"] * (np.dot(R, R) * np.eye(3) - np.outer(R, R))
        outputs["turbine_I_base"] = util.unassembleI(I_tower + I_rna)
Beispiel #5
0
    def compute_rigid_body_periods(self, inputs, outputs):
        # Unpack variables
        ncolumn = int(inputs['number_of_offset_columns'])
        R_semi = inputs['radius_to_offset_column']

        m_main = np.sum(inputs['main_mass'])
        m_column = np.sum(inputs['offset_mass'])
        m_tower = np.sum(inputs['tower_mass'])
        m_rna = inputs['rna_mass']
        m_mooring = inputs['mooring_mass']
        m_total = outputs['total_mass']
        m_water = np.maximum(0.0, outputs['variable_ballast_mass'])
        m_a_main = inputs['main_added_mass']
        m_a_column = inputs['offset_added_mass']

        rhoWater = inputs['water_density']
        V_system = inputs['total_displacement']
        h_metacenter = outputs['metacentric_height']

        Awater_main = inputs['main_Awaterplane']
        Awater_column = inputs['offset_Awaterplane']
        I_main = inputs['main_moments_of_inertia']
        I_column = inputs['offset_moments_of_inertia']
        I_mooring = inputs['mooring_moments_of_inertia']
        I_water = outputs['variable_ballast_moments_of_inertia']
        I_tower = inputs['tower_I_base']
        I_rna = inputs['rna_I']
        I_waterplane = outputs['Iwaterplane_system']

        z_cg_main = float(inputs['main_center_of_mass'])
        z_cb_main = float(inputs['main_center_of_buoyancy'])
        z_cg_column = float(inputs['offset_center_of_mass'])
        z_cb_column = float(inputs['offset_center_of_buoyancy'])
        z_cb = float(inputs['z_center_of_buoyancy'])
        z_cg_water = float(outputs['variable_ballast_center_of_mass'])
        z_fairlead = float(inputs['fairlead'] * (-1))

        r_cg = outputs['center_of_mass']
        cg_rna = inputs['rna_cg']
        z_tower = inputs['tower_z_full']

        K_moor = np.diag(inputs['mooring_stiffness'])

        # Number of degrees of freedom
        nDOF = 6

        # Compute elements on mass matrix diagonal
        M_mat = np.zeros((nDOF, ))
        # Surge, sway, heave just use normal inertia (without mooring according to Senu)
        M_mat[:3] = m_total + m_water - m_mooring
        # Add in moments of inertia of primary column
        I_total = assembleI(np.zeros(6))
        I_main = assembleI(I_main)
        R = np.array([0.0, 0.0, z_cg_main]) - r_cg
        I_total += I_main + m_main * (np.dot(R, R) * np.eye(3) -
                                      np.outer(R, R))
        # Add up moments of intertia of other columns
        radii_x = R_semi * np.cos(np.linspace(0, 2 * np.pi, ncolumn + 1))
        radii_y = R_semi * np.sin(np.linspace(0, 2 * np.pi, ncolumn + 1))
        I_column = assembleI(I_column)
        for k in range(ncolumn):
            R = np.array([radii_x[k], radii_y[k], z_cg_column]) - r_cg
            I_total += I_column + m_column * (np.dot(R, R) * np.eye(3) -
                                              np.outer(R, R))
        # Add in variable ballast
        R = np.array([0.0, 0.0, z_cg_water]) - r_cg
        I_total += assembleI(I_water) + m_water * (np.dot(R, R) * np.eye(3) -
                                                   np.outer(R, R))

        # Save what we have so far as m_substructure & I_substructure and move to its own CM
        m_subs = m_main + ncolumn * m_column + m_water
        z_cg_subs = (m_main * z_cg_main + ncolumn * m_column * z_cg_column +
                     m_water * z_cg_water) / m_subs
        R = r_cg - np.array([0.0, 0.0, z_cg_subs])
        I_substructure = I_total + m_subs * (np.dot(R, R) * np.eye(3) -
                                             np.outer(R, R))
        outputs['substructure_moments_of_inertia'] = unassembleI(I_total)

        # Now go back to the total
        # Add in mooring system- Not needed according to Senu
        #R         = np.array([0.0, 0.0, z_fairlead]) - r_cg
        #I_total  += assembleI(I_mooring) + m_mooring*(np.dot(R, R)*np.eye(3) - np.outer(R, R))
        # Add in tower
        R = np.array([0.0, 0.0, z_tower[0]]) - r_cg
        I_total += assembleI(I_tower) + m_tower * (np.dot(R, R) * np.eye(3) -
                                                   np.outer(R, R))
        # Add in RNA
        R = np.array([0.0, 0.0, z_tower[-1]]) + cg_rna - r_cg
        I_total += assembleI(I_rna) + m_rna * (np.dot(R, R) * np.eye(3) -
                                               np.outer(R, R))
        # Stuff moments of inertia into mass matrix
        M_mat[3:] = unassembleI(I_total)[:3]
        outputs['mass_matrix'] = M_mat

        # Add up all added mass entries in a similar way
        A_mat = np.zeros((nDOF, ))
        # Surge, sway, heave just use normal inertia
        A_mat[:3] = m_a_main[:3] + ncolumn * m_a_column[:3]
        # Add up moments of inertia, move added mass moments from CofB to CofG
        I_main = assembleI(np.r_[m_a_main[3:], np.zeros(3)])
        R = np.array([0.0, 0.0, z_cb_main]) - r_cg
        I_total = I_main + m_a_main[0] * (np.dot(R, R) * np.eye(3) -
                                          np.outer(R, R))
        # Add up added moments of intertia of all columns for other entries
        I_column = assembleI(np.r_[m_a_column[3:], np.zeros(3)])
        for k in range(ncolumn):
            R = np.array([radii_x[k], radii_y[k], z_cb_column]) - r_cg
            I_total += I_column + m_a_column[0] * (np.dot(R, R) * np.eye(3) -
                                                   np.outer(R, R))
        A_mat[3:] = unassembleI(I_total)[:3]
        outputs['added_mass_matrix'] = A_mat

        # Hydrostatic stiffness has contributions in heave (K33) and roll/pitch (K44/55)
        # See DNV-RP-H103: Modeling and Analyis of Marine Operations
        K_hydro = np.zeros((nDOF, ))
        K_hydro[2] = rhoWater * gravity * (Awater_main +
                                           ncolumn * Awater_column)
        K_hydro[
            3:
            5] = rhoWater * gravity * V_system * h_metacenter  # FAST eqns: (I_waterplane + V_system * z_cb)
        outputs['hydrostatic_stiffness'] = K_hydro

        # Now compute all six natural periods at once
        epsilon = 1e-6  # Avoids numerical issues
        K_total = np.maximum(K_hydro + K_moor, 0.0)
        outputs['rigid_body_periods'] = 2 * np.pi * np.sqrt(
            (M_mat + A_mat) / (K_total + epsilon))
Beispiel #6
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
Beispiel #7
0
    def compute(self, inputs, outputs):
        # Unpack variables for thickness and average radius at each can interface
        twall = inputs["t_full"]
        Rb = 0.5 * inputs["d_full"][:-1]
        Rt = 0.5 * inputs["d_full"][1:]
        zz = inputs["z_full"]
        H = np.diff(zz)
        rho = inputs["rho"]
        coeff = inputs["outfitting_factor"]
        coeff = coeff + np.where(coeff < 1.0, 1.0, 0.0)

        # Total mass of cylinder
        V_shell = frustum.frustumShellVol(Rb, Rt, twall, H)
        mass = outputs["mass"] = coeff * rho * V_shell

        # Center of mass of each can/section
        cm_section = zz[:-1] + frustum.frustumShellCG(Rb, Rt, twall, H)
        outputs["section_center_of_mass"] = cm_section

        # Center of mass of cylinder
        V_shell += eps
        outputs["center_of_mass"] = np.dot(V_shell, cm_section) / V_shell.sum()

        # Moments of inertia
        Izz_section = coeff * rho * frustum.frustumShellIzz(Rb, Rt, twall, H)
        Ixx_section = Iyy_section = coeff * rho * frustum.frustumShellIxx(Rb, Rt, twall, H)

        # Sum up each cylinder section using parallel axis theorem
        I_base = np.zeros((3, 3))
        for k in range(Izz_section.size):
            R = np.array([0.0, 0.0, cm_section[k] - zz[0]])
            Icg = util.assembleI([Ixx_section[k], Iyy_section[k], Izz_section[k], 0.0, 0.0, 0.0])

            I_base += Icg + mass[k] * (np.dot(R, R) * np.eye(3) - np.outer(R, R))

        outputs["I_base"] = util.unassembleI(I_base)

        # Compute costs based on "Optimum Design of Steel Structures" by Farkas and Jarmai
        # All dimensions for correlations based on mm, not meters.
        R_ave = 0.5 * (Rb + Rt)
        taper = np.minimum(Rb / Rt, Rt / Rb)
        nsec = twall.size
        mshell = rho * V_shell
        mshell_tot = np.sum(rho * V_shell)
        k_m = inputs["material_cost_rate"]  # 1.1 # USD / kg carbon steel plate
        k_f = inputs["labor_cost_rate"]  # 1.0 # USD / min labor
        k_p = inputs["painting_cost_rate"]  # USD / m^2 painting
        k_e = 0.064  # Industrial electricity rate $/kWh https://www.eia.gov/electricity/monthly/epm_table_grapher.php?t=epmt_5_6_a
        e_f = 15.9  # Electricity usage kWh/kg for steel
        e_fo = 26.9  # Electricity usage kWh/kg for stainless steel

        # Cost Step 1) Cutting flat plates for taper using plasma cutter
        cutLengths = 2.0 * np.sqrt((Rt - Rb) ** 2.0 + H ** 2.0)  # Factor of 2 for both sides
        # Cost Step 2) Rolling plates
        # Cost Step 3) Welding rolled plates into shells (set difficulty factor based on tapering with logistic function)
        theta_F = 4.0 - 3.0 / (1 + np.exp(-5.0 * (taper - 0.75)))
        # Cost Step 4) Circumferential welds to join cans together
        theta_A = 2.0

        # Labor-based expenses
        K_f = k_f * (
            manufacture.steel_cutting_plasma_time(cutLengths, twall)
            + manufacture.steel_rolling_time(theta_F, R_ave, twall)
            + manufacture.steel_butt_welding_time(theta_A, nsec, mshell_tot, cutLengths, twall)
            + manufacture.steel_butt_welding_time(theta_A, nsec, mshell_tot, 2 * np.pi * Rb[1:], twall[1:])
        )

        # Cost step 5) Painting- outside and inside
        theta_p = 2
        K_p = k_p * theta_p * 2 * (2 * np.pi * R_ave * H).sum()

        # Cost step 6) Outfitting with electricity usage
        K_o = np.sum(1.5 * k_m * (coeff - 1.0) * mshell)

        # Material cost with waste fraction, but without outfitting,
        K_m = 1.21 * np.sum(k_m * mshell)

        # Electricity usage
        K_e = np.sum(k_e * (e_f * mshell + e_fo * (coeff - 1.0) * mshell))

        # Assemble all costs for now
        tempSum = K_m + K_e + K_o + K_p + K_f

        # Capital cost share from BLS MFP by NAICS
        K_c = 0.118 * tempSum / (1.0 - 0.118)

        outputs["cost"] = tempSum + K_c
Beispiel #8
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_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)
Beispiel #9
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"]
Beispiel #10
0
    def compute(self, inputs, outputs):
        # Unpack variables for thickness and average radius at each can interface
        twall = inputs['t_full']
        Rb = 0.5 * inputs['d_full'][:-1]
        Rt = 0.5 * inputs['d_full'][1:]
        zz = inputs['z_full']
        H = np.diff(zz)
        rho = inputs['material_density']
        coeff = inputs['outfitting_factor']
        if coeff < 1.0: coeff += 1.0

        # Total mass of cylinder
        V_shell = frustum.frustumShellVol(Rb, Rt, twall, H)
        outputs['mass'] = coeff * rho * V_shell

        # Center of mass of each can/section
        cm_section = zz[:-1] + frustum.frustumShellCG(Rb, Rt, twall, H)
        outputs['section_center_of_mass'] = cm_section

        # Center of mass of cylinder
        V_shell += eps
        outputs['center_of_mass'] = np.dot(V_shell, cm_section) / V_shell.sum()

        # Moments of inertia
        Izz_section = frustum.frustumShellIzz(Rb, Rt, twall, H)
        Ixx_section = Iyy_section = frustum.frustumShellIxx(Rb, Rt, twall, H)

        # Sum up each cylinder section using parallel axis theorem
        I_base = np.zeros((3, 3))
        for k in range(Izz_section.size):
            R = np.array([0.0, 0.0, cm_section[k]])
            Icg = assembleI([
                Ixx_section[k], Iyy_section[k], Izz_section[k], 0.0, 0.0, 0.0
            ])

            I_base += Icg + V_shell[k] * (np.dot(R, R) * np.eye(3) -
                                          np.outer(R, R))

        # All of the mass and volume terms need to be multiplied by density
        I_base *= coeff * rho

        outputs['I_base'] = unassembleI(I_base)

        # Compute costs based on "Optimum Design of Steel Structures" by Farkas and Jarmai
        # All dimensions for correlations based on mm, not meters.
        R_ave = 0.5 * (Rb + Rt)
        taper = np.minimum(Rb / Rt, Rt / Rb)
        nsec = twall.size
        mplate = rho * V_shell.sum()
        k_m = inputs['material_cost_rate']  #1.1 # USD / kg carbon steel plate
        k_f = inputs['labor_cost_rate']  #1.0 # USD / min labor
        k_p = inputs['painting_cost_rate']  #USD / m^2 painting

        # Cost Step 1) Cutting flat plates for taper using plasma cutter
        cutLengths = 2.0 * np.sqrt(
            (Rt - Rb)**2.0 + H**2.0)  # Factor of 2 for both sides
        # Cost Step 2) Rolling plates
        # Cost Step 3) Welding rolled plates into shells (set difficulty factor based on tapering with logistic function)
        theta_F = 4.0 - 3.0 / (1 + np.exp(-5.0 * (taper - 0.75)))
        # Cost Step 4) Circumferential welds to join cans together
        theta_A = 2.0

        # Labor-based expenses
        K_f = k_f * (manufacture.steel_cutting_plasma_time(cutLengths, twall) +
                     manufacture.steel_rolling_time(theta_F, R_ave, twall) +
                     manufacture.steel_butt_welding_time(
                         theta_A, nsec, mplate, cutLengths, twall) +
                     manufacture.steel_butt_welding_time(
                         theta_A, nsec, mplate, 2 * np.pi * Rb[1:], twall[1:]))

        # Cost step 5) Painting- outside and inside
        theta_p = 2
        K_p = k_p * theta_p * 2 * (2 * np.pi * R_ave * H).sum()

        # Cost step 6) Outfitting
        K_o = 1.5 * k_m * (coeff - 1.0) * mplate

        # Material cost, without outfitting
        K_m = k_m * mplate

        # Assemble all costs
        outputs['cost'] = K_m + K_o + K_p + K_f
Beispiel #11
0
    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):
        opt = self.options['modeling_options']

        # Frequencies to calculate
        outputs['frequency_range'] = np.array(opt['Level1']['frequencies'])

        # Tower layer sections
        outputs['turbine_tower_t'] = inputs['tower_layer_thickness'].sum(
            axis=0)
        outputs['turbine_tower_rho_shell'] = inputs['tower_rho'].mean()
        k_tow_tor = inputs['tower_torsional_stiffness'] / inputs[
            'tower_section_height']
        outputs['turbine_yaw_stiffness'] = 1.0 / np.sum(1.0 / k_tow_tor)

        # Tower drag
        Re = float(inputs['rho_air']
                   ) * inputs['tower_U'] * inputs['turbine_tower_d'] / float(
                       inputs['mu_air'])
        cd, _ = cylinderDrag(Re)
        outputs['turbine_tower_Cd'] = cd

        # Move tower-top MoI to hub height
        m_rna = float(inputs['turbine_mRNA'])
        I_rna = util.assembleI(inputs['rna_I_TT'])
        # Vector from WISDEM tower-top c.s. to raft tower center-line at hub height c.s.
        r = np.r_[0.0, 0.0, float(inputs['drive_height'])]
        outputs['turbine_xCG_RNA'] = (inputs['rna_cm'] - r)[0]
        I_rna_raft = util.unassembleI(
            I_rna + m_rna * (np.dot(r, r) * np.eye(3) - np.outer(r, r)))
        outputs['turbine_IxRNA'] = I_rna_raft[0]
        outputs['turbine_IrRNA'] = 0.5 * (I_rna_raft[1] + I_rna_raft[2])

        # Floating member data structure transfer
        n_member = len(opt["floating"]["members"]["name"])
        var_height = inputs['member_variable_height']
        for k in range(n_member):
            discrete_outputs[f"platform_member{k+1}_potMod"] = opt["Level1"][
                "model_potential"][k]

            # Member thickness
            outputs[f"platform_member{k+1}_t"] = inputs[
                f"member{k}:layer_thickness"].sum(axis=0)
            outputs[f"platform_member{k+1}_rho_shell"] = inputs[
                f"member{k}:rho"].mean()

            # Ring stiffener discretization conversion
            if float(inputs[f"member{k}:ring_stiffener_spacing"]) > 0.0:
                outputs[f"platform_member{k+1}_ring_spacing"] = (
                    inputs[f"member{k}:ring_stiffener_spacing"] /
                    inputs[f"member{k}:height"])
                h_web = inputs[f"member{k}:ring_stiffener_web_height"]
                t_web = inputs[f"member{k}:ring_stiffener_web_thickness"]
                t_flange = inputs[f"member{k}:ring_stiffener_flange_thickness"]
                w_flange = inputs[f"member{k}:ring_stiffener_flange_width"]
                outputs[f"platform_member{k+1}_ring_h"] = h_web + t_flange
                outputs[f"platform_member{k+1}_ring_t"] = (
                    t_web * h_web + w_flange * t_flange) / (h_web + t_flange)

            # Ballast discretization conversion
            s_grid = inputs[f"platform_member{k+1}_stations"]
            s_ballast = inputs[f"member{k}:ballast_grid"]
            rho_ballast = inputs[f"member{k}:ballast_density"]
            h_ballast = inputs[f"member{k}:ballast_height"]
            l_fill = np.zeros(s_grid.size - 1)
            rho_fill = np.zeros(s_grid.size - 1)
            for ii in range(s_ballast.shape[0]):
                iball = np.where(s_ballast[ii, 0] >= s_grid)[0][0]
                rho_fill[iball] = rho_ballast[ii]
                l_fill[iball] = h_ballast[
                    ii] if rho_ballast[ii] < 1100.0 else var_height[k]
            outputs[f"platform_member{k+1}_l_fill"] = l_fill
            outputs[f"platform_member{k+1}_rho_fill"] = rho_fill

        # Mooring
        for k in range(opt['mooring']['n_nodes']):
            discrete_outputs[f'mooring_point{k+1}_name'] = opt['mooring'][
                'node_names'][k]
            discrete_outputs[f'mooring_point{k+1}_type'] = opt['mooring'][
                'node_type'][k]
            outputs[f'mooring_point{k+1}_location'] = inputs['mooring_nodes'][
                k, :]

        for k in range(opt['mooring']['n_lines']):
            discrete_outputs[f'mooring_line{k+1}_endA'] = opt['mooring'][
                "node1"][k]
            discrete_outputs[f'mooring_line{k+1}_endB'] = opt['mooring'][
                "node2"][k]
            discrete_outputs[f'mooring_line{k+1}_type'] = opt['mooring'][
                "line_type"][k]
            outputs[f'mooring_line{k+1}_length'] = inputs[
                'unstretched_length'][k]

        for k in range(opt['mooring']['n_line_types']):
            discrete_outputs[f'mooring_line_type{k+1}_name'] = opt['mooring'][
                "line_type_name"][k]
            outputs[f'mooring_line_type{k+1}_cost'] = inputs['line_cost_rate'][
                k]

            for var in [
                    'diameter', 'mass_density', 'stiffness', 'breaking_load',
                    'transverse_added_mass', 'tangential_added_mass',
                    'transverse_drag', 'tangential_drag'
            ]:
                outputs[f'mooring_line_type{k+1}_{var}'] = inputs[
                    f'line_{var}'][k]
Beispiel #12
0
    def set_element_props(self, inputs, outputs):
        # Load in number of members
        opt = self.options["options"]
        n_member = opt["floating"]["members"]["n_members"]

        # Initialize running lists across all members
        elem_D = np.array([])
        elem_t = np.array([])
        elem_A = np.array([])
        elem_Asx = np.array([])
        elem_Asy = np.array([])
        elem_Ixx = np.array([])
        elem_Iyy = np.array([])
        elem_Izz = np.array([])
        elem_rho = np.array([])
        elem_E = np.array([])
        elem_G = np.array([])

        mass = 0.0
        cost = 0.0
        volume = 0.0
        Awater = 0.0
        Iwater = 0.0
        m_added = np.zeros(6)
        cg_plat = np.zeros(3)
        cb_plat = np.zeros(3)

        # Append all member data
        for k in range(n_member):
            n = np.where(inputs[f"member{k}:section_A"] == NULL)[0][0]
            elem_D = np.append(elem_D, inputs[f"member{k}:section_D"][:n])
            elem_t = np.append(elem_t, inputs[f"member{k}:section_t"][:n])
            elem_A = np.append(elem_A, inputs[f"member{k}:section_A"][:n])
            elem_Asx = np.append(elem_Asx,
                                 inputs[f"member{k}:section_Asx"][:n])
            elem_Asy = np.append(elem_Asy,
                                 inputs[f"member{k}:section_Asy"][:n])
            elem_Ixx = np.append(elem_Ixx,
                                 inputs[f"member{k}:section_Ixx"][:n])
            elem_Iyy = np.append(elem_Iyy,
                                 inputs[f"member{k}:section_Iyy"][:n])
            elem_Izz = np.append(elem_Izz,
                                 inputs[f"member{k}:section_Izz"][:n])
            elem_rho = np.append(elem_rho,
                                 inputs[f"member{k}:section_rho"][:n])
            elem_E = np.append(elem_E, inputs[f"member{k}:section_E"][:n])
            elem_G = np.append(elem_G, inputs[f"member{k}:section_G"][:n])

            # Mass, volume, cost tallies
            imass = inputs[f"member{k}:total_mass"]
            ivol = inputs[f"member{k}:displacement"]

            mass += imass
            volume += ivol
            cost += inputs[f"member{k}:total_cost"]
            Awater += inputs[f"member{k}:Awater"]
            Iwater += inputs[f"member{k}:Iwater"]
            m_added += inputs[f"member{k}:added_mass"]

            # Center of mass / buoyancy tallies
            cg_plat += imass * inputs[f"member{k}:center_of_mass"]
            cb_plat += ivol * inputs[f"member{k}:center_of_buoyancy"]

        # Finalize outputs
        cg_plat /= mass
        cb_plat /= volume

        # With CG known, loop back through to compute platform I
        unit_z = np.array([0.0, 0.0, 1.0])
        I_total = np.zeros((3, 3))
        for k in range(n_member):
            xyz_k = inputs[f"member{k}:nodes_xyz"]
            inodes = np.where(xyz_k[:, 0] == NULL)[0][0]
            xyz_k = xyz_k[:inodes, :]

            imass = inputs[f"member{k}:total_mass"]
            cg_k = inputs[f"member{k}:center_of_mass"]
            R = cg_plat - cg_k

            # Figure out angle to make member parallel to global c.s.
            vec_k = xyz_k[-1, :] - xyz_k[0, :]
            T = util.rotate_align_vectors(vec_k, unit_z)

            # Rotate member inertia tensor
            I_k = util.assembleI(inputs[f"member{k}:I_total"])
            I_k2 = T * np.asmatrix(I_k) * T.T

            # Now do parallel axis theorem
            I_total += np.array(I_k2) + imass * (np.dot(R, R) * np.eye(3) -
                                                 np.outer(R, R))

        # Store outputs
        nelem = elem_A.size
        outputs["platform_elem_D"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_t"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_A"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_Asx"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_Asy"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_Ixx"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_Iyy"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_Izz"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_rho"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_E"] = NULL * np.ones(NELEM_MAX)
        outputs["platform_elem_G"] = NULL * np.ones(NELEM_MAX)

        outputs["platform_elem_D"][:nelem] = elem_D
        outputs["platform_elem_t"][:nelem] = elem_t
        outputs["platform_elem_A"][:nelem] = elem_A
        outputs["platform_elem_Asx"][:nelem] = elem_Asx
        outputs["platform_elem_Asy"][:nelem] = elem_Asy
        outputs["platform_elem_Ixx"][:nelem] = elem_Ixx
        outputs["platform_elem_Iyy"][:nelem] = elem_Iyy
        outputs["platform_elem_Izz"][:nelem] = elem_Izz
        outputs["platform_elem_rho"][:nelem] = elem_rho
        outputs["platform_elem_E"][:nelem] = elem_E
        outputs["platform_elem_G"][:nelem] = elem_G

        outputs["platform_mass"] = mass
        outputs["platform_cost"] = cost
        outputs["platform_displacement"] = volume
        outputs["platform_center_of_mass"] = cg_plat
        outputs["platform_center_of_buoyancy"] = cb_plat
        outputs["platform_I_total"] = util.unassembleI(I_total)
        outputs["platform_Awater"] = Awater
        outputs["platform_Iwater"] = Iwater
        outputs["platform_added_mass"] = m_added