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