xref: /petsc/src/ksp/pc/impls/amgx/amgx.cxx (revision e6f8f31134103d08b80e713a26ca64d6e7cf8e7b)
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