xref: /petsc/src/ksp/pc/impls/ml/ml.c (revision 523895ee4f068e6401cdb7a71a1bc8643023609c)
1 /*
2    Provides an interface to the ML smoothed Aggregation
3    Note: Something non-obvious breaks -pc_mg_type ADDITIVE for parallel runs
4                                     Jed Brown, see [PETSC #18321, #18449].
5 */
6 #include <petsc/private/pcimpl.h>   /*I "petscpc.h" I*/
7 #include <petsc/private/pcmgimpl.h> /*I "petscksp.h" I*/
8 #include <../src/mat/impls/aij/seq/aij.h>
9 #include <../src/mat/impls/aij/mpi/mpiaij.h>
10 #include <petscdm.h> /* for DMDestroy(&pc->mg) hack */
11 
12 EXTERN_C_BEGIN
13 /* HAVE_CONFIG_H flag is required by ML include files */
14 #ifndef HAVE_CONFIG_H
15   #define HAVE_CONFIG_H
16 #endif
17 #include <ml_include.h>
18 #include <ml_viz_stats.h>
19 EXTERN_C_END
20 
21 typedef enum {
22   PCML_NULLSPACE_AUTO,
23   PCML_NULLSPACE_USER,
24   PCML_NULLSPACE_BLOCK,
25   PCML_NULLSPACE_SCALAR
26 } PCMLNullSpaceType;
27 static const char *const PCMLNullSpaceTypes[] = {"AUTO", "USER", "BLOCK", "SCALAR", "PCMLNullSpaceType", "PCML_NULLSPACE_", 0};
28 
29 /* The context (data structure) at each grid level */
30 typedef struct {
31   Vec x, b, r; /* global vectors */
32   Mat A, P, R;
33   KSP ksp;
34   Vec coords; /* projected by ML, if PCSetCoordinates is called; values packed by node */
35 } GridCtx;
36 
37 /* The context used to input PETSc matrix into ML at fine grid */
38 typedef struct {
39   Mat          A;    /* PETSc matrix in aij format */
40   Mat          Aloc; /* local portion of A to be used by ML */
41   Vec          x, y;
42   ML_Operator *mlmat;
43   PetscScalar *pwork; /* tmp array used by PetscML_comm() */
44 } FineGridCtx;
45 
46 /* The context associates a ML matrix with a PETSc shell matrix */
47 typedef struct {
48   Mat          A;     /* PETSc shell matrix associated with mlmat */
49   ML_Operator *mlmat; /* ML matrix assorciated with A */
50 } Mat_MLShell;
51 
52 /* Private context for the ML preconditioner */
53 typedef struct {
54   ML               *ml_object;
55   ML_Aggregate     *agg_object;
56   GridCtx          *gridctx;
57   FineGridCtx      *PetscMLdata;
58   PetscInt          Nlevels, MaxNlevels, MaxCoarseSize, CoarsenScheme, EnergyMinimization, MinPerProc, PutOnSingleProc, RepartitionType, ZoltanScheme;
59   PetscReal         Threshold, DampingFactor, EnergyMinimizationDropTol, MaxMinRatio, AuxThreshold;
60   PetscBool         SpectralNormScheme_Anorm, BlockScaling, EnergyMinimizationCheap, Symmetrize, OldHierarchy, KeepAggInfo, Reusable, Repartition, Aux;
61   PetscBool         reuse_interpolation;
62   PCMLNullSpaceType nulltype;
63   PetscMPIInt       size; /* size of communicator for pc->pmat */
64   PetscInt          dim;  /* data from PCSetCoordinates(_ML) */
65   PetscInt          nloc;
66   PetscReal        *coords; /* ML has a grid object for each level: the finest grid will point into coords */
67 } PC_ML;
68 
69 static int PetscML_getrow(ML_Operator *ML_data, int N_requested_rows, int requested_rows[], int allocated_space, int columns[], double values[], int row_lengths[])
70 {
71   PetscInt     m, i, j, k = 0, row, *aj;
72   PetscScalar *aa;
73   FineGridCtx *ml = (FineGridCtx *)ML_Get_MyGetrowData(ML_data);
74   Mat_SeqAIJ  *a  = (Mat_SeqAIJ *)ml->Aloc->data;
75 
76   if (MatGetSize(ml->Aloc, &m, NULL)) return 0;
77   for (i = 0; i < N_requested_rows; i++) {
78     row            = requested_rows[i];
79     row_lengths[i] = a->ilen[row];
80     if (allocated_space < k + row_lengths[i]) return 0;
81     if ((row >= 0) || (row <= (m - 1))) {
82       aj = a->j + a->i[row];
83       aa = a->a + a->i[row];
84       for (j = 0; j < row_lengths[i]; j++) {
85         columns[k]  = aj[j];
86         values[k++] = aa[j];
87       }
88     }
89   }
90   return 1;
91 }
92 
93 static PetscErrorCode PetscML_comm(double p[], void *ML_data)
94 {
95   FineGridCtx       *ml = (FineGridCtx *)ML_data;
96   Mat                A  = ml->A;
97   Mat_MPIAIJ        *a  = (Mat_MPIAIJ *)A->data;
98   PetscMPIInt        size;
99   PetscInt           i, in_length = A->rmap->n, out_length = ml->Aloc->cmap->n;
100   const PetscScalar *array;
101 
102   PetscFunctionBegin;
103   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
104   if (size == 1) PetscFunctionReturn(PETSC_SUCCESS);
105 
106   PetscCall(VecPlaceArray(ml->y, p));
107   PetscCall(VecScatterBegin(a->Mvctx, ml->y, a->lvec, INSERT_VALUES, SCATTER_FORWARD));
108   PetscCall(VecScatterEnd(a->Mvctx, ml->y, a->lvec, INSERT_VALUES, SCATTER_FORWARD));
109   PetscCall(VecResetArray(ml->y));
110   PetscCall(VecGetArrayRead(a->lvec, &array));
111   for (i = in_length; i < out_length; i++) p[i] = array[i - in_length];
112   PetscCall(VecRestoreArrayRead(a->lvec, &array));
113   PetscFunctionReturn(PETSC_SUCCESS);
114 }
115 
116 /*
117   Needed since ML expects an int (*)(double *, void *) but PetscErrorCode may be an
118   enum. Instead of modifying PetscML_comm() it is easier to just wrap it
119 */
120 static int ML_PetscML_comm(double p[], void *ML_data)
121 {
122   return (int)PetscML_comm(p, ML_data);
123 }
124 
125 static int PetscML_matvec(ML_Operator *ML_data, int in_length, double p[], int out_length, double ap[])
126 {
127   FineGridCtx *ml = (FineGridCtx *)ML_Get_MyMatvecData(ML_data);
128   Mat          A = ml->A, Aloc = ml->Aloc;
129   PetscMPIInt  size;
130   PetscScalar *pwork = ml->pwork;
131   PetscInt     i;
132 
133   PetscFunctionBegin;
134   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
135   if (size == 1) {
136     PetscCall(VecPlaceArray(ml->x, p));
137   } else {
138     for (i = 0; i < in_length; i++) pwork[i] = p[i];
139     PetscCall(PetscML_comm(pwork, ml));
140     PetscCall(VecPlaceArray(ml->x, pwork));
141   }
142   PetscCall(VecPlaceArray(ml->y, ap));
143   PetscCall(MatMult(Aloc, ml->x, ml->y));
144   PetscCall(VecResetArray(ml->x));
145   PetscCall(VecResetArray(ml->y));
146   PetscFunctionReturn(PETSC_SUCCESS);
147 }
148 
149 static PetscErrorCode MatMult_ML(Mat A, Vec x, Vec y)
150 {
151   Mat_MLShell       *shell;
152   PetscScalar       *yarray;
153   const PetscScalar *xarray;
154   PetscInt           x_length, y_length;
155 
156   PetscFunctionBegin;
157   PetscCall(MatShellGetContext(A, &shell));
158   PetscCall(VecGetArrayRead(x, &xarray));
159   PetscCall(VecGetArray(y, &yarray));
160   x_length = shell->mlmat->invec_leng;
161   y_length = shell->mlmat->outvec_leng;
162   PetscStackCallExternalVoid("ML_Operator_Apply", ML_Operator_Apply(shell->mlmat, x_length, (PetscScalar *)xarray, y_length, yarray));
163   PetscCall(VecRestoreArrayRead(x, &xarray));
164   PetscCall(VecRestoreArray(y, &yarray));
165   PetscFunctionReturn(PETSC_SUCCESS);
166 }
167 
168 /* newtype is ignored since only handles one case */
169 static PetscErrorCode MatConvert_MPIAIJ_ML(Mat A, MatType newtype, MatReuse scall, Mat *Aloc)
170 {
171   Mat_MPIAIJ  *mpimat = (Mat_MPIAIJ *)A->data;
172   Mat_SeqAIJ  *mat, *a = (Mat_SeqAIJ *)mpimat->A->data, *b = (Mat_SeqAIJ *)mpimat->B->data;
173   PetscInt    *ai = a->i, *aj = a->j, *bi = b->i, *bj = b->j;
174   PetscScalar *aa, *ba, *ca;
175   PetscInt     am = A->rmap->n, an = A->cmap->n, i, j, k;
176   PetscInt    *ci, *cj, ncols;
177 
178   PetscFunctionBegin;
179   PetscCheck(am == an, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "A must have a square diagonal portion, am: %d != an: %d", am, an);
180   PetscCall(MatSeqAIJGetArrayRead(mpimat->A, (const PetscScalar **)&aa));
181   PetscCall(MatSeqAIJGetArrayRead(mpimat->B, (const PetscScalar **)&ba));
182   if (scall == MAT_INITIAL_MATRIX) {
183     PetscCall(PetscMalloc1(1 + am, &ci));
184     ci[0] = 0;
185     for (i = 0; i < am; i++) ci[i + 1] = ci[i] + (ai[i + 1] - ai[i]) + (bi[i + 1] - bi[i]);
186     PetscCall(PetscMalloc1(1 + ci[am], &cj));
187     PetscCall(PetscMalloc1(1 + ci[am], &ca));
188 
189     k = 0;
190     for (i = 0; i < am; i++) {
191       /* diagonal portion of A */
192       ncols = ai[i + 1] - ai[i];
193       for (j = 0; j < ncols; j++) {
194         cj[k]   = *aj++;
195         ca[k++] = *aa++;
196       }
197       /* off-diagonal portion of A */
198       ncols = bi[i + 1] - bi[i];
199       for (j = 0; j < ncols; j++) {
200         cj[k] = an + (*bj);
201         bj++;
202         ca[k++] = *ba++;
203       }
204     }
205     PetscCheck(k == ci[am], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "k: %d != ci[am]: %d", k, ci[am]);
206 
207     /* put together the new matrix */
208     an = mpimat->A->cmap->n + mpimat->B->cmap->n;
209     PetscCall(MatCreateSeqAIJWithArrays(PETSC_COMM_SELF, am, an, ci, cj, ca, Aloc));
210 
211     /* MatCreateSeqAIJWithArrays flags matrix so PETSc doesn't free the user's arrays. */
212     /* Since these are PETSc arrays, change flags to free them as necessary. */
213     mat          = (Mat_SeqAIJ *)(*Aloc)->data;
214     mat->free_a  = PETSC_TRUE;
215     mat->free_ij = PETSC_TRUE;
216 
217     mat->nonew = 0;
218   } else if (scall == MAT_REUSE_MATRIX) {
219     mat = (Mat_SeqAIJ *)(*Aloc)->data;
220     ci  = mat->i;
221     cj  = mat->j;
222     ca  = mat->a;
223     for (i = 0; i < am; i++) {
224       /* diagonal portion of A */
225       ncols = ai[i + 1] - ai[i];
226       for (j = 0; j < ncols; j++) *ca++ = *aa++;
227       /* off-diagonal portion of A */
228       ncols = bi[i + 1] - bi[i];
229       for (j = 0; j < ncols; j++) *ca++ = *ba++;
230     }
231   } else SETERRQ(PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONG, "Invalid MatReuse %d", (int)scall);
232   PetscCall(MatSeqAIJRestoreArrayRead(mpimat->A, (const PetscScalar **)&aa));
233   PetscCall(MatSeqAIJRestoreArrayRead(mpimat->B, (const PetscScalar **)&ba));
234   PetscFunctionReturn(PETSC_SUCCESS);
235 }
236 
237 static PetscErrorCode MatDestroy_ML(Mat A)
238 {
239   Mat_MLShell *shell;
240 
241   PetscFunctionBegin;
242   PetscCall(MatShellGetContext(A, &shell));
243   PetscCall(PetscFree(shell));
244   PetscFunctionReturn(PETSC_SUCCESS);
245 }
246 
247 static PetscErrorCode MatWrapML_SeqAIJ(ML_Operator *mlmat, MatReuse reuse, Mat *newmat)
248 {
249   struct ML_CSR_MSRdata *matdata = (struct ML_CSR_MSRdata *)mlmat->data;
250   PetscInt               m = mlmat->outvec_leng, n = mlmat->invec_leng, *nnz = NULL, nz_max;
251   PetscInt              *ml_cols = matdata->columns, *ml_rowptr = matdata->rowptr, *aj, i;
252   PetscScalar           *ml_vals = matdata->values, *aa;
253 
254   PetscFunctionBegin;
255   PetscCheck(mlmat->getrow, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "mlmat->getrow = NULL");
256   if (m != n) { /* ML Pmat and Rmat are in CSR format. Pass array pointers into SeqAIJ matrix */
257     if (reuse) {
258       Mat_SeqAIJ *aij = (Mat_SeqAIJ *)(*newmat)->data;
259       aij->i          = ml_rowptr;
260       aij->j          = ml_cols;
261       aij->a          = ml_vals;
262     } else {
263       /* sort ml_cols and ml_vals */
264       PetscCall(PetscMalloc1(m + 1, &nnz));
265       for (i = 0; i < m; i++) nnz[i] = ml_rowptr[i + 1] - ml_rowptr[i];
266       aj = ml_cols;
267       aa = ml_vals;
268       for (i = 0; i < m; i++) {
269         PetscCall(PetscSortIntWithScalarArray(nnz[i], aj, aa));
270         aj += nnz[i];
271         aa += nnz[i];
272       }
273       PetscCall(MatCreateSeqAIJWithArrays(PETSC_COMM_SELF, m, n, ml_rowptr, ml_cols, ml_vals, newmat));
274       PetscCall(PetscFree(nnz));
275     }
276     PetscCall(MatAssemblyBegin(*newmat, MAT_FINAL_ASSEMBLY));
277     PetscCall(MatAssemblyEnd(*newmat, MAT_FINAL_ASSEMBLY));
278     PetscFunctionReturn(PETSC_SUCCESS);
279   }
280 
281   nz_max = PetscMax(1, mlmat->max_nz_per_row);
282   PetscCall(PetscMalloc2(nz_max, &aa, nz_max, &aj));
283   if (!reuse) {
284     PetscCall(MatCreate(PETSC_COMM_SELF, newmat));
285     PetscCall(MatSetSizes(*newmat, m, n, PETSC_DECIDE, PETSC_DECIDE));
286     PetscCall(MatSetType(*newmat, MATSEQAIJ));
287     /* keep track of block size for A matrices */
288     PetscCall(MatSetBlockSize(*newmat, mlmat->num_PDEs));
289 
290     PetscCall(PetscMalloc1(m, &nnz));
291     for (i = 0; i < m; i++) PetscStackCallExternalVoid("ML_Operator_Getrow", ML_Operator_Getrow(mlmat, 1, &i, nz_max, aj, aa, &nnz[i]));
292     PetscCall(MatSeqAIJSetPreallocation(*newmat, 0, nnz));
293   }
294   for (i = 0; i < m; i++) {
295     PetscInt ncols;
296 
297     PetscStackCallExternalVoid("ML_Operator_Getrow", ML_Operator_Getrow(mlmat, 1, &i, nz_max, aj, aa, &ncols));
298     PetscCall(MatSetValues(*newmat, 1, &i, ncols, aj, aa, INSERT_VALUES));
299   }
300   PetscCall(MatAssemblyBegin(*newmat, MAT_FINAL_ASSEMBLY));
301   PetscCall(MatAssemblyEnd(*newmat, MAT_FINAL_ASSEMBLY));
302 
303   PetscCall(PetscFree2(aa, aj));
304   PetscCall(PetscFree(nnz));
305   PetscFunctionReturn(PETSC_SUCCESS);
306 }
307 
308 static PetscErrorCode MatWrapML_SHELL(ML_Operator *mlmat, MatReuse reuse, Mat *newmat)
309 {
310   PetscInt     m, n;
311   ML_Comm     *MLcomm;
312   Mat_MLShell *shellctx;
313 
314   PetscFunctionBegin;
315   m = mlmat->outvec_leng;
316   n = mlmat->invec_leng;
317 
318   if (reuse) {
319     PetscCall(MatShellGetContext(*newmat, &shellctx));
320     shellctx->mlmat = mlmat;
321     PetscFunctionReturn(PETSC_SUCCESS);
322   }
323 
324   MLcomm = mlmat->comm;
325 
326   PetscCall(PetscNew(&shellctx));
327   PetscCall(MatCreateShell(MLcomm->USR_comm, m, n, PETSC_DETERMINE, PETSC_DETERMINE, shellctx, newmat));
328   PetscCall(MatShellSetOperation(*newmat, MATOP_MULT, (void (*)(void))MatMult_ML));
329   PetscCall(MatShellSetOperation(*newmat, MATOP_DESTROY, (void (*)(void))MatDestroy_ML));
330 
331   shellctx->A     = *newmat;
332   shellctx->mlmat = mlmat;
333   PetscFunctionReturn(PETSC_SUCCESS);
334 }
335 
336 static PetscErrorCode MatWrapML_MPIAIJ(ML_Operator *mlmat, MatReuse reuse, Mat *newmat)
337 {
338   PetscInt    *aj;
339   PetscScalar *aa;
340   PetscInt     i, j, *gordering;
341   PetscInt     m = mlmat->outvec_leng, n, nz_max, row;
342   Mat          A;
343 
344   PetscFunctionBegin;
345   PetscCheck(mlmat->getrow, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "mlmat->getrow = NULL");
346   n = mlmat->invec_leng;
347   PetscCheck(m == n, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "m %d must equal to n %d", m, n);
348 
349   /* create global row numbering for a ML_Operator */
350   PetscStackCallExternalVoid("ML_build_global_numbering", ML_build_global_numbering(mlmat, &gordering, "rows"));
351 
352   nz_max = PetscMax(1, mlmat->max_nz_per_row) + 1;
353   PetscCall(PetscMalloc2(nz_max, &aa, nz_max, &aj));
354   if (reuse) {
355     A = *newmat;
356   } else {
357     PetscInt *nnzA, *nnzB, *nnz;
358     PetscInt  rstart;
359     PetscCall(MatCreate(mlmat->comm->USR_comm, &A));
360     PetscCall(MatSetSizes(A, m, n, PETSC_DECIDE, PETSC_DECIDE));
361     PetscCall(MatSetType(A, MATMPIAIJ));
362     /* keep track of block size for A matrices */
363     PetscCall(MatSetBlockSize(A, mlmat->num_PDEs));
364     PetscCall(PetscMalloc3(m, &nnzA, m, &nnzB, m, &nnz));
365     PetscCallMPI(MPI_Scan(&m, &rstart, 1, MPIU_INT, MPI_SUM, mlmat->comm->USR_comm));
366     rstart -= m;
367 
368     for (i = 0; i < m; i++) {
369       row = gordering[i] - rstart;
370       PetscStackCallExternalVoid("ML_Operator_Getrow", ML_Operator_Getrow(mlmat, 1, &i, nz_max, aj, aa, &nnz[i]));
371       nnzA[row] = 0;
372       for (j = 0; j < nnz[i]; j++) {
373         if (aj[j] < m) nnzA[row]++;
374       }
375       nnzB[row] = nnz[i] - nnzA[row];
376     }
377     PetscCall(MatMPIAIJSetPreallocation(A, 0, nnzA, 0, nnzB));
378     PetscCall(PetscFree3(nnzA, nnzB, nnz));
379   }
380   for (i = 0; i < m; i++) {
381     PetscInt ncols;
382     row = gordering[i];
383 
384     PetscStackCallExternalVoid(",ML_Operator_Getrow", ML_Operator_Getrow(mlmat, 1, &i, nz_max, aj, aa, &ncols));
385     for (j = 0; j < ncols; j++) aj[j] = gordering[aj[j]];
386     PetscCall(MatSetValues(A, 1, &row, ncols, aj, aa, INSERT_VALUES));
387   }
388   PetscStackCallExternalVoid("ML_free", ML_free(gordering));
389   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
390   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
391   *newmat = A;
392 
393   PetscCall(PetscFree2(aa, aj));
394   PetscFunctionReturn(PETSC_SUCCESS);
395 }
396 
397 /*
398    PCSetCoordinates_ML
399 
400    Input Parameter:
401    .  pc - the preconditioner context
402 */
403 static PetscErrorCode PCSetCoordinates_ML(PC pc, PetscInt ndm, PetscInt a_nloc, PetscReal *coords)
404 {
405   PC_MG   *mg    = (PC_MG *)pc->data;
406   PC_ML   *pc_ml = (PC_ML *)mg->innerctx;
407   PetscInt arrsz, oldarrsz, bs, my0, kk, ii, nloc, Iend, aloc;
408   Mat      Amat = pc->pmat;
409 
410   /* this function copied and modified from PCSetCoordinates_GEO -TGI */
411   PetscFunctionBegin;
412   PetscValidHeaderSpecific(Amat, MAT_CLASSID, 1);
413   PetscCall(MatGetBlockSize(Amat, &bs));
414 
415   PetscCall(MatGetOwnershipRange(Amat, &my0, &Iend));
416   aloc = (Iend - my0);
417   nloc = (Iend - my0) / bs;
418 
419   PetscCheck((nloc == a_nloc) || (aloc == a_nloc), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of local blocks %" PetscInt_FMT " must be %" PetscInt_FMT " or %" PetscInt_FMT ".", a_nloc, nloc, aloc);
420 
421   oldarrsz    = pc_ml->dim * pc_ml->nloc;
422   pc_ml->dim  = ndm;
423   pc_ml->nloc = nloc;
424   arrsz       = ndm * nloc;
425 
426   /* create data - syntactic sugar that should be refactored at some point */
427   if (pc_ml->coords == 0 || (oldarrsz != arrsz)) {
428     PetscCall(PetscFree(pc_ml->coords));
429     PetscCall(PetscMalloc1(arrsz, &pc_ml->coords));
430   }
431   for (kk = 0; kk < arrsz; kk++) pc_ml->coords[kk] = -999.;
432   /* copy data in - column-oriented */
433   if (nloc == a_nloc) {
434     for (kk = 0; kk < nloc; kk++) {
435       for (ii = 0; ii < ndm; ii++) pc_ml->coords[ii * nloc + kk] = coords[kk * ndm + ii];
436     }
437   } else { /* assumes the coordinates are blocked */
438     for (kk = 0; kk < nloc; kk++) {
439       for (ii = 0; ii < ndm; ii++) pc_ml->coords[ii * nloc + kk] = coords[bs * kk * ndm + ii];
440     }
441   }
442   PetscFunctionReturn(PETSC_SUCCESS);
443 }
444 
445 extern PetscErrorCode PCReset_MG(PC);
446 static PetscErrorCode PCReset_ML(PC pc)
447 {
448   PC_MG   *mg    = (PC_MG *)pc->data;
449   PC_ML   *pc_ml = (PC_ML *)mg->innerctx;
450   PetscInt level, fine_level = pc_ml->Nlevels - 1, dim = pc_ml->dim;
451 
452   PetscFunctionBegin;
453   if (dim) {
454     for (level = 0; level <= fine_level; level++) PetscCall(VecDestroy(&pc_ml->gridctx[level].coords));
455     if (pc_ml->ml_object && pc_ml->ml_object->Grid) {
456       ML_Aggregate_Viz_Stats *grid_info = (ML_Aggregate_Viz_Stats *)pc_ml->ml_object->Grid[0].Grid;
457       grid_info->x                      = 0; /* do this so ML doesn't try to free coordinates */
458       grid_info->y                      = 0;
459       grid_info->z                      = 0;
460       PetscStackCallExternalVoid("ML_Operator_Getrow", ML_Aggregate_VizAndStats_Clean(pc_ml->ml_object));
461     }
462   }
463   PetscStackCallExternalVoid("ML_Aggregate_Destroy", ML_Aggregate_Destroy(&pc_ml->agg_object));
464   PetscStackCallExternalVoid("ML_Aggregate_Destroy", ML_Destroy(&pc_ml->ml_object));
465 
466   if (pc_ml->PetscMLdata) {
467     PetscCall(PetscFree(pc_ml->PetscMLdata->pwork));
468     PetscCall(MatDestroy(&pc_ml->PetscMLdata->Aloc));
469     PetscCall(VecDestroy(&pc_ml->PetscMLdata->x));
470     PetscCall(VecDestroy(&pc_ml->PetscMLdata->y));
471   }
472   PetscCall(PetscFree(pc_ml->PetscMLdata));
473 
474   if (pc_ml->gridctx) {
475     for (level = 0; level < fine_level; level++) {
476       if (pc_ml->gridctx[level].A) PetscCall(MatDestroy(&pc_ml->gridctx[level].A));
477       if (pc_ml->gridctx[level].P) PetscCall(MatDestroy(&pc_ml->gridctx[level].P));
478       if (pc_ml->gridctx[level].R) PetscCall(MatDestroy(&pc_ml->gridctx[level].R));
479       if (pc_ml->gridctx[level].x) PetscCall(VecDestroy(&pc_ml->gridctx[level].x));
480       if (pc_ml->gridctx[level].b) PetscCall(VecDestroy(&pc_ml->gridctx[level].b));
481       if (pc_ml->gridctx[level + 1].r) PetscCall(VecDestroy(&pc_ml->gridctx[level + 1].r));
482     }
483   }
484   PetscCall(PetscFree(pc_ml->gridctx));
485   PetscCall(PetscFree(pc_ml->coords));
486 
487   pc_ml->dim  = 0;
488   pc_ml->nloc = 0;
489   PetscCall(PCReset_MG(pc));
490   PetscFunctionReturn(PETSC_SUCCESS);
491 }
492 
493 /*
494    PCSetUp_ML - Prepares for the use of the ML preconditioner
495                     by setting data structures and options.
496 
497    Input Parameter:
498 .  pc - the preconditioner context
499 
500    Application Interface Routine: PCSetUp()
501 
502    Note:
503    The interface routine PCSetUp() is not usually called directly by
504    the user, but instead is called by PCApply() if necessary.
505 */
506 static PetscErrorCode PCSetUp_ML(PC pc)
507 {
508   PetscMPIInt      size;
509   FineGridCtx     *PetscMLdata;
510   ML              *ml_object;
511   ML_Aggregate    *agg_object;
512   ML_Operator     *mlmat;
513   PetscInt         nlocal_allcols, Nlevels, mllevel, level, level1, m, fine_level, bs;
514   Mat              A, Aloc;
515   GridCtx         *gridctx;
516   PC_MG           *mg    = (PC_MG *)pc->data;
517   PC_ML           *pc_ml = (PC_ML *)mg->innerctx;
518   PetscBool        isSeq, isMPI;
519   KSP              smoother;
520   PC               subpc;
521   PetscInt         mesh_level, old_mesh_level;
522   MatInfo          info;
523   static PetscBool cite = PETSC_FALSE;
524 
525   PetscFunctionBegin;
526   PetscCall(PetscCitationsRegister("@TechReport{ml_users_guide,\n  author = {M. Sala and J.J. Hu and R.S. Tuminaro},\n  title = {{ML}3.1 {S}moothed {A}ggregation {U}ser's {G}uide},\n  institution =  {Sandia National Laboratories},\n  number = "
527                                    "{SAND2004-4821},\n  year = 2004\n}\n",
528                                    &cite));
529   A = pc->pmat;
530   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
531 
532   if (pc->setupcalled) {
533     if (pc->flag == SAME_NONZERO_PATTERN && pc_ml->reuse_interpolation) {
534       /*
535        Reuse interpolaton instead of recomputing aggregates and updating the whole hierarchy. This is less expensive for
536        multiple solves in which the matrix is not changing too quickly.
537        */
538       ml_object             = pc_ml->ml_object;
539       gridctx               = pc_ml->gridctx;
540       Nlevels               = pc_ml->Nlevels;
541       fine_level            = Nlevels - 1;
542       gridctx[fine_level].A = A;
543 
544       PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATSEQAIJ, &isSeq));
545       PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATMPIAIJ, &isMPI));
546       PetscCheck(isMPI || isSeq, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "Matrix type '%s' cannot be used with ML. ML can only handle AIJ matrices.", ((PetscObject)A)->type_name);
547       if (isMPI) {
548         PetscCall(MatConvert_MPIAIJ_ML(A, NULL, MAT_INITIAL_MATRIX, &Aloc));
549       } else {
550         Aloc = A;
551         PetscCall(PetscObjectReference((PetscObject)Aloc));
552       }
553 
554       PetscCall(MatGetSize(Aloc, &m, &nlocal_allcols));
555       PetscMLdata = pc_ml->PetscMLdata;
556       PetscCall(MatDestroy(&PetscMLdata->Aloc));
557       PetscMLdata->A    = A;
558       PetscMLdata->Aloc = Aloc;
559       PetscStackCallExternalVoid("ML_Aggregate_Destroy", ML_Init_Amatrix(ml_object, 0, m, m, PetscMLdata));
560       PetscStackCallExternalVoid("ML_Set_Amatrix_Matvec", ML_Set_Amatrix_Matvec(ml_object, 0, PetscML_matvec));
561 
562       mesh_level = ml_object->ML_finest_level;
563       while (ml_object->SingleLevel[mesh_level].Rmat->to) {
564         old_mesh_level = mesh_level;
565         mesh_level     = ml_object->SingleLevel[mesh_level].Rmat->to->levelnum;
566 
567         /* clean and regenerate A */
568         mlmat = &ml_object->Amat[mesh_level];
569         PetscStackCallExternalVoid("ML_Operator_Clean", ML_Operator_Clean(mlmat));
570         PetscStackCallExternalVoid("ML_Operator_Init", ML_Operator_Init(mlmat, ml_object->comm));
571         PetscStackCallExternalVoid("ML_Gen_AmatrixRAP", ML_Gen_AmatrixRAP(ml_object, old_mesh_level, mesh_level));
572       }
573 
574       level = fine_level - 1;
575       if (size == 1) { /* convert ML P, R and A into seqaij format */
576         for (mllevel = 1; mllevel < Nlevels; mllevel++) {
577           mlmat = &ml_object->Amat[mllevel];
578           PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_REUSE_MATRIX, &gridctx[level].A));
579           level--;
580         }
581       } else { /* convert ML P and R into shell format, ML A into mpiaij format */
582         for (mllevel = 1; mllevel < Nlevels; mllevel++) {
583           mlmat = &ml_object->Amat[mllevel];
584           PetscCall(MatWrapML_MPIAIJ(mlmat, MAT_REUSE_MATRIX, &gridctx[level].A));
585           level--;
586         }
587       }
588 
589       for (level = 0; level < fine_level; level++) {
590         if (level > 0) PetscCall(PCMGSetResidual(pc, level, PCMGResidualDefault, gridctx[level].A));
591         PetscCall(KSPSetOperators(gridctx[level].ksp, gridctx[level].A, gridctx[level].A));
592       }
593       PetscCall(PCMGSetResidual(pc, fine_level, PCMGResidualDefault, gridctx[fine_level].A));
594       PetscCall(KSPSetOperators(gridctx[fine_level].ksp, gridctx[level].A, gridctx[fine_level].A));
595 
596       PetscCall(PCSetUp_MG(pc));
597       PetscFunctionReturn(PETSC_SUCCESS);
598     } else {
599       /* since ML can change the size of vectors/matrices at any level we must destroy everything */
600       PetscCall(PCReset_ML(pc));
601     }
602   }
603 
604   /* setup special features of PCML */
605   /* convert A to Aloc to be used by ML at fine grid */
606   pc_ml->size = size;
607   PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATSEQAIJ, &isSeq));
608   PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATMPIAIJ, &isMPI));
609   PetscCheck(isMPI || isSeq, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "Matrix type '%s' cannot be used with ML. ML can only handle AIJ matrices.", ((PetscObject)A)->type_name);
610   if (isMPI) {
611     PetscCall(MatConvert_MPIAIJ_ML(A, NULL, MAT_INITIAL_MATRIX, &Aloc));
612   } else {
613     Aloc = A;
614     PetscCall(PetscObjectReference((PetscObject)Aloc));
615   }
616 
617   /* create and initialize struct 'PetscMLdata' */
618   PetscCall(PetscNew(&PetscMLdata));
619   pc_ml->PetscMLdata = PetscMLdata;
620   PetscCall(PetscMalloc1(Aloc->cmap->n + 1, &PetscMLdata->pwork));
621 
622   PetscCall(MatCreateVecs(Aloc, &PetscMLdata->x, &PetscMLdata->y));
623 
624   PetscMLdata->A    = A;
625   PetscMLdata->Aloc = Aloc;
626   if (pc_ml->dim) { /* create vecs around the coordinate data given */
627     PetscInt   i, j, dim = pc_ml->dim;
628     PetscInt   nloc = pc_ml->nloc, nlocghost;
629     PetscReal *ghostedcoords;
630 
631     PetscCall(MatGetBlockSize(A, &bs));
632     nlocghost = Aloc->cmap->n / bs;
633     PetscCall(PetscMalloc1(dim * nlocghost, &ghostedcoords));
634     for (i = 0; i < dim; i++) {
635       /* copy coordinate values into first component of pwork */
636       for (j = 0; j < nloc; j++) PetscMLdata->pwork[bs * j] = pc_ml->coords[nloc * i + j];
637       /* get the ghost values */
638       PetscCall(PetscML_comm(PetscMLdata->pwork, PetscMLdata));
639       /* write into the vector */
640       for (j = 0; j < nlocghost; j++) ghostedcoords[i * nlocghost + j] = PetscMLdata->pwork[bs * j];
641     }
642     /* replace the original coords with the ghosted coords, because these are
643      * what ML needs */
644     PetscCall(PetscFree(pc_ml->coords));
645     pc_ml->coords = ghostedcoords;
646   }
647 
648   /* create ML discretization matrix at fine grid */
649   /* ML requires input of fine-grid matrix. It determines nlevels. */
650   PetscCall(MatGetSize(Aloc, &m, &nlocal_allcols));
651   PetscCall(MatGetBlockSize(A, &bs));
652   PetscStackCallExternalVoid("ML_Create", ML_Create(&ml_object, pc_ml->MaxNlevels));
653   PetscStackCallExternalVoid("ML_Comm_Set_UsrComm", ML_Comm_Set_UsrComm(ml_object->comm, PetscObjectComm((PetscObject)A)));
654   pc_ml->ml_object = ml_object;
655   PetscStackCallExternalVoid("ML_Init_Amatrix", ML_Init_Amatrix(ml_object, 0, m, m, PetscMLdata));
656   PetscStackCallExternalVoid("ML_Set_Amatrix_Getrow", ML_Set_Amatrix_Getrow(ml_object, 0, PetscML_getrow, ML_PetscML_comm, nlocal_allcols));
657   PetscStackCallExternalVoid("ML_Set_Amatrix_Matvec", ML_Set_Amatrix_Matvec(ml_object, 0, PetscML_matvec));
658 
659   PetscStackCallExternalVoid("ML_Set_Symmetrize", ML_Set_Symmetrize(ml_object, pc_ml->Symmetrize ? ML_YES : ML_NO));
660 
661   /* aggregation */
662   PetscStackCallExternalVoid("ML_Aggregate_Create", ML_Aggregate_Create(&agg_object));
663   pc_ml->agg_object = agg_object;
664 
665   {
666     MatNullSpace mnull;
667     PetscCall(MatGetNearNullSpace(A, &mnull));
668     if (pc_ml->nulltype == PCML_NULLSPACE_AUTO) {
669       if (mnull) pc_ml->nulltype = PCML_NULLSPACE_USER;
670       else if (bs > 1) pc_ml->nulltype = PCML_NULLSPACE_BLOCK;
671       else pc_ml->nulltype = PCML_NULLSPACE_SCALAR;
672     }
673     switch (pc_ml->nulltype) {
674     case PCML_NULLSPACE_USER: {
675       PetscScalar       *nullvec;
676       const PetscScalar *v;
677       PetscBool          has_const;
678       PetscInt           i, j, mlocal, nvec, M;
679       const Vec         *vecs;
680 
681       PetscCheck(mnull, PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "Must provide explicit null space using MatSetNearNullSpace() to use user-specified null space");
682       PetscCall(MatGetSize(A, &M, NULL));
683       PetscCall(MatGetLocalSize(Aloc, &mlocal, NULL));
684       PetscCall(MatNullSpaceGetVecs(mnull, &has_const, &nvec, &vecs));
685       PetscCall(PetscMalloc1((nvec + !!has_const) * mlocal, &nullvec));
686       if (has_const)
687         for (i = 0; i < mlocal; i++) nullvec[i] = 1.0 / M;
688       for (i = 0; i < nvec; i++) {
689         PetscCall(VecGetArrayRead(vecs[i], &v));
690         for (j = 0; j < mlocal; j++) nullvec[(i + !!has_const) * mlocal + j] = v[j];
691         PetscCall(VecRestoreArrayRead(vecs[i], &v));
692       }
693       PetscStackCallExternalVoid("ML_Aggregate_Set_NullSpace", ML_Aggregate_Set_NullSpace(agg_object, bs, nvec + !!has_const, nullvec, mlocal));
694       PetscCall(PetscFree(nullvec));
695     } break;
696     case PCML_NULLSPACE_BLOCK:
697       PetscStackCallExternalVoid("ML_Aggregate_Set_NullSpace", ML_Aggregate_Set_NullSpace(agg_object, bs, bs, 0, 0));
698       break;
699     case PCML_NULLSPACE_SCALAR:
700       break;
701     default:
702       SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Unknown null space type");
703     }
704   }
705   PetscStackCallExternalVoid("ML_Aggregate_Set_MaxCoarseSize", ML_Aggregate_Set_MaxCoarseSize(agg_object, pc_ml->MaxCoarseSize));
706   /* set options */
707   switch (pc_ml->CoarsenScheme) {
708   case 1:
709     PetscStackCallExternalVoid("ML_Aggregate_Set_CoarsenScheme_Coupled", ML_Aggregate_Set_CoarsenScheme_Coupled(agg_object));
710     break;
711   case 2:
712     PetscStackCallExternalVoid("ML_Aggregate_Set_CoarsenScheme_MIS", ML_Aggregate_Set_CoarsenScheme_MIS(agg_object));
713     break;
714   case 3:
715     PetscStackCallExternalVoid("ML_Aggregate_Set_CoarsenScheme_METIS", ML_Aggregate_Set_CoarsenScheme_METIS(agg_object));
716     break;
717   }
718   PetscStackCallExternalVoid("ML_Aggregate_Set_Threshold", ML_Aggregate_Set_Threshold(agg_object, pc_ml->Threshold));
719   PetscStackCallExternalVoid("ML_Aggregate_Set_DampingFactor", ML_Aggregate_Set_DampingFactor(agg_object, pc_ml->DampingFactor));
720   if (pc_ml->SpectralNormScheme_Anorm) PetscStackCallExternalVoid("ML_Set_SpectralNormScheme_Anorm", ML_Set_SpectralNormScheme_Anorm(ml_object));
721   agg_object->keep_agg_information      = (int)pc_ml->KeepAggInfo;
722   agg_object->keep_P_tentative          = (int)pc_ml->Reusable;
723   agg_object->block_scaled_SA           = (int)pc_ml->BlockScaling;
724   agg_object->minimizing_energy         = (int)pc_ml->EnergyMinimization;
725   agg_object->minimizing_energy_droptol = (double)pc_ml->EnergyMinimizationDropTol;
726   agg_object->cheap_minimizing_energy   = (int)pc_ml->EnergyMinimizationCheap;
727 
728   if (pc_ml->Aux) {
729     PetscCheck(pc_ml->dim, PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "Auxiliary matrix requires coordinates");
730     ml_object->Amat[0].aux_data->threshold = pc_ml->AuxThreshold;
731     ml_object->Amat[0].aux_data->enable    = 1;
732     ml_object->Amat[0].aux_data->max_level = 10;
733     ml_object->Amat[0].num_PDEs            = bs;
734   }
735 
736   PetscCall(MatGetInfo(A, MAT_LOCAL, &info));
737   ml_object->Amat[0].N_nonzeros = (int)info.nz_used;
738 
739   if (pc_ml->dim) {
740     PetscInt                i, dim = pc_ml->dim;
741     ML_Aggregate_Viz_Stats *grid_info;
742     PetscInt                nlocghost;
743 
744     PetscCall(MatGetBlockSize(A, &bs));
745     nlocghost = Aloc->cmap->n / bs;
746 
747     PetscStackCallExternalVoid("ML_Aggregate_VizAndStats_Setup(", ML_Aggregate_VizAndStats_Setup(ml_object)); /* create ml info for coords */
748     grid_info = (ML_Aggregate_Viz_Stats *)ml_object->Grid[0].Grid;
749     for (i = 0; i < dim; i++) {
750       /* set the finest level coordinates to point to the column-order array
751        * in pc_ml */
752       /* NOTE: must point away before VizAndStats_Clean so ML doesn't free */
753       switch (i) {
754       case 0:
755         grid_info->x = pc_ml->coords + nlocghost * i;
756         break;
757       case 1:
758         grid_info->y = pc_ml->coords + nlocghost * i;
759         break;
760       case 2:
761         grid_info->z = pc_ml->coords + nlocghost * i;
762         break;
763       default:
764         SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_SIZ, "PCML coordinate dimension must be <= 3");
765       }
766     }
767     grid_info->Ndim = dim;
768   }
769 
770   /* repartitioning */
771   if (pc_ml->Repartition) {
772     PetscStackCallExternalVoid("ML_Repartition_Activate", ML_Repartition_Activate(ml_object));
773     PetscStackCallExternalVoid("ML_Repartition_Set_LargestMinMaxRatio", ML_Repartition_Set_LargestMinMaxRatio(ml_object, pc_ml->MaxMinRatio));
774     PetscStackCallExternalVoid("ML_Repartition_Set_MinPerProc", ML_Repartition_Set_MinPerProc(ml_object, pc_ml->MinPerProc));
775     PetscStackCallExternalVoid("ML_Repartition_Set_PutOnSingleProc", ML_Repartition_Set_PutOnSingleProc(ml_object, pc_ml->PutOnSingleProc));
776 #if 0 /* Function not yet defined in ml-6.2 */
777     /* I'm not sure what compatibility issues might crop up if we partitioned
778      * on the finest level, so to be safe repartition starts on the next
779      * finest level (reflection default behavior in
780      * ml_MultiLevelPreconditioner) */
781     PetscStackCallExternalVoid("ML_Repartition_Set_StartLevel",ML_Repartition_Set_StartLevel(ml_object,1));
782 #endif
783 
784     if (!pc_ml->RepartitionType) {
785       PetscInt i;
786 
787       PetscCheck(pc_ml->dim, PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "ML Zoltan repartitioning requires coordinates");
788       PetscStackCallExternalVoid("ML_Repartition_Set_Partitioner", ML_Repartition_Set_Partitioner(ml_object, ML_USEZOLTAN));
789       PetscStackCallExternalVoid("ML_Aggregate_Set_Dimensions", ML_Aggregate_Set_Dimensions(agg_object, pc_ml->dim));
790 
791       for (i = 0; i < ml_object->ML_num_levels; i++) {
792         ML_Aggregate_Viz_Stats *grid_info = (ML_Aggregate_Viz_Stats *)ml_object->Grid[i].Grid;
793         grid_info->zoltan_type            = pc_ml->ZoltanScheme + 1; /* ml numbers options 1, 2, 3 */
794         /* defaults from ml_agg_info.c */
795         grid_info->zoltan_estimated_its = 40; /* only relevant to hypergraph / fast hypergraph */
796         grid_info->zoltan_timers        = 0;
797         grid_info->smoothing_steps      = 4; /* only relevant to hypergraph / fast hypergraph */
798       }
799     } else {
800       PetscStackCallExternalVoid("ML_Repartition_Set_Partitioner", ML_Repartition_Set_Partitioner(ml_object, ML_USEPARMETIS));
801     }
802   }
803 
804   if (pc_ml->OldHierarchy) {
805     PetscStackCallExternalVoid("ML_Gen_MGHierarchy_UsingAggregation", Nlevels = ML_Gen_MGHierarchy_UsingAggregation(ml_object, 0, ML_INCREASING, agg_object));
806   } else {
807     PetscStackCallExternalVoid("ML_Gen_MultiLevelHierarchy_UsingAggregation", Nlevels = ML_Gen_MultiLevelHierarchy_UsingAggregation(ml_object, 0, ML_INCREASING, agg_object));
808   }
809   PetscCheck(Nlevels > 0, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Nlevels %d must > 0", Nlevels);
810   pc_ml->Nlevels = Nlevels;
811   fine_level     = Nlevels - 1;
812 
813   PetscCall(PCMGSetLevels(pc, Nlevels, NULL));
814   /* set default smoothers */
815   for (level = 1; level <= fine_level; level++) {
816     PetscCall(PCMGGetSmoother(pc, level, &smoother));
817     PetscCall(KSPSetType(smoother, KSPRICHARDSON));
818     PetscCall(KSPGetPC(smoother, &subpc));
819     PetscCall(PCSetType(subpc, PCSOR));
820   }
821   PetscObjectOptionsBegin((PetscObject)pc);
822   PetscCall(PCSetFromOptions_MG(pc, PetscOptionsObject)); /* should be called in PCSetFromOptions_ML(), but cannot be called prior to PCMGSetLevels() */
823   PetscOptionsEnd();
824 
825   PetscCall(PetscMalloc1(Nlevels, &gridctx));
826 
827   pc_ml->gridctx = gridctx;
828 
829   /* wrap ML matrices by PETSc shell matrices at coarsened grids.
830      Level 0 is the finest grid for ML, but coarsest for PETSc! */
831   gridctx[fine_level].A = A;
832 
833   level = fine_level - 1;
834   /* TODO: support for GPUs */
835   if (size == 1) { /* convert ML P, R and A into seqaij format */
836     for (mllevel = 1; mllevel < Nlevels; mllevel++) {
837       mlmat = &ml_object->Pmat[mllevel];
838       PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].P));
839       mlmat = &ml_object->Rmat[mllevel - 1];
840       PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].R));
841 
842       mlmat = &ml_object->Amat[mllevel];
843       PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].A));
844       level--;
845     }
846   } else { /* convert ML P and R into shell format, ML A into mpiaij format */
847     for (mllevel = 1; mllevel < Nlevels; mllevel++) {
848       mlmat = &ml_object->Pmat[mllevel];
849       PetscCall(MatWrapML_SHELL(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].P));
850       mlmat = &ml_object->Rmat[mllevel - 1];
851       PetscCall(MatWrapML_SHELL(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].R));
852 
853       mlmat = &ml_object->Amat[mllevel];
854       PetscCall(MatWrapML_MPIAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].A));
855       level--;
856     }
857   }
858 
859   /* create vectors and ksp at all levels */
860   for (level = 0; level < fine_level; level++) {
861     level1 = level + 1;
862 
863     PetscCall(MatCreateVecs(gridctx[level].A, &gridctx[level].x, &gridctx[level].b));
864     PetscCall(MatCreateVecs(gridctx[level1].A, NULL, &gridctx[level1].r));
865     PetscCall(PCMGSetX(pc, level, gridctx[level].x));
866     PetscCall(PCMGSetRhs(pc, level, gridctx[level].b));
867     PetscCall(PCMGSetR(pc, level1, gridctx[level1].r));
868 
869     if (level == 0) {
870       PetscCall(PCMGGetCoarseSolve(pc, &gridctx[level].ksp));
871     } else {
872       PetscCall(PCMGGetSmoother(pc, level, &gridctx[level].ksp));
873     }
874   }
875   PetscCall(PCMGGetSmoother(pc, fine_level, &gridctx[fine_level].ksp));
876 
877   /* create coarse level and the interpolation between the levels */
878   for (level = 0; level < fine_level; level++) {
879     level1 = level + 1;
880 
881     PetscCall(PCMGSetInterpolation(pc, level1, gridctx[level].P));
882     PetscCall(PCMGSetRestriction(pc, level1, gridctx[level].R));
883     if (level > 0) PetscCall(PCMGSetResidual(pc, level, PCMGResidualDefault, gridctx[level].A));
884     PetscCall(KSPSetOperators(gridctx[level].ksp, gridctx[level].A, gridctx[level].A));
885   }
886   PetscCall(PCMGSetResidual(pc, fine_level, PCMGResidualDefault, gridctx[fine_level].A));
887   PetscCall(KSPSetOperators(gridctx[fine_level].ksp, gridctx[level].A, gridctx[fine_level].A));
888 
889   /* put coordinate info in levels */
890   if (pc_ml->dim) {
891     PetscInt   i, j, dim = pc_ml->dim;
892     PetscInt   bs, nloc;
893     PC         subpc;
894     PetscReal *array;
895 
896     level = fine_level;
897     for (mllevel = 0; mllevel < Nlevels; mllevel++) {
898       ML_Aggregate_Viz_Stats *grid_info = (ML_Aggregate_Viz_Stats *)ml_object->Amat[mllevel].to->Grid->Grid;
899       MPI_Comm                comm      = ((PetscObject)gridctx[level].A)->comm;
900 
901       PetscCall(MatGetBlockSize(gridctx[level].A, &bs));
902       PetscCall(MatGetLocalSize(gridctx[level].A, NULL, &nloc));
903       nloc /= bs; /* number of local nodes */
904 
905       PetscCall(VecCreate(comm, &gridctx[level].coords));
906       PetscCall(VecSetSizes(gridctx[level].coords, dim * nloc, PETSC_DECIDE));
907       PetscCall(VecSetType(gridctx[level].coords, VECMPI));
908       PetscCall(VecGetArray(gridctx[level].coords, &array));
909       for (j = 0; j < nloc; j++) {
910         for (i = 0; i < dim; i++) {
911           switch (i) {
912           case 0:
913             array[dim * j + i] = grid_info->x[j];
914             break;
915           case 1:
916             array[dim * j + i] = grid_info->y[j];
917             break;
918           case 2:
919             array[dim * j + i] = grid_info->z[j];
920             break;
921           default:
922             SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_SIZ, "PCML coordinate dimension must be <= 3");
923           }
924         }
925       }
926 
927       /* passing coordinates to smoothers/coarse solver, should they need them */
928       PetscCall(KSPGetPC(gridctx[level].ksp, &subpc));
929       PetscCall(PCSetCoordinates(subpc, dim, nloc, array));
930       PetscCall(VecRestoreArray(gridctx[level].coords, &array));
931       level--;
932     }
933   }
934 
935   /* setupcalled is set to 0 so that MG is setup from scratch */
936   pc->setupcalled = 0;
937   PetscCall(PCSetUp_MG(pc));
938   PetscFunctionReturn(PETSC_SUCCESS);
939 }
940 
941 /*
942    PCDestroy_ML - Destroys the private context for the ML preconditioner
943    that was created with PCCreate_ML().
944 
945    Input Parameter:
946 .  pc - the preconditioner context
947 
948    Application Interface Routine: PCDestroy()
949 */
950 static PetscErrorCode PCDestroy_ML(PC pc)
951 {
952   PC_MG *mg    = (PC_MG *)pc->data;
953   PC_ML *pc_ml = (PC_ML *)mg->innerctx;
954 
955   PetscFunctionBegin;
956   PetscCall(PCReset_ML(pc));
957   PetscCall(PetscFree(pc_ml));
958   PetscCall(PCDestroy_MG(pc));
959   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCSetCoordinates_C", NULL));
960   PetscFunctionReturn(PETSC_SUCCESS);
961 }
962 
963 static PetscErrorCode PCSetFromOptions_ML(PC pc, PetscOptionItems PetscOptionsObject)
964 {
965   PetscInt    indx, PrintLevel, partindx;
966   const char *scheme[] = {"Uncoupled", "Coupled", "MIS", "METIS"};
967   const char *part[]   = {"Zoltan", "ParMETIS"};
968 #if defined(HAVE_ML_ZOLTAN)
969   const char *zscheme[] = {"RCB", "hypergraph", "fast_hypergraph"};
970 #endif
971   PC_MG      *mg    = (PC_MG *)pc->data;
972   PC_ML      *pc_ml = (PC_ML *)mg->innerctx;
973   PetscMPIInt size;
974   MPI_Comm    comm;
975 
976   PetscFunctionBegin;
977   PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
978   PetscCallMPI(MPI_Comm_size(comm, &size));
979   PetscOptionsHeadBegin(PetscOptionsObject, "ML options");
980 
981   PrintLevel = 0;
982   indx       = 0;
983   partindx   = 0;
984 
985   PetscCall(PetscOptionsInt("-pc_ml_PrintLevel", "Print level", "ML_Set_PrintLevel", PrintLevel, &PrintLevel, NULL));
986   PetscStackCallExternalVoid("ML_Set_PrintLevel", ML_Set_PrintLevel(PrintLevel));
987   PetscCall(PetscOptionsInt("-pc_ml_maxNlevels", "Maximum number of levels", "None", pc_ml->MaxNlevels, &pc_ml->MaxNlevels, NULL));
988   PetscCall(PetscOptionsInt("-pc_ml_maxCoarseSize", "Maximum coarsest mesh size", "ML_Aggregate_Set_MaxCoarseSize", pc_ml->MaxCoarseSize, &pc_ml->MaxCoarseSize, NULL));
989   PetscCall(PetscOptionsEList("-pc_ml_CoarsenScheme", "Aggregate Coarsen Scheme", "ML_Aggregate_Set_CoarsenScheme_*", scheme, 4, scheme[0], &indx, NULL));
990 
991   pc_ml->CoarsenScheme = indx;
992 
993   PetscCall(PetscOptionsReal("-pc_ml_DampingFactor", "P damping factor", "ML_Aggregate_Set_DampingFactor", pc_ml->DampingFactor, &pc_ml->DampingFactor, NULL));
994   PetscCall(PetscOptionsReal("-pc_ml_Threshold", "Smoother drop tol", "ML_Aggregate_Set_Threshold", pc_ml->Threshold, &pc_ml->Threshold, NULL));
995   PetscCall(PetscOptionsBool("-pc_ml_SpectralNormScheme_Anorm", "Method used for estimating spectral radius", "ML_Set_SpectralNormScheme_Anorm", pc_ml->SpectralNormScheme_Anorm, &pc_ml->SpectralNormScheme_Anorm, NULL));
996   PetscCall(PetscOptionsBool("-pc_ml_Symmetrize", "Symmetrize aggregation", "ML_Set_Symmetrize", pc_ml->Symmetrize, &pc_ml->Symmetrize, NULL));
997   PetscCall(PetscOptionsBool("-pc_ml_BlockScaling", "Scale all dofs at each node together", "None", pc_ml->BlockScaling, &pc_ml->BlockScaling, NULL));
998   PetscCall(PetscOptionsEnum("-pc_ml_nullspace", "Which type of null space information to use", "None", PCMLNullSpaceTypes, (PetscEnum)pc_ml->nulltype, (PetscEnum *)&pc_ml->nulltype, NULL));
999   PetscCall(PetscOptionsInt("-pc_ml_EnergyMinimization", "Energy minimization norm type (0=no minimization; see ML manual for 1,2,3; -1 and 4 undocumented)", "None", pc_ml->EnergyMinimization, &pc_ml->EnergyMinimization, NULL));
1000   PetscCall(PetscOptionsBool("-pc_ml_reuse_interpolation", "Reuse the interpolation operators when possible (cheaper, weaker when matrix entries change a lot)", "None", pc_ml->reuse_interpolation, &pc_ml->reuse_interpolation, NULL));
1001   /*
1002     The following checks a number of conditions.  If we let this stuff slip by, then ML's error handling will take over.
1003     This is suboptimal because it amounts to calling exit(1) so we check for the most common conditions.
1004 
1005     We also try to set some sane defaults when energy minimization is activated, otherwise it's hard to find a working
1006     combination of options and ML's exit(1) explanations don't help matters.
1007   */
1008   PetscCheck(pc_ml->EnergyMinimization >= -1 && pc_ml->EnergyMinimization <= 4, comm, PETSC_ERR_ARG_OUTOFRANGE, "EnergyMinimization must be in range -1..4");
1009   PetscCheck(pc_ml->EnergyMinimization != 4 || size == 1, comm, PETSC_ERR_SUP, "Energy minimization type 4 does not work in parallel");
1010   if (pc_ml->EnergyMinimization == 4) PetscCall(PetscInfo(pc, "Mandel's energy minimization scheme is experimental and broken in ML-6.2\n"));
1011   if (pc_ml->EnergyMinimization) PetscCall(PetscOptionsReal("-pc_ml_EnergyMinimizationDropTol", "Energy minimization drop tolerance", "None", pc_ml->EnergyMinimizationDropTol, &pc_ml->EnergyMinimizationDropTol, NULL));
1012   if (pc_ml->EnergyMinimization == 2) {
1013     /* According to ml_MultiLevelPreconditioner.cpp, this option is only meaningful for norm type (2) */
1014     PetscCall(PetscOptionsBool("-pc_ml_EnergyMinimizationCheap", "Use cheaper variant of norm type 2", "None", pc_ml->EnergyMinimizationCheap, &pc_ml->EnergyMinimizationCheap, NULL));
1015   }
1016   /* energy minimization sometimes breaks if this is turned off, the more classical stuff should be okay without it */
1017   if (pc_ml->EnergyMinimization) pc_ml->KeepAggInfo = PETSC_TRUE;
1018   PetscCall(PetscOptionsBool("-pc_ml_KeepAggInfo", "Allows the preconditioner to be reused, or auxiliary matrices to be generated", "None", pc_ml->KeepAggInfo, &pc_ml->KeepAggInfo, NULL));
1019   /* Option (-1) doesn't work at all (calls exit(1)) if the tentative restriction operator isn't stored. */
1020   if (pc_ml->EnergyMinimization == -1) pc_ml->Reusable = PETSC_TRUE;
1021   PetscCall(PetscOptionsBool("-pc_ml_Reusable", "Store intermedaiate data structures so that the multilevel hierarchy is reusable", "None", pc_ml->Reusable, &pc_ml->Reusable, NULL));
1022   /*
1023     ML's C API is severely underdocumented and lacks significant functionality.  The C++ API calls
1024     ML_Gen_MultiLevelHierarchy_UsingAggregation() which is a modified copy (!?) of the documented function
1025     ML_Gen_MGHierarchy_UsingAggregation().  This modification, however, does not provide a strict superset of the
1026     functionality in the old function, so some users may still want to use it.  Note that many options are ignored in
1027     this context, but ML doesn't provide a way to find out which ones.
1028    */
1029   PetscCall(PetscOptionsBool("-pc_ml_OldHierarchy", "Use old routine to generate hierarchy", "None", pc_ml->OldHierarchy, &pc_ml->OldHierarchy, NULL));
1030   PetscCall(PetscOptionsBool("-pc_ml_repartition", "Allow ML to repartition levels of the hierarchy", "ML_Repartition_Activate", pc_ml->Repartition, &pc_ml->Repartition, NULL));
1031   if (pc_ml->Repartition) {
1032     PetscCall(PetscOptionsReal("-pc_ml_repartitionMaxMinRatio", "Acceptable ratio of repartitioned sizes", "ML_Repartition_Set_LargestMinMaxRatio", pc_ml->MaxMinRatio, &pc_ml->MaxMinRatio, NULL));
1033     PetscCall(PetscOptionsInt("-pc_ml_repartitionMinPerProc", "Smallest repartitioned size", "ML_Repartition_Set_MinPerProc", pc_ml->MinPerProc, &pc_ml->MinPerProc, NULL));
1034     PetscCall(PetscOptionsInt("-pc_ml_repartitionPutOnSingleProc", "Problem size automatically repartitioned to one processor", "ML_Repartition_Set_PutOnSingleProc", pc_ml->PutOnSingleProc, &pc_ml->PutOnSingleProc, NULL));
1035 #if defined(HAVE_ML_ZOLTAN)
1036     partindx = 0;
1037     PetscCall(PetscOptionsEList("-pc_ml_repartitionType", "Repartitioning library to use", "ML_Repartition_Set_Partitioner", part, 2, part[0], &partindx, NULL));
1038 
1039     pc_ml->RepartitionType = partindx;
1040     if (!partindx) {
1041       PetscInt zindx = 0;
1042 
1043       PetscCall(PetscOptionsEList("-pc_ml_repartitionZoltanScheme", "Repartitioning scheme to use", "None", zscheme, 3, zscheme[0], &zindx, NULL));
1044 
1045       pc_ml->ZoltanScheme = zindx;
1046     }
1047 #else
1048     partindx = 1;
1049     PetscCall(PetscOptionsEList("-pc_ml_repartitionType", "Repartitioning library to use", "ML_Repartition_Set_Partitioner", part, 2, part[1], &partindx, NULL));
1050     pc_ml->RepartitionType = partindx;
1051     PetscCheck(partindx, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP_SYS, "ML not compiled with Zoltan");
1052 #endif
1053     PetscCall(PetscOptionsBool("-pc_ml_Aux", "Aggregate using auxiliary coordinate-based laplacian", "None", pc_ml->Aux, &pc_ml->Aux, NULL));
1054     PetscCall(PetscOptionsReal("-pc_ml_AuxThreshold", "Auxiliary smoother drop tol", "None", pc_ml->AuxThreshold, &pc_ml->AuxThreshold, NULL));
1055   }
1056   PetscOptionsHeadEnd();
1057   PetscFunctionReturn(PETSC_SUCCESS);
1058 }
1059 
1060 /*
1061    PCCreate_ML - Creates a ML preconditioner context, PC_ML,
1062    and sets this as the private data within the generic preconditioning
1063    context, PC, that was created within PCCreate().
1064 
1065    Input Parameter:
1066 .  pc - the preconditioner context
1067 
1068    Application Interface Routine: PCCreate()
1069 */
1070 
1071 /*MC
1072      PCML - Use the SNL ML algebraic multigrid preconditioner.
1073 
1074    Options Database Keys:
1075    Multigrid options(inherited):
1076 +  -pc_mg_cycle_type <v> - v for V cycle, w for W-cycle (`PCMGSetCycleType()`)
1077 .  -pc_mg_distinct_smoothup - Should one configure the up and down smoothers separately (`PCMGSetDistinctSmoothUp()`)
1078 -  -pc_mg_type <multiplicative> - (one of) additive multiplicative full kascade
1079 
1080    ML Options Database Key:
1081 +  -pc_ml_PrintLevel <0>                    - Print level (`ML_Set_PrintLevel()`)
1082 .  -pc_ml_maxNlevels <10>                   - Maximum number of levels (None)
1083 .  -pc_ml_maxCoarseSize <1>                 - Maximum coarsest mesh size (`ML_Aggregate_Set_MaxCoarseSize()`)
1084 .  -pc_ml_CoarsenScheme <Uncoupled>         - (one of) Uncoupled Coupled MIS METIS
1085 .  -pc_ml_DampingFactor <1.33333>           - P damping factor (`ML_Aggregate_Set_DampingFactor()`)
1086 .  -pc_ml_Threshold <0>                     - Smoother drop tol (`ML_Aggregate_Set_Threshold()`)
1087 .  -pc_ml_SpectralNormScheme_Anorm <false>  - Method used for estimating spectral radius (`ML_Set_SpectralNormScheme_Anorm()`)
1088 .  -pc_ml_repartition <false>               - Allow ML to repartition levels of the hierarchy (`ML_Repartition_Activate()`)
1089 .  -pc_ml_repartitionMaxMinRatio <1.3>      - Acceptable ratio of repartitioned sizes (`ML_Repartition_Set_LargestMinMaxRatio()`)
1090 .  -pc_ml_repartitionMinPerProc <512>       - Smallest repartitioned size (`ML_Repartition_Set_MinPerProc()`)
1091 .  -pc_ml_repartitionPutOnSingleProc <5000> - Problem size automatically repartitioned to one processor (`ML_Repartition_Set_PutOnSingleProc()`)
1092 .  -pc_ml_repartitionType <Zoltan>          - Repartitioning library to use (`ML_Repartition_Set_Partitioner()`)
1093 .  -pc_ml_repartitionZoltanScheme <RCB>     - Repartitioning scheme to use (None)
1094 .  -pc_ml_Aux <false>                       - Aggregate using auxiliary coordinate-based Laplacian (None)
1095 -  -pc_ml_AuxThreshold <0.0>                - Auxiliary smoother drop tol (None)
1096 
1097    Level: intermediate
1098 
1099    Developer Note:
1100    The coarser grid matrices and restriction/interpolation
1101    operators are computed by ML, with the matrices converted to PETSc matrices in `MATAIJ` format
1102    and the restriction/interpolation operators wrapped as PETSc shell matrices.
1103 
1104 .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCMGType`, `PCMG`, `PCHYPRE`, `PCGAMG`,
1105           `PCMGSetLevels()`, `PCMGGetLevels()`, `PCMGSetType()`, `MPSetCycles()`, `PCMGSetDistinctSmoothUp()`,
1106           `PCMGGetCoarseSolve()`, `PCMGSetResidual()`, `PCMGSetInterpolation()`,
1107           `PCMGSetRestriction()`, `PCMGGetSmoother()`, `PCMGGetSmootherUp()`, `PCMGGetSmootherDown()`,
1108           `PCMGSetCycleTypeOnLevel()`, `PCMGSetRhs()`, `PCMGSetX()`, `PCMGSetR()`
1109 M*/
1110 
1111 PETSC_EXTERN PetscErrorCode PCCreate_ML(PC pc)
1112 {
1113   PC_ML *pc_ml;
1114   PC_MG *mg;
1115 
1116   PetscFunctionBegin;
1117   /* PCML is an inherited class of PCMG. Initialize pc as PCMG */
1118   PetscCall(PCSetType(pc, PCMG)); /* calls PCCreate_MG() and MGCreate_Private() */
1119   PetscCall(PetscObjectChangeTypeName((PetscObject)pc, PCML));
1120   /* Since PCMG tries to use DM associated with PC must delete it */
1121   PetscCall(DMDestroy(&pc->dm));
1122   PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_EXTERNAL));
1123   mg = (PC_MG *)pc->data;
1124 
1125   /* create a supporting struct and attach it to pc */
1126   PetscCall(PetscNew(&pc_ml));
1127   mg->innerctx = pc_ml;
1128 
1129   pc_ml->ml_object                = 0;
1130   pc_ml->agg_object               = 0;
1131   pc_ml->gridctx                  = 0;
1132   pc_ml->PetscMLdata              = 0;
1133   pc_ml->Nlevels                  = -1;
1134   pc_ml->MaxNlevels               = 10;
1135   pc_ml->MaxCoarseSize            = 1;
1136   pc_ml->CoarsenScheme            = 1;
1137   pc_ml->Threshold                = 0.0;
1138   pc_ml->DampingFactor            = 4.0 / 3.0;
1139   pc_ml->SpectralNormScheme_Anorm = PETSC_FALSE;
1140   pc_ml->size                     = 0;
1141   pc_ml->dim                      = 0;
1142   pc_ml->nloc                     = 0;
1143   pc_ml->coords                   = 0;
1144   pc_ml->Repartition              = PETSC_FALSE;
1145   pc_ml->MaxMinRatio              = 1.3;
1146   pc_ml->MinPerProc               = 512;
1147   pc_ml->PutOnSingleProc          = 5000;
1148   pc_ml->RepartitionType          = 0;
1149   pc_ml->ZoltanScheme             = 0;
1150   pc_ml->Aux                      = PETSC_FALSE;
1151   pc_ml->AuxThreshold             = 0.0;
1152 
1153   /* allow for coordinates to be passed */
1154   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCSetCoordinates_C", PCSetCoordinates_ML));
1155 
1156   /* overwrite the pointers of PCMG by the functions of PCML */
1157   pc->ops->setfromoptions = PCSetFromOptions_ML;
1158   pc->ops->setup          = PCSetUp_ML;
1159   pc->ops->reset          = PCReset_ML;
1160   pc->ops->destroy        = PCDestroy_ML;
1161   PetscFunctionReturn(PETSC_SUCCESS);
1162 }
1163