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