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