def NewtonRaphson(self, function_spaces, formulation, solver, Increment, K, NodalForces, Residual, mesh, Eulerx, materials, boundary_condition, AppliedDirichletInc): Tolerance = self.newton_raphson_tolerance LoadIncrement = self.number_of_load_increments Iter = 0 self.iterative_norm_history = [] # APPLY INCREMENTAL DIRICHLET PER LOAD STEP (THIS IS INCREMENTAL NOT ACCUMULATIVE) IncDirichlet = boundary_condition.UpdateFixDoFs(AppliedDirichletInc, K.shape[0],formulation.nvar) # UPDATE EULERIAN COORDINATE Eulerx += IncDirichlet[:,:formulation.ndim] while self.norm_residual > Tolerance or Iter==0: # GET THE REDUCED SYSTEM OF EQUATIONS K_b, F_b = boundary_condition.GetReducedMatrices(K,Residual)[:2] # SOLVE THE SYSTEM sol = solver.Solve(K_b,-F_b) # GET ITERATIVE SOLUTION dU = boundary_condition.UpdateFreeDoFs(sol,K.shape[0],formulation.nvar) # UPDATE THE EULERIAN COMPONENTS # UPDATE THE GEOMETRY Eulerx += dU[:,:formulation.ndim] # RE-ASSEMBLE - COMPUTE STIFFNESS AND INTERNAL TRACTION FORCES K, TractionForces = Assemble(self, function_spaces, formulation, mesh, materials, boundary_condition, Eulerx)[:2] # COMPUTE ROBIN STIFFNESS AND FORCES (EXTERNAL) K, TractionForces = boundary_condition.ComputeRobinForces(mesh, materials, function_spaces, self, Eulerx, K, TractionForces) # FIND THE RESIDUAL Residual[boundary_condition.columns_in] = TractionForces[boundary_condition.columns_in] - \ NodalForces[boundary_condition.columns_in] # SAVE THE NORM self.abs_norm_residual = la.norm(Residual[boundary_condition.columns_in]) if Iter==0: self.NormForces = la.norm(Residual[boundary_condition.columns_in]) self.norm_residual = np.abs(la.norm(Residual[boundary_condition.columns_in])/self.NormForces) # SAVE THE NORM self.NRConvergence['Increment_'+str(Increment)] = np.append(self.NRConvergence['Increment_'+str(Increment)],\ self.norm_residual) print("Iteration {} for increment {}.".format(Iter, Increment) +\ " Residual (abs) {0:>16.7g}".format(self.abs_norm_residual), "\t Residual (rel) {0:>16.7g}".format(self.norm_residual)) # BREAK BASED ON RELATIVE NORM if np.abs(self.abs_norm_residual) < Tolerance: break # BREAK BASED ON INCREMENTAL SOLUTION - KEEP IT AFTER UPDATE if norm(dU) <= self.newton_raphson_solution_tolerance: # and Iter!=0: print("Incremental solution within tolerance i.e. norm(dU): {}".format(norm(dU))) break # UPDATE ITERATION NUMBER Iter +=1 if Iter==self.maximum_iteration_for_newton_raphson: self.newton_raphson_failed_to_converge = True break if np.isnan(self.norm_residual) or self.norm_residual>1e06: self.newton_raphson_failed_to_converge = True break # IF BREAK WHEN NEWTON RAPHSON STAGNATES IS ACTIVATED if self.break_at_stagnation: self.iterative_norm_history.append(self.norm_residual) if Iter >= 5: if np.mean(self.iterative_norm_history) < 1.: break # USER DEFINED CRITERIA TO BREAK OUT OF NEWTON-RAPHSON if self.user_defined_break_func != None: if self.user_defined_break_func(Increment,Iter,self.norm_residual,self.abs_norm_residual, Tolerance): break # USER DEFINED CRITERIA TO STOP NEWTON-RAPHSON AND THE WHOLE ANALYSIS if self.user_defined_stop_func != None: if self.user_defined_stop_func(Increment,Iter,self.norm_residual,self.abs_norm_residual, Tolerance): self.newton_raphson_failed_to_converge = True break return Eulerx, K, Residual
def Solver(self, function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, materials, boundary_condition, fem_solver): if len(materials) > 1 and self.density_turnover == "muscle": warn( "More than one material. I will assume the first material is the Media" ) # check materials with growth and remodeling gr_materials = [] for imat in range(len(materials)): if materials[imat].has_growth_remodeling: gr_materials.append(imat) gr_materials = np.array(gr_materials, dtype=np.int64, copy=True).flatten() GRVariables = [[] for i in range(gr_materials.shape[0])] for imat in range(gr_materials.shape[0]): GRVariables[imat] = np.zeros( (materials[gr_materials[imat]].node_set.shape[0], 12, fem_solver.number_of_time_increments), dtype=np.float64) TimeIncrements = fem_solver.number_of_time_increments TimeFactor = fem_solver.total_time / TimeIncrements LoadIncrements = fem_solver.number_of_load_increments LoadFactor = 1. / LoadIncrements AppliedDirichletInc = np.zeros( boundary_condition.applied_dirichlet.shape[0], dtype=np.float64) # HOMEOSTATIC STATE #TotalDisp = self.GetHomeostaticState(function_spaces, formulation, solver, # K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, # material, boundary_condition, fem_solver, AppliedDirichletInc) IncrementalTime = 0.0 # TIME LOOP for TIncrement in range(TimeIncrements): # CHECK ADAPTIVE STEP TIME FACTOR if fem_solver.time_factor is not None: TimeFactor = fem_solver.time_factor[TIncrement] Delta_t = TimeFactor # COMPUTE THE GROWTH AND REMODELING if TIncrement != 0: for imat in range(gr_materials.shape[0]): Rates = self.RatesGrowthRemodeling( mesh, materials[gr_materials[imat]], FibreStress, Softness, imat) GRVariables[ imat][:, :, TIncrement] = self.ExplicitGrowthRemodeling( mesh, materials[gr_materials[imat]], IncrementalTime, Delta_t, Rates) else: for imat in range(gr_materials.shape[0]): materials[gr_materials[imat]].den0_e = np.copy( materials[gr_materials[imat]].state_variables[:, 14]) GRVariables[imat][:, :, 0] = np.copy( materials[gr_materials[imat]].state_variables[:, 9:21]) IncrementalLoad = 1.0 print("=============================") print("== Time elapsed, {:4.0f} days ==".format(IncrementalTime)) print("=============================") # MATERIALS CHANGE AT EACH STEP if TIncrement != 0: tAssembly = time() K, TractionForces = Assemble(fem_solver, function_spaces, formulation, mesh, materials, boundary_condition, Eulerx)[:2] # APPLY ROBIN BOUNDARY CONDITIONS - STIFFNESS(_) AND FORCES boundary_condition.pressure_increment = IncrementalLoad K, RobinForces = boundary_condition.ComputeRobinForces( mesh, materials, function_spaces, fem_solver, Eulerx, K, np.zeros_like(Residual)) # APPLY NEUMANN BOUNDARY CONDITIONS DeltaF = LoadFactor * NeumannForces NodalForces += DeltaF # OBRTAIN INCREMENTAL RESIDUAL - CONTRIBUTION FROM BOTH NEUMANN AND DIRICHLET Residual = -boundary_condition.ApplyDirichletGetReducedMatrices( K, np.zeros_like(Residual), boundary_condition.applied_dirichlet, LoadFactor=LoadFactor, only_residual=True) Residual += RobinForces - DeltaF # GET THE INCREMENTAL DISPLACEMENT AppliedDirichletInc = LoadFactor * boundary_condition.applied_dirichlet if TIncrement != 0: print('Finished the assembly stage. Time elapsed was', time() - tAssembly, 'seconds') t_increment = time() # LET NORM OF THE FIRST RESIDUAL BE THE NORM WITH RESPECT TO WHICH WE # HAVE TO CHECK THE CONVERGENCE OF NEWTON RAPHSON. TYPICALLY THIS IS # NORM OF NODAL FORCES if TIncrement == 0: fem_solver.NormForces = np.linalg.norm(Residual) # AVOID DIVISION BY ZERO if np.isclose(fem_solver.NormForces, 0.0): fem_solver.NormForces = 1e-14 fem_solver.norm_residual = np.linalg.norm( Residual) / fem_solver.NormForces if fem_solver.nonlinear_iterative_technique == "newton_raphson": Eulerx, K, Residual = self.NewtonRaphson( function_spaces, formulation, solver, TIncrement, K, NodalForces, Residual, mesh, Eulerx, materials, boundary_condition, AppliedDirichletInc, fem_solver) else: raise RuntimeError( "Iterative technique for nonlinear solver not understood") # UPDATE DISPLACEMENTS FOR THE CURRENT LOAD INCREMENT TotalDisp[:, :formulation.ndim, TIncrement] = Eulerx - mesh.points #CHECK HOMEOSTATIC DISTORTION #if TIncrement==0: # self.HomeostaticDistortion(fem_solver, formulation, TotalDisp, TIncrement) # COMPUTE THE FIBRE-STRESS AND SOFTNESS FibreStress = [[] for i in range(gr_materials.shape[0])] Softness = [[] for i in range(gr_materials.shape[0])] for imat in range(gr_materials.shape[0]): FibreStress[imat], Softness[ imat] = self.GetFibreStressAndSoftness( mesh, formulation, materials[gr_materials[imat]], fem_solver, Eulerx) # SET HOMEOSTATIC FIBRE-STRESS if TIncrement == 0: self.HomeostaticStress = [[] for i in range(gr_materials.shape[0]) ] for imat in range(gr_materials.shape[0]): self.HomeostaticStress[imat] = FibreStress[imat] # PRINT LOG IF ASKED FOR self.LogSave(fem_solver, formulation, TotalDisp, TIncrement, materials, FibreStress, gr_materials) # UPDATE THE TIME IncrementalTime += TimeFactor print('\nFinished Time increment', TIncrement, 'in', time() - t_increment, 'seconds') try: print( 'Norm of Residual is', np.abs( la.norm(Residual[boundary_condition.columns_in]) / fem_solver.NormForces), '\n') except RuntimeWarning: print( "Invalid value encountered in norm of Newton-Raphson residual" ) # STORE THE INFORMATION IF NEWTON-RAPHSON FAILS if fem_solver.newton_raphson_failed_to_converge: solver.condA = np.NAN TIncrement = TIncrement if TIncrement != 0 else 1 TotalDisp = TotalDisp[:, :, :TIncrement] for imat in range(gr_materials.shape[0]): GRVariables[imat] = GRVariables[imat][:, :, :TIncrement] fem_solver.number_of_time_increments = TIncrement break # BREAK AT A SPECIFICED TIME INCREMENT IF ASKED FOR if fem_solver.break_at_increment != -1 and fem_solver.break_at_increment is not None: if fem_solver.break_at_increment == TIncrement: if fem_solver.break_at_increment < TimeIncrements - 1: print( "\nStopping at time increment {} as specified\n\n". format(TIncrement)) TotalDisp = TotalDisp[:, :, :TIncrement] fem_solver.number_of_time_increments = TIncrement break # # UPDATE. MATERIAL ADAPTATIVE LOAD FACTOR. FOR DEPOSITION STRETCH # if Increment is not (LoadIncrement-1): # for imat in range(len(materials)): # if materials[imat].load_factor is not None: # materials[imat].factor_increment += materials[imat].load_factor[Increment+1] return TotalDisp, GRVariables
def Solve(self, formulation=None, mesh=None, materials=None, boundary_condition=None, function_spaces=None, solver=None, contact_formulation=None, growth_remodeling=None, Eulerx=None): """Main solution routine for FEMSolver """ # LOG LEVEL CONTROLLER if self.report_log_level == 0: sys.stdout = open(os.devnull, "w") # CHECK DATA CONSISTENCY #---------------------------------------------------------------------------# function_spaces, solver = self.__checkdata__(materials, boundary_condition, formulation, mesh, function_spaces, solver, contact_formulation=contact_formulation, growth_remodeling=growth_remodeling) # PRINT INFO #---------------------------------------------------------------------------# self.PrintPreAnalysisInfo(mesh, formulation) #---------------------------------------------------------------------------# # COMPUTE SPARSITY PATTERN #---------------------------------------------------------------------------# self.ComputeSparsityFEM(mesh, formulation) #---------------------------------------------------------------------------# # LAUNCH DISTRIBUTED SCHEDULER #---------------------------------------------------------------------------# if self.parallel and self.parallel_model=="dask" and self.is_dask_scheduler_initialised is False: self.LaunchDaskDistributedClient() #---------------------------------------------------------------------------# # INITIATE DATA FOR THE ANALYSIS NodalForces, Residual = np.zeros((mesh.points.shape[0]*formulation.nvar,1),dtype=np.float64),\ np.zeros((mesh.points.shape[0]*formulation.nvar,1),dtype=np.float64) # SET NON-LINEAR PARAMETERS if not self.has_growth_remodeling: self.NRConvergence = { 'Increment_'+str(Increment) : [] for Increment in range(\ self.number_of_load_increments) } else: self.NRConvergence = { 'Increment_'+str(Increment) : [] for Increment in range(\ self.number_of_time_increments) } # ALLOCATE FOR SOLUTION FIELDS if self.has_growth_remodeling: if self.save_frequency == 1: TotalDisp = np.zeros((mesh.points.shape[0],formulation.nvar, self.number_of_time_increments),dtype=np.float64) else: TotalDisp = np.zeros((mesh.points.shape[0],formulation.nvar, int(self.number_of_time_increments/self.save_frequency)),dtype=np.float64) else: if self.save_frequency == 1: TotalDisp = np.zeros((mesh.points.shape[0],formulation.nvar, self.number_of_load_increments),dtype=np.float64) else: TotalDisp = np.zeros((mesh.points.shape[0],formulation.nvar, int(self.number_of_load_increments/self.save_frequency)),dtype=np.float64) # PRE-ASSEMBLY print('Assembling the system and acquiring neccessary information for the analysis...') tAssembly=time() # APPLY DIRICHELT BOUNDARY CONDITIONS AND GET DIRICHLET RELATED FORCES boundary_condition.GetDirichletBoundaryConditions(formulation, mesh, materials, solver, self) # ALLOCATE FOR GEOMETRY - GetDirichletBoundaryConditions CHANGES THE MESH # SO EULERX SHOULD BE ALLOCATED AFTERWARDS Eulerx = np.copy(mesh.points) # FIND PURE NEUMANN (EXTERNAL) NODAL FORCE VECTOR NeumannForces = boundary_condition.ComputeNeumannForces(mesh, materials, function_spaces, compute_traction_forces=True, compute_body_forces=self.add_self_weight) # ADOPT A DIFFERENT PATH FOR INCREMENTAL LINEAR ELASTICITY if formulation.fields == "mechanics" and self.analysis_nature != "nonlinear": if self.analysis_type == "static": # DISPATCH INCREMENTAL LINEAR ELASTICITY SOLVER TotalDisp = self.IncrementalLinearElasticitySolver(function_spaces, formulation, mesh, materials, boundary_condition, solver, TotalDisp, Eulerx, NeumannForces) return self.__makeoutput__(mesh, TotalDisp, formulation, function_spaces, material) # ASSEMBLE STIFFNESS MATRIX AND TRACTION FORCES FOR THE FIRST TIME (INTERNAL ENERGY) if self.analysis_type == "static": K, TractionForces, _ = Assemble(self, function_spaces, formulation, mesh, materials, boundary_condition, Eulerx) else: if self.reduce_quadrature_for_quads_hexes: fspace = function_spaces[0] if (mesh.element_type=="hex" or mesh.element_type=="quad") else function_spaces[1] else: fspace = function_spaces[1] # COMPUTE CONSTANT PART OF MASS MATRIX formulation.GetConstantMassIntegrand(fspace, material) if self.analysis_subtype != "explicit": # COMPUTE BOTH STIFFNESS AND MASS USING HIGHER QUADRATURE RULE K, TractionForces, M = Assemble(self, fspace, formulation, mesh, material, boundary_condition, Eulerx) else: # lmesh = mesh.ConvertToLinearMesh() # COMPUTE BOTH STIFFNESS AND MASS USING HIGHER QUADRATURE RULE TractionForces, _, M = AssembleExplicit(self, fspace, formulation, mesh, material, Eulerx) if self.analysis_nature == 'nonlinear': print('Finished all pre-processing stage. Time elapsed was', time()-tAssembly, 'seconds') else: print('Finished the assembly stage. Time elapsed was', time()-tAssembly, 'seconds') if self.analysis_type != 'static': if self.analysis_subtype != "explicit": if self.analysis_nature == "nonlinear": structural_integrator = NonlinearImplicitStructuralDynamicIntegrator() TotalDisp = structural_integrator.Solver(function_spaces, formulation, solver, K, M, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, material, boundary_condition, self) elif self.analysis_nature == "linear": boundary_condition.ConvertStaticsToDynamics(mesh, self.number_of_load_increments) structural_integrator = LinearImplicitStructuralDynamicIntegrator() TotalDisp = structural_integrator.Solver(function_spaces, formulation, solver, K, M, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, material, boundary_condition, self) else: structural_integrator = ExplicitStructuralDynamicIntegrator() TotalDisp = structural_integrator.Solver(function_spaces, formulation, solver, TractionForces, M, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, material, boundary_condition, self) else: if self.has_growth_remodeling: TotalDisp,GRVariables = growth_remodeling.Solver(function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, materials, boundary_condition, self) return self.__makeoutput__(mesh, TotalDisp, formulation, function_spaces, materials, gr_variables=GRVariables) else: if self.nonlinear_iterative_technique == "newton_raphson" or \ self.nonlinear_iterative_technique == "modified_newton_raphson" or \ self.nonlinear_iterative_technique == "line_search_newton_raphson" or \ self.nonlinear_iterative_technique == "snes": TotalDisp = self.StaticSolver(function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, materials, boundary_condition) elif self.nonlinear_iterative_technique == "arc_length": from FEMSolverArcLength import StaticSolverArcLength TotalDisp = StaticSolverArcLength(self,function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, materials, boundary_condition) else: raise RuntimeError("Iterative technique for nonlinear solver not understood") # LOG LEVEL CONTROLLER if self.report_log_level == 0: sys.stdout = sys.__stdout__ return self.__makeoutput__(mesh, TotalDisp, formulation, function_spaces, materials)