示例#1
0
    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 = self.settings['dt'].value
        else:
            self.settings['dt'] = ct.c_float(dt)

        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,
                                               self.data.ts)

        # 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')

        # 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 iteration in range(self.settings['max_iterations'].value):
            # Check if the maximum of iterations has been reached
            if iteration == self.settings['max_iterations'].value - 1:
                error = (
                    'Solver did not converge in %d iterations.\n res = %e \n LM_res = %e'
                    % (iteration, res, LM_res))
                raise exc.NotConvergedSolver(error)

            # 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 iteration:
                res = np.max(np.abs(Dq[0:self.sys_size])) / old_Dq
                if np.isnan(res):
                    raise exc.NotConvergedSolver('Multibody res = NaN')
                if num_LM_eq:
                    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):
                    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 not iteration:
                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
示例#2
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
示例#3
0
    def time_loop(self, in_queue=None, out_queue=None, finish_event=None):
        self.logger.debug('Inside time loop')
        # 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 + 1):
            initial_time = time.perf_counter()

            # network only
            # get input from the other thread
            if in_queue:
                self.logger.info('Time Loop - Waiting for input')
                values = in_queue.get()  # should be list of tuples
                self.logger.debug('Time loop - received {}'.format(values))
                self.set_of_variables.update_timestep(self.data, values)

            structural_kstep = self.data.structure.timestep_info[-1].copy()
            aero_kstep = self.data.aero.timestep_info[-1].copy()
            self.logger.debug('Time step {}'.format(self.data.ts))

            # 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)

            # Add external forces
            if self.with_runtime_generators:
                params = dict()
                params['data'] = self.data
                params['struct_tstep'] = structural_kstep
                params['aero_tstep'] = aero_kstep
                for id, runtime_generator in self.runtime_generators.items():
                    runtime_generator.generate(params)

            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']):
                    print_res = 0 if self.res_dqdt == 0. else np.log10(
                        self.res_dqdt)
                    cout.cout_wrap(
                        ("The FSI solver did not converge!!! residual: %f" %
                         print_res))
                    self.aero_solver.update_custom_grid(
                        structural_kstep, aero_kstep)
                    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
                ) or self.settings['aero_solver'].lower() == 'noaero':
                    # 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:
                print_res = 0 if self.res_dqdt == 0. else np.log10(
                    self.res_dqdt)
                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, print_res,
                    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)

            # network only
            # put result back in queue
            if out_queue:
                self.logger.debug(
                    'Time loop - about to get out variables from data')
                self.set_of_variables.get_value(self.data)
                if out_queue.full():
                    # clear the queue such that it always contains the latest time step
                    out_queue.get()  # clear item from queue
                    self.logger.debug(
                        'Data output Queue is full - clearing output')
                out_queue.put(self.set_of_variables)

        if finish_event:
            finish_event.set()
            self.logger.info('Time loop - Complete')