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