xref: /petsc/src/ksp/pc/impls/amgx/amgx.cxx (revision 9f196a0264fbaf0568fead3a30c861c7ae4cf663)
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*/
110e6b6b59SJacob Faibussowitsch #include <petscdevice_cuda.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 
209371c9d4SSatish Balay enum class AmgXSmoother {
219371c9d4SSatish Balay   PCG,
229371c9d4SSatish Balay   PCGF,
239371c9d4SSatish Balay   PBiCGStab,
249371c9d4SSatish Balay   GMRES,
259371c9d4SSatish Balay   FGMRES,
269371c9d4SSatish Balay   JacobiL1,
279371c9d4SSatish Balay   BlockJacobi,
289371c9d4SSatish Balay   GS,
299371c9d4SSatish Balay   MulticolorGS,
309371c9d4SSatish Balay   MulticolorILU,
319371c9d4SSatish Balay   MulticolorDILU,
329371c9d4SSatish Balay   ChebyshevPoly,
339371c9d4SSatish Balay   NoSolver
349371c9d4SSatish Balay };
359371c9d4SSatish Balay enum class AmgXAMGMethod {
369371c9d4SSatish Balay   Classical,
379371c9d4SSatish Balay   Aggregation
389371c9d4SSatish Balay };
399371c9d4SSatish Balay enum class AmgXSelector {
409371c9d4SSatish Balay   Size2,
419371c9d4SSatish Balay   Size4,
429371c9d4SSatish Balay   Size8,
439371c9d4SSatish Balay   MultiPairwise,
449371c9d4SSatish Balay   PMIS,
459371c9d4SSatish Balay   HMIS
469371c9d4SSatish Balay };
479371c9d4SSatish Balay enum class AmgXCoarseSolver {
489371c9d4SSatish Balay   DenseLU,
499371c9d4SSatish Balay   NoSolver
509371c9d4SSatish Balay };
519371c9d4SSatish Balay enum class AmgXAMGCycle {
529371c9d4SSatish Balay   V,
539371c9d4SSatish Balay   W,
549371c9d4SSatish Balay   F,
559371c9d4SSatish Balay   CG,
569371c9d4SSatish Balay   CGF
579371c9d4SSatish Balay };
58e6f8f311SMark Adams 
599371c9d4SSatish 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
168d71ae5a4SJacob Faibussowitsch static void print_callback(const char *msg, int length)
169d71ae5a4SJacob Faibussowitsch {
170e6f8f311SMark Adams   amgx_output.append(msg);
171e6f8f311SMark Adams }
172e6f8f311SMark Adams 
173e6f8f311SMark Adams // Outputs messages from the AmgX message buffer and clears it
17466976f2fSJacob Faibussowitsch static PetscErrorCode amgx_output_messages(PC_AMGX *amgx)
175d71ae5a4SJacob Faibussowitsch {
176e6f8f311SMark Adams   PetscFunctionBegin;
177e6f8f311SMark Adams   // If AmgX output is enabled and we have a message, output it
178e6f8f311SMark Adams   if (amgx->verbose && !amgx_output.empty()) {
179e6f8f311SMark Adams     // Only a single rank to output the AmgX messages
180e6f8f311SMark Adams     PetscCall(PetscPrintf(amgx->comm, "AMGX: %s", amgx_output.c_str()));
181e6f8f311SMark Adams 
182e6f8f311SMark Adams     // Note that all ranks clear their received output
183e6f8f311SMark Adams     amgx_output.clear();
184e6f8f311SMark Adams   }
1853ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
186e6f8f311SMark Adams }
187e6f8f311SMark Adams 
188e6f8f311SMark Adams // XXX Need to add call in AmgX API that gracefully destroys everything
189e6f8f311SMark Adams // without abort etc.
1909371c9d4SSatish Balay #define PetscCallAmgX(rc) \
1919371c9d4SSatish Balay   do { \
192e6f8f311SMark Adams     AMGX_RC err = (rc); \
193e6f8f311SMark Adams     char    msg[4096]; \
194e6f8f311SMark Adams     switch (err) { \
195d71ae5a4SJacob Faibussowitsch     case AMGX_RC_OK: \
196d71ae5a4SJacob Faibussowitsch       break; \
197d71ae5a4SJacob Faibussowitsch     default: \
198d71ae5a4SJacob Faibussowitsch       AMGX_get_error_string(err, msg, 4096); \
199d71ae5a4SJacob Faibussowitsch       SETERRQ(amgx->comm, PETSC_ERR_LIB, "%s", msg); \
200e6f8f311SMark Adams     } \
201e6f8f311SMark Adams   } while (0)
202e6f8f311SMark Adams 
203e6f8f311SMark Adams /*
204e6f8f311SMark Adams    PCSetUp_AMGX - Prepares for the use of the AmgX preconditioner
205e6f8f311SMark Adams                     by setting data structures and options.
206e6f8f311SMark Adams 
207e6f8f311SMark Adams    Input Parameter:
208e6f8f311SMark Adams .  pc - the preconditioner context
209e6f8f311SMark Adams 
210e6f8f311SMark Adams    Application Interface Routine: PCSetUp()
211e6f8f311SMark Adams 
212f1580f4eSBarry Smith    Note:
213e6f8f311SMark Adams    The interface routine PCSetUp() is not usually called directly by
214e6f8f311SMark Adams    the user, but instead is called by PCApply() if necessary.
215e6f8f311SMark Adams */
216d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCSetUp_AMGX(PC pc)
217d71ae5a4SJacob Faibussowitsch {
218e6f8f311SMark Adams   PC_AMGX  *amgx = (PC_AMGX *)pc->data;
219e6f8f311SMark Adams   Mat       Pmat = pc->pmat;
220e6f8f311SMark Adams   PetscBool is_dev_ptrs;
221e6f8f311SMark Adams 
222e6f8f311SMark Adams   PetscFunctionBegin;
223e6f8f311SMark Adams   PetscCall(PetscObjectTypeCompareAny((PetscObject)Pmat, &is_dev_ptrs, MATAIJCUSPARSE, MATSEQAIJCUSPARSE, MATMPIAIJCUSPARSE, ""));
224e6f8f311SMark Adams 
225e6f8f311SMark Adams   // At the present time, an AmgX matrix is a sequential matrix
226e6f8f311SMark Adams   // Non-sequential/MPI matrices must be adapted to extract the local matrix
227e6f8f311SMark Adams   bool partial_setup_allowed = (pc->setupcalled && pc->flag != DIFFERENT_NONZERO_PATTERN);
228e6f8f311SMark Adams   if (amgx->nranks > 1) {
229e6f8f311SMark Adams     if (partial_setup_allowed) {
230e6f8f311SMark Adams       PetscCall(MatMPIAIJGetLocalMat(Pmat, MAT_REUSE_MATRIX, &amgx->localA));
231e6f8f311SMark Adams     } else {
232e6f8f311SMark Adams       PetscCall(MatMPIAIJGetLocalMat(Pmat, MAT_INITIAL_MATRIX, &amgx->localA));
233e6f8f311SMark Adams     }
234e6f8f311SMark Adams 
23548a46eb9SPierre Jolivet     if (is_dev_ptrs) PetscCall(MatConvert(amgx->localA, MATSEQAIJCUSPARSE, MAT_INPLACE_MATRIX, &amgx->localA));
236e6f8f311SMark Adams   } else {
237e6f8f311SMark Adams     amgx->localA = Pmat;
238e6f8f311SMark Adams   }
239e6f8f311SMark Adams 
240e6f8f311SMark Adams   if (is_dev_ptrs) {
241e6f8f311SMark Adams     PetscCall(MatSeqAIJCUSPARSEGetArrayRead(amgx->localA, &amgx->values));
242e6f8f311SMark Adams   } else {
243e6f8f311SMark Adams     PetscCall(MatSeqAIJGetArrayRead(amgx->localA, &amgx->values));
244e6f8f311SMark Adams   }
245e6f8f311SMark Adams 
246e6f8f311SMark Adams   if (!partial_setup_allowed) {
247e6f8f311SMark Adams     // Initialise resources and matrices
248e6f8f311SMark Adams     if (!amgx->rsrc_init) {
249e6f8f311SMark Adams       // Read configuration file
250e6f8f311SMark Adams       PetscCallAmgX(AMGX_config_create(&amgx->cfg, amgx->cfg_contents.c_str()));
251e6f8f311SMark Adams       PetscCallAmgX(AMGX_resources_create(&amgx->rsrc, amgx->cfg, &amgx->comm, 1, &amgx->devID));
252e6f8f311SMark Adams       amgx->rsrc_init = true;
253e6f8f311SMark Adams     }
254e6f8f311SMark Adams 
255e6f8f311SMark Adams     PetscCheck(!amgx->solve_state_init, amgx->comm, PETSC_ERR_PLIB, "AmgX solve state initialisation already called.");
256e6f8f311SMark Adams     PetscCallAmgX(AMGX_matrix_create(&amgx->A, amgx->rsrc, AMGX_mode_dDDI));
257e6f8f311SMark Adams     PetscCallAmgX(AMGX_vector_create(&amgx->sol, amgx->rsrc, AMGX_mode_dDDI));
258e6f8f311SMark Adams     PetscCallAmgX(AMGX_vector_create(&amgx->rhs, amgx->rsrc, AMGX_mode_dDDI));
259e6f8f311SMark Adams     PetscCallAmgX(AMGX_solver_create(&amgx->solver, amgx->rsrc, AMGX_mode_dDDI, amgx->cfg));
260e6f8f311SMark Adams     amgx->solve_state_init = true;
261e6f8f311SMark Adams 
262e6f8f311SMark Adams     // Extract the CSR data
263e6f8f311SMark Adams     PetscBool       done;
264e6f8f311SMark Adams     const PetscInt *colIndices;
265e6f8f311SMark Adams     const PetscInt *rowOffsets;
266e6f8f311SMark Adams     PetscCall(MatGetRowIJ(amgx->localA, 0, PETSC_FALSE, PETSC_FALSE, &amgx->nLocalRows, &rowOffsets, &colIndices, &done));
267e6f8f311SMark Adams     PetscCheck(done, amgx->comm, PETSC_ERR_PLIB, "MatGetRowIJ was not successful");
268e6f8f311SMark 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);
269e6f8f311SMark Adams 
270e6f8f311SMark Adams     if (is_dev_ptrs) {
271e6f8f311SMark Adams       PetscCallCUDA(cudaMemcpy(&amgx->nnz, &rowOffsets[amgx->nLocalRows], sizeof(int), cudaMemcpyDefault));
272e6f8f311SMark Adams     } else {
273e6f8f311SMark Adams       amgx->nnz = rowOffsets[amgx->nLocalRows];
274e6f8f311SMark Adams     }
275e6f8f311SMark Adams 
276e6f8f311SMark 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);
277e6f8f311SMark Adams 
278e6f8f311SMark Adams     // Allocate space for some partition offsets
279e6f8f311SMark Adams     std::vector<PetscInt> partitionOffsets(amgx->nranks + 1);
280e6f8f311SMark Adams 
281e6f8f311SMark Adams     // Fetch the number of local rows per rank
282e6f8f311SMark Adams     partitionOffsets[0] = 0; /* could use PetscLayoutGetRanges */
283e6f8f311SMark Adams     PetscCallMPI(MPI_Allgather(&amgx->nLocalRows, 1, MPIU_INT, partitionOffsets.data() + 1, 1, MPIU_INT, amgx->comm));
284e6f8f311SMark Adams     std::partial_sum(partitionOffsets.begin(), partitionOffsets.end(), partitionOffsets.begin());
285e6f8f311SMark Adams 
286e6f8f311SMark Adams     // Fetch the number of global rows
287e6f8f311SMark Adams     amgx->nGlobalRows = partitionOffsets[amgx->nranks];
288e6f8f311SMark Adams 
289e6f8f311SMark Adams     PetscCall(MatGetBlockSize(Pmat, &amgx->bSize));
290e6f8f311SMark Adams 
291e6f8f311SMark Adams     // XXX Currently constrained to 32-bit indices, to be changed in the future
292e6f8f311SMark Adams     // Create the distribution and upload the matrix data
293e6f8f311SMark Adams     AMGX_distribution_handle dist;
294e6f8f311SMark Adams     PetscCallAmgX(AMGX_distribution_create(&dist, amgx->cfg));
295e6f8f311SMark Adams     PetscCallAmgX(AMGX_distribution_set_32bit_colindices(dist, true));
296e6f8f311SMark Adams     PetscCallAmgX(AMGX_distribution_set_partition_data(dist, AMGX_DIST_PARTITION_OFFSETS, partitionOffsets.data()));
297e6f8f311SMark 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));
298e6f8f311SMark Adams     PetscCallAmgX(AMGX_solver_setup(amgx->solver, amgx->A));
299e6f8f311SMark Adams     PetscCallAmgX(AMGX_vector_bind(amgx->sol, amgx->A));
300e6f8f311SMark Adams     PetscCallAmgX(AMGX_vector_bind(amgx->rhs, amgx->A));
301e6f8f311SMark Adams 
302e6f8f311SMark Adams     PetscInt nlr = 0;
303e6f8f311SMark Adams     PetscCall(MatRestoreRowIJ(amgx->localA, 0, PETSC_FALSE, PETSC_FALSE, &nlr, &rowOffsets, &colIndices, &done));
304e6f8f311SMark Adams   } else {
305e6f8f311SMark Adams     // The fast path for if the sparsity pattern persists
306e6f8f311SMark Adams     PetscCallAmgX(AMGX_matrix_replace_coefficients(amgx->A, amgx->nLocalRows, amgx->nnz, amgx->values, NULL));
307e6f8f311SMark Adams     PetscCallAmgX(AMGX_solver_resetup(amgx->solver, amgx->A));
308e6f8f311SMark Adams   }
309e6f8f311SMark Adams 
310e6f8f311SMark Adams   if (is_dev_ptrs) {
311e6f8f311SMark Adams     PetscCall(MatSeqAIJCUSPARSERestoreArrayRead(amgx->localA, &amgx->values));
312e6f8f311SMark Adams   } else {
313e6f8f311SMark Adams     PetscCall(MatSeqAIJRestoreArrayRead(amgx->localA, &amgx->values));
314e6f8f311SMark Adams   }
3153ba16761SJacob Faibussowitsch   PetscCall(amgx_output_messages(amgx));
3163ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
317e6f8f311SMark Adams }
318e6f8f311SMark Adams 
319e6f8f311SMark Adams /*
320e6f8f311SMark Adams    PCApply_AMGX - Applies the AmgX preconditioner to a vector.
321e6f8f311SMark Adams 
322e6f8f311SMark Adams    Input Parameters:
323e6f8f311SMark Adams .  pc - the preconditioner context
324e6f8f311SMark Adams .  b - rhs vector
325e6f8f311SMark Adams 
326e6f8f311SMark Adams    Output Parameter:
327e6f8f311SMark Adams .  x - solution vector
328e6f8f311SMark Adams 
329e6f8f311SMark Adams    Application Interface Routine: PCApply()
330e6f8f311SMark Adams  */
331d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCApply_AMGX(PC pc, Vec b, Vec x)
332d71ae5a4SJacob Faibussowitsch {
333e6f8f311SMark Adams   PC_AMGX           *amgx = (PC_AMGX *)pc->data;
334e6f8f311SMark Adams   PetscScalar       *x_;
335e6f8f311SMark Adams   const PetscScalar *b_;
336e6f8f311SMark Adams   PetscBool          is_dev_ptrs;
337e6f8f311SMark Adams 
338e6f8f311SMark Adams   PetscFunctionBegin;
339e6f8f311SMark Adams   PetscCall(PetscObjectTypeCompareAny((PetscObject)x, &is_dev_ptrs, VECCUDA, VECMPICUDA, VECSEQCUDA, ""));
340e6f8f311SMark Adams 
341e6f8f311SMark Adams   if (is_dev_ptrs) {
342e6f8f311SMark Adams     PetscCall(VecCUDAGetArrayWrite(x, &x_));
343e6f8f311SMark Adams     PetscCall(VecCUDAGetArrayRead(b, &b_));
344e6f8f311SMark Adams   } else {
345e6f8f311SMark Adams     PetscCall(VecGetArrayWrite(x, &x_));
346e6f8f311SMark Adams     PetscCall(VecGetArrayRead(b, &b_));
347e6f8f311SMark Adams   }
348e6f8f311SMark Adams 
349e6f8f311SMark Adams   PetscCallAmgX(AMGX_vector_upload(amgx->sol, amgx->nLocalRows, 1, x_));
350e6f8f311SMark Adams   PetscCallAmgX(AMGX_vector_upload(amgx->rhs, amgx->nLocalRows, 1, b_));
351e6f8f311SMark Adams   PetscCallAmgX(AMGX_solver_solve_with_0_initial_guess(amgx->solver, amgx->rhs, amgx->sol));
352e6f8f311SMark Adams 
353e6f8f311SMark Adams   AMGX_SOLVE_STATUS status;
354e6f8f311SMark Adams   PetscCallAmgX(AMGX_solver_get_status(amgx->solver, &status));
355e6f8f311SMark Adams   PetscCall(PCSetErrorIfFailure(pc, static_cast<PetscBool>(status == AMGX_SOLVE_FAILED)));
356e6f8f311SMark 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);
357e6f8f311SMark Adams   PetscCallAmgX(AMGX_vector_download(amgx->sol, x_));
358e6f8f311SMark Adams 
359e6f8f311SMark Adams   if (is_dev_ptrs) {
360e6f8f311SMark Adams     PetscCall(VecCUDARestoreArrayWrite(x, &x_));
361e6f8f311SMark Adams     PetscCall(VecCUDARestoreArrayRead(b, &b_));
362e6f8f311SMark Adams   } else {
363e6f8f311SMark Adams     PetscCall(VecRestoreArrayWrite(x, &x_));
364e6f8f311SMark Adams     PetscCall(VecRestoreArrayRead(b, &b_));
365e6f8f311SMark Adams   }
3663ba16761SJacob Faibussowitsch   PetscCall(amgx_output_messages(amgx));
3673ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
368e6f8f311SMark Adams }
369e6f8f311SMark Adams 
370d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCReset_AMGX(PC pc)
371d71ae5a4SJacob Faibussowitsch {
372e6f8f311SMark Adams   PC_AMGX *amgx = (PC_AMGX *)pc->data;
373e6f8f311SMark Adams 
374e6f8f311SMark Adams   PetscFunctionBegin;
375e6f8f311SMark Adams   if (amgx->solve_state_init) {
376e6f8f311SMark Adams     PetscCallAmgX(AMGX_solver_destroy(amgx->solver));
377e6f8f311SMark Adams     PetscCallAmgX(AMGX_matrix_destroy(amgx->A));
378e6f8f311SMark Adams     PetscCallAmgX(AMGX_vector_destroy(amgx->sol));
379e6f8f311SMark Adams     PetscCallAmgX(AMGX_vector_destroy(amgx->rhs));
380e6f8f311SMark Adams     if (amgx->nranks > 1) PetscCall(MatDestroy(&amgx->localA));
3813ba16761SJacob Faibussowitsch     PetscCall(amgx_output_messages(amgx));
382e6f8f311SMark Adams     amgx->solve_state_init = false;
383e6f8f311SMark Adams   }
3843ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
385e6f8f311SMark Adams }
386e6f8f311SMark Adams 
387e6f8f311SMark Adams /*
388e6f8f311SMark Adams    PCDestroy_AMGX - Destroys the private context for the AmgX preconditioner
389e6f8f311SMark Adams    that was created with PCCreate_AMGX().
390e6f8f311SMark Adams 
391e6f8f311SMark Adams    Input Parameter:
392e6f8f311SMark Adams .  pc - the preconditioner context
393e6f8f311SMark Adams 
394e6f8f311SMark Adams    Application Interface Routine: PCDestroy()
395e6f8f311SMark Adams */
396d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCDestroy_AMGX(PC pc)
397d71ae5a4SJacob Faibussowitsch {
398e6f8f311SMark Adams   PC_AMGX *amgx = (PC_AMGX *)pc->data;
399e6f8f311SMark Adams 
400e6f8f311SMark Adams   PetscFunctionBegin;
401e6f8f311SMark Adams   /* decrease the number of instances, only the last instance need to destroy resource and finalizing AmgX */
402e6f8f311SMark Adams   if (s_count == 1) {
403e6f8f311SMark Adams     /* can put this in a PCAMGXInitializePackage method */
404e6f8f311SMark Adams     PetscCheck(amgx->rsrc != nullptr, PETSC_COMM_SELF, PETSC_ERR_PLIB, "s_rsrc == NULL");
405e6f8f311SMark Adams     PetscCallAmgX(AMGX_resources_destroy(amgx->rsrc));
406e6f8f311SMark Adams     /* destroy config (need to use AMGX_SAFE_CALL after this point) */
407e6f8f311SMark Adams     PetscCallAmgX(AMGX_config_destroy(amgx->cfg));
408e6f8f311SMark Adams     PetscCallAmgX(AMGX_finalize_plugins());
409e6f8f311SMark Adams     PetscCallAmgX(AMGX_finalize());
410e6f8f311SMark Adams     PetscCallMPI(MPI_Comm_free(&amgx->comm));
411e6f8f311SMark Adams   } else {
412e6f8f311SMark Adams     PetscCallAmgX(AMGX_config_destroy(amgx->cfg));
413e6f8f311SMark Adams   }
414e6f8f311SMark Adams   s_count -= 1;
415e6f8f311SMark Adams   PetscCall(PetscFree(amgx));
4163ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
417e6f8f311SMark Adams }
418e6f8f311SMark Adams 
419e6f8f311SMark Adams template <class T>
420d71ae5a4SJacob Faibussowitsch std::string map_reverse_lookup(const std::map<std::string, T> &map, const T &key)
421d71ae5a4SJacob Faibussowitsch {
422e6f8f311SMark Adams   for (auto const &m : map) {
423ad540459SPierre Jolivet     if (m.second == key) return m.first;
424e6f8f311SMark Adams   }
425e6f8f311SMark Adams   return "";
426e6f8f311SMark Adams }
427e6f8f311SMark Adams 
428ce78bad3SBarry Smith static PetscErrorCode PCSetFromOptions_AMGX(PC pc, PetscOptionItems PetscOptionsObject)
429d71ae5a4SJacob Faibussowitsch {
430e6f8f311SMark Adams   PC_AMGX      *amgx          = (PC_AMGX *)pc->data;
431e6f8f311SMark Adams   constexpr int MAX_PARAM_LEN = 128;
432e6f8f311SMark Adams   char          option[MAX_PARAM_LEN];
433e6f8f311SMark Adams 
434e6f8f311SMark Adams   PetscFunctionBegin;
435e6f8f311SMark Adams   PetscOptionsHeadBegin(PetscOptionsObject, "AmgX options");
436e6f8f311SMark Adams   amgx->cfg_contents = "config_version=2,";
437e6f8f311SMark Adams   amgx->cfg_contents += "determinism_flag=1,";
438e6f8f311SMark Adams 
439e6f8f311SMark Adams   // Set exact coarse solve
440e6f8f311SMark Adams   PetscCall(PetscOptionsBool("-pc_amgx_exact_coarse_solve", "AmgX AMG Exact Coarse Solve", "", amgx->exact_coarse_solve, &amgx->exact_coarse_solve, NULL));
441ad540459SPierre Jolivet   if (amgx->exact_coarse_solve) amgx->cfg_contents += "exact_coarse_solve=1,";
442e6f8f311SMark Adams 
443e6f8f311SMark Adams   amgx->cfg_contents += "solver(amg)=AMG,";
444e6f8f311SMark Adams 
445e6f8f311SMark Adams   // Set method
446e6f8f311SMark Adams   std::string def_amg_method = map_reverse_lookup(AmgXControlMap::AMGMethods, amgx->amg_method);
447c6a7a370SJeremy L Thompson   PetscCall(PetscStrncpy(option, def_amg_method.c_str(), sizeof(option)));
448e6f8f311SMark Adams   PetscCall(PetscOptionsString("-pc_amgx_amg_method", "AmgX AMG Method", "", option, option, MAX_PARAM_LEN, NULL));
449e6f8f311SMark Adams   PetscCheck(AmgXControlMap::AMGMethods.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "AMG Method %s not registered for AmgX.", option);
450e6f8f311SMark Adams   amgx->amg_method = AmgXControlMap::AMGMethods.at(option);
451e6f8f311SMark Adams   amgx->cfg_contents += "amg:algorithm=" + std::string(option) + ",";
452e6f8f311SMark Adams 
453e6f8f311SMark Adams   // Set cycle
454e6f8f311SMark Adams   std::string def_amg_cycle = map_reverse_lookup(AmgXControlMap::AMGCycles, amgx->amg_cycle);
455c6a7a370SJeremy L Thompson   PetscCall(PetscStrncpy(option, def_amg_cycle.c_str(), sizeof(option)));
456e6f8f311SMark Adams   PetscCall(PetscOptionsString("-pc_amgx_amg_cycle", "AmgX AMG Cycle", "", option, option, MAX_PARAM_LEN, NULL));
457e6f8f311SMark Adams   PetscCheck(AmgXControlMap::AMGCycles.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "AMG Cycle %s not registered for AmgX.", option);
458e6f8f311SMark Adams   amgx->amg_cycle = AmgXControlMap::AMGCycles.at(option);
459e6f8f311SMark Adams   amgx->cfg_contents += "amg:cycle=" + std::string(option) + ",";
460e6f8f311SMark Adams 
461e6f8f311SMark Adams   // Set smoother
462e6f8f311SMark Adams   std::string def_smoother = map_reverse_lookup(AmgXControlMap::Smoothers, amgx->smoother);
463c6a7a370SJeremy L Thompson   PetscCall(PetscStrncpy(option, def_smoother.c_str(), sizeof(option)));
464e6f8f311SMark Adams   PetscCall(PetscOptionsString("-pc_amgx_smoother", "AmgX Smoother", "", option, option, MAX_PARAM_LEN, NULL));
465e6f8f311SMark Adams   PetscCheck(AmgXControlMap::Smoothers.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Smoother %s not registered for AmgX.", option);
466e6f8f311SMark Adams   amgx->smoother = AmgXControlMap::Smoothers.at(option);
467e6f8f311SMark Adams   amgx->cfg_contents += "amg:smoother(smooth)=" + std::string(option) + ",";
468e6f8f311SMark Adams 
469e6f8f311SMark Adams   if (amgx->smoother == AmgXSmoother::JacobiL1 || amgx->smoother == AmgXSmoother::BlockJacobi) {
470e6f8f311SMark Adams     PetscCall(PetscOptionsScalar("-pc_amgx_jacobi_relaxation_factor", "AmgX AMG Jacobi Relaxation Factor", "", amgx->jacobi_relaxation_factor, &amgx->jacobi_relaxation_factor, NULL));
471e6f8f311SMark Adams     amgx->cfg_contents += "smooth:relaxation_factor=" + std::to_string(amgx->jacobi_relaxation_factor) + ",";
472e6f8f311SMark Adams   } else if (amgx->smoother == AmgXSmoother::GS || amgx->smoother == AmgXSmoother::MulticolorGS) {
473e6f8f311SMark Adams     PetscCall(PetscOptionsScalar("-pc_amgx_gs_symmetric", "AmgX AMG Gauss Seidel Symmetric", "", amgx->gs_symmetric, &amgx->gs_symmetric, NULL));
474e6f8f311SMark Adams     amgx->cfg_contents += "smooth:symmetric_GS=" + std::to_string(amgx->gs_symmetric) + ",";
475e6f8f311SMark Adams   }
476e6f8f311SMark Adams 
477e6f8f311SMark Adams   // Set selector
478e6f8f311SMark Adams   std::string def_selector = map_reverse_lookup(AmgXControlMap::Selectors, amgx->selector);
479c6a7a370SJeremy L Thompson   PetscCall(PetscStrncpy(option, def_selector.c_str(), sizeof(option)));
480e6f8f311SMark Adams   PetscCall(PetscOptionsString("-pc_amgx_selector", "AmgX Selector", "", option, option, MAX_PARAM_LEN, NULL));
481e6f8f311SMark Adams   PetscCheck(AmgXControlMap::Selectors.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Selector %s not registered for AmgX.", option);
482e6f8f311SMark Adams 
483e6f8f311SMark Adams   // Double check that the user has selected an appropriate selector for the AMG method
484e6f8f311SMark Adams   if (amgx->amg_method == AmgXAMGMethod::Classical) {
485e6f8f311SMark 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);
486e6f8f311SMark Adams     amgx->cfg_contents += "amg:interpolator=D2,";
487e6f8f311SMark Adams   } else if (amgx->amg_method == AmgXAMGMethod::Aggregation) {
488e6f8f311SMark 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");
489e6f8f311SMark Adams   }
490e6f8f311SMark Adams   amgx->selector = AmgXControlMap::Selectors.at(option);
491e6f8f311SMark Adams   amgx->cfg_contents += "amg:selector=" + std::string(option) + ",";
492e6f8f311SMark Adams 
493e6f8f311SMark Adams   // Set presweeps
494e6f8f311SMark Adams   PetscCall(PetscOptionsInt("-pc_amgx_presweeps", "AmgX AMG Presweep Count", "", amgx->presweeps, &amgx->presweeps, NULL));
495e6f8f311SMark Adams   amgx->cfg_contents += "amg:presweeps=" + std::to_string(amgx->presweeps) + ",";
496e6f8f311SMark Adams 
497e6f8f311SMark Adams   // Set postsweeps
498e6f8f311SMark Adams   PetscCall(PetscOptionsInt("-pc_amgx_postsweeps", "AmgX AMG Postsweep Count", "", amgx->postsweeps, &amgx->postsweeps, NULL));
499e6f8f311SMark Adams   amgx->cfg_contents += "amg:postsweeps=" + std::to_string(amgx->postsweeps) + ",";
500e6f8f311SMark Adams 
501e6f8f311SMark Adams   // Set max levels
502e6f8f311SMark Adams   PetscCall(PetscOptionsInt("-pc_amgx_max_levels", "AmgX AMG Max Level Count", "", amgx->max_levels, &amgx->max_levels, NULL));
5030bdf8ccaSPierre Jolivet   amgx->cfg_contents += "amg:max_levels=" + std::to_string(amgx->max_levels) + ",";
504e6f8f311SMark Adams 
505e6f8f311SMark Adams   // Set dense LU num rows
506e6f8f311SMark 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));
507e6f8f311SMark Adams   amgx->cfg_contents += "amg:dense_lu_num_rows=" + std::to_string(amgx->dense_lu_num_rows) + ",";
508e6f8f311SMark Adams 
509e6f8f311SMark Adams   // Set strength threshold
510e6f8f311SMark Adams   PetscCall(PetscOptionsScalar("-pc_amgx_strength_threshold", "AmgX AMG Strength Threshold", "", amgx->strength_threshold, &amgx->strength_threshold, NULL));
511e6f8f311SMark Adams   amgx->cfg_contents += "amg:strength_threshold=" + std::to_string(amgx->strength_threshold) + ",";
512e6f8f311SMark Adams 
513e6f8f311SMark Adams   // Set aggressive_levels
514e6f8f311SMark Adams   PetscCall(PetscOptionsInt("-pc_amgx_aggressive_levels", "AmgX AMG Presweep Count", "", amgx->aggressive_levels, &amgx->aggressive_levels, NULL));
515ad540459SPierre Jolivet   if (amgx->aggressive_levels > 0) amgx->cfg_contents += "amg:aggressive_levels=" + std::to_string(amgx->aggressive_levels) + ",";
516e6f8f311SMark Adams 
517e6f8f311SMark Adams   // Set coarse solver
518e6f8f311SMark Adams   std::string def_coarse_solver = map_reverse_lookup(AmgXControlMap::CoarseSolvers, amgx->coarse_solver);
519c6a7a370SJeremy L Thompson   PetscCall(PetscStrncpy(option, def_coarse_solver.c_str(), sizeof(option)));
520e6f8f311SMark Adams   PetscCall(PetscOptionsString("-pc_amgx_coarse_solver", "AmgX CoarseSolver", "", option, option, MAX_PARAM_LEN, NULL));
521e6f8f311SMark Adams   PetscCheck(AmgXControlMap::CoarseSolvers.count(option) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "CoarseSolver %s not registered for AmgX.", option);
522e6f8f311SMark Adams   amgx->coarse_solver = AmgXControlMap::CoarseSolvers.at(option);
523e6f8f311SMark Adams   amgx->cfg_contents += "amg:coarse_solver=" + std::string(option) + ",";
524e6f8f311SMark Adams 
525e6f8f311SMark Adams   // Set max iterations
526e6f8f311SMark Adams   amgx->cfg_contents += "amg:max_iters=1,";
527e6f8f311SMark Adams 
528e6f8f311SMark Adams   // Set output control parameters
529e6f8f311SMark Adams   PetscCall(PetscOptionsBool("-pc_amgx_print_grid_stats", "AmgX Print Grid Stats", "", amgx->print_grid_stats, &amgx->print_grid_stats, NULL));
530e6f8f311SMark Adams 
531ad540459SPierre Jolivet   if (amgx->print_grid_stats) amgx->cfg_contents += "amg:print_grid_stats=1,";
532e6f8f311SMark Adams   amgx->cfg_contents += "amg:monitor_residual=0";
533e6f8f311SMark Adams 
534e6f8f311SMark Adams   // Set whether AmgX output will be seen
535e6f8f311SMark Adams   PetscCall(PetscOptionsBool("-pc_amgx_verbose", "Enable output from AmgX", "", amgx->verbose, &amgx->verbose, NULL));
536e6f8f311SMark Adams   PetscOptionsHeadEnd();
5373ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
538e6f8f311SMark Adams }
539e6f8f311SMark Adams 
540d71ae5a4SJacob Faibussowitsch static PetscErrorCode PCView_AMGX(PC pc, PetscViewer viewer)
541d71ae5a4SJacob Faibussowitsch {
542e6f8f311SMark Adams   PC_AMGX  *amgx = (PC_AMGX *)pc->data;
543*9f196a02SMartin Diehl   PetscBool isascii;
544e6f8f311SMark Adams 
545e6f8f311SMark Adams   PetscFunctionBegin;
546*9f196a02SMartin Diehl   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
547*9f196a02SMartin Diehl   if (isascii) {
548e6f8f311SMark Adams     std::string output_cfg(amgx->cfg_contents);
549e6f8f311SMark Adams     std::replace(output_cfg.begin(), output_cfg.end(), ',', '\n');
550e6f8f311SMark Adams     PetscCall(PetscViewerASCIIPrintf(viewer, "\n%s\n", output_cfg.c_str()));
551e6f8f311SMark Adams   }
5523ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
553e6f8f311SMark Adams }
554e6f8f311SMark Adams 
555e6f8f311SMark Adams /*MC
556e6f8f311SMark Adams      PCAMGX - Interface to NVIDIA's AmgX algebraic multigrid
557e6f8f311SMark Adams 
558e6f8f311SMark Adams    Options Database Keys:
559e6f8f311SMark Adams +    -pc_amgx_amg_method <CLASSICAL,AGGREGATION> - set the AMG algorithm to use
560e6f8f311SMark Adams .    -pc_amgx_amg_cycle <V,W,F,CG> - set the AMG cycle type
561e6f8f311SMark 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
562e6f8f311SMark Adams .    -pc_amgx_jacobi_relaxation_factor - set the relaxation factor for Jacobi smoothing
563e6f8f311SMark Adams .    -pc_amgx_gs_symmetric - enforce symmetric Gauss-Seidel smoothing (only applies if GS smoothing is selected)
564e6f8f311SMark Adams .    -pc_amgx_selector <SIZE_2,SIZE_4,SIZE_8,MULTI_PAIRWISE,PMIS,HMIS> - set the AMG coarse selector
565e6f8f311SMark Adams .    -pc_amgx_presweeps - set the number of AMG pre-sweeps
566e6f8f311SMark Adams .    -pc_amgx_postsweeps - set the number of AMG post-sweeps
567e6f8f311SMark Adams .    -pc_amgx_max_levels - set the maximum number of levels in the AMG level hierarchy
568e6f8f311SMark Adams .    -pc_amgx_strength_threshold - set the strength threshold for the AMG coarsening
569e6f8f311SMark Adams .    -pc_amgx_aggressive_levels - set the number of levels (from the finest) that should apply aggressive coarsening
570e6f8f311SMark Adams .    -pc_amgx_coarse_solver <DENSE_LU_SOLVER,NOSOLVER> - set the coarse solve
571e6f8f311SMark Adams .    -pc_amgx_print_grid_stats - output the AMG grid hierarchy to stdout
572e6f8f311SMark Adams -    -pc_amgx_verbose - enable AmgX output
573e6f8f311SMark Adams 
574e6f8f311SMark Adams    Level: intermediate
575e6f8f311SMark Adams 
576f1580f4eSBarry Smith    Note:
577f1580f4eSBarry Smith      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.
578e6f8f311SMark Adams 
579562efe2eSBarry Smith .seealso: [](ch_ksp), `PCGAMG`, `PCHYPRE`, `PCMG`, `PCAmgXGetResources()`, `PCCreate()`, `PCSetType()`, `PCType` (for list of available types), `PC`
580e6f8f311SMark Adams M*/
581e6f8f311SMark Adams 
582d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PCCreate_AMGX(PC pc)
583d71ae5a4SJacob Faibussowitsch {
584e6f8f311SMark Adams   PC_AMGX *amgx;
585e6f8f311SMark Adams 
586e6f8f311SMark Adams   PetscFunctionBegin;
5874dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&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 
6303ba16761SJacob Faibussowitsch   PetscCall(amgx_output_messages(amgx));
6313ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
632e6f8f311SMark Adams }
633e6f8f311SMark Adams 
634a22370e2Smarkadams4 /*@C
635e6f8f311SMark Adams   PCAmgXGetResources - get AMGx's internal resource object
636e6f8f311SMark Adams 
637cc4c1da9SBarry Smith   Not Collective, No Fortran Support
638e6f8f311SMark Adams 
639f1580f4eSBarry Smith   Input Parameter:
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 
647562efe2eSBarry Smith .seealso: [](ch_ksp), `PCAMGX`, `PC`, `PCGAMG`
648e6f8f311SMark Adams @*/
649d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PCAmgXGetResources(PC pc, void *rsrc_out)
650d71ae5a4SJacob Faibussowitsch {
651e6f8f311SMark Adams   PC_AMGX *amgx = (PC_AMGX *)pc->data;
652e6f8f311SMark Adams 
653e6f8f311SMark Adams   PetscFunctionBegin;
654e6f8f311SMark Adams   if (!amgx->rsrc_init) {
655e6f8f311SMark Adams     // Read configuration file
656e6f8f311SMark Adams     PetscCallAmgX(AMGX_config_create(&amgx->cfg, amgx->cfg_contents.c_str()));
657e6f8f311SMark Adams     PetscCallAmgX(AMGX_resources_create(&amgx->rsrc, amgx->cfg, &amgx->comm, 1, &amgx->devID));
658e6f8f311SMark Adams     amgx->rsrc_init = true;
659e6f8f311SMark Adams   }
660e6f8f311SMark Adams   *static_cast<AMGX_resources_handle *>(rsrc_out) = amgx->rsrc;
6613ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
662e6f8f311SMark Adams }
663