/* GAMG geometric-algebric multiogrid PC - Mark Adams 2011 */ #include <../src/ksp/pc/impls/gamg/gamg.h> PetscLogEvent gamg_setup_stages[NUM_SET]; /* Private context for the GAMG preconditioner */ typedef struct gamg_TAG { PetscInt m_dim; PetscInt m_Nlevels; PetscInt m_data_sz; PetscReal *m_data; /* blocked vector of vertex data on fine grid (coordinates) */ } PC_GAMG; #define TOP_GRID_LIM 100 /* -----------------------------------------------------------------------------*/ #undef __FUNCT__ #define __FUNCT__ "PCReset_GAMG" PetscErrorCode PCReset_GAMG(PC pc) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_GAMG *pc_gamg = (PC_GAMG*)mg->innerctx; PetscFunctionBegin; ierr = PetscFree(pc_gamg->m_data);CHKERRQ(ierr); PetscFunctionReturn(0); } /* -------------------------------------------------------------------------- */ /* PCSetCoordinates_GAMG Input Parameter: . pc - the preconditioner context */ EXTERN_C_BEGIN #undef __FUNCT__ #define __FUNCT__ "PCSetCoordinates_GAMG" PetscErrorCode PCSetCoordinates_GAMG(PC pc, const int ndm,PetscReal *coords ) { PC_MG *mg = (PC_MG*)pc->data; PC_GAMG *pc_gamg = (PC_GAMG*)mg->innerctx; PetscErrorCode ierr; PetscInt bs, my0, tt; Mat mat = pc->pmat; PetscInt arrsz; PetscFunctionBegin; ierr = MatGetBlockSize( mat, &bs ); CHKERRQ( ierr ); ierr = MatGetOwnershipRange( mat, &my0, &tt ); CHKERRQ(ierr); arrsz = (tt-my0)/bs*ndm; // put coordinates if (!pc_gamg->m_data || (pc_gamg->m_data_sz != arrsz)) { ierr = PetscFree( pc_gamg->m_data ); CHKERRQ(ierr); ierr = PetscMalloc(arrsz*sizeof(double), &pc_gamg->m_data ); CHKERRQ(ierr); } /* copy data in */ for(tt=0;ttm_data[tt] = coords[tt]; } pc_gamg->m_data_sz = arrsz; pc_gamg->m_dim = ndm; PetscFunctionReturn(0); } EXTERN_C_END /* -------------------------------------------------------------------------- */ /* partitionLevel Input Parameter: . a_Amat_fine - matrix on this fine (k) level . a_dime - 2 or 3 In/Output Parameter: . a_P_inout - prolongation operator to the next level (k-1) . a_coarse_crds - coordinates that need to be moved . a_active_proc - number of active procs Output Parameter: . a_Amat_crs - coarse matrix that is created (k-1) */ #undef __FUNCT__ #define __FUNCT__ "partitionLevel" PetscErrorCode partitionLevel( Mat a_Amat_fine, PetscInt a_dim, Mat *a_P_inout, PetscReal **a_coarse_crds, PetscMPIInt *a_active_proc, Mat *a_Amat_crs ) { PetscErrorCode ierr; Mat Amat, Pnew, Pold = *a_P_inout; IS new_indices,isnum; MPI_Comm wcomm = ((PetscObject)a_Amat_fine)->comm; PetscMPIInt nactive_procs,mype,npe; PetscInt Istart,Iend,Istart0,Iend0,ncrs0,ncrs_new,bs=1; /* bs ??? */ PetscFunctionBegin; ierr = MPI_Comm_rank(wcomm,&mype);CHKERRQ(ierr); ierr = MPI_Comm_size(wcomm,&npe);CHKERRQ(ierr); /* RAP */ ierr = MatPtAP( a_Amat_fine, Pold, MAT_INITIAL_MATRIX, 2.0, &Amat ); CHKERRQ(ierr); ierr = MatGetOwnershipRange( Amat, &Istart0, &Iend0 ); CHKERRQ(ierr); /* x2 size of 'a_coarse_crds' */ ncrs0 = (Iend0 - Istart0)/bs; /* Repartition Amat_{k} and move colums of P^{k}_{k-1} and coordinates accordingly */ { PetscInt neq,N,counts[npe]; IS isnewproc; PetscMPIInt new_npe,targ_npe; ierr = MatGetSize( Amat, &neq, &N );CHKERRQ(ierr); #define MIN_EQ_PROC 100 nactive_procs = *a_active_proc; targ_npe = neq/MIN_EQ_PROC; /* hardwire min. number of eq/proc */ if( targ_npe == 0 || neq < TOP_GRID_LIM ) new_npe = 1; /* chop coarsest grid */ else if (targ_npe >= nactive_procs ) new_npe = nactive_procs; /* no change */ else { PetscMPIInt factstart,fact; new_npe = -9999; factstart = nactive_procs; for(fact=factstart;fact>0;fact--){ /* try to find a better number of procs */ if( nactive_procs%fact==0 && neq/(nactive_procs/fact) > MIN_EQ_PROC ) { new_npe = nactive_procs/fact; } } assert(new_npe != -9999); } *a_active_proc = new_npe; /* output for next time */ PetscPrintf(PETSC_COMM_WORLD,"\t\t%s num active procs = %d (N loc = %d)\n",__FUNCT__,new_npe,ncrs0); { /* partition: get 'isnewproc' */ MatPartitioning mpart; Mat adj; const PetscInt *is_idx; PetscInt is_sz,kk,jj,old_fact=(npe/nactive_procs),new_fact=(npe/new_npe),*isnewproc_idx; /* create sub communicator */ MPI_Comm cm,new_comm; int membershipKey = mype % old_fact; ierr = MPI_Comm_split(wcomm, membershipKey, mype, &cm); CHKERRQ(ierr); ierr = PetscCommDuplicate( cm, &new_comm, PETSC_NULL ); CHKERRQ(ierr); ierr = MPI_Comm_free( &cm ); CHKERRQ(ierr); /* MatPartitioningApply call MatConvert, which is collective */ ierr = MatConvert(Amat,MATMPIADJ,MAT_INITIAL_MATRIX,&adj);CHKERRQ(ierr); if( membershipKey == 0 ) { /* hack to fix global data that pmetis.c uses in 'adj' */ for( kk=0 , jj=0 ; kk<=npe ; jj++, kk += old_fact ) { adj->rmap->range[jj] = adj->rmap->range[kk]; } ierr = MatPartitioningCreate( new_comm, &mpart ); CHKERRQ(ierr); ierr = MatPartitioningSetAdjacency( mpart, adj ); CHKERRQ(ierr); ierr = MatPartitioningSetFromOptions( mpart ); CHKERRQ(ierr); ierr = MatPartitioningSetNParts( mpart, new_npe ); CHKERRQ(ierr); ierr = MatPartitioningApply( mpart, &isnewproc ); CHKERRQ(ierr); ierr = MatPartitioningDestroy( &mpart ); CHKERRQ(ierr); /* collect IS info */ ierr = ISGetLocalSize( isnewproc, &is_sz ); CHKERRQ(ierr); ierr = PetscMalloc( is_sz*sizeof(PetscInt), &isnewproc_idx ); CHKERRQ(ierr); ierr = ISGetIndices( isnewproc, &is_idx ); CHKERRQ(ierr); for(kk=0;kkele */ ierr = PetscFree( *a_coarse_crds ); CHKERRQ(ierr); ierr = PetscMalloc( a_dim*ncrs_new*sizeof(PetscReal), a_coarse_crds ); CHKERRQ(ierr); coords = *a_coarse_crds; /* convient */ ierr = VecGetArray( dest_crd, &array ); CHKERRQ(ierr); for (i=0; idata; PC_GAMG *pc_gamg = (PC_GAMG*)mg->innerctx; Mat Amat = pc->mat, Pmat = pc->pmat; PetscBool isSeq, isMPI; PetscInt fine_level, level, level1, M, N, bs, lidx; MPI_Comm wcomm = ((PetscObject)pc)->comm; PetscMPIInt mype,npe,nactivepe; PetscBool isOK; Mat Aarr[GAMG_MAXLEVELS], Parr[GAMG_MAXLEVELS]; PetscReal *coarse_crds = 0, *crds = pc_gamg->m_data; PetscFunctionBegin; ierr = MPI_Comm_rank(wcomm,&mype);CHKERRQ(ierr); ierr = MPI_Comm_size(wcomm,&npe);CHKERRQ(ierr); if (pc->setupcalled){ /* no state data in GAMG to destroy (now) */ ierr = PCReset_MG(pc); CHKERRQ(ierr); } if (!pc_gamg->m_data) SETERRQ(wcomm,PETSC_ERR_SUP,"PCSetUp_GAMG called before PCSetCoordinates"); /* setup special features of PCGAMG */ ierr = PetscTypeCompare((PetscObject)Amat, MATSEQAIJ, &isSeq);CHKERRQ(ierr); ierr = PetscTypeCompare((PetscObject)Amat, MATMPIAIJ, &isMPI);CHKERRQ(ierr); if (isMPI) { } else if (isSeq) { } else SETERRQ1(wcomm,PETSC_ERR_ARG_WRONG, "Matrix type '%s' cannot be used with GAMG. GAMG can only handle AIJ matrices.",((PetscObject)Amat)->type_name); /* GAMG requires input of fine-grid matrix. It determines nlevels. */ ierr = MatGetBlockSize( Amat, &bs ); CHKERRQ(ierr); if(bs!=1) SETERRQ1(wcomm,PETSC_ERR_ARG_WRONG, "GAMG only supports scalar prblems bs = '%d'.",bs); /* Get A_i and R_i */ ierr = MatGetSize( Amat, &M, &N );CHKERRQ(ierr); PetscPrintf(PETSC_COMM_WORLD,"[%d]%s level %d N=%d\n",0,__FUNCT__,0,N); for (level=0, Aarr[0] = Pmat, nactivepe = npe; /* hard wired stopping logic */ level < GAMG_MAXLEVELS-1 && (level==0 || M>TOP_GRID_LIM) && (npe==1 || nactivepe>1); level++ ) { level1 = level + 1; ierr = PetscLogEventBegin(gamg_setup_stages[SET1],0,0,0,0);CHKERRQ(ierr); ierr = createProlongation( Aarr[level], crds, pc_gamg->m_dim, &Parr[level1], &coarse_crds, &isOK ); CHKERRQ(ierr); ierr = PetscLogEventEnd(gamg_setup_stages[SET1],0,0,0,0);CHKERRQ(ierr); ierr = PetscFree( crds ); CHKERRQ( ierr ); if(level==0) Aarr[0] = Amat; /* use Pmat for finest level setup, but use mat for solver */ if( isOK ) { ierr = PetscLogEventBegin(gamg_setup_stages[SET2],0,0,0,0);CHKERRQ(ierr); ierr = partitionLevel( Aarr[level], pc_gamg->m_dim, &Parr[level1], &coarse_crds, &nactivepe, &Aarr[level1] ); CHKERRQ(ierr); ierr = PetscLogEventEnd(gamg_setup_stages[SET2],0,0,0,0);CHKERRQ(ierr); ierr = MatGetSize( Aarr[level1], &M, &N );CHKERRQ(ierr); PetscPrintf(PETSC_COMM_WORLD,"[%d]%s done level %d N=%d, active pe = %d\n",0,__FUNCT__,level1,N,nactivepe); } else{ break; } crds = coarse_crds; } ierr = PetscFree( coarse_crds ); CHKERRQ( ierr ); PetscPrintf(PETSC_COMM_WORLD,"\t[%d]%s %d levels\n",0,__FUNCT__,level + 1); pc_gamg->m_data = 0; /* destroyed coordinate data */ pc_gamg->m_Nlevels = level + 1; fine_level = level; ierr = PCMGSetLevels(pc,pc_gamg->m_Nlevels,PETSC_NULL);CHKERRQ(ierr); /* set default smoothers */ for (level=1,lidx=pc_gamg->m_Nlevels-2; level <= fine_level; level++,lidx--) { PetscReal emax, emin; KSP smoother; PC subpc; ierr = PCMGGetSmoother( pc, level, &smoother ); CHKERRQ(ierr); ierr = KSPSetType( smoother, KSPCHEBYCHEV );CHKERRQ(ierr); { /* eigen estimate 'emax' */ KSP eksp; Mat Lmat = Aarr[lidx]; Vec bb, xx; PC pc; PetscInt N1, N0, tt; ierr = MatGetVecs( Lmat, &bb, 0 ); CHKERRQ(ierr); ierr = MatGetVecs( Lmat, &xx, 0 ); CHKERRQ(ierr); { PetscRandom rctx; ierr = PetscRandomCreate(wcomm,&rctx);CHKERRQ(ierr); ierr = PetscRandomSetFromOptions(rctx);CHKERRQ(ierr); ierr = VecSetRandom(bb,rctx);CHKERRQ(ierr); ierr = PetscRandomDestroy( &rctx ); CHKERRQ(ierr); } ierr = KSPCreate(wcomm,&eksp);CHKERRQ(ierr); ierr = KSPSetInitialGuessNonzero( eksp, PETSC_FALSE ); CHKERRQ(ierr); ierr = KSPSetOperators( eksp, Lmat, Lmat, DIFFERENT_NONZERO_PATTERN ); CHKERRQ( ierr ); ierr = KSPGetPC( eksp, &pc );CHKERRQ( ierr ); ierr = PCSetType( pc, PCJACOBI ); CHKERRQ(ierr); /* should be same as above */ ierr = KSPSetTolerances( eksp, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 5 ); CHKERRQ(ierr); ierr = KSPSetConvergenceTest( eksp, KSPSkipConverged, 0, 0 ); CHKERRQ(ierr); ierr = KSPSetNormType( eksp, KSP_NORM_NONE ); CHKERRQ(ierr); ierr = KSPSetComputeSingularValues( eksp,PETSC_TRUE ); CHKERRQ(ierr); ierr = KSPSolve( eksp, bb, xx ); CHKERRQ(ierr); ierr = KSPComputeExtremeSingularValues( eksp, &emax, &emin ); CHKERRQ(ierr); ierr = MatGetSize( Lmat, &N1, &tt ); CHKERRQ(ierr); ierr = MatGetSize( Aarr[lidx+1], &N0, &tt );CHKERRQ(ierr); PetscPrintf(PETSC_COMM_WORLD,"\t\t%s max eigen = %e (N=%d)\n",__FUNCT__,emax,N1); emax *= 1.05; ierr = VecDestroy( &xx ); CHKERRQ(ierr); ierr = VecDestroy( &bb ); CHKERRQ(ierr); ierr = KSPDestroy( &eksp ); CHKERRQ(ierr); emin = emax/((PetscReal)N1/(PetscReal)N0); /* this should be about the coarsening rate */ ierr = KSPSetOperators( smoother, Lmat, Lmat, DIFFERENT_NONZERO_PATTERN ); } ierr = KSPChebychevSetEigenvalues( smoother, emax, emin );CHKERRQ(ierr); ierr = KSPGetPC( smoother, &subpc ); CHKERRQ(ierr); ierr = PCSetType( subpc, PCJACOBI ); CHKERRQ(ierr); ierr = KSPSetNormType( smoother, KSP_NORM_NONE ); CHKERRQ(ierr); } { KSP smoother; /* coarse grid */ Mat Lmat = Aarr[pc_gamg->m_Nlevels-1]; ierr = PCMGGetSmoother( pc, 0, &smoother ); CHKERRQ(ierr); ierr = KSPSetOperators( smoother, Lmat, Lmat, DIFFERENT_NONZERO_PATTERN ); CHKERRQ(ierr); ierr = KSPSetNormType( smoother, KSP_NORM_NONE ); CHKERRQ(ierr); } /* should be called in PCSetFromOptions_GAMG(), but cannot be called prior to PCMGSetLevels() */ ierr = PCSetFromOptions_MG(pc); CHKERRQ(ierr); { PetscBool galerkin; ierr = PCMGGetGalerkin( pc, &galerkin); CHKERRQ(ierr); if(galerkin){ SETERRQ(wcomm,PETSC_ERR_ARG_WRONG, "GAMG does galerkin manually so it must not be used in PC_MG."); } } /* set interpolation between the levels, create timer stages, clean up */ if( PETSC_FALSE ) { char str[32]; sprintf(str,"MG Level %d (%d)",0,pc_gamg->m_Nlevels-1); PetscLogStageRegister(str, &gamg_stages[fine_level]); } for (level=0,lidx=pc_gamg->m_Nlevels-1; levelsetupcalled = 0; ierr = PCSetUp_MG(pc);CHKERRQ(ierr); PetscFunctionReturn(0); } /* -------------------------------------------------------------------------- */ /* PCDestroy_GAMG - Destroys the private context for the GAMG preconditioner that was created with PCCreate_GAMG(). Input Parameter: . pc - the preconditioner context Application Interface Routine: PCDestroy() */ #undef __FUNCT__ #define __FUNCT__ "PCDestroy_GAMG" PetscErrorCode PCDestroy_GAMG(PC pc) { PetscErrorCode ierr; PC_MG *mg = (PC_MG*)pc->data; PC_GAMG *pc_gamg= (PC_GAMG*)mg->innerctx; PetscFunctionBegin; ierr = PCReset_GAMG(pc);CHKERRQ(ierr); ierr = PetscFree(pc_gamg);CHKERRQ(ierr); ierr = PCDestroy_MG(pc);CHKERRQ(ierr); PetscFunctionReturn(0); } #undef __FUNCT__ #define __FUNCT__ "PCSetFromOptions_GAMG" PetscErrorCode PCSetFromOptions_GAMG(PC pc) { /* PetscErrorCode ierr; */ /* PC_MG *mg = (PC_MG*)pc->data; */ /* PC_GAMG *pc_gamg = (PC_GAMG*)mg->innerctx; */ /* MPI_Comm comm = ((PetscObject)pc)->comm; */ PetscFunctionBegin; PetscFunctionReturn(0); } /* -------------------------------------------------------------------------- */ /* PCCreate_GAMG - Creates a GAMG preconditioner context, PC_GAMG Input Parameter: . pc - the preconditioner context Application Interface Routine: PCCreate() */ /* MC PCGAMG - Use algebraic multigrid preconditioning. This preconditioner requires you provide fine grid discretization matrix and coordinates on the fine grid. Options Database Key: Multigrid options(inherited) + -pc_mg_cycles <1>: 1 for V cycle, 2 for W-cycle (MGSetCycles) . -pc_mg_smoothup <1>: Number of post-smoothing steps (MGSetNumberSmoothUp) . -pc_mg_smoothdown <1>: Number of pre-smoothing steps (MGSetNumberSmoothDown) -pc_mg_type : (one of) additive multiplicative full cascade kascade GAMG options: Level: intermediate Concepts: multigrid .seealso: PCCreate(), PCSetType(), PCType (for list of available types), PC, PCMGType, PCMGSetLevels(), PCMGGetLevels(), PCMGSetType(), MPSetCycles(), PCMGSetNumberSmoothDown(), PCMGSetNumberSmoothUp(), PCMGGetCoarseSolve(), PCMGSetResidual(), PCMGSetInterpolation(), PCMGSetRestriction(), PCMGGetSmoother(), PCMGGetSmootherUp(), PCMGGetSmootherDown(), PCMGSetCyclesOnLevel(), PCMGSetRhs(), PCMGSetX(), PCMGSetR() M */ EXTERN_C_BEGIN #undef __FUNCT__ #define __FUNCT__ "PCCreate_GAMG" PetscErrorCode PCCreate_GAMG(PC pc) { PetscErrorCode ierr; PC_GAMG *pc_gamg; PC_MG *mg; PetscClassId cookie; PetscFunctionBegin; /* PCGAMG is an inherited class of PCMG. Initialize pc as PCMG */ ierr = PCSetType(pc,PCMG);CHKERRQ(ierr); /* calls PCCreate_MG() and MGCreate_Private() */ ierr = PetscObjectChangeTypeName((PetscObject)pc,PCGAMG);CHKERRQ(ierr); /* create a supporting struct and attach it to pc */ ierr = PetscNewLog(pc,PC_GAMG,&pc_gamg);CHKERRQ(ierr); mg = (PC_MG*)pc->data; mg->innerctx = pc_gamg; pc_gamg->m_Nlevels = -1; /* overwrite the pointers of PCMG by the functions of PCGAMG */ pc->ops->setfromoptions = PCSetFromOptions_GAMG; pc->ops->setup = PCSetUp_GAMG; pc->ops->reset = PCReset_GAMG; pc->ops->destroy = PCDestroy_GAMG; ierr = PetscObjectComposeFunctionDynamic( (PetscObject)pc, "PCSetCoordinates_C", "PCSetCoordinates_GAMG", PCSetCoordinates_GAMG);CHKERRQ(ierr); PetscClassIdRegister("GAMG Setup",&cookie); PetscLogEventRegister("GAMG-createProl", cookie, &gamg_setup_stages[SET1]); PetscLogEventRegister("GAMG-partLevel", cookie, &gamg_setup_stages[SET2]); PetscLogEventRegister("GAMG-MIS Graph", cookie, &gamg_setup_stages[SET3]); PetscLogEventRegister("GAMG-MIS-Agg", cookie, &gamg_setup_stages[SET4]); PetscLogEventRegister("GAMG-growSupp", cookie, &gamg_setup_stages[SET5]); PetscLogEventRegister("GAMG-tri-Prol", cookie, &gamg_setup_stages[SET6]); PetscLogEventRegister("GAMG-find-prol", cookie, &gamg_setup_stages[FIND_V]); PetscFunctionReturn(0); } EXTERN_C_END