1*e6f8f311SMark Adams /* 2*e6f8f311SMark Adams This file implements an AmgX preconditioner in PETSc as part of PC. 3*e6f8f311SMark Adams */ 4*e6f8f311SMark Adams 5*e6f8f311SMark Adams /* 6*e6f8f311SMark Adams Include files needed for the AmgX preconditioner: 7*e6f8f311SMark Adams pcimpl.h - private include file intended for use by all preconditioners 8*e6f8f311SMark Adams */ 9*e6f8f311SMark Adams 10*e6f8f311SMark Adams #include <petsc/private/pcimpl.h> /*I "petscpc.h" I*/ 11*e6f8f311SMark Adams #include <petscdevice.h> 12*e6f8f311SMark Adams #include <amgx_c.h> 13*e6f8f311SMark Adams #include <limits> 14*e6f8f311SMark Adams #include <vector> 15*e6f8f311SMark Adams #include <algorithm> 16*e6f8f311SMark Adams #include <map> 17*e6f8f311SMark Adams #include <numeric> 18*e6f8f311SMark Adams #include "cuda_runtime.h" 19*e6f8f311SMark Adams 20*e6f8f311SMark Adams enum class AmgXSmoother { PCG, PCGF, PBiCGStab, GMRES, FGMRES, JacobiL1, 21*e6f8f311SMark Adams BlockJacobi, GS, MulticolorGS, MulticolorILU, MulticolorDILU, ChebyshevPoly, NoSolver }; 22*e6f8f311SMark Adams enum class AmgXAMGMethod { Classical, Aggregation }; 23*e6f8f311SMark Adams enum class AmgXSelector { Size2, Size4, Size8, MultiPairwise, PMIS, HMIS }; 24*e6f8f311SMark Adams enum class AmgXCoarseSolver { DenseLU, NoSolver }; 25*e6f8f311SMark Adams enum class AmgXAMGCycle { V, W, F, CG, CGF }; 26*e6f8f311SMark Adams 27*e6f8f311SMark Adams struct AmgXControlMap 28*e6f8f311SMark Adams { 29*e6f8f311SMark Adams static const std::map<std::string, AmgXAMGMethod> AMGMethods; 30*e6f8f311SMark Adams static const std::map<std::string, AmgXSmoother> Smoothers; 31*e6f8f311SMark Adams static const std::map<std::string, AmgXSelector> Selectors; 32*e6f8f311SMark Adams static const std::map<std::string, AmgXCoarseSolver> CoarseSolvers; 33*e6f8f311SMark Adams static const std::map<std::string, AmgXAMGCycle> AMGCycles; 34*e6f8f311SMark Adams }; 35*e6f8f311SMark Adams 36*e6f8f311SMark Adams const std::map<std::string, AmgXAMGMethod> AmgXControlMap::AMGMethods = { 37*e6f8f311SMark Adams { "CLASSICAL", AmgXAMGMethod::Classical }, 38*e6f8f311SMark Adams { "AGGREGATION", AmgXAMGMethod::Aggregation } 39*e6f8f311SMark Adams }; 40*e6f8f311SMark Adams 41*e6f8f311SMark Adams const std::map<std::string, AmgXSmoother> AmgXControlMap::Smoothers = { 42*e6f8f311SMark Adams { "PCG", AmgXSmoother::PCG }, 43*e6f8f311SMark Adams { "PCGF", AmgXSmoother::PCGF }, 44*e6f8f311SMark Adams { "PBICGSTAB", AmgXSmoother::PBiCGStab }, 45*e6f8f311SMark Adams { "GMRES", AmgXSmoother::GMRES }, 46*e6f8f311SMark Adams { "FGMRES", AmgXSmoother::FGMRES }, 47*e6f8f311SMark Adams { "JACOBI_L1", AmgXSmoother::JacobiL1 }, 48*e6f8f311SMark Adams { "BLOCK_JACOBI", AmgXSmoother::BlockJacobi }, 49*e6f8f311SMark Adams { "GS", AmgXSmoother::GS }, 50*e6f8f311SMark Adams { "MULTICOLOR_GS", AmgXSmoother::MulticolorGS }, 51*e6f8f311SMark Adams { "MULTICOLOR_ILU", AmgXSmoother::MulticolorILU }, 52*e6f8f311SMark Adams { "MULTICOLOR_DILU", AmgXSmoother::MulticolorDILU }, 53*e6f8f311SMark Adams { "CHEBYSHEV_POLY", AmgXSmoother::ChebyshevPoly }, 54*e6f8f311SMark Adams { "NOSOLVER", AmgXSmoother::NoSolver } 55*e6f8f311SMark Adams }; 56*e6f8f311SMark Adams 57*e6f8f311SMark Adams const std::map<std::string, AmgXSelector> AmgXControlMap::Selectors = { 58*e6f8f311SMark Adams { "SIZE_2", AmgXSelector::Size2 }, 59*e6f8f311SMark Adams { "SIZE_4", AmgXSelector::Size4 }, 60*e6f8f311SMark Adams { "SIZE_8", AmgXSelector::Size8 }, 61*e6f8f311SMark Adams { "MULTI_PAIRWISE", AmgXSelector::MultiPairwise }, 62*e6f8f311SMark Adams { "PMIS", AmgXSelector::PMIS }, 63*e6f8f311SMark Adams { "HMIS", AmgXSelector::HMIS } 64*e6f8f311SMark Adams }; 65*e6f8f311SMark Adams 66*e6f8f311SMark Adams const std::map<std::string, AmgXCoarseSolver> AmgXControlMap::CoarseSolvers = { 67*e6f8f311SMark Adams { "DENSE_LU_SOLVER", AmgXCoarseSolver::DenseLU }, 68*e6f8f311SMark Adams { "NOSOLVER", AmgXCoarseSolver::NoSolver } 69*e6f8f311SMark Adams }; 70*e6f8f311SMark Adams 71*e6f8f311SMark Adams const std::map<std::string, AmgXAMGCycle> AmgXControlMap::AMGCycles = { 72*e6f8f311SMark Adams { "V", AmgXAMGCycle::V }, 73*e6f8f311SMark Adams { "W", AmgXAMGCycle::W }, 74*e6f8f311SMark Adams { "F", AmgXAMGCycle::F }, 75*e6f8f311SMark Adams { "CG", AmgXAMGCycle::CG }, 76*e6f8f311SMark Adams { "CGF", AmgXAMGCycle::CGF } 77*e6f8f311SMark Adams }; 78*e6f8f311SMark Adams 79*e6f8f311SMark Adams /* 80*e6f8f311SMark Adams Private context (data structure) for the AMGX preconditioner. 81*e6f8f311SMark Adams */ 82*e6f8f311SMark Adams struct PC_AMGX { 83*e6f8f311SMark Adams AMGX_solver_handle solver; 84*e6f8f311SMark Adams AMGX_config_handle cfg; 85*e6f8f311SMark Adams AMGX_resources_handle rsrc; 86*e6f8f311SMark Adams bool solve_state_init; 87*e6f8f311SMark Adams bool rsrc_init; 88*e6f8f311SMark Adams PetscBool verbose; 89*e6f8f311SMark Adams 90*e6f8f311SMark Adams AMGX_matrix_handle A; 91*e6f8f311SMark Adams AMGX_vector_handle sol; 92*e6f8f311SMark Adams AMGX_vector_handle rhs; 93*e6f8f311SMark Adams 94*e6f8f311SMark Adams MPI_Comm comm; 95*e6f8f311SMark Adams PetscMPIInt rank = 0; 96*e6f8f311SMark Adams PetscMPIInt nranks = 0; 97*e6f8f311SMark Adams int devID = 0; 98*e6f8f311SMark Adams 99*e6f8f311SMark Adams void *lib_handle = 0; 100*e6f8f311SMark Adams std::string cfg_contents; 101*e6f8f311SMark Adams 102*e6f8f311SMark Adams // Cached state for re-setup 103*e6f8f311SMark Adams PetscInt nnz; 104*e6f8f311SMark Adams PetscInt nLocalRows; 105*e6f8f311SMark Adams PetscInt nGlobalRows; 106*e6f8f311SMark Adams PetscInt bSize; 107*e6f8f311SMark Adams Mat localA; 108*e6f8f311SMark Adams const PetscScalar *values; 109*e6f8f311SMark Adams 110*e6f8f311SMark Adams // AMG Control parameters 111*e6f8f311SMark Adams AmgXSmoother smoother; 112*e6f8f311SMark Adams AmgXAMGMethod amg_method; 113*e6f8f311SMark Adams AmgXSelector selector; 114*e6f8f311SMark Adams AmgXCoarseSolver coarse_solver; 115*e6f8f311SMark Adams AmgXAMGCycle amg_cycle; 116*e6f8f311SMark Adams PetscInt presweeps; 117*e6f8f311SMark Adams PetscInt postsweeps; 118*e6f8f311SMark Adams PetscInt max_levels; 119*e6f8f311SMark Adams PetscInt aggressive_levels; 120*e6f8f311SMark Adams PetscInt dense_lu_num_rows; 121*e6f8f311SMark Adams PetscScalar strength_threshold; 122*e6f8f311SMark Adams PetscBool print_grid_stats; 123*e6f8f311SMark Adams PetscBool exact_coarse_solve; 124*e6f8f311SMark Adams 125*e6f8f311SMark Adams // Smoother control parameters 126*e6f8f311SMark Adams PetscScalar jacobi_relaxation_factor; 127*e6f8f311SMark Adams PetscScalar gs_symmetric; 128*e6f8f311SMark Adams }; 129*e6f8f311SMark Adams 130*e6f8f311SMark Adams static PetscInt s_count = 0; 131*e6f8f311SMark Adams 132*e6f8f311SMark Adams // Buffer of messages from AmgX 133*e6f8f311SMark Adams // Currently necessary hack before we adapt AmgX to print from single rank only 134*e6f8f311SMark Adams static std::string amgx_output {}; 135*e6f8f311SMark Adams 136*e6f8f311SMark Adams // A print callback that allows AmgX to return status messages 137*e6f8f311SMark Adams static void print_callback(const char *msg, int length) 138*e6f8f311SMark Adams { 139*e6f8f311SMark Adams amgx_output.append(msg); 140*e6f8f311SMark Adams } 141*e6f8f311SMark Adams 142*e6f8f311SMark Adams // Outputs messages from the AmgX message buffer and clears it 143*e6f8f311SMark Adams PetscErrorCode amgx_output_messages(PC_AMGX* amgx) 144*e6f8f311SMark Adams { 145*e6f8f311SMark Adams PetscFunctionBegin; 146*e6f8f311SMark Adams 147*e6f8f311SMark Adams // If AmgX output is enabled and we have a message, output it 148*e6f8f311SMark Adams if (amgx->verbose && !amgx_output.empty()) { 149*e6f8f311SMark Adams // Only a single rank to output the AmgX messages 150*e6f8f311SMark Adams PetscCall(PetscPrintf(amgx->comm, "AMGX: %s", amgx_output.c_str())); 151*e6f8f311SMark Adams 152*e6f8f311SMark Adams // Note that all ranks clear their received output 153*e6f8f311SMark Adams amgx_output.clear(); 154*e6f8f311SMark Adams } 155*e6f8f311SMark Adams 156*e6f8f311SMark Adams PetscFunctionReturn(0); 157*e6f8f311SMark Adams } 158*e6f8f311SMark Adams 159*e6f8f311SMark Adams // XXX Need to add call in AmgX API that gracefully destroys everything 160*e6f8f311SMark Adams // without abort etc. 161*e6f8f311SMark Adams #define PetscCallAmgX(rc) do { \ 162*e6f8f311SMark Adams AMGX_RC err = (rc); \ 163*e6f8f311SMark Adams char msg[4096]; \ 164*e6f8f311SMark Adams switch (err) { \ 165*e6f8f311SMark Adams case AMGX_RC_OK: \ 166*e6f8f311SMark Adams break; \ 167*e6f8f311SMark Adams default: \ 168*e6f8f311SMark Adams AMGX_get_error_string(err, msg, 4096); \ 169*e6f8f311SMark Adams SETERRQ(amgx->comm, PETSC_ERR_LIB, "%s", msg); \ 170*e6f8f311SMark Adams } \ 171*e6f8f311SMark Adams } while (0) 172*e6f8f311SMark Adams 173*e6f8f311SMark Adams /* 174*e6f8f311SMark Adams PCSetUp_AMGX - Prepares for the use of the AmgX preconditioner 175*e6f8f311SMark Adams by setting data structures and options. 176*e6f8f311SMark Adams 177*e6f8f311SMark Adams Input Parameter: 178*e6f8f311SMark Adams . pc - the preconditioner context 179*e6f8f311SMark Adams 180*e6f8f311SMark Adams Application Interface Routine: PCSetUp() 181*e6f8f311SMark Adams 182*e6f8f311SMark Adams Notes: 183*e6f8f311SMark Adams The interface routine PCSetUp() is not usually called directly by 184*e6f8f311SMark Adams the user, but instead is called by PCApply() if necessary. 185*e6f8f311SMark Adams */ 186*e6f8f311SMark Adams static PetscErrorCode PCSetUp_AMGX(PC pc) 187*e6f8f311SMark Adams { 188*e6f8f311SMark Adams PC_AMGX *amgx = (PC_AMGX *)pc->data; 189*e6f8f311SMark Adams Mat Pmat = pc->pmat; 190*e6f8f311SMark Adams PetscBool is_dev_ptrs; 191*e6f8f311SMark Adams 192*e6f8f311SMark Adams PetscFunctionBegin; 193*e6f8f311SMark Adams PetscCall(PetscObjectTypeCompareAny((PetscObject)Pmat, &is_dev_ptrs, MATAIJCUSPARSE, MATSEQAIJCUSPARSE, MATMPIAIJCUSPARSE, "")); 194*e6f8f311SMark Adams 195*e6f8f311SMark Adams // At the present time, an AmgX matrix is a sequential matrix 196*e6f8f311SMark Adams // Non-sequential/MPI matrices must be adapted to extract the local matrix 197*e6f8f311SMark Adams bool partial_setup_allowed = (pc->setupcalled && pc->flag != DIFFERENT_NONZERO_PATTERN); 198*e6f8f311SMark Adams if (amgx->nranks > 1) { 199*e6f8f311SMark Adams if (partial_setup_allowed) { 200*e6f8f311SMark Adams PetscCall(MatMPIAIJGetLocalMat(Pmat, MAT_REUSE_MATRIX, &amgx->localA)); 201*e6f8f311SMark Adams } else { 202*e6f8f311SMark Adams PetscCall(MatMPIAIJGetLocalMat(Pmat, MAT_INITIAL_MATRIX, &amgx->localA)); 203*e6f8f311SMark Adams } 204*e6f8f311SMark Adams 205*e6f8f311SMark Adams if (is_dev_ptrs) { 206*e6f8f311SMark Adams PetscCall(MatConvert(amgx->localA,MATSEQAIJCUSPARSE,MAT_INPLACE_MATRIX,&amgx->localA)); 207*e6f8f311SMark Adams } 208*e6f8f311SMark Adams } else { 209*e6f8f311SMark Adams amgx->localA = Pmat; 210*e6f8f311SMark Adams } 211*e6f8f311SMark Adams 212*e6f8f311SMark Adams if (is_dev_ptrs) { 213*e6f8f311SMark Adams PetscCall(MatSeqAIJCUSPARSEGetArrayRead(amgx->localA, &amgx->values)); 214*e6f8f311SMark Adams } else { 215*e6f8f311SMark Adams PetscCall(MatSeqAIJGetArrayRead(amgx->localA, &amgx->values)); 216*e6f8f311SMark Adams } 217*e6f8f311SMark Adams 218*e6f8f311SMark Adams if (!partial_setup_allowed) { 219*e6f8f311SMark Adams // Initialise resources and matrices 220*e6f8f311SMark Adams if (!amgx->rsrc_init) { 221*e6f8f311SMark Adams // Read configuration file 222*e6f8f311SMark Adams PetscCallAmgX(AMGX_config_create(&amgx->cfg, amgx->cfg_contents.c_str())); 223*e6f8f311SMark Adams PetscCallAmgX(AMGX_resources_create(&amgx->rsrc, amgx->cfg, &amgx->comm, 1, &amgx->devID)); 224*e6f8f311SMark Adams amgx->rsrc_init = true; 225*e6f8f311SMark Adams } 226*e6f8f311SMark Adams 227*e6f8f311SMark Adams PetscCheck(!amgx->solve_state_init, amgx->comm, PETSC_ERR_PLIB, "AmgX solve state initialisation already called."); 228*e6f8f311SMark Adams PetscCallAmgX(AMGX_matrix_create(&amgx->A, amgx->rsrc, AMGX_mode_dDDI)); 229*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_create(&amgx->sol, amgx->rsrc, AMGX_mode_dDDI)); 230*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_create(&amgx->rhs, amgx->rsrc, AMGX_mode_dDDI)); 231*e6f8f311SMark Adams PetscCallAmgX(AMGX_solver_create(&amgx->solver, amgx->rsrc, AMGX_mode_dDDI, amgx->cfg)); 232*e6f8f311SMark Adams amgx->solve_state_init = true; 233*e6f8f311SMark Adams 234*e6f8f311SMark Adams // Extract the CSR data 235*e6f8f311SMark Adams PetscBool done; 236*e6f8f311SMark Adams const PetscInt *colIndices; 237*e6f8f311SMark Adams const PetscInt *rowOffsets; 238*e6f8f311SMark Adams PetscCall(MatGetRowIJ(amgx->localA, 0, PETSC_FALSE, PETSC_FALSE, &amgx->nLocalRows, &rowOffsets, &colIndices, &done)); 239*e6f8f311SMark Adams PetscCheck(done, amgx->comm, PETSC_ERR_PLIB, "MatGetRowIJ was not successful"); 240*e6f8f311SMark Adams PetscCheck(amgx->nLocalRows < std::numeric_limits<int>::max(), PETSC_COMM_SELF,PETSC_ERR_PLIB, "AmgX restricted to int local rows but nLocalRows = %" PetscInt_FMT " > max<int>", amgx->nLocalRows); 241*e6f8f311SMark Adams 242*e6f8f311SMark Adams if (is_dev_ptrs) { 243*e6f8f311SMark Adams PetscCallCUDA(cudaMemcpy(&amgx->nnz, &rowOffsets[amgx->nLocalRows], sizeof(int), cudaMemcpyDefault)); 244*e6f8f311SMark Adams } else { 245*e6f8f311SMark Adams amgx->nnz = rowOffsets[amgx->nLocalRows]; 246*e6f8f311SMark Adams } 247*e6f8f311SMark Adams 248*e6f8f311SMark Adams PetscCheck(amgx->nnz < std::numeric_limits<int>::max(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Support for 64-bit integer nnz not yet implemented, nnz = %" PetscInt_FMT ".", amgx->nnz); 249*e6f8f311SMark Adams 250*e6f8f311SMark Adams // Allocate space for some partition offsets 251*e6f8f311SMark Adams std::vector<PetscInt> partitionOffsets(amgx->nranks + 1); 252*e6f8f311SMark Adams 253*e6f8f311SMark Adams // Fetch the number of local rows per rank 254*e6f8f311SMark Adams partitionOffsets[0] = 0; /* could use PetscLayoutGetRanges */ 255*e6f8f311SMark Adams PetscCallMPI(MPI_Allgather(&amgx->nLocalRows, 1, MPIU_INT, partitionOffsets.data()+1, 1, MPIU_INT, amgx->comm)); 256*e6f8f311SMark Adams std::partial_sum(partitionOffsets.begin(), partitionOffsets.end(), partitionOffsets.begin()); 257*e6f8f311SMark Adams 258*e6f8f311SMark Adams // Fetch the number of global rows 259*e6f8f311SMark Adams amgx->nGlobalRows = partitionOffsets[amgx->nranks]; 260*e6f8f311SMark Adams 261*e6f8f311SMark Adams PetscCall(MatGetBlockSize(Pmat, &amgx->bSize)); 262*e6f8f311SMark Adams 263*e6f8f311SMark Adams // XXX Currently constrained to 32-bit indices, to be changed in the future 264*e6f8f311SMark Adams // Create the distribution and upload the matrix data 265*e6f8f311SMark Adams AMGX_distribution_handle dist; 266*e6f8f311SMark Adams PetscCallAmgX(AMGX_distribution_create(&dist, amgx->cfg)); 267*e6f8f311SMark Adams PetscCallAmgX(AMGX_distribution_set_32bit_colindices(dist, true)); 268*e6f8f311SMark Adams PetscCallAmgX(AMGX_distribution_set_partition_data(dist, AMGX_DIST_PARTITION_OFFSETS, partitionOffsets.data())); 269*e6f8f311SMark Adams PetscCallAmgX(AMGX_matrix_upload_distributed(amgx->A, amgx->nGlobalRows, (int)amgx->nLocalRows, (int)amgx->nnz, amgx->bSize, amgx->bSize, rowOffsets, colIndices, amgx->values, NULL, dist)); 270*e6f8f311SMark Adams PetscCallAmgX(AMGX_solver_setup(amgx->solver, amgx->A)); 271*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_bind(amgx->sol, amgx->A)); 272*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_bind(amgx->rhs, amgx->A)); 273*e6f8f311SMark Adams 274*e6f8f311SMark Adams PetscInt nlr = 0; 275*e6f8f311SMark Adams PetscCall(MatRestoreRowIJ(amgx->localA, 0, PETSC_FALSE, PETSC_FALSE, &nlr, &rowOffsets, &colIndices, &done)); 276*e6f8f311SMark Adams } else { 277*e6f8f311SMark Adams // The fast path for if the sparsity pattern persists 278*e6f8f311SMark Adams PetscCallAmgX(AMGX_matrix_replace_coefficients(amgx->A, amgx->nLocalRows, amgx->nnz, amgx->values, NULL)); 279*e6f8f311SMark Adams PetscCallAmgX(AMGX_solver_resetup(amgx->solver, amgx->A)); 280*e6f8f311SMark Adams } 281*e6f8f311SMark Adams 282*e6f8f311SMark Adams if (is_dev_ptrs) { 283*e6f8f311SMark Adams PetscCall(MatSeqAIJCUSPARSERestoreArrayRead(amgx->localA, &amgx->values)); 284*e6f8f311SMark Adams } else { 285*e6f8f311SMark Adams PetscCall(MatSeqAIJRestoreArrayRead(amgx->localA, &amgx->values)); 286*e6f8f311SMark Adams } 287*e6f8f311SMark Adams amgx_output_messages(amgx); 288*e6f8f311SMark Adams PetscFunctionReturn(0); 289*e6f8f311SMark Adams } 290*e6f8f311SMark Adams 291*e6f8f311SMark Adams /* 292*e6f8f311SMark Adams PCApply_AMGX - Applies the AmgX preconditioner to a vector. 293*e6f8f311SMark Adams 294*e6f8f311SMark Adams Input Parameters: 295*e6f8f311SMark Adams . pc - the preconditioner context 296*e6f8f311SMark Adams . b - rhs vector 297*e6f8f311SMark Adams 298*e6f8f311SMark Adams Output Parameter: 299*e6f8f311SMark Adams . x - solution vector 300*e6f8f311SMark Adams 301*e6f8f311SMark Adams Application Interface Routine: PCApply() 302*e6f8f311SMark Adams */ 303*e6f8f311SMark Adams static PetscErrorCode PCApply_AMGX(PC pc, Vec b, Vec x) 304*e6f8f311SMark Adams { 305*e6f8f311SMark Adams PC_AMGX *amgx = (PC_AMGX *)pc->data; 306*e6f8f311SMark Adams PetscScalar *x_; 307*e6f8f311SMark Adams const PetscScalar *b_; 308*e6f8f311SMark Adams PetscBool is_dev_ptrs; 309*e6f8f311SMark Adams 310*e6f8f311SMark Adams PetscFunctionBegin; 311*e6f8f311SMark Adams PetscCall(PetscObjectTypeCompareAny((PetscObject)x,&is_dev_ptrs,VECCUDA,VECMPICUDA,VECSEQCUDA,"")); 312*e6f8f311SMark Adams 313*e6f8f311SMark Adams if (is_dev_ptrs) { 314*e6f8f311SMark Adams PetscCall(VecCUDAGetArrayWrite(x, &x_)); 315*e6f8f311SMark Adams PetscCall(VecCUDAGetArrayRead(b, &b_)); 316*e6f8f311SMark Adams } else { 317*e6f8f311SMark Adams PetscCall(VecGetArrayWrite(x, &x_)); 318*e6f8f311SMark Adams PetscCall(VecGetArrayRead(b, &b_)); 319*e6f8f311SMark Adams } 320*e6f8f311SMark Adams 321*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_upload(amgx->sol, amgx->nLocalRows, 1, x_)); 322*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_upload(amgx->rhs, amgx->nLocalRows, 1, b_)); 323*e6f8f311SMark Adams PetscCallAmgX(AMGX_solver_solve_with_0_initial_guess(amgx->solver, amgx->rhs, amgx->sol)); 324*e6f8f311SMark Adams 325*e6f8f311SMark Adams AMGX_SOLVE_STATUS status; 326*e6f8f311SMark Adams PetscCallAmgX(AMGX_solver_get_status(amgx->solver, &status)); 327*e6f8f311SMark Adams PetscCall(PCSetErrorIfFailure(pc, static_cast<PetscBool>(status == AMGX_SOLVE_FAILED))); 328*e6f8f311SMark Adams PetscCheck(status != AMGX_SOLVE_FAILED, amgx->comm, PETSC_ERR_CONV_FAILED, "AmgX solver failed to solve the system! The error code is %d.", status); 329*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_download(amgx->sol, x_)); 330*e6f8f311SMark Adams 331*e6f8f311SMark Adams if (is_dev_ptrs) { 332*e6f8f311SMark Adams PetscCall(VecCUDARestoreArrayWrite(x, &x_)); 333*e6f8f311SMark Adams PetscCall(VecCUDARestoreArrayRead(b, &b_)); 334*e6f8f311SMark Adams } else { 335*e6f8f311SMark Adams PetscCall(VecRestoreArrayWrite(x, &x_)); 336*e6f8f311SMark Adams PetscCall(VecRestoreArrayRead(b, &b_)); 337*e6f8f311SMark Adams } 338*e6f8f311SMark Adams amgx_output_messages(amgx); 339*e6f8f311SMark Adams PetscFunctionReturn(0); 340*e6f8f311SMark Adams } 341*e6f8f311SMark Adams 342*e6f8f311SMark Adams static PetscErrorCode PCReset_AMGX(PC pc) 343*e6f8f311SMark Adams { 344*e6f8f311SMark Adams PC_AMGX *amgx = (PC_AMGX *)pc->data; 345*e6f8f311SMark Adams 346*e6f8f311SMark Adams PetscFunctionBegin; 347*e6f8f311SMark Adams if (amgx->solve_state_init) { 348*e6f8f311SMark Adams PetscCallAmgX(AMGX_solver_destroy(amgx->solver)); 349*e6f8f311SMark Adams PetscCallAmgX(AMGX_matrix_destroy(amgx->A)); 350*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_destroy(amgx->sol)); 351*e6f8f311SMark Adams PetscCallAmgX(AMGX_vector_destroy(amgx->rhs)); 352*e6f8f311SMark Adams if (amgx->nranks > 1) PetscCall(MatDestroy(&amgx->localA)); 353*e6f8f311SMark Adams amgx_output_messages(amgx); 354*e6f8f311SMark Adams amgx->solve_state_init = false; 355*e6f8f311SMark Adams } 356*e6f8f311SMark Adams PetscFunctionReturn(0); 357*e6f8f311SMark Adams } 358*e6f8f311SMark Adams 359*e6f8f311SMark Adams /* 360*e6f8f311SMark Adams PCDestroy_AMGX - Destroys the private context for the AmgX preconditioner 361*e6f8f311SMark Adams that was created with PCCreate_AMGX(). 362*e6f8f311SMark Adams 363*e6f8f311SMark Adams Input Parameter: 364*e6f8f311SMark Adams . pc - the preconditioner context 365*e6f8f311SMark Adams 366*e6f8f311SMark Adams Application Interface Routine: PCDestroy() 367*e6f8f311SMark Adams */ 368*e6f8f311SMark Adams static PetscErrorCode PCDestroy_AMGX(PC pc) 369*e6f8f311SMark Adams { 370*e6f8f311SMark Adams PC_AMGX *amgx = (PC_AMGX *)pc->data; 371*e6f8f311SMark Adams 372*e6f8f311SMark Adams PetscFunctionBegin; 373*e6f8f311SMark Adams /* decrease the number of instances, only the last instance need to destroy resource and finalizing AmgX */ 374*e6f8f311SMark Adams if (s_count == 1) { 375*e6f8f311SMark Adams /* can put this in a PCAMGXInitializePackage method */ 376*e6f8f311SMark Adams PetscCheck(amgx->rsrc != nullptr, PETSC_COMM_SELF, PETSC_ERR_PLIB, "s_rsrc == NULL"); 377*e6f8f311SMark Adams PetscCallAmgX(AMGX_resources_destroy(amgx->rsrc)); 378*e6f8f311SMark Adams /* destroy config (need to use AMGX_SAFE_CALL after this point) */ 379*e6f8f311SMark Adams PetscCallAmgX(AMGX_config_destroy(amgx->cfg)); 380*e6f8f311SMark Adams PetscCallAmgX(AMGX_finalize_plugins()); 381*e6f8f311SMark Adams PetscCallAmgX(AMGX_finalize()); 382*e6f8f311SMark Adams PetscCallMPI(MPI_Comm_free(&amgx->comm)); 383*e6f8f311SMark Adams } else { 384*e6f8f311SMark Adams PetscCallAmgX(AMGX_config_destroy(amgx->cfg)); 385*e6f8f311SMark Adams } 386*e6f8f311SMark Adams s_count -= 1; 387*e6f8f311SMark Adams PetscCall(PetscFree(amgx)); 388*e6f8f311SMark Adams PetscFunctionReturn(0); 389*e6f8f311SMark Adams } 390*e6f8f311SMark Adams 391*e6f8f311SMark Adams template <class T> 392*e6f8f311SMark Adams std::string map_reverse_lookup(const std::map<std::string, T>& map, const T& key) 393*e6f8f311SMark Adams { 394*e6f8f311SMark Adams for (auto const& m : map) { 395*e6f8f311SMark Adams if (m.second == key) { 396*e6f8f311SMark Adams return m.first; 397*e6f8f311SMark Adams } 398*e6f8f311SMark Adams } 399*e6f8f311SMark Adams return ""; 400*e6f8f311SMark Adams } 401*e6f8f311SMark Adams 402*e6f8f311SMark Adams static PetscErrorCode PCSetFromOptions_AMGX(PetscOptionItems *PetscOptionsObject,PC pc) 403*e6f8f311SMark Adams { 404*e6f8f311SMark Adams PC_AMGX *amgx = (PC_AMGX *)pc->data; 405*e6f8f311SMark Adams constexpr int MAX_PARAM_LEN = 128; 406*e6f8f311SMark Adams char option[MAX_PARAM_LEN]; 407*e6f8f311SMark Adams 408*e6f8f311SMark Adams PetscFunctionBegin; 409*e6f8f311SMark Adams PetscOptionsHeadBegin(PetscOptionsObject, "AmgX options"); 410*e6f8f311SMark Adams amgx->cfg_contents = "config_version=2,"; 411*e6f8f311SMark Adams amgx->cfg_contents += "determinism_flag=1,"; 412*e6f8f311SMark Adams 413*e6f8f311SMark Adams // Set exact coarse solve 414*e6f8f311SMark Adams PetscCall(PetscOptionsBool("-pc_amgx_exact_coarse_solve", "AmgX AMG Exact Coarse Solve", "", amgx->exact_coarse_solve, &amgx->exact_coarse_solve, NULL)); 415*e6f8f311SMark Adams if (amgx->exact_coarse_solve) { 416*e6f8f311SMark Adams amgx->cfg_contents += "exact_coarse_solve=1,"; 417*e6f8f311SMark Adams } 418*e6f8f311SMark Adams 419*e6f8f311SMark Adams amgx->cfg_contents += "solver(amg)=AMG,"; 420*e6f8f311SMark Adams 421*e6f8f311SMark Adams // Set method 422*e6f8f311SMark Adams std::string def_amg_method = map_reverse_lookup(AmgXControlMap::AMGMethods, amgx->amg_method); 423*e6f8f311SMark Adams PetscCall(PetscStrcpy(option, def_amg_method.c_str())); 424*e6f8f311SMark Adams PetscCall(PetscOptionsString("-pc_amgx_amg_method", "AmgX AMG Method", "", option, option, MAX_PARAM_LEN, NULL)); 425*e6f8f311SMark Adams PetscCheck(AmgXControlMap::AMGMethods.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "AMG Method %s not registered for AmgX.", option); 426*e6f8f311SMark Adams amgx->amg_method = AmgXControlMap::AMGMethods.at(option); 427*e6f8f311SMark Adams amgx->cfg_contents += "amg:algorithm=" + std::string(option) + ","; 428*e6f8f311SMark Adams 429*e6f8f311SMark Adams // Set cycle 430*e6f8f311SMark Adams std::string def_amg_cycle = map_reverse_lookup(AmgXControlMap::AMGCycles, amgx->amg_cycle); 431*e6f8f311SMark Adams PetscCall(PetscStrcpy(option, def_amg_cycle.c_str())); 432*e6f8f311SMark Adams PetscCall(PetscOptionsString("-pc_amgx_amg_cycle", "AmgX AMG Cycle", "", option, option, MAX_PARAM_LEN, NULL)); 433*e6f8f311SMark Adams PetscCheck(AmgXControlMap::AMGCycles.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "AMG Cycle %s not registered for AmgX.", option); 434*e6f8f311SMark Adams amgx->amg_cycle = AmgXControlMap::AMGCycles.at(option); 435*e6f8f311SMark Adams amgx->cfg_contents += "amg:cycle=" + std::string(option) + ","; 436*e6f8f311SMark Adams 437*e6f8f311SMark Adams // Set smoother 438*e6f8f311SMark Adams std::string def_smoother = map_reverse_lookup(AmgXControlMap::Smoothers, amgx->smoother); 439*e6f8f311SMark Adams PetscCall(PetscStrcpy(option, def_smoother.c_str())); 440*e6f8f311SMark Adams PetscCall(PetscOptionsString("-pc_amgx_smoother", "AmgX Smoother", "", option, option, MAX_PARAM_LEN, NULL)); 441*e6f8f311SMark Adams PetscCheck(AmgXControlMap::Smoothers.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Smoother %s not registered for AmgX.", option); 442*e6f8f311SMark Adams amgx->smoother = AmgXControlMap::Smoothers.at(option); 443*e6f8f311SMark Adams amgx->cfg_contents += "amg:smoother(smooth)=" + std::string(option) + ","; 444*e6f8f311SMark Adams 445*e6f8f311SMark Adams if (amgx->smoother == AmgXSmoother::JacobiL1 || amgx->smoother == AmgXSmoother::BlockJacobi) { 446*e6f8f311SMark Adams PetscCall(PetscOptionsScalar("-pc_amgx_jacobi_relaxation_factor", "AmgX AMG Jacobi Relaxation Factor", "", amgx->jacobi_relaxation_factor, &amgx->jacobi_relaxation_factor, NULL)); 447*e6f8f311SMark Adams amgx->cfg_contents += "smooth:relaxation_factor=" + std::to_string(amgx->jacobi_relaxation_factor) + ","; 448*e6f8f311SMark Adams } else if (amgx->smoother == AmgXSmoother::GS || amgx->smoother == AmgXSmoother::MulticolorGS) { 449*e6f8f311SMark Adams PetscCall(PetscOptionsScalar("-pc_amgx_gs_symmetric", "AmgX AMG Gauss Seidel Symmetric", "", amgx->gs_symmetric, &amgx->gs_symmetric, NULL)); 450*e6f8f311SMark Adams amgx->cfg_contents += "smooth:symmetric_GS=" + std::to_string(amgx->gs_symmetric) + ","; 451*e6f8f311SMark Adams } 452*e6f8f311SMark Adams 453*e6f8f311SMark Adams // Set selector 454*e6f8f311SMark Adams std::string def_selector = map_reverse_lookup(AmgXControlMap::Selectors, amgx->selector); 455*e6f8f311SMark Adams PetscCall(PetscStrcpy(option, def_selector.c_str())); 456*e6f8f311SMark Adams PetscCall(PetscOptionsString("-pc_amgx_selector", "AmgX Selector", "", option, option, MAX_PARAM_LEN, NULL)); 457*e6f8f311SMark Adams PetscCheck(AmgXControlMap::Selectors.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Selector %s not registered for AmgX.", option); 458*e6f8f311SMark Adams 459*e6f8f311SMark Adams // Double check that the user has selected an appropriate selector for the AMG method 460*e6f8f311SMark Adams if (amgx->amg_method == AmgXAMGMethod::Classical) { 461*e6f8f311SMark Adams PetscCheck(amgx->selector == AmgXSelector::PMIS || amgx->selector == AmgXSelector::HMIS, amgx->comm, PETSC_ERR_PLIB, "Chosen selector is not used for AmgX Classical AMG: selector=%s", option); 462*e6f8f311SMark Adams amgx->cfg_contents += "amg:interpolator=D2,"; 463*e6f8f311SMark Adams } else if (amgx->amg_method == AmgXAMGMethod::Aggregation) { 464*e6f8f311SMark Adams PetscCheck(amgx->selector == AmgXSelector::Size2 || amgx->selector == AmgXSelector::Size4 || amgx->selector == AmgXSelector::Size8 || amgx->selector == AmgXSelector::MultiPairwise, amgx->comm, PETSC_ERR_PLIB, "Chosen selector is not used for AmgX Aggregation AMG"); 465*e6f8f311SMark Adams } 466*e6f8f311SMark Adams amgx->selector = AmgXControlMap::Selectors.at(option); 467*e6f8f311SMark Adams amgx->cfg_contents += "amg:selector=" + std::string(option) + ","; 468*e6f8f311SMark Adams 469*e6f8f311SMark Adams // Set presweeps 470*e6f8f311SMark Adams PetscCall(PetscOptionsInt("-pc_amgx_presweeps", "AmgX AMG Presweep Count", "", amgx->presweeps, &amgx->presweeps, NULL)); 471*e6f8f311SMark Adams amgx->cfg_contents += "amg:presweeps=" + std::to_string(amgx->presweeps) + ","; 472*e6f8f311SMark Adams 473*e6f8f311SMark Adams // Set postsweeps 474*e6f8f311SMark Adams PetscCall(PetscOptionsInt("-pc_amgx_postsweeps", "AmgX AMG Postsweep Count", "", amgx->postsweeps, &amgx->postsweeps, NULL)); 475*e6f8f311SMark Adams amgx->cfg_contents += "amg:postsweeps=" + std::to_string(amgx->postsweeps) + ","; 476*e6f8f311SMark Adams 477*e6f8f311SMark Adams // Set max levels 478*e6f8f311SMark Adams PetscCall(PetscOptionsInt("-pc_amgx_max_levels", "AmgX AMG Max Level Count", "", amgx->max_levels, &amgx->max_levels, NULL)); 479*e6f8f311SMark Adams amgx->cfg_contents += "amg:max_levels=100,"; 480*e6f8f311SMark Adams 481*e6f8f311SMark Adams // Set dense LU num rows 482*e6f8f311SMark Adams PetscCall(PetscOptionsInt("-pc_amgx_dense_lu_num_rows", "AmgX Dense LU Number of Rows", "", amgx->dense_lu_num_rows, &amgx->dense_lu_num_rows, NULL)); 483*e6f8f311SMark Adams amgx->cfg_contents += "amg:dense_lu_num_rows=" + std::to_string(amgx->dense_lu_num_rows) + ","; 484*e6f8f311SMark Adams 485*e6f8f311SMark Adams // Set strength threshold 486*e6f8f311SMark Adams PetscCall(PetscOptionsScalar("-pc_amgx_strength_threshold", "AmgX AMG Strength Threshold", "", amgx->strength_threshold, &amgx->strength_threshold, NULL)); 487*e6f8f311SMark Adams amgx->cfg_contents += "amg:strength_threshold=" + std::to_string(amgx->strength_threshold) + ","; 488*e6f8f311SMark Adams 489*e6f8f311SMark Adams // Set aggressive_levels 490*e6f8f311SMark Adams PetscCall(PetscOptionsInt("-pc_amgx_aggressive_levels", "AmgX AMG Presweep Count", "", amgx->aggressive_levels, &amgx->aggressive_levels, NULL)); 491*e6f8f311SMark Adams if (amgx->aggressive_levels > 0) { 492*e6f8f311SMark Adams amgx->cfg_contents += "amg:aggressive_levels=" + std::to_string(amgx->aggressive_levels) + ","; 493*e6f8f311SMark Adams } 494*e6f8f311SMark Adams 495*e6f8f311SMark Adams // Set coarse solver 496*e6f8f311SMark Adams std::string def_coarse_solver = map_reverse_lookup(AmgXControlMap::CoarseSolvers, amgx->coarse_solver); 497*e6f8f311SMark Adams PetscCall(PetscStrcpy(option, def_coarse_solver.c_str())); 498*e6f8f311SMark Adams PetscCall(PetscOptionsString("-pc_amgx_coarse_solver", "AmgX CoarseSolver", "", option, option, MAX_PARAM_LEN, NULL)); 499*e6f8f311SMark Adams PetscCheck(AmgXControlMap::CoarseSolvers.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "CoarseSolver %s not registered for AmgX.", option); 500*e6f8f311SMark Adams amgx->coarse_solver = AmgXControlMap::CoarseSolvers.at(option); 501*e6f8f311SMark Adams amgx->cfg_contents += "amg:coarse_solver=" + std::string(option) + ","; 502*e6f8f311SMark Adams 503*e6f8f311SMark Adams // Set max iterations 504*e6f8f311SMark Adams amgx->cfg_contents += "amg:max_iters=1,"; 505*e6f8f311SMark Adams 506*e6f8f311SMark Adams // Set output control parameters 507*e6f8f311SMark Adams PetscCall(PetscOptionsBool("-pc_amgx_print_grid_stats", "AmgX Print Grid Stats", "", amgx->print_grid_stats, &amgx->print_grid_stats, NULL)); 508*e6f8f311SMark Adams 509*e6f8f311SMark Adams if (amgx->print_grid_stats) { 510*e6f8f311SMark Adams amgx->cfg_contents += "amg:print_grid_stats=1,"; 511*e6f8f311SMark Adams } 512*e6f8f311SMark Adams amgx->cfg_contents += "amg:monitor_residual=0"; 513*e6f8f311SMark Adams 514*e6f8f311SMark Adams // Set whether AmgX output will be seen 515*e6f8f311SMark Adams PetscCall(PetscOptionsBool("-pc_amgx_verbose", "Enable output from AmgX", "", amgx->verbose, &amgx->verbose, NULL)); 516*e6f8f311SMark Adams PetscOptionsHeadEnd(); 517*e6f8f311SMark Adams PetscFunctionReturn(0); 518*e6f8f311SMark Adams } 519*e6f8f311SMark Adams 520*e6f8f311SMark Adams static PetscErrorCode PCView_AMGX(PC pc, PetscViewer viewer) 521*e6f8f311SMark Adams { 522*e6f8f311SMark Adams PC_AMGX *amgx = (PC_AMGX *)pc->data; 523*e6f8f311SMark Adams PetscBool iascii; 524*e6f8f311SMark Adams 525*e6f8f311SMark Adams PetscFunctionBegin; 526*e6f8f311SMark Adams PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 527*e6f8f311SMark Adams if (iascii) { 528*e6f8f311SMark Adams std::string output_cfg(amgx->cfg_contents); 529*e6f8f311SMark Adams std::replace(output_cfg.begin(), output_cfg.end(), ',', '\n'); 530*e6f8f311SMark Adams PetscCall(PetscViewerASCIIPrintf(viewer, "\n%s\n", output_cfg.c_str())); 531*e6f8f311SMark Adams } 532*e6f8f311SMark Adams PetscFunctionReturn(0); 533*e6f8f311SMark Adams } 534*e6f8f311SMark Adams 535*e6f8f311SMark Adams /* 536*e6f8f311SMark Adams PCCreate_AMGX - Creates a AmgX preconditioner context, PC_AMGX, 537*e6f8f311SMark Adams and sets this as the private data within the generic preconditioning 538*e6f8f311SMark Adams context, PC, that was created within PCCreate(). 539*e6f8f311SMark Adams 540*e6f8f311SMark Adams Input Parameter: 541*e6f8f311SMark Adams . pc - the preconditioner context 542*e6f8f311SMark Adams 543*e6f8f311SMark Adams Application Interface Routine: PCCreate() 544*e6f8f311SMark Adams */ 545*e6f8f311SMark Adams 546*e6f8f311SMark Adams /*MC 547*e6f8f311SMark Adams PCAMGX - Interface to NVIDIA's AmgX algebraic multigrid 548*e6f8f311SMark Adams 549*e6f8f311SMark Adams Options Database Keys: 550*e6f8f311SMark Adams + -pc_amgx_amg_method <CLASSICAL,AGGREGATION> - set the AMG algorithm to use 551*e6f8f311SMark Adams . -pc_amgx_amg_cycle <V,W,F,CG> - set the AMG cycle type 552*e6f8f311SMark Adams . -pc_amgx_smoother <PCG,PCGF,PBICGSTAB,GMRES,FGMRES,JACOBI_L1,BLOCK_JACOBI,GS,MULTICOLOR_GS,MULTICOLOR_ILU,MULTICOLOR_DILU,CHEBYSHEV_POLY,NOSOLVER> - set the AMG pre/post smoother 553*e6f8f311SMark Adams . -pc_amgx_jacobi_relaxation_factor - set the relaxation factor for Jacobi smoothing 554*e6f8f311SMark Adams . -pc_amgx_gs_symmetric - enforce symmetric Gauss-Seidel smoothing (only applies if GS smoothing is selected) 555*e6f8f311SMark Adams . -pc_amgx_selector <SIZE_2,SIZE_4,SIZE_8,MULTI_PAIRWISE,PMIS,HMIS> - set the AMG coarse selector 556*e6f8f311SMark Adams . -pc_amgx_presweeps - set the number of AMG pre-sweeps 557*e6f8f311SMark Adams . -pc_amgx_postsweeps - set the number of AMG post-sweeps 558*e6f8f311SMark Adams . -pc_amgx_max_levels - set the maximum number of levels in the AMG level hierarchy 559*e6f8f311SMark Adams . -pc_amgx_strength_threshold - set the strength threshold for the AMG coarsening 560*e6f8f311SMark Adams . -pc_amgx_aggressive_levels - set the number of levels (from the finest) that should apply aggressive coarsening 561*e6f8f311SMark Adams . -pc_amgx_coarse_solver <DENSE_LU_SOLVER,NOSOLVER> - set the coarse solve 562*e6f8f311SMark Adams . -pc_amgx_print_grid_stats - output the AMG grid hierarchy to stdout 563*e6f8f311SMark Adams - -pc_amgx_verbose - enable AmgX output 564*e6f8f311SMark Adams 565*e6f8f311SMark Adams Level: intermediate 566*e6f8f311SMark Adams 567*e6f8f311SMark Adams Notes: 568*e6f8f311SMark Adams Preconditioner supplied by the GPU accelerated library AmgX. Implementation will accept host or device pointers, but good performance will require that the KSP is also GPU accelerated so that data is not frequently transferred between host and device. 569*e6f8f311SMark Adams 570*e6f8f311SMark Adams .seealso: `PCGAMG`, `PCHYPRE`, `PCMG`, `PCAmgXGetResources()`, `PCCreate()`, `PCSetType()`, `PCType` (for list of available types), `PC` 571*e6f8f311SMark Adams M*/ 572*e6f8f311SMark Adams 573*e6f8f311SMark Adams PETSC_EXTERN PetscErrorCode PCCreate_AMGX(PC pc) 574*e6f8f311SMark Adams { 575*e6f8f311SMark Adams PC_AMGX *amgx; 576*e6f8f311SMark Adams 577*e6f8f311SMark Adams PetscFunctionBegin; 578*e6f8f311SMark Adams PetscCall(PetscNewLog(pc, &amgx)); 579*e6f8f311SMark Adams pc->ops->apply = PCApply_AMGX; 580*e6f8f311SMark Adams pc->ops->setfromoptions = PCSetFromOptions_AMGX; 581*e6f8f311SMark Adams pc->ops->setup = PCSetUp_AMGX; 582*e6f8f311SMark Adams pc->ops->view = PCView_AMGX; 583*e6f8f311SMark Adams pc->ops->destroy = PCDestroy_AMGX; 584*e6f8f311SMark Adams pc->ops->reset = PCReset_AMGX; 585*e6f8f311SMark Adams pc->data = (void *)amgx; 586*e6f8f311SMark Adams 587*e6f8f311SMark Adams // Set the defaults 588*e6f8f311SMark Adams amgx->selector = AmgXSelector::PMIS; 589*e6f8f311SMark Adams amgx->smoother = AmgXSmoother::BlockJacobi; 590*e6f8f311SMark Adams amgx->amg_method = AmgXAMGMethod::Classical; 591*e6f8f311SMark Adams amgx->coarse_solver = AmgXCoarseSolver::DenseLU; 592*e6f8f311SMark Adams amgx->amg_cycle = AmgXAMGCycle::V; 593*e6f8f311SMark Adams amgx->exact_coarse_solve = PETSC_TRUE; 594*e6f8f311SMark Adams amgx->presweeps = 1; 595*e6f8f311SMark Adams amgx->postsweeps = 1; 596*e6f8f311SMark Adams amgx->max_levels = 100; 597*e6f8f311SMark Adams amgx->strength_threshold = 0.5; 598*e6f8f311SMark Adams amgx->aggressive_levels = 0; 599*e6f8f311SMark Adams amgx->dense_lu_num_rows = 1; 600*e6f8f311SMark Adams amgx->jacobi_relaxation_factor = 0.9; 601*e6f8f311SMark Adams amgx->gs_symmetric = PETSC_FALSE; 602*e6f8f311SMark Adams amgx->print_grid_stats = PETSC_FALSE; 603*e6f8f311SMark Adams amgx->verbose = PETSC_FALSE; 604*e6f8f311SMark Adams amgx->rsrc_init = false; 605*e6f8f311SMark Adams amgx->solve_state_init = false; 606*e6f8f311SMark Adams 607*e6f8f311SMark Adams s_count++; 608*e6f8f311SMark Adams 609*e6f8f311SMark Adams PetscCallCUDA(cudaGetDevice(&amgx->devID)); 610*e6f8f311SMark Adams if (s_count == 1) { 611*e6f8f311SMark Adams PetscCallAmgX(AMGX_initialize()); 612*e6f8f311SMark Adams PetscCallAmgX(AMGX_initialize_plugins()); 613*e6f8f311SMark Adams PetscCallAmgX(AMGX_register_print_callback(&print_callback)); 614*e6f8f311SMark Adams PetscCallAmgX(AMGX_install_signal_handler()); 615*e6f8f311SMark Adams } 616*e6f8f311SMark Adams /* This communicator is not yet known to this system, so we duplicate it and make an internal communicator */ 617*e6f8f311SMark Adams PetscCallMPI(MPI_Comm_dup(PetscObjectComm((PetscObject)pc), &amgx->comm)); 618*e6f8f311SMark Adams PetscCallMPI(MPI_Comm_size(amgx->comm, &amgx->nranks)); 619*e6f8f311SMark Adams PetscCallMPI(MPI_Comm_rank(amgx->comm, &amgx->rank)); 620*e6f8f311SMark Adams 621*e6f8f311SMark Adams amgx_output_messages(amgx); 622*e6f8f311SMark Adams PetscFunctionReturn(0); 623*e6f8f311SMark Adams } 624*e6f8f311SMark Adams 625*e6f8f311SMark Adams /*@ 626*e6f8f311SMark Adams PCAmgXGetResources - get AMGx's internal resource object 627*e6f8f311SMark Adams 628*e6f8f311SMark Adams Not Collective 629*e6f8f311SMark Adams 630*e6f8f311SMark Adams Input Parameters: 631*e6f8f311SMark Adams . pc - the PC 632*e6f8f311SMark Adams 633*e6f8f311SMark Adams Output Parameter: 634*e6f8f311SMark Adams . rsrc_out - pointer to the AMGx resource object 635*e6f8f311SMark Adams 636*e6f8f311SMark Adams Level: advanced 637*e6f8f311SMark Adams 638*e6f8f311SMark Adams .seealso: `PCCreate_AMGX()` 639*e6f8f311SMark Adams @*/ 640*e6f8f311SMark Adams PETSC_EXTERN PetscErrorCode PCAmgXGetResources(PC pc, void* rsrc_out) 641*e6f8f311SMark Adams { 642*e6f8f311SMark Adams PC_AMGX *amgx = (PC_AMGX *)pc->data; 643*e6f8f311SMark Adams 644*e6f8f311SMark Adams PetscFunctionBegin; 645*e6f8f311SMark Adams if (!amgx->rsrc_init) { 646*e6f8f311SMark Adams // Read configuration file 647*e6f8f311SMark Adams PetscCallAmgX(AMGX_config_create(&amgx->cfg, amgx->cfg_contents.c_str())); 648*e6f8f311SMark Adams PetscCallAmgX(AMGX_resources_create(&amgx->rsrc, amgx->cfg, &amgx->comm, 1, &amgx->devID)); 649*e6f8f311SMark Adams amgx->rsrc_init = true; 650*e6f8f311SMark Adams } 651*e6f8f311SMark Adams 652*e6f8f311SMark Adams *static_cast<AMGX_resources_handle*>(rsrc_out) = amgx->rsrc; 653*e6f8f311SMark Adams PetscFunctionReturn(0); 654*e6f8f311SMark Adams } 655*e6f8f311SMark Adams 656