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