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