xref: /petsc/src/ksp/pc/impls/gamg/classical.c (revision ebead697dbf761eb322f829370bbe90b3bd93fa3)
1 #include <../src/ksp/pc/impls/gamg/gamg.h>        /*I "petscpc.h" I*/
2 #include <petscsf.h>
3 
4 PetscFunctionList PCGAMGClassicalProlongatorList    = NULL;
5 PetscBool         PCGAMGClassicalPackageInitialized = PETSC_FALSE;
6 
7 typedef struct {
8   PetscReal interp_threshold; /* interpolation threshold */
9   char      prolongtype[256];
10   PetscInt  nsmooths;         /* number of jacobi smoothings on the prolongator */
11 } PC_GAMG_Classical;
12 
13 /*@C
14    PCGAMGClassicalSetType - Sets the type of classical interpolation to use
15 
16    Collective on PC
17 
18    Input Parameters:
19 .  pc - the preconditioner context
20 
21    Options Database Key:
22 .  -pc_gamg_classical_type <direct,standard> - set type of Classical AMG prolongation
23 
24    Level: intermediate
25 
26 .seealso: `()`
27 @*/
28 PetscErrorCode PCGAMGClassicalSetType(PC pc, PCGAMGClassicalType type)
29 {
30   PetscFunctionBegin;
31   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
32   PetscTryMethod(pc,"PCGAMGClassicalSetType_C",(PC,PCGAMGClassicalType),(pc,type));
33   PetscFunctionReturn(0);
34 }
35 
36 /*@C
37    PCGAMGClassicalGetType - Gets the type of classical interpolation to use
38 
39    Collective on PC
40 
41    Input Parameter:
42 .  pc - the preconditioner context
43 
44    Output Parameter:
45 .  type - the type used
46 
47    Level: intermediate
48 
49 .seealso: `()`
50 @*/
51 PetscErrorCode PCGAMGClassicalGetType(PC pc, PCGAMGClassicalType *type)
52 {
53   PetscFunctionBegin;
54   PetscValidHeaderSpecific(pc,PC_CLASSID,1);
55   PetscUseMethod(pc,"PCGAMGClassicalGetType_C",(PC,PCGAMGClassicalType*),(pc,type));
56   PetscFunctionReturn(0);
57 }
58 
59 static PetscErrorCode PCGAMGClassicalSetType_GAMG(PC pc, PCGAMGClassicalType type)
60 {
61   PC_MG             *mg          = (PC_MG*)pc->data;
62   PC_GAMG           *pc_gamg     = (PC_GAMG*)mg->innerctx;
63   PC_GAMG_Classical *cls         = (PC_GAMG_Classical*)pc_gamg->subctx;
64 
65   PetscFunctionBegin;
66   PetscCall(PetscStrcpy(cls->prolongtype,type));
67   PetscFunctionReturn(0);
68 }
69 
70 static PetscErrorCode PCGAMGClassicalGetType_GAMG(PC pc, PCGAMGClassicalType *type)
71 {
72   PC_MG             *mg          = (PC_MG*)pc->data;
73   PC_GAMG           *pc_gamg     = (PC_GAMG*)mg->innerctx;
74   PC_GAMG_Classical *cls         = (PC_GAMG_Classical*)pc_gamg->subctx;
75 
76   PetscFunctionBegin;
77   *type = cls->prolongtype;
78   PetscFunctionReturn(0);
79 }
80 
81 PetscErrorCode PCGAMGGraph_Classical(PC pc,Mat A,Mat *G)
82 {
83   PetscInt          s,f,n,idx,lidx,gidx;
84   PetscInt          r,c,ncols;
85   const PetscInt    *rcol;
86   const PetscScalar *rval;
87   PetscInt          *gcol;
88   PetscScalar       *gval;
89   PetscReal         rmax;
90   PetscInt          cmax = 0;
91   PC_MG             *mg = (PC_MG *)pc->data;
92   PC_GAMG           *gamg = (PC_GAMG *)mg->innerctx;
93   PetscInt          *gsparse,*lsparse;
94   PetscScalar       *Amax;
95   MatType           mtype;
96 
97   PetscFunctionBegin;
98   PetscCall(MatGetOwnershipRange(A,&s,&f));
99   n=f-s;
100   PetscCall(PetscMalloc3(n,&lsparse,n,&gsparse,n,&Amax));
101 
102   for (r = 0;r < n;r++) {
103     lsparse[r] = 0;
104     gsparse[r] = 0;
105   }
106 
107   for (r = s;r < f;r++) {
108     /* determine the maximum off-diagonal in each row */
109     rmax = 0.;
110     PetscCall(MatGetRow(A,r,&ncols,&rcol,&rval));
111     for (c = 0; c < ncols; c++) {
112       if (PetscRealPart(-rval[c]) > rmax && rcol[c] != r) {
113         rmax = PetscRealPart(-rval[c]);
114       }
115     }
116     Amax[r-s] = rmax;
117     if (ncols > cmax) cmax = ncols;
118     lidx = 0;
119     gidx = 0;
120     /* create the local and global sparsity patterns */
121     for (c = 0; c < ncols; c++) {
122       if (PetscRealPart(-rval[c]) > gamg->threshold[0]*PetscRealPart(Amax[r-s]) || rcol[c] == r) {
123         if (rcol[c] < f && rcol[c] >= s) {
124           lidx++;
125         } else {
126           gidx++;
127         }
128       }
129     }
130     PetscCall(MatRestoreRow(A,r,&ncols,&rcol,&rval));
131     lsparse[r-s] = lidx;
132     gsparse[r-s] = gidx;
133   }
134   PetscCall(PetscMalloc2(cmax,&gval,cmax,&gcol));
135 
136   PetscCall(MatCreate(PetscObjectComm((PetscObject)A),G));
137   PetscCall(MatGetType(A,&mtype));
138   PetscCall(MatSetType(*G,mtype));
139   PetscCall(MatSetSizes(*G,n,n,PETSC_DETERMINE,PETSC_DETERMINE));
140   PetscCall(MatMPIAIJSetPreallocation(*G,0,lsparse,0,gsparse));
141   PetscCall(MatSeqAIJSetPreallocation(*G,0,lsparse));
142   for (r = s;r < f;r++) {
143     PetscCall(MatGetRow(A,r,&ncols,&rcol,&rval));
144     idx = 0;
145     for (c = 0; c < ncols; c++) {
146       /* classical strength of connection */
147       if (PetscRealPart(-rval[c]) > gamg->threshold[0]*PetscRealPart(Amax[r-s]) || rcol[c] == r) {
148         gcol[idx] = rcol[c];
149         gval[idx] = rval[c];
150         idx++;
151       }
152     }
153     PetscCall(MatSetValues(*G,1,&r,idx,gcol,gval,INSERT_VALUES));
154     PetscCall(MatRestoreRow(A,r,&ncols,&rcol,&rval));
155   }
156   PetscCall(MatAssemblyBegin(*G, MAT_FINAL_ASSEMBLY));
157   PetscCall(MatAssemblyEnd(*G, MAT_FINAL_ASSEMBLY));
158 
159   PetscCall(PetscFree2(gval,gcol));
160   PetscCall(PetscFree3(lsparse,gsparse,Amax));
161   PetscFunctionReturn(0);
162 }
163 
164 PetscErrorCode PCGAMGCoarsen_Classical(PC pc,Mat *G,PetscCoarsenData **agg_lists)
165 {
166   MatCoarsen       crs;
167   MPI_Comm         fcomm = ((PetscObject)pc)->comm;
168 
169   PetscFunctionBegin;
170   PetscCheck(G,fcomm,PETSC_ERR_ARG_WRONGSTATE,"Must set Graph in PC in PCGAMG before coarsening");
171 
172   PetscCall(MatCoarsenCreate(fcomm,&crs));
173   PetscCall(MatCoarsenSetFromOptions(crs));
174   PetscCall(MatCoarsenSetAdjacency(crs,*G));
175   PetscCall(MatCoarsenSetStrictAggs(crs,PETSC_TRUE));
176   PetscCall(MatCoarsenApply(crs));
177   PetscCall(MatCoarsenGetData(crs,agg_lists));
178   PetscCall(MatCoarsenDestroy(&crs));
179   PetscFunctionReturn(0);
180 }
181 
182 PetscErrorCode PCGAMGProlongator_Classical_Direct(PC pc, Mat A, Mat G, PetscCoarsenData *agg_lists,Mat *P)
183 {
184   PC_MG             *mg          = (PC_MG*)pc->data;
185   PC_GAMG           *gamg        = (PC_GAMG*)mg->innerctx;
186   PetscBool         iscoarse,isMPIAIJ,isSEQAIJ;
187   PetscInt          fn,cn,fs,fe,cs,ce,i,j,ncols,col,row_f,row_c,cmax=0,idx,noff;
188   PetscInt          *lcid,*gcid,*lsparse,*gsparse,*colmap,*pcols;
189   const PetscInt    *rcol;
190   PetscReal         *Amax_pos,*Amax_neg;
191   PetscScalar       g_pos,g_neg,a_pos,a_neg,diag,invdiag,alpha,beta,pij;
192   PetscScalar       *pvals;
193   const PetscScalar *rval;
194   Mat               lA,gA=NULL;
195   MatType           mtype;
196   Vec               C,lvec;
197   PetscLayout       clayout;
198   PetscSF           sf;
199   Mat_MPIAIJ        *mpiaij;
200 
201   PetscFunctionBegin;
202   PetscCall(MatGetOwnershipRange(A,&fs,&fe));
203   fn = fe-fs;
204   PetscCall(PetscObjectTypeCompare((PetscObject)A,MATMPIAIJ,&isMPIAIJ));
205   PetscCall(PetscObjectTypeCompare((PetscObject)A,MATSEQAIJ,&isSEQAIJ));
206   PetscCheck(isMPIAIJ || isSEQAIJ,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONG,"Classical AMG requires MPIAIJ matrix");
207   if (isMPIAIJ) {
208     mpiaij = (Mat_MPIAIJ*)A->data;
209     lA = mpiaij->A;
210     gA = mpiaij->B;
211     lvec = mpiaij->lvec;
212     PetscCall(VecGetSize(lvec,&noff));
213     colmap = mpiaij->garray;
214     PetscCall(MatGetLayouts(A,NULL,&clayout));
215     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)A),&sf));
216     PetscCall(PetscSFSetGraphLayout(sf,clayout,noff,NULL,PETSC_COPY_VALUES,colmap));
217     PetscCall(PetscMalloc1(noff,&gcid));
218   } else {
219     lA = A;
220   }
221   PetscCall(PetscMalloc5(fn,&lsparse,fn,&gsparse,fn,&lcid,fn,&Amax_pos,fn,&Amax_neg));
222 
223   /* count the number of coarse unknowns */
224   cn = 0;
225   for (i=0;i<fn;i++) {
226     /* filter out singletons */
227     PetscCall(PetscCDEmptyAt(agg_lists,i,&iscoarse));
228     lcid[i] = -1;
229     if (!iscoarse) {
230       cn++;
231     }
232   }
233 
234    /* create the coarse vector */
235   PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)A),cn,PETSC_DECIDE,&C));
236   PetscCall(VecGetOwnershipRange(C,&cs,&ce));
237 
238   cn = 0;
239   for (i=0;i<fn;i++) {
240     PetscCall(PetscCDEmptyAt(agg_lists,i,&iscoarse));
241     if (!iscoarse) {
242       lcid[i] = cs+cn;
243       cn++;
244     } else {
245       lcid[i] = -1;
246     }
247   }
248 
249   if (gA) {
250     PetscCall(PetscSFBcastBegin(sf,MPIU_INT,lcid,gcid,MPI_REPLACE));
251     PetscCall(PetscSFBcastEnd(sf,MPIU_INT,lcid,gcid,MPI_REPLACE));
252   }
253 
254   /* determine the largest off-diagonal entries in each row */
255   for (i=fs;i<fe;i++) {
256     Amax_pos[i-fs] = 0.;
257     Amax_neg[i-fs] = 0.;
258     PetscCall(MatGetRow(A,i,&ncols,&rcol,&rval));
259     for (j=0;j<ncols;j++) {
260       if ((PetscRealPart(-rval[j]) > Amax_neg[i-fs]) && i != rcol[j]) Amax_neg[i-fs] = PetscAbsScalar(rval[j]);
261       if ((PetscRealPart(rval[j])  > Amax_pos[i-fs]) && i != rcol[j]) Amax_pos[i-fs] = PetscAbsScalar(rval[j]);
262     }
263     if (ncols > cmax) cmax = ncols;
264     PetscCall(MatRestoreRow(A,i,&ncols,&rcol,&rval));
265   }
266   PetscCall(PetscMalloc2(cmax,&pcols,cmax,&pvals));
267   PetscCall(VecDestroy(&C));
268 
269   /* count the on and off processor sparsity patterns for the prolongator */
270   for (i=0;i<fn;i++) {
271     /* on */
272     lsparse[i] = 0;
273     gsparse[i] = 0;
274     if (lcid[i] >= 0) {
275       lsparse[i] = 1;
276       gsparse[i] = 0;
277     } else {
278       PetscCall(MatGetRow(lA,i,&ncols,&rcol,&rval));
279       for (j = 0;j < ncols;j++) {
280         col = rcol[j];
281         if (lcid[col] >= 0 && (PetscRealPart(rval[j]) > gamg->threshold[0]*Amax_pos[i] || PetscRealPart(-rval[j]) > gamg->threshold[0]*Amax_neg[i])) {
282           lsparse[i] += 1;
283         }
284       }
285       PetscCall(MatRestoreRow(lA,i,&ncols,&rcol,&rval));
286       /* off */
287       if (gA) {
288         PetscCall(MatGetRow(gA,i,&ncols,&rcol,&rval));
289         for (j = 0; j < ncols; j++) {
290           col = rcol[j];
291           if (gcid[col] >= 0 && (PetscRealPart(rval[j]) > gamg->threshold[0]*Amax_pos[i] || PetscRealPart(-rval[j]) > gamg->threshold[0]*Amax_neg[i])) {
292             gsparse[i] += 1;
293           }
294         }
295         PetscCall(MatRestoreRow(gA,i,&ncols,&rcol,&rval));
296       }
297     }
298   }
299 
300   /* preallocate and create the prolongator */
301   PetscCall(MatCreate(PetscObjectComm((PetscObject)A),P));
302   PetscCall(MatGetType(G,&mtype));
303   PetscCall(MatSetType(*P,mtype));
304   PetscCall(MatSetSizes(*P,fn,cn,PETSC_DETERMINE,PETSC_DETERMINE));
305   PetscCall(MatMPIAIJSetPreallocation(*P,0,lsparse,0,gsparse));
306   PetscCall(MatSeqAIJSetPreallocation(*P,0,lsparse));
307 
308   /* loop over local fine nodes -- get the diagonal, the sum of positive and negative strong and weak weights, and set up the row */
309   for (i = 0;i < fn;i++) {
310     /* determine on or off */
311     row_f = i + fs;
312     row_c = lcid[i];
313     if (row_c >= 0) {
314       pij = 1.;
315       PetscCall(MatSetValues(*P,1,&row_f,1,&row_c,&pij,INSERT_VALUES));
316     } else {
317       g_pos = 0.;
318       g_neg = 0.;
319       a_pos = 0.;
320       a_neg = 0.;
321       diag  = 0.;
322 
323       /* local connections */
324       PetscCall(MatGetRow(lA,i,&ncols,&rcol,&rval));
325       for (j = 0; j < ncols; j++) {
326         col = rcol[j];
327         if (lcid[col] >= 0 && (PetscRealPart(rval[j]) > gamg->threshold[0]*Amax_pos[i] || PetscRealPart(-rval[j]) > gamg->threshold[0]*Amax_neg[i])) {
328           if (PetscRealPart(rval[j]) > 0.) {
329             g_pos += rval[j];
330           } else {
331             g_neg += rval[j];
332           }
333         }
334         if (col != i) {
335           if (PetscRealPart(rval[j]) > 0.) {
336             a_pos += rval[j];
337           } else {
338             a_neg += rval[j];
339           }
340         } else {
341           diag = rval[j];
342         }
343       }
344       PetscCall(MatRestoreRow(lA,i,&ncols,&rcol,&rval));
345 
346       /* ghosted connections */
347       if (gA) {
348         PetscCall(MatGetRow(gA,i,&ncols,&rcol,&rval));
349         for (j = 0; j < ncols; j++) {
350           col = rcol[j];
351           if (gcid[col] >= 0 && (PetscRealPart(rval[j]) > gamg->threshold[0]*Amax_pos[i] || PetscRealPart(-rval[j]) > gamg->threshold[0]*Amax_neg[i])) {
352             if (PetscRealPart(rval[j]) > 0.) {
353               g_pos += rval[j];
354             } else {
355               g_neg += rval[j];
356             }
357           }
358           if (PetscRealPart(rval[j]) > 0.) {
359             a_pos += rval[j];
360           } else {
361             a_neg += rval[j];
362           }
363         }
364         PetscCall(MatRestoreRow(gA,i,&ncols,&rcol,&rval));
365       }
366 
367       if (g_neg == 0.) {
368         alpha = 0.;
369       } else {
370         alpha = -a_neg/g_neg;
371       }
372 
373       if (g_pos == 0.) {
374         diag += a_pos;
375         beta = 0.;
376       } else {
377         beta = -a_pos/g_pos;
378       }
379       if (diag == 0.) {
380         invdiag = 0.;
381       } else invdiag = 1. / diag;
382       /* on */
383       PetscCall(MatGetRow(lA,i,&ncols,&rcol,&rval));
384       idx = 0;
385       for (j = 0;j < ncols;j++) {
386         col = rcol[j];
387         if (lcid[col] >= 0 && (PetscRealPart(rval[j]) > gamg->threshold[0]*Amax_pos[i] || PetscRealPart(-rval[j]) > gamg->threshold[0]*Amax_neg[i])) {
388           row_f = i + fs;
389           row_c = lcid[col];
390           /* set the values for on-processor ones */
391           if (PetscRealPart(rval[j]) < 0.) {
392             pij = rval[j]*alpha*invdiag;
393           } else {
394             pij = rval[j]*beta*invdiag;
395           }
396           if (PetscAbsScalar(pij) != 0.) {
397             pvals[idx] = pij;
398             pcols[idx] = row_c;
399             idx++;
400           }
401         }
402       }
403       PetscCall(MatRestoreRow(lA,i,&ncols,&rcol,&rval));
404       /* off */
405       if (gA) {
406         PetscCall(MatGetRow(gA,i,&ncols,&rcol,&rval));
407         for (j = 0; j < ncols; j++) {
408           col = rcol[j];
409           if (gcid[col] >= 0 && (PetscRealPart(rval[j]) > gamg->threshold[0]*Amax_pos[i] || PetscRealPart(-rval[j]) > gamg->threshold[0]*Amax_neg[i])) {
410             row_f = i + fs;
411             row_c = gcid[col];
412             /* set the values for on-processor ones */
413             if (PetscRealPart(rval[j]) < 0.) {
414               pij = rval[j]*alpha*invdiag;
415             } else {
416               pij = rval[j]*beta*invdiag;
417             }
418             if (PetscAbsScalar(pij) != 0.) {
419               pvals[idx] = pij;
420               pcols[idx] = row_c;
421               idx++;
422             }
423           }
424         }
425         PetscCall(MatRestoreRow(gA,i,&ncols,&rcol,&rval));
426       }
427       PetscCall(MatSetValues(*P,1,&row_f,idx,pcols,pvals,INSERT_VALUES));
428     }
429   }
430 
431   PetscCall(MatAssemblyBegin(*P, MAT_FINAL_ASSEMBLY));
432   PetscCall(MatAssemblyEnd(*P, MAT_FINAL_ASSEMBLY));
433 
434   PetscCall(PetscFree5(lsparse,gsparse,lcid,Amax_pos,Amax_neg));
435 
436   PetscCall(PetscFree2(pcols,pvals));
437   if (gA) {
438     PetscCall(PetscSFDestroy(&sf));
439     PetscCall(PetscFree(gcid));
440   }
441   PetscFunctionReturn(0);
442 }
443 
444 PetscErrorCode PCGAMGTruncateProlongator_Private(PC pc,Mat *P)
445 {
446   PetscInt          j,i,ps,pf,pn,pcs,pcf,pcn,idx,cmax;
447   const PetscScalar *pval;
448   const PetscInt    *pcol;
449   PetscScalar       *pnval;
450   PetscInt          *pncol;
451   PetscInt          ncols;
452   Mat               Pnew;
453   PetscInt          *lsparse,*gsparse;
454   PetscReal         pmax_pos,pmax_neg,ptot_pos,ptot_neg,pthresh_pos,pthresh_neg;
455   PC_MG             *mg          = (PC_MG*)pc->data;
456   PC_GAMG           *pc_gamg     = (PC_GAMG*)mg->innerctx;
457   PC_GAMG_Classical *cls         = (PC_GAMG_Classical*)pc_gamg->subctx;
458   MatType           mtype;
459 
460   PetscFunctionBegin;
461   /* trim and rescale with reallocation */
462   PetscCall(MatGetOwnershipRange(*P,&ps,&pf));
463   PetscCall(MatGetOwnershipRangeColumn(*P,&pcs,&pcf));
464   pn = pf-ps;
465   pcn = pcf-pcs;
466   PetscCall(PetscMalloc2(pn,&lsparse,pn,&gsparse));
467   /* allocate */
468   cmax = 0;
469   for (i=ps;i<pf;i++) {
470     lsparse[i-ps] = 0;
471     gsparse[i-ps] = 0;
472     PetscCall(MatGetRow(*P,i,&ncols,&pcol,&pval));
473     if (ncols > cmax) {
474       cmax = ncols;
475     }
476     pmax_pos = 0.;
477     pmax_neg = 0.;
478     for (j=0;j<ncols;j++) {
479       if (PetscRealPart(pval[j]) > pmax_pos) {
480         pmax_pos = PetscRealPart(pval[j]);
481       } else if (PetscRealPart(pval[j]) < pmax_neg) {
482         pmax_neg = PetscRealPart(pval[j]);
483       }
484     }
485     for (j=0;j<ncols;j++) {
486       if (PetscRealPart(pval[j]) >= pmax_pos*cls->interp_threshold || PetscRealPart(pval[j]) <= pmax_neg*cls->interp_threshold) {
487         if (pcol[j] >= pcs && pcol[j] < pcf) {
488           lsparse[i-ps]++;
489         } else {
490           gsparse[i-ps]++;
491         }
492       }
493     }
494     PetscCall(MatRestoreRow(*P,i,&ncols,&pcol,&pval));
495   }
496 
497   PetscCall(PetscMalloc2(cmax,&pnval,cmax,&pncol));
498 
499   PetscCall(MatGetType(*P,&mtype));
500   PetscCall(MatCreate(PetscObjectComm((PetscObject)*P),&Pnew));
501   PetscCall(MatSetType(Pnew, mtype));
502   PetscCall(MatSetSizes(Pnew,pn,pcn,PETSC_DETERMINE,PETSC_DETERMINE));
503   PetscCall(MatSeqAIJSetPreallocation(Pnew,0,lsparse));
504   PetscCall(MatMPIAIJSetPreallocation(Pnew,0,lsparse,0,gsparse));
505 
506   for (i=ps;i<pf;i++) {
507     PetscCall(MatGetRow(*P,i,&ncols,&pcol,&pval));
508     pmax_pos = 0.;
509     pmax_neg = 0.;
510     for (j=0;j<ncols;j++) {
511       if (PetscRealPart(pval[j]) > pmax_pos) {
512         pmax_pos = PetscRealPart(pval[j]);
513       } else if (PetscRealPart(pval[j]) < pmax_neg) {
514         pmax_neg = PetscRealPart(pval[j]);
515       }
516     }
517     pthresh_pos = 0.;
518     pthresh_neg = 0.;
519     ptot_pos = 0.;
520     ptot_neg = 0.;
521     for (j=0;j<ncols;j++) {
522       if (PetscRealPart(pval[j]) >= cls->interp_threshold*pmax_pos) {
523         pthresh_pos += PetscRealPart(pval[j]);
524       } else if (PetscRealPart(pval[j]) <= cls->interp_threshold*pmax_neg) {
525         pthresh_neg += PetscRealPart(pval[j]);
526       }
527       if (PetscRealPart(pval[j]) > 0.) {
528         ptot_pos += PetscRealPart(pval[j]);
529       } else {
530         ptot_neg += PetscRealPart(pval[j]);
531       }
532     }
533     if (PetscAbsReal(pthresh_pos) > 0.) ptot_pos /= pthresh_pos;
534     if (PetscAbsReal(pthresh_neg) > 0.) ptot_neg /= pthresh_neg;
535     idx=0;
536     for (j=0;j<ncols;j++) {
537       if (PetscRealPart(pval[j]) >= pmax_pos*cls->interp_threshold) {
538         pnval[idx] = ptot_pos*pval[j];
539         pncol[idx] = pcol[j];
540         idx++;
541       } else if (PetscRealPart(pval[j]) <= pmax_neg*cls->interp_threshold) {
542         pnval[idx] = ptot_neg*pval[j];
543         pncol[idx] = pcol[j];
544         idx++;
545       }
546     }
547     PetscCall(MatRestoreRow(*P,i,&ncols,&pcol,&pval));
548     PetscCall(MatSetValues(Pnew,1,&i,idx,pncol,pnval,INSERT_VALUES));
549   }
550 
551   PetscCall(MatAssemblyBegin(Pnew, MAT_FINAL_ASSEMBLY));
552   PetscCall(MatAssemblyEnd(Pnew, MAT_FINAL_ASSEMBLY));
553   PetscCall(MatDestroy(P));
554 
555   *P = Pnew;
556   PetscCall(PetscFree2(lsparse,gsparse));
557   PetscCall(PetscFree2(pnval,pncol));
558   PetscFunctionReturn(0);
559 }
560 
561 PetscErrorCode PCGAMGProlongator_Classical_Standard(PC pc, Mat A, Mat G, PetscCoarsenData *agg_lists,Mat *P)
562 {
563   Mat               lA,*lAs;
564   MatType           mtype;
565   Vec               cv;
566   PetscInt          *gcid,*lcid,*lsparse,*gsparse,*picol;
567   PetscInt          fs,fe,cs,ce,nl,i,j,k,li,lni,ci,ncols,maxcols,fn,cn,cid;
568   PetscMPIInt       size;
569   const PetscInt    *lidx,*icol,*gidx;
570   PetscBool         iscoarse;
571   PetscScalar       vi,pentry,pjentry;
572   PetscScalar       *pcontrib,*pvcol;
573   const PetscScalar *vcol;
574   PetscReal         diag,jdiag,jwttotal;
575   PetscInt          pncols;
576   PetscSF           sf;
577   PetscLayout       clayout;
578   IS                lis;
579 
580   PetscFunctionBegin;
581   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A),&size));
582   PetscCall(MatGetOwnershipRange(A,&fs,&fe));
583   fn = fe-fs;
584   PetscCall(ISCreateStride(PETSC_COMM_SELF,fe-fs,fs,1,&lis));
585   if (size > 1) {
586     PetscCall(MatGetLayouts(A,NULL,&clayout));
587     /* increase the overlap by two to get neighbors of neighbors */
588     PetscCall(MatIncreaseOverlap(A,1,&lis,2));
589     PetscCall(ISSort(lis));
590     /* get the local part of A */
591     PetscCall(MatCreateSubMatrices(A,1,&lis,&lis,MAT_INITIAL_MATRIX,&lAs));
592     lA = lAs[0];
593     /* build an SF out of it */
594     PetscCall(ISGetLocalSize(lis,&nl));
595     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)A),&sf));
596     PetscCall(ISGetIndices(lis,&lidx));
597     PetscCall(PetscSFSetGraphLayout(sf,clayout,nl,NULL,PETSC_COPY_VALUES,lidx));
598     PetscCall(ISRestoreIndices(lis,&lidx));
599   } else {
600     lA = A;
601     nl = fn;
602   }
603   /* create a communication structure for the overlapped portion and transmit coarse indices */
604   PetscCall(PetscMalloc3(fn,&lsparse,fn,&gsparse,nl,&pcontrib));
605   /* create coarse vector */
606   cn = 0;
607   for (i=0;i<fn;i++) {
608     PetscCall(PetscCDEmptyAt(agg_lists,i,&iscoarse));
609     if (!iscoarse) {
610       cn++;
611     }
612   }
613   PetscCall(PetscMalloc1(fn,&gcid));
614   PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)A),cn,PETSC_DECIDE,&cv));
615   PetscCall(VecGetOwnershipRange(cv,&cs,&ce));
616   cn = 0;
617   for (i=0;i<fn;i++) {
618     PetscCall(PetscCDEmptyAt(agg_lists,i,&iscoarse));
619     if (!iscoarse) {
620       gcid[i] = cs+cn;
621       cn++;
622     } else {
623       gcid[i] = -1;
624     }
625   }
626   if (size > 1) {
627     PetscCall(PetscMalloc1(nl,&lcid));
628     PetscCall(PetscSFBcastBegin(sf,MPIU_INT,gcid,lcid,MPI_REPLACE));
629     PetscCall(PetscSFBcastEnd(sf,MPIU_INT,gcid,lcid,MPI_REPLACE));
630   } else {
631     lcid = gcid;
632   }
633   /* count to preallocate the prolongator */
634   PetscCall(ISGetIndices(lis,&gidx));
635   maxcols = 0;
636   /* count the number of unique contributing coarse cells for each fine */
637   for (i=0;i<nl;i++) {
638     pcontrib[i] = 0.;
639     PetscCall(MatGetRow(lA,i,&ncols,&icol,NULL));
640     if (gidx[i] >= fs && gidx[i] < fe) {
641       li = gidx[i] - fs;
642       lsparse[li] = 0;
643       gsparse[li] = 0;
644       cid = lcid[i];
645       if (cid >= 0) {
646         lsparse[li] = 1;
647       } else {
648         for (j=0;j<ncols;j++) {
649           if (lcid[icol[j]] >= 0) {
650             pcontrib[icol[j]] = 1.;
651           } else {
652             ci = icol[j];
653             PetscCall(MatRestoreRow(lA,i,&ncols,&icol,NULL));
654             PetscCall(MatGetRow(lA,ci,&ncols,&icol,NULL));
655             for (k=0;k<ncols;k++) {
656               if (lcid[icol[k]] >= 0) {
657                 pcontrib[icol[k]] = 1.;
658               }
659             }
660             PetscCall(MatRestoreRow(lA,ci,&ncols,&icol,NULL));
661             PetscCall(MatGetRow(lA,i,&ncols,&icol,NULL));
662           }
663         }
664         for (j=0;j<ncols;j++) {
665           if (lcid[icol[j]] >= 0 && pcontrib[icol[j]] != 0.) {
666             lni = lcid[icol[j]];
667             if (lni >= cs && lni < ce) {
668               lsparse[li]++;
669             } else {
670               gsparse[li]++;
671             }
672             pcontrib[icol[j]] = 0.;
673           } else {
674             ci = icol[j];
675             PetscCall(MatRestoreRow(lA,i,&ncols,&icol,NULL));
676             PetscCall(MatGetRow(lA,ci,&ncols,&icol,NULL));
677             for (k=0;k<ncols;k++) {
678               if (lcid[icol[k]] >= 0 && pcontrib[icol[k]] != 0.) {
679                 lni = lcid[icol[k]];
680                 if (lni >= cs && lni < ce) {
681                   lsparse[li]++;
682                 } else {
683                   gsparse[li]++;
684                 }
685                 pcontrib[icol[k]] = 0.;
686               }
687             }
688             PetscCall(MatRestoreRow(lA,ci,&ncols,&icol,NULL));
689             PetscCall(MatGetRow(lA,i,&ncols,&icol,NULL));
690           }
691         }
692       }
693       if (lsparse[li] + gsparse[li] > maxcols) maxcols = lsparse[li]+gsparse[li];
694     }
695     PetscCall(MatRestoreRow(lA,i,&ncols,&icol,&vcol));
696   }
697   PetscCall(PetscMalloc2(maxcols,&picol,maxcols,&pvcol));
698   PetscCall(MatCreate(PetscObjectComm((PetscObject)A),P));
699   PetscCall(MatGetType(A,&mtype));
700   PetscCall(MatSetType(*P,mtype));
701   PetscCall(MatSetSizes(*P,fn,cn,PETSC_DETERMINE,PETSC_DETERMINE));
702   PetscCall(MatMPIAIJSetPreallocation(*P,0,lsparse,0,gsparse));
703   PetscCall(MatSeqAIJSetPreallocation(*P,0,lsparse));
704   for (i=0;i<nl;i++) {
705     diag = 0.;
706     if (gidx[i] >= fs && gidx[i] < fe) {
707       pncols=0;
708       cid = lcid[i];
709       if (cid >= 0) {
710         pncols = 1;
711         picol[0] = cid;
712         pvcol[0] = 1.;
713       } else {
714         PetscCall(MatGetRow(lA,i,&ncols,&icol,&vcol));
715         for (j=0;j<ncols;j++) {
716           pentry = vcol[j];
717           if (lcid[icol[j]] >= 0) {
718             /* coarse neighbor */
719             pcontrib[icol[j]] += pentry;
720           } else if (icol[j] != i) {
721             /* the neighbor is a strongly connected fine node */
722             ci = icol[j];
723             vi = vcol[j];
724             PetscCall(MatRestoreRow(lA,i,&ncols,&icol,&vcol));
725             PetscCall(MatGetRow(lA,ci,&ncols,&icol,&vcol));
726             jwttotal=0.;
727             jdiag = 0.;
728             for (k=0;k<ncols;k++) {
729               if (ci == icol[k]) {
730                 jdiag = PetscRealPart(vcol[k]);
731               }
732             }
733             for (k=0;k<ncols;k++) {
734               if (lcid[icol[k]] >= 0 && jdiag*PetscRealPart(vcol[k]) < 0.) {
735                 pjentry = vcol[k];
736                 jwttotal += PetscRealPart(pjentry);
737               }
738             }
739             if (jwttotal != 0.) {
740               jwttotal = PetscRealPart(vi)/jwttotal;
741               for (k=0;k<ncols;k++) {
742                 if (lcid[icol[k]] >= 0 && jdiag*PetscRealPart(vcol[k]) < 0.) {
743                   pjentry = vcol[k]*jwttotal;
744                   pcontrib[icol[k]] += pjentry;
745                 }
746               }
747             } else {
748               diag += PetscRealPart(vi);
749             }
750             PetscCall(MatRestoreRow(lA,ci,&ncols,&icol,&vcol));
751             PetscCall(MatGetRow(lA,i,&ncols,&icol,&vcol));
752           } else {
753             diag += PetscRealPart(vcol[j]);
754           }
755         }
756         if (diag != 0.) {
757           diag = 1./diag;
758           for (j=0;j<ncols;j++) {
759             if (lcid[icol[j]] >= 0 && pcontrib[icol[j]] != 0.) {
760               /* the neighbor is a coarse node */
761               if (PetscAbsScalar(pcontrib[icol[j]]) > 0.0) {
762                 lni = lcid[icol[j]];
763                 pvcol[pncols] = -pcontrib[icol[j]]*diag;
764                 picol[pncols] = lni;
765                 pncols++;
766               }
767               pcontrib[icol[j]] = 0.;
768             } else {
769               /* the neighbor is a strongly connected fine node */
770               ci = icol[j];
771               PetscCall(MatRestoreRow(lA,i,&ncols,&icol,&vcol));
772               PetscCall(MatGetRow(lA,ci,&ncols,&icol,&vcol));
773               for (k=0;k<ncols;k++) {
774                 if (lcid[icol[k]] >= 0 && pcontrib[icol[k]] != 0.) {
775                   if (PetscAbsScalar(pcontrib[icol[k]]) > 0.0) {
776                     lni = lcid[icol[k]];
777                     pvcol[pncols] = -pcontrib[icol[k]]*diag;
778                     picol[pncols] = lni;
779                     pncols++;
780                   }
781                   pcontrib[icol[k]] = 0.;
782                 }
783               }
784               PetscCall(MatRestoreRow(lA,ci,&ncols,&icol,&vcol));
785               PetscCall(MatGetRow(lA,i,&ncols,&icol,&vcol));
786             }
787             pcontrib[icol[j]] = 0.;
788           }
789           PetscCall(MatRestoreRow(lA,i,&ncols,&icol,&vcol));
790         }
791       }
792       ci = gidx[i];
793       if (pncols > 0) {
794         PetscCall(MatSetValues(*P,1,&ci,pncols,picol,pvcol,INSERT_VALUES));
795       }
796     }
797   }
798   PetscCall(ISRestoreIndices(lis,&gidx));
799   PetscCall(PetscFree2(picol,pvcol));
800   PetscCall(PetscFree3(lsparse,gsparse,pcontrib));
801   PetscCall(ISDestroy(&lis));
802   PetscCall(PetscFree(gcid));
803   if (size > 1) {
804     PetscCall(PetscFree(lcid));
805     PetscCall(MatDestroyMatrices(1,&lAs));
806     PetscCall(PetscSFDestroy(&sf));
807   }
808   PetscCall(VecDestroy(&cv));
809   PetscCall(MatAssemblyBegin(*P, MAT_FINAL_ASSEMBLY));
810   PetscCall(MatAssemblyEnd(*P, MAT_FINAL_ASSEMBLY));
811   PetscFunctionReturn(0);
812 }
813 
814 PetscErrorCode PCGAMGOptProlongator_Classical_Jacobi(PC pc,Mat A,Mat *P)
815 {
816 
817   PetscInt          f,s,n,cf,cs,i,idx;
818   PetscInt          *coarserows;
819   PetscInt          ncols;
820   const PetscInt    *pcols;
821   const PetscScalar *pvals;
822   Mat               Pnew;
823   Vec               diag;
824   PC_MG             *mg          = (PC_MG*)pc->data;
825   PC_GAMG           *pc_gamg     = (PC_GAMG*)mg->innerctx;
826   PC_GAMG_Classical *cls         = (PC_GAMG_Classical*)pc_gamg->subctx;
827 
828   PetscFunctionBegin;
829   if (cls->nsmooths == 0) {
830     PetscCall(PCGAMGTruncateProlongator_Private(pc,P));
831     PetscFunctionReturn(0);
832   }
833   PetscCall(MatGetOwnershipRange(*P,&s,&f));
834   n = f-s;
835   PetscCall(MatGetOwnershipRangeColumn(*P,&cs,&cf));
836   PetscCall(PetscMalloc1(n,&coarserows));
837   /* identify the rows corresponding to coarse unknowns */
838   idx = 0;
839   for (i=s;i<f;i++) {
840     PetscCall(MatGetRow(*P,i,&ncols,&pcols,&pvals));
841     /* assume, for now, that it's a coarse unknown if it has a single unit entry */
842     if (ncols == 1) {
843       if (pvals[0] == 1.) {
844         coarserows[idx] = i;
845         idx++;
846       }
847     }
848     PetscCall(MatRestoreRow(*P,i,&ncols,&pcols,&pvals));
849   }
850   PetscCall(MatCreateVecs(A,&diag,NULL));
851   PetscCall(MatGetDiagonal(A,diag));
852   PetscCall(VecReciprocal(diag));
853   for (i=0;i<cls->nsmooths;i++) {
854     PetscCall(MatMatMult(A,*P,MAT_INITIAL_MATRIX,PETSC_DEFAULT,&Pnew));
855     PetscCall(MatZeroRows(Pnew,idx,coarserows,0.,NULL,NULL));
856     PetscCall(MatDiagonalScale(Pnew,diag,NULL));
857     PetscCall(MatAYPX(Pnew,-1.0,*P,DIFFERENT_NONZERO_PATTERN));
858     PetscCall(MatDestroy(P));
859     *P  = Pnew;
860     Pnew = NULL;
861   }
862   PetscCall(VecDestroy(&diag));
863   PetscCall(PetscFree(coarserows));
864   PetscCall(PCGAMGTruncateProlongator_Private(pc,P));
865   PetscFunctionReturn(0);
866 }
867 
868 static PetscErrorCode PCGAMGProlongator_Classical(PC pc, Mat A, Mat G, PetscCoarsenData *agg_lists,Mat *P)
869 {
870   PetscErrorCode    (*f)(PC,Mat,Mat,PetscCoarsenData*,Mat*);
871   PC_MG             *mg          = (PC_MG*)pc->data;
872   PC_GAMG           *pc_gamg     = (PC_GAMG*)mg->innerctx;
873   PC_GAMG_Classical *cls         = (PC_GAMG_Classical*)pc_gamg->subctx;
874 
875   PetscFunctionBegin;
876   PetscCall(PetscFunctionListFind(PCGAMGClassicalProlongatorList,cls->prolongtype,&f));
877   PetscCheck(f,PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_WRONGSTATE,"Cannot find PCGAMG Classical prolongator type");
878   PetscCall((*f)(pc,A,G,agg_lists,P));
879   PetscFunctionReturn(0);
880 }
881 
882 static PetscErrorCode PCGAMGDestroy_Classical(PC pc)
883 {
884   PC_MG          *mg          = (PC_MG*)pc->data;
885   PC_GAMG        *pc_gamg     = (PC_GAMG*)mg->innerctx;
886 
887   PetscFunctionBegin;
888   PetscCall(PetscFree(pc_gamg->subctx));
889   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCGAMGClassicalSetType_C",NULL));
890   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCGAMGClassicalGetType_C",NULL));
891   PetscFunctionReturn(0);
892 }
893 
894 PetscErrorCode PCGAMGSetFromOptions_Classical(PetscOptionItems *PetscOptionsObject,PC pc)
895 {
896   PC_MG             *mg          = (PC_MG*)pc->data;
897   PC_GAMG           *pc_gamg     = (PC_GAMG*)mg->innerctx;
898   PC_GAMG_Classical *cls         = (PC_GAMG_Classical*)pc_gamg->subctx;
899   char              tname[256];
900   PetscBool         flg;
901 
902   PetscFunctionBegin;
903   PetscOptionsHeadBegin(PetscOptionsObject,"GAMG-Classical options");
904   PetscCall(PetscOptionsFList("-pc_gamg_classical_type","Type of Classical AMG prolongation","PCGAMGClassicalSetType",PCGAMGClassicalProlongatorList,cls->prolongtype, tname, sizeof(tname), &flg));
905   if (flg) PetscCall(PCGAMGClassicalSetType(pc,tname));
906   PetscCall(PetscOptionsReal("-pc_gamg_classical_interp_threshold","Threshold for classical interpolator entries","",cls->interp_threshold,&cls->interp_threshold,NULL));
907   PetscCall(PetscOptionsInt("-pc_gamg_classical_nsmooths","Threshold for classical interpolator entries","",cls->nsmooths,&cls->nsmooths,NULL));
908   PetscOptionsHeadEnd();
909   PetscFunctionReturn(0);
910 }
911 
912 static PetscErrorCode PCGAMGSetData_Classical(PC pc, Mat A)
913 {
914   PC_MG          *mg      = (PC_MG*)pc->data;
915   PC_GAMG        *pc_gamg = (PC_GAMG*)mg->innerctx;
916 
917   PetscFunctionBegin;
918   /* no data for classical AMG */
919   pc_gamg->data           = NULL;
920   pc_gamg->data_cell_cols = 0;
921   pc_gamg->data_cell_rows = 0;
922   pc_gamg->data_sz        = 0;
923   PetscFunctionReturn(0);
924 }
925 
926 PetscErrorCode PCGAMGClassicalFinalizePackage(void)
927 {
928   PetscFunctionBegin;
929   PCGAMGClassicalPackageInitialized = PETSC_FALSE;
930   PetscCall(PetscFunctionListDestroy(&PCGAMGClassicalProlongatorList));
931   PetscFunctionReturn(0);
932 }
933 
934 PetscErrorCode PCGAMGClassicalInitializePackage(void)
935 {
936   PetscFunctionBegin;
937   if (PCGAMGClassicalPackageInitialized) PetscFunctionReturn(0);
938   PetscCall(PetscFunctionListAdd(&PCGAMGClassicalProlongatorList,PCGAMGCLASSICALDIRECT,PCGAMGProlongator_Classical_Direct));
939   PetscCall(PetscFunctionListAdd(&PCGAMGClassicalProlongatorList,PCGAMGCLASSICALSTANDARD,PCGAMGProlongator_Classical_Standard));
940   PetscCall(PetscRegisterFinalize(PCGAMGClassicalFinalizePackage));
941   PetscFunctionReturn(0);
942 }
943 
944 /* -------------------------------------------------------------------------- */
945 /*
946    PCCreateGAMG_Classical
947 
948 */
949 PetscErrorCode  PCCreateGAMG_Classical(PC pc)
950 {
951   PC_MG             *mg      = (PC_MG*)pc->data;
952   PC_GAMG           *pc_gamg = (PC_GAMG*)mg->innerctx;
953   PC_GAMG_Classical *pc_gamg_classical;
954 
955   PetscFunctionBegin;
956   PetscCall(PCGAMGClassicalInitializePackage());
957   if (pc_gamg->subctx) {
958     /* call base class */
959     PetscCall(PCDestroy_GAMG(pc));
960   }
961 
962   /* create sub context for SA */
963   PetscCall(PetscNewLog(pc,&pc_gamg_classical));
964   pc_gamg->subctx = pc_gamg_classical;
965   pc->ops->setfromoptions = PCGAMGSetFromOptions_Classical;
966   /* reset does not do anything; setup not virtual */
967 
968   /* set internal function pointers */
969   pc_gamg->ops->destroy        = PCGAMGDestroy_Classical;
970   pc_gamg->ops->graph          = PCGAMGGraph_Classical;
971   pc_gamg->ops->coarsen        = PCGAMGCoarsen_Classical;
972   pc_gamg->ops->prolongator    = PCGAMGProlongator_Classical;
973   pc_gamg->ops->optprolongator = PCGAMGOptProlongator_Classical_Jacobi;
974   pc_gamg->ops->setfromoptions = PCGAMGSetFromOptions_Classical;
975 
976   pc_gamg->ops->createdefaultdata = PCGAMGSetData_Classical;
977   pc_gamg_classical->interp_threshold = 0.2;
978   pc_gamg_classical->nsmooths         = 0;
979   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCGAMGClassicalSetType_C",PCGAMGClassicalSetType_GAMG));
980   PetscCall(PetscObjectComposeFunction((PetscObject)pc,"PCGAMGClassicalGetType_C",PCGAMGClassicalGetType_GAMG));
981   PetscCall(PCGAMGClassicalSetType(pc,PCGAMGCLASSICALSTANDARD));
982   PetscFunctionReturn(0);
983 }
984