xref: /petsc/src/ksp/pc/impls/gamg/gamg.c (revision 2a8071de6fa9a9762c1783d32d2ca09b23d2a0c5)
1 /*
2  GAMG geometric-algebric multigrid PC - Mark Adams 2011
3  */
4 #include <petsc/private/matimpl.h>
5 #include <../src/ksp/pc/impls/gamg/gamg.h>            /*I "petscpc.h" I*/
6 #include <../src/ksp/ksp/impls/cheby/chebyshevimpl.h> /*I "petscksp.h" I*/
7 
8 #if defined(PETSC_HAVE_CUDA)
9   #include <cuda_runtime.h>
10 #endif
11 
12 #if defined(PETSC_HAVE_HIP)
13   #include <hip/hip_runtime.h>
14 #endif
15 
16 PetscLogEvent petsc_gamg_setup_events[GAMG_NUM_SET];
17 PetscLogEvent petsc_gamg_setup_matmat_events[PETSC_MG_MAXLEVELS][3];
18 
19 // #define GAMG_STAGES
20 #if defined(GAMG_STAGES)
21 static PetscLogStage gamg_stages[PETSC_MG_MAXLEVELS];
22 #endif
23 
24 static PetscFunctionList GAMGList = NULL;
25 static PetscBool         PCGAMGPackageInitialized;
26 
27 static PetscErrorCode PCReset_GAMG(PC pc)
28 {
29   PC_MG   *mg      = (PC_MG *)pc->data;
30   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
31 
32   PetscFunctionBegin;
33   PetscCall(PetscFree(pc_gamg->data));
34   pc_gamg->data_sz = 0;
35   PetscCall(PetscFree(pc_gamg->orig_data));
36   for (PetscInt level = 0; level < PETSC_MG_MAXLEVELS; level++) {
37     mg->min_eigen_DinvA[level] = 0;
38     mg->max_eigen_DinvA[level] = 0;
39   }
40   pc_gamg->emin = 0;
41   pc_gamg->emax = 0;
42   PetscCall(PCReset_MG(pc));
43   PetscFunctionReturn(PETSC_SUCCESS);
44 }
45 
46 /*
47    PCGAMGCreateLevel_GAMG: create coarse op with RAP.  repartition and/or reduce number
48      of active processors.
49 
50    Input Parameter:
51    . pc - parameters + side effect: coarse data in 'pc_gamg->data' and
52           'pc_gamg->data_sz' are changed via repartitioning/reduction.
53    . Amat_fine - matrix on this fine (k) level
54    . cr_bs - coarse block size
55    In/Output Parameter:
56    . a_P_inout - prolongation operator to the next level (k-->k-1)
57    . a_nactive_proc - number of active procs
58    Output Parameter:
59    . a_Amat_crs - coarse matrix that is created (k-1)
60 */
61 static PetscErrorCode PCGAMGCreateLevel_GAMG(PC pc, Mat Amat_fine, PetscInt cr_bs, Mat *a_P_inout, Mat *a_Amat_crs, PetscMPIInt *a_nactive_proc, IS *Pcolumnperm, PetscBool is_last)
62 {
63   PC_MG      *mg      = (PC_MG *)pc->data;
64   PC_GAMG    *pc_gamg = (PC_GAMG *)mg->innerctx;
65   Mat         Cmat, Pold = *a_P_inout;
66   MPI_Comm    comm;
67   PetscMPIInt rank, size, new_size, nactive = *a_nactive_proc;
68   PetscInt    ncrs_eq, ncrs, f_bs;
69 
70   PetscFunctionBegin;
71   PetscCall(PetscObjectGetComm((PetscObject)Amat_fine, &comm));
72   PetscCallMPI(MPI_Comm_rank(comm, &rank));
73   PetscCallMPI(MPI_Comm_size(comm, &size));
74   PetscCall(MatGetBlockSize(Amat_fine, &f_bs));
75   PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_PTAP], 0, 0, 0, 0));
76   PetscCall(PetscLogEventBegin(petsc_gamg_setup_matmat_events[pc_gamg->current_level][1], 0, 0, 0, 0));
77   PetscCall(MatPtAP(Amat_fine, Pold, MAT_INITIAL_MATRIX, 2.0, &Cmat));
78   PetscCall(PetscLogEventEnd(petsc_gamg_setup_matmat_events[pc_gamg->current_level][1], 0, 0, 0, 0));
79   PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_PTAP], 0, 0, 0, 0));
80 
81   if (Pcolumnperm) *Pcolumnperm = NULL;
82 
83   /* set 'ncrs' (nodes), 'ncrs_eq' (equations)*/
84   PetscCall(MatGetLocalSize(Cmat, &ncrs_eq, NULL));
85   if (pc_gamg->data_cell_rows > 0) {
86     ncrs = pc_gamg->data_sz / pc_gamg->data_cell_cols / pc_gamg->data_cell_rows;
87   } else {
88     PetscInt bs;
89     PetscCall(MatGetBlockSize(Cmat, &bs));
90     ncrs = ncrs_eq / bs;
91   }
92   /* get number of PEs to make active 'new_size', reduce, can be any integer 1-P */
93   if (pc_gamg->level_reduction_factors[pc_gamg->current_level] == 0 && PetscDefined(HAVE_CUDA) && pc_gamg->current_level == 0) { /* 0 turns reducing to 1 process/device on; do for HIP, etc. */
94 #if defined(PETSC_HAVE_CUDA)
95     PetscShmComm pshmcomm;
96     PetscMPIInt  locrank;
97     MPI_Comm     loccomm;
98     PetscInt     s_nnodes, r_nnodes, new_new_size;
99     cudaError_t  cerr;
100     int          devCount;
101     PetscCall(PetscShmCommGet(comm, &pshmcomm));
102     PetscCall(PetscShmCommGetMpiShmComm(pshmcomm, &loccomm));
103     PetscCallMPI(MPI_Comm_rank(loccomm, &locrank));
104     s_nnodes = !locrank;
105     PetscCall(MPIU_Allreduce(&s_nnodes, &r_nnodes, 1, MPIU_INT, MPI_SUM, comm));
106     PetscCheck((size % r_nnodes) == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "odd number of nodes np=%d nnodes%" PetscInt_FMT, size, r_nnodes);
107     devCount = 0;
108     cerr     = cudaGetDeviceCount(&devCount);
109     cudaGetLastError();                         /* Reset the last error */
110     if (cerr == cudaSuccess && devCount >= 1) { /* There are devices, else go to heuristic */
111       new_new_size = r_nnodes * devCount;
112       new_size     = new_new_size;
113       PetscCall(PetscInfo(pc, "%s: Fine grid with Cuda. %" PetscInt_FMT " nodes. Change new active set size %d --> %d (devCount=%d #nodes=%" PetscInt_FMT ")\n", ((PetscObject)pc)->prefix, r_nnodes, nactive, new_size, devCount, r_nnodes));
114     } else {
115       PetscCall(PetscInfo(pc, "%s: With Cuda but no device. Use heuristics.\n", ((PetscObject)pc)->prefix));
116       goto HEURISTIC;
117     }
118 #else
119     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "should not be here");
120 #endif
121   } else if (pc_gamg->level_reduction_factors[pc_gamg->current_level] > 0) {
122     PetscCheck(nactive % pc_gamg->level_reduction_factors[pc_gamg->current_level] == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "odd number of active process %d wrt reduction factor %" PetscInt_FMT, nactive, pc_gamg->level_reduction_factors[pc_gamg->current_level]);
123     new_size = nactive / pc_gamg->level_reduction_factors[pc_gamg->current_level];
124     PetscCall(PetscInfo(pc, "%s: Manually setting reduction to %d active processes (%d/%" PetscInt_FMT ")\n", ((PetscObject)pc)->prefix, new_size, nactive, pc_gamg->level_reduction_factors[pc_gamg->current_level]));
125   } else if (is_last && !pc_gamg->use_parallel_coarse_grid_solver) {
126     new_size = 1;
127     PetscCall(PetscInfo(pc, "%s: Force coarsest grid reduction to %d active processes\n", ((PetscObject)pc)->prefix, new_size));
128   } else {
129     PetscInt ncrs_eq_glob;
130 #if defined(PETSC_HAVE_CUDA)
131   HEURISTIC:
132 #endif
133     PetscCall(MatGetSize(Cmat, &ncrs_eq_glob, NULL));
134     new_size = (PetscMPIInt)((float)ncrs_eq_glob / (float)pc_gamg->min_eq_proc + 0.5); /* hardwire min. number of eq/proc */
135     if (!new_size) new_size = 1;                                                       /* not likely, possible? */
136     else if (new_size >= nactive) new_size = nactive;                                  /* no change, rare */
137     PetscCall(PetscInfo(pc, "%s: Coarse grid reduction from %d to %d active processes\n", ((PetscObject)pc)->prefix, nactive, new_size));
138   }
139   if (new_size == nactive) {
140     *a_Amat_crs = Cmat; /* output - no repartitioning or reduction - could bail here */
141     if (new_size < size) {
142       /* odd case where multiple coarse grids are on one processor or no coarsening ... */
143       PetscCall(PetscInfo(pc, "%s: reduced grid using same number of processors (%d) as last grid (use larger coarse grid)\n", ((PetscObject)pc)->prefix, nactive));
144       if (pc_gamg->cpu_pin_coarse_grids) {
145         PetscCall(MatBindToCPU(*a_Amat_crs, PETSC_TRUE));
146         PetscCall(MatBindToCPU(*a_P_inout, PETSC_TRUE));
147       }
148     }
149     /* we know that the grid structure can be reused in MatPtAP */
150   } else { /* reduce active processors - we know that the grid structure can NOT be reused in MatPtAP */
151     PetscInt *counts, *newproc_idx, ii, jj, kk, strideNew, *tidx, ncrs_new, ncrs_eq_new, nloc_old, expand_factor = 1, rfactor = 1;
152     IS        is_eq_newproc, is_eq_num, is_eq_num_prim, new_eq_indices;
153     PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_REDUCE], 0, 0, 0, 0));
154     nloc_old = ncrs_eq / cr_bs;
155     PetscCheck(ncrs_eq % cr_bs == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "ncrs_eq %" PetscInt_FMT " not divisible by cr_bs %" PetscInt_FMT, ncrs_eq, cr_bs);
156     /* get new_size and rfactor */
157     if (pc_gamg->layout_type == PCGAMG_LAYOUT_SPREAD || !pc_gamg->repart) {
158       /* find factor */
159       if (new_size == 1) rfactor = size; /* don't modify */
160       else {
161         PetscReal best_fact = 0.;
162         jj                  = -1;
163         for (kk = 1; kk <= size; kk++) {
164           if (!(size % kk)) { /* a candidate */
165             PetscReal nactpe = (PetscReal)size / (PetscReal)kk, fact = nactpe / (PetscReal)new_size;
166             if (fact > 1.0) fact = 1. / fact; /* keep fact < 1 */
167             if (fact > best_fact) {
168               best_fact = fact;
169               jj        = kk;
170             }
171           }
172         }
173         if (jj != -1) rfactor = jj;
174         else rfactor = 1; /* a prime */
175         if (pc_gamg->layout_type == PCGAMG_LAYOUT_COMPACT) expand_factor = 1;
176         else expand_factor = rfactor;
177       }
178       new_size = size / rfactor; /* make new size one that is factor */
179       if (new_size == nactive) { /* no repartitioning or reduction, bail out because nested here (rare) */
180         *a_Amat_crs = Cmat;
181         PetscCall(PetscInfo(pc, "%s: Finding factorable processor set stopped reduction: new_size=%d, neq(loc)=%" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, new_size, ncrs_eq));
182         PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_REDUCE], 0, 0, 0, 0));
183         PetscFunctionReturn(PETSC_SUCCESS);
184       }
185     }
186     /* make 'is_eq_newproc' */
187     PetscCall(PetscMalloc1(size, &counts));
188     if (pc_gamg->repart) { /* Repartition Cmat_{k} and move columns of P^{k}_{k-1} and coordinates of primal part accordingly */
189       Mat adj;
190       PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_REPART], 0, 0, 0, 0));
191       PetscCall(PetscInfo(pc, "%s: Repartition: size (active): %d --> %d, %" PetscInt_FMT " local equations, using %s process layout\n", ((PetscObject)pc)->prefix, *a_nactive_proc, new_size, ncrs_eq, (pc_gamg->layout_type == PCGAMG_LAYOUT_COMPACT) ? "compact" : "spread"));
192       /* get 'adj' */
193       if (cr_bs == 1) {
194         PetscCall(MatConvert(Cmat, MATMPIADJ, MAT_INITIAL_MATRIX, &adj));
195       } else {
196         /* make a scalar matrix to partition (no Stokes here) */
197         Mat                tMat;
198         PetscInt           Istart_crs, Iend_crs, ncols, jj, Ii;
199         const PetscScalar *vals;
200         const PetscInt    *idx;
201         PetscInt          *d_nnz, *o_nnz, M, N;
202         static PetscInt    llev = 0; /* ugly but just used for debugging */
203         MatType            mtype;
204 
205         PetscCall(PetscMalloc2(ncrs, &d_nnz, ncrs, &o_nnz));
206         PetscCall(MatGetOwnershipRange(Cmat, &Istart_crs, &Iend_crs));
207         PetscCall(MatGetSize(Cmat, &M, &N));
208         for (Ii = Istart_crs, jj = 0; Ii < Iend_crs; Ii += cr_bs, jj++) {
209           PetscCall(MatGetRow(Cmat, Ii, &ncols, NULL, NULL));
210           d_nnz[jj] = ncols / cr_bs;
211           o_nnz[jj] = ncols / cr_bs;
212           PetscCall(MatRestoreRow(Cmat, Ii, &ncols, NULL, NULL));
213           if (d_nnz[jj] > ncrs) d_nnz[jj] = ncrs;
214           if (o_nnz[jj] > (M / cr_bs - ncrs)) o_nnz[jj] = M / cr_bs - ncrs;
215         }
216 
217         PetscCall(MatGetType(Amat_fine, &mtype));
218         PetscCall(MatCreate(comm, &tMat));
219         PetscCall(MatSetSizes(tMat, ncrs, ncrs, PETSC_DETERMINE, PETSC_DETERMINE));
220         PetscCall(MatSetType(tMat, mtype));
221         PetscCall(MatSeqAIJSetPreallocation(tMat, 0, d_nnz));
222         PetscCall(MatMPIAIJSetPreallocation(tMat, 0, d_nnz, 0, o_nnz));
223         PetscCall(PetscFree2(d_nnz, o_nnz));
224 
225         for (ii = Istart_crs; ii < Iend_crs; ii++) {
226           PetscInt dest_row = ii / cr_bs;
227           PetscCall(MatGetRow(Cmat, ii, &ncols, &idx, &vals));
228           for (jj = 0; jj < ncols; jj++) {
229             PetscInt    dest_col = idx[jj] / cr_bs;
230             PetscScalar v        = 1.0;
231             PetscCall(MatSetValues(tMat, 1, &dest_row, 1, &dest_col, &v, ADD_VALUES));
232           }
233           PetscCall(MatRestoreRow(Cmat, ii, &ncols, &idx, &vals));
234         }
235         PetscCall(MatAssemblyBegin(tMat, MAT_FINAL_ASSEMBLY));
236         PetscCall(MatAssemblyEnd(tMat, MAT_FINAL_ASSEMBLY));
237 
238         if (llev++ == -1) {
239           PetscViewer viewer;
240           char        fname[32];
241           PetscCall(PetscSNPrintf(fname, sizeof(fname), "part_mat_%" PetscInt_FMT ".mat", llev));
242           PetscCall(PetscViewerBinaryOpen(comm, fname, FILE_MODE_WRITE, &viewer));
243           PetscCall(MatView(tMat, viewer));
244           PetscCall(PetscViewerDestroy(&viewer));
245         }
246         PetscCall(MatConvert(tMat, MATMPIADJ, MAT_INITIAL_MATRIX, &adj));
247         PetscCall(MatDestroy(&tMat));
248       } /* create 'adj' */
249 
250       { /* partition: get newproc_idx */
251         char            prefix[256];
252         const char     *pcpre;
253         const PetscInt *is_idx;
254         MatPartitioning mpart;
255         IS              proc_is;
256 
257         PetscCall(MatPartitioningCreate(comm, &mpart));
258         PetscCall(MatPartitioningSetAdjacency(mpart, adj));
259         PetscCall(PCGetOptionsPrefix(pc, &pcpre));
260         PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_gamg_", pcpre ? pcpre : ""));
261         PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mpart, prefix));
262         PetscCall(MatPartitioningSetFromOptions(mpart));
263         PetscCall(MatPartitioningSetNParts(mpart, new_size));
264         PetscCall(MatPartitioningApply(mpart, &proc_is));
265         PetscCall(MatPartitioningDestroy(&mpart));
266 
267         /* collect IS info */
268         PetscCall(PetscMalloc1(ncrs_eq, &newproc_idx));
269         PetscCall(ISGetIndices(proc_is, &is_idx));
270         for (kk = jj = 0; kk < nloc_old; kk++) {
271           for (ii = 0; ii < cr_bs; ii++, jj++) { newproc_idx[jj] = is_idx[kk] * expand_factor; /* distribution */ }
272         }
273         PetscCall(ISRestoreIndices(proc_is, &is_idx));
274         PetscCall(ISDestroy(&proc_is));
275       }
276       PetscCall(MatDestroy(&adj));
277 
278       PetscCall(ISCreateGeneral(comm, ncrs_eq, newproc_idx, PETSC_COPY_VALUES, &is_eq_newproc));
279       PetscCall(PetscFree(newproc_idx));
280       PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_REPART], 0, 0, 0, 0));
281     } else { /* simple aggregation of parts -- 'is_eq_newproc' */
282       PetscInt targetPE;
283       PetscCheck(new_size != nactive, PETSC_COMM_SELF, PETSC_ERR_PLIB, "new_size==nactive. Should not happen");
284       PetscCall(PetscInfo(pc, "%s: Number of equations (loc) %" PetscInt_FMT " with simple aggregation\n", ((PetscObject)pc)->prefix, ncrs_eq));
285       targetPE = (rank / rfactor) * expand_factor;
286       PetscCall(ISCreateStride(comm, ncrs_eq, targetPE, 0, &is_eq_newproc));
287     } /* end simple 'is_eq_newproc' */
288 
289     /*
290       Create an index set from the is_eq_newproc index set to indicate the mapping TO
291     */
292     PetscCall(ISPartitioningToNumbering(is_eq_newproc, &is_eq_num));
293     is_eq_num_prim = is_eq_num;
294     /*
295       Determine how many equations/vertices are assigned to each processor
296     */
297     PetscCall(ISPartitioningCount(is_eq_newproc, size, counts));
298     ncrs_eq_new = counts[rank];
299     PetscCall(ISDestroy(&is_eq_newproc));
300     ncrs_new = ncrs_eq_new / cr_bs;
301 
302     PetscCall(PetscFree(counts));
303     /* data movement scope -- this could be moved to subclasses so that we don't try to cram all auxiliary data into some complex abstracted thing */
304     {
305       Vec             src_crd, dest_crd;
306       const PetscInt *idx, ndata_rows = pc_gamg->data_cell_rows, ndata_cols = pc_gamg->data_cell_cols, node_data_sz = ndata_rows * ndata_cols;
307       VecScatter      vecscat;
308       PetscScalar    *array;
309       IS              isscat;
310       /* move data (for primal equations only) */
311       /* Create a vector to contain the newly ordered element information */
312       PetscCall(VecCreate(comm, &dest_crd));
313       PetscCall(VecSetSizes(dest_crd, node_data_sz * ncrs_new, PETSC_DECIDE));
314       PetscCall(VecSetType(dest_crd, VECSTANDARD)); /* this is needed! */
315       /*
316         There are 'ndata_rows*ndata_cols' data items per node, (one can think of the vectors of having
317         a block size of ...).  Note, ISs are expanded into equation space by 'cr_bs'.
318       */
319       PetscCall(PetscMalloc1(ncrs * node_data_sz, &tidx));
320       PetscCall(ISGetIndices(is_eq_num_prim, &idx));
321       for (ii = 0, jj = 0; ii < ncrs; ii++) {
322         PetscInt id = idx[ii * cr_bs] / cr_bs; /* get node back */
323         for (kk = 0; kk < node_data_sz; kk++, jj++) tidx[jj] = id * node_data_sz + kk;
324       }
325       PetscCall(ISRestoreIndices(is_eq_num_prim, &idx));
326       PetscCall(ISCreateGeneral(comm, node_data_sz * ncrs, tidx, PETSC_COPY_VALUES, &isscat));
327       PetscCall(PetscFree(tidx));
328       /*
329         Create a vector to contain the original vertex information for each element
330       */
331       PetscCall(VecCreateSeq(PETSC_COMM_SELF, node_data_sz * ncrs, &src_crd));
332       for (jj = 0; jj < ndata_cols; jj++) {
333         const PetscInt stride0 = ncrs * pc_gamg->data_cell_rows;
334         for (ii = 0; ii < ncrs; ii++) {
335           for (kk = 0; kk < ndata_rows; kk++) {
336             PetscInt    ix = ii * ndata_rows + kk + jj * stride0, jx = ii * node_data_sz + kk * ndata_cols + jj;
337             PetscScalar tt = (PetscScalar)pc_gamg->data[ix];
338             PetscCall(VecSetValues(src_crd, 1, &jx, &tt, INSERT_VALUES));
339           }
340         }
341       }
342       PetscCall(VecAssemblyBegin(src_crd));
343       PetscCall(VecAssemblyEnd(src_crd));
344       /*
345         Scatter the element vertex information (still in the original vertex ordering)
346         to the correct processor
347       */
348       PetscCall(VecScatterCreate(src_crd, NULL, dest_crd, isscat, &vecscat));
349       PetscCall(ISDestroy(&isscat));
350       PetscCall(VecScatterBegin(vecscat, src_crd, dest_crd, INSERT_VALUES, SCATTER_FORWARD));
351       PetscCall(VecScatterEnd(vecscat, src_crd, dest_crd, INSERT_VALUES, SCATTER_FORWARD));
352       PetscCall(VecScatterDestroy(&vecscat));
353       PetscCall(VecDestroy(&src_crd));
354       /*
355         Put the element vertex data into a new allocation of the gdata->ele
356       */
357       PetscCall(PetscFree(pc_gamg->data));
358       PetscCall(PetscMalloc1(node_data_sz * ncrs_new, &pc_gamg->data));
359 
360       pc_gamg->data_sz = node_data_sz * ncrs_new;
361       strideNew        = ncrs_new * ndata_rows;
362 
363       PetscCall(VecGetArray(dest_crd, &array));
364       for (jj = 0; jj < ndata_cols; jj++) {
365         for (ii = 0; ii < ncrs_new; ii++) {
366           for (kk = 0; kk < ndata_rows; kk++) {
367             PetscInt ix = ii * ndata_rows + kk + jj * strideNew, jx = ii * node_data_sz + kk * ndata_cols + jj;
368             pc_gamg->data[ix] = PetscRealPart(array[jx]);
369           }
370         }
371       }
372       PetscCall(VecRestoreArray(dest_crd, &array));
373       PetscCall(VecDestroy(&dest_crd));
374     }
375     /* move A and P (columns) with new layout */
376     /* PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[SET13],0,0,0,0)); */
377     /*
378       Invert for MatCreateSubMatrix
379     */
380     PetscCall(ISInvertPermutation(is_eq_num, ncrs_eq_new, &new_eq_indices));
381     PetscCall(ISSort(new_eq_indices)); /* is this needed? */
382     PetscCall(ISSetBlockSize(new_eq_indices, cr_bs));
383     if (is_eq_num != is_eq_num_prim) { PetscCall(ISDestroy(&is_eq_num_prim)); /* could be same as 'is_eq_num' */ }
384     if (Pcolumnperm) {
385       PetscCall(PetscObjectReference((PetscObject)new_eq_indices));
386       *Pcolumnperm = new_eq_indices;
387     }
388     PetscCall(ISDestroy(&is_eq_num));
389     /* PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[SET13],0,0,0,0)); */
390     /* PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[SET14],0,0,0,0)); */
391 
392     /* 'a_Amat_crs' output */
393     {
394       Mat       mat;
395       PetscBool isset, isspd, isher;
396 #if !defined(PETSC_USE_COMPLEX)
397       PetscBool issym;
398 #endif
399 
400       PetscCall(MatCreateSubMatrix(Cmat, new_eq_indices, new_eq_indices, MAT_INITIAL_MATRIX, &mat));
401       PetscCall(MatIsSPDKnown(Cmat, &isset, &isspd)); // like MatPropagateSymmetryOptions, but should set MAT_STRUCTURALLY_SYMMETRIC ?
402       if (isset) PetscCall(MatSetOption(mat, MAT_SPD, isspd));
403       else {
404         PetscCall(MatIsHermitianKnown(Cmat, &isset, &isher));
405         if (isset) PetscCall(MatSetOption(mat, MAT_HERMITIAN, isher));
406         else {
407 #if !defined(PETSC_USE_COMPLEX)
408           PetscCall(MatIsSymmetricKnown(Cmat, &isset, &issym));
409           if (isset) PetscCall(MatSetOption(mat, MAT_SYMMETRIC, issym));
410 #endif
411         }
412       }
413       *a_Amat_crs = mat;
414     }
415     PetscCall(MatDestroy(&Cmat));
416 
417     /* PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[SET14],0,0,0,0)); */
418     /* prolongator */
419     {
420       IS       findices;
421       PetscInt Istart, Iend;
422       Mat      Pnew;
423 
424       PetscCall(MatGetOwnershipRange(Pold, &Istart, &Iend));
425       /* PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[SET15],0,0,0,0)); */
426       PetscCall(ISCreateStride(comm, Iend - Istart, Istart, 1, &findices));
427       PetscCall(ISSetBlockSize(findices, f_bs));
428       PetscCall(MatCreateSubMatrix(Pold, findices, new_eq_indices, MAT_INITIAL_MATRIX, &Pnew));
429       PetscCall(ISDestroy(&findices));
430       PetscCall(MatSetOption(Pnew, MAT_FORM_EXPLICIT_TRANSPOSE, PETSC_TRUE));
431 
432       /* PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[SET15],0,0,0,0)); */
433       PetscCall(MatDestroy(a_P_inout));
434 
435       /* output - repartitioned */
436       *a_P_inout = Pnew;
437     }
438     PetscCall(ISDestroy(&new_eq_indices));
439 
440     *a_nactive_proc = new_size; /* output */
441 
442     /* pinning on reduced grids, not a bad heuristic and optimization gets folded into process reduction optimization */
443     if (pc_gamg->cpu_pin_coarse_grids) {
444 #if defined(PETSC_HAVE_VIENNACL) || defined(PETSC_HAVE_CUDA)
445       static PetscInt llev = 2;
446       PetscCall(PetscInfo(pc, "%s: Pinning level %" PetscInt_FMT " to the CPU\n", ((PetscObject)pc)->prefix, llev++));
447 #endif
448       PetscCall(MatBindToCPU(*a_Amat_crs, PETSC_TRUE));
449       PetscCall(MatBindToCPU(*a_P_inout, PETSC_TRUE));
450       if (1) { /* HACK: move this to MatBindCPU_MPIAIJXXX; lvec is created, need to pin it, this is done in MatSetUpMultiply_MPIAIJ. Hack */
451         Mat         A = *a_Amat_crs, P = *a_P_inout;
452         PetscMPIInt size;
453         PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
454         if (size > 1) {
455           Mat_MPIAIJ *a = (Mat_MPIAIJ *)A->data, *p = (Mat_MPIAIJ *)P->data;
456           PetscCall(VecBindToCPU(a->lvec, PETSC_TRUE));
457           PetscCall(VecBindToCPU(p->lvec, PETSC_TRUE));
458         }
459       }
460     }
461     PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_REDUCE], 0, 0, 0, 0));
462   } // processor reduce
463   PetscFunctionReturn(PETSC_SUCCESS);
464 }
465 
466 // used in GEO
467 PetscErrorCode PCGAMGSquareGraph_GAMG(PC a_pc, Mat Gmat1, Mat *Gmat2)
468 {
469   const char *prefix;
470   char        addp[32];
471   PC_MG      *mg      = (PC_MG *)a_pc->data;
472   PC_GAMG    *pc_gamg = (PC_GAMG *)mg->innerctx;
473 
474   PetscFunctionBegin;
475   PetscCall(PCGetOptionsPrefix(a_pc, &prefix));
476   PetscCall(PetscInfo(a_pc, "%s: Square Graph on level %" PetscInt_FMT "\n", ((PetscObject)a_pc)->prefix, pc_gamg->current_level + 1));
477   PetscCall(MatProductCreate(Gmat1, Gmat1, NULL, Gmat2));
478   PetscCall(MatSetOptionsPrefix(*Gmat2, prefix));
479   PetscCall(PetscSNPrintf(addp, sizeof(addp), "pc_gamg_square_%" PetscInt_FMT "_", pc_gamg->current_level));
480   PetscCall(MatAppendOptionsPrefix(*Gmat2, addp));
481   if ((*Gmat2)->structurally_symmetric == PETSC_BOOL3_TRUE) {
482     PetscCall(MatProductSetType(*Gmat2, MATPRODUCT_AB));
483   } else {
484     PetscCall(MatSetOption(Gmat1, MAT_FORM_EXPLICIT_TRANSPOSE, PETSC_TRUE));
485     PetscCall(MatProductSetType(*Gmat2, MATPRODUCT_AtB));
486   }
487   PetscCall(MatProductSetFromOptions(*Gmat2));
488   PetscCall(PetscLogEventBegin(petsc_gamg_setup_matmat_events[pc_gamg->current_level][0], 0, 0, 0, 0));
489   PetscCall(MatProductSymbolic(*Gmat2));
490   PetscCall(PetscLogEventEnd(petsc_gamg_setup_matmat_events[pc_gamg->current_level][0], 0, 0, 0, 0));
491   PetscCall(MatProductClear(*Gmat2));
492   /* we only need the sparsity, cheat and tell PETSc the matrix has been assembled */
493   (*Gmat2)->assembled = PETSC_TRUE;
494   PetscFunctionReturn(PETSC_SUCCESS);
495 }
496 
497 /*
498    PCSetUp_GAMG - Prepares for the use of the GAMG preconditioner
499                     by setting data structures and options.
500 
501    Input Parameter:
502 .  pc - the preconditioner context
503 
504 */
505 static PetscErrorCode PCSetUp_GAMG(PC pc)
506 {
507   PC_MG      *mg      = (PC_MG *)pc->data;
508   PC_GAMG    *pc_gamg = (PC_GAMG *)mg->innerctx;
509   Mat         Pmat    = pc->pmat;
510   PetscInt    fine_level, level, level1, bs, M, N, qq, lidx, nASMBlocksArr[PETSC_MG_MAXLEVELS];
511   MPI_Comm    comm;
512   PetscMPIInt rank, size, nactivepe;
513   Mat         Aarr[PETSC_MG_MAXLEVELS], Parr[PETSC_MG_MAXLEVELS];
514   IS         *ASMLocalIDsArr[PETSC_MG_MAXLEVELS];
515   PetscBool   is_last = PETSC_FALSE;
516 #if defined(PETSC_USE_INFO)
517   PetscLogDouble nnz0 = 0., nnztot = 0.;
518   MatInfo        info;
519 #endif
520 
521   PetscFunctionBegin;
522   PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
523   PetscCallMPI(MPI_Comm_rank(comm, &rank));
524   PetscCallMPI(MPI_Comm_size(comm, &size));
525   PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_SETUP], 0, 0, 0, 0));
526   if (pc->setupcalled) {
527     if (!pc_gamg->reuse_prol || pc->flag == DIFFERENT_NONZERO_PATTERN) {
528       /* reset everything */
529       PetscCall(PCReset_MG(pc));
530       pc->setupcalled = 0;
531     } else {
532       PC_MG_Levels **mglevels = mg->levels;
533       /* just do Galerkin grids */
534       Mat B, dA, dB;
535       if (pc_gamg->Nlevels > 1) {
536         PetscInt gl;
537         /* currently only handle case where mat and pmat are the same on coarser levels */
538         PetscCall(KSPGetOperators(mglevels[pc_gamg->Nlevels - 1]->smoothd, &dA, &dB));
539         /* (re)set to get dirty flag */
540         PetscCall(KSPSetOperators(mglevels[pc_gamg->Nlevels - 1]->smoothd, dA, dB));
541 
542         for (level = pc_gamg->Nlevels - 2, gl = 0; level >= 0; level--, gl++) {
543           MatReuse reuse = MAT_INITIAL_MATRIX;
544 #if defined(GAMG_STAGES)
545           PetscCall(PetscLogStagePush(gamg_stages[gl]));
546 #endif
547           /* matrix structure can change from repartitioning or process reduction but don't know if we have process reduction here. Should fix */
548           PetscCall(KSPGetOperators(mglevels[level]->smoothd, NULL, &B));
549           if (B->product) {
550             if (B->product->A == dB && B->product->B == mglevels[level + 1]->interpolate) reuse = MAT_REUSE_MATRIX;
551           }
552           if (reuse == MAT_INITIAL_MATRIX) PetscCall(MatDestroy(&mglevels[level]->A));
553           if (reuse == MAT_REUSE_MATRIX) {
554             PetscCall(PetscInfo(pc, "%s: RAP after first solve, reuse matrix level %" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, level));
555           } else {
556             PetscCall(PetscInfo(pc, "%s: RAP after first solve, new matrix level %" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, level));
557           }
558           PetscCall(PetscLogEventBegin(petsc_gamg_setup_matmat_events[gl][1], 0, 0, 0, 0));
559           PetscCall(MatPtAP(dB, mglevels[level + 1]->interpolate, reuse, PETSC_DEFAULT, &B));
560           PetscCall(PetscLogEventEnd(petsc_gamg_setup_matmat_events[gl][1], 0, 0, 0, 0));
561           if (reuse == MAT_INITIAL_MATRIX) mglevels[level]->A = B;
562           PetscCall(KSPSetOperators(mglevels[level]->smoothd, B, B));
563           // check for redoing eigen estimates
564           if (pc_gamg->recompute_esteig) {
565             PetscBool ischeb;
566             KSP       smoother;
567             PetscCall(PCMGGetSmoother(pc, level + 1, &smoother));
568             PetscCall(PetscObjectTypeCompare((PetscObject)smoother, KSPCHEBYSHEV, &ischeb));
569             if (ischeb) {
570               KSP_Chebyshev *cheb = (KSP_Chebyshev *)smoother->data;
571               cheb->emin_provided = 0;
572               cheb->emax_provided = 0;
573             }
574             /* we could call PetscCall(KSPChebyshevSetEigenvalues(smoother, 0, 0)); but the logic does not work properly */
575           }
576           // inc
577           dB = B;
578 #if defined(GAMG_STAGES)
579           PetscCall(PetscLogStagePop());
580 #endif
581         }
582       }
583 
584       PetscCall(PCSetUp_MG(pc));
585       PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_SETUP], 0, 0, 0, 0));
586       PetscFunctionReturn(PETSC_SUCCESS);
587     }
588   }
589 
590   if (!pc_gamg->data) {
591     if (pc_gamg->orig_data) {
592       PetscCall(MatGetBlockSize(Pmat, &bs));
593       PetscCall(MatGetLocalSize(Pmat, &qq, NULL));
594 
595       pc_gamg->data_sz        = (qq / bs) * pc_gamg->orig_data_cell_rows * pc_gamg->orig_data_cell_cols;
596       pc_gamg->data_cell_rows = pc_gamg->orig_data_cell_rows;
597       pc_gamg->data_cell_cols = pc_gamg->orig_data_cell_cols;
598 
599       PetscCall(PetscMalloc1(pc_gamg->data_sz, &pc_gamg->data));
600       for (qq = 0; qq < pc_gamg->data_sz; qq++) pc_gamg->data[qq] = pc_gamg->orig_data[qq];
601     } else {
602       PetscCheck(pc_gamg->ops->createdefaultdata, comm, PETSC_ERR_PLIB, "'createdefaultdata' not set(?) need to support NULL data");
603       PetscCall(pc_gamg->ops->createdefaultdata(pc, Pmat));
604     }
605   }
606 
607   /* cache original data for reuse */
608   if (!pc_gamg->orig_data && (PetscBool)(!pc_gamg->reuse_prol)) {
609     PetscCall(PetscMalloc1(pc_gamg->data_sz, &pc_gamg->orig_data));
610     for (qq = 0; qq < pc_gamg->data_sz; qq++) pc_gamg->orig_data[qq] = pc_gamg->data[qq];
611     pc_gamg->orig_data_cell_rows = pc_gamg->data_cell_rows;
612     pc_gamg->orig_data_cell_cols = pc_gamg->data_cell_cols;
613   }
614 
615   /* get basic dims */
616   PetscCall(MatGetBlockSize(Pmat, &bs));
617   PetscCall(MatGetSize(Pmat, &M, &N));
618 
619 #if defined(PETSC_USE_INFO)
620   PetscCall(MatGetInfo(Pmat, MAT_GLOBAL_SUM, &info)); /* global reduction */
621   nnz0   = info.nz_used;
622   nnztot = info.nz_used;
623 #endif
624   PetscCall(PetscInfo(pc, "%s: level %d) N=%" PetscInt_FMT ", n data rows=%" PetscInt_FMT ", n data cols=%" PetscInt_FMT ", nnz/row (ave)=%" PetscInt_FMT ", np=%d\n", ((PetscObject)pc)->prefix, 0, M, pc_gamg->data_cell_rows, pc_gamg->data_cell_cols, (PetscInt)(nnz0 / (PetscReal)M + 0.5), size));
625 
626   /* Get A_i and R_i */
627   for (level = 0, Aarr[0] = Pmat, nactivepe = size; level < (pc_gamg->Nlevels - 1) && (!level || M > pc_gamg->coarse_eq_limit); level++) {
628     pc_gamg->current_level = level;
629     PetscCheck(level < PETSC_MG_MAXLEVELS, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Too many levels %" PetscInt_FMT, level);
630     level1 = level + 1;
631 #if defined(GAMG_STAGES)
632     if (!gamg_stages[level]) {
633       char str[32];
634       PetscCall(PetscSNPrintf(str, PETSC_STATIC_ARRAY_LENGTH(str), "GAMG Level %d", (int)level));
635       PetscCall(PetscLogStageRegister(str, &gamg_stages[level]));
636     }
637     PetscCall(PetscLogStagePush(gamg_stages[level]));
638 #endif
639     { /* construct prolongator */
640       Mat               Gmat;
641       PetscCoarsenData *agg_lists;
642       Mat               Prol11;
643 
644       PetscCall(PCGAMGCreateGraph(pc, Aarr[level], &Gmat));
645       PetscCall(pc_gamg->ops->coarsen(pc, &Gmat, &agg_lists));
646       PetscCall(pc_gamg->ops->prolongator(pc, Aarr[level], Gmat, agg_lists, &Prol11));
647 
648       /* could have failed to create new level */
649       if (Prol11) {
650         const char *prefix;
651         char        addp[32];
652 
653         /* get new block size of coarse matrices */
654         PetscCall(MatGetBlockSizes(Prol11, NULL, &bs));
655 
656         if (pc_gamg->ops->optprolongator) {
657           /* smooth */
658           PetscCall(pc_gamg->ops->optprolongator(pc, Aarr[level], &Prol11));
659         }
660 
661         if (pc_gamg->use_aggs_in_asm) {
662           PetscInt bs;
663           PetscCall(MatGetBlockSizes(Prol11, &bs, NULL)); // not timed directly, ugly, could remove, but good ASM method
664           PetscCall(PetscCDGetASMBlocks(agg_lists, bs, Gmat, &nASMBlocksArr[level], &ASMLocalIDsArr[level]));
665         }
666 
667         PetscCall(PCGetOptionsPrefix(pc, &prefix));
668         PetscCall(MatSetOptionsPrefix(Prol11, prefix));
669         PetscCall(PetscSNPrintf(addp, sizeof(addp), "pc_gamg_prolongator_%d_", (int)level));
670         PetscCall(MatAppendOptionsPrefix(Prol11, addp));
671         /* Always generate the transpose with CUDA
672            Such behaviour can be adapted with -pc_gamg_prolongator_ prefixed options */
673         PetscCall(MatSetOption(Prol11, MAT_FORM_EXPLICIT_TRANSPOSE, PETSC_TRUE));
674         PetscCall(MatSetFromOptions(Prol11));
675         Parr[level1] = Prol11;
676       } else Parr[level1] = NULL; /* failed to coarsen */
677 
678       PetscCall(MatDestroy(&Gmat));
679       PetscCall(PetscCDDestroy(agg_lists));
680     }                           /* construct prolongator scope */
681     if (!level) Aarr[0] = Pmat; /* use Pmat for finest level setup */
682     if (!Parr[level1]) {        /* failed to coarsen */
683       PetscCall(PetscInfo(pc, "%s: Stop gridding, level %" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, level));
684 #if defined(GAMG_STAGES)
685       PetscCall(PetscLogStagePop());
686 #endif
687       break;
688     }
689     PetscCall(MatGetSize(Parr[level1], &M, &N)); /* N is next M, a loop test variables */
690     PetscCheck(!is_last, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Is last ?");
691     if (N <= pc_gamg->coarse_eq_limit) is_last = PETSC_TRUE;
692     if (level1 == pc_gamg->Nlevels - 1) is_last = PETSC_TRUE;
693     PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_LEVEL], 0, 0, 0, 0));
694     PetscCall(pc_gamg->ops->createlevel(pc, Aarr[level], bs, &Parr[level1], &Aarr[level1], &nactivepe, NULL, is_last));
695     PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_LEVEL], 0, 0, 0, 0));
696 
697     PetscCall(MatGetSize(Aarr[level1], &M, &N)); /* M is loop test variables */
698 #if defined(PETSC_USE_INFO)
699     PetscCall(MatGetInfo(Aarr[level1], MAT_GLOBAL_SUM, &info));
700     nnztot += info.nz_used;
701 #endif
702     PetscCall(PetscInfo(pc, "%s: %d) N=%" PetscInt_FMT ", n data cols=%" PetscInt_FMT ", nnz/row (ave)=%" PetscInt_FMT ", %d active pes\n", ((PetscObject)pc)->prefix, (int)level1, M, pc_gamg->data_cell_cols, (PetscInt)(info.nz_used / (PetscReal)M), nactivepe));
703 
704 #if defined(GAMG_STAGES)
705     PetscCall(PetscLogStagePop());
706 #endif
707     /* stop if one node or one proc -- could pull back for singular problems */
708     if ((pc_gamg->data_cell_cols && M / pc_gamg->data_cell_cols < 2) || (!pc_gamg->data_cell_cols && M / bs < 2)) {
709       PetscCall(PetscInfo(pc, "%s: HARD stop of coarsening on level %" PetscInt_FMT ".  Grid too small: %" PetscInt_FMT " block nodes\n", ((PetscObject)pc)->prefix, level, M / bs));
710       level++;
711       break;
712     }
713   } /* levels */
714   PetscCall(PetscFree(pc_gamg->data));
715 
716   PetscCall(PetscInfo(pc, "%s: %" PetscInt_FMT " levels, operator complexity = %g\n", ((PetscObject)pc)->prefix, level + 1, nnztot / nnz0));
717   pc_gamg->Nlevels = level + 1;
718   fine_level       = level;
719   PetscCall(PCMGSetLevels(pc, pc_gamg->Nlevels, NULL));
720 
721   if (pc_gamg->Nlevels > 1) { /* don't setup MG if one level */
722 
723     /* set default smoothers & set operators */
724     for (lidx = 1, level = pc_gamg->Nlevels - 2; lidx <= fine_level; lidx++, level--) {
725       KSP smoother;
726       PC  subpc;
727 
728       PetscCall(PCMGGetSmoother(pc, lidx, &smoother));
729       PetscCall(KSPGetPC(smoother, &subpc));
730 
731       PetscCall(KSPSetNormType(smoother, KSP_NORM_NONE));
732       /* set ops */
733       PetscCall(KSPSetOperators(smoother, Aarr[level], Aarr[level]));
734       PetscCall(PCMGSetInterpolation(pc, lidx, Parr[level + 1]));
735 
736       /* set defaults */
737       PetscCall(KSPSetType(smoother, KSPCHEBYSHEV));
738 
739       /* set blocks for ASM smoother that uses the 'aggregates' */
740       if (pc_gamg->use_aggs_in_asm) {
741         PetscInt sz;
742         IS      *iss;
743 
744         sz  = nASMBlocksArr[level];
745         iss = ASMLocalIDsArr[level];
746         PetscCall(PCSetType(subpc, PCASM));
747         PetscCall(PCASMSetOverlap(subpc, 0));
748         PetscCall(PCASMSetType(subpc, PC_ASM_BASIC));
749         if (!sz) {
750           IS is;
751           PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 0, NULL, PETSC_COPY_VALUES, &is));
752           PetscCall(PCASMSetLocalSubdomains(subpc, 1, NULL, &is));
753           PetscCall(ISDestroy(&is));
754         } else {
755           PetscInt kk;
756           PetscCall(PCASMSetLocalSubdomains(subpc, sz, iss, NULL));
757           for (kk = 0; kk < sz; kk++) PetscCall(ISDestroy(&iss[kk]));
758           PetscCall(PetscFree(iss));
759         }
760         ASMLocalIDsArr[level] = NULL;
761         nASMBlocksArr[level]  = 0;
762       } else {
763         PetscCall(PCSetType(subpc, PCJACOBI));
764       }
765     }
766     {
767       /* coarse grid */
768       KSP      smoother, *k2;
769       PC       subpc, pc2;
770       PetscInt ii, first;
771       Mat      Lmat = Aarr[(level = pc_gamg->Nlevels - 1)];
772       lidx          = 0;
773 
774       PetscCall(PCMGGetSmoother(pc, lidx, &smoother));
775       PetscCall(KSPSetOperators(smoother, Lmat, Lmat));
776       if (!pc_gamg->use_parallel_coarse_grid_solver) {
777         PetscCall(KSPSetNormType(smoother, KSP_NORM_NONE));
778         PetscCall(KSPGetPC(smoother, &subpc));
779         PetscCall(PCSetType(subpc, PCBJACOBI));
780         PetscCall(PCSetUp(subpc));
781         PetscCall(PCBJacobiGetSubKSP(subpc, &ii, &first, &k2));
782         PetscCheck(ii == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "ii %" PetscInt_FMT " is not one", ii);
783         PetscCall(KSPGetPC(k2[0], &pc2));
784         PetscCall(PCSetType(pc2, PCLU));
785         PetscCall(PCFactorSetShiftType(pc2, MAT_SHIFT_INBLOCKS));
786         PetscCall(KSPSetTolerances(k2[0], PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 1));
787         PetscCall(KSPSetType(k2[0], KSPPREONLY));
788       }
789     }
790 
791     /* should be called in PCSetFromOptions_GAMG(), but cannot be called prior to PCMGSetLevels() */
792     PetscObjectOptionsBegin((PetscObject)pc);
793     PetscCall(PCSetFromOptions_MG(pc, PetscOptionsObject));
794     PetscOptionsEnd();
795     PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_EXTERNAL));
796 
797     /* set cheby eigen estimates from SA to use in the solver */
798     if (pc_gamg->use_sa_esteig) {
799       for (lidx = 1, level = pc_gamg->Nlevels - 2; level >= 0; lidx++, level--) {
800         KSP       smoother;
801         PetscBool ischeb;
802 
803         PetscCall(PCMGGetSmoother(pc, lidx, &smoother));
804         PetscCall(PetscObjectTypeCompare((PetscObject)smoother, KSPCHEBYSHEV, &ischeb));
805         if (ischeb) {
806           KSP_Chebyshev *cheb = (KSP_Chebyshev *)smoother->data;
807 
808           // The command line will override these settings because KSPSetFromOptions is called in PCSetUp_MG
809           if (mg->max_eigen_DinvA[level] > 0) {
810             // SA uses Jacobi for P; we use SA estimates if the smoother is also Jacobi or if the user explicitly requested it.
811             // TODO: This should test whether it's the same Jacobi variant (DIAG, ROWSUM, etc.)
812             PetscReal emax, emin;
813 
814             emin = mg->min_eigen_DinvA[level];
815             emax = mg->max_eigen_DinvA[level];
816             PetscCall(PetscInfo(pc, "%s: PCSetUp_GAMG: call KSPChebyshevSetEigenvalues on level %" PetscInt_FMT " (N=%" PetscInt_FMT ") with emax = %g emin = %g\n", ((PetscObject)pc)->prefix, level, Aarr[level]->rmap->N, (double)emax, (double)emin));
817             cheb->emin_provided = emin;
818             cheb->emax_provided = emax;
819           }
820         }
821       }
822     }
823 
824     PetscCall(PCSetUp_MG(pc));
825 
826     /* clean up */
827     for (level = 1; level < pc_gamg->Nlevels; level++) {
828       PetscCall(MatDestroy(&Parr[level]));
829       PetscCall(MatDestroy(&Aarr[level]));
830     }
831   } else {
832     KSP smoother;
833 
834     PetscCall(PetscInfo(pc, "%s: One level solver used (system is seen as DD). Using default solver.\n", ((PetscObject)pc)->prefix));
835     PetscCall(PCMGGetSmoother(pc, 0, &smoother));
836     PetscCall(KSPSetOperators(smoother, Aarr[0], Aarr[0]));
837     PetscCall(KSPSetType(smoother, KSPPREONLY));
838     PetscCall(PCSetUp_MG(pc));
839   }
840   PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_SETUP], 0, 0, 0, 0));
841   PetscFunctionReturn(PETSC_SUCCESS);
842 }
843 
844 /*
845  PCDestroy_GAMG - Destroys the private context for the GAMG preconditioner
846    that was created with PCCreate_GAMG().
847 
848    Input Parameter:
849 .  pc - the preconditioner context
850 
851    Application Interface Routine: PCDestroy()
852 */
853 PetscErrorCode PCDestroy_GAMG(PC pc)
854 {
855   PC_MG   *mg      = (PC_MG *)pc->data;
856   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
857 
858   PetscFunctionBegin;
859   PetscCall(PCReset_GAMG(pc));
860   if (pc_gamg->ops->destroy) PetscCall((*pc_gamg->ops->destroy)(pc));
861   PetscCall(PetscFree(pc_gamg->ops));
862   PetscCall(PetscFree(pc_gamg->gamg_type_name));
863   PetscCall(PetscFree(pc_gamg));
864   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetProcEqLim_C", NULL));
865   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseEqLim_C", NULL));
866   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRepartition_C", NULL));
867   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetEigenvalues_C", NULL));
868   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetUseSAEstEig_C", NULL));
869   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRecomputeEstEig_C", NULL));
870   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetReuseInterpolation_C", NULL));
871   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGASMSetUseAggs_C", NULL));
872   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetParallelCoarseGridSolve_C", NULL));
873   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCpuPinCoarseGrids_C", NULL));
874   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseGridLayoutType_C", NULL));
875   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThreshold_C", NULL));
876   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRankReductionFactors_C", NULL));
877   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThresholdScale_C", NULL));
878   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetType_C", NULL));
879   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGGetType_C", NULL));
880   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetNlevels_C", NULL));
881   PetscCall(PCDestroy_MG(pc));
882   PetscFunctionReturn(PETSC_SUCCESS);
883 }
884 
885 /*@
886   PCGAMGSetProcEqLim - Set number of equations to aim for per process on the coarse grids via processor reduction in `PCGAMG`
887 
888   Logically Collective
889 
890   Input Parameters:
891 + pc - the preconditioner context
892 - n  - the number of equations
893 
894   Options Database Key:
895 . -pc_gamg_process_eq_limit <limit> - set the limit
896 
897   Level: intermediate
898 
899   Note:
900   `PCGAMG` will reduce the number of MPI processes used directly on the coarse grids so that there are around <limit> equations on each process
901   that has degrees of freedom
902 
903 .seealso: `PCGAMG`, `PCGAMGSetCoarseEqLim()`, `PCGAMGSetRankReductionFactors()`, `PCGAMGSetRepartition()`
904 @*/
905 PetscErrorCode PCGAMGSetProcEqLim(PC pc, PetscInt n)
906 {
907   PetscFunctionBegin;
908   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
909   PetscTryMethod(pc, "PCGAMGSetProcEqLim_C", (PC, PetscInt), (pc, n));
910   PetscFunctionReturn(PETSC_SUCCESS);
911 }
912 
913 static PetscErrorCode PCGAMGSetProcEqLim_GAMG(PC pc, PetscInt n)
914 {
915   PC_MG   *mg      = (PC_MG *)pc->data;
916   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
917 
918   PetscFunctionBegin;
919   if (n > 0) pc_gamg->min_eq_proc = n;
920   PetscFunctionReturn(PETSC_SUCCESS);
921 }
922 
923 /*@
924   PCGAMGSetCoarseEqLim - Set maximum number of equations on the coarsest grid of `PCGAMG`
925 
926   Collective
927 
928   Input Parameters:
929 + pc - the preconditioner context
930 - n  - maximum number of equations to aim for
931 
932   Options Database Key:
933 . -pc_gamg_coarse_eq_limit <limit> - set the limit
934 
935   Level: intermediate
936 
937   Note:
938   For example -pc_gamg_coarse_eq_limit 1000 will stop coarsening once the coarse grid
939   has less than 1000 unknowns.
940 
941 .seealso: `PCGAMG`, `PCGAMGSetProcEqLim()`, `PCGAMGSetRankReductionFactors()`, `PCGAMGSetRepartition()`
942 @*/
943 PetscErrorCode PCGAMGSetCoarseEqLim(PC pc, PetscInt n)
944 {
945   PetscFunctionBegin;
946   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
947   PetscTryMethod(pc, "PCGAMGSetCoarseEqLim_C", (PC, PetscInt), (pc, n));
948   PetscFunctionReturn(PETSC_SUCCESS);
949 }
950 
951 static PetscErrorCode PCGAMGSetCoarseEqLim_GAMG(PC pc, PetscInt n)
952 {
953   PC_MG   *mg      = (PC_MG *)pc->data;
954   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
955 
956   PetscFunctionBegin;
957   if (n > 0) pc_gamg->coarse_eq_limit = n;
958   PetscFunctionReturn(PETSC_SUCCESS);
959 }
960 
961 /*@
962   PCGAMGSetRepartition - Repartition the degrees of freedom across the processors on the coarser grids when reducing the number of MPI ranks to use
963 
964   Collective
965 
966   Input Parameters:
967 + pc - the preconditioner context
968 - n  - `PETSC_TRUE` or `PETSC_FALSE`
969 
970   Options Database Key:
971 . -pc_gamg_repartition <true,false> - turn on the repartitioning
972 
973   Level: intermediate
974 
975   Note:
976   This will generally improve the loading balancing of the work on each level
977 
978 .seealso: `PCGAMG`, `PCGAMGSetProcEqLim()`, `PCGAMGSetRankReductionFactors()`
979 @*/
980 PetscErrorCode PCGAMGSetRepartition(PC pc, PetscBool n)
981 {
982   PetscFunctionBegin;
983   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
984   PetscTryMethod(pc, "PCGAMGSetRepartition_C", (PC, PetscBool), (pc, n));
985   PetscFunctionReturn(PETSC_SUCCESS);
986 }
987 
988 static PetscErrorCode PCGAMGSetRepartition_GAMG(PC pc, PetscBool n)
989 {
990   PC_MG   *mg      = (PC_MG *)pc->data;
991   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
992 
993   PetscFunctionBegin;
994   pc_gamg->repart = n;
995   PetscFunctionReturn(PETSC_SUCCESS);
996 }
997 
998 /*@
999   PCGAMGSetUseSAEstEig - Use the eigen estimate from smoothed aggregation for the Chebyshev smoother during the solution process
1000 
1001   Collective
1002 
1003   Input Parameters:
1004 + pc - the preconditioner context
1005 - b  - flag
1006 
1007   Options Database Key:
1008 . -pc_gamg_use_sa_esteig <true,false> - use the eigen estimate
1009 
1010   Level: advanced
1011 
1012   Notes:
1013   Smoothed aggregation constructs the smoothed prolongator $P = (I - \omega D^{-1} A) T$ where $T$ is the tentative prolongator and $D$ is the diagonal of $A$.
1014   Eigenvalue estimates (based on a few `PCCG` or `PCGMRES` iterations) are computed to choose $\omega$ so that this is a stable smoothing operation.
1015   If Chebyshev with Jacobi (diagonal) preconditioning is used for smoothing, then the eigenvalue estimates can be reused during the solution process
1016   This option is only used when the smoother uses Jacobi, and should be turned off if a different `PCJacobiType` is used.
1017   It became default in PETSc 3.17.
1018 
1019 .seealso: `PCGAMG`, `KSPChebyshevSetEigenvalues()`, `KSPChebyshevEstEigSet()`, `PCGAMGSetRecomputeEstEig()`
1020 @*/
1021 PetscErrorCode PCGAMGSetUseSAEstEig(PC pc, PetscBool b)
1022 {
1023   PetscFunctionBegin;
1024   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1025   PetscTryMethod(pc, "PCGAMGSetUseSAEstEig_C", (PC, PetscBool), (pc, b));
1026   PetscFunctionReturn(PETSC_SUCCESS);
1027 }
1028 
1029 static PetscErrorCode PCGAMGSetUseSAEstEig_GAMG(PC pc, PetscBool b)
1030 {
1031   PC_MG   *mg      = (PC_MG *)pc->data;
1032   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1033 
1034   PetscFunctionBegin;
1035   pc_gamg->use_sa_esteig = b;
1036   PetscFunctionReturn(PETSC_SUCCESS);
1037 }
1038 
1039 /*@
1040   PCGAMGSetRecomputeEstEig - Set flag for Chebyshev smoothers to recompute the eigen estimates
1041 
1042   Collective
1043 
1044   Input Parameters:
1045 + pc - the preconditioner context
1046 - b  - flag
1047 
1048   Options Database Key:
1049 . -pc_gamg_recompute_esteig <true> - use the eigen estimate
1050 
1051   Level: advanced
1052 
1053 .seealso: `PCGAMG`, `KSPChebyshevSetEigenvalues()`, `KSPChebyshevEstEigSet()`
1054 @*/
1055 PetscErrorCode PCGAMGSetRecomputeEstEig(PC pc, PetscBool b)
1056 {
1057   PetscFunctionBegin;
1058   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1059   PetscTryMethod(pc, "PCGAMGSetRecomputeEstEig_C", (PC, PetscBool), (pc, b));
1060   PetscFunctionReturn(PETSC_SUCCESS);
1061 }
1062 
1063 static PetscErrorCode PCGAMGSetRecomputeEstEig_GAMG(PC pc, PetscBool b)
1064 {
1065   PC_MG   *mg      = (PC_MG *)pc->data;
1066   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1067 
1068   PetscFunctionBegin;
1069   pc_gamg->recompute_esteig = b;
1070   PetscFunctionReturn(PETSC_SUCCESS);
1071 }
1072 
1073 /*@
1074   PCGAMGSetEigenvalues - Set WHAT eigenvalues WHY?
1075 
1076   Collective
1077 
1078   Input Parameters:
1079 + pc   - the preconditioner context
1080 . emax - max eigenvalue
1081 - emin - min eigenvalue
1082 
1083   Options Database Key:
1084 . -pc_gamg_eigenvalues <emin,emax> - estimates of the eigenvalues
1085 
1086   Level: intermediate
1087 
1088 .seealso: `PCGAMG`, `PCGAMGSetUseSAEstEig()`
1089 @*/
1090 PetscErrorCode PCGAMGSetEigenvalues(PC pc, PetscReal emax, PetscReal emin)
1091 {
1092   PetscFunctionBegin;
1093   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1094   PetscTryMethod(pc, "PCGAMGSetEigenvalues_C", (PC, PetscReal, PetscReal), (pc, emax, emin));
1095   PetscFunctionReturn(PETSC_SUCCESS);
1096 }
1097 
1098 static PetscErrorCode PCGAMGSetEigenvalues_GAMG(PC pc, PetscReal emax, PetscReal emin)
1099 {
1100   PC_MG   *mg      = (PC_MG *)pc->data;
1101   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1102 
1103   PetscFunctionBegin;
1104   PetscCheck(emax > emin, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "Maximum eigenvalue must be larger than minimum: max %g min %g", (double)emax, (double)emin);
1105   PetscCheck(emax * emin > 0.0, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "Both eigenvalues must be of the same sign: max %g min %g", (double)emax, (double)emin);
1106   pc_gamg->emax = emax;
1107   pc_gamg->emin = emin;
1108   PetscFunctionReturn(PETSC_SUCCESS);
1109 }
1110 
1111 /*@
1112   PCGAMGSetReuseInterpolation - Reuse prolongation when rebuilding a `PCGAMG` algebraic multigrid preconditioner
1113 
1114   Collective
1115 
1116   Input Parameters:
1117 + pc - the preconditioner context
1118 - n  - `PETSC_TRUE` or `PETSC_FALSE`
1119 
1120   Options Database Key:
1121 . -pc_gamg_reuse_interpolation <true,false> - reuse the previous interpolation
1122 
1123   Level: intermediate
1124 
1125   Note:
1126   May negatively affect the convergence rate of the method on new matrices if the matrix entries change a great deal, but allows
1127   rebuilding the preconditioner quicker.
1128 
1129 .seealso: `PCGAMG`
1130 @*/
1131 PetscErrorCode PCGAMGSetReuseInterpolation(PC pc, PetscBool n)
1132 {
1133   PetscFunctionBegin;
1134   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1135   PetscTryMethod(pc, "PCGAMGSetReuseInterpolation_C", (PC, PetscBool), (pc, n));
1136   PetscFunctionReturn(PETSC_SUCCESS);
1137 }
1138 
1139 static PetscErrorCode PCGAMGSetReuseInterpolation_GAMG(PC pc, PetscBool n)
1140 {
1141   PC_MG   *mg      = (PC_MG *)pc->data;
1142   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1143 
1144   PetscFunctionBegin;
1145   pc_gamg->reuse_prol = n;
1146   PetscFunctionReturn(PETSC_SUCCESS);
1147 }
1148 
1149 /*@
1150   PCGAMGASMSetUseAggs - Have the `PCGAMG` smoother on each level use the aggregates defined by the coarsening process as the subdomains for the additive Schwarz preconditioner
1151   used as the smoother
1152 
1153   Collective
1154 
1155   Input Parameters:
1156 + pc  - the preconditioner context
1157 - flg - `PETSC_TRUE` to use aggregates, `PETSC_FALSE` to not
1158 
1159   Options Database Key:
1160 . -pc_gamg_asm_use_agg <true,false> - use aggregates to define the additive Schwarz subdomains
1161 
1162   Level: intermediate
1163 
1164 .seealso: `PCGAMG`, `PCASM`, `PCSetType`
1165 @*/
1166 PetscErrorCode PCGAMGASMSetUseAggs(PC pc, PetscBool flg)
1167 {
1168   PetscFunctionBegin;
1169   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1170   PetscTryMethod(pc, "PCGAMGASMSetUseAggs_C", (PC, PetscBool), (pc, flg));
1171   PetscFunctionReturn(PETSC_SUCCESS);
1172 }
1173 
1174 static PetscErrorCode PCGAMGASMSetUseAggs_GAMG(PC pc, PetscBool flg)
1175 {
1176   PC_MG   *mg      = (PC_MG *)pc->data;
1177   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1178 
1179   PetscFunctionBegin;
1180   pc_gamg->use_aggs_in_asm = flg;
1181   PetscFunctionReturn(PETSC_SUCCESS);
1182 }
1183 
1184 /*@
1185   PCGAMGSetParallelCoarseGridSolve - allow a parallel coarse grid solver
1186 
1187   Collective
1188 
1189   Input Parameters:
1190 + pc  - the preconditioner context
1191 - flg - `PETSC_TRUE` to not force coarse grid onto one processor
1192 
1193   Options Database Key:
1194 . -pc_gamg_parallel_coarse_grid_solver - use a parallel coarse grid direct solver
1195 
1196   Level: intermediate
1197 
1198 .seealso: `PCGAMG`, `PCGAMGSetCoarseGridLayoutType()`, `PCGAMGSetCpuPinCoarseGrids()`
1199 @*/
1200 PetscErrorCode PCGAMGSetParallelCoarseGridSolve(PC pc, PetscBool flg)
1201 {
1202   PetscFunctionBegin;
1203   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1204   PetscTryMethod(pc, "PCGAMGSetParallelCoarseGridSolve_C", (PC, PetscBool), (pc, flg));
1205   PetscFunctionReturn(PETSC_SUCCESS);
1206 }
1207 
1208 static PetscErrorCode PCGAMGSetParallelCoarseGridSolve_GAMG(PC pc, PetscBool flg)
1209 {
1210   PC_MG   *mg      = (PC_MG *)pc->data;
1211   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1212 
1213   PetscFunctionBegin;
1214   pc_gamg->use_parallel_coarse_grid_solver = flg;
1215   PetscFunctionReturn(PETSC_SUCCESS);
1216 }
1217 
1218 /*@
1219   PCGAMGSetCpuPinCoarseGrids - pin the coarse grids created in `PCGAMG` to run only on the CPU since the problems may be too small to run efficiently on the GPUs
1220 
1221   Collective
1222 
1223   Input Parameters:
1224 + pc  - the preconditioner context
1225 - flg - `PETSC_TRUE` to pin coarse grids to the CPU
1226 
1227   Options Database Key:
1228 . -pc_gamg_cpu_pin_coarse_grids - pin the coarse grids to the CPU
1229 
1230   Level: intermediate
1231 
1232 .seealso: `PCGAMG`, `PCGAMGSetCoarseGridLayoutType()`, `PCGAMGSetParallelCoarseGridSolve()`
1233 @*/
1234 PetscErrorCode PCGAMGSetCpuPinCoarseGrids(PC pc, PetscBool flg)
1235 {
1236   PetscFunctionBegin;
1237   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1238   PetscTryMethod(pc, "PCGAMGSetCpuPinCoarseGrids_C", (PC, PetscBool), (pc, flg));
1239   PetscFunctionReturn(PETSC_SUCCESS);
1240 }
1241 
1242 static PetscErrorCode PCGAMGSetCpuPinCoarseGrids_GAMG(PC pc, PetscBool flg)
1243 {
1244   PC_MG   *mg      = (PC_MG *)pc->data;
1245   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1246 
1247   PetscFunctionBegin;
1248   pc_gamg->cpu_pin_coarse_grids = flg;
1249   PetscFunctionReturn(PETSC_SUCCESS);
1250 }
1251 
1252 /*@
1253   PCGAMGSetCoarseGridLayoutType - place coarse grids on processors with natural order (compact type)
1254 
1255   Collective
1256 
1257   Input Parameters:
1258 + pc  - the preconditioner context
1259 - flg - `PCGAMGLayoutType` type, either `PCGAMG_LAYOUT_COMPACT` or `PCGAMG_LAYOUT_SPREAD`
1260 
1261   Options Database Key:
1262 . -pc_gamg_coarse_grid_layout_type - place the coarse grids with natural ordering
1263 
1264   Level: intermediate
1265 
1266 .seealso: `PCGAMG`, `PCGAMGSetParallelCoarseGridSolve()`, `PCGAMGSetCpuPinCoarseGrids()`, `PCGAMGLayoutType`, `PCGAMG_LAYOUT_COMPACT`, `PCGAMG_LAYOUT_SPREAD`
1267 @*/
1268 PetscErrorCode PCGAMGSetCoarseGridLayoutType(PC pc, PCGAMGLayoutType flg)
1269 {
1270   PetscFunctionBegin;
1271   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1272   PetscTryMethod(pc, "PCGAMGSetCoarseGridLayoutType_C", (PC, PCGAMGLayoutType), (pc, flg));
1273   PetscFunctionReturn(PETSC_SUCCESS);
1274 }
1275 
1276 static PetscErrorCode PCGAMGSetCoarseGridLayoutType_GAMG(PC pc, PCGAMGLayoutType flg)
1277 {
1278   PC_MG   *mg      = (PC_MG *)pc->data;
1279   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1280 
1281   PetscFunctionBegin;
1282   pc_gamg->layout_type = flg;
1283   PetscFunctionReturn(PETSC_SUCCESS);
1284 }
1285 
1286 /*@
1287   PCGAMGSetNlevels -  Sets the maximum number of levels `PCGAMG` will use
1288 
1289   Not Collective
1290 
1291   Input Parameters:
1292 + pc - the preconditioner
1293 - n  - the maximum number of levels to use
1294 
1295   Options Database Key:
1296 . -pc_mg_levels <n> - set the maximum number of levels to allow
1297 
1298   Level: intermediate
1299 
1300   Developer Notes:
1301   Should be called `PCGAMGSetMaximumNumberlevels()` and possible be shared with `PCMG`
1302 
1303 .seealso: `PCGAMG`
1304 @*/
1305 PetscErrorCode PCGAMGSetNlevels(PC pc, PetscInt n)
1306 {
1307   PetscFunctionBegin;
1308   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1309   PetscTryMethod(pc, "PCGAMGSetNlevels_C", (PC, PetscInt), (pc, n));
1310   PetscFunctionReturn(PETSC_SUCCESS);
1311 }
1312 
1313 static PetscErrorCode PCGAMGSetNlevels_GAMG(PC pc, PetscInt n)
1314 {
1315   PC_MG   *mg      = (PC_MG *)pc->data;
1316   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1317 
1318   PetscFunctionBegin;
1319   pc_gamg->Nlevels = n;
1320   PetscFunctionReturn(PETSC_SUCCESS);
1321 }
1322 
1323 /*@
1324   PCGAMGSetThreshold - Relative threshold to use for dropping edges in aggregation graph
1325 
1326   Not Collective
1327 
1328   Input Parameters:
1329 + pc - the preconditioner context
1330 . v  - array of threshold values for finest n levels; 0.0 means keep all nonzero entries in the graph; negative means keep even zero entries in the graph
1331 - n  - number of threshold values provided in array
1332 
1333   Options Database Key:
1334 . -pc_gamg_threshold <threshold> - the threshold to drop edges
1335 
1336   Level: intermediate
1337 
1338   Notes:
1339   Increasing the threshold decreases the rate of coarsening. Conversely reducing the threshold increases the rate of coarsening (aggressive coarsening) and thereby reduces the complexity of the coarse grids, and generally results in slower solver converge rates. Reducing coarse grid complexity reduced the complexity of Galerkin coarse grid construction considerably.
1340   Before coarsening or aggregating the graph, `PCGAMG` removes small values from the graph with this threshold, and thus reducing the coupling in the graph and a different (perhaps better) coarser set of points.
1341 
1342   If `n` is less than the total number of coarsenings (see `PCGAMGSetNlevels()`), then threshold scaling (see `PCGAMGSetThresholdScale()`) is used for each successive coarsening.
1343   In this case, `PCGAMGSetThresholdScale()` must be called before `PCGAMGSetThreshold()`.
1344   If `n` is greater than the total number of levels, the excess entries in threshold will not be used.
1345 
1346 .seealso: `PCGAMG`, `PCGAMGSetAggressiveLevels()`, `PCGAMGMISkSetAggressive()`, `PCGAMGSetMinDegreeOrderingMISk()`, `PCGAMGSetThresholdScale()`
1347 @*/
1348 PetscErrorCode PCGAMGSetThreshold(PC pc, PetscReal v[], PetscInt n)
1349 {
1350   PetscFunctionBegin;
1351   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1352   if (n) PetscAssertPointer(v, 2);
1353   PetscTryMethod(pc, "PCGAMGSetThreshold_C", (PC, PetscReal[], PetscInt), (pc, v, n));
1354   PetscFunctionReturn(PETSC_SUCCESS);
1355 }
1356 
1357 static PetscErrorCode PCGAMGSetThreshold_GAMG(PC pc, PetscReal v[], PetscInt n)
1358 {
1359   PC_MG   *mg      = (PC_MG *)pc->data;
1360   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1361   PetscInt i;
1362   PetscFunctionBegin;
1363   for (i = 0; i < PetscMin(n, PETSC_MG_MAXLEVELS); i++) pc_gamg->threshold[i] = v[i];
1364   for (; i < PETSC_MG_MAXLEVELS; i++) pc_gamg->threshold[i] = pc_gamg->threshold[i - 1] * pc_gamg->threshold_scale;
1365   PetscFunctionReturn(PETSC_SUCCESS);
1366 }
1367 
1368 /*@
1369   PCGAMGSetRankReductionFactors - Set a manual schedule for MPI rank reduction on coarse grids
1370 
1371   Collective
1372 
1373   Input Parameters:
1374 + pc - the preconditioner context
1375 . v  - array of reduction factors. 0 for first value forces a reduction to one process/device on first level in CUDA
1376 - n  - number of values provided in array
1377 
1378   Options Database Key:
1379 . -pc_gamg_rank_reduction_factors <factors> - provide the schedule
1380 
1381   Level: intermediate
1382 
1383 .seealso: `PCGAMG`, `PCGAMGSetProcEqLim()`, `PCGAMGSetCoarseEqLim()`
1384 @*/
1385 PetscErrorCode PCGAMGSetRankReductionFactors(PC pc, PetscInt v[], PetscInt n)
1386 {
1387   PetscFunctionBegin;
1388   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1389   if (n) PetscAssertPointer(v, 2);
1390   PetscTryMethod(pc, "PCGAMGSetRankReductionFactors_C", (PC, PetscInt[], PetscInt), (pc, v, n));
1391   PetscFunctionReturn(PETSC_SUCCESS);
1392 }
1393 
1394 static PetscErrorCode PCGAMGSetRankReductionFactors_GAMG(PC pc, PetscInt v[], PetscInt n)
1395 {
1396   PC_MG   *mg      = (PC_MG *)pc->data;
1397   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1398   PetscInt i;
1399   PetscFunctionBegin;
1400   for (i = 0; i < PetscMin(n, PETSC_MG_MAXLEVELS); i++) pc_gamg->level_reduction_factors[i] = v[i];
1401   for (; i < PETSC_MG_MAXLEVELS; i++) pc_gamg->level_reduction_factors[i] = -1; /* 0 stop putting one process/device on first level */
1402   PetscFunctionReturn(PETSC_SUCCESS);
1403 }
1404 
1405 /*@
1406   PCGAMGSetThresholdScale - Relative threshold reduction at each level
1407 
1408   Not Collective
1409 
1410   Input Parameters:
1411 + pc - the preconditioner context
1412 - v  - the threshold value reduction, usually < 1.0
1413 
1414   Options Database Key:
1415 . -pc_gamg_threshold_scale <v> - set the relative threshold reduction on each level
1416 
1417   Level: advanced
1418 
1419   Note:
1420   The initial threshold (for an arbitrary number of levels starting from the finest) can be set with `PCGAMGSetThreshold()`.
1421   This scaling is used for each subsequent coarsening, but must be called before `PCGAMGSetThreshold()`.
1422 
1423 .seealso: `PCGAMG`, `PCGAMGSetThreshold()`
1424 @*/
1425 PetscErrorCode PCGAMGSetThresholdScale(PC pc, PetscReal v)
1426 {
1427   PetscFunctionBegin;
1428   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1429   PetscTryMethod(pc, "PCGAMGSetThresholdScale_C", (PC, PetscReal), (pc, v));
1430   PetscFunctionReturn(PETSC_SUCCESS);
1431 }
1432 
1433 static PetscErrorCode PCGAMGSetThresholdScale_GAMG(PC pc, PetscReal v)
1434 {
1435   PC_MG   *mg      = (PC_MG *)pc->data;
1436   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1437   PetscFunctionBegin;
1438   pc_gamg->threshold_scale = v;
1439   PetscFunctionReturn(PETSC_SUCCESS);
1440 }
1441 
1442 /*@C
1443   PCGAMGSetType - Set the type of algorithm `PCGAMG` should use
1444 
1445   Collective
1446 
1447   Input Parameters:
1448 + pc   - the preconditioner context
1449 - type - `PCGAMGAGG`, `PCGAMGGEO`, or `PCGAMGCLASSICAL`
1450 
1451   Options Database Key:
1452 . -pc_gamg_type <agg,geo,classical> - type of algebraic multigrid to apply - only agg supported
1453 
1454   Level: intermediate
1455 
1456 .seealso: `PCGAMGGetType()`, `PCGAMG`, `PCGAMGType`
1457 @*/
1458 PetscErrorCode PCGAMGSetType(PC pc, PCGAMGType type)
1459 {
1460   PetscFunctionBegin;
1461   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1462   PetscTryMethod(pc, "PCGAMGSetType_C", (PC, PCGAMGType), (pc, type));
1463   PetscFunctionReturn(PETSC_SUCCESS);
1464 }
1465 
1466 /*@C
1467   PCGAMGGetType - Get the type of algorithm `PCGAMG` will use
1468 
1469   Collective
1470 
1471   Input Parameter:
1472 . pc - the preconditioner context
1473 
1474   Output Parameter:
1475 . type - the type of algorithm used
1476 
1477   Level: intermediate
1478 
1479 .seealso: `PCGAMG`, `PCGAMGSetType()`, `PCGAMGType`
1480 @*/
1481 PetscErrorCode PCGAMGGetType(PC pc, PCGAMGType *type)
1482 {
1483   PetscFunctionBegin;
1484   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1485   PetscUseMethod(pc, "PCGAMGGetType_C", (PC, PCGAMGType *), (pc, type));
1486   PetscFunctionReturn(PETSC_SUCCESS);
1487 }
1488 
1489 static PetscErrorCode PCGAMGGetType_GAMG(PC pc, PCGAMGType *type)
1490 {
1491   PC_MG   *mg      = (PC_MG *)pc->data;
1492   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1493 
1494   PetscFunctionBegin;
1495   *type = pc_gamg->type;
1496   PetscFunctionReturn(PETSC_SUCCESS);
1497 }
1498 
1499 static PetscErrorCode PCGAMGSetType_GAMG(PC pc, PCGAMGType type)
1500 {
1501   PC_MG   *mg      = (PC_MG *)pc->data;
1502   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1503   PetscErrorCode (*r)(PC);
1504 
1505   PetscFunctionBegin;
1506   pc_gamg->type = type;
1507   PetscCall(PetscFunctionListFind(GAMGList, type, &r));
1508   PetscCheck(r, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown GAMG type %s given", type);
1509   if (pc_gamg->ops->destroy) {
1510     PetscCall((*pc_gamg->ops->destroy)(pc));
1511     PetscCall(PetscMemzero(pc_gamg->ops, sizeof(struct _PCGAMGOps)));
1512     pc_gamg->ops->createlevel = PCGAMGCreateLevel_GAMG;
1513     /* cleaning up common data in pc_gamg - this should disappear someday */
1514     pc_gamg->data_cell_cols      = 0;
1515     pc_gamg->data_cell_rows      = 0;
1516     pc_gamg->orig_data_cell_cols = 0;
1517     pc_gamg->orig_data_cell_rows = 0;
1518     PetscCall(PetscFree(pc_gamg->data));
1519     pc_gamg->data_sz = 0;
1520   }
1521   PetscCall(PetscFree(pc_gamg->gamg_type_name));
1522   PetscCall(PetscStrallocpy(type, &pc_gamg->gamg_type_name));
1523   PetscCall((*r)(pc));
1524   PetscFunctionReturn(PETSC_SUCCESS);
1525 }
1526 
1527 static PetscErrorCode PCView_GAMG(PC pc, PetscViewer viewer)
1528 {
1529   PC_MG    *mg      = (PC_MG *)pc->data;
1530   PC_GAMG  *pc_gamg = (PC_GAMG *)mg->innerctx;
1531   PetscReal gc = 0, oc = 0;
1532 
1533   PetscFunctionBegin;
1534   PetscCall(PetscViewerASCIIPrintf(viewer, "    GAMG specific options\n"));
1535   PetscCall(PetscViewerASCIIPrintf(viewer, "      Threshold for dropping small values in graph on each level ="));
1536   for (PetscInt i = 0; i < mg->nlevels; i++) PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)pc_gamg->threshold[i]));
1537   PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1538   PetscCall(PetscViewerASCIIPrintf(viewer, "      Threshold scaling factor for each level not specified = %g\n", (double)pc_gamg->threshold_scale));
1539   if (pc_gamg->use_aggs_in_asm) PetscCall(PetscViewerASCIIPrintf(viewer, "      Using aggregates from coarsening process to define subdomains for PCASM\n"));
1540   if (pc_gamg->use_parallel_coarse_grid_solver) PetscCall(PetscViewerASCIIPrintf(viewer, "      Using parallel coarse grid solver (all coarse grid equations not put on one process)\n"));
1541   if (pc_gamg->ops->view) PetscCall((*pc_gamg->ops->view)(pc, viewer));
1542   PetscCall(PCMGGetGridComplexity(pc, &gc, &oc));
1543   PetscCall(PetscViewerASCIIPrintf(viewer, "      Complexity:    grid = %g    operator = %g\n", (double)gc, (double)oc));
1544   PetscFunctionReturn(PETSC_SUCCESS);
1545 }
1546 
1547 static PetscErrorCode PCSetFromOptions_GAMG(PC pc, PetscOptionItems *PetscOptionsObject)
1548 {
1549   PC_MG             *mg      = (PC_MG *)pc->data;
1550   PC_GAMG           *pc_gamg = (PC_GAMG *)mg->innerctx;
1551   PetscBool          flag;
1552   MPI_Comm           comm;
1553   char               prefix[256], tname[32];
1554   PetscInt           i, n;
1555   const char        *pcpre;
1556   static const char *LayoutTypes[] = {"compact", "spread", "PCGAMGLayoutType", "PC_GAMG_LAYOUT", NULL};
1557   PetscFunctionBegin;
1558   PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
1559   PetscOptionsHeadBegin(PetscOptionsObject, "GAMG options");
1560   PetscCall(PetscOptionsFList("-pc_gamg_type", "Type of AMG method (only 'agg' supported and useful)", "PCGAMGSetType", GAMGList, pc_gamg->gamg_type_name, tname, sizeof(tname), &flag));
1561   if (flag) PetscCall(PCGAMGSetType(pc, tname));
1562   PetscCall(PetscOptionsBool("-pc_gamg_repartition", "Repartion coarse grids", "PCGAMGSetRepartition", pc_gamg->repart, &pc_gamg->repart, NULL));
1563   PetscCall(PetscOptionsBool("-pc_gamg_use_sa_esteig", "Use eigen estimate from smoothed aggregation for smoother", "PCGAMGSetUseSAEstEig", pc_gamg->use_sa_esteig, &pc_gamg->use_sa_esteig, NULL));
1564   PetscCall(PetscOptionsBool("-pc_gamg_recompute_esteig", "Set flag to recompute eigen estimates for Chebyshev when matrix changes", "PCGAMGSetRecomputeEstEig", pc_gamg->recompute_esteig, &pc_gamg->recompute_esteig, NULL));
1565   PetscCall(PetscOptionsBool("-pc_gamg_reuse_interpolation", "Reuse prolongation operator", "PCGAMGReuseInterpolation", pc_gamg->reuse_prol, &pc_gamg->reuse_prol, NULL));
1566   PetscCall(PetscOptionsBool("-pc_gamg_asm_use_agg", "Use aggregation aggregates for ASM smoother", "PCGAMGASMSetUseAggs", pc_gamg->use_aggs_in_asm, &pc_gamg->use_aggs_in_asm, NULL));
1567   PetscCall(PetscOptionsBool("-pc_gamg_parallel_coarse_grid_solver", "Use parallel coarse grid solver (otherwise put last grid on one process)", "PCGAMGSetParallelCoarseGridSolve", pc_gamg->use_parallel_coarse_grid_solver, &pc_gamg->use_parallel_coarse_grid_solver, NULL));
1568   PetscCall(PetscOptionsBool("-pc_gamg_cpu_pin_coarse_grids", "Pin coarse grids to the CPU", "PCGAMGSetCpuPinCoarseGrids", pc_gamg->cpu_pin_coarse_grids, &pc_gamg->cpu_pin_coarse_grids, NULL));
1569   PetscCall(PetscOptionsEnum("-pc_gamg_coarse_grid_layout_type", "compact: place reduced grids on processes in natural order; spread: distribute to whole machine for more memory bandwidth", "PCGAMGSetCoarseGridLayoutType", LayoutTypes,
1570                              (PetscEnum)pc_gamg->layout_type, (PetscEnum *)&pc_gamg->layout_type, NULL));
1571   PetscCall(PetscOptionsInt("-pc_gamg_process_eq_limit", "Limit (goal) on number of equations per process on coarse grids", "PCGAMGSetProcEqLim", pc_gamg->min_eq_proc, &pc_gamg->min_eq_proc, NULL));
1572   PetscCall(PetscOptionsInt("-pc_gamg_coarse_eq_limit", "Limit on number of equations for the coarse grid", "PCGAMGSetCoarseEqLim", pc_gamg->coarse_eq_limit, &pc_gamg->coarse_eq_limit, NULL));
1573   PetscCall(PetscOptionsReal("-pc_gamg_threshold_scale", "Scaling of threshold for each level not specified", "PCGAMGSetThresholdScale", pc_gamg->threshold_scale, &pc_gamg->threshold_scale, NULL));
1574   n = PETSC_MG_MAXLEVELS;
1575   PetscCall(PetscOptionsRealArray("-pc_gamg_threshold", "Relative threshold to use for dropping edges in aggregation graph", "PCGAMGSetThreshold", pc_gamg->threshold, &n, &flag));
1576   if (!flag || n < PETSC_MG_MAXLEVELS) {
1577     if (!flag) n = 1;
1578     i = n;
1579     do {
1580       pc_gamg->threshold[i] = pc_gamg->threshold[i - 1] * pc_gamg->threshold_scale;
1581     } while (++i < PETSC_MG_MAXLEVELS);
1582   }
1583   n = PETSC_MG_MAXLEVELS;
1584   PetscCall(PetscOptionsIntArray("-pc_gamg_rank_reduction_factors", "Manual schedule of coarse grid reduction factors that overrides internal heuristics (0 for first reduction puts one process/device)", "PCGAMGSetRankReductionFactors", pc_gamg->level_reduction_factors, &n, &flag));
1585   if (!flag) i = 0;
1586   else i = n;
1587   do {
1588     pc_gamg->level_reduction_factors[i] = -1;
1589   } while (++i < PETSC_MG_MAXLEVELS);
1590   PetscCall(PetscOptionsInt("-pc_mg_levels", "Set number of MG levels", "PCGAMGSetNlevels", pc_gamg->Nlevels, &pc_gamg->Nlevels, NULL));
1591   {
1592     PetscReal eminmax[2] = {0., 0.};
1593     n                    = 2;
1594     PetscCall(PetscOptionsRealArray("-pc_gamg_eigenvalues", "extreme eigenvalues for smoothed aggregation", "PCGAMGSetEigenvalues", eminmax, &n, &flag));
1595     if (flag) {
1596       PetscCheck(n == 2, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "-pc_gamg_eigenvalues: must specify 2 parameters, min and max eigenvalues");
1597       PetscCall(PCGAMGSetEigenvalues(pc, eminmax[1], eminmax[0]));
1598     }
1599   }
1600   /* set options for subtype */
1601   PetscCall((*pc_gamg->ops->setfromoptions)(pc, PetscOptionsObject));
1602 
1603   PetscCall(PCGetOptionsPrefix(pc, &pcpre));
1604   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_gamg_", pcpre ? pcpre : ""));
1605   PetscOptionsHeadEnd();
1606   PetscFunctionReturn(PETSC_SUCCESS);
1607 }
1608 
1609 /*MC
1610   PCGAMG - Geometric algebraic multigrid (AMG) preconditioner
1611 
1612   Options Database Keys:
1613 + -pc_gamg_type <type,default=agg> - one of agg, geo, or classical (only smoothed aggregation, agg, supported)
1614 . -pc_gamg_repartition  <bool,default=false> - repartition the degrees of freedom across the coarse grids as they are determined
1615 . -pc_gamg_asm_use_agg <bool,default=false> - use the aggregates from the coasening process to defined the subdomains on each level for the PCASM smoother
1616 . -pc_gamg_process_eq_limit <limit, default=50> - `PCGAMG` will reduce the number of MPI ranks used directly on the coarse grids so that there are around <limit>
1617                                         equations on each process that has degrees of freedom
1618 . -pc_gamg_coarse_eq_limit <limit, default=50> - Set maximum number of equations on coarsest grid to aim for.
1619 . -pc_gamg_reuse_interpolation <bool,default=true> - when rebuilding the algebraic multigrid preconditioner reuse the previously computed interpolations (should always be true)
1620 . -pc_gamg_threshold[] <thresh,default=[-1,...]> - Before aggregating the graph `PCGAMG` will remove small values from the graph on each level (< 0 does no filtering)
1621 - -pc_gamg_threshold_scale <scale,default=1> - Scaling of threshold on each coarser grid if not specified
1622 
1623   Options Database Keys for Aggregation:
1624 + -pc_gamg_agg_nsmooths <nsmooth, default=1> - number of smoothing steps to use with smooth aggregation
1625 . -pc_gamg_aggressive_coarsening <n,default=1> - number of aggressive coarsening (MIS-2) levels from finest.
1626 . -pc_gamg_aggressive_square_graph <bool,default=false> - Use square graph (A'A) or MIS-k (k=2) for aggressive coarsening
1627 . -pc_gamg_mis_k_minimum_degree_ordering <bool,default=true> - Use minimum degree ordering in greedy MIS algorithm
1628 - -pc_gamg_aggressive_mis_k <n,default=2> - Number (k) distance in MIS coarsening (>2 is 'aggressive')
1629 
1630   Options Database Keys for Multigrid:
1631 + -pc_mg_cycle_type <v> - v or w, see `PCMGSetCycleType()`
1632 . -pc_mg_distinct_smoothup - configure the up and down (pre and post) smoothers separately, see PCMGSetDistinctSmoothUp()
1633 . -pc_mg_type <multiplicative> - (one of) additive multiplicative full kascade
1634 - -pc_mg_levels <levels> - Number of levels of multigrid to use. GAMG has a heuristic so pc_mg_levels is not usually used with GAMG
1635 
1636   Level: intermediate
1637 
1638   Notes:
1639   To obtain good performance for `PCGAMG` for vector valued problems you must
1640   call `MatSetBlockSize()` to indicate the number of degrees of freedom per grid point
1641   call `MatSetNearNullSpace()` (or `PCSetCoordinates()` if solving the equations of elasticity) to indicate the near null space of the operator
1642 
1643   The many options for `PCMG` also work directly for `PCGAMG` such as controlling the smoothers on each level etc.
1644 
1645   See [the Users Manual section on PCGAMG](sec_amg) and [the Users Manual section on PCMG](sec_mg)for more details.
1646 
1647 .seealso: `PCCreate()`, `PCSetType()`, `MatSetBlockSize()`, `PCMGType`, `PCSetCoordinates()`, `MatSetNearNullSpace()`, `PCGAMGSetType()`, `PCGAMGAGG`, `PCGAMGGEO`, `PCGAMGCLASSICAL`, `PCGAMGSetProcEqLim()`,
1648           `PCGAMGSetCoarseEqLim()`, `PCGAMGSetRepartition()`, `PCGAMGRegister()`, `PCGAMGSetReuseInterpolation()`, `PCGAMGASMSetUseAggs()`, `PCGAMGSetParallelCoarseGridSolve()`, `PCGAMGSetNlevels()`, `PCGAMGSetThreshold()`, `PCGAMGGetType()`, `PCGAMGSetUseSAEstEig()`
1649 M*/
1650 PETSC_EXTERN PetscErrorCode PCCreate_GAMG(PC pc)
1651 {
1652   PC_GAMG *pc_gamg;
1653   PC_MG   *mg;
1654 
1655   PetscFunctionBegin;
1656   /* register AMG type */
1657   PetscCall(PCGAMGInitializePackage());
1658 
1659   /* PCGAMG is an inherited class of PCMG. Initialize pc as PCMG */
1660   PetscCall(PCSetType(pc, PCMG));
1661   PetscCall(PetscObjectChangeTypeName((PetscObject)pc, PCGAMG));
1662 
1663   /* create a supporting struct and attach it to pc */
1664   PetscCall(PetscNew(&pc_gamg));
1665   PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_EXTERNAL));
1666   mg           = (PC_MG *)pc->data;
1667   mg->innerctx = pc_gamg;
1668 
1669   PetscCall(PetscNew(&pc_gamg->ops));
1670 
1671   /* these should be in subctx but repartitioning needs simple arrays */
1672   pc_gamg->data_sz = 0;
1673   pc_gamg->data    = NULL;
1674 
1675   /* overwrite the pointers of PCMG by the functions of base class PCGAMG */
1676   pc->ops->setfromoptions = PCSetFromOptions_GAMG;
1677   pc->ops->setup          = PCSetUp_GAMG;
1678   pc->ops->reset          = PCReset_GAMG;
1679   pc->ops->destroy        = PCDestroy_GAMG;
1680   mg->view                = PCView_GAMG;
1681 
1682   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetProcEqLim_C", PCGAMGSetProcEqLim_GAMG));
1683   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseEqLim_C", PCGAMGSetCoarseEqLim_GAMG));
1684   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRepartition_C", PCGAMGSetRepartition_GAMG));
1685   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetEigenvalues_C", PCGAMGSetEigenvalues_GAMG));
1686   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetUseSAEstEig_C", PCGAMGSetUseSAEstEig_GAMG));
1687   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRecomputeEstEig_C", PCGAMGSetRecomputeEstEig_GAMG));
1688   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetReuseInterpolation_C", PCGAMGSetReuseInterpolation_GAMG));
1689   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGASMSetUseAggs_C", PCGAMGASMSetUseAggs_GAMG));
1690   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetParallelCoarseGridSolve_C", PCGAMGSetParallelCoarseGridSolve_GAMG));
1691   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCpuPinCoarseGrids_C", PCGAMGSetCpuPinCoarseGrids_GAMG));
1692   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseGridLayoutType_C", PCGAMGSetCoarseGridLayoutType_GAMG));
1693   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThreshold_C", PCGAMGSetThreshold_GAMG));
1694   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRankReductionFactors_C", PCGAMGSetRankReductionFactors_GAMG));
1695   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThresholdScale_C", PCGAMGSetThresholdScale_GAMG));
1696   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetType_C", PCGAMGSetType_GAMG));
1697   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGGetType_C", PCGAMGGetType_GAMG));
1698   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetNlevels_C", PCGAMGSetNlevels_GAMG));
1699   pc_gamg->repart                          = PETSC_FALSE;
1700   pc_gamg->reuse_prol                      = PETSC_TRUE;
1701   pc_gamg->use_aggs_in_asm                 = PETSC_FALSE;
1702   pc_gamg->use_parallel_coarse_grid_solver = PETSC_FALSE;
1703   pc_gamg->cpu_pin_coarse_grids            = PETSC_FALSE;
1704   pc_gamg->layout_type                     = PCGAMG_LAYOUT_SPREAD;
1705   pc_gamg->min_eq_proc                     = 50;
1706   pc_gamg->coarse_eq_limit                 = 50;
1707   for (int i = 0; i < PETSC_MG_MAXLEVELS; i++) pc_gamg->threshold[i] = -1;
1708   pc_gamg->threshold_scale  = 1.;
1709   pc_gamg->Nlevels          = PETSC_MG_MAXLEVELS;
1710   pc_gamg->current_level    = 0; /* don't need to init really */
1711   pc_gamg->use_sa_esteig    = PETSC_TRUE;
1712   pc_gamg->recompute_esteig = PETSC_TRUE;
1713   pc_gamg->emin             = 0;
1714   pc_gamg->emax             = 0;
1715 
1716   pc_gamg->ops->createlevel = PCGAMGCreateLevel_GAMG;
1717 
1718   /* PCSetUp_GAMG assumes that the type has been set, so set it to the default now */
1719   PetscCall(PCGAMGSetType(pc, PCGAMGAGG));
1720   PetscFunctionReturn(PETSC_SUCCESS);
1721 }
1722 
1723 /*@C
1724   PCGAMGInitializePackage - This function initializes everything in the `PCGAMG` package. It is called
1725   from `PCInitializePackage()`.
1726 
1727   Level: developer
1728 
1729 .seealso: `PetscInitialize()`
1730 @*/
1731 PetscErrorCode PCGAMGInitializePackage(void)
1732 {
1733   PetscInt l;
1734 
1735   PetscFunctionBegin;
1736   if (PCGAMGPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS);
1737   PCGAMGPackageInitialized = PETSC_TRUE;
1738   PetscCall(PetscFunctionListAdd(&GAMGList, PCGAMGGEO, PCCreateGAMG_GEO));
1739   PetscCall(PetscFunctionListAdd(&GAMGList, PCGAMGAGG, PCCreateGAMG_AGG));
1740   PetscCall(PetscFunctionListAdd(&GAMGList, PCGAMGCLASSICAL, PCCreateGAMG_Classical));
1741   PetscCall(PetscRegisterFinalize(PCGAMGFinalizePackage));
1742 
1743   /* general events */
1744   PetscCall(PetscLogEventRegister("PCSetUp_GAMG+", PC_CLASSID, &petsc_gamg_setup_events[GAMG_SETUP]));
1745   PetscCall(PetscLogEventRegister(" PCGAMGCreateG", PC_CLASSID, &petsc_gamg_setup_events[GAMG_GRAPH]));
1746   PetscCall(PetscLogEventRegister(" GAMG Coarsen", PC_CLASSID, &petsc_gamg_setup_events[GAMG_COARSEN]));
1747   PetscCall(PetscLogEventRegister("  GAMG MIS/Agg", PC_CLASSID, &petsc_gamg_setup_events[GAMG_MIS]));
1748   PetscCall(PetscLogEventRegister(" PCGAMGProl", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PROL]));
1749   PetscCall(PetscLogEventRegister("  GAMG Prol-col", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PROLA]));
1750   PetscCall(PetscLogEventRegister("  GAMG Prol-lift", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PROLB]));
1751   PetscCall(PetscLogEventRegister(" PCGAMGOptProl", PC_CLASSID, &petsc_gamg_setup_events[GAMG_OPT]));
1752   PetscCall(PetscLogEventRegister("  GAMG smooth", PC_CLASSID, &petsc_gamg_setup_events[GAMG_OPTSM]));
1753   PetscCall(PetscLogEventRegister(" PCGAMGCreateL", PC_CLASSID, &petsc_gamg_setup_events[GAMG_LEVEL]));
1754   PetscCall(PetscLogEventRegister("  GAMG PtAP", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PTAP]));
1755   PetscCall(PetscLogEventRegister("  GAMG Reduce", PC_CLASSID, &petsc_gamg_setup_events[GAMG_REDUCE]));
1756   PetscCall(PetscLogEventRegister("   GAMG Repart", PC_CLASSID, &petsc_gamg_setup_events[GAMG_REPART]));
1757   /* PetscCall(PetscLogEventRegister("   GAMG Inv-Srt", PC_CLASSID, &petsc_gamg_setup_events[SET13])); */
1758   /* PetscCall(PetscLogEventRegister("   GAMG Move A", PC_CLASSID, &petsc_gamg_setup_events[SET14])); */
1759   /* PetscCall(PetscLogEventRegister("   GAMG Move P", PC_CLASSID, &petsc_gamg_setup_events[SET15])); */
1760   for (l = 0; l < PETSC_MG_MAXLEVELS; l++) {
1761     char ename[32];
1762 
1763     PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCGAMG Squ l%02" PetscInt_FMT, l));
1764     PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &petsc_gamg_setup_matmat_events[l][0]));
1765     PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCGAMG Gal l%02" PetscInt_FMT, l));
1766     PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &petsc_gamg_setup_matmat_events[l][1]));
1767     PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCGAMG Opt l%02" PetscInt_FMT, l));
1768     PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &petsc_gamg_setup_matmat_events[l][2]));
1769   }
1770 #if defined(GAMG_STAGES)
1771   { /* create timer stages */
1772     char str[32];
1773     PetscCall(PetscSNPrintf(str, PETSC_STATIC_ARRAY_LENGTH(str), "GAMG Level %d", 0));
1774     PetscCall(PetscLogStageRegister(str, &gamg_stages[0]));
1775   }
1776 #endif
1777   PetscFunctionReturn(PETSC_SUCCESS);
1778 }
1779 
1780 /*@C
1781   PCGAMGFinalizePackage - This function frees everything from the `PCGAMG` package. It is
1782   called from `PetscFinalize()` automatically.
1783 
1784   Level: developer
1785 
1786 .seealso: `PetscFinalize()`
1787 @*/
1788 PetscErrorCode PCGAMGFinalizePackage(void)
1789 {
1790   PetscFunctionBegin;
1791   PCGAMGPackageInitialized = PETSC_FALSE;
1792   PetscCall(PetscFunctionListDestroy(&GAMGList));
1793   PetscFunctionReturn(PETSC_SUCCESS);
1794 }
1795 
1796 /*@C
1797   PCGAMGRegister - Register a `PCGAMG` implementation.
1798 
1799   Input Parameters:
1800 + type   - string that will be used as the name of the `PCGAMG` type.
1801 - create - function for creating the gamg context.
1802 
1803   Level: developer
1804 
1805 .seealso: `PCGAMGType`, `PCGAMG`, `PCGAMGSetType()`
1806 @*/
1807 PetscErrorCode PCGAMGRegister(PCGAMGType type, PetscErrorCode (*create)(PC))
1808 {
1809   PetscFunctionBegin;
1810   PetscCall(PCGAMGInitializePackage());
1811   PetscCall(PetscFunctionListAdd(&GAMGList, type, create));
1812   PetscFunctionReturn(PETSC_SUCCESS);
1813 }
1814 
1815 /*@C
1816   PCGAMGCreateGraph - Creates a graph that is used by the ``PCGAMGType`` in the coarsening process
1817 
1818   Input Parameters:
1819 + pc - the `PCGAMG`
1820 - A  - the matrix, for any level
1821 
1822   Output Parameter:
1823 . G - the graph
1824 
1825   Level: advanced
1826 
1827 .seealso: `PCGAMGType`, `PCGAMG`, `PCGAMGSetType()`
1828 @*/
1829 PetscErrorCode PCGAMGCreateGraph(PC pc, Mat A, Mat *G)
1830 {
1831   PC_MG   *mg      = (PC_MG *)pc->data;
1832   PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx;
1833 
1834   PetscFunctionBegin;
1835   PetscCall(pc_gamg->ops->creategraph(pc, A, G));
1836   PetscFunctionReturn(PETSC_SUCCESS);
1837 }
1838