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