Example #1
0
    def solve_nonlinear(self, params, unknowns, resids):
        unknowns['turbine_mass'] = params['rna_mass'] + params['tower_mass']
        
        cg_rna   = params['rna_cg'] + np.array([0.0, 0.0, params['hubH']])
        cg_tower = np.array([0.0, 0.0, params['tower_center_of_mass']])
        unknowns['turbine_center_of_mass'] = (params['rna_mass']*cg_rna + params['tower_mass']*cg_tower) / unknowns['turbine_mass']

        R = cg_rna
        I_tower = assembleI(params['tower_I_base'])
        I_rna   = assembleI(params['rna_I']) + params['rna_mass']*(np.dot(R, R)*np.eye(3) - np.outer(R, R))
        unknowns['turbine_I_base'] = unassembleI(I_tower + I_rna)
Example #2
0
    def solve_nonlinear(self, params, unknowns, resids):
        # Unpack variables
        z_full = params['z_full']  # at section nodes
        z_param = params['z_param']
        R_od = 0.5 * params['d_full']  # at section nodes
        twall = params['t_full']  # at section nodes
        t_bulk = params['bulkhead_thickness']  # at section nodes

        # Map bulkhead locations to finer computation grid
        Zf, Zp = np.meshgrid(z_full, z_param)
        idx = np.argmin(np.abs(Zf - Zp), axis=1)
        t_bulk_full = np.zeros(z_full.shape)
        t_bulk_full[idx] = t_bulk

        # Compute bulkhead volume at every section node
        # Assume bulkheads are same thickness as shell wall
        V_bulk = np.pi * (R_od - twall)**2 * t_bulk_full

        # Convert to mass with fudge factor for design features not captured in this simple approach
        m_bulk = params['bulkhead_mass_factor'] * params['rho'] * V_bulk

        # Compute moments of inertia at keel
        # Assume bulkheads are just simple thin discs with radius R_od-t_wall and mass already computed
        Izz = 0.5 * m_bulk * (R_od - twall)**2
        Ixx = Iyy = 0.5 * Izz
        I_keel = np.zeros((3, 3))
        dz = z_full - z_full[0]
        for k in xrange(m_bulk.size):
            R = np.array([0.0, 0.0, dz[k]])
            Icg = assembleI([Ixx[k], Iyy[k], Izz[k], 0.0, 0.0, 0.0])
            I_keel += Icg + m_bulk[k] * (np.dot(R, R) * np.eye(3) -
                                         np.outer(R, R))

        # Store results
        unknowns['bulkhead_I_keel'] = unassembleI(I_keel)
        unknowns['bulkhead_mass'] = m_bulk
Example #3
0
    def runMAP(self, params, unknowns):
        """Writes MAP input file, executes, and then queries MAP to find 
        maximum loading and displacement from vessel displacement around all 360 degrees
        
        INPUTS:
        ----------
        params   : dictionary of input parameters
        unknowns : dictionary of output parameters
        
        OUTPUTS  : none (multiple unknown dictionary values set)
        """
        # Unpack variables
        rhoWater = params['water_density']
        waterDepth = params['water_depth']
        fairleadDepth = params['fairlead']
        Dmooring = params['mooring_diameter']
        offset = params['max_offset']
        heel = params['operational_heel']
        gamma = params['gamma_f']
        n_connect = int(params['number_of_mooring_connections'])
        n_lines = int(params['mooring_lines_per_connection'])
        ntotal = n_connect * n_lines

        # Write the mooring system input file for this design
        self.write_input_file(params)

        # Initiate MAP++ for this design
        mymap = pyMAP()
        #mymap.ierr = 0
        mymap.map_set_sea_depth(waterDepth)
        mymap.map_set_gravity(gravity)
        mymap.map_set_sea_density(rhoWater)
        mymap.read_list_input(self.finput)
        mymap.init()

        # Get the stiffness matrix at neutral position
        mymap.displace_vessel(0, 0, 0, 0, 0, 0)
        mymap.update_states(0.0, 0)
        K = mymap.linear(1e-4)  # Input finite difference epsilon
        unknowns['mooring_stiffness'] = np.array(K)
        mymap.displace_vessel(0, 0, 0, 0, 0, 0)
        mymap.update_states(0.0, 0)

        # Get the vertical load on the structure and plotting data
        F_neutral = np.zeros((NLINES_MAX, 3))
        plotMat = np.zeros((NLINES_MAX, NPTS_PLOT, 3))
        nptsMOI = 100
        xyzpts = np.zeros((ntotal, nptsMOI, 3))  # For MOI calculation
        for k in range(ntotal):
            (F_neutral[k, 0], F_neutral[k, 1],
             F_neutral[k, 2]) = mymap.get_fairlead_force_3d(k)
            plotMat[k, :, 0] = mymap.plot_x(k, NPTS_PLOT)
            plotMat[k, :, 1] = mymap.plot_y(k, NPTS_PLOT)
            plotMat[k, :, 2] = mymap.plot_z(k, NPTS_PLOT)
            xyzpts[k, :, 0] = mymap.plot_x(k, nptsMOI)
            xyzpts[k, :, 1] = mymap.plot_y(k, nptsMOI)
            xyzpts[k, :, 2] = mymap.plot_z(k, nptsMOI)
            if self.tlpFlag:
                # Seems to be a bug in the plot arrays from MAP++ for plotting output with taut lines
                plotMat[k, :, 2] = np.linspace(-fairleadDepth, -waterDepth,
                                               NPTS_PLOT)
                xyzpts[k, :, 2] = np.linspace(-fairleadDepth, -waterDepth,
                                              nptsMOI)
        unknowns['mooring_neutral_load'] = F_neutral
        unknowns['mooring_plot_matrix'] = plotMat

        # Fine line segment length, ds = sqrt(dx^2 + dy^2 + dz^2)
        xyzpts_dx = np.gradient(xyzpts[:, :, 0], axis=1)
        xyzpts_dy = np.gradient(xyzpts[:, :, 1], axis=1)
        xyzpts_dz = np.gradient(xyzpts[:, :, 2], axis=1)
        xyzpts_ds = np.sqrt(xyzpts_dx**2 + xyzpts_dy**2 + xyzpts_dz**2)

        # Initialize inertia tensor integrands in https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor
        # Taking MOI relative to body centerline at fairlead depth
        r0 = np.array([0.0, 0.0, -fairleadDepth])
        R = np.zeros((ntotal, nptsMOI, 6))
        for ii in range(nptsMOI):
            for k in range(ntotal):
                r = xyzpts[k, ii, :] - r0
                R[k, ii, :] = unassembleI(
                    np.dot(r, r) * np.eye(3) - np.outer(r, r))
        Imat = self.wet_mass_per_length * np.trapz(
            R, x=xyzpts_ds[:, :, np.newaxis], axis=1)
        unknowns['mooring_moments_of_inertia'] = np.abs(Imat.sum(axis=0))

        # Get the restoring moment at maximum angle of heel
        # Since we don't know the substucture CG, have to just get the forces of the lines now and do the cross product later
        # We also want to allow for arbitraty wind direction and yaw of rotor relative to mooring lines, so we will compare
        # pitch and roll forces as extremes
        # TODO: This still isgn't quite the same as clocking the mooring lines in different directions,
        # which is what we want to do, but that requires multiple input files and solutions
        Fh = np.zeros((NLINES_MAX, 3))
        mymap.displace_vessel(0, 0, 0, 0, heel, 0)
        mymap.update_states(0.0, 0)
        for k in range(ntotal):
            Fh[k][0], Fh[k][1], Fh[k][2] = mymap.get_fairlead_force_3d(k)

        unknowns['operational_heel_restoring_force'] = Fh

        # Get angles by which to find the weakest line
        dangle = 2.0
        angles = np.deg2rad(np.arange(0.0, 360.0, dangle))
        nangles = len(angles)

        # Get restoring force at weakest line at maximum allowable offset
        # Will global minimum always be along mooring angle?
        max_tension = 0.0
        max_angle = None
        min_angle = None
        F_max_tension = None
        F_min = np.inf
        T = np.zeros((NLINES_MAX, ))
        F = np.zeros((NLINES_MAX, ))
        # Loop around all angles to find weakest point
        for a in angles:
            # Unit vector and offset in x-y components
            idir = np.array([np.cos(a), np.sin(a)])
            surge = offset * idir[0]
            sway = offset * idir[1]

            # Get restoring force of offset at this angle
            mymap.displace_vessel(surge, sway, 0, 0, 0, 0)  # 0s for z, angles
            mymap.update_states(0.0, 0)
            for k in range(ntotal):
                # Force in x-y-z coordinates
                fx, fy, fz = mymap.get_fairlead_force_3d(k)
                T[k] = np.sqrt(fx * fx + fy * fy + fz * fz)
                # Total restoring force
                F[k] = np.dot([fx, fy], idir)

            # Check if this is the weakest direction (highest tension)
            tempMax = T.max()
            if tempMax > max_tension:
                max_tension = tempMax
                F_max_tension = F.sum()
                max_angle = a
            if F.sum() < F_min:
                F_min = F.sum()
                min_angle = a

        # Store the weakest restoring force when the vessel is offset the maximum amount
        unknowns['max_offset_restoring_force'] = F_min

        # Check for good convergence
        if (plotMat[0, -1, -1] + fairleadDepth) > 1.0:
            unknowns['axial_unity'] = 1e30
        else:
            unknowns['axial_unity'] = gamma * max_tension / self.min_break_load

        mymap.end()
Example #4
0
    def solve_nonlinear(self, params, unknowns, resids):
        # Unpack variables
        R_od, _ = nodal2sectional(params['d_full'])  # at section nodes
        R_od *= 0.5
        t_wall, _ = nodal2sectional(params['t_full'])  # at section nodes
        z_full = params['z_full']  # at section nodes
        z_param = params['z_param']
        z_section, _ = nodal2sectional(params['z_full'])
        h_section = np.diff(z_full)
        t_web = sectionalInterp(z_section, z_param,
                                params['stiffener_web_thickness'])
        t_flange = sectionalInterp(z_section, z_param,
                                   params['stiffener_flange_thickness'])
        h_web = sectionalInterp(z_section, z_param,
                                params['stiffener_web_height'])
        w_flange = sectionalInterp(z_section, z_param,
                                   params['stiffener_flange_width'])
        L_stiffener = sectionalInterp(z_section, z_param,
                                      params['stiffener_spacing'])
        rho = params['rho']

        # Outer and inner radius of web by section
        R_wo = R_od - t_wall
        R_wi = R_wo - h_web
        # Outer and inner radius of flange by section
        R_fo = R_wi
        R_fi = R_fo - t_flange

        # Material volumes by section
        V_web = np.pi * (R_wo**2 - R_wi**2) * t_web
        V_flange = np.pi * (R_fo**2 - R_fi**2) * w_flange

        # Ring mass by volume by section
        # Include fudge factor for design features not captured in this simple approach
        m_web = params['ring_mass_factor'] * rho * V_web
        m_flange = params['ring_mass_factor'] * rho * V_flange
        m_ring = m_web + m_flange
        n_stiff = np.zeros(z_section.shape, dtype=np.int_)

        # Compute moments of inertia for stiffeners (lumped by section for simplicity) at keel
        I_web = I_tube(R_wi, R_wo, t_web, m_web)
        I_flange = I_tube(R_fi, R_fo, w_flange, m_flange)
        I_ring = I_web + I_flange
        I_keel = np.zeros((3, 3))

        # Now march up the column, adding stiffeners at correct spacing until we are done
        z_stiff = []
        isection = 0
        epsilon = 1e-6
        while True:
            if len(z_stiff) == 0:
                z_march = np.minimum(z_full[isection + 1], z_full[0] +
                                     0.5 * L_stiffener[isection]) + epsilon
            else:
                z_march = np.minimum(z_full[isection + 1], z_stiff[-1] +
                                     L_stiffener[isection]) + epsilon
            if z_march >= z_full[-1]: break

            isection = np.searchsorted(z_full, z_march) - 1

            if len(z_stiff) == 0:
                add_stiff = (z_march -
                             z_full[0]) >= 0.5 * L_stiffener[isection]
            else:
                add_stiff = (z_march - z_stiff[-1]) >= L_stiffener[isection]

            if add_stiff:
                z_stiff.append(z_march)
                n_stiff[isection] += 1

                R = np.array([0.0, 0.0, (z_march - z_full[0])])
                Icg = assembleI(I_ring[isection, :])
                I_keel += Icg + m_ring[isection] * (np.dot(R, R) * np.eye(3) -
                                                    np.outer(R, R))

        # Number of stiffener rings per section (height of section divided by spacing)
        unknowns['stiffener_mass'] = n_stiff * m_ring

        # Find total number of stiffeners in each original section
        npts_per = z_section.size / (z_param.size - 1)
        n_stiff_sec = np.zeros(z_param.size - 1)
        for k in range(npts_per):
            n_stiff_sec += n_stiff[k::npts_per]
        unknowns['number_of_stiffeners'] = n_stiff_sec

        # Store results
        unknowns['stiffener_I_keel'] = unassembleI(I_keel)

        # Create some constraints for reasonable stiffener designs for an optimizer
        unknowns['flange_spacing_ratio'] = w_flange / (0.5 * L_stiffener)
        unknowns['stiffener_radius_ratio'] = (h_web + t_flange + t_wall) / R_od
Example #5
0
    def compute_ballast_mass_cg(self, params, unknowns):
        """Computes permanent ballast mass and center of mass
        Assumes permanent ballast is located at bottom of spar (at the keel)
        From the user/optimizer input of ballast height, computes the mass based on varying radius of the spar
        
        INPUTS:
        ----------
        params   : dictionary of input parameters
        unknowns : dictionary of output parameters
        
        OUTPUTS:
        ----------
        m_ballast     : permanent ballast mass
        z_cg          : center of mass along z-axis for the ballast
        z_ballast_var : z-position of where variable ballast starts
        ballast_mass in 'unknowns' dictionary set

        """
        # Unpack variables
        R_od = 0.5 * params['d_full']
        t_wall = params['t_full']
        h_ballast = params['permanent_ballast_height']
        rho_ballast = params['permanent_ballast_density']
        rho_water = params['water_density']
        z_nodes = params['z_full']

        npts = R_od.size

        # Geometry of the spar in our coordinate system (z=0 at waterline)
        z_draft = z_nodes[0]

        # Fixed and total ballast mass and cg
        # Assume they are bottled in columns a the keel of the spar- first the permanent then the fixed
        zpts = np.linspace(z_draft, z_draft + h_ballast, npts)
        R_id = np.interp(zpts, z_nodes, R_od - t_wall)
        V_perm = np.pi * np.trapz(R_id**2, zpts)
        m_perm = rho_ballast * V_perm
        z_cg_perm = rho_ballast * np.pi * np.trapz(
            zpts * R_id**2, zpts) / m_perm if m_perm > 0.0 else 0.0
        for k in xrange(z_nodes.size - 1):
            ind = np.logical_and(zpts >= z_nodes[k], zpts <= z_nodes[k + 1])
            self.section_mass[k] += rho_ballast * np.pi * np.trapz(
                R_id[ind]**2, zpts[ind])

        Ixx = Iyy = frustum.frustumIxx(R_id[:-1], R_id[1:], np.diff(zpts))
        Izz = frustum.frustumIzz(R_id[:-1], R_id[1:], np.diff(zpts))
        V_slice = frustum.frustumVol(R_id[:-1], R_id[1:], np.diff(zpts))
        I_keel = np.zeros((3, 3))
        dz = frustum.frustumCG(R_id[:-1], R_id[1:],
                               np.diff(zpts)) + zpts[:-1] - z_draft
        for k in xrange(V_slice.size):
            R = np.array([0.0, 0.0, dz[k]])
            Icg = assembleI([Ixx[k], Iyy[k], Izz[k], 0.0, 0.0, 0.0])
            I_keel += Icg + V_slice[k] * (np.dot(R, R) * np.eye(3) -
                                          np.outer(R, R))
        I_keel = rho_ballast * unassembleI(I_keel)

        # Water ballast will start at top of fixed ballast
        z_water_start = z_draft + h_ballast

        # Find height of water ballast numerically by finding the height that integrates to the mass we want
        # This step is completed in spar.py or semi.py because we must account for other substructure elements too
        zpts = np.linspace(z_water_start, z_nodes[-1], npts)
        R_id = np.interp(zpts, z_nodes, R_od - t_wall)
        m_water = rho_water * cumtrapz(np.pi * R_id**2, zpts)
        unknowns['variable_ballast_interp_mass'] = np.r_[
            0.0, m_water]  #cumtrapz has length-1
        unknowns['variable_ballast_interp_zpts'] = zpts

        # Save permanent ballast mass and variable height
        unknowns['ballast_mass'] = m_perm
        unknowns['ballast_I_keel'] = I_keel
        unknowns['ballast_z_cg'] = z_cg_perm

        return m_perm, z_cg_perm, I_keel
Example #6
0
    def compute_rigid_body_periods(self, params, unknowns):
        # Unpack variables
        ncolumn = int(params['number_of_offset_columns'])
        R_semi = params['radius_to_offset_column']

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

        rhoWater = params['water_density']
        V_system = params['total_displacement']
        h_metacenter = unknowns['metacentric_height']

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

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

        r_cg = unknowns['center_of_mass']
        cg_rna = params['rna_cg']
        z_tower = params['tower_z_full']

        K_moor = np.diag(params['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))
        unknowns['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]
        unknowns['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]
        unknowns['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)
        unknowns['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)
        unknowns['rigid_body_periods'] = 2 * np.pi * np.sqrt(
            (M_mat + A_mat) / (K_total + epsilon))
Example #7
0
    def solve_nonlinear(self, params, unknowns, resids):
        # Unpack variables for thickness and average radius at each can interface
        twall = params['t_full']
        Rb    = 0.5*params['d_full'][:-1]
        Rt    = 0.5*params['d_full'][1:]
        zz    = params['z_full']
        H     = np.diff(zz)
        rho   = params['material_density']
        coeff = params['outfitting_factor']
        if coeff < 1.0: coeff += 1.0

        # Total mass of cylinder
        V_shell = frustum.frustumShellVol(Rb, Rt, twall, H)
        unknowns['mass'] = coeff * rho * V_shell
        
        # Center of mass of each can/section
        cm_section = zz[:-1] + frustum.frustumShellCG(Rb, Rt, twall, H)
        unknowns['section_center_of_mass'] = cm_section

        # Center of mass of cylinder
        V_shell += eps
        unknowns['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

        unknowns['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    = params['material_cost_rate'] #1.1 # USD / kg carbon steel plate
        k_f    = params['labor_cost_rate'] #1.0 # USD / min labor
        k_p    = params['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
        unknowns['cost'] = K_m + K_o + K_p + K_f
Example #8
0
    def compute_natural_periods(self, params, unknowns):
        # Unpack variables
        ncolumn         = int(params['number_of_auxiliary_columns'])
        R_semi          = params['radius_to_auxiliary_column']
        
        m_column        = np.sum(params['auxiliary_column_mass'])
        m_struct        = params['structural_mass']
        m_water         = unknowns['variable_ballast_mass']
        m_a_base        = params['base_column_added_mass']
        m_a_column      = params['auxiliary_column_added_mass']
        
        rhoWater        = params['water_density']
        V_system        = params['total_displacement']
        h_metacenter    = unknowns['metacentric_height']

        Awater_base     = params['base_column_Awaterplane']
        Awater_column   = params['auxiliary_column_Awaterplane']
        I_base          = params['base_column_moments_of_inertia']
        I_column        = params['auxiliary_column_moments_of_inertia']

        z_cg_base       = params['base_column_center_of_mass']
        z_cb_base       = params['base_column_center_of_buoyancy']
        z_cg_column     = params['auxiliary_column_center_of_mass']
        z_cb_column     = params['auxiliary_column_center_of_buoyancy']
        
        K_moor          = np.diag( params['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
        M_mat[:3] = m_struct + np.maximum(m_water, 0.0)
        # Add up moments of intertia of all columns for other entries
        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) )
        dz_cg     = z_cg_column - z_cg_base
        I_total   = assembleI( I_base )
        I_column  = assembleI( I_column )
        for k in xrange(ncolumn):
            R        = np.array([radii_x[k], radii_y[k], dz_cg])
            I_total += I_column + m_column*(np.dot(R, R)*np.eye(3) - np.outer(R, R))
        M_mat[3:] = unassembleI( I_total )[:3]
        unknowns['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_base[:3] + ncolumn*m_a_column[:3]

        # Add up moments of inertia, move added mass moments from CofB to CofG
        dz_cgcb   = z_cb_base - z_cg_base
        I_base    = assembleI( np.r_[m_a_base[3:]  , np.zeros(3)] )
        R         = np.array([0.0, 0.0, dz_cgcb])
        I_total   = I_base + m_a_base[0]*(np.dot(R, R)*np.eye(3) - np.outer(R, R))

        # Add up added moments of intertia of all columns for other entries
        dz_cgcb   = z_cb_column - z_cg_base
        I_column  = assembleI( np.r_[m_a_column[3:], np.zeros(3)] )
        for k in xrange(ncolumn):
            R        = np.array([radii_x[k], radii_y[k], dz_cgcb])
            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]
        unknowns['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_base + ncolumn*Awater_column)
        K_hydro[3:5] = rhoWater * gravity * V_system * h_metacenter
        unknowns['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)
        unknowns['natural_periods'] = 2*np.pi * np.sqrt( (M_mat + A_mat) / (K_total + epsilon) )
Example #9
0
    def runMAP(self, params, unknowns):
        """Writes MAP input file, executes, and then queries MAP to find 
        maximum loading and displacement from vessel displacement around all 360 degrees
        
        INPUTS:
        ----------
        params   : dictionary of input parameters
        unknowns : dictionary of output parameters
        
        OUTPUTS  : none (multiple unknown dictionary values set)
        """
        # Unpack variables
        rhoWater      = params['water_density']
        waterDepth    = params['water_depth']
        fairleadDepth = params['fairlead']
        Dmooring      = params['mooring_diameter']
        offset        = params['max_offset']
        heel          = params['operational_heel']
        gamma         = params['gamma_f']
        n_connect     = int(params['number_of_mooring_connections'])
        n_lines       = int(params['mooring_lines_per_connection'])
        ntotal        = n_connect * n_lines

        # Write the mooring system input file for this design
        self.write_input_file(params)

        # Initiate MAP++ for this design
        mymap = pyMAP( )
        #mymap.ierr = 0
        mymap.map_set_sea_depth(waterDepth)
        mymap.map_set_gravity(gravity)
        mymap.map_set_sea_density(rhoWater)
        mymap.read_list_input(self.finput)
        mymap.init( )

        # Get the stiffness matrix at neutral position
        mymap.displace_vessel(0, 0, 0, 0, 0, 0)
        mymap.update_states(0.0, 0)
        K = mymap.linear(1e-4) # Input finite difference epsilon
        unknowns['mooring_stiffness'] = np.array( K )
        mymap.displace_vessel(0, 0, 0, 0, 0, 0)
        mymap.update_states(0.0, 0)
        
        # Get the vertical load on the structure and plotting data
        F_neutral = np.zeros((NLINES_MAX, 3))
        plotMat   = np.zeros((NLINES_MAX, NPTS_PLOT, 3))
        nptsMOI   = 100
        xyzpts    = np.zeros((ntotal, nptsMOI, 3)) # For MOI calculation
        for k in range(ntotal):
            (F_neutral[k,0], F_neutral[k,1], F_neutral[k,2]) = mymap.get_fairlead_force_3d(k)
            plotMat[k,:,0] = mymap.plot_x(k, NPTS_PLOT)
            plotMat[k,:,1] = mymap.plot_y(k, NPTS_PLOT)
            plotMat[k,:,2] = mymap.plot_z(k, NPTS_PLOT)
            xyzpts[k,:,0]  = mymap.plot_x(k, nptsMOI)
            xyzpts[k,:,1]  = mymap.plot_y(k, nptsMOI)
            xyzpts[k,:,2]  = mymap.plot_z(k, nptsMOI)
            if self.tlpFlag:
                # Seems to be a bug in the plot arrays from MAP++ for plotting output with taut lines
                plotMat[k,:,2] = np.linspace(-fairleadDepth, -waterDepth, NPTS_PLOT)
                xyzpts[k,:,2]  = np.linspace(-fairleadDepth, -waterDepth, nptsMOI)
        unknowns['mooring_neutral_load'] = F_neutral
        unknowns['mooring_plot_matrix']  = plotMat
        
        # Fine line segment length, ds = sqrt(dx^2 + dy^2 + dz^2)
        xyzpts_dx = np.gradient(xyzpts[:,:,0], axis=1)
        xyzpts_dy = np.gradient(xyzpts[:,:,1], axis=1)
        xyzpts_dz = np.gradient(xyzpts[:,:,2], axis=1)
        xyzpts_ds = np.sqrt(xyzpts_dx**2 + xyzpts_dy**2 + xyzpts_dz**2)

        # Initialize inertia tensor integrands in https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor
        # Taking MOI relative to body centerline at fairlead depth
        r0 = np.array([0.0, 0.0, -fairleadDepth])
        R  = np.zeros((ntotal, nptsMOI, 6))
        for ii in range(nptsMOI):
            for k in range(ntotal):
                r = xyzpts[k,ii,:] - r0
                R[k,ii,:] = unassembleI(np.dot(r,r)*np.eye(3) - np.outer(r,r))
        Imat = self.wet_mass_per_length * np.trapz(R, x=xyzpts_ds[:,:,np.newaxis], axis=1)
        unknowns['mooring_moments_of_inertia'] = np.abs( Imat.sum(axis=0) )

        # Get the restoring moment at maximum angle of heel
        # Since we don't know the substucture CG, have to just get the forces of the lines now and do the cross product later
        # We also want to allow for arbitraty wind direction and yaw of rotor relative to mooring lines, so we will compare
        # pitch and roll forces as extremes
        # TODO: This still isgn't quite the same as clocking the mooring lines in different directions,
        # which is what we want to do, but that requires multiple input files and solutions
        Fh = np.zeros((NLINES_MAX,3))
        mymap.displace_vessel(0, 0, 0, 0, heel, 0)
        mymap.update_states(0.0, 0)
        for k in range(ntotal):
            Fh[k][0], Fh[k][1], Fh[k][2] = mymap.get_fairlead_force_3d(k)

        unknowns['operational_heel_restoring_force'] = Fh

        # Get angles by which to find the weakest line
        dangle  = 2.0
        angles  = np.deg2rad( np.arange(0.0, 360.0, dangle) )
        nangles = len(angles)

        # Get restoring force at weakest line at maximum allowable offset
        # Will global minimum always be along mooring angle?
        max_tension   = 0.0
        max_angle     = None
        min_angle     = None
        F_max_tension = None
        F_min         = np.inf
        T = np.zeros((NLINES_MAX,))
        F = np.zeros((NLINES_MAX,))
        # Loop around all angles to find weakest point
        for a in angles:
            # Unit vector and offset in x-y components
            idir  = np.array([np.cos(a), np.sin(a)])
            surge = offset * idir[0]
            sway  = offset * idir[1]

            # Get restoring force of offset at this angle
            mymap.displace_vessel(surge, sway, 0, 0, 0, 0) # 0s for z, angles
            mymap.update_states(0.0, 0)
            for k in range(ntotal):
                # Force in x-y-z coordinates
                fx,fy,fz = mymap.get_fairlead_force_3d(k)
                T[k]     = np.sqrt(fx*fx + fy*fy + fz*fz)
                # Total restoring force
                F[k]     = np.dot([fx, fy], idir)
                
            # Check if this is the weakest direction (highest tension)
            tempMax = T.max()
            if tempMax > max_tension:
                max_tension   = tempMax
                F_max_tension = F.sum()
                max_angle     = a
            if F.sum() < F_min:
                F_min     = F.sum()
                min_angle = a
                
        # Store the weakest restoring force when the vessel is offset the maximum amount
        unknowns['max_offset_restoring_force'] = F_min

        # Check for good convergence
        if (plotMat[0,-1,-1] + fairleadDepth) > 1.0:
            unknowns['axial_unity'] = 1e30
        else:
            unknowns['axial_unity'] = gamma * max_tension / self.min_break_load

        mymap.end()
Example #10
0
    def solve_nonlinear(self, params, unknowns, resids):
        # Unpack variables for thickness and average radius at each can interface
        twall = params['t_full']
        Rb = 0.5 * params['d_full'][:-1]
        Rt = 0.5 * params['d_full'][1:]
        zz = params['z_full']
        H = np.diff(zz)
        rho = params['material_density']
        coeff = params['outfitting_factor']
        if coeff < 1.0: coeff += 1.0

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

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

        # Center of mass of cylinder
        V_shell += eps
        unknowns['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

        unknowns['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 = params['material_cost_rate']  #1.1 # USD / kg carbon steel plate
        k_f = params['labor_cost_rate']  #1.0 # USD / min labor
        k_p = params['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
        unknowns['cost'] = K_m + K_o + K_p + K_f