/********************************************************************************/ /* 888888 888888888 88 888 88888 888 888 88888888 */ /* 8 8 8 8 8 8 8 8 8 8 */ /* 8 8 8 8 8 8 8 8 8 */ /* 8 888888888 8 8 8 8 8 8 8888888 */ /* 8 8888 8 8 8 8 8 8 8 8 */ /* 8 8 8 8 8 8 8 8 8 8 */ /* 888888 888888888 888 88 88888 88888888 88888888 */ /* */ /* A Three-Dimensional General Purpose Semiconductor Simulator. */ /* */ /* */ /* Copyright (C) 2007-2008 */ /* Cogenda Pte Ltd */ /* */ /* Please contact Cogenda Pte Ltd for license information */ /* */ /* Author: Gong Ding gdiso@ustc.edu */ /* */ /********************************************************************************/ #include #include "fvm_nonlinear_solver.h" #include "parallel.h" //-------------------------------------------------------------------- // Functions with C linkage to pass to PETSc. PETSc will call these // methods as needed. // // Since they must have C linkage they have no knowledge of a namespace. // Give them an obscure name to avoid namespace pollution. extern "C" { //------------------------------------------------------------------- // this function is called by PETSc at the end of each nonlinear step static PetscErrorCode __genius_petsc_snes_monitor (SNES, PetscInt its, PetscReal fnorm, void *ctx) { int ierr=0; // convert void* to FVM_NonlinearSolver* FVM_NonlinearSolver * nonlinear_solver = (FVM_NonlinearSolver *)ctx; nonlinear_solver->petsc_snes_monitor(its, fnorm); return ierr; } //--------------------------------------------------------------- // this function is called by PETSc to test for convergence of the nonlinear iterative solution. static PetscErrorCode __genius_petsc_snes_convergence_test (SNES, PetscInt its, PetscReal xnorm, PetscReal gnorm, PetscReal fnorm, SNESConvergedReason *reason, void *ctx) { int ierr=0; // convert void* to FVM_NonlinearSolver* FVM_NonlinearSolver * nonlinear_solver = (FVM_NonlinearSolver *)ctx; nonlinear_solver->petsc_snes_convergence_test(its, xnorm, gnorm, fnorm, reason); return ierr; } //--------------------------------------------------------------- // this function is called by PETSc to test for convergence of the linear iterative solution. static PetscErrorCode __genius_petsc_ksp_convergence_test(KSP, PetscInt its, PetscReal rnorm, KSPConvergedReason* reason, void *ctx) { int ierr=0; // convert void* to FVM_NonlinearSolver* FVM_NonlinearSolver * nonlinear_solver = (FVM_NonlinearSolver *)ctx; nonlinear_solver->petsc_ksp_convergence_test(its, rnorm, reason); return ierr; } //--------------------------------------------------------------- // this function is called by PETSc to evaluate the residual at X static PetscErrorCode __genius_petsc_snes_residual (SNES, Vec x, Vec f, void *ctx) { int ierr=0; // convert void* to FVM_NonlinearSolver* FVM_NonlinearSolver * nonlinear_solver = (FVM_NonlinearSolver *)ctx; nonlinear_solver->build_petsc_sens_residual(x, f); return ierr; } //--------------------------------------------------------------- // this function is called by PETSc to evaluate the Jacobian at X static PetscErrorCode __genius_petsc_snes_jacobian (SNES, Vec x, Mat *jac, Mat *pc, MatStructure *msflag, void *ctx) { int ierr=0; // convert void* to FVM_NonlinearSolver* FVM_NonlinearSolver * nonlinear_solver = (FVM_NonlinearSolver *)ctx; nonlinear_solver->build_petsc_sens_jacobian(x, jac, pc); //*msflag = SAME_NONZERO_PATTERN; *msflag = DIFFERENT_NONZERO_PATTERN; return ierr; } //--------------------------------------------------------------- // this function is called by PETSc to do pre check after each line search static PetscErrorCode __genius_petsc_snes_pre_check(SNES , Vec x, Vec y, void *ctx, PetscTruth *changed_y) { PetscErrorCode ierr=0; // convert void* to FVM_NonlinearSolver* FVM_NonlinearSolver * nonlinear_solver = (FVM_NonlinearSolver *)ctx; nonlinear_solver->sens_line_search_pre_check(x, y, changed_y); return ierr; } //--------------------------------------------------------------- // this function is called by PETSc to do post check after each line search static PetscErrorCode __genius_petsc_snes_post_check(SNES , Vec x, Vec y, Vec w, void *ctx, PetscTruth *changed_y, PetscTruth *changed_w) { PetscErrorCode ierr=0; // convert void* to FVM_NonlinearSolver* FVM_NonlinearSolver * nonlinear_solver = (FVM_NonlinearSolver *)ctx; nonlinear_solver->sens_line_search_post_check(x, y, w, changed_y, changed_w); return ierr; } } // end extern "C" //--------------------------------------------------------------------- /*------------------------------------------------------------------ * constructor, setup context */ FVM_NonlinearSolver::FVM_NonlinearSolver(SimulationSystem & system): FVM_PDESolver(system) { PetscErrorCode ierr; // create petsc nonlinear solver context ierr = SNESCreate(PETSC_COMM_WORLD, &snes); genius_assert(!ierr); } /*------------------------------------------------------------------ * setup nonlinear matrix/vector */ void FVM_NonlinearSolver::setup_nonlinear_data() { // map mesh to PETSC solver build_dof_map(); // set petsc routine PetscErrorCode ierr; // create the global solution vector ierr = VecCreateMPI(PETSC_COMM_WORLD, n_local_dofs, n_global_dofs, &x); genius_assert(!ierr); // use VecDuplicate to create vector with same pattern ierr = VecDuplicate(x, &f);genius_assert(!ierr); ierr = VecDuplicate(x, &L);genius_assert(!ierr); // set all the components of scale vector L to 1.0 ierr = VecSet(L, 1.0); genius_assert(!ierr); // create local vector, which has extra room for ghost dofs! the MPI_COMM here is PETSC_COMM_SELF ierr = VecCreateSeq(PETSC_COMM_SELF, local_index_array.size() , &lx); genius_assert(!ierr); // use VecDuplicate to create vector with same pattern ierr = VecDuplicate(lx, &lf);genius_assert(!ierr); // create the index for vector statter ierr = ISCreateGeneral(PETSC_COMM_WORLD, global_index_array.size() , &global_index_array[0] , &gis); genius_assert(!ierr); ierr = ISCreateGeneral(PETSC_COMM_SELF, local_index_array.size() , &local_index_array[0] , &lis); genius_assert(!ierr); // it seems we can free global_index_array and local_index_array to save the memory // create the vector statter ierr = VecScatterCreate(x, gis, lx, lis, &scatter); genius_assert(!ierr); // create the jacobian matrix ierr = MatCreate(PETSC_COMM_WORLD,&J); genius_assert(!ierr); ierr = MatSetSizes(J, n_local_dofs, n_local_dofs, n_global_dofs, n_global_dofs); genius_assert(!ierr); #if (PETSC_VERSION_GE(3,0,0) || defined(HAVE_PETSC_DEV)) // we are using petsc-3 or petsc-devel if (Genius::n_processors()>1) { ierr = MatSetType(J,MATMPIAIJ); genius_assert(!ierr); // alloc memory for parallel matrix here ierr = MatMPIAIJSetPreallocation(J, 0, &n_nz[0], 0, &n_oz[0]); genius_assert(!ierr); } else { ierr = MatSetType(J,MATSEQAIJ); genius_assert(!ierr); // alloc memory for sequence matrix here ierr = MatSeqAIJSetPreallocation(J, 0, &n_nz[0]); genius_assert(!ierr); } #endif #if PETSC_VERSION_LE(2,3,3) if (Genius::n_processors()>1) { // if LU method is required, we should check if SuperLU_DIST/MUMPS is installed // the deault parallel LU solver is set to MUMPS switch(SolverSpecify::LS) { case SolverSpecify::LU : case SolverSpecify::MUMPS : { #ifdef PETSC_HAVE_MUMPS MESSAGE<< "Using MUMPS linear solver..."<1) { switch(linear_solver_type) { case SolverSpecify::LU : case SolverSpecify::MUMPS : // if LU method is required, we should check if SuperLU_DIST/MUMPS is installed // the default parallel LU solver is set to MUMPS #ifdef PETSC_HAVE_MUMPS MESSAGE<< "Using MUMPS linear solver..."<1) { #ifdef PETSC_HAVE_LIBHYPRE MESSAGE<< "Using Hypre/Euclid preconditioner..."< 1) { ierr = PCSetType (pc, (char*) PCASM); genius_assert(!ierr); ierr = PetscOptionsSetValue("-sub_pc_type","ilu"); genius_assert(!ierr); ierr = PetscOptionsSetValue("-sub_pc_factor_reuse_fill","1"); genius_assert(!ierr); ierr = PetscOptionsSetValue("-sub_pc_factor_shift_nonzero","1e-12"); genius_assert(!ierr); } else { ierr = PCSetType (pc, (char*) PCILU); genius_assert(!ierr); ierr = PCFactorSetReuseFill(pc, PETSC_TRUE);genius_assert(!ierr); ierr = PCFactorSetPivoting(pc, 1.0); genius_assert(!ierr); ierr = PCFactorReorderForNonzeroDiagonal(pc, 1e-20); genius_assert(!ierr); ierr = PCFactorSetShiftNonzero(pc, 1e-12); genius_assert(!ierr); //ierr = PetscOptionsSetValue("-pc_factor_nonzeros_along_diagonal", 0); genius_assert(!ierr); ierr = PetscOptionsSetValue("-pc_factor_diagonal_fill", 0); genius_assert(!ierr); } return; case SolverSpecify::JACOBI_PRECOND: ierr = PCSetType (pc, (char*) PCJACOBI); genius_assert(!ierr); return; case SolverSpecify::BLOCK_JACOBI_PRECOND: ierr = PCSetType (pc, (char*) PCBJACOBI); genius_assert(!ierr); return; case SolverSpecify::SOR_PRECOND: ierr = PCSetType (pc, (char*) PCSOR); genius_assert(!ierr); return; case SolverSpecify::EISENSTAT_PRECOND: ierr = PCSetType (pc, (char*) PCEISENSTAT); genius_assert(!ierr); return; case SolverSpecify::USER_PRECOND: ierr = PCSetType (pc, (char*) PCMAT); genius_assert(!ierr); return; case SolverSpecify::SHELL_PRECOND: ierr = PCSetType (pc, (char*) PCSHELL); genius_assert(!ierr); return; default: std::cerr << "ERROR: Unsupported PETSC Preconditioner: " << preconditioner_type << std::endl << "Continuing with PETSC defaults" << std::endl; } } void FVM_NonlinearSolver::sens_solve() { // do snes solve SNESSolve ( snes, PETSC_NULL, x ); // get the converged reason SNESConvergedReason reason; SNESGetConvergedReason ( snes,&reason ); // if Line search failed, disable Line search if ( reason == SNES_DIVERGED_LS_FAILURE || reason == SNES_DIVERGED_LOCAL_MIN ) { MESSAGE <<"------> nonlinear solver " << SNESConvergedReasons[reason] <<". Disable Line Search.\n\n\n"; RECORD(); SNESLineSearchSet ( snes,SNESLineSearchNo,PETSC_NULL ); SNESSolve ( snes, PETSC_NULL, x ); } #if 0 if ( reason == SNES_DIVERGED_LINEAR_SOLVE ) { MESSAGE <<"------> nonlinear solver " << SNESConvergedReasons[reason] <<". Set to use direct solver.\n\n\n"; RECORD(); #if (PETSC_VERSION_GE(3,0,0) || defined(HAVE_PETSC_DEV)) KSPSetType (ksp, (char*) KSPPREONLY); PCSetType (pc, (char*) PCLU); PCFactorSetMatSolverPackage (pc, MAT_SOLVER_MUMPS); PetscOptionsSetValue("-mat_mumps_icntl_14", "80"); #endif #if PETSC_VERSION_LE(2,3,3) MatSetType(J,MATAIJMUMPS); PetscOptionsSetValue("-mat_mumps_icntl_14","80"); #endif SNESSolve ( snes, PETSC_NULL, x ); } #endif }