Exemplo n.º 1
0
    def screen_output(self):
        line = ''
        cout.cout_wrap.print_separator()
        # output header
        if self.settings['coefficients']:
            line = "{0:5s} | {1:10s} | {2:10s} | {3:10s} | {4:10s} | {5:10s} | {6:10s}".format(
                'tstep', '  fx_g', '  fy_g', '  fz_g', '  Cfx_g', '  Cfy_g',
                '  Cfz_g')
            cout.cout_wrap(line, 1)
            for self.ts in range(self.ts_max):
                fx = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 0], 0) + \
                     np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 0], 0)

                fy = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 1], 0) + \
                     np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 1], 0)

                fz = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 2], 0) + \
                     np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 2], 0)

                Cfx, Cfy, Cfz = self.calculate_coefficients(fx, fy, fz)

                line = "{0:5d} | {1: 8.3e} | {2: 8.3e} | {3: 8.3e} | {4: 8.3e} | {5: 8.3e} | {6: 8.3e}".format(
                    self.ts, fx, fy, fz, Cfx, Cfy, Cfz)
                cout.cout_wrap(line, 1)
        else:
            line = "{0:5s} | {1:10s} | {2:10s} | {3:10s}".format(
                'tstep', '  fx_g', '  fy_g', '  fz_g')
            cout.cout_wrap(line, 1)
            for self.ts in range(self.ts_max):
                fx = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 0], 0) + \
                     np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 0], 0)

                fy = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 1], 0) + \
                     np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 1], 0)

                fz = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 2], 0) + \
                     np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 2], 0)

                line = "{0:5d} | {1: 8.3e} | {2: 8.3e} | {3: 8.3e}".format(
                    self.ts, fx, fy, fz)
                cout.cout_wrap(line, 1)
Exemplo n.º 2
0
 def run(self):
     prescribed_motion = False
     try:
         prescribed_motion = self.settings['prescribed_motion'].value
     except KeyError:
         pass
     if prescribed_motion is True:
         cout.cout_wrap('Running non linear dynamic solver...', 2)
         # xbeamlib.cbeam3_solv_nlndyn(self.data.beam, self.settings)
     else:
         cout.cout_wrap('Running non linear dynamic solver with RB...', 2)
         xbeamlib.xbeam_solv_couplednlndyn(self.data.structure,
                                           self.settings)
     cout.cout_wrap('...Finished', 2)
     return self.data
Exemplo n.º 3
0
    def initialise(self, in_dict):
        self.in_dict = in_dict
        settings.to_custom_types(self.in_dict,
                                 self.settings_types,
                                 self.settings_default,
                                 no_ctype=True)
        self.settings = self.in_dict

        self.x_grid, self.y_grid, self.z_grid, self.vel = self.read_turbsim_bts(
            self.settings['turbulent_field'], self.settings['case_with_tower'])
        if not self.settings['new_orientation'] == 'xyz':
            # self.settings['new_orientation'] = 'zyx'
            self.x_grid, self.y_grid, self.z_grid, self.vel = self.change_orientation(
                self.x_grid, self.y_grid, self.z_grid, self.vel,
                self.settings['new_orientation'])

        self.bbox = self.get_field_bbox(self.x_grid, self.y_grid, self.z_grid)
        if self.settings['print_info']:
            cout.cout_wrap('The domain bbox is:', 1)
            cout.cout_wrap(
                ' x = [' + str(self.bbox[0, 0]) + ', ' + str(self.bbox[0, 1]) +
                ']', 1)
            cout.cout_wrap(
                ' y = [' + str(self.bbox[1, 0]) + ', ' + str(self.bbox[1, 1]) +
                ']', 1)
            cout.cout_wrap(
                ' z = [' + str(self.bbox[2, 0]) + ', ' + str(self.bbox[2, 1]) +
                ']', 1)

        self.dist_to_recirculate = 0.
        self.grid_size_vec = np.array([
            np.max(self.x_grid) - np.min(self.x_grid),
            np.max(self.y_grid) - np.min(self.y_grid),
            np.max(self.z_grid) - np.min(self.z_grid)
        ])
        self.grid_size_ufed_dir = np.dot(
            self.grid_size_vec,
            self.settings['u_fed'] / np.linalg.norm(self.settings['u_fed']))
Exemplo n.º 4
0
    def run(self):
        """
        Run the time stepping procedure with controllers and postprocessors
        included.
        """
        # dynamic simulations start at tstep == 1, 0 is reserved for the initial state
        for self.data.ts in range(
                len(self.data.structure.timestep_info),
                self.settings['n_time_steps'].value +
                len(self.data.structure.timestep_info)):
            initial_time = time.perf_counter()
            structural_kstep = self.data.structure.timestep_info[-1].copy()
            aero_kstep = self.data.aero.timestep_info[-1].copy()

            # Add the controller here
            if self.with_controllers:
                state = {'structural': structural_kstep, 'aero': aero_kstep}
                for k, v in self.controllers.items():
                    state = v.control(self.data, state)
                    # this takes care of the changes in options for the solver
                    structural_kstep, aero_kstep = self.process_controller_output(
                        state)

            self.time_aero = 0.0
            self.time_struc = 0.0

            # Copy the controlled states so that the interpolation does not
            # destroy the previous information
            controlled_structural_kstep = structural_kstep.copy()
            controlled_aero_kstep = aero_kstep.copy()

            k = 0
            for k in range(self.settings['fsi_substeps'].value + 1):
                if (k == self.settings['fsi_substeps'].value
                        and self.settings['fsi_substeps']):
                    cout.cout_wrap('The FSI solver did not converge!!!')
                    break

                # generate new grid (already rotated)
                aero_kstep = controlled_aero_kstep.copy()
                self.aero_solver.update_custom_grid(structural_kstep,
                                                    aero_kstep)

                # compute unsteady contribution
                force_coeff = 0.0
                unsteady_contribution = False
                if self.settings['include_unsteady_force_contribution'].value:
                    if self.data.ts > self.settings[
                            'steps_without_unsteady_force'].value:
                        unsteady_contribution = True
                        if k < self.settings[
                                'pseudosteps_ramp_unsteady_force'].value:
                            force_coeff = k / self.settings[
                                'pseudosteps_ramp_unsteady_force'].value
                        else:
                            force_coeff = 1.

                # run the solver
                ini_time_aero = time.perf_counter()
                self.data = self.aero_solver.run(
                    aero_kstep,
                    structural_kstep,
                    convect_wake=True,
                    unsteady_contribution=unsteady_contribution)
                self.time_aero += time.perf_counter() - ini_time_aero

                previous_kstep = structural_kstep.copy()
                structural_kstep = controlled_structural_kstep.copy()

                # move the aerodynamic surface according the the structural one
                self.aero_solver.update_custom_grid(structural_kstep,
                                                    aero_kstep)
                self.map_forces(aero_kstep, structural_kstep, force_coeff)

                # relaxation
                relax_factor = self.relaxation_factor(k)
                relax(self.data.structure, structural_kstep, previous_kstep,
                      relax_factor)

                # check if nan anywhere.
                # if yes, raise exception
                if np.isnan(structural_kstep.steady_applied_forces).any():
                    raise exc.NotConvergedSolver(
                        'NaN found in steady_applied_forces!')
                if np.isnan(structural_kstep.unsteady_applied_forces).any():
                    raise exc.NotConvergedSolver(
                        'NaN found in unsteady_applied_forces!')

                copy_structural_kstep = structural_kstep.copy()
                ini_time_struc = time.perf_counter()
                for i_substep in range(
                        self.settings['structural_substeps'].value + 1):
                    # run structural solver
                    coeff = ((i_substep + 1) /
                             (self.settings['structural_substeps'].value + 1))

                    structural_kstep = self.interpolate_timesteps(
                        step0=self.data.structure.timestep_info[-1],
                        step1=copy_structural_kstep,
                        out_step=structural_kstep,
                        coeff=coeff)

                    self.data = self.structural_solver.run(
                        structural_step=structural_kstep, dt=self.substep_dt)

                self.time_struc += time.perf_counter() - ini_time_struc

                # check convergence
                if self.convergence(k, structural_kstep, previous_kstep):
                    # move the aerodynamic surface according to the structural one
                    self.aero_solver.update_custom_grid(
                        structural_kstep, aero_kstep)
                    break

            # move the aerodynamic surface according the the structural one
            self.aero_solver.update_custom_grid(structural_kstep, aero_kstep)

            self.aero_solver.add_step()
            self.data.aero.timestep_info[-1] = aero_kstep.copy()
            self.structural_solver.add_step()
            self.data.structure.timestep_info[-1] = structural_kstep.copy()

            final_time = time.perf_counter()

            if self.print_info:
                self.residual_table.print_line([
                    self.data.ts, self.data.ts * self.dt.value, k,
                    self.time_struc / (self.time_aero + self.time_struc),
                    final_time - initial_time,
                    np.log10(self.res_dqdt), structural_kstep.for_vel[0],
                    structural_kstep.for_vel[2],
                    np.sum(structural_kstep.steady_applied_forces[:, 0]),
                    np.sum(structural_kstep.steady_applied_forces[:, 2])
                ])
            self.structural_solver.extract_resultants()
            # run postprocessors
            if self.with_postprocessors:
                for postproc in self.postprocessors:
                    self.data = self.postprocessors[postproc].run(online=True)

        if self.print_info:
            cout.cout_wrap('...Finished', 1)
        return self.data
Exemplo n.º 5
0
    def initialise(self, data, in_dict=None):
        self.in_dict = in_dict

        # For backwards compatibility
        if len(self.in_dict.keys()) == 0:
            cout.cout_wrap(
                "WARNING: The code will run for backwards compatibility. \
                   In future releases you will need to define a 'wake_shape_generator' in ``AerogridLoader''. \
                   Please, check the documentation", 3)

            # Look for an aerodynamic solver
            if 'StaticUvlm' in data.settings:
                aero_solver_name = 'StaticUvlm'
                aero_solver_settings = data.settings['StaticUvlm']
            elif 'SHWUvlm' in data.settings:
                aero_solver_name = 'SHWUvlm'
                aero_solver_settings = data.settings['SHWUvlm']
            elif 'StaticCoupled' in data.settings:
                aero_solver_name = data.settings['StaticCoupled'][
                    'aero_solver']
                aero_solver_settings = data.settings['StaticCoupled'][
                    'aero_solver_settings']
            elif 'StaticCoupledRBM' in data.settings:
                aero_solver_name = data.settings['StaticCoupledRBM'][
                    'aero_solver']
                aero_solver_settings = data.settings['StaticCoupledRBM'][
                    'aero_solver_settings']
            elif 'DynamicCoupled' in data.settings:
                aero_solver_name = data.settings['DynamicCoupled'][
                    'aero_solver']
                aero_solver_settings = data.settings['DynamicCoupled'][
                    'aero_solver_settings']
            elif 'StepUvlm' in data.settings:
                aero_solver_name = 'StepUvlm'
                aero_solver_settings = data.settings['StepUvlm']
            else:
                raise RuntimeError("ERROR: aerodynamic solver not found")

            # Get the minimum parameters needed to define the wake
            aero_solver = solver_interface.solver_from_string(aero_solver_name)
            settings.to_custom_types(aero_solver_settings,
                                     aero_solver.settings_types,
                                     aero_solver.settings_default)

            if 'dt' in aero_solver_settings.keys():
                dt = aero_solver_settings['dt'].value
            elif 'rollup_dt' in aero_solver_settings.keys():
                dt = aero_solver_settings['rollup_dt'].value
            else:
                # print(aero_solver['velocity_field_input']['u_inf'])
                dt = 1. / aero_solver_settings['velocity_field_input'][
                    'u_inf'].value
            self.in_dict = {
                'u_inf':
                aero_solver_settings['velocity_field_input']['u_inf'],
                'u_inf_direction':
                aero_solver_settings['velocity_field_input']
                ['u_inf_direction'],
                'dt':
                dt
            }

        settings.to_custom_types(self.in_dict,
                                 self.settings_types,
                                 self.settings_default,
                                 no_ctype=True)

        self.u_inf = self.in_dict['u_inf']
        self.u_inf_direction = self.in_dict['u_inf_direction']
        self.dt = self.in_dict['dt']

        if self.in_dict['dx1'] == -1:
            self.dx1 = self.u_inf * self.dt
        else:
            self.dx1 = self.in_dict['dx1']

        self.ndx1 = self.in_dict['ndx1']
        self.r = self.in_dict['r']

        if self.in_dict['dxmax'] == -1:
            self.dxmax = self.dx1
Exemplo n.º 6
0
def initialise_solver(solver_name, print_info=True):
    if print_info:
        cout.cout_wrap('Generating an instance of %s' % solver_name, 2)
    cls_type = solver_from_string(solver_name)
    solver = cls_type()
    return solver
Exemplo n.º 7
0
    def check_stability(self, restart_arnoldi=False):
        r"""
        Checks the stability of the ROM by computing its eigenvalues.

        If the resulting system is unstable, the Arnoldi procedure can be restarted to eliminate the eigenvalues
        outside the stability boundary.

        However, if this is the case, the ROM no longer matches the moments of the original system at the specific
        frequencies since now the approximation is done with respect to a system of the form:

            .. math::
                \Sigma = \left(\begin{array}{c|c} \mathbf{A} & \mathbf{\bar{B}}
                \\ \hline \mathbf{C} & \ \end{array}\right)

        where :math:`\mathbf{\bar{B}} = (\mu \mathbf{I}_n - \mathbf{A})\mathbf{B}`

        Args:
            restart_arnoldi (bool): Restart the relevant Arnoldi algorithm with the unstable eigenvalues removed.


        """
        assert self.ssrom is not None, 'ROM not calculated yet'

        eigs = sclalg.eigvals(self.ssrom.A)

        eigs_abs = np.abs(eigs)

        order = np.argsort(eigs_abs)[::-1]
        eigs = eigs[order]
        eigs_abs = eigs_abs[order]

        unstable = False
        if self.sstype == 'dt':
            if any(eigs_abs > 1.):
                unstable = True
                unstable_eigenvalues = eigs[eigs_abs > 1.]
                try:
                    cout.cout_wrap(
                        'Unstable ROM - %d Eigenvalues with |r| > 1' %
                        len(unstable_eigenvalues))
                except ValueError:
                    pass
                for mu in unstable_eigenvalues:
                    try:
                        cout.cout_wrap('\tmu = %f + %fj' % (mu.real, mu.imag))
                    except ValueError:
                        pass
            else:
                try:
                    cout.cout_wrap('ROM is stable')
                    cout.cout_wrap('\tDT Eigenvalues:')
                    cout.cout_wrap(
                        len(eigs_abs) * '\t\tmu = %4f + %4fj\n' %
                        tuple(eigs.view(float)))
                except ValueError:
                    pass

        else:
            if any(eigs.real > 0):
                unstable = True
                cout.cout_wrap('Unstable ROM', 3)
                unstable_eigenvalues = eigs[eigs.real > 0]
            else:
                cout.cout_wrap('ROM is stable')

        # Restarted Arnoldi
        # Modify the B matrix in the full state system -> maybe better to have a copy
        if unstable and restart_arnoldi:
            print(
                'Restarting the Arnoldi method - Reducing ROM order from r = %d to r = %d'
                % (self.r, self.r - 1))
            self.ss_original = self.ss

            remove_unstable = np.eye(self.ss.states)
            for mu in unstable_eigenvalues:
                remove_unstable = np.matmul(
                    remove_unstable,
                    mu * np.eye(self.ss.states) - self.ss.A)

            self.ss.B = remove_unstable.dot(self.ss.B)
            # self.ss.C = self.ss.C.dot(remove_unstable.T)

            if self.r > 1:
                self.r -= 1
                self.run(self.algorithm, self.r, self.frequency)
            else:
                print(
                    'Unable to reduce ROM any further - ROM still unstable...')

        return not unstable
Exemplo n.º 8
0
def generate_from_excel_type02(
        chord_panels,
        rotation_velocity,
        pitch_deg,
        excel_file_name='database_excel_type02.xlsx',
        excel_sheet_parameters='parameters',
        excel_sheet_structural_blade='structural_blade',
        excel_sheet_discretization_blade='discretization_blade',
        excel_sheet_aero_blade='aero_blade',
        excel_sheet_airfoil_info='airfoil_info',
        excel_sheet_airfoil_coord='airfoil_coord',
        excel_sheet_structural_tower='structural_tower',
        m_distribution='uniform',
        h5_cross_sec_prop=None,
        n_points_camber=100,
        tol_remove_points=1e-3,
        user_defined_m_distribution_type=None,
        wsp=0.,
        dt=0.):
    """
    generate_from_excel_type02

    Function needed to generate a wind turbine from an excel database according to OpenFAST inputs

    Args:
        chord_panels (int): Number of panels on the blade surface in the chord direction
        rotation_velocity (float): Rotation velocity of the rotor
        pitch_deg (float): pitch angle in degrees
        excel_file_name (str):
        excel_sheet_structural_blade (str):
        excel_sheet_aero_blade (str):
        excel_sheet_airfoil_coord (str):
        excel_sheet_parameters (str):
        excel_sheet_structural_tower (str):
        m_distribution (str):
        n_points_camber (int): number of points to define the camber of the airfoil,
        tol_remove_points (float): maximum distance to remove adjacent points
        user_defined_m_distribution_type (string): type of distribution of the chordwise panels when 'm_distribution' == 'user_defined'
        camber_effects_on_twist (bool): When true plain airfoils are used and the blade is twisted and preloaded based on thin airfoil theory
        wsp (float): wind speed (It may be needed for discretisation purposes)
        dt (float): time step (It may be needed for discretisation purposes)

    Returns:
        wt (sharpy.utils.generate_cases.AeroelasticInfromation): Aeroelastic infrmation of the wind turbine
        LC (list): list of all the Lagrange constraints needed in the cases (sharpy.utils.generate_cases.LagrangeConstraint)
        MB (list): list of the multibody information of each body (sharpy.utils.generate_cases.BodyInfrmation)
    """

    rotor = rotor_from_excel_type02(
        chord_panels,
        rotation_velocity,
        pitch_deg,
        excel_file_name=excel_file_name,
        excel_sheet_parameters=excel_sheet_parameters,
        excel_sheet_structural_blade=excel_sheet_structural_blade,
        excel_sheet_discretization_blade=excel_sheet_discretization_blade,
        excel_sheet_aero_blade=excel_sheet_aero_blade,
        excel_sheet_airfoil_info=excel_sheet_airfoil_info,
        excel_sheet_airfoil_coord=excel_sheet_airfoil_coord,
        m_distribution=m_distribution,
        h5_cross_sec_prop=h5_cross_sec_prop,
        n_points_camber=n_points_camber,
        tol_remove_points=tol_remove_points,
        user_defined_m_distribution_type=user_defined_m_distribution_type,
        wsp=0.,
        dt=0.)

    ######################################################################
    ## TOWER
    ######################################################################

    # Read from excel file
    HtFract = gc.read_column_sheet_type01(excel_file_name,
                                          excel_sheet_structural_tower,
                                          'HtFract')
    TMassDen = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TMassDen')
    TwFAStif = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwFAStif')
    TwSSStif = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwSSStif')
    # TODO> variables to be defined
    TwGJStif = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwGJStif')
    TwEAStif = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwEAStif')
    TwFAIner = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwFAIner')
    TwSSIner = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwSSIner')
    TwFAcgOf = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwFAcgOf')
    TwSScgOf = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_tower,
                                           'TwSScgOf')

    # Define the TOWER
    TowerHt = gc.read_column_sheet_type01(excel_file_name,
                                          excel_sheet_parameters, 'TowerHt')
    Elevation = TowerHt * HtFract

    tower = gc.AeroelasticInformation()
    tower.StructuralInformation.num_elem = len(Elevation) - 2
    tower.StructuralInformation.num_node_elem = 3
    tower.StructuralInformation.compute_basic_num_node()

    # Interpolate excel variables into the correct locations
    node_r, elem_r = create_node_radial_pos_from_elem_centres(
        Elevation, tower.StructuralInformation.num_node,
        tower.StructuralInformation.num_elem,
        tower.StructuralInformation.num_node_elem)

    # Stiffness
    elem_EA = np.interp(elem_r, Elevation, TwEAStif)
    elem_EIz = np.interp(elem_r, Elevation, TwSSStif)
    elem_EIy = np.interp(elem_r, Elevation, TwFAStif)
    elem_GJ = np.interp(elem_r, Elevation, TwGJStif)
    # Stiffness: estimate unknown properties
    cout.cout_wrap.print_file = False
    cout.cout_wrap('WARNING: The poisson cofficient is assumed equal to 0.3',
                   3)
    cout.cout_wrap('WARNING: Cross-section area is used as shear area', 3)
    poisson_coef = 0.3
    elem_GAy = elem_EA / 2.0 / (1.0 + poisson_coef)
    elem_GAz = elem_EA / 2.0 / (1.0 + poisson_coef)

    # Inertia
    elem_mass_per_unit_length = np.interp(elem_r, Elevation, TMassDen)
    elem_mass_iner_y = np.interp(elem_r, Elevation, TwFAIner)
    elem_mass_iner_z = np.interp(elem_r, Elevation, TwSSIner)
    # TODO: check yz axis and Flap-edge
    elem_pos_cg_B = np.zeros((tower.StructuralInformation.num_elem, 3), )
    elem_pos_cg_B[:, 1] = np.interp(elem_r, Elevation, TwSScgOf)
    elem_pos_cg_B[:, 2] = np.interp(elem_r, Elevation, TwFAcgOf)

    # Stiffness: estimate unknown properties
    cout.cout_wrap(
        'WARNING: Using perpendicular axis theorem to compute the inertia around xB',
        3)
    elem_mass_iner_x = elem_mass_iner_y + elem_mass_iner_z

    # Create the tower
    tower.StructuralInformation.create_mass_db_from_vector(
        elem_mass_per_unit_length, elem_mass_iner_x, elem_mass_iner_y,
        elem_mass_iner_z, elem_pos_cg_B)
    tower.StructuralInformation.create_stiff_db_from_vector(
        elem_EA, elem_GAy, elem_GAz, elem_GJ, elem_EIy, elem_EIz)

    coordinates = np.zeros((tower.StructuralInformation.num_node, 3), )
    coordinates[:, 0] = node_r

    tower.StructuralInformation.generate_1to1_from_vectors(
        num_node_elem=tower.StructuralInformation.num_node_elem,
        num_node=tower.StructuralInformation.num_node,
        num_elem=tower.StructuralInformation.num_elem,
        coordinates=coordinates,
        stiffness_db=tower.StructuralInformation.stiffness_db,
        mass_db=tower.StructuralInformation.mass_db,
        frame_of_reference_delta='y_AFoR',
        vec_node_structural_twist=np.zeros(
            (tower.StructuralInformation.num_node, ), ),
        num_lumped_mass=1)

    tower.StructuralInformation.boundary_conditions = np.zeros(
        (tower.StructuralInformation.num_node), dtype=int)
    tower.StructuralInformation.boundary_conditions[0] = 1

    # Read overhang and nacelle properties from excel file
    overhang_len = gc.read_column_sheet_type01(excel_file_name,
                                               excel_sheet_parameters,
                                               'overhang')
    # HubMass = gc.read_column_sheet_type01(excel_file_name, excel_sheet_nacelle, 'HubMass')
    NacelleMass = gc.read_column_sheet_type01(excel_file_name,
                                              excel_sheet_parameters,
                                              'NacMass')
    # NacelleYawIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_nacelle, 'NacelleYawIner')

    # Include nacelle mass
    tower.StructuralInformation.lumped_mass_nodes = np.array(
        [tower.StructuralInformation.num_node - 1], dtype=int)
    tower.StructuralInformation.lumped_mass = np.array([NacelleMass],
                                                       dtype=float)

    tower.AerodynamicInformation.set_to_zero(
        tower.StructuralInformation.num_node_elem,
        tower.StructuralInformation.num_node,
        tower.StructuralInformation.num_elem)

    # Assembly overhang with the tower
    # numberOfBlades = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'NumBl')
    tilt = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters,
                                       'ShftTilt') * deg2rad
    # cone = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'Cone')*deg2rad

    overhang = gc.AeroelasticInformation()
    overhang.StructuralInformation.num_node = 3
    overhang.StructuralInformation.num_node_elem = 3
    overhang.StructuralInformation.compute_basic_num_elem()
    node_pos = np.zeros((overhang.StructuralInformation.num_node, 3), )
    node_pos[:, 0] += tower.StructuralInformation.coordinates[-1, 0]
    node_pos[:, 0] += np.linspace(0., overhang_len * np.sin(tilt * deg2rad),
                                  overhang.StructuralInformation.num_node)
    node_pos[:, 2] = np.linspace(0., -overhang_len * np.cos(tilt * deg2rad),
                                 overhang.StructuralInformation.num_node)
    # TODO: change the following by real values
    # Same properties as the last element of the tower
    cout.cout_wrap(
        "WARNING: Using the structural properties of the last tower section for the overhang",
        3)
    oh_mass_per_unit_length = tower.StructuralInformation.mass_db[-1, 0, 0]
    oh_mass_iner = tower.StructuralInformation.mass_db[-1, 3, 3]
    oh_EA = tower.StructuralInformation.stiffness_db[-1, 0, 0]
    oh_GA = tower.StructuralInformation.stiffness_db[-1, 1, 1]
    oh_GJ = tower.StructuralInformation.stiffness_db[-1, 3, 3]
    oh_EI = tower.StructuralInformation.stiffness_db[-1, 4, 4]
    overhang.StructuralInformation.generate_uniform_sym_beam(
        node_pos,
        oh_mass_per_unit_length,
        oh_mass_iner,
        oh_EA,
        oh_GA,
        oh_GJ,
        oh_EI,
        num_node_elem=3,
        y_BFoR='y_AFoR',
        num_lumped_mass=0)

    overhang.StructuralInformation.boundary_conditions = np.zeros(
        (overhang.StructuralInformation.num_node), dtype=int)
    overhang.StructuralInformation.boundary_conditions[-1] = -1

    overhang.AerodynamicInformation.set_to_zero(
        overhang.StructuralInformation.num_node_elem,
        overhang.StructuralInformation.num_node,
        overhang.StructuralInformation.num_elem)

    tower.assembly(overhang)
    tower.remove_duplicated_points(tol_remove_points)

    ######################################################################
    ##  WIND TURBINE
    ######################################################################
    # Assembly the whole case
    wt = tower.copy()
    hub_position = tower.StructuralInformation.coordinates[-1, :]
    rotor.StructuralInformation.coordinates += hub_position
    wt.assembly(rotor)

    # Redefine the body numbers
    wt.StructuralInformation.body_number *= 0
    wt.StructuralInformation.body_number[tower.StructuralInformation.
                                         num_elem:wt.StructuralInformation.
                                         num_elem] += 1

    ######################################################################
    ## MULTIBODY
    ######################################################################
    # Define the boundary condition between the rotor and the tower tip
    LC1 = gc.LagrangeConstraint()
    LC1.behaviour = 'hinge_node_FoR_constant_vel'
    LC1.node_in_body = tower.StructuralInformation.num_node - 1
    LC1.body = 0
    LC1.body_FoR = 1
    LC1.rot_axisB = np.array([1., 0., 0.0])
    LC1.rot_vel = -rotation_velocity

    LC = []
    LC.append(LC1)

    # Define the multibody infromation for the tower and the rotor
    MB1 = gc.BodyInformation()
    MB1.body_number = 0
    MB1.FoR_position = np.zeros((6, ), )
    MB1.FoR_velocity = np.zeros((6, ), )
    MB1.FoR_acceleration = np.zeros((6, ), )
    MB1.FoR_movement = 'prescribed'
    MB1.quat = np.array([1.0, 0.0, 0.0, 0.0])

    MB2 = gc.BodyInformation()
    MB2.body_number = 1
    MB2.FoR_position = np.array([
        rotor.StructuralInformation.coordinates[0, 0],
        rotor.StructuralInformation.coordinates[0, 1],
        rotor.StructuralInformation.coordinates[0, 2], 0.0, 0.0, 0.0
    ])
    MB2.FoR_velocity = np.array([0., 0., 0., 0., 0., rotation_velocity])
    MB2.FoR_acceleration = np.zeros((6, ), )
    MB2.FoR_movement = 'free'
    MB2.quat = algebra.euler2quat(np.array([0.0, tilt, 0.0]))

    MB = []
    MB.append(MB1)
    MB.append(MB2)

    ######################################################################
    ## RETURN
    ######################################################################
    return wt, LC, MB
Exemplo n.º 9
0
def rotor_from_excel_type03(in_op_params, in_geom_params, in_excel_description,
                            in_options):
    """
    generate_from_excel_type03_db

    Function needed to generate a wind turbine from an excel database type03

    Args:
        op_param (dict): Dictionary with operating parameters
        geom_param (dict): Dictionray with geometical parameters
        excel_description (dict): Dictionary describing the sheets of the excel file
        option (dict): Dictionary with the different options for the wind turbine generation

    Returns:
        rotor (sharpy.utils.generate_cases.AeroelasticInfromation): Aeroelastic information of the rotor
    """
    # Default values
    op_params = {}
    op_params['rotation_velocity'] = None  # Rotation velocity of the rotor
    op_params['pitch_deg'] = None  # pitch angle in degrees
    op_params[
        'wsp'] = 0.  # wind speed (It may be needed for discretisation purposes)
    op_params[
        'dt'] = 0.  # time step (It may be needed for discretisation purposes)

    geom_params = {}
    geom_params[
        'chord_panels'] = None  # Number of panels on the blade surface in the chord direction
    geom_params[
        'tol_remove_points'] = 1e-3  # maximum distance to remove adjacent points
    geom_params[
        'n_points_camber'] = 100  # number of points to define the camber of the airfoil
    geom_params[
        'h5_cross_sec_prop'] = None  # h5 containing mass and stiffness matrices along the blade
    geom_params['m_distribution'] = 'uniform'  #

    options = {}
    options[
        'camber_effect_on_twist'] = False  # When true plain airfoils are used and the blade is twisted and preloaded based on thin airfoil theory
    options[
        'user_defined_m_distribution_type'] = None  # type of distribution of the chordwise panels when 'm_distribution' == 'user_defined'
    options['include_polars'] = False  #

    excel_description = {}
    excel_description['excel_file_name'] = 'database_excel_type02.xlsx'
    excel_description['excel_sheet_parameters'] = 'parameters'
    excel_description['excel_sheet_structural_blade'] = 'structural_blade'
    excel_description[
        'excel_sheet_discretization_blade'] = 'discretization_blade'
    excel_description['excel_sheet_aero_blade'] = 'aero_blade'
    excel_description['excel_sheet_airfoil_info'] = 'airfoil_info'
    excel_description['excel_sheet_airfoil_chord'] = 'airfoil_coord'

    # Overwrite the default values with the values of the input arguments
    for key in in_op_params:
        op_params[key] = in_op_params[key]
    for key in in_geom_params:
        geom_params[key] = in_geom_params[key]
    for key in in_options:
        options[key] = in_options[key]
    for key in in_excel_description:
        excel_description[key] = in_excel_description[key]

    # Put the dictionaries information into variables (to avoid changing the function)
    rotation_velocity = op_params['rotation_velocity']
    pitch_deg = op_params['pitch_deg']
    wsp = op_params['wsp']
    dt = op_params['dt']

    chord_panels = geom_params['chord_panels']
    tol_remove_points = geom_params['tol_remove_points']
    n_points_camber = geom_params['n_points_camber']
    h5_cross_sec_prop = geom_params['h5_cross_sec_prop']
    m_distribution = geom_params['m_distribution']

    camber_effect_on_twist = options['camber_effect_on_twist']
    user_defined_m_distribution_type = options[
        'user_defined_m_distribution_type']
    include_polars = options['include_polars']

    excel_file_name = excel_description['excel_file_name']
    excel_sheet_parameters = excel_description['excel_sheet_parameters']
    excel_sheet_structural_blade = excel_description[
        'excel_sheet_structural_blade']
    excel_sheet_discretization_blade = excel_description[
        'excel_sheet_discretization_blade']
    excel_sheet_aero_blade = excel_description['excel_sheet_aero_blade']
    excel_sheet_airfoil_info = excel_description['excel_sheet_airfoil_info']
    excel_sheet_airfoil_coord = excel_description['excel_sheet_airfoil_chord']

    ######################################################################
    ## BLADE
    ######################################################################
    blade = gc.AeroelasticInformation()

    ######################################################################
    ### STRUCTURE
    ######################################################################
    # Read blade structural information from excel file
    rR_structural = gc.read_column_sheet_type01(excel_file_name,
                                                excel_sheet_structural_blade,
                                                'rR')
    OutPElAxis = gc.read_column_sheet_type01(excel_file_name,
                                             excel_sheet_structural_blade,
                                             'OutPElAxis')
    InPElAxis = gc.read_column_sheet_type01(excel_file_name,
                                            excel_sheet_structural_blade,
                                            'InPElAxis')
    ElAxisAftLEc = gc.read_column_sheet_type01(excel_file_name,
                                               excel_sheet_structural_blade,
                                               'ElAxisAftLEc')
    StrcTwst = gc.read_column_sheet_type01(
        excel_file_name, excel_sheet_structural_blade, 'StrcTwst') * deg2rad
    BMassDen = gc.read_column_sheet_type01(excel_file_name,
                                           excel_sheet_structural_blade,
                                           'BMassDen')
    FlpStff = gc.read_column_sheet_type01(excel_file_name,
                                          excel_sheet_structural_blade,
                                          'FlpStff')
    EdgStff = gc.read_column_sheet_type01(excel_file_name,
                                          excel_sheet_structural_blade,
                                          'EdgStff')
    FlapEdgeStiff = gc.read_column_sheet_type01(excel_file_name,
                                                excel_sheet_structural_blade,
                                                'FlapEdgeStiff')
    GJStff = gc.read_column_sheet_type01(excel_file_name,
                                         excel_sheet_structural_blade,
                                         'GJStff')
    EAStff = gc.read_column_sheet_type01(excel_file_name,
                                         excel_sheet_structural_blade,
                                         'EAStff')
    FlpIner = gc.read_column_sheet_type01(excel_file_name,
                                          excel_sheet_structural_blade,
                                          'FlpIner')
    EdgIner = gc.read_column_sheet_type01(excel_file_name,
                                          excel_sheet_structural_blade,
                                          'EdgIner')
    FlapEdgeIner = gc.read_column_sheet_type01(excel_file_name,
                                               excel_sheet_structural_blade,
                                               'FlapEdgeIner')
    PrebendRef = gc.read_column_sheet_type01(excel_file_name,
                                             excel_sheet_structural_blade,
                                             'PrebendRef')
    PreswpRef = gc.read_column_sheet_type01(excel_file_name,
                                            excel_sheet_structural_blade,
                                            'PreswpRef')
    OutPcg = gc.read_column_sheet_type01(excel_file_name,
                                         excel_sheet_structural_blade,
                                         'OutPcg')
    InPcg = gc.read_column_sheet_type01(excel_file_name,
                                        excel_sheet_structural_blade, 'InPcg')

    # Blade parameters
    TipRad = gc.read_column_sheet_type01(excel_file_name,
                                         excel_sheet_parameters, 'TipRad')
    # HubRad = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'HubRad')

    # Discretization points
    rR = gc.read_column_sheet_type01(excel_file_name,
                                     excel_sheet_discretization_blade, 'rR')

    # Interpolate excel variables into the correct locations
    # Geometry
    if rR[0] < rR_structural[0]:
        rR_structural = np.concatenate((np.array([0.]), rR_structural), )
        OutPElAxis = np.concatenate((np.array([OutPElAxis[0]]), OutPElAxis), )
        InPElAxis = np.concatenate((np.array([InPElAxis[0]]), InPElAxis), )
        ElAxisAftLEc = np.concatenate(
            (np.array([ElAxisAftLEc[0]]), ElAxisAftLEc), )
        StrcTwst = np.concatenate((np.array([StrcTwst[0]]), StrcTwst), )
        BMassDen = np.concatenate((np.array([BMassDen[0]]), BMassDen), )
        FlpStff = np.concatenate((np.array([FlpStff[0]]), FlpStff), )
        EdgStff = np.concatenate((np.array([EdgStff[0]]), EdgStff), )
        FlapEdgeStiff = np.concatenate(
            (np.array([FlapEdgeStiff[0]]), FlapEdgeStiff), )
        GJStff = np.concatenate((np.array([GJStff[0]]), GJStff), )
        EAStff = np.concatenate((np.array([EAStff[0]]), EAStff), )
        FlpIner = np.concatenate((np.array([FlpIner[0]]), FlpIner), )
        EdgIner = np.concatenate((np.array([EdgIner[0]]), EdgIner), )
        FlapEdgeIner = np.concatenate(
            (np.array([FlapEdgeIner[0]]), FlapEdgeIner), )
        PrebendRef = np.concatenate((np.array([PrebendRef[0]]), PrebendRef), )
        PreswpRef = np.concatenate((np.array([PreswpRef[0]]), PreswpRef), )
        OutPcg = np.concatenate((np.array([OutPcg[0]]), OutPcg), )
        InPcg = np.concatenate((np.array([InPcg[0]]), InPcg), )

    # Base parameters
    use_excel_struct_as_elem = False
    if use_excel_struct_as_elem:
        blade.StructuralInformation.num_node_elem = 3
        blade.StructuralInformation.num_elem = len(rR) - 2
        blade.StructuralInformation.compute_basic_num_node()

        node_r, elem_r = create_node_radial_pos_from_elem_centres(
            rR * TipRad, blade.StructuralInformation.num_node,
            blade.StructuralInformation.num_elem,
            blade.StructuralInformation.num_node_elem)
    else:
        # Use excel struct as nodes
        # Check the number of nodes
        blade.StructuralInformation.num_node_elem = 3
        blade.StructuralInformation.num_node = len(rR)
        if ((len(rR) - 1) %
            (blade.StructuralInformation.num_node_elem - 1)) == 0:
            blade.StructuralInformation.num_elem = int(
                (len(rR) - 1) /
                (blade.StructuralInformation.num_node_elem - 1))
            node_r = rR * TipRad
            elem_rR = rR[1::2] + 0.
            elem_r = rR[1::2] * TipRad + 0.
        else:
            raise RuntimeError(
                ("ERROR: Cannot build %d-noded elements from %d nodes" %
                 (blade.StructuralInformation.num_node_elem,
                  blade.StructuralInformation.num_node)))

    node_y = np.interp(rR, rR_structural, InPElAxis) + np.interp(
        rR, rR_structural, PreswpRef)
    node_z = -np.interp(rR, rR_structural, OutPElAxis) - np.interp(
        rR, rR_structural, PrebendRef)
    node_twist = -1.0 * np.interp(rR, rR_structural, StrcTwst)

    coordinates = create_blade_coordinates(
        blade.StructuralInformation.num_node, node_r, node_y, node_z)

    if h5_cross_sec_prop is None:
        # Stiffness
        elem_EA = np.interp(elem_rR, rR_structural, EAStff)
        elem_EIy = np.interp(elem_rR, rR_structural, FlpStff)
        elem_EIz = np.interp(elem_rR, rR_structural, EdgStff)
        elem_EIyz = np.interp(elem_rR, rR_structural, FlapEdgeStiff)
        elem_GJ = np.interp(elem_rR, rR_structural, GJStff)

        # Stiffness: estimate unknown properties
        cout.cout_wrap.print_file = False
        cout.cout_wrap(
            'WARNING: The poisson cofficient is assumed equal to 0.3', 3)
        cout.cout_wrap('WARNING: Cross-section area is used as shear area', 3)
        poisson_coef = 0.3
        elem_GAy = elem_EA / 2.0 / (1.0 + poisson_coef)
        elem_GAz = elem_EA / 2.0 / (1.0 + poisson_coef)
        # Inertia
        elem_pos_cg_B = np.zeros((blade.StructuralInformation.num_elem, 3), )
        elem_pos_cg_B[:, 1] = np.interp(elem_rR, rR_structural, InPcg)
        elem_pos_cg_B[:, 2] = -np.interp(elem_rR, rR_structural, OutPcg)

        elem_mass_per_unit_length = np.interp(elem_rR, rR_structural, BMassDen)
        elem_mass_iner_y = np.interp(elem_rR, rR_structural, FlpIner)
        elem_mass_iner_z = np.interp(elem_rR, rR_structural, EdgIner)
        elem_mass_iner_yz = np.interp(elem_rR, rR_structural, FlapEdgeIner)

        # Inertia: estimate unknown properties
        cout.cout_wrap(
            'WARNING: Using perpendicular axis theorem to compute the inertia around xB',
            3)
        elem_mass_iner_x = elem_mass_iner_y + elem_mass_iner_z

        # Generate blade structural properties
        blade.StructuralInformation.create_mass_db_from_vector(
            elem_mass_per_unit_length, elem_mass_iner_x, elem_mass_iner_y,
            elem_mass_iner_z, elem_pos_cg_B, elem_mass_iner_yz)
        blade.StructuralInformation.create_stiff_db_from_vector(
            elem_EA, elem_GAy, elem_GAz, elem_GJ, elem_EIy, elem_EIz,
            elem_EIyz)

    else:
        # read Mass/Stiffness from database
        cross_prop = h5.readh5(h5_cross_sec_prop).str_prop

        # create mass_db/stiffness_db (interpolate at mid-node of each element)
        blade.StructuralInformation.mass_db = scint.interp1d(
            cross_prop.radius,
            cross_prop.M,
            kind='cubic',
            copy=False,
            assume_sorted=True,
            axis=0,
            bounds_error=False,
            fill_value='extrapolate')(node_r[1::2])
        blade.StructuralInformation.stiffness_db = scint.interp1d(
            cross_prop.radius,
            cross_prop.K,
            kind='cubic',
            copy=False,
            assume_sorted=True,
            axis=0,
            bounds_error=False,
            fill_value='extrapolate')(node_r[1::2])

    blade.StructuralInformation.generate_1to1_from_vectors(
        num_node_elem=blade.StructuralInformation.num_node_elem,
        num_node=blade.StructuralInformation.num_node,
        num_elem=blade.StructuralInformation.num_elem,
        coordinates=coordinates,
        stiffness_db=blade.StructuralInformation.stiffness_db,
        mass_db=blade.StructuralInformation.mass_db,
        frame_of_reference_delta='y_AFoR',
        vec_node_structural_twist=node_twist,
        num_lumped_mass=0)

    # Boundary conditions
    blade.StructuralInformation.boundary_conditions = np.zeros(
        (blade.StructuralInformation.num_node), dtype=int)
    blade.StructuralInformation.boundary_conditions[0] = 1
    blade.StructuralInformation.boundary_conditions[-1] = -1

    ######################################################################
    ### AERODYNAMICS
    ######################################################################
    # Read blade aerodynamic information from excel file
    rR_aero = gc.read_column_sheet_type01(excel_file_name,
                                          excel_sheet_aero_blade, 'rR')
    chord_aero = gc.read_column_sheet_type01(excel_file_name,
                                             excel_sheet_aero_blade, 'BlChord')
    thickness_aero = gc.read_column_sheet_type01(excel_file_name,
                                                 excel_sheet_aero_blade,
                                                 'BlThickness')

    pure_airfoils_names = gc.read_column_sheet_type01(
        excel_file_name, excel_sheet_airfoil_info, 'Name')
    pure_airfoils_thickness = gc.read_column_sheet_type01(
        excel_file_name, excel_sheet_airfoil_info, 'Thickness')

    node_ElAxisAftLEc = np.interp(node_r, rR_structural * TipRad, ElAxisAftLEc)

    # Read coordinates of the pure airfoils
    n_pure_airfoils = len(pure_airfoils_names)

    pure_airfoils_camber = np.zeros((n_pure_airfoils, n_points_camber, 2), )
    xls = pd.ExcelFile(excel_file_name)
    excel_db = pd.read_excel(xls, sheet_name=excel_sheet_airfoil_coord)
    for iairfoil in range(n_pure_airfoils):
        # Look for the NaN
        icoord = 2
        while (not (math.isnan(
                excel_db["%s_x" % pure_airfoils_names[iairfoil]][icoord]))):
            icoord += 1
            if (icoord == len(excel_db["%s_x" %
                                       pure_airfoils_names[iairfoil]])):
                break

        # Compute the camber of the airfoils at the defined chord points
        pure_airfoils_camber[iairfoil, :, 0], pure_airfoils_camber[
            iairfoil, :, 1] = gc.get_airfoil_camber(
                excel_db["%s_x" % pure_airfoils_names[iairfoil]][2:icoord],
                excel_db["%s_y" % pure_airfoils_names[iairfoil]][2:icoord],
                n_points_camber)

    # Basic variables
    surface_distribution = np.zeros((blade.StructuralInformation.num_elem),
                                    dtype=int)

    # Interpolate in the correct positions
    node_chord = np.interp(node_r, rR_aero * TipRad, chord_aero)

    # Define the nodes with aerodynamic properties
    # Look for the first element that is goint to be aerodynamic
    first_aero_elem = 0
    while (elem_r[first_aero_elem] <= rR_aero[0] * TipRad):
        first_aero_elem += 1
    first_aero_node = first_aero_elem * (
        blade.StructuralInformation.num_node_elem - 1)
    aero_node = np.zeros((blade.StructuralInformation.num_node, ), dtype=bool)
    aero_node[first_aero_node:] = np.ones(
        (blade.StructuralInformation.num_node - first_aero_node, ), dtype=bool)

    # Define the airfoil at each stage
    # airfoils = blade.AerodynamicInformation.interpolate_airfoils_camber(pure_airfoils_camber,excel_aero_r, node_r, n_points_camber)

    node_thickness = np.interp(node_r, rR_aero * TipRad, thickness_aero)

    airfoils = blade.AerodynamicInformation.interpolate_airfoils_camber_thickness(
        pure_airfoils_camber, pure_airfoils_thickness, node_thickness,
        n_points_camber)
    airfoil_distribution = np.linspace(0,
                                       blade.StructuralInformation.num_node -
                                       1,
                                       blade.StructuralInformation.num_node,
                                       dtype=int)

    # User defined m distribution
    if (m_distribution == 'user_defined') and (user_defined_m_distribution_type
                                               == 'last_geometric'):
        blade_nodes = blade.StructuralInformation.num_node
        udmd_by_nodes = np.zeros((blade_nodes, chord_panels[0] + 1))
        for inode in range(blade_nodes):
            r = np.linalg.norm(
                blade.StructuralInformation.coordinates[inode, :])
            vrel = np.sqrt(rotation_velocity**2 * r**2 + wsp**2)
            last_length = vrel * dt / node_chord[inode]
            last_length = np.minimum(last_length, 0.5)
            if last_length <= 0.5:
                ratio = gc.get_factor_geometric_progression(
                    last_length, 1., chord_panels)
                udmd_by_nodes[inode, -1] = 1.
                udmd_by_nodes[inode, 0] = 0.
                for im in range(chord_panels[0] - 1, 0, -1):
                    udmd_by_nodes[inode,
                                  im] = udmd_by_nodes[inode,
                                                      im + 1] - last_length
                    last_length *= ratio
                # Check
                if (np.diff(udmd_by_nodes[inode, :]) < 0.).any():
                    sys.error(
                        "ERROR in the panel discretization of the blade in node %d"
                        % (inode))
            else:
                raise RuntimeError(
                    ("ERROR: cannot match the last panel size for node: %d" %
                     inode))
                udmd_by_nodes[inode, :] = np.linspace(0, 1, chord_panels + 1)
    else:
        udmd_by_nodes = None

    node_twist = np.zeros_like(node_chord)
    if camber_effect_on_twist:
        cout.cout_wrap(
            "WARNING: The steady applied Mx should be manually multiplied by the density",
            3)
        for inode in range(blade.StructuralInformation.num_node):
            node_twist[inode] = gc.get_aoacl0_from_camber(
                airfoils[inode, :, 0], airfoils[inode, :, 1])
            mu0 = gc.get_mu0_from_camber(airfoils[inode, :, 0],
                                         airfoils[inode, :, 1])
            r = np.linalg.norm(
                blade.StructuralInformation.coordinates[inode, :])
            vrel = np.sqrt(rotation_velocity**2 * r**2 + wsp**2)
            if inode == 0:
                dr = 0.5 * np.linalg.norm(
                    blade.StructuralInformation.coordinates[1, :] -
                    blade.StructuralInformation.coordinates[0, :])
            elif inode == len(blade.StructuralInformation.coordinates[:,
                                                                      0]) - 1:
                dr = 0.5 * np.linalg.norm(
                    blade.StructuralInformation.coordinates[-1, :] -
                    blade.StructuralInformation.coordinates[-2, :])
            else:
                dr = 0.5 * np.linalg.norm(
                    blade.StructuralInformation.coordinates[inode + 1, :] -
                    blade.StructuralInformation.coordinates[inode - 1, :])
            moment_factor = 0.5 * vrel**2 * node_chord[inode]**2 * dr
            # print("node", inode, "mu0", mu0, "CMc/4", 2.*mu0 + np.pi/2*node_twist[inode])
            blade.StructuralInformation.app_forces[
                inode,
                3] = (2. * mu0 + np.pi / 2 * node_twist[inode]) * moment_factor
            airfoils[inode, :, 1] *= 0.

    # Write SHARPy format
    blade.AerodynamicInformation.create_aerodynamics_from_vec(
        blade.StructuralInformation, aero_node, node_chord, node_twist,
        np.pi * np.ones_like(node_chord), chord_panels, surface_distribution,
        m_distribution, node_ElAxisAftLEc, airfoil_distribution, airfoils,
        udmd_by_nodes)

    # Read the polars of the pure airfoils
    if include_polars:
        pure_polars = [None] * n_pure_airfoils
        for iairfoil in range(n_pure_airfoils):
            excel_sheet_polar = pure_airfoils_names[iairfoil]
            aoa = gc.read_column_sheet_type01(excel_file_name,
                                              excel_sheet_polar, 'AoA')
            cl = gc.read_column_sheet_type01(excel_file_name,
                                             excel_sheet_polar, 'CL')
            cd = gc.read_column_sheet_type01(excel_file_name,
                                             excel_sheet_polar, 'CD')
            cm = gc.read_column_sheet_type01(excel_file_name,
                                             excel_sheet_polar, 'CM')
            polar = ap.polar()
            polar.initialise(np.column_stack((aoa, cl, cd, cm)))
            pure_polars[iairfoil] = polar

        # Generate the polars for each airfoil
        blade.AerodynamicInformation.polars = [
            None
        ] * blade.StructuralInformation.num_node
        for inode in range(blade.StructuralInformation.num_node):
            # Find the airfoils between which the node is;
            ipure = 0
            while pure_airfoils_thickness[ipure] > node_thickness[inode]:
                ipure += 1
                if (ipure == n_pure_airfoils):
                    ipure -= 1
                    break

            coef = (node_thickness[inode] - pure_airfoils_thickness[ipure - 1]
                    ) / (pure_airfoils_thickness[ipure] -
                         pure_airfoils_thickness[ipure - 1])
            polar = ap.interpolate(pure_polars[ipure - 1], pure_polars[ipure],
                                   coef)
            blade.AerodynamicInformation.polars[inode] = polar.table

    ######################################################################
    ## ROTOR
    ######################################################################

    # Read from excel file
    numberOfBlades = gc.read_column_sheet_type01(excel_file_name,
                                                 excel_sheet_parameters,
                                                 'NumBl')
    tilt = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters,
                                       'ShftTilt') * deg2rad
    cone = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters,
                                       'Cone') * deg2rad
    # pitch = gc.read_column_sheet_type01(excel_file_name, excel_sheet_rotor, 'Pitch')*deg2rad

    # Apply pitch
    blade.StructuralInformation.rotate_around_origin(np.array([1., 0., 0.]),
                                                     -pitch_deg * deg2rad)

    # Apply coning
    blade.StructuralInformation.rotate_around_origin(np.array([0., 1., 0.]),
                                                     -cone)

    # Build the whole rotor
    rotor = blade.copy()
    for iblade in range(numberOfBlades - 1):
        blade2 = blade.copy()
        blade2.StructuralInformation.rotate_around_origin(
            np.array([0., 0., 1.]),
            (iblade + 1) * (360.0 / numberOfBlades) * deg2rad)
        rotor.assembly(blade2)
        blade2 = None

    rotor.remove_duplicated_points(tol_remove_points)

    # Apply tilt
    rotor.StructuralInformation.rotate_around_origin(np.array([0., 1., 0.]),
                                                     tilt)

    return rotor
Exemplo n.º 10
0
    def run(self):
        cout.cout_wrap('Running static coupled solver...', 1)

        for i_step in range(self.settings['n_load_steps'].value):
            # load step coefficient
            if not self.settings['n_load_steps'].value == 0:
                load_step_multiplier = (
                    i_step + 1.0) / self.settings['n_load_steps'].value
            else:
                load_step_multiplier = 1.0

            # new storage every load step
            if i_step > 0:
                self.increase_ts()

            for i_iter in range(self.settings['max_iter'].value):
                cout.cout_wrap('i_step: %u, i_iter: %u' % (i_step, i_iter))

                # run aero
                self.data = self.aero_solver.run()

                print('Beam last node: ')
                print(
                    self.data.structure.timestep_info[self.data.ts].pos[20, :])

                # map force
                struct_forces = mapping.aero2struct_force_mapping(
                    self.data.aero.timestep_info[self.data.ts].forces,
                    self.data.aero.struct2aero_mapping,
                    self.data.aero.timestep_info[self.data.ts].zeta,
                    self.data.structure.timestep_info[self.data.ts].pos,
                    self.data.structure.timestep_info[self.data.ts].psi,
                    self.data.structure.node_master_elem,
                    self.data.structure.master,
                    algebra.quat2rot(self.data.structure.timestep_info[
                        self.data.ts].quat).T)

                if not self.settings['relaxation_factor'].value == 0.:
                    if i_iter == 0:
                        self.previous_force = struct_forces.copy()

                    temp = struct_forces.copy()
                    struct_forces = (
                        (1.0 - self.settings['relaxation_factor'].value) *
                        struct_forces +
                        self.settings['relaxation_factor'].value *
                        self.previous_force)
                    self.previous_force = temp

                # copy force in beam
                temp1 = load_step_multiplier * struct_forces
                self.data.structure.timestep_info[
                    self.data.ts].steady_applied_forces = temp1.astype(
                        dtype=ct.c_double, order='F')

                # update gravity direction
                # TODO

                # run beam
                self.data = self.structural_solver.run()

                # update grid
                self.aero_solver.update_step()

                # convergence
                if self.convergence(i_iter, i_step):
                    break

        # for i_step in range(self.settings['n_load_steps']):
        #     coeff = ct.c_double((i_step + 1.0)/(self.settings['n_load_steps']))
        #     for i_iter in range(self.settings['max_iter']):
        #         cout.cout_wrap('Iter: %u, step: %u' % (i_iter, i_step), 2)
        #
        #         self.aero_solver.initialise(self.data, update_flightcon=False, quiet=True)
        #         self.data = self.aero_solver.run()
        #
        #         struct_forces = mapping.aero2struct_force_mapping(
        #             self.data.grid.timestep_info[self.ts].forces,
        #             self.data.grid.struct2aero_mapping,
        #             self.data.grid.timestep_info[self.ts].zeta,
        #             self.data.beam.timestep_info[self.ts].pos_def,
        #             self.data.beam.timestep_info[self.ts].psi_def,
        #             self.data.beam.node_master_elem,
        #             self.data.beam.master,
        #             self.data.grid.inertial2aero)
        #
        #         if self.relaxation_factor > 0.0:
        #             struct_forces = ((1.0 - self.relaxation_factor)*struct_forces +
        #                             self.relaxation_factor*self.previous_forces)
        #
        #         self.previous_forces = struct_forces
        #         self.data.beam.update_forces(struct_forces)
        #         self.structural_solver.initialise(self.data)
        #         self.data = self.structural_solver.run(coeff)
        #
        #         if self.convergence(i_iter):
        #             self.data.flightconditions['FlightCon']['u_inf'] = self.original_u_inf
        #             self.aero_solver.initialise(self.data, quiet=True)
        #             self.data = self.aero_solver.run()
        #             if i_step == self.settings['n_load_steps'] - 1:
        #                 cout.cout_wrap('Converged in %u iterations' % (i_iter + 1), 2)
        #             break

        cout.cout_wrap('...Finished', 1)
        return self.data
Exemplo n.º 11
0
    def run(self, online=False):

        # Use the following statement in case the ct types are not defined and
        # you need them on uvlm3d
        # self.data.aero.timestep_info[-1].generate_ctypes_pointers()

        if self.settings['format'] == 'h5':
            file_exists = os.path.isfile(self.filename)
            hdfile = h5py.File(self.filename, 'a')

            if (online and file_exists):
                self.save_timestep(self.data, self.settings, self.data.ts, hdfile)
            else:
                skip_attr_init = copy.deepcopy(self.settings['skip_attr'])
                skip_attr_init.append('timestep_info')

                h5utils.add_as_grp(self.data, hdfile, grpname='data',
                                   ClassesToSave=self.ClassesToSave, SkipAttr=skip_attr_init,
                                   compress_float=self.settings['compress_float'])

                if self.settings['save_struct']:
                    h5utils.add_as_grp(list(),
                               hdfile['data']['structure'],
                               grpname='timestep_info')
                if self.settings['save_aero']:
                    h5utils.add_as_grp(list(),
                               hdfile['data']['aero'],
                               grpname='timestep_info')

                for it in range(len(self.data.structure.timestep_info)):
                    tstep_p = self.data.structure.timestep_info[it]
                    if tstep_p is not None:
                        self.save_timestep(self.data, self.settings, it, hdfile)

            hdfile.close()

            if self.settings['save_linear_uvlm']:
                linhdffile = h5py.File(self.filename.replace('.data.h5', '.uvlmss.h5'), 'a')
                h5utils.add_as_grp(self.data.linear.linear_system.uvlm.ss, linhdffile, grpname='ss',
                                   ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'],
                                   compress_float=self.settings['compress_float'])
                h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linhdffile,
                                   grpname='linearisation_vectors',
                                   ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'],
                                   compress_float=self.settings['compress_float'])
                linhdffile.close()

            if self.settings['save_linear']:
                with h5py.File(self.filename_linear, 'a') as linfile:
                    h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linfile,
                                       grpname='linearisation_vectors',
                                       ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'],
                                       compress_float=self.settings['compress_float'])
                    h5utils.add_as_grp(self.data.linear.ss, linfile, grpname='ss',
                                       ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'],
                                       compress_float=self.settings['compress_float'])

            if self.settings['save_rom']:
                try:
                    for k, rom in self.data.linear.linear_system.uvlm.rom.items():
                        rom.save(self.filename.replace('.data.h5', '_{:s}.rom.h5'.format(k.lower())))
                except AttributeError:
                    cout.cout_wrap('Could not locate a reduced order model to save')

        elif self.settings['format'] == 'mat':
            from scipy.io import savemat
            if self.settings['save_linear']:
                # reference-forces
                linearisation_vectors = self.data.linear.linear_system.linearisation_vectors

                matfilename = self.filename.replace('.data.h5', '.linss.mat')
                A, B, C, D = self.data.linear.ss.get_mats()
                savedict = {'A': A,
                            'B': B,
                            'C': C,
                            'D': D}
                for k, v in linearisation_vectors.items():
                    savedict[k] = v
                try:
                    dt = self.data.linear.ss.dt
                    savedict['dt'] = dt
                except AttributeError:
                    pass
                savemat(matfilename, savedict)

            if self.settings['save_linear_uvlm']:
                matfilename = self.filename.replace('.data.h5', '.uvlmss.mat')
                linearisation_vectors = self.data.linear.linear_system.uvlm.linearisation_vectors
                A, B, C, D = self.data.linear.linear_system.uvlm.ss.get_mats()
                savedict = {'A': A,
                            'B': B,
                            'C': C,
                            'D': D}
                for k, v in linearisation_vectors.items():
                    savedict[k] = v
                try:
                    dt = self.data.linear.ss.dt
                    savedict['dt'] = dt
                except AttributeError:
                    pass
                savemat(matfilename, savedict)

        return self.data
Exemplo n.º 12
0
def generate_fem_file(route, case_name, num_elem, num_node_elem=3):

    global num_node
    num_node = (num_node_elem - 1) * num_elem + 1
    # import pdb; pdb.set_trace()
    angle = 0 * np.pi / 180.0
    x = (np.linspace(0, length, num_node)) * np.cos(angle)
    y = (np.linspace(0, length, num_node)) * np.sin(angle)
    z = np.zeros((num_node, ))

    structural_twist = np.zeros((num_elem, num_node_elem))

    frame_of_reference_delta = np.zeros((num_elem, num_node_elem, 3))
    for ielem in range(num_elem):
        for inode in range(num_node_elem):
            frame_of_reference_delta[ielem, inode, :] = [
                -np.sin(angle), np.cos(angle), 0
            ]

    scale = 1

    x *= scale
    y *= scale
    z *= scale

    conn = np.zeros((num_elem, num_node_elem), dtype=int)
    for ielem in range(num_elem):
        conn[ielem, :] = (np.ones(
            (3, )) * ielem * (num_node_elem - 1) + [0, 2, 1])

    # stiffness array
    # import pdb; pdb.set_trace()
    num_stiffness = 1

    ea = E * A
    # APPROXIMATION!!!
    cout.cout_wrap("Assuming isotropic material", 2)
    G = E / 2.0 / (1.0 + 0.3)
    cout.cout_wrap("Using total cross-section area as shear area", 2)
    ga = G * A
    cout.cout_wrap("Assuming planar cross-sections", 2)
    J = 2.0 * I
    gj = G * J

    base_stiffness = np.diag([ea, ga, ga, gj, ei, ei])
    stiffness = np.zeros((num_stiffness, 6, 6))
    # import pdb; pdb.set_trace()
    for i in range(num_stiffness):
        stiffness[i, :, :] = base_stiffness
    # element stiffness
    elem_stiffness = np.zeros((num_elem, ), dtype=int)

    # mass array
    num_mass = 1
    base_mass = m_bar * np.diag([1.0, 1.0, 1.0, J / A, I / A, I / A])
    # base_mass = m_bar*np.diag([1.0, 1.0, 1.0, 1.0,1.0,1.0])
    mass = np.zeros((num_mass, 6, 6))
    for i in range(num_mass):
        mass[i, :, :] = base_mass
    # element masses
    elem_mass = np.zeros((num_elem, ), dtype=int)

    # bocos
    boundary_conditions = np.zeros((num_node, 1), dtype=int)
    boundary_conditions[0] = 1
    boundary_conditions[-1] = -1

    # beam number
    beam_number = np.zeros((num_node, 1), dtype=int)

    # new app forces scheme (only follower)
    app_forces = np.zeros((num_node, 6))
    # app_forces[0, :] = [0, 0, 3000000, 0, 0, 0]

    # lumped masses input
    n_lumped_mass = 1
    lumped_mass_nodes = np.array([num_node - 1], dtype=int)
    lumped_mass = np.zeros((n_lumped_mass, ))
    lumped_mass[0] = 0.0
    lumped_mass_inertia = np.zeros((n_lumped_mass, 3, 3))
    lumped_mass_position = np.zeros((n_lumped_mass, 3))

    #n_lumped_mass = 1
    #lumped_mass_nodes = np.ones((num_node,), dtype=int)
    #lumped_mass = np.zeros((n_lumped_mass, ))
    #lumped_mass[0] = m_bar*length/num_elem/(num_node_elem-1)
    #lumped_mass_inertia[0,:,:] = np.diag([J, I, I])
    #lumped_mass_position = np.zeros((n_lumped_mass, 3))

    with h5.File(route + '/' + case_name + '.fem.h5', 'a') as h5file:

        # CHECKING
        if (elem_stiffness.shape[0] != num_elem):
            sys.exit(
                "ERROR: Element stiffness must be defined for each element")
        if (elem_mass.shape[0] != num_elem):
            sys.exit("ERROR: Element mass must be defined for each element")
        if (frame_of_reference_delta.shape[0] != num_elem):
            sys.exit(
                "ERROR: The first dimension of FoR does not match the number of elements"
            )
        if (frame_of_reference_delta.shape[1] != num_node_elem):
            sys.exit(
                "ERROR: The second dimension of FoR does not match the number of nodes element"
            )
        if (frame_of_reference_delta.shape[2] != 3):
            sys.exit("ERROR: The third dimension of FoR must be 3")
        if (structural_twist.shape[0] != num_node):
            sys.exit(
                "ERROR: The structural twist must be defined for each node")
        if (boundary_conditions.shape[0] != num_node):
            sys.exit(
                "ERROR: The boundary conditions must be defined for each node")
        if (beam_number.shape[0] != num_node):
            sys.exit("ERROR: The beam number must be defined for each node")
        if (app_forces.shape[0] != num_node):
            sys.exit(
                "ERROR: The first dimension of the applied forces matrix does not match the number of nodes"
            )
        if (app_forces.shape[1] != 6):
            sys.exit(
                "ERROR: The second dimension of the applied forces matrix must be 6"
            )

        coordinates = h5file.create_dataset('coordinates',
                                            data=np.column_stack((x, y, z)))
        conectivities = h5file.create_dataset('connectivities', data=conn)
        num_nodes_elem_handle = h5file.create_dataset('num_node_elem',
                                                      data=num_node_elem)
        num_nodes_handle = h5file.create_dataset('num_node', data=num_node)
        num_elem_handle = h5file.create_dataset('num_elem', data=num_elem)
        stiffness_db_handle = h5file.create_dataset('stiffness_db',
                                                    data=stiffness)
        stiffness_handle = h5file.create_dataset('elem_stiffness',
                                                 data=elem_stiffness)
        mass_db_handle = h5file.create_dataset('mass_db', data=mass)
        mass_handle = h5file.create_dataset('elem_mass', data=elem_mass)
        frame_of_reference_delta_handle = h5file.create_dataset(
            'frame_of_reference_delta', data=frame_of_reference_delta)
        structural_twist_handle = h5file.create_dataset('structural_twist',
                                                        data=structural_twist)
        bocos_handle = h5file.create_dataset('boundary_conditions',
                                             data=boundary_conditions)
        beam_handle = h5file.create_dataset('beam_number', data=beam_number)
        app_forces_handle = h5file.create_dataset('app_forces',
                                                  data=app_forces)
        lumped_mass_nodes_handle = h5file.create_dataset(
            'lumped_mass_nodes', data=lumped_mass_nodes)
        lumped_mass_handle = h5file.create_dataset('lumped_mass',
                                                   data=lumped_mass)
        lumped_mass_inertia_handle = h5file.create_dataset(
            'lumped_mass_inertia', data=lumped_mass_inertia)
        lumped_mass_position_handle = h5file.create_dataset(
            'lumped_mass_position', data=lumped_mass_position)
    return num_node, coordinates
Exemplo n.º 13
0
    def run(self):
        # previous_kstep = self.data.structure.timestep_info[-1].copy()
        # dynamic simulations start at tstep == 1, 0 is reserved for the initial state
        for self.data.ts in range(
                len(self.data.structure.timestep_info),
                self.settings['n_time_steps'].value +
                len(self.data.structure.timestep_info)):
            # aero_kstep = self.data.aero.timestep_info[-1].copy()
            structural_kstep = self.data.structure.timestep_info[-1].copy()

            # previous_kstep = self.data.structure.timestep_info[-1].copy()
            k = 0
            for k in range(self.settings['fsi_substeps'].value + 1):
                if k == self.settings[
                        'fsi_substeps'].value and not self.settings[
                            'fsi_substeps'] == 0:
                    cout.cout_wrap('The FSI solver did not converge!!!')
                    print('K step q:')
                    print(structural_kstep.q)
                    print('K step dq:')
                    print(structural_kstep.dqdt)
                    print('K step ddq:')
                    print(structural_kstep.dqddt)
                    a = 1
                    break

                # generate new grid (already rotated)
                aero_kstep = self.data.aero.timestep_info[-1].copy()
                self.aero_solver.update_custom_grid(structural_kstep,
                                                    aero_kstep)

                # run the solver
                self.data = self.aero_solver.run(aero_kstep,
                                                 structural_kstep,
                                                 convect_wake=True)

                previous_kstep = structural_kstep.copy()
                structural_kstep = self.data.structure.timestep_info[-1].copy()
                # map forces
                force_coeff = 0.0
                if self.settings['include_unsteady_force_contribution']:
                    force_coeff = -1.0
                self.map_forces(aero_kstep, structural_kstep, force_coeff)

                # relaxation
                relax_factor = self.relaxation_factor(k)
                relax(self.data.structure, structural_kstep, previous_kstep,
                      relax_factor)

                if k > 0.9 * self.settings['fsi_substeps'].value:
                    relax_factor = 0.3
                elif k > 0.8 * self.settings['fsi_substeps'].value:
                    relax_factor = 0.8

                # run structural solver
                self.data = self.structural_solver.run(
                    structural_step=structural_kstep)

                # check convergence
                if self.convergence(k, structural_kstep, previous_kstep):
                    break

            self.aero_solver.add_step()
            self.data.aero.timestep_info[-1] = aero_kstep.copy()

            self.structural_solver.add_step()
            self.data.structure.timestep_info[-1] = structural_kstep.copy()
            self.data.structure.integrate_position(-1,
                                                   self.settings['dt'].value)

            if self.print_info:
                self.residual_table.print_line([
                    self.data.ts, self.data.ts * self.dt.value, k,
                    np.log10(self.res),
                    np.log10(self.res_dqdt),
                    np.log10(self.res_dqddt), structural_kstep.for_vel[2]
                ])
            self.structural_solver.extract_resultants()
            # run postprocessors
            if self.with_postprocessors:
                for postproc in self.postprocessors:
                    self.data = self.postprocessors[postproc].run(online=True)

        if self.print_info:
            cout.cout_wrap('...Finished', 1)
        return self.data
Exemplo n.º 14
0
            'AerogridPlot': settings['AerogridPlot']
        }
    }

    settings['Modal'] = {
        'folder': route + '/output',
        'include_rbm': 'on',
        'NumLambda': 10000,
        'num_steps': 1,
        'print_matrices': 'on'
    }

    import configobj
    config = configobj.ConfigObj()
    config.filename = file_name
    for k, v in settings.items():
        config[k] = v
    config.write()


# run everything
clean_test_files()
generate_fem_file(route, case_name, num_elem, num_node_elem)
generate_aero_file()
generate_dyn_file()
generate_solver_file()

cout.cout_wrap(
    'Reference for validation: "Rotary wing structural dynamics and aeroelasticity", R.L. Bielawa. AIAA education series. Second edition',
    1)
Exemplo n.º 15
0
 def __init__(self, solver_name, n_iter=None, message=''):
     super().__init__(message)
     cout.cout_wrap("The solver " + solver_name + " did not converge in " + str(n_iter) + " iterations.", 3)
Exemplo n.º 16
0
 def __init__(self, solver_name, message=''):
     super().__init__(message)
     if cout.cout_wrap is None:
         print("The solver " + solver_name + " is not implemented. Check the list of available solvers when starting SHARPy")
     else:
         cout.cout_wrap("The solver " + solver_name + " is not implemented. Check the list of available solvers when starting SHARPy", 3)
Exemplo n.º 17
0
 def print_info(self):
     cout.cout_wrap('Trajectory information:', 2)
     cout.cout_wrap('\t-travel time: {}'.format(self.travel_time), 2)
     cout.cout_wrap('\t-curve length: {}'.format(self.curve_length), 2)
     cout.cout_wrap('-------------------------------', 2)
Exemplo n.º 18
0
    def stable_realisation(self, *args, **kwargs):
        r"""Remove unstable poles left after reduction

        Using a Schur decomposition of the reduced plant matrix :math:`\mathbf{A}_m\in\mathbb{C}^{m\times m}`,
        the method removes the unstable eigenvalues that could have appeared after the moment-matching reduction.

        The oblique projection matrices :math:`\mathbf{T}_L\in\mathbb{C}^{m \times p}` and
        :math:`\mathbf{T}_R\in\mathbb{C}^{m \times p}`` result in a stable realisation

        .. math:: \mathbf{A}_s = \mathbf{T}_L^\top\mathbf{AT}_R \in \mathbb{C}^{p\times p}.

        Args:
            A (np.ndarray): plant matrix (if not provided ``self.ssrom.A`` will be used).

        Returns:
            tuple: Left and right projection matrices :math:`\mathbf{T}_L\in\mathbb{C}^{m \times p}` and
                :math:`\mathbf{T}_R\in\mathbb{C}^{m \times p}`

        References:
            Jaimoukha, I. M., Kasenally, E. D.. Implicitly Restarted Krylov Subspace Methods for Stable Partial
            Realizations. SIAM Journal of Matrix Analysis and Applications, 1997.

        See Also:
            The method employs :func:`sharpy.rom.utils.krylovutils.schur_ordered()` and
            :func:`sharpy.rom.utils.krylovutils.remove_a12`.
        """

        cout.cout_wrap(
            'Stabilising system by removing unstable eigenvalues using a Schur decomposition',
            1)
        if self.ssrom is None:
            A = args[0]
            ct = kwargs['ct']
            assert type(ct) == bool, 'CT system flag should be a bool'
        else:
            A = self.ssrom.A

            if self.sstype == 'ct':
                ct = True
            else:
                ct = False

        m = A.shape[0]
        As, T1, n_stable = krylovutils.schur_ordered(A, ct=ct)

        # Remove the (1,2) block of the Schur ordered matrix
        T2, X = krylovutils.remove_a12(As, n_stable)

        T3 = np.eye(m, n_stable)

        TL = T3.T.dot(T2.dot(np.conj(T1)))
        TR = T1.T.dot(np.linalg.inv(T2).dot(T3))

        cout.cout_wrap('System reduced to %g states' % n_stable, 1)

        # for debugging
        # import matplotlib.pyplot as plt
        Ar = TL.dot(A.dot(TR))
        eigsA = np.linalg.eigvals(A)
        eigsAr = np.linalg.eigvals(Ar)
        #
        # plt.scatter(eigsA.real, eigsA.imag)
        # plt.scatter(eigsAr.real, eigsAr.imag)
        # plt.show()
        if ct:
            assert np.sum(
                np.real(eigsAr) <= 0.
            ) == n_stable, 'Number of stable eigvals computed not equal to those in Ar'
        else:
            assert np.sum(
                np.abs(eigsAr) <= 1.
            ) == n_stable, 'Number of stable eigvals computed not equal to those in Ar'

        return TL.T, TR
Exemplo n.º 19
0
    def convergence(self, i_iter, i_step):
        if i_iter == self.settings['max_iter'].value - 1:
            cout.cout_wrap('StaticCoupled did not converge!', 0)
            # quit(-1)

        return_value = None
        if i_iter == 0:
            self.initial_residual = np.linalg.norm(
                self.data.structure.timestep_info[self.data.ts].pos)
            self.previous_residual = self.initial_residual
            self.current_residual = self.initial_residual
            if self.print_info:
                forces = self.data.structure.timestep_info[
                    self.data.ts].total_forces
                self.residual_table.print_line([
                    i_iter,
                    i_step,
                    0.0,
                    forces[0],
                    forces[1],
                    forces[2],
                    forces[3],
                    forces[4],
                    forces[5],
                ])
            return False

        self.current_residual = np.linalg.norm(
            self.data.structure.timestep_info[self.data.ts].pos)
        if self.print_info:
            forces = self.data.structure.timestep_info[
                self.data.ts].total_forces
            res_print = np.NINF
            if (np.abs(self.current_residual - self.previous_residual) >
                    sys.float_info.epsilon * 10):
                res_print = np.log10(
                    np.abs(self.current_residual - self.previous_residual) /
                    self.initial_residual)

            self.residual_table.print_line([
                i_iter,
                i_step,
                res_print,
                forces[0],
                forces[1],
                forces[2],
                forces[3],
                forces[4],
                forces[5],
            ])

        if return_value is None:
            if np.abs(
                    self.current_residual - self.previous_residual
            ) / self.initial_residual < self.settings['tolerance'].value:
                return_value = True
            else:
                self.previous_residual = self.current_residual
                return_value = False

        if return_value is None:
            return_value = False

        return return_value
Exemplo n.º 20
0
    def run(self):

        n_steps = self.settings['n_tsteps'].value
        x0 = self.input_data_dict['x0']
        u = self.input_data_dict['u']

        ss = self.data.linear.ss

        if len(x0) != ss.states:
            warnings.warn(
                'Number of states in the initial state vector not equal to the number of states'
            )
            x0 = np.zeros(ss.states)

        if u.shape[1] != ss.inputs:
            warnings.warn(
                'Dimensions of the input vector not equal to the number of inputs'
            )
            cout.cout_wrap('Number of inputs: %g' % ss.inputs, 3)
            cout.cout_wrap('Number of timesteps: %g' % n_steps, 3)
            cout.cout_wrap(
                'Number of UVLM inputs: %g' %
                self.data.linear.linear_system.uvlm.ss.inputs, 3)
            cout.cout_wrap(
                'Number of beam inputs: %g' %
                self.data.linear.linear_system.beam.ss.inputs, 3)
            breakpoint()

        try:
            dt = ss.dt
        except AttributeError:
            dt = self.settings['dt'].value

        # Total time to run
        T = n_steps * dt

        u_ref = self.settings['reference_velocity'].value
        # If the system is scaled:
        if u_ref != 1.:
            scaling_factors = self.data.linear.linear_system.uvlm.sys.ScalingFacts
            dt_dimensional = scaling_factors['length'] / u_ref
            T_dimensional = n_steps * dt_dimensional
            T = T_dimensional / scaling_factors['time']
            ss = self.data.linear.linear_system.update(
                self.settings['reference_velocity'].value)
        t_dom = np.linspace(0, T, n_steps)

        # Use the scipy linear solver
        sys = libss.ss_to_scipy(ss)
        cout.cout_wrap('Solving linear system using scipy...')
        t0 = time.time()
        # breakpoint()
        out = sys.output(u, t=t_dom, x0=x0)
        ts = time.time() - t0
        cout.cout_wrap('\tSolved in %.2fs' % ts, 1)

        t_out = out[0]
        x_out = out[2]
        y_out = out[1]

        if self.settings['write_dat']:
            cout.cout_wrap(
                'Writing linear simulation output .dat files to %s' %
                self.folder)
            if 'y' in self.settings['write_dat']:
                np.savetxt(self.folder + '/y_out.dat', y_out)
                cout.cout_wrap('Output vector written', 2)
            if 'x' in self.settings['write_dat']:
                np.savetxt(self.folder + '/x_out.dat', x_out)
                cout.cout_wrap('State vector written', 2)
            if 'u' in self.settings['write_dat']:
                np.savetxt(self.folder + '/u_out.dat', u)
                cout.cout_wrap('Input vector written', 2)
            if 't' in self.settings['write_dat']:
                np.savetxt(self.folder + '/t_out.dat', t_out)
                cout.cout_wrap('Time domain written', 2)
            cout.cout_wrap('Success', 1)

        # Pack state variables into linear timestep info
        cout.cout_wrap('Plotting results...')
        for n in range(len(t_out) - 1):
            tstep = LinearTimeStepInfo()
            tstep.x = x_out[n, :]
            tstep.y = y_out[n, :]
            tstep.t = t_out[n]
            tstep.u = u[n, :]
            self.data.linear.timestep_info.append(tstep)
            # TODO: option to save to h5

            # Pack variables into respective aero or structural time step infos (with the + f0 from lin)
            # Need to obtain information from the variables in a similar fashion as done with the database
            # for the beam case

            aero_tstep, struct_tstep = state_to_timestep(
                self.data, tstep.x, tstep.u, tstep.y)

            self.data.aero.timestep_info.append(aero_tstep)
            self.data.structure.timestep_info.append(struct_tstep)

            # run postprocessors
            if self.with_postprocessors:
                for postproc in self.postprocessors:
                    self.data = self.postprocessors[postproc].run(online=True)

        return self.data
Exemplo n.º 21
0
    def initialise(self, data, custom_settings=None, caller=None):
        self.data = data
        if custom_settings is None:
            self.settings = data.settings[self.solver_id]
        else:
            self.settings = custom_settings

        settings.to_custom_types(self.settings,
                                 self.settings_types, self.settings_default, options=self.settings_options)
        
        # Add these anyway - therefore if you add your own skip_attr you don't have to retype all of these
        self.settings['skip_attr'].extend(['fortran',
                                            'airfoils',
                                            'airfoil_db',
                                            'settings_types',
                                            'ct_dynamic_forces_list',
                                            'ct_forces_list',
                                            'ct_gamma_dot_list',
                                            'ct_gamma_list',
                                            'ct_gamma_star_list',
                                            'ct_normals_list',
                                            'ct_u_ext_list',
                                            'ct_u_ext_star_list',
                                            'ct_zeta_dot_list',
                                            'ct_zeta_list',
                                            'ct_zeta_star_list',
                                            'dynamic_input'])

        self.ts_max = self.data.ts + 1

        # create folder for containing files if necessary
        if not os.path.exists(self.settings['folder']):
            os.makedirs(self.settings['folder'])
        self.folder = self.settings['folder'] + '/' + self.data.settings['SHARPy']['case'] + '/savedata/'
        if not os.path.exists(self.folder):
            os.makedirs(self.folder)
        self.filename = self.folder + self.data.settings['SHARPy']['case'] + '.data.h5'
        self.filename_linear = self.folder + self.data.settings['SHARPy']['case'] + '.linss.h5'

        if os.path.isfile(self.filename):
            os.remove(self.filename)

        # check that there is a linear system - else return setting to false
        if self.settings['save_linear'] or self.settings['save_linear_uvlm']:
            try:
                self.data.linear
            except AttributeError:
                cout.cout_wrap('SaveData variables ``save_linear`` and/or ``save_linear`` are True but no linear system'
                               'been found', 3)
                self.settings['save_linear'] = False
                self.settings['save_linear_uvlm'] = False

        if self.settings['format'] == 'h5':

            # allocate list of classes to be saved
            if self.settings['save_aero']:
                self.ClassesToSave += (sharpy.aero.models.aerogrid.Aerogrid,
                                       sharpy.utils.datastructures.AeroTimeStepInfo,)

            if self.settings['save_struct']:
                self.ClassesToSave += (
                    sharpy.structure.models.beam.Beam,
                    sharpy.utils.datastructures.StructTimeStepInfo,)

            if self.settings['save_linear']:
                self.ClassesToSave += (sharpy.solvers.linearassembler.Linear,
                                       sharpy.linear.assembler.linearaeroelastic.LinearAeroelastic,
                                       sharpy.linear.assembler.linearbeam.LinearBeam,
                                       sharpy.linear.assembler.linearuvlm.LinearUVLM,
                                       sharpy.linear.src.libss.ss,
                                       sharpy.linear.src.lingebm.FlexDynamic,)

            if self.settings['save_linear_uvlm']:
                self.ClassesToSave += (sharpy.solvers.linearassembler.Linear, sharpy.linear.src.libss.ss)
        self.caller = caller
Exemplo n.º 22
0
def main(args=None, sharpy_input_dict=None):
    """
    Main ``SHARPy`` routine

    This is the main ``SHARPy`` routine.
    It starts the solution process by reading the settings that are
    included in the ``.sharpy`` file that is parsed
    as an argument, or an equivalent dictionary given as ``sharpy_input_dict``.
    It reads the solvers specific settings and runs them in order

    Args:
        args (str): ``.sharpy`` file with the problem information and settings
        sharpy_input_dict (dict): ``dict`` with the same contents as the
            ``solver.txt`` file would have.

    Returns:
        sharpy.presharpy.presharpy.PreSharpy: object containing the simulation results.

    """
    import time
    import argparse

    import sharpy.utils.input_arg as input_arg
    import sharpy.utils.solver_interface as solver_interface
    from sharpy.presharpy.presharpy import PreSharpy
    from sharpy.utils.cout_utils import start_writer, finish_writer
    import logging
    import os

    import h5py
    import sharpy.utils.h5utils as h5utils

    # Loading solvers and postprocessors
    import sharpy.solvers
    import sharpy.postproc
    import sharpy.generators
    import sharpy.controllers
    # ------------

    try:
        # output writer
        start_writer()
        # timing
        t = time.process_time()
        t0_wall = time.perf_counter()

        if sharpy_input_dict is None:
            parser = argparse.ArgumentParser(prog='SHARPy', description=
            """This is the executable for Simulation of High Aspect Ratio Planes.\n
            Imperial College London 2020""")
            parser.add_argument('input_filename', help='path to the *.sharpy input file', type=str, default='')
            parser.add_argument('-r', '--restart', help='restart the solution with a given snapshot', type=str,
                                default=None)
            parser.add_argument('-d', '--docs', help='generates the solver documentation in the specified location. '
                                                     'Code does not execute if running this flag', action='store_true')
            if args is not None:
                args = parser.parse_args(args[1:])
            else:
                args = parser.parse_args()

        if args.docs:
            import subprocess
            import sharpy.utils.docutils as docutils
            import sharpy.utils.sharpydir as sharpydir
            docutils.generate_documentation()

            # run make
            cout.cout_wrap('Running make html in sharpy/docs')
            subprocess.Popen(['make', 'html'],
                             stdout=None,
                             cwd=sharpydir.SharpyDir + '/docs')

            return 0

        if args.input_filename == '':
            parser.error('input_filename is a required argument of SHARPy.')
        settings = input_arg.read_settings(args)
        if args.restart is None:
            # run preSHARPy
            data = PreSharpy(settings)
        else:
            try:
                with open(args.restart, 'rb') as restart_file:
                    data = pickle.load(restart_file)
            except FileNotFoundError:
                raise FileNotFoundError('The file specified for the snapshot \
                    restart (-r) does not exist. Please check.')


            # update the settings
            data.update_settings(settings)

            # Read again the dyn.h5 file
            data.structure.dynamic_input = []
            dyn_file_name = data.case_route + '/' + data.case_name + '.dyn.h5'
            if os.path.isfile(dyn_file_name):
                fid = h5py.File(dyn_file_name, 'r')
                data.structure.dyn_dict = h5utils.load_h5_in_dict(fid)
            # for it in range(self.num_steps):
            #     data.structure.dynamic_input.append(dict())

        # Loop for the solvers specified in *.sharpy['SHARPy']['flow']
        for solver_name in settings['SHARPy']['flow']:
            solver = solver_interface.initialise_solver(solver_name)
            solver.initialise(data)
            data = solver.run()

        cpu_time = time.process_time() - t
        wall_time = time.perf_counter() - t0_wall
        cout.cout_wrap('FINISHED - Elapsed time = %f6 seconds' % wall_time, 2)
        cout.cout_wrap('FINISHED - CPU process time = %f6 seconds' % cpu_time, 2)
        finish_writer()

    except Exception as e:
        try:
            logdir = settings['SHARPy']['log_folder']
        except KeyError:
            logdir = './'
        except NameError:
            logdir = './'
        logdir = os.path.abspath(logdir)
        cout.cout_wrap(('Exception raised, writing error log in %s/error.log' % logdir), 4)
        logging.basicConfig(filename='%s/error.log' % logdir,
                            filemode='w',
                            format='%(asctime)s-%(levelname)s-%(message)s',
                            datefmt='%d-%b-%y %H:%M:%S',
                            level=logging.INFO)
        logging.info('SHARPy Error Log')
        logging.error("Exception occurred", exc_info=True)
        raise e

    return data
Exemplo n.º 23
0
    def assemble(self):
        r"""
        Assembly of the linearised aeroelastic system.

        The UVLM state-space system has already been assembled. Prior to assembling the beam's first order state-space,
        the damping and stiffness matrices have to be modified to include the damping and stiffenning terms that arise
        from the linearisation of the aeordynamic forces with respect to the A frame of reference. See
        :func:`sharpy.linear.src.lin_aeroela.get_gebm2uvlm_gains()` for details on the linearisation.

        Then the beam is assembled as per the given settings in normalised time if the aerodynamic system has been
        scaled. The discrete time systems of the UVLM and the beam must have the same time step.

        The UVLM inputs and outputs are then projected onto the structural degrees of freedom (obviously with the
        exception of external gusts and control surfaces). Hence, the gains :math:`\mathbf{K}_{sa}` and
        :math:`\mathbf{K}_{as}` are added to the output and input of the UVLM system, respectively. These gains perform
        the following relation:

        .. math:: \begin{bmatrix}\zeta \\ \zeta' \\ u_g \\ \delta \end{bmatrix} = \mathbf{K}_{as}
            \begin{bmatrix} \eta \\ \eta' \\ u_g \\ \delta \end{bmatrix} =

        .. math:: \mathbf{N}_{nodes} = \mathbf{K}_{sa} \mathbf{f}_{vertices}

        If the beam is expressed in modal form, the UVLM is further projected onto the beam's modes to have the
        following input/output structure:


        Returns:

        """
        uvlm = self.uvlm
        beam = self.beam

        # Linearisation of the aerodynamic forces introduces stiffenning and damping terms into the beam matrices
        flex_nodes = self.beam.sys.num_dof_flex

        stiff_aero = np.zeros_like(beam.sys.Kstr)
        damping_aero = np.zeros_like(beam.sys.Cstr)
        stiff_aero[:flex_nodes, :flex_nodes] = self.Kss

        rigid_dof = beam.sys.Kstr.shape[0] - flex_nodes
        total_dof = flex_nodes + rigid_dof

        if rigid_dof > 0:
            rigid_dof = beam.sys.Kstr.shape[0] - self.Kss.shape[0]
            stiff_aero[flex_nodes:, :flex_nodes] = self.Krs

            damping_aero[:flex_nodes, flex_nodes:] = self.Csr
            damping_aero[flex_nodes:, flex_nodes:] = self.Crr
            damping_aero[flex_nodes:, :flex_nodes] = self.Crs

        beam.sys.Cstr += damping_aero
        beam.sys.Kstr += stiff_aero

        if uvlm.scaled:
            beam.assemble(t_ref=uvlm.sys.ScalingFacts['time'])
        else:
            beam.assemble()

        if not self.load_uvlm_from_file:
            # Projecting the UVLM inputs and outputs onto the structural degrees of freedom
            Ksa = self.Kforces[:beam.sys.
                               num_dof, :]  # maps aerodynamic grid forces to nodal forces

            # Map the nodal displacement and velocities onto the grid displacements and velocities
            Kas = np.zeros((uvlm.ss.inputs, 2 * beam.sys.num_dof +
                            (uvlm.ss.inputs - 2 * self.Kdisp.shape[0])))
            Kas[:2*self.Kdisp.shape[0], :2*beam.sys.num_dof] = \
                np.block([[self.Kdisp[:, :beam.sys.num_dof], self.Kdisp_vel[:, :beam.sys.num_dof]],
                [self.Kvel_disp[:, :beam.sys.num_dof], self.Kvel_vel[:, :beam.sys.num_dof]]])

            # Retain other inputs
            Kas[2 * self.Kdisp.shape[0]:,
                2 * beam.sys.num_dof:] = np.eye(uvlm.ss.inputs -
                                                2 * self.Kdisp.shape[0])

            # Scaling
            if uvlm.scaled:
                Kas /= uvlm.sys.ScalingFacts['length']

            uvlm.ss.addGain(Ksa, where='out')
            uvlm.ss.addGain(Kas, where='in')

            self.couplings['Ksa'] = Ksa
            self.couplings['Kas'] = Kas

            if self.settings['beam_settings']['modal_projection'] == True and \
                    self.settings['beam_settings']['inout_coords'] == 'modes':
                # Project UVLM onto modal space
                phi = beam.sys.U
                in_mode_matrix = np.zeros(
                    (uvlm.ss.inputs, beam.ss.outputs +
                     (uvlm.ss.inputs - 2 * beam.sys.num_dof)))
                in_mode_matrix[:2 * beam.sys.num_dof, :2 *
                               beam.sys.num_modes] = sclalg.block_diag(
                                   phi, phi)
                in_mode_matrix[2 * beam.sys.num_dof:, 2 *
                               beam.sys.num_modes:] = np.eye(uvlm.ss.inputs -
                                                             2 *
                                                             beam.sys.num_dof)
                out_mode_matrix = phi.T

                uvlm.ss.addGain(in_mode_matrix, where='in')
                uvlm.ss.addGain(out_mode_matrix, where='out')

            # Reduce uvlm projected onto structural coordinates
            if uvlm.rom:
                if rigid_dof != 0:
                    self.runrom_rbm(uvlm)
                else:
                    for k, rom in uvlm.rom.items():
                        uvlm.ss = rom.run(uvlm.ss)

        else:
            uvlm.ss = self.load_uvlm(self.settings['uvlm_filename'])

        # Coupling matrices
        Tas = np.eye(uvlm.ss.inputs, beam.ss.outputs)
        Tsa = np.eye(beam.ss.inputs, uvlm.ss.outputs)

        # Scale coupling matrices
        if uvlm.scaled:
            Tsa *= uvlm.sys.ScalingFacts['force'] * uvlm.sys.ScalingFacts[
                'time']**2
            if rigid_dof > 0:
                warnings.warn(
                    'Time scaling for problems with rigid body motion under development.'
                )
                Tas[:flex_nodes + 6, :flex_nodes +
                    6] /= uvlm.sys.ScalingFacts['length']
                Tas[total_dof:total_dof + flex_nodes +
                    6] /= uvlm.sys.ScalingFacts['length']
            else:
                if not self.settings['beam_settings']['modal_projection']:
                    Tas /= uvlm.sys.ScalingFacts['length']

        ss = libss.couple(ss01=uvlm.ss, ss02=beam.ss, K12=Tas, K21=Tsa)
        # Conditioning of A matrix
        # cond_a = np.linalg.cond(ss.A)
        # if type(uvlm.ss.A) != np.ndarray:
        #     cond_a_uvlm = np.linalg.cond(uvlm.ss.A.todense())
        # else:
        #     cond_a_uvlm = np.linalg.cond(uvlm.ss.A)
        # cond_a_beam = np.linalg.cond(beam.ss.A)
        # cout.cout_wrap('Matrix A condition = %e' % cond_a)
        # cout.cout_wrap('Matrix A_uvlm condition = %e' % cond_a_uvlm)
        # cout.cout_wrap('Matrix A_beam condition = %e' % cond_a_beam)

        self.couplings['Tas'] = Tas
        self.couplings['Tsa'] = Tsa
        self.state_variables = {'aero': uvlm.ss.states, 'beam': beam.ss.states}

        # Save zero force reference
        self.linearisation_vectors['forces_aero_beam_dof'] = Ksa.dot(
            self.linearisation_vectors['forces_aero'])

        if self.settings['beam_settings']['modal_projection'] == True and \
                self.settings['beam_settings']['inout_coords'] == 'modes':
            self.linearisation_vectors[
                'forces_aero_beam_dof'] = out_mode_matrix.dot(
                    self.linearisation_vectors['forces_aero_beam_dof'])

        cout.cout_wrap('Aeroelastic system assembled:')
        cout.cout_wrap('\tAerodynamic states: %g' % uvlm.ss.states, 1)
        cout.cout_wrap('\tStructural states: %g' % beam.ss.states, 1)
        cout.cout_wrap('\tTotal states: %g' % ss.states, 1)
        cout.cout_wrap('\tInputs: %g' % ss.inputs, 1)
        cout.cout_wrap('\tOutputs: %g' % ss.outputs, 1)
        return ss
Exemplo n.º 24
0
def xbeam_solv_couplednlndyn(beam, settings):
    n_elem = ct.c_int(beam.num_elem)
    n_nodes = ct.c_int(beam.num_node)
    n_mass = ct.c_int(beam.n_mass)
    n_stiff = ct.c_int(beam.n_stiff)

    dt = settings['dt'].value
    n_tsteps = settings['num_steps'].value
    time = np.zeros((n_tsteps,), dtype=ct.c_double, order='F')
    for i in range(n_tsteps):
        time[i] = i*dt

    # deformation history matrices
    for_vel = np.zeros((n_tsteps + 1, 6), order='F')
    for_acc = np.zeros((n_tsteps + 1, 6), order='F')
    quat_history = np.zeros((n_tsteps, 4), order='F')

    dt = ct.c_double(dt)
    n_tsteps = ct.c_int(n_tsteps)

    xbopts = Xbopts()
    xbopts.PrintInfo = ct.c_bool(settings['print_info'])
    xbopts.Solution = ct.c_int(910)
    xbopts.OutInaframe = ct.c_bool(False)
    xbopts.MaxIterations = settings['max_iterations']
    xbopts.NumLoadSteps = settings['num_load_steps']
    # xbopts.NumGauss = ct.c_int(0)
    xbopts.DeltaCurved = settings['delta_curved']
    xbopts.MinDelta = settings['min_delta']
    xbopts.NewmarkDamp = settings['newmark_damp']
    xbopts.gravity_on = settings['gravity_on']
    xbopts.gravity = settings['gravity']
    xbopts.gravity_dir_x = ct.c_double(settings['gravity_dir'][0])
    xbopts.gravity_dir_y = ct.c_double(settings['gravity_dir'][1])
    xbopts.gravity_dir_z = ct.c_double(settings['gravity_dir'][2])

    pos_def_history = np.zeros((n_tsteps.value, beam.num_node, 3), order='F', dtype=ct.c_double)
    pos_dot_def_history = np.zeros((n_tsteps.value, beam.num_node, 3), order='F', dtype=ct.c_double)
    psi_def_history = np.zeros((n_tsteps.value, beam.num_elem, 3, 3), order='F', dtype=ct.c_double)
    psi_dot_def_history = np.zeros((n_tsteps.value, beam.num_elem, 3, 3), order='F', dtype=ct.c_double)

    dynamic_force = np.zeros((n_nodes.value, 6, n_tsteps.value), dtype=ct.c_double, order='F')
    for it in range(n_tsteps.value):
        dynamic_force[:, :, it] = beam.dynamic_input[it]['dynamic_forces']

    # status flag
    success = ct.c_bool(True)

    # here we only need to set the flags at True, all the forces are follower
    xbopts.FollowerForce = ct.c_bool(True)
    xbopts.FollowerForceRig = ct.c_bool(True)
    import time as ti
    start_time = ti.time()
    f_xbeam_solv_couplednlndyn(ct.byref(n_elem),
                               ct.byref(n_nodes),
                               ct.byref(n_tsteps),
                               time.ctypes.data_as(doubleP),
                               beam.fortran['num_nodes'].ctypes.data_as(intP),
                               beam.fortran['num_mem'].ctypes.data_as(intP),
                               beam.fortran['connectivities'].ctypes.data_as(intP),
                               beam.fortran['master'].ctypes.data_as(intP),
                               ct.byref(n_mass),
                               beam.fortran['mass'].ctypes.data_as(doubleP),
                               beam.fortran['mass_indices'].ctypes.data_as(intP),
                               ct.byref(n_stiff),
                               beam.fortran['stiffness'].ctypes.data_as(doubleP),
                               beam.fortran['inv_stiffness'].ctypes.data_as(doubleP),
                               beam.fortran['stiffness_indices'].ctypes.data_as(intP),
                               beam.fortran['frame_of_reference_delta'].ctypes.data_as(doubleP),
                               beam.fortran['rbmass'].ctypes.data_as(doubleP),
                               beam.fortran['node_master_elem'].ctypes.data_as(intP),
                               beam.fortran['vdof'].ctypes.data_as(intP),
                               beam.fortran['fdof'].ctypes.data_as(intP),
                               ct.byref(xbopts),
                               beam.ini_info.pos.ctypes.data_as(doubleP),
                               beam.ini_info.psi.ctypes.data_as(doubleP),
                               beam.timestep_info[0].steady_applied_forces.ctypes.data_as(doubleP),
                               dynamic_force.ctypes.data_as(doubleP),
                               for_vel.ctypes.data_as(doubleP),
                               for_acc.ctypes.data_as(doubleP),
                               pos_def_history.ctypes.data_as(doubleP),
                               psi_def_history.ctypes.data_as(doubleP),
                               pos_dot_def_history.ctypes.data_as(doubleP),
                               psi_dot_def_history.ctypes.data_as(doubleP),
                               quat_history.ctypes.data_as(doubleP),
                               ct.byref(success))
    cout.cout_wrap("\n--- %s seconds ---" % (ti.time() - start_time), 1)
    if not success:
        raise Exception('couplednlndyn did not converge')

    for_pos = np.zeros_like(for_vel)
    for_pos[:, 0] = sc.integrate.cumtrapz(for_vel[:, 0], dx=dt.value, initial=0)
    for_pos[:, 1] = sc.integrate.cumtrapz(for_vel[:, 1], dx=dt.value, initial=0)
    for_pos[:, 2] = sc.integrate.cumtrapz(for_vel[:, 2], dx=dt.value, initial=0)

    glob_pos_def = np.zeros_like(pos_def_history)
    for it in range(n_tsteps.value):
        rot = algebra.quat2rot(quat_history[it, :]).transpose()
        for inode in range(beam.num_node):
            glob_pos_def[it, inode, :] = np.dot(rot.T, pos_def_history[it, inode, :])

    for i in range(n_tsteps.value - 1):
        beam.timestep_info[i + 1].pos[:] = pos_def_history[i+1, :]
        beam.timestep_info[i + 1].psi[:] = psi_def_history[i+1, :]
        beam.timestep_info[i + 1].pos_dot[:] = pos_dot_def_history[i+1, :]
        beam.timestep_info[i + 1].psi_dot[:] = psi_dot_def_history[i+1, :]

        beam.timestep_info[i + 1].quat[:] = quat_history[i+1, :]
        beam.timestep_info[i + 1].for_pos[:] = for_pos[i+1, :]
        beam.timestep_info[i + 1].for_vel[:] = for_vel[i+1, :]
Exemplo n.º 25
0
def rotor_from_excel_type02(
        chord_panels,
        rotation_velocity,
        pitch_deg,
        excel_file_name='database_excel_type02.xlsx',
        excel_sheet_parameters='parameters',
        excel_sheet_structural_blade='structural_blade',
        excel_sheet_discretization_blade='discretization_blade',
        excel_sheet_aero_blade='aero_blade',
        excel_sheet_airfoil_info='airfoil_info',
        excel_sheet_airfoil_coord='airfoil_coord',
        excel_sheet_structural_tower='structural_tower',
        m_distribution='uniform',
        h5_cross_sec_prop=None,
        n_points_camber=100,
        tol_remove_points=1e-3,
        user_defined_m_distribution_type=None,
        camber_effect_on_twist=False,
        wsp=0.,
        dt=0.):

    # Warning for back compatibility
    cout.cout_wrap(
        'rotor_from_excel_type02 is obsolete! rotor_from_excel_type03 instead!',
        3)

    # Assign values to dictionaries
    op_params = {}
    op_params['rotation_velocity'] = rotation_velocity
    op_params['pitch_deg'] = pitch_deg
    op_params['wsp'] = wsp
    op_params['dt'] = dt

    geom_params = {}
    geom_params['chord_panels'] = chord_panels
    geom_params['tol_remove_points'] = tol_remove_points
    geom_params['n_points_camber'] = n_points_camber
    geom_params['h5_cross_sec_prop'] = h5_cross_sec_prop
    geom_params['m_distribution'] = m_distribution

    options = {}
    options['camber_effect_on_twist'] = camber_effect_on_twist
    options[
        'user_defined_m_distribution_type'] = user_defined_m_distribution_type
    options['include_polars'] = False

    excel_description = {}
    excel_description['excel_file_name'] = excel_file_name
    excel_description['excel_sheet_parameters'] = excel_sheet_parameters
    excel_description[
        'excel_sheet_structural_blade'] = excel_sheet_structural_blade
    excel_description[
        'excel_sheet_discretization_blade'] = excel_sheet_discretization_blade
    excel_description['excel_sheet_aero_blade'] = excel_sheet_aero_blade
    excel_description['excel_sheet_airfoil_info'] = excel_sheet_airfoil_info
    excel_description['excel_sheet_airfoil_chord'] = excel_sheet_airfoil_coord

    rotor = rotor_from_excel_type03(op_params, geom_params, excel_description,
                                    options)

    return rotor
    def run(self, structural_step=None, dt=None):
        if structural_step is None:
            structural_step = self.data.structure.timestep_info[-1]

        if structural_step.mb_dict is not None:
            MBdict = structural_step.mb_dict
        else:
            MBdict = self.data.structure.ini_mb_dict

        if dt is None:
            dt = ct.c_float(0.)

        self.lc_list = lagrangeconstraints.initialize_constraints(MBdict)
        self.num_LM_eq = lagrangeconstraints.define_num_LM_eq(self.lc_list)

        # TODO: only working for constant forces
        MB_beam, MB_tstep = mb.split_multibody(self.data.structure,
                                               structural_step, MBdict, 0)
        q = np.zeros((self.sys_size + self.num_LM_eq, ),
                     dtype=ct.c_double,
                     order='F')
        dqdt = np.zeros((self.sys_size + self.num_LM_eq, ),
                        dtype=ct.c_double,
                        order='F')
        dqddt = np.zeros((self.sys_size + self.num_LM_eq, ),
                         dtype=ct.c_double,
                         order='F')
        mb.disp2state(MB_beam, MB_tstep, q, dqdt, dqddt)
        # Lagrange multipliers parameters
        num_LM_eq = self.num_LM_eq
        Lambda = np.zeros((num_LM_eq, ), dtype=ct.c_double, order='F')
        # Lambda_dot = np.zeros((num_LM_eq,), dtype=ct.c_double, order='F')
        Dq_old = 0.
        Dq = np.zeros((self.sys_size, ))

        for iLoadStep in range(0, self.settings['num_load_steps'].value + 1):
            iter = -1
            # delta = settings.min_delta + 1.
            converged = False
            while converged == False:
                iter += 1
                if (iter == self.settings['max_iterations'].value - 1):
                    cout.cout_wrap(("Residual is: %f" % np.amax(np.abs(Dq))),
                                   4)
                    cout.cout_wrap("Static equations did not converge", 4)
                    break

                MB_K, MB_Q = self.assembly_MB_eq_system(
                    MB_beam, MB_tstep, Lambda, MBdict, iLoadStep)
                Dq = np.linalg.solve(MB_K, -MB_Q)

                # Dq *= 0.7
                q += Dq
                mb.state2disp(q, dqdt, dqddt, MB_beam, MB_tstep)

                if (iter > 0):
                    if (np.amax(np.abs(Dq)) < Dq_old):
                        converged = True

                if iter == 0:
                    Dq_old = np.amax(np.array([1., np.amax(
                        np.abs(Dq))])) * self.settings['min_delta'].value

                lagrangeconstraints.postprocess(self.lc_list, MB_beam,
                                                MB_tstep, "static")

        self.compute_forces_constraints(MB_beam, MB_tstep, Lambda)
        if self.settings['gravity_on']:
            for ibody in range(len(MB_beam)):
                xbeamlib.cbeam3_correct_gravity_forces(MB_beam[ibody],
                                                       MB_tstep[ibody],
                                                       self.settings)
        mb.merge_multibody(MB_tstep, MB_beam, self.data.structure,
                           structural_step, MBdict, 0.)

        # # Initialize
        # q = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F')
        # dqdt = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F')
        # dqddt = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F')
        #
        # # Predictor step
        # mb.disp2state(MB_beam, MB_tstep, q, dqdt, dqddt)
        #
        # q += dt*dqdt + (0.5 - self.beta)*dt*dt*dqddt
        # dqdt += (1.0 - self.gamma)*dt*dqddt
        # dqddt = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F')
        # if not num_LM_eq == 0:
        #     Lambda = q[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F')
        #     Lambda_dot = dqdt[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F')
        # else:
        #     Lambda = 0
        #     Lambda_dot = 0
        #
        # # Newmark-beta iterations
        # old_Dq = 1.0
        # LM_old_Dq = 1.0
        #
        # converged = False
        # for iter in range(self.settings['max_iterations'].value):
        #     # Check if the maximum of iterations has been reached
        #     if (iter == self.settings['max_iterations'].value - 1):
        #         print('Solver did not converge in ', iter, ' iterations.')
        #         print('res = ', res)
        #         print('LM_res = ', LM_res)
        #         import pdb; pdb.set_trace()
        #         break
        #
        #     # Update positions and velocities
        #     mb.state2disp(q, dqdt, dqddt, MB_beam, MB_tstep)
        #     MB_Asys, MB_Q = self.assembly_MB_eq_system(MB_beam, MB_tstep, self.data.ts, dt, Lambda, Lambda_dot, MBdict)
        #
        #     # Compute the correction
        #     # ADC next line not necessary
        #     # Dq = np.zeros((self.sys_size+num_LM_eq,), dtype=ct.c_double, order='F')
        #     # MB_Asys_balanced, T = scipy.linalg.matrix_balance(MB_Asys)
        #     # invT = np.matrix(T).I
        #     # MB_Q_balanced = np.dot(invT, MB_Q).T
        #
        #     Dq = np.linalg.solve(MB_Asys, -MB_Q)
        #     # least squares solver
        #     # Dq = np.linalg.lstsq(np.dot(MB_Asys_balanced, invT), -MB_Q_balanced, rcond=None)[0]
        #
        #     # Evaluate convergence
        #     if (iter > 0):
        #         res = np.max(np.abs(Dq[0:self.sys_size]))/old_Dq
        #         if not num_LM_eq == 0:
        #             LM_res = np.max(np.abs(Dq[self.sys_size:self.sys_size+num_LM_eq]))/LM_old_Dq
        #         else:
        #             LM_res = 0.0
        #         if (res < self.settings['min_delta'].value) and (LM_res < self.settings['min_delta'].value*1e-2):
        #             converged = True
        #
        #     # Compute variables from previous values and increments
        #     # TODO:decide If I want other way of updating lambda
        #     # this for least sq
        #     # q[:, np.newaxis] += Dq
        #     # dqdt[:, np.newaxis] += self.gamma/(self.beta*dt)*Dq
        #     # dqddt[:, np.newaxis] += 1.0/(self.beta*dt*dt)*Dq
        #
        #     # this for direct solver
        #     q += Dq
        #     dqdt += self.gamma/(self.beta*dt)*Dq
        #     dqddt += 1.0/(self.beta*dt*dt)*Dq
        #
        #     if not num_LM_eq == 0:
        #         Lambda = q[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F')
        #         Lambda_dot = dqdt[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F')
        #     else:
        #         Lambda = 0
        #         Lambda_dot = 0
        #
        #     if converged:
        #         break
        #
        #     if iter == 0:
        #         old_Dq = np.max(np.abs(Dq[0:self.sys_size]))
        #         if old_Dq < 1.0:
        #             old_Dq = 1.0
        #         if num_LM_eq:
        #             LM_old_Dq = np.max(np.abs(Dq[self.sys_size:self.sys_size+num_LM_eq]))
        #         else:
        #             LM_old_Dq = 1.0
        #
        # mb.state2disp(q, dqdt, dqddt, MB_beam, MB_tstep)
        # # end: comment time stepping
        #
        # # End of Newmark-beta iterations
        # self.integrate_position(MB_beam, MB_tstep, dt)
        # # lagrangeconstraints.postprocess(self.lc_list, MB_beam, MB_tstep, MBdict, "dynamic")
        # lagrangeconstraints.postprocess(self.lc_list, MB_beam, MB_tstep, "dynamic")
        # self.compute_forces_constraints(MB_beam, MB_tstep, self.data.ts, dt, Lambda, Lambda_dot)
        # if self.settings['gravity_on']:
        #     for ibody in range(len(MB_beam)):
        #         xbeamlib.cbeam3_correct_gravity_forces(MB_beam[ibody], MB_tstep[ibody], self.settings)
        # mb.merge_multibody(MB_tstep, MB_beam, self.data.structure, structural_step, MBdict, dt)

        # structural_step.q[:] = q[:self.sys_size].copy()
        # structural_step.dqdt[:] = dqdt[:self.sys_size].copy()
        # structural_step.dqddt[:] = dqddt[:self.sys_size].copy()

        return self.data
Exemplo n.º 27
0
def print_available_solvers():
    cout.cout_wrap('The available solvers on this session are:', 2)
    for name, i_solver in dict_of_solvers.items():
        cout.cout_wrap('%s ' % i_solver.solver_id, 2)
Exemplo n.º 28
0
def solver_wrapper(x, x_info, solver_data, i_dim=-1):
    if solver_data.settings['print_info']:
        cout.cout_wrap('x = ' + str(x), 1)
    # print('x = ', x)
    alpha = x[x_info['i_alpha']]
    beta = x[x_info['i_beta']]
    roll = x[x_info['i_roll']]
    # change input data
    solver_data.data.structure.timestep_info[
        solver_data.data.ts] = solver_data.data.structure.ini_info.copy()
    tstep = solver_data.data.structure.timestep_info[solver_data.data.ts]
    aero_tstep = solver_data.data.aero.timestep_info[solver_data.data.ts]
    orientation_quat = algebra.euler2quat(np.array([roll, alpha, beta]))
    tstep.quat[:] = orientation_quat
    # control surface deflection
    for i_cs in range(len(x_info['i_control_surfaces'])):
        solver_data.data.aero.aero_dict['control_surface_deflection'][
            x_info['control_surfaces_id'][i_cs]] = x[
                x_info['i_control_surfaces'][i_cs]]
    # thrust input
    tstep.steady_applied_forces[:] = 0.0
    try:
        x_info['special_case']
    except KeyError:
        for i_thrust in range(len(x_info['i_thrust'])):
            thrust = x[x_info['i_thrust'][i_thrust]]
            i_node = x_info['thrust_nodes'][i_thrust]
            solver_data.data.structure.ini_info.steady_applied_forces[
                i_node, 0:3] = thrust * x_info['thrust_direction'][i_thrust]
    else:
        if x_info['special_case'] == 'differential_thrust':
            base_thrust = x[x_info['i_base_thrust']]
            pos_thrust = base_thrust * (1.0 +
                                        x[x_info['i_differential_parameter']])
            neg_thrust = -base_thrust * (1.0 -
                                         x[x_info['i_differential_parameter']])
            for i_base_node in x_info['base_thrust_nodes']:
                solver_data.data.structure.ini_info.steady_applied_forces[
                    i_base_node, 1] = base_thrust
            for i_pos_diff_node in x_info['positive_thrust_nodes']:
                solver_data.data.structure.ini_info.steady_applied_forces[
                    i_pos_diff_node, 1] = pos_thrust
            for i_neg_diff_node in x_info['negative_thrust_nodes']:
                solver_data.data.structure.ini_info.steady_applied_forces[
                    i_neg_diff_node, 1] = neg_thrust

    # run the solver
    solver_data.solver.run()
    # extract resultants
    forces, moments = solver_data.solver.extract_resultants()

    totals = np.zeros((6, ))
    totals[0:3] = forces
    totals[3:6] = moments
    if solver_data.settings['print_info']:
        cout.cout_wrap(' forces = ' + str(totals), 1)
    # print('total forces = ', totals)
    # try:
    #     totals += x[x_info['i_none']]
    # except KeyError:
    #     pass
    # return resultant forces and moments
    # return np.linalg.norm(totals)
    if i_dim >= 0:
        return totals[i_dim]
    elif i_dim == -1:
        # return [np.sum(totals[0:3]**2), np.sum(totals[4:6]**2)]
        return totals
    elif i_dim == -2:
        coeffs = np.array([1.0, 1.0, 1.0, 2, 2, 2])
        # print('return = ', np.dot(coeffs*totals, coeffs*totals))
        if solver_data.settings['print_info']:
            cout.cout_wrap(
                ' val = ' + str(np.dot(coeffs * totals, coeffs * totals)), 1)
        return np.dot(coeffs * totals, coeffs * totals)
Exemplo n.º 29
0
 def output_info(self):
     cout.cout_wrap(
         'The aerodynamic grid contains %u surfaces' % self.n_surf, 1)
     for i_surf in range(self.n_surf):
         cout.cout_wrap(
             '  Surface %u, M=%u, N=%u' %
             (i_surf, self.aero_dimensions[i_surf, 0],
              self.aero_dimensions[i_surf, 1]), 1)
         cout.cout_wrap('     Wake %u, M=%u, N=%u' %
                        (i_surf, self.aero_dimensions_star[i_surf, 0],
                         self.aero_dimensions_star[i_surf, 1]))
     cout.cout_wrap(
         '  In total: %u bound panels' %
         (sum(self.aero_dimensions[:, 0] * self.aero_dimensions[:, 1])))
     cout.cout_wrap('  In total: %u wake panels' %
                    (sum(self.aero_dimensions_star[:, 0] *
                         self.aero_dimensions_star[:, 1])))
     cout.cout_wrap(
         '  Total number of panels = %u' %
         (sum(self.aero_dimensions[:, 0] * self.aero_dimensions[:, 1]) +
          sum(self.aero_dimensions_star[:, 0] *
              self.aero_dimensions_star[:, 1])))
Exemplo n.º 30
0
def mode_sign_convention(bocos,
                         eigenvectors,
                         rigid_body_motion=False,
                         use_euler=False):
    """
    When comparing against different cases, it is important that the modes share a common sign convention.

    In this case, modes will be arranged such that the z-coordinate of the first free end is positive.

    If the z-coordinate is 0, then the y-coordinate is forced to be positive, then x, followed by the CRV in y, x and z.

    Returns:
        np.ndarray: Eigenvectors following the aforementioned sign convention.
    """

    if use_euler:
        num_rigid_modes = 9
    else:
        num_rigid_modes = 10

    if rigid_body_motion:
        eigenvectors = order_rigid_body_modes(eigenvectors, use_euler)

        # A frame reference
        z_coord = -num_rigid_modes + 2
        y_coord = -num_rigid_modes + 1
        x_coord = -num_rigid_modes + 0
        mz_coord = -num_rigid_modes + 5
        my_coord = -num_rigid_modes + 4
        mx_coord = -num_rigid_modes + 3
    else:
        first_free_end_node = np.where(bocos == -1)[0][0]

        z_coord = 6 * (first_free_end_node - 1) + 2
        y_coord = 6 * (first_free_end_node - 1) + 1
        x_coord = 6 * (first_free_end_node - 1) + 0
        my_coord = 6 * (first_free_end_node - 1) + 4
        mz_coord = 6 * (first_free_end_node - 1) + 5
        mx_coord = 6 * (first_free_end_node - 1) + 3

    for i in range(0, eigenvectors.shape[1]):
        if np.abs(eigenvectors[z_coord, i]) > 1e-8:
            eigenvectors[:, i] = np.sign(eigenvectors[z_coord,
                                                      i]) * eigenvectors[:, i]

        elif np.abs(eigenvectors[y_coord, i]) > 1e-8:
            eigenvectors[:, i] = np.sign(eigenvectors[y_coord,
                                                      i]) * eigenvectors[:, i]

        elif np.abs(eigenvectors[x_coord, i]) > 1e-8:
            eigenvectors[:, i] = np.sign(eigenvectors[x_coord,
                                                      i]) * eigenvectors[:, i]

        elif np.abs(eigenvectors[my_coord, i]) > 1e-8:
            eigenvectors[:, i] = np.sign(eigenvectors[my_coord,
                                                      i]) * eigenvectors[:, i]

        elif np.abs(eigenvectors[mx_coord, i]) > 1e-8:
            eigenvectors[:, i] = np.sign(eigenvectors[mx_coord,
                                                      i]) * eigenvectors[:, i]

        elif np.abs(eigenvectors[mz_coord, i]) > 1e-8:
            eigenvectors[:, i] = np.sign(eigenvectors[mz_coord,
                                                      i]) * eigenvectors[:, i]

        else:
            if rigid_body_motion:
                if not np.max(np.abs(eigenvectors[-num_rigid_modes + 6:, i])
                              ) == 1.0:  # orientation mode, either euler/quat
                    cout.cout_wrap(
                        'Implementing mode sign convention. Mode {:g} component at the A frame is 0.'
                        .format(i), 3)
            else:
                # cout.cout_wrap('Mode component at the first free end (node {:g}) is 0.'.format(first_free_end_node), 3)

                # this will be the case for symmetric clamped structures, where modes will be present for the left and
                # right wings. Method should be called again when symmetric modes are removed.
                pass

    return eigenvectors