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 extern PetscErrorCode PCSetFromOptions_MG(PC, PetscOptionItems *PetscOptionsObject); 507 extern PetscErrorCode PCReset_MG(PC); 508 509 static PetscErrorCode PCSetUp_ML(PC pc) 510 { 511 PetscMPIInt size; 512 FineGridCtx *PetscMLdata; 513 ML *ml_object; 514 ML_Aggregate *agg_object; 515 ML_Operator *mlmat; 516 PetscInt nlocal_allcols, Nlevels, mllevel, level, level1, m, fine_level, bs; 517 Mat A, Aloc; 518 GridCtx *gridctx; 519 PC_MG *mg = (PC_MG *)pc->data; 520 PC_ML *pc_ml = (PC_ML *)mg->innerctx; 521 PetscBool isSeq, isMPI; 522 KSP smoother; 523 PC subpc; 524 PetscInt mesh_level, old_mesh_level; 525 MatInfo info; 526 static PetscBool cite = PETSC_FALSE; 527 528 PetscFunctionBegin; 529 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 = " 530 "{SAND2004-4821},\n year = 2004\n}\n", 531 &cite)); 532 A = pc->pmat; 533 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size)); 534 535 if (pc->setupcalled) { 536 if (pc->flag == SAME_NONZERO_PATTERN && pc_ml->reuse_interpolation) { 537 /* 538 Reuse interpolaton instead of recomputing aggregates and updating the whole hierarchy. This is less expensive for 539 multiple solves in which the matrix is not changing too quickly. 540 */ 541 ml_object = pc_ml->ml_object; 542 gridctx = pc_ml->gridctx; 543 Nlevels = pc_ml->Nlevels; 544 fine_level = Nlevels - 1; 545 gridctx[fine_level].A = A; 546 547 PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATSEQAIJ, &isSeq)); 548 PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATMPIAIJ, &isMPI)); 549 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); 550 if (isMPI) { 551 PetscCall(MatConvert_MPIAIJ_ML(A, NULL, MAT_INITIAL_MATRIX, &Aloc)); 552 } else { 553 Aloc = A; 554 PetscCall(PetscObjectReference((PetscObject)Aloc)); 555 } 556 557 PetscCall(MatGetSize(Aloc, &m, &nlocal_allcols)); 558 PetscMLdata = pc_ml->PetscMLdata; 559 PetscCall(MatDestroy(&PetscMLdata->Aloc)); 560 PetscMLdata->A = A; 561 PetscMLdata->Aloc = Aloc; 562 PetscStackCallExternalVoid("ML_Aggregate_Destroy", ML_Init_Amatrix(ml_object, 0, m, m, PetscMLdata)); 563 PetscStackCallExternalVoid("ML_Set_Amatrix_Matvec", ML_Set_Amatrix_Matvec(ml_object, 0, PetscML_matvec)); 564 565 mesh_level = ml_object->ML_finest_level; 566 while (ml_object->SingleLevel[mesh_level].Rmat->to) { 567 old_mesh_level = mesh_level; 568 mesh_level = ml_object->SingleLevel[mesh_level].Rmat->to->levelnum; 569 570 /* clean and regenerate A */ 571 mlmat = &ml_object->Amat[mesh_level]; 572 PetscStackCallExternalVoid("ML_Operator_Clean", ML_Operator_Clean(mlmat)); 573 PetscStackCallExternalVoid("ML_Operator_Init", ML_Operator_Init(mlmat, ml_object->comm)); 574 PetscStackCallExternalVoid("ML_Gen_AmatrixRAP", ML_Gen_AmatrixRAP(ml_object, old_mesh_level, mesh_level)); 575 } 576 577 level = fine_level - 1; 578 if (size == 1) { /* convert ML P, R and A into seqaij format */ 579 for (mllevel = 1; mllevel < Nlevels; mllevel++) { 580 mlmat = &ml_object->Amat[mllevel]; 581 PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_REUSE_MATRIX, &gridctx[level].A)); 582 level--; 583 } 584 } else { /* convert ML P and R into shell format, ML A into mpiaij format */ 585 for (mllevel = 1; mllevel < Nlevels; mllevel++) { 586 mlmat = &ml_object->Amat[mllevel]; 587 PetscCall(MatWrapML_MPIAIJ(mlmat, MAT_REUSE_MATRIX, &gridctx[level].A)); 588 level--; 589 } 590 } 591 592 for (level = 0; level < fine_level; level++) { 593 if (level > 0) PetscCall(PCMGSetResidual(pc, level, PCMGResidualDefault, gridctx[level].A)); 594 PetscCall(KSPSetOperators(gridctx[level].ksp, gridctx[level].A, gridctx[level].A)); 595 } 596 PetscCall(PCMGSetResidual(pc, fine_level, PCMGResidualDefault, gridctx[fine_level].A)); 597 PetscCall(KSPSetOperators(gridctx[fine_level].ksp, gridctx[level].A, gridctx[fine_level].A)); 598 599 PetscCall(PCSetUp_MG(pc)); 600 PetscFunctionReturn(PETSC_SUCCESS); 601 } else { 602 /* since ML can change the size of vectors/matrices at any level we must destroy everything */ 603 PetscCall(PCReset_ML(pc)); 604 } 605 } 606 607 /* setup special features of PCML */ 608 /* convert A to Aloc to be used by ML at fine grid */ 609 pc_ml->size = size; 610 PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATSEQAIJ, &isSeq)); 611 PetscCall(PetscObjectBaseTypeCompare((PetscObject)A, MATMPIAIJ, &isMPI)); 612 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); 613 if (isMPI) { 614 PetscCall(MatConvert_MPIAIJ_ML(A, NULL, MAT_INITIAL_MATRIX, &Aloc)); 615 } else { 616 Aloc = A; 617 PetscCall(PetscObjectReference((PetscObject)Aloc)); 618 } 619 620 /* create and initialize struct 'PetscMLdata' */ 621 PetscCall(PetscNew(&PetscMLdata)); 622 pc_ml->PetscMLdata = PetscMLdata; 623 PetscCall(PetscMalloc1(Aloc->cmap->n + 1, &PetscMLdata->pwork)); 624 625 PetscCall(MatCreateVecs(Aloc, &PetscMLdata->x, &PetscMLdata->y)); 626 627 PetscMLdata->A = A; 628 PetscMLdata->Aloc = Aloc; 629 if (pc_ml->dim) { /* create vecs around the coordinate data given */ 630 PetscInt i, j, dim = pc_ml->dim; 631 PetscInt nloc = pc_ml->nloc, nlocghost; 632 PetscReal *ghostedcoords; 633 634 PetscCall(MatGetBlockSize(A, &bs)); 635 nlocghost = Aloc->cmap->n / bs; 636 PetscCall(PetscMalloc1(dim * nlocghost, &ghostedcoords)); 637 for (i = 0; i < dim; i++) { 638 /* copy coordinate values into first component of pwork */ 639 for (j = 0; j < nloc; j++) PetscMLdata->pwork[bs * j] = pc_ml->coords[nloc * i + j]; 640 /* get the ghost values */ 641 PetscCall(PetscML_comm(PetscMLdata->pwork, PetscMLdata)); 642 /* write into the vector */ 643 for (j = 0; j < nlocghost; j++) ghostedcoords[i * nlocghost + j] = PetscMLdata->pwork[bs * j]; 644 } 645 /* replace the original coords with the ghosted coords, because these are 646 * what ML needs */ 647 PetscCall(PetscFree(pc_ml->coords)); 648 pc_ml->coords = ghostedcoords; 649 } 650 651 /* create ML discretization matrix at fine grid */ 652 /* ML requires input of fine-grid matrix. It determines nlevels. */ 653 PetscCall(MatGetSize(Aloc, &m, &nlocal_allcols)); 654 PetscCall(MatGetBlockSize(A, &bs)); 655 PetscStackCallExternalVoid("ML_Create", ML_Create(&ml_object, pc_ml->MaxNlevels)); 656 PetscStackCallExternalVoid("ML_Comm_Set_UsrComm", ML_Comm_Set_UsrComm(ml_object->comm, PetscObjectComm((PetscObject)A))); 657 pc_ml->ml_object = ml_object; 658 PetscStackCallExternalVoid("ML_Init_Amatrix", ML_Init_Amatrix(ml_object, 0, m, m, PetscMLdata)); 659 PetscStackCallExternalVoid("ML_Set_Amatrix_Getrow", ML_Set_Amatrix_Getrow(ml_object, 0, PetscML_getrow, ML_PetscML_comm, nlocal_allcols)); 660 PetscStackCallExternalVoid("ML_Set_Amatrix_Matvec", ML_Set_Amatrix_Matvec(ml_object, 0, PetscML_matvec)); 661 662 PetscStackCallExternalVoid("ML_Set_Symmetrize", ML_Set_Symmetrize(ml_object, pc_ml->Symmetrize ? ML_YES : ML_NO)); 663 664 /* aggregation */ 665 PetscStackCallExternalVoid("ML_Aggregate_Create", ML_Aggregate_Create(&agg_object)); 666 pc_ml->agg_object = agg_object; 667 668 { 669 MatNullSpace mnull; 670 PetscCall(MatGetNearNullSpace(A, &mnull)); 671 if (pc_ml->nulltype == PCML_NULLSPACE_AUTO) { 672 if (mnull) pc_ml->nulltype = PCML_NULLSPACE_USER; 673 else if (bs > 1) pc_ml->nulltype = PCML_NULLSPACE_BLOCK; 674 else pc_ml->nulltype = PCML_NULLSPACE_SCALAR; 675 } 676 switch (pc_ml->nulltype) { 677 case PCML_NULLSPACE_USER: { 678 PetscScalar *nullvec; 679 const PetscScalar *v; 680 PetscBool has_const; 681 PetscInt i, j, mlocal, nvec, M; 682 const Vec *vecs; 683 684 PetscCheck(mnull, PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "Must provide explicit null space using MatSetNearNullSpace() to use user-specified null space"); 685 PetscCall(MatGetSize(A, &M, NULL)); 686 PetscCall(MatGetLocalSize(Aloc, &mlocal, NULL)); 687 PetscCall(MatNullSpaceGetVecs(mnull, &has_const, &nvec, &vecs)); 688 PetscCall(PetscMalloc1((nvec + !!has_const) * mlocal, &nullvec)); 689 if (has_const) 690 for (i = 0; i < mlocal; i++) nullvec[i] = 1.0 / M; 691 for (i = 0; i < nvec; i++) { 692 PetscCall(VecGetArrayRead(vecs[i], &v)); 693 for (j = 0; j < mlocal; j++) nullvec[(i + !!has_const) * mlocal + j] = v[j]; 694 PetscCall(VecRestoreArrayRead(vecs[i], &v)); 695 } 696 PetscStackCallExternalVoid("ML_Aggregate_Create", ML_Aggregate_Set_NullSpace(agg_object, bs, nvec + !!has_const, nullvec, mlocal)); 697 PetscCall(PetscFree(nullvec)); 698 } break; 699 case PCML_NULLSPACE_BLOCK: 700 PetscStackCallExternalVoid("ML_Aggregate_Set_NullSpace", ML_Aggregate_Set_NullSpace(agg_object, bs, bs, 0, 0)); 701 break; 702 case PCML_NULLSPACE_SCALAR: 703 break; 704 default: 705 SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Unknown null space type"); 706 } 707 } 708 PetscStackCallExternalVoid("ML_Aggregate_Set_MaxCoarseSize", ML_Aggregate_Set_MaxCoarseSize(agg_object, pc_ml->MaxCoarseSize)); 709 /* set options */ 710 switch (pc_ml->CoarsenScheme) { 711 case 1: 712 PetscStackCallExternalVoid("ML_Aggregate_Set_CoarsenScheme_Coupled", ML_Aggregate_Set_CoarsenScheme_Coupled(agg_object)); 713 break; 714 case 2: 715 PetscStackCallExternalVoid("ML_Aggregate_Set_CoarsenScheme_MIS", ML_Aggregate_Set_CoarsenScheme_MIS(agg_object)); 716 break; 717 case 3: 718 PetscStackCallExternalVoid("ML_Aggregate_Set_CoarsenScheme_METIS", ML_Aggregate_Set_CoarsenScheme_METIS(agg_object)); 719 break; 720 } 721 PetscStackCallExternalVoid("ML_Aggregate_Set_Threshold", ML_Aggregate_Set_Threshold(agg_object, pc_ml->Threshold)); 722 PetscStackCallExternalVoid("ML_Aggregate_Set_DampingFactor", ML_Aggregate_Set_DampingFactor(agg_object, pc_ml->DampingFactor)); 723 if (pc_ml->SpectralNormScheme_Anorm) PetscStackCallExternalVoid("ML_Set_SpectralNormScheme_Anorm", ML_Set_SpectralNormScheme_Anorm(ml_object)); 724 agg_object->keep_agg_information = (int)pc_ml->KeepAggInfo; 725 agg_object->keep_P_tentative = (int)pc_ml->Reusable; 726 agg_object->block_scaled_SA = (int)pc_ml->BlockScaling; 727 agg_object->minimizing_energy = (int)pc_ml->EnergyMinimization; 728 agg_object->minimizing_energy_droptol = (double)pc_ml->EnergyMinimizationDropTol; 729 agg_object->cheap_minimizing_energy = (int)pc_ml->EnergyMinimizationCheap; 730 731 if (pc_ml->Aux) { 732 PetscCheck(pc_ml->dim, PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "Auxiliary matrix requires coordinates"); 733 ml_object->Amat[0].aux_data->threshold = pc_ml->AuxThreshold; 734 ml_object->Amat[0].aux_data->enable = 1; 735 ml_object->Amat[0].aux_data->max_level = 10; 736 ml_object->Amat[0].num_PDEs = bs; 737 } 738 739 PetscCall(MatGetInfo(A, MAT_LOCAL, &info)); 740 ml_object->Amat[0].N_nonzeros = (int)info.nz_used; 741 742 if (pc_ml->dim) { 743 PetscInt i, dim = pc_ml->dim; 744 ML_Aggregate_Viz_Stats *grid_info; 745 PetscInt nlocghost; 746 747 PetscCall(MatGetBlockSize(A, &bs)); 748 nlocghost = Aloc->cmap->n / bs; 749 750 PetscStackCallExternalVoid("ML_Aggregate_VizAndStats_Setup(", ML_Aggregate_VizAndStats_Setup(ml_object)); /* create ml info for coords */ 751 grid_info = (ML_Aggregate_Viz_Stats *)ml_object->Grid[0].Grid; 752 for (i = 0; i < dim; i++) { 753 /* set the finest level coordinates to point to the column-order array 754 * in pc_ml */ 755 /* NOTE: must point away before VizAndStats_Clean so ML doesn't free */ 756 switch (i) { 757 case 0: 758 grid_info->x = pc_ml->coords + nlocghost * i; 759 break; 760 case 1: 761 grid_info->y = pc_ml->coords + nlocghost * i; 762 break; 763 case 2: 764 grid_info->z = pc_ml->coords + nlocghost * i; 765 break; 766 default: 767 SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_SIZ, "PCML coordinate dimension must be <= 3"); 768 } 769 } 770 grid_info->Ndim = dim; 771 } 772 773 /* repartitioning */ 774 if (pc_ml->Repartition) { 775 PetscStackCallExternalVoid("ML_Repartition_Activate", ML_Repartition_Activate(ml_object)); 776 PetscStackCallExternalVoid("ML_Repartition_Set_LargestMinMaxRatio", ML_Repartition_Set_LargestMinMaxRatio(ml_object, pc_ml->MaxMinRatio)); 777 PetscStackCallExternalVoid("ML_Repartition_Set_MinPerProc", ML_Repartition_Set_MinPerProc(ml_object, pc_ml->MinPerProc)); 778 PetscStackCallExternalVoid("ML_Repartition_Set_PutOnSingleProc", ML_Repartition_Set_PutOnSingleProc(ml_object, pc_ml->PutOnSingleProc)); 779 #if 0 /* Function not yet defined in ml-6.2 */ 780 /* I'm not sure what compatibility issues might crop up if we partitioned 781 * on the finest level, so to be safe repartition starts on the next 782 * finest level (reflection default behavior in 783 * ml_MultiLevelPreconditioner) */ 784 PetscStackCallExternalVoid("ML_Repartition_Set_StartLevel",ML_Repartition_Set_StartLevel(ml_object,1)); 785 #endif 786 787 if (!pc_ml->RepartitionType) { 788 PetscInt i; 789 790 PetscCheck(pc_ml->dim, PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "ML Zoltan repartitioning requires coordinates"); 791 PetscStackCallExternalVoid("ML_Repartition_Set_Partitioner", ML_Repartition_Set_Partitioner(ml_object, ML_USEZOLTAN)); 792 PetscStackCallExternalVoid("ML_Aggregate_Set_Dimensions", ML_Aggregate_Set_Dimensions(agg_object, pc_ml->dim)); 793 794 for (i = 0; i < ml_object->ML_num_levels; i++) { 795 ML_Aggregate_Viz_Stats *grid_info = (ML_Aggregate_Viz_Stats *)ml_object->Grid[i].Grid; 796 grid_info->zoltan_type = pc_ml->ZoltanScheme + 1; /* ml numbers options 1, 2, 3 */ 797 /* defaults from ml_agg_info.c */ 798 grid_info->zoltan_estimated_its = 40; /* only relevant to hypergraph / fast hypergraph */ 799 grid_info->zoltan_timers = 0; 800 grid_info->smoothing_steps = 4; /* only relevant to hypergraph / fast hypergraph */ 801 } 802 } else { 803 PetscStackCallExternalVoid("ML_Repartition_Set_Partitioner", ML_Repartition_Set_Partitioner(ml_object, ML_USEPARMETIS)); 804 } 805 } 806 807 if (pc_ml->OldHierarchy) { 808 PetscStackCallExternalVoid("ML_Gen_MGHierarchy_UsingAggregation", Nlevels = ML_Gen_MGHierarchy_UsingAggregation(ml_object, 0, ML_INCREASING, agg_object)); 809 } else { 810 PetscStackCallExternalVoid("ML_Gen_MultiLevelHierarchy_UsingAggregation", Nlevels = ML_Gen_MultiLevelHierarchy_UsingAggregation(ml_object, 0, ML_INCREASING, agg_object)); 811 } 812 PetscCheck(Nlevels > 0, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Nlevels %d must > 0", Nlevels); 813 pc_ml->Nlevels = Nlevels; 814 fine_level = Nlevels - 1; 815 816 PetscCall(PCMGSetLevels(pc, Nlevels, NULL)); 817 /* set default smoothers */ 818 for (level = 1; level <= fine_level; level++) { 819 PetscCall(PCMGGetSmoother(pc, level, &smoother)); 820 PetscCall(KSPSetType(smoother, KSPRICHARDSON)); 821 PetscCall(KSPGetPC(smoother, &subpc)); 822 PetscCall(PCSetType(subpc, PCSOR)); 823 } 824 PetscObjectOptionsBegin((PetscObject)pc); 825 PetscCall(PCSetFromOptions_MG(pc, PetscOptionsObject)); /* should be called in PCSetFromOptions_ML(), but cannot be called prior to PCMGSetLevels() */ 826 PetscOptionsEnd(); 827 828 PetscCall(PetscMalloc1(Nlevels, &gridctx)); 829 830 pc_ml->gridctx = gridctx; 831 832 /* wrap ML matrices by PETSc shell matrices at coarsened grids. 833 Level 0 is the finest grid for ML, but coarsest for PETSc! */ 834 gridctx[fine_level].A = A; 835 836 level = fine_level - 1; 837 /* TODO: support for GPUs */ 838 if (size == 1) { /* convert ML P, R and A into seqaij format */ 839 for (mllevel = 1; mllevel < Nlevels; mllevel++) { 840 mlmat = &ml_object->Pmat[mllevel]; 841 PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].P)); 842 mlmat = &ml_object->Rmat[mllevel - 1]; 843 PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].R)); 844 845 mlmat = &ml_object->Amat[mllevel]; 846 PetscCall(MatWrapML_SeqAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].A)); 847 level--; 848 } 849 } else { /* convert ML P and R into shell format, ML A into mpiaij format */ 850 for (mllevel = 1; mllevel < Nlevels; mllevel++) { 851 mlmat = &ml_object->Pmat[mllevel]; 852 PetscCall(MatWrapML_SHELL(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].P)); 853 mlmat = &ml_object->Rmat[mllevel - 1]; 854 PetscCall(MatWrapML_SHELL(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].R)); 855 856 mlmat = &ml_object->Amat[mllevel]; 857 PetscCall(MatWrapML_MPIAIJ(mlmat, MAT_INITIAL_MATRIX, &gridctx[level].A)); 858 level--; 859 } 860 } 861 862 /* create vectors and ksp at all levels */ 863 for (level = 0; level < fine_level; level++) { 864 level1 = level + 1; 865 866 PetscCall(MatCreateVecs(gridctx[level].A, &gridctx[level].x, &gridctx[level].b)); 867 PetscCall(MatCreateVecs(gridctx[level1].A, NULL, &gridctx[level1].r)); 868 PetscCall(PCMGSetX(pc, level, gridctx[level].x)); 869 PetscCall(PCMGSetRhs(pc, level, gridctx[level].b)); 870 PetscCall(PCMGSetR(pc, level1, gridctx[level1].r)); 871 872 if (level == 0) { 873 PetscCall(PCMGGetCoarseSolve(pc, &gridctx[level].ksp)); 874 } else { 875 PetscCall(PCMGGetSmoother(pc, level, &gridctx[level].ksp)); 876 } 877 } 878 PetscCall(PCMGGetSmoother(pc, fine_level, &gridctx[fine_level].ksp)); 879 880 /* create coarse level and the interpolation between the levels */ 881 for (level = 0; level < fine_level; level++) { 882 level1 = level + 1; 883 884 PetscCall(PCMGSetInterpolation(pc, level1, gridctx[level].P)); 885 PetscCall(PCMGSetRestriction(pc, level1, gridctx[level].R)); 886 if (level > 0) PetscCall(PCMGSetResidual(pc, level, PCMGResidualDefault, gridctx[level].A)); 887 PetscCall(KSPSetOperators(gridctx[level].ksp, gridctx[level].A, gridctx[level].A)); 888 } 889 PetscCall(PCMGSetResidual(pc, fine_level, PCMGResidualDefault, gridctx[fine_level].A)); 890 PetscCall(KSPSetOperators(gridctx[fine_level].ksp, gridctx[level].A, gridctx[fine_level].A)); 891 892 /* put coordinate info in levels */ 893 if (pc_ml->dim) { 894 PetscInt i, j, dim = pc_ml->dim; 895 PetscInt bs, nloc; 896 PC subpc; 897 PetscReal *array; 898 899 level = fine_level; 900 for (mllevel = 0; mllevel < Nlevels; mllevel++) { 901 ML_Aggregate_Viz_Stats *grid_info = (ML_Aggregate_Viz_Stats *)ml_object->Amat[mllevel].to->Grid->Grid; 902 MPI_Comm comm = ((PetscObject)gridctx[level].A)->comm; 903 904 PetscCall(MatGetBlockSize(gridctx[level].A, &bs)); 905 PetscCall(MatGetLocalSize(gridctx[level].A, NULL, &nloc)); 906 nloc /= bs; /* number of local nodes */ 907 908 PetscCall(VecCreate(comm, &gridctx[level].coords)); 909 PetscCall(VecSetSizes(gridctx[level].coords, dim * nloc, PETSC_DECIDE)); 910 PetscCall(VecSetType(gridctx[level].coords, VECMPI)); 911 PetscCall(VecGetArray(gridctx[level].coords, &array)); 912 for (j = 0; j < nloc; j++) { 913 for (i = 0; i < dim; i++) { 914 switch (i) { 915 case 0: 916 array[dim * j + i] = grid_info->x[j]; 917 break; 918 case 1: 919 array[dim * j + i] = grid_info->y[j]; 920 break; 921 case 2: 922 array[dim * j + i] = grid_info->z[j]; 923 break; 924 default: 925 SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_SIZ, "PCML coordinate dimension must be <= 3"); 926 } 927 } 928 } 929 930 /* passing coordinates to smoothers/coarse solver, should they need them */ 931 PetscCall(KSPGetPC(gridctx[level].ksp, &subpc)); 932 PetscCall(PCSetCoordinates(subpc, dim, nloc, array)); 933 PetscCall(VecRestoreArray(gridctx[level].coords, &array)); 934 level--; 935 } 936 } 937 938 /* setupcalled is set to 0 so that MG is setup from scratch */ 939 pc->setupcalled = 0; 940 PetscCall(PCSetUp_MG(pc)); 941 PetscFunctionReturn(PETSC_SUCCESS); 942 } 943 944 /* 945 PCDestroy_ML - Destroys the private context for the ML preconditioner 946 that was created with PCCreate_ML(). 947 948 Input Parameter: 949 . pc - the preconditioner context 950 951 Application Interface Routine: PCDestroy() 952 */ 953 static PetscErrorCode PCDestroy_ML(PC pc) 954 { 955 PC_MG *mg = (PC_MG *)pc->data; 956 PC_ML *pc_ml = (PC_ML *)mg->innerctx; 957 958 PetscFunctionBegin; 959 PetscCall(PCReset_ML(pc)); 960 PetscCall(PetscFree(pc_ml)); 961 PetscCall(PCDestroy_MG(pc)); 962 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCSetCoordinates_C", NULL)); 963 PetscFunctionReturn(PETSC_SUCCESS); 964 } 965 966 static PetscErrorCode PCSetFromOptions_ML(PC pc, PetscOptionItems *PetscOptionsObject) 967 { 968 PetscInt indx, PrintLevel, partindx; 969 const char *scheme[] = {"Uncoupled", "Coupled", "MIS", "METIS"}; 970 const char *part[] = {"Zoltan", "ParMETIS"}; 971 #if defined(HAVE_ML_ZOLTAN) 972 const char *zscheme[] = {"RCB", "hypergraph", "fast_hypergraph"}; 973 #endif 974 PC_MG *mg = (PC_MG *)pc->data; 975 PC_ML *pc_ml = (PC_ML *)mg->innerctx; 976 PetscMPIInt size; 977 MPI_Comm comm; 978 979 PetscFunctionBegin; 980 PetscCall(PetscObjectGetComm((PetscObject)pc, &comm)); 981 PetscCallMPI(MPI_Comm_size(comm, &size)); 982 PetscOptionsHeadBegin(PetscOptionsObject, "ML options"); 983 984 PrintLevel = 0; 985 indx = 0; 986 partindx = 0; 987 988 PetscCall(PetscOptionsInt("-pc_ml_PrintLevel", "Print level", "ML_Set_PrintLevel", PrintLevel, &PrintLevel, NULL)); 989 PetscStackCallExternalVoid("ML_Set_PrintLevel", ML_Set_PrintLevel(PrintLevel)); 990 PetscCall(PetscOptionsInt("-pc_ml_maxNlevels", "Maximum number of levels", "None", pc_ml->MaxNlevels, &pc_ml->MaxNlevels, NULL)); 991 PetscCall(PetscOptionsInt("-pc_ml_maxCoarseSize", "Maximum coarsest mesh size", "ML_Aggregate_Set_MaxCoarseSize", pc_ml->MaxCoarseSize, &pc_ml->MaxCoarseSize, NULL)); 992 PetscCall(PetscOptionsEList("-pc_ml_CoarsenScheme", "Aggregate Coarsen Scheme", "ML_Aggregate_Set_CoarsenScheme_*", scheme, 4, scheme[0], &indx, NULL)); 993 994 pc_ml->CoarsenScheme = indx; 995 996 PetscCall(PetscOptionsReal("-pc_ml_DampingFactor", "P damping factor", "ML_Aggregate_Set_DampingFactor", pc_ml->DampingFactor, &pc_ml->DampingFactor, NULL)); 997 PetscCall(PetscOptionsReal("-pc_ml_Threshold", "Smoother drop tol", "ML_Aggregate_Set_Threshold", pc_ml->Threshold, &pc_ml->Threshold, NULL)); 998 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)); 999 PetscCall(PetscOptionsBool("-pc_ml_Symmetrize", "Symmetrize aggregation", "ML_Set_Symmetrize", pc_ml->Symmetrize, &pc_ml->Symmetrize, NULL)); 1000 PetscCall(PetscOptionsBool("-pc_ml_BlockScaling", "Scale all dofs at each node together", "None", pc_ml->BlockScaling, &pc_ml->BlockScaling, NULL)); 1001 PetscCall(PetscOptionsEnum("-pc_ml_nullspace", "Which type of null space information to use", "None", PCMLNullSpaceTypes, (PetscEnum)pc_ml->nulltype, (PetscEnum *)&pc_ml->nulltype, NULL)); 1002 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)); 1003 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)); 1004 /* 1005 The following checks a number of conditions. If we let this stuff slip by, then ML's error handling will take over. 1006 This is suboptimal because it amounts to calling exit(1) so we check for the most common conditions. 1007 1008 We also try to set some sane defaults when energy minimization is activated, otherwise it's hard to find a working 1009 combination of options and ML's exit(1) explanations don't help matters. 1010 */ 1011 PetscCheck(pc_ml->EnergyMinimization >= -1 && pc_ml->EnergyMinimization <= 4, comm, PETSC_ERR_ARG_OUTOFRANGE, "EnergyMinimization must be in range -1..4"); 1012 PetscCheck(pc_ml->EnergyMinimization != 4 || size == 1, comm, PETSC_ERR_SUP, "Energy minimization type 4 does not work in parallel"); 1013 if (pc_ml->EnergyMinimization == 4) PetscCall(PetscInfo(pc, "Mandel's energy minimization scheme is experimental and broken in ML-6.2\n")); 1014 if (pc_ml->EnergyMinimization) PetscCall(PetscOptionsReal("-pc_ml_EnergyMinimizationDropTol", "Energy minimization drop tolerance", "None", pc_ml->EnergyMinimizationDropTol, &pc_ml->EnergyMinimizationDropTol, NULL)); 1015 if (pc_ml->EnergyMinimization == 2) { 1016 /* According to ml_MultiLevelPreconditioner.cpp, this option is only meaningful for norm type (2) */ 1017 PetscCall(PetscOptionsBool("-pc_ml_EnergyMinimizationCheap", "Use cheaper variant of norm type 2", "None", pc_ml->EnergyMinimizationCheap, &pc_ml->EnergyMinimizationCheap, NULL)); 1018 } 1019 /* energy minimization sometimes breaks if this is turned off, the more classical stuff should be okay without it */ 1020 if (pc_ml->EnergyMinimization) pc_ml->KeepAggInfo = PETSC_TRUE; 1021 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)); 1022 /* Option (-1) doesn't work at all (calls exit(1)) if the tentative restriction operator isn't stored. */ 1023 if (pc_ml->EnergyMinimization == -1) pc_ml->Reusable = PETSC_TRUE; 1024 PetscCall(PetscOptionsBool("-pc_ml_Reusable", "Store intermedaiate data structures so that the multilevel hierarchy is reusable", "None", pc_ml->Reusable, &pc_ml->Reusable, NULL)); 1025 /* 1026 ML's C API is severely underdocumented and lacks significant functionality. The C++ API calls 1027 ML_Gen_MultiLevelHierarchy_UsingAggregation() which is a modified copy (!?) of the documented function 1028 ML_Gen_MGHierarchy_UsingAggregation(). This modification, however, does not provide a strict superset of the 1029 functionality in the old function, so some users may still want to use it. Note that many options are ignored in 1030 this context, but ML doesn't provide a way to find out which ones. 1031 */ 1032 PetscCall(PetscOptionsBool("-pc_ml_OldHierarchy", "Use old routine to generate hierarchy", "None", pc_ml->OldHierarchy, &pc_ml->OldHierarchy, NULL)); 1033 PetscCall(PetscOptionsBool("-pc_ml_repartition", "Allow ML to repartition levels of the hierarchy", "ML_Repartition_Activate", pc_ml->Repartition, &pc_ml->Repartition, NULL)); 1034 if (pc_ml->Repartition) { 1035 PetscCall(PetscOptionsReal("-pc_ml_repartitionMaxMinRatio", "Acceptable ratio of repartitioned sizes", "ML_Repartition_Set_LargestMinMaxRatio", pc_ml->MaxMinRatio, &pc_ml->MaxMinRatio, NULL)); 1036 PetscCall(PetscOptionsInt("-pc_ml_repartitionMinPerProc", "Smallest repartitioned size", "ML_Repartition_Set_MinPerProc", pc_ml->MinPerProc, &pc_ml->MinPerProc, NULL)); 1037 PetscCall(PetscOptionsInt("-pc_ml_repartitionPutOnSingleProc", "Problem size automatically repartitioned to one processor", "ML_Repartition_Set_PutOnSingleProc", pc_ml->PutOnSingleProc, &pc_ml->PutOnSingleProc, NULL)); 1038 #if defined(HAVE_ML_ZOLTAN) 1039 partindx = 0; 1040 PetscCall(PetscOptionsEList("-pc_ml_repartitionType", "Repartitioning library to use", "ML_Repartition_Set_Partitioner", part, 2, part[0], &partindx, NULL)); 1041 1042 pc_ml->RepartitionType = partindx; 1043 if (!partindx) { 1044 PetscInt zindx = 0; 1045 1046 PetscCall(PetscOptionsEList("-pc_ml_repartitionZoltanScheme", "Repartitioning scheme to use", "None", zscheme, 3, zscheme[0], &zindx, NULL)); 1047 1048 pc_ml->ZoltanScheme = zindx; 1049 } 1050 #else 1051 partindx = 1; 1052 PetscCall(PetscOptionsEList("-pc_ml_repartitionType", "Repartitioning library to use", "ML_Repartition_Set_Partitioner", part, 2, part[1], &partindx, NULL)); 1053 pc_ml->RepartitionType = partindx; 1054 PetscCheck(partindx, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP_SYS, "ML not compiled with Zoltan"); 1055 #endif 1056 PetscCall(PetscOptionsBool("-pc_ml_Aux", "Aggregate using auxiliary coordinate-based laplacian", "None", pc_ml->Aux, &pc_ml->Aux, NULL)); 1057 PetscCall(PetscOptionsReal("-pc_ml_AuxThreshold", "Auxiliary smoother drop tol", "None", pc_ml->AuxThreshold, &pc_ml->AuxThreshold, NULL)); 1058 } 1059 PetscOptionsHeadEnd(); 1060 PetscFunctionReturn(PETSC_SUCCESS); 1061 } 1062 1063 /* 1064 PCCreate_ML - Creates a ML preconditioner context, PC_ML, 1065 and sets this as the private data within the generic preconditioning 1066 context, PC, that was created within PCCreate(). 1067 1068 Input Parameter: 1069 . pc - the preconditioner context 1070 1071 Application Interface Routine: PCCreate() 1072 */ 1073 1074 /*MC 1075 PCML - Use the SNL ML algebraic multigrid preconditioner. 1076 1077 Options Database Keys: 1078 Multigrid options(inherited): 1079 + -pc_mg_cycle_type <v> - v for V cycle, w for W-cycle (`PCMGSetCycleType()`) 1080 . -pc_mg_distinct_smoothup - Should one configure the up and down smoothers separately (`PCMGSetDistinctSmoothUp()`) 1081 - -pc_mg_type <multiplicative> - (one of) additive multiplicative full kascade 1082 1083 ML Options Database Key: 1084 + -pc_ml_PrintLevel <0> - Print level (`ML_Set_PrintLevel()`) 1085 . -pc_ml_maxNlevels <10> - Maximum number of levels (None) 1086 . -pc_ml_maxCoarseSize <1> - Maximum coarsest mesh size (`ML_Aggregate_Set_MaxCoarseSize()`) 1087 . -pc_ml_CoarsenScheme <Uncoupled> - (one of) Uncoupled Coupled MIS METIS 1088 . -pc_ml_DampingFactor <1.33333> - P damping factor (`ML_Aggregate_Set_DampingFactor()`) 1089 . -pc_ml_Threshold <0> - Smoother drop tol (`ML_Aggregate_Set_Threshold()`) 1090 . -pc_ml_SpectralNormScheme_Anorm <false> - Method used for estimating spectral radius (`ML_Set_SpectralNormScheme_Anorm()`) 1091 . -pc_ml_repartition <false> - Allow ML to repartition levels of the hierarchy (`ML_Repartition_Activate()`) 1092 . -pc_ml_repartitionMaxMinRatio <1.3> - Acceptable ratio of repartitioned sizes (`ML_Repartition_Set_LargestMinMaxRatio()`) 1093 . -pc_ml_repartitionMinPerProc <512> - Smallest repartitioned size (`ML_Repartition_Set_MinPerProc()`) 1094 . -pc_ml_repartitionPutOnSingleProc <5000> - Problem size automatically repartitioned to one processor (`ML_Repartition_Set_PutOnSingleProc()`) 1095 . -pc_ml_repartitionType <Zoltan> - Repartitioning library to use (`ML_Repartition_Set_Partitioner()`) 1096 . -pc_ml_repartitionZoltanScheme <RCB> - Repartitioning scheme to use (None) 1097 . -pc_ml_Aux <false> - Aggregate using auxiliary coordinate-based Laplacian (None) 1098 - -pc_ml_AuxThreshold <0.0> - Auxiliary smoother drop tol (None) 1099 1100 Level: intermediate 1101 1102 Developer Note: 1103 The coarser grid matrices and restriction/interpolation 1104 operators are computed by ML, with the matrices converted to PETSc matrices in `MATAIJ` format 1105 and the restriction/interpolation operators wrapped as PETSc shell matrices. 1106 1107 .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCMGType`, `PCMG`, `PCHYPRE`, `PCGAMG`, 1108 `PCMGSetLevels()`, `PCMGGetLevels()`, `PCMGSetType()`, `MPSetCycles()`, `PCMGSetDistinctSmoothUp()`, 1109 `PCMGGetCoarseSolve()`, `PCMGSetResidual()`, `PCMGSetInterpolation()`, 1110 `PCMGSetRestriction()`, `PCMGGetSmoother()`, `PCMGGetSmootherUp()`, `PCMGGetSmootherDown()`, 1111 `PCMGSetCycleTypeOnLevel()`, `PCMGSetRhs()`, `PCMGSetX()`, `PCMGSetR()` 1112 M*/ 1113 1114 PETSC_EXTERN PetscErrorCode PCCreate_ML(PC pc) 1115 { 1116 PC_ML *pc_ml; 1117 PC_MG *mg; 1118 1119 PetscFunctionBegin; 1120 /* PCML is an inherited class of PCMG. Initialize pc as PCMG */ 1121 PetscCall(PCSetType(pc, PCMG)); /* calls PCCreate_MG() and MGCreate_Private() */ 1122 PetscCall(PetscObjectChangeTypeName((PetscObject)pc, PCML)); 1123 /* Since PCMG tries to use DM associated with PC must delete it */ 1124 PetscCall(DMDestroy(&pc->dm)); 1125 PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_EXTERNAL)); 1126 mg = (PC_MG *)pc->data; 1127 1128 /* create a supporting struct and attach it to pc */ 1129 PetscCall(PetscNew(&pc_ml)); 1130 mg->innerctx = pc_ml; 1131 1132 pc_ml->ml_object = 0; 1133 pc_ml->agg_object = 0; 1134 pc_ml->gridctx = 0; 1135 pc_ml->PetscMLdata = 0; 1136 pc_ml->Nlevels = -1; 1137 pc_ml->MaxNlevels = 10; 1138 pc_ml->MaxCoarseSize = 1; 1139 pc_ml->CoarsenScheme = 1; 1140 pc_ml->Threshold = 0.0; 1141 pc_ml->DampingFactor = 4.0 / 3.0; 1142 pc_ml->SpectralNormScheme_Anorm = PETSC_FALSE; 1143 pc_ml->size = 0; 1144 pc_ml->dim = 0; 1145 pc_ml->nloc = 0; 1146 pc_ml->coords = 0; 1147 pc_ml->Repartition = PETSC_FALSE; 1148 pc_ml->MaxMinRatio = 1.3; 1149 pc_ml->MinPerProc = 512; 1150 pc_ml->PutOnSingleProc = 5000; 1151 pc_ml->RepartitionType = 0; 1152 pc_ml->ZoltanScheme = 0; 1153 pc_ml->Aux = PETSC_FALSE; 1154 pc_ml->AuxThreshold = 0.0; 1155 1156 /* allow for coordinates to be passed */ 1157 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCSetCoordinates_C", PCSetCoordinates_ML)); 1158 1159 /* overwrite the pointers of PCMG by the functions of PCML */ 1160 pc->ops->setfromoptions = PCSetFromOptions_ML; 1161 pc->ops->setup = PCSetUp_ML; 1162 pc->ops->reset = PCReset_ML; 1163 pc->ops->destroy = PCDestroy_ML; 1164 PetscFunctionReturn(PETSC_SUCCESS); 1165 } 1166