Пример #1
0
    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
Пример #3
0
    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)