boundary_condition.GetDirichletBoundaryConditions(formulation, mesh, material, solver, self) # ALLOCATE FOR GEOMETRY - GetDirichletBoundaryConditions CHANGES THE MESH # SO EULERX SHOULD BE ALLOCATED AFTERWARDS Eulerx = np.copy(mesh.points) Eulerp = np.zeros((mesh.points.shape[0])) # FIND PURE NEUMANN (EXTERNAL) NODAL FORCE VECTOR <<<<<<< HEAD NeumannForces = boundary_condition.ComputeNeumannForces(mesh, material) ======= NeumannForces = boundary_condition.ComputeNeumannForces(mesh, material, function_spaces) >>>>>>> upstream/master # ASSEMBLE STIFFNESS MATRIX AND TRACTION FORCES K, TractionForces = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[:2] print('Finished all pre-processing stage. Time elapsed was', time()-tAssembly, 'seconds') <<<<<<< HEAD self.StaggeredSolver(function_spaces, formulation, solver, ======= TotalDisp = self.StaggeredSolver(function_spaces, formulation, solver, >>>>>>> upstream/master K,NeumannForces,NodalForces,Residual, mesh,TotalDisp,Eulerx,Eulerp,material, boundary_condition) return self.__makeoutput__(mesh, TotalDisp, formulation, function_spaces, material)
def NewtonRaphson(self, function_spaces, formulation, solver, Increment, K, D, M, NodalForces, Residual, mesh, Eulerx, Eulerp, material, boundary_condition, AppliedDirichletInc, fem_solver, velocities, accelerations): Tolerance = fem_solver.newton_raphson_tolerance LoadIncrement = fem_solver.number_of_load_increments LoadFactor = fem_solver.total_time/fem_solver.number_of_load_increments Iter = 0 # EulerxPrev = np.copy(Eulerx) # EulerVPrev = np.copy(velocities[:,:,Increment-1]) # EulerAPrev = np.copy(accelerations[:,:,Increment-1]) # PREDICTOR STEP tmpV = (1. - self.gamma/self.beta)*velocities + (1. - self.gamma/2./self.beta)*LoadFactor*accelerations tmpA = (-1./self.beta/LoadFactor)*velocities - (1./2./self.beta)*(1.- 2.*self.beta)*accelerations velocities = tmpV accelerations = tmpA if formulation.fields == "electro_mechanics": M_mech = M[self.mechanical_dofs,:][:,self.mechanical_dofs] InertiaResidual = np.zeros((Residual.shape[0],1)) InertiaResidual[self.mechanical_dofs,0] = M_mech.dot(accelerations.ravel()) if fem_solver.include_physical_damping: D_mech = D[self.mechanical_dofs,:][:,self.mechanical_dofs] InertiaResidual[self.mechanical_dofs,0] += D_mech.dot(velocities.ravel()) else: InertiaResidual = np.zeros((Residual.shape[0],1)) InertiaResidual[:,0] = M.dot(accelerations.ravel()) if fem_solver.include_physical_damping: InertiaResidual[:,0] += D.dot(velocities.ravel()) Residual[boundary_condition.columns_in] += InertiaResidual[boundary_condition.columns_in] # 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] Eulerp[:] = IncDirichlet[:,-1] # ENSURES Eulerp IS CONTIGUOUS - NECESSARY FOR LOW-LEVEL DISPATCHER while np.abs(la.norm(Residual[boundary_condition.columns_in])/self.NormForces) > Tolerance or Iter==0: # GET EFFECTIVE STIFFNESS # K += (1./self.beta/LoadFactor**2)*M K += (self.gamma/self.beta/LoadFactor)*D + (1./self.beta/LoadFactor**2)*M # GET THE REDUCED SYSTEM OF EQUATIONS K_b, F_b, _ = boundary_condition.GetReducedMatrices(K,Residual) # 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] # GET ITERATIVE ELECTRIC POTENTIAL Eulerp += dU[:,-1] # UPDATE VELOCITY AND ACCELERATION velocities += self.gamma/self.beta/LoadFactor*dU[:,:formulation.ndim] accelerations += 1./self.beta/LoadFactor**2*dU[:,:formulation.ndim] # OR ALTERNATIVELY # dumA = 1./self.beta/LoadFactor**2*(Eulerx - EulerxPrev) -\ # 1./self.beta/LoadFactor*(EulerVPrev) -\ # 1./2./self.beta*(1. - 2.*self.beta)*(EulerAPrev) # dumV = (1. - self.gamma/self.beta)*(EulerVPrev) +\ # (1. - self.gamma/2./self.beta)*LoadFactor*(EulerAPrev) +\ # self.gamma/self.beta/LoadFactor*(Eulerx - EulerxPrev) # velocities = dumV # accelerations = dumA # RE-ASSEMBLE - COMPUTE STIFFNESS AND INTERNAL TRACTION FORCES K, TractionForces, _, _ = Assemble(fem_solver,function_spaces[0], formulation, mesh, material, Eulerx, Eulerp) # FIND INITIAL RESIDUAL if formulation.fields == "electro_mechanics": InertiaResidual = np.zeros((TractionForces.shape[0],1)) InertiaResidual[self.mechanical_dofs,0] = M_mech.dot(accelerations.ravel()) if fem_solver.include_physical_damping: InertiaResidual[self.mechanical_dofs,0] += D_mech.dot(velocities.ravel()) else: InertiaResidual = np.zeros((TractionForces.shape[0],1)) InertiaResidual[:,0] = M.dot(accelerations.ravel()) if fem_solver.include_physical_damping: InertiaResidual[:,0] += D.dot(velocities.ravel()) # UPDATE RESIDUAL Residual[boundary_condition.columns_in] = TractionForces[boundary_condition.columns_in] \ - NodalForces[boundary_condition.columns_in] + InertiaResidual[boundary_condition.columns_in] # SAVE THE NORM self.rel_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.rel_norm_residual), "\t Residual (rel) {0:>16.7g}".format(self.norm_residual)) # BREAK BASED ON RELATIVE NORM if np.abs(self.rel_norm_residual) < Tolerance: break # BREAK BASED ON INCREMENTAL SOLUTION - KEEP IT AFTER UPDATE if norm(dU) <= fem_solver.newton_raphson_solution_tolerance: print("Incremental solution within tolerance i.e. norm(dU): {}".format(norm(dU))) break # UPDATE ITERATION NUMBER Iter +=1 if Iter==fem_solver.maximum_iteration_for_newton_raphson and formulation.fields == "electro_mechanics": raise StopIteration("\n\nNewton Raphson did not converge! Maximum number of iterations reached.") if Iter==fem_solver.maximum_iteration_for_newton_raphson: fem_solver.newton_raphson_failed_to_converge = True break if np.isnan(self.norm_residual) or self.norm_residual>1e06: fem_solver.newton_raphson_failed_to_converge = True break # USER DEFINED CRITERIA TO BREAK OUT OF NEWTON-RAPHSON if fem_solver.user_defined_break_func != None: if fem_solver.user_defined_break_func(Increment,Iter,self.norm_residual,self.rel_norm_residual, Tolerance): break # USER DEFINED CRITERIA TO STOP NEWTON-RAPHSON AND THE WHOLE ANALYSIS if fem_solver.user_defined_stop_func != None: if fem_solver.user_defined_stop_func(Increment,Iter,self.norm_residual,self.rel_norm_residual, Tolerance): fem_solver.newton_raphson_failed_to_converge = True break return Eulerx, Eulerp, K, Residual, velocities, accelerations
def Solve(self, formulation=None, mesh=None, material=None, boundary_condition=None, function_spaces=None, solver=None, Eulerx=None, Eulerp=None): """Main solution routine for LaplacianSolver """ # CHECK FOR ATTRIBUTE FOR LOWLEVEL ASSEMBLY if material.nature == "linear" and material.has_low_level_dispatcher and self.has_low_level_dispatcher: if hasattr(material, 'e'): if material.e is None or isinstance(material.e, float): if material.mtype == "IdealDielectric": material.e = material.eps_1 * np.eye( formulation.ndim, formulation.ndim) else: raise ValueError( "For optimise=True, you need to provide the material permittivity tensor (ndimxndim)" ) else: raise ValueError( "For optimise=True, you need to provide the material permittivity tensor (ndimxndim)" ) # 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 self.NRConvergence = { 'Increment_' + str(Increment): [] for Increment in range(self.number_of_load_increments) } # ALLOCATE FOR SOLUTION FIELDS if self.save_frequency == 1: # TotalDisp = np.zeros((mesh.points.shape[0],self.number_of_load_increments),dtype=np.float32) TotalDisp = np.zeros( (mesh.points.shape[0], self.number_of_load_increments), dtype=np.float64) else: TotalDisp = np.zeros( (mesh.points.shape[0], 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, material, solver, self) # ALLOCATE FOR GEOMETRY - GetDirichletBoundaryConditions CHANGES THE MESH # SO EULERX SHOULD BE ALLOCATED AFTERWARDS if Eulerx is None: Eulerx = np.copy(mesh.points) if Eulerp is None: Eulerp = np.zeros((mesh.points.shape[0])) # FIND PURE NEUMANN (EXTERNAL) NODAL FORCE VECTOR NeumannForces = boundary_condition.ComputeNeumannForces( mesh, material, function_spaces, compute_traction_forces=True, compute_body_forces=self.add_self_weight) # ASSEMBLE STIFFNESS MATRIX AND TRACTION FORCES FOR THE FIRST TIME if self.analysis_type == "static": K, TractionForces, _, _ = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp) else: pass 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': pass else: if self.iterative_technique == "newton_raphson" or self.iterative_technique == "modified_newton_raphson": TotalDisp = self.StaticSolver(function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, Eulerp, material, boundary_condition) return self.__makeoutput__(mesh, TotalDisp, formulation, function_spaces, material)
def ModifiedNewtonRaphson(self, function_spaces, formulation, solver, Increment, K, NodalForces, Residual, mesh, Eulerx, Eulerp, material, boundary_condition, AppliedDirichletInc): from Florence.FiniteElements.Assembly import AssembleInternalTractionForces Tolerance = self.newton_raphson_tolerance LoadIncrement = self.number_of_load_increments Iter = 0 # 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] Eulerp += IncDirichlet[:, -1] # ASSEMBLE STIFFNESS PER TIME STEP K, TractionForces = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[:2] 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] # GET ITERATIVE ELECTRIC POTENTIAL Eulerp += dU[:, -1] # RE-ASSEMBLE - COMPUTE INTERNAL TRACTION FORCES TractionForces = AssembleInternalTractionForces( self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp) # FIND THE RESIDUAL Residual[boundary_condition.columns_in] = TractionForces[boundary_condition.columns_in] -\ NodalForces[boundary_condition.columns_in] # SAVE THE NORM self.rel_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.rel_norm_residual), "\t Residual (rel) {0:>16.7g}".format(self.norm_residual)) # BREAK BASED ON RELATIVE NORM if np.abs(self.rel_norm_residual) < Tolerance: break # BREAK BASED ON INCREMENTAL SOLUTION - KEEP IT AFTER UPDATE if norm(dU) <= self.newton_raphson_solution_tolerance: 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 and formulation.fields == "electro_mechanics": # raise StopIteration("\n\nNewton Raphson did not converge! Maximum number of iterations reached.") warn( "\n\nNewton Raphson did not converge! Maximum number of iterations reached." ) self.newton_raphson_failed_to_converge = True break 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 # 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.rel_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.rel_norm_residual, Tolerance): self.newton_raphson_failed_to_converge = True break return Eulerx, Eulerp, K, Residual
def StaggeredSolver(self, function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, Eulerp, material, boundary_condition): Tolerance = self.newton_raphson_tolerance LoadIncrement = self.number_of_load_increments LoadFactor = 1. / LoadIncrement AppliedDirichletInc = np.zeros( boundary_condition.applied_dirichlet.shape[0], dtype=np.float64) # GET BOUNDARY CONDITIONS INFO FOR EACH INDIVIDUAL PROBLEM self.GetBoundaryInfo(K, boundary_condition, formulation) # SOLVE THE FIRST MECHANICAL PROBLEM nodal_forces_mech = np.zeros((self.mechanical_dofs.shape[0], 1)) residual_mech_neumann = -LoadFactor * NeumannForces[ self.mechanical_dofs, :] residual_mech = residual_mech_neumann - self.ApplyDirichlet( K[self.mechanical_dofs, :][:, self.mechanical_dofs], nodal_forces_mech, self.mech_out, self.mech_in, self.applied_dirichlet_mech, LoadFactor=LoadFactor) dUm = self.SolveMechanics(mesh, formulation, solver, K, residual_mech, LoadFactor, initial_solution=True) # INITIATE TRANSMITTIVE FORCES self.force_up = np.zeros(formulation.ndim * mesh.points.shape[0]) self.force_pu = np.zeros(mesh.points.shape[0]) # INITIATE ELECTRIC NODAL FORCES AND RESIDUAL nodal_forces_electric = NodalForces[self.electric_dofs] for Increment in range(LoadIncrement): t_increment = time() # UPDATE FIXED DOFs FOR ELECTROSTATICS DeltaF_electric = LoadFactor * NeumannForces[self.electric_dofs] nodal_forces_electric += DeltaF_electric residual_electric = -self.ApplyDirichlet( K[self.electric_dofs, :][:, self.electric_dofs], nodal_forces_electric, self.electric_out, self.electric_in, self.applied_dirichlet_electric, LoadFactor) # COMPUTE FORCE TO BE TRANSMITTED TO ELECTROSTATIC K_pu = K[self.electric_dofs, :][:, self.mechanical_dofs] self.force_pu = K_pu.dot(dUm.flatten()) # STORE PREVIOUS ELECTRIC POTENTIAL AT THIS INCREMENT Eulerp_n = np.copy(Eulerp) # APPLY INCREMENTAL POTENTIAL FOR FIXED POTENTIAL DoFs applied_dirichlet_electric_inc = LoadFactor * self.applied_dirichlet_electric Eulerp[self.electric_out] += applied_dirichlet_electric_inc # GET ONLY NORM OF FIXED DOFs (THAT IS WHERE RESIDUAL FORCES GET GENERATED) if Increment == 0: self.NormForces = la.norm(residual_electric) # AVOID DIVISION BY ZERO if np.abs(self.NormForces) < 1e-14: self.NormForces = 1e-14 Ke = deepcopy(K) residual_mech_from_elec = np.zeros( (self.mechanical_dofs.shape[0], 1)) # DeltaF_mech = LoadFactor*NeumannForces[self.mechanical_dofs] # nodal_forces_mech += DeltaF_mech Iter = 0 # ENTER NEWTON-RAPHSON FOR ELECTROSTATICS while np.abs( la.norm(residual_electric[self.electric_in]) / self.NormForces) > Tolerance: # SOLVE ELECTROSTATIC PROBLEM ITERATIVELY dUe = self.SolveElectrostatics(Ke, residual_electric, formulation, solver, Iter) # UPDATE EULERIAN POTENTIAL - GET ITERATIVE ELECTRIC POTENTIAL Eulerp += dUe # RE-ASSEMBLE - COMPUTE INTERNAL TRACTION FORCES FOR ELECTROSTATICS Ke, TractionForces = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[:2] # FIND THE ITERATIVE RESIDUAL residual_electric[self.electric_in] = TractionForces[ self.columns_in_electric] - nodal_forces_electric[ self.electric_in] # traction_forces_mech += TractionForces[self.mechanical_dofs] residual_mech_from_elec += TractionForces[ self.mechanical_dofs] - nodal_forces_mech self.NRConvergence['Increment_'+str(Increment)] = np.append(self.NRConvergence['Increment_'+str(Increment)],\ np.abs(la.norm(residual_electric[self.electric_in])/self.NormForces)) print('Iteration number', Iter, 'for load increment', Increment, 'with a residual of \t\t', \ np.abs(la.norm(residual_electric[self.electric_in])/self.NormForces)) # UPDATE ITERATION NUMBER Iter += 1 if Iter == self.maximum_iteration_for_newton_raphson or self.NRConvergence[ 'Increment_' + str(Increment)][-1] > 500: self.newton_raphson_failed_to_converge = True break if np.isnan( np.abs( la.norm(residual_electric[self.electric_in]) / self.NormForces)): self.newton_raphson_failed_to_converge = True break if self.newton_raphson_failed_to_converge: print( "Solver blew up! Norm of incremental solution is too large" ) solver.condA = np.NAN Increment = Increment if Increment != 0 else 1 TotalDisp = TotalDisp[:, :, :Increment] self.number_of_load_increments = Increment return TotalDisp # COMPUTE FORCE TO BE TRANSMITTED TO MECHANICS K_up = Ke[self.mechanical_dofs, :][:, self.electric_dofs] dUe = Eulerp - Eulerp_n self.force_up = K_up.dot(dUe) # self.force_up = K_up.dot(dUe[:,None]) # print(self.force_up) # if Increment>0: # nodal_forces_mech = np.zeros_like(nodal_forces_mech) # residual_mech = residual_mech_neumann - self.ApplyDirichlet(K[self.mechanical_dofs,:][:,self.mechanical_dofs], # nodal_forces_mech, self.mech_out, self.mech_in, self.applied_dirichlet_mech, LoadFactor=LoadFactor) nodal_forces_mech = np.zeros_like(nodal_forces_mech) residual_mech = residual_mech_from_elec + residual_mech_neumann - \ self.ApplyDirichlet(K[self.mechanical_dofs,:][:,self.mechanical_dofs], nodal_forces_mech, self.mech_out, self.mech_in, self.applied_dirichlet_mech, LoadFactor=LoadFactor) # SOLVE MECHANICS PROBLEM WITH OLD GEOMETRY (K), AND THE FORCE self.force_up AS A RESIDUAL dUm = self.SolveMechanics(mesh, formulation, solver, K, residual_mech, LoadFactor, initial_solution=False) # dUm = self.SolveMechanics(mesh, formulation, solver, K, traction_forces_mech, LoadFactor, initial_solution=False) # UPDATE GEOMETRY INCREMENTALLY Eulerx += dUm # UPDATE SOLUTION FOR THE CURRENT LOAD INCREMENT TotalDisp[:, :formulation.ndim, Increment] = Eulerx - mesh.points TotalDisp[:, -1, Increment] = Eulerp K = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[0] # LOG REULTS self.LogSave(formulation, TotalDisp, Increment) print('\nFinished Load increment', Increment, 'in', time() - t_increment, 'seconds') # BREAK AT A SPECIFICED LOAD INCREMENT IF ASKED FOR if self.break_at_increment != -1 and self.break_at_increment is not None: if self.break_at_increment == Increment: if self.break_at_increment < LoadIncrement - 1: print("\nStopping at increment {} as specified\n\n". format(Increment)) TotalDisp = TotalDisp[:, :, :Increment] self.number_of_load_increments = Increment break return TotalDisp
def Solve(self, formulation=None, mesh=None, material=None, boundary_condition=None, function_spaces=None, solver=None): """Main solution routine for FEMSolver """ # CHECK DATA CONSISTENCY #---------------------------------------------------------------------------# function_spaces, solver = self.__checkdata__(material, boundary_condition, formulation, mesh, function_spaces, solver) #---------------------------------------------------------------------------# caller = inspect.getouterframes(inspect.currentframe(), 2)[1][3] if caller != "Solve": self.PrintPreAnalysisInfo(mesh, formulation) #---------------------------------------------------------------------------# # INITIATE DATA FOR NON-LINEAR 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 self.NRConvergence = { 'Increment_' + str(Increment): [] for Increment in range(self.number_of_load_increments) } # ALLOCATE FOR SOLUTION FIELDS TotalDisp = np.zeros((mesh.points.shape[0], formulation.nvar, self.number_of_load_increments), 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, material, solver, self) # ALLOCATE FOR GEOMETRY - GetDirichletBoundaryConditions CHANGES THE MESH # SO EULERX SHOULD BE ALLOCATED AFTERWARDS Eulerx = np.copy(mesh.points) Eulerp = np.zeros((mesh.points.shape[0])) # FIND PURE NEUMANN (EXTERNAL) NODAL FORCE VECTOR NeumannForces = boundary_condition.ComputeNeumannForces( mesh, material, function_spaces) # ASSEMBLE STIFFNESS MATRIX AND TRACTION FORCES K, TractionForces = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[:2] print('Finished all pre-processing stage. Time elapsed was', time() - tAssembly, 'seconds') TotalDisp = self.StaggeredSolver(function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, Eulerp, material, boundary_condition) return self.__makeoutput__(mesh, TotalDisp, formulation, function_spaces, material)
Eulerp = np.zeros((formulation.meshes[0].points.shape[0])) # FIND PURE NEUMANN (EXTERNAL) NODAL FORCE VECTOR NeumannForces = boundary_condition.ComputeNeumannForces(mesh, material, function_spaces, compute_traction_forces=True, compute_body_forces=self.add_self_weight) # NeumannForces = np.zeros((mesh.points.shape[0]*formulation.ndim)) # ASSEMBLE STIFFNESS MATRIX AND TRACTION FORCES if self.analysis_type != 'static': if formulation.fields == "couple_stress" or formulation.fields == "flexoelectric": K, TractionForces, _, M = formulation.Assemble(self, material, Eulerx, Eulerw, Eulers, Eulerp) elif formulation.fields == "mechanics" or formulation.fields == "electro_mechanics": # STANDARD MECHANINCS ELECTROMECHANICS DYNAMIC ANALYSIS ARE DISPATCHED HERE from Florence.FiniteElements.Assembly import Assemble fspace = function_spaces[0] if (mesh.element_type=="hex" or mesh.element_type=="quad") else function_spaces[1] K, TractionForces, _, M = Assemble(self, fspace, formulation, mesh, material, Eulerx, Eulerp) else: K, TractionForces = formulation.Assemble(self, material, Eulerx, Eulerw, Eulers, Eulerp)[:2] print('Finished all pre-processing stage. Time elapsed was', time()-tAssembly, 'seconds') if self.analysis_type != 'static': <<<<<<< HEAD ======= boundary_condition.ConvertStaticsToDynamics(mesh, self.number_of_load_increments) >>>>>>> upstream/master TotalDisp, TotalW, TotalS = self.DynamicSolver(formulation, solver, K, M, NeumannForces, NodalForces, Residual, mesh, TotalDisp, TotalW, TotalS, Eulerx, Eulerw, Eulers, Eulerp, material, boundary_condition) else:
def NewtonRaphsonArchLength(self, function_spaces, formulation, solver, Increment, K, NodalForces, Residual, mesh, Eulerx, Eulerp, material, boundary_condition, AppliedDirichletInc, NeumannForces, TotalDisp): Tolerance = self.newton_raphson_tolerance LoadIncrement = self.number_of_load_increments Iter = 0 # 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] Eulerp += IncDirichlet[:, -1] # Predictor if Increment == 0: # GET THE REDUCED SYSTEM OF EQUATIONS # K_b, F_b = boundary_condition.GetReducedMatrices(K,self.accumulated_load_factor*NeumannForces)[:2] K_b, F_b = boundary_condition.GetReducedMatrices(K, NeumannForces)[:2] # SOLVE THE SYSTEM sol = solver.Solve(K_b, F_b) # GET ITERATIVE SOLUTION dU = boundary_condition.UpdateFreeDoFs(sol, K.shape[0], formulation.nvar) # self.incremental_load_factor = 1./LoadIncrement else: dU = TotalDisp[:, :, Increment - 1] * self.arc_length_scaling_factor self.incremental_load_factor *= self.arc_length_scaling_factor self.accumulated_load_factor += self.incremental_load_factor # UPDATE THE EULERIAN COMPONENTS Eulerx += dU[:, :formulation.ndim] Eulerp += dU[:, -1] while self.norm_residual > Tolerance or Iter == 0: # GET THE REDUCED SYSTEM OF EQUATIONS K_b, F_b = boundary_condition.GetReducedMatrices(K, NeumannForces)[:2] # SOLVE THE SYSTEM sol = solver.Solve(K_b, F_b) # GET ITERATIVE SOLUTION dU1 = boundary_condition.UpdateFreeDoFs(sol, K.shape[0], formulation.nvar) # 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 dU2 = boundary_condition.UpdateFreeDoFs(sol, K.shape[0], formulation.nvar) iterative_load_factor = -np.dot(dU.flatten(), dU2.flatten()) / np.dot( dU.flatten(), dU1.flatten()) ddU = iterative_load_factor * dU1 + dU2 # print(ddlam) # dU = dU2 # UPDATE THE EULERIAN COMPONENTS self.incremental_load_factor += iterative_load_factor self.accumulated_load_factor += iterative_load_factor dU[:, :] += ddU[:, :] Eulerx += ddU[:, :formulation.ndim] Eulerp += ddU[:, -1] # Eulerx += dU[:,:formulation.ndim] # Eulerp += dU[:,-1] # print(self.accumulated_load_factor) # RE-ASSEMBLE - COMPUTE INTERNAL TRACTION FORCES K, TractionForces = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[:2] # FIND THE RESIDUAL # Residual[boundary_condition.columns_in] = TractionForces[boundary_condition.columns_in] -\ # NodalForces[boundary_condition.columns_in] - NeumannForces[boundary_condition.columns_in]*self.accumulated_load_factor Residual[boundary_condition.columns_in] = TractionForces[boundary_condition.columns_in] -\ NeumannForces[boundary_condition.columns_in]*self.accumulated_load_factor # SAVE THE NORM self.rel_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.rel_norm_residual), "\t Residual (rel) {0:>16.7g}".format(self.norm_residual)) if np.abs(self.rel_norm_residual) < Tolerance: break # UPDATE ITERATION NUMBER Iter += 1 self.arc_length_scaling_factor = 0.5**(0.25 * (Iter - 5)) if Iter == self.maximum_iteration_for_newton_raphson and formulation.fields == "electro_mechanics": # raise StopIteration("\n\nNewton Raphson did not converge! Maximum number of iterations reached.") warn( "\n\nNewton Raphson did not converge! Maximum number of iterations reached." ) self.newton_raphson_failed_to_converge = True break 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 # 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.rel_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.rel_norm_residual, Tolerance): self.newton_raphson_failed_to_converge = True break return Eulerx, Eulerp, K, Residual # def NewtonRaphsonArchLength(self, function_spaces, formulation, solver, # Increment, K, NodalForces, Residual, mesh, Eulerx, Eulerp, material, # boundary_condition, AppliedDirichletInc, DeltaF, TotalDisp): # Tolerance = self.newton_raphson_tolerance # LoadIncrement = self.number_of_load_increments # LoadFactor = 1./LoadIncrement # accumulated_load_factor = Increment/LoadIncrement # Iter = 0 # dL = 1. # psi = 1. # # NodalForces = DeltaF # Dlam = 0. # dU = np.zeros((mesh.points.shape[0],formulation.nvar)) # dU_b = np.zeros((mesh.points.shape[0],formulation.nvar)) # # SOLVE WITH INCREMENTAL LOAD # K_b, DF_b = boundary_condition.GetReducedMatrices(K,NodalForces)[:2] # dU_t = solver.Solve(K_b,DF_b) # dU_t = boundary_condition.UpdateFreeDoFs(dU_t,K.shape[0],formulation.nvar) # # print(NodalForces) # # dU = IncDirichlet # # GET TOTAL ITERATIVE SOLUTION # # dU = dU_actual + LoadFactor*dU_current # # GET ARC LENGTH QUADRATIC EQUATIONS COEFFICIENTS # # c1 = np.dot(dU.ravel(),dU.ravel()) + psi**2 * np.dot(DeltaF.ravel(),DeltaF.ravel()) # # c2 = 2.*np.dot(DU.ravel()+dU_actual.ravel(),dU_current.ravel()) + 2.*psi**2 * LoadFactor * np.dot(DeltaF.ravel(),DeltaF.ravel()) # # c3 = np.dot((DU+dU_actual).ravel(),(DU+dU_actual).ravel()) + psi**2 * LoadFactor**2 * np.dot(DeltaF.ravel(),DeltaF.ravel()) - dL**2 # # coeffs = [c1,c2,c3] # # c1 = np.dot(dU_t.ravel(),dU_t.ravel()) + psi**2 * np.dot(NodalForces.ravel(),NodalForces.ravel()) # # c2 = 2.*np.dot(dU.ravel()+dU_b.ravel(),dU_t.ravel()) + 2.*psi**2 * Dlam * np.dot(NodalForces.ravel(),NodalForces.ravel()) # # c3 = np.dot((dU+dU_b).ravel(),(dU+dU_b).ravel()) + psi**2 * Dlam**2 * np.dot(NodalForces.ravel(),NodalForces.ravel()) - dL**2 # # coeffs = [c1,c2,c3] # # # FIND THE NEW LOAD FACTOR # # dlams = np.roots(coeffs) # # dlam = np.real(dlams.max()) # # # print(c1,c2,c3,dlams, dlam) # # # CORRECTOR # # dU_iter = dU_b + dlam*dU_t # # # print (dU_iter) # # # exit() # # 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] # Eulerp += IncDirichlet[:,-1] # # Eulerx += IncDirichlet[:,:formulation.ndim] + dU_iter[:,:formulation.ndim] # # Eulerp += IncDirichlet[:,-1] + dU_iter[:,-1] # # accumulated_load_factor += dlam # # if Increment>0: # # DU = TotalDisp[:,:,Increment] - TotalDisp[:,:,Increment-1] # # else: # # DU = np.zeros((mesh.points.shape[0],formulation.nvar)) # # DU = np.zeros((mesh.points.shape[0],formulation.nvar)) # 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_b = boundary_condition.UpdateFreeDoFs(sol,K.shape[0],formulation.nvar) # dU = boundary_condition.UpdateFreeDoFs(sol,K.shape[0],formulation.nvar) # # print(dlams) # # exit() # # LoadFactor += np.real(np.max(dlams)) # # print(LoadFactor) # c1 = np.dot(dU_t.ravel(),dU_t.ravel()) + psi**2 * np.dot(NodalForces.ravel(),NodalForces.ravel()) # c2 = 2.*np.dot(dU.ravel()+dU_b.ravel(),dU_t.ravel()) + 2.*psi**2 * Dlam * np.dot(NodalForces.ravel(),NodalForces.ravel()) # c3 = np.dot((dU+dU_b).ravel(),(dU+dU_b).ravel()) + psi**2 * Dlam**2 * np.dot(NodalForces.ravel(),NodalForces.ravel()) - dL**2 # coeffs = [c1,c2,c3] # # FIND THE NEW LOAD FACTOR # dlams = np.roots(coeffs) # dlam = np.real(dlams.max()) # print(dlam) # # CORRECTOR # dU_iter = dU_b + dlam*dU_t # accumulated_load_factor += dlam # # UPDATE THE EULERIAN COMPONENTS # Eulerx += dU[:,:formulation.ndim] # Eulerp += dU[:,-1] # # Eulerx += dU_iter[:,:formulation.ndim] # # Eulerp += dU_iter[:,-1] # # RE-ASSEMBLE - COMPUTE INTERNAL TRACTION FORCES # K, TractionForces = Assemble(self, function_spaces[0], formulation, mesh, material, # Eulerx,Eulerp)[:2] # # FIND THE RESIDUAL # Residual[boundary_condition.columns_in] = TractionForces[boundary_condition.columns_in] -\ # NodalForces[boundary_condition.columns_in] # # SAVE THE NORM # self.rel_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.rel_norm_residual), # "\t Residual (rel) {0:>16.7g}".format(self.norm_residual)) # if np.abs(self.rel_norm_residual) < Tolerance: # break # # UPDATE ITERATION NUMBER # Iter +=1 # if Iter==self.maximum_iteration_for_newton_raphson and formulation.fields == "electro_mechanics": # # raise StopIteration("\n\nNewton Raphson did not converge! Maximum number of iterations reached.") # warn("\n\nNewton Raphson did not converge! Maximum number of iterations reached.") # self.newton_raphson_failed_to_converge = True # break # 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 # # 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.rel_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.rel_norm_residual, Tolerance): # self.newton_raphson_failed_to_converge = True # break # return Eulerx, Eulerp, K, Residual
def NewtonRaphsonDisplacementControl(self, function_spaces, formulation, solver, Increment, K, NodalForces, Residual, mesh, Eulerx, Eulerp, material, boundary_condition, AppliedDirichletInc, NeumannForces, TotalForce, TotalDisp): Tolerance = self.newton_raphson_tolerance LoadIncrement = self.number_of_load_increments Iter = 0 iterative_load_factor = 0.0 # 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] Eulerp += IncDirichlet[:, -1] 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) # GET THE REDUCED SYSTEM OF EQUATIONS F_bb = boundary_condition.GetReducedMatrices(K, TotalForce)[1] # SOLVE THE SYSTEM sol_b = solver.Solve(K_b, F_bb) # GET ITERATIVE SOLUTION dU_b = boundary_condition.UpdateFreeDoFs(sol_b, K.shape[0], formulation.nvar) # ratio = np.max(np.abs(dU))/np.max(np.abs(dU_b)) # ratio = np.max(np.abs(dU))/self.max_prescribed_displacement max_occured_displacement = np.max(np.abs(dU_b)) ratio = np.max(np.abs(dU)) / max_occured_displacement iterative_load_factor += ratio # self.local_load_factor += iterative_load_factor print(ratio) # print(iterative_load_factor) # print(self.local_load_factor) # print(self.accumulated_load_factor) # # GET THE REDUCED SYSTEM OF EQUATIONS # K_b, F_b = boundary_condition.GetReducedMatrices(K, Residual - ratio*TotalForce)[: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 # Eulerx += dU[:,:formulation.ndim] # Eulerp += dU[:,-1] # UPDATE THE EULERIAN COMPONENTS Eulerx += dU[:, :formulation.ndim] + ratio * dU_b[:, :formulation.ndim] Eulerp += dU[:, -1] + ratio * dU_b[:, -1] # RE-ASSEMBLE - COMPUTE INTERNAL TRACTION FORCES K, TractionForces = Assemble(self, function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[:2] # FIND THE RESIDUAL Residual[boundary_condition.columns_in] = TractionForces[boundary_condition.columns_in] -\ NodalForces[boundary_condition.columns_in] - ratio*TotalForce[boundary_condition.columns_in] # SAVE THE NORM self.rel_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.rel_norm_residual), "\t Residual (rel) {0:>16.7g}".format(self.norm_residual)) if np.abs(self.rel_norm_residual) < Tolerance: break # UPDATE ITERATION NUMBER Iter += 1 if Iter == self.maximum_iteration_for_newton_raphson and formulation.fields == "electro_mechanics": # raise StopIteration("\n\nNewton Raphson did not converge! Maximum number of iterations reached.") warn( "\n\nNewton Raphson did not converge! Maximum number of iterations reached." ) self.newton_raphson_failed_to_converge = True break 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 # 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.rel_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.rel_norm_residual, Tolerance): self.newton_raphson_failed_to_converge = True break if self.accumulated_load_factor >= 1.0: print("Load factor: 1.0, Breaking") self.newton_raphson_failed_to_converge = True break self.local_load_factor += iterative_load_factor return Eulerx, Eulerp, K, Residual
def Solver(self, function_spaces, formulation, solver, K, M, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, Eulerp, material, boundary_condition, fem_solver): # CHECK FORMULATION if formulation.fields != "mechanics" and formulation.fields != "electro_mechanics": raise NotImplementedError("Linear implicit solver for {} is not available".format(formulation.fields)) if formulation.fields == "electro_mechanics": warn("Linear implicit solver for electromechanics formulation is not thoroughly checked and may return incorrect results. " "Please use nonlinear explicit dynamic solver instead") # GET BOUNDARY CONDITIONS INFROMATION self.GetBoundaryInfo(mesh, formulation, boundary_condition) LoadIncrement = fem_solver.number_of_load_increments LoadFactor = fem_solver.total_time/LoadIncrement post_process = PostProcess(formulation.ndim,formulation.nvar) post_process.SetAnalysis(analysis_type=fem_solver.analysis_type, analysis_nature=fem_solver.analysis_nature) if NeumannForces.ndim == 2 and NeumannForces.shape[1]==1: tmp = np.zeros((NeumannForces.shape[0],LoadIncrement)) tmp[:,0] = NeumannForces[:,0] NeumannForces = tmp dU = boundary_condition.UpdateFixDoFs(boundary_condition.applied_dirichlet[:,0], mesh.points.shape[0]*formulation.nvar, formulation.nvar) TotalDisp[:,:formulation.nvar,0] = dU # INITIALISE VELOCITY AND ACCELERATION velocities = np.zeros((mesh.points.shape[0]*formulation.ndim)) accelerations = np.zeros((mesh.points.shape[0]*formulation.ndim)) # COMPUTE DAMPING MATRIX BASED ON MASS D = 0.0 if fem_solver.include_physical_damping: D = fem_solver.damping_factor*M if formulation.fields == "electro_mechanics": M_mech = M[self.mechanical_dofs,:][:,self.mechanical_dofs] if fem_solver.include_physical_damping: D_mech = D[self.mechanical_dofs,:][:,self.mechanical_dofs] else: M_mech = M D_mech = D # COMPUTE INITIAL ACCELERATION FOR TIME STEP 0 Residual = np.zeros_like(Residual) InitResidual = Residual + NeumannForces[:,0][:,None] if formulation.fields == "electro_mechanics": accelerations[:] = solver.Solve(M_mech, -InitResidual[self.mechanical_dofs].ravel()) else: accelerations[:] = solver.Solve(M, InitResidual.ravel() ) # COMPUTE AUGMENTED K (INCLUDES INERTIA EFFECT) K += (self.gamma/self.beta/LoadFactor)*D + (1./self.beta/LoadFactor**2)*M # GET REDUCED VARIABLES K_b, F_b, _ = boundary_condition.GetReducedMatrices(K,Residual) if self.lump_rhs: M_mech = M_mech.sum(axis=1).A.ravel() # FOR CSR # M_mech = M_mech.sum(axis=0).ravel() # FOR CSC if self.include_physical_damping: D_mech = D_mech.sum(axis=1).A.ravel() reuse_factorisation = False if formulation.fields == "electro_mechanics" else True for Increment in range(1,LoadIncrement): t_increment=time() # FIXED INCREMENTAL DIRICHLET AppliedDirichletInc = boundary_condition.applied_dirichlet[:,Increment-1] # APPLY NEUMANN BOUNDARY CONDITIONS DeltaF = NeumannForces[:,Increment][:,None] NodalForces = DeltaF # ACCUMULATED FORCE if fem_solver.include_physical_damping: if self.lump_rhs: Residual[self.mechanical_dofs,0] = (1./self.beta/LoadFactor**2)*M_mech*TotalDisp[:,:formulation.ndim,Increment-1].ravel() +\ (1./self.beta/LoadFactor)*M_mech*velocities + (0.5/self.beta - 1.)*M_mech*accelerations +\ (self.gamma/self.beta/LoadFactor)*D_mech*TotalDisp[:,:formulation.ndim,Increment-1].ravel() +\ (self.gamma/self.beta - 1.)*D_mech*velocities -\ LoadFactor*((1-self.gamma)-self.gamma*(0.5/self.beta - 1.))*D_mech*accelerations else: Residual[self.mechanical_dofs,0] = (1./self.beta/LoadFactor**2)*M_mech.dot(TotalDisp[:,:formulation.ndim,Increment-1].ravel()) +\ (1./self.beta/LoadFactor)*M_mech.dot(velocities) + (0.5/self.beta - 1.)*M_mech.dot(accelerations) +\ (self.gamma/self.beta/LoadFactor)*D_mech.dot(TotalDisp[:,:formulation.ndim,Increment-1].ravel()) +\ (self.gamma/self.beta - 1.)*D_mech.dot(velocities) -\ LoadFactor*((1-self.gamma)-self.gamma*(0.5/self.beta - 1.))*D_mech.dot(accelerations) else: if self.lump_rhs: Residual[self.mechanical_dofs,0] = (1./self.beta/LoadFactor**2)*M_mech*TotalDisp[:,:formulation.ndim,Increment-1].ravel() +\ (1./self.beta/LoadFactor)*M_mech*velocities + (0.5/self.beta - 1.)*M_mech*accelerations else: Residual[self.mechanical_dofs,0] = (1./self.beta/LoadFactor**2)*M_mech.dot(TotalDisp[:,:formulation.ndim,Increment-1].ravel()) +\ (1./self.beta/LoadFactor)*M_mech.dot(velocities) + (0.5/self.beta - 1.)*M_mech.dot(accelerations) Residual += DeltaF if formulation.fields == "electro_mechanics": K = Assemble(fem_solver,function_spaces[0], formulation, mesh, material, Eulerx, Eulerp)[0] K += (self.gamma/self.beta/LoadFactor)*D + (1./self.beta/LoadFactor**2)*M # CHECK CONTACT AND ASSEMBLE IF DETECTED if fem_solver.has_contact: Eulerx = mesh.points + TotalDisp[:,:formulation.ndim,Increment-1] TractionForcesContact = np.zeros_like(Residual) TractionForcesContact = fem_solver.contact_formulation.AssembleTractions(mesh,material,Eulerx).ravel()*LoadFactor if formulation.fields == "electro_mechanics" or formulation.fields == "flexoelectric": Residual[self.mechanical_dofs,0] -= TractionForcesContact elif formulation.fields == "mechanics" or formulation.fields == "couple_stress": Residual[:,0] -= TractionForcesContact else: raise NotImplementedError("Contact algorithm for {} is not available".format(formulation.fields)) # REDUCED ACCUMULATED FORCE if formulation.fields == "mechanics": F_b = boundary_condition.ApplyDirichletGetReducedMatrices(K,Residual, boundary_condition.applied_dirichlet[:,Increment],LoadFactor=1.0, mass=M,only_residual=True)[boundary_condition.columns_in,0] else: K_b, F_b = boundary_condition.ApplyDirichletGetReducedMatrices(K,Residual, boundary_condition.applied_dirichlet[:,Increment],LoadFactor=1.0, mass=M)[:2] # SOLVE THE SYSTEM sol = solver.Solve(K_b, F_b, reuse_factorisation=reuse_factorisation) dU = post_process.TotalComponentSol(sol, boundary_condition.columns_in, boundary_condition.columns_out, AppliedDirichletInc,0,K.shape[0]) # STORE TOTAL SOLUTION DATA TotalDisp[:,:,Increment] += dU # UPDATE VELOCITY AND ACCELERATION accelerations_old = np.copy(accelerations) accelerations = (1./self.beta/LoadFactor**2)*(TotalDisp[:,:formulation.ndim,Increment] -\ TotalDisp[:,:formulation.ndim,Increment-1]).ravel() -\ 1./self.beta/LoadFactor*velocities + (1.-0.5/self.beta)*accelerations_old velocities += LoadFactor*(self.gamma*accelerations + (1-self.gamma)*accelerations_old) # UPDATE Eulerx += dU[:,:formulation.ndim] Eulerp += dU[:,-1] # LOG REQUESTS fem_solver.LogSave(formulation, TotalDisp, Increment) # BREAK AT A SPECIFICED LOAD 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 == Increment: if fem_solver.break_at_increment < LoadIncrement - 1: print("\nStopping at increment {} as specified\n\n".format(Increment)) TotalDisp = TotalDisp[:,:,:Increment] fem_solver.number_of_load_increments = Increment break # STORE THE INFORMATION IF THE SOLVER BLOWS UP if Increment > 0: U0 = TotalDisp[:,:,Increment-1].ravel() U = TotalDisp[:,:,Increment].ravel() tol = 1e200 if Increment < 5 else 10. if np.isnan(norm(U)) or np.abs(U.max()/(U0.max()+1e-14)) > tol: print("Solver blew up! Norm of incremental solution is too large") TotalDisp = TotalDisp[:,:,:Increment] fem_solver.number_of_load_increments = Increment break print('Finished Load increment', Increment, 'in', time()-t_increment, 'seconds\n') solver.CleanUp() return TotalDisp
def Solve(self, formulation=None, mesh=None, material=None, boundary_condition=None, function_spaces=None, solver=None, contact_formulation=None): """Main solution routine for FEMSolver """ # CHECK DATA CONSISTENCY mesh = formulation.meshes[0] #---------------------------------------------------------------------------# function_spaces, solver = self.__checkdata__( material, boundary_condition, formulation, mesh, function_spaces, solver, contact_formulation=contact_formulation) #---------------------------------------------------------------------------# caller = inspect.getouterframes(inspect.currentframe(), 2)[1][3] if caller != "Solve": self.PrintPreAnalysisInfo(mesh, formulation) #---------------------------------------------------------------------------# # INITIATE DATA FOR NON-LINEAR 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 self.NRConvergence = { 'Increment_' + str(Increment): [] for Increment in range(self.number_of_load_increments) } # ALLOCATE FOR SOLUTION FIELDS TotalDisp = np.zeros((mesh.points.shape[0], formulation.nvar, self.number_of_load_increments), dtype=np.float64) TotalW = np.zeros((formulation.meshes[1].points.shape[0], formulation.ndim, self.number_of_load_increments), dtype=np.float64) TotalS = np.zeros((formulation.meshes[2].points.shape[0], formulation.ndim, self.number_of_load_increments), dtype=np.float64) # TotalDisp = np.zeros((mesh.points.shape[0],int(formulation.ndim**2),self.number_of_load_increments),dtype=np.float64) # PRE-ASSEMBLY if caller != "Solve": 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, material, solver, self) # GET BOUNDARY INFO self.GetBoundaryInfo(mesh, formulation, boundary_condition) # ALLOCATE FOR GEOMETRY - GetDirichletBoundaryConditions CHANGES THE MESH # SO EULERX SHOULD BE ALLOCATED AFTERWARDS Eulerx = np.copy(formulation.meshes[0].points) Eulerw = np.zeros_like(formulation.meshes[1].points) Eulers = np.zeros_like(formulation.meshes[1].points) Eulerp = np.zeros((formulation.meshes[0].points.shape[0])) # FIND PURE NEUMANN (EXTERNAL) NODAL FORCE VECTOR NeumannForces = boundary_condition.ComputeNeumannForces( mesh, material, function_spaces, compute_traction_forces=True, compute_body_forces=self.add_self_weight) # NeumannForces = np.zeros((mesh.points.shape[0]*formulation.ndim)) # ASSEMBLE STIFFNESS MATRIX AND TRACTION FORCES if self.analysis_type != 'static': if formulation.fields == "couple_stress" or formulation.fields == "flexoelectric": K, TractionForces, _, M = formulation.Assemble( self, material, Eulerx, Eulerw, Eulers, Eulerp) elif formulation.fields == "mechanics" or formulation.fields == "electro_mechanics": # STANDARD MECHANINCS ELECTROMECHANICS DYNAMIC ANALYSIS ARE DISPATCHED HERE from Florence.FiniteElements.Assembly import Assemble fspace = function_spaces[0] if ( mesh.element_type == "hex" or mesh.element_type == "quad") else function_spaces[1] K, TractionForces, _, M = Assemble(self, fspace, formulation, mesh, material, Eulerx, Eulerp) else: K, TractionForces = formulation.Assemble(self, material, Eulerx, Eulerw, Eulers, Eulerp)[:2] print('Finished all pre-processing stage. Time elapsed was', time() - tAssembly, 'seconds') if self.analysis_type != 'static': boundary_condition.ConvertStaticsToDynamics( mesh, self.number_of_load_increments) TotalDisp, TotalW, TotalS = self.DynamicSolver( formulation, solver, K, M, NeumannForces, NodalForces, Residual, mesh, TotalDisp, TotalW, TotalS, Eulerx, Eulerw, Eulers, Eulerp, material, boundary_condition) else: TotalDisp, TotalW, TotalS = self.StaticSolver( formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, TotalW, TotalS, Eulerx, Eulerw, Eulers, Eulerp, material, boundary_condition) solution = self.__makeoutput__(mesh, TotalDisp, formulation, function_spaces, material) if formulation.fields == "couple_stress" or formulation.fields == "flexoelectric": solution.solW = TotalW solution.solS = TotalS return solution