1 /* 2 GAMG geometric-algebric multigrid PC - Mark Adams 2011 3 */ 4 #include <petsc/private/matimpl.h> 5 #include <../src/ksp/pc/impls/gamg/gamg.h> /*I "petscpc.h" I*/ 6 #include <../src/ksp/ksp/impls/cheby/chebyshevimpl.h> /*I "petscksp.h" I*/ 7 8 #if defined(PETSC_HAVE_CUDA) 9 #include <cuda_runtime.h> 10 #endif 11 12 #if defined(PETSC_HAVE_HIP) 13 #include <hip/hip_runtime.h> 14 #endif 15 16 PetscLogEvent petsc_gamg_setup_events[GAMG_NUM_SET]; 17 PetscLogEvent petsc_gamg_setup_matmat_events[PETSC_MG_MAXLEVELS][3]; 18 19 // #define GAMG_STAGES 20 #if defined(GAMG_STAGES) 21 static PetscLogStage gamg_stages[PETSC_MG_MAXLEVELS]; 22 #endif 23 24 static PetscFunctionList GAMGList = NULL; 25 static PetscBool PCGAMGPackageInitialized; 26 27 static PetscErrorCode PCReset_GAMG(PC pc) 28 { 29 PC_MG *mg = (PC_MG *)pc->data; 30 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 31 32 PetscFunctionBegin; 33 PetscCall(PetscFree(pc_gamg->data)); 34 pc_gamg->data_sz = 0; 35 PetscCall(PetscFree(pc_gamg->orig_data)); 36 for (PetscInt level = 0; level < PETSC_MG_MAXLEVELS; level++) { 37 mg->min_eigen_DinvA[level] = 0; 38 mg->max_eigen_DinvA[level] = 0; 39 } 40 pc_gamg->emin = 0; 41 pc_gamg->emax = 0; 42 PetscCall(PCReset_MG(pc)); 43 PetscFunctionReturn(PETSC_SUCCESS); 44 } 45 46 /* 47 PCGAMGCreateLevel_GAMG: create coarse op with RAP. repartition and/or reduce number 48 of active processors. 49 50 Input Parameter: 51 . pc - parameters + side effect: coarse data in 'pc_gamg->data' and 52 'pc_gamg->data_sz' are changed via repartitioning/reduction. 53 . Amat_fine - matrix on this fine (k) level 54 . cr_bs - coarse block size 55 In/Output Parameter: 56 . a_P_inout - prolongation operator to the next level (k-->k-1) 57 . a_nactive_proc - number of active procs 58 Output Parameter: 59 . a_Amat_crs - coarse matrix that is created (k-1) 60 */ 61 static PetscErrorCode PCGAMGCreateLevel_GAMG(PC pc, Mat Amat_fine, PetscInt cr_bs, Mat *a_P_inout, Mat *a_Amat_crs, PetscMPIInt *a_nactive_proc, IS *Pcolumnperm, PetscBool is_last) 62 { 63 PC_MG *mg = (PC_MG *)pc->data; 64 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 65 Mat Cmat, Pold = *a_P_inout; 66 MPI_Comm comm; 67 PetscMPIInt rank, size, new_size, nactive = *a_nactive_proc; 68 PetscInt ncrs_eq, ncrs, f_bs; 69 70 PetscFunctionBegin; 71 PetscCall(PetscObjectGetComm((PetscObject)Amat_fine, &comm)); 72 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 73 PetscCallMPI(MPI_Comm_size(comm, &size)); 74 PetscCall(MatGetBlockSize(Amat_fine, &f_bs)); 75 PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_PTAP], 0, 0, 0, 0)); 76 PetscCall(PetscLogEventBegin(petsc_gamg_setup_matmat_events[pc_gamg->current_level][1], 0, 0, 0, 0)); 77 PetscCall(MatPtAP(Amat_fine, Pold, MAT_INITIAL_MATRIX, 2.0, &Cmat)); 78 PetscCall(PetscLogEventEnd(petsc_gamg_setup_matmat_events[pc_gamg->current_level][1], 0, 0, 0, 0)); 79 PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_PTAP], 0, 0, 0, 0)); 80 81 if (Pcolumnperm) *Pcolumnperm = NULL; 82 83 /* set 'ncrs' (nodes), 'ncrs_eq' (equations)*/ 84 PetscCall(MatGetLocalSize(Cmat, &ncrs_eq, NULL)); 85 if (pc_gamg->data_cell_rows > 0) { 86 ncrs = pc_gamg->data_sz / pc_gamg->data_cell_cols / pc_gamg->data_cell_rows; 87 } else { 88 PetscInt bs; 89 PetscCall(MatGetBlockSize(Cmat, &bs)); 90 ncrs = ncrs_eq / bs; 91 } 92 /* get number of PEs to make active 'new_size', reduce, can be any integer 1-P */ 93 if (pc_gamg->level_reduction_factors[pc_gamg->current_level] == 0 && PetscDefined(HAVE_CUDA) && pc_gamg->current_level == 0) { /* 0 turns reducing to 1 process/device on; do for HIP, etc. */ 94 #if defined(PETSC_HAVE_CUDA) 95 PetscShmComm pshmcomm; 96 PetscMPIInt locrank; 97 MPI_Comm loccomm; 98 PetscInt s_nnodes, r_nnodes, new_new_size; 99 cudaError_t cerr; 100 int devCount; 101 PetscCall(PetscShmCommGet(comm, &pshmcomm)); 102 PetscCall(PetscShmCommGetMpiShmComm(pshmcomm, &loccomm)); 103 PetscCallMPI(MPI_Comm_rank(loccomm, &locrank)); 104 s_nnodes = !locrank; 105 PetscCall(MPIU_Allreduce(&s_nnodes, &r_nnodes, 1, MPIU_INT, MPI_SUM, comm)); 106 PetscCheck((size % r_nnodes) == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "odd number of nodes np=%d nnodes%" PetscInt_FMT, size, r_nnodes); 107 devCount = 0; 108 cerr = cudaGetDeviceCount(&devCount); 109 cudaGetLastError(); /* Reset the last error */ 110 if (cerr == cudaSuccess && devCount >= 1) { /* There are devices, else go to heuristic */ 111 new_new_size = r_nnodes * devCount; 112 new_size = new_new_size; 113 PetscCall(PetscInfo(pc, "%s: Fine grid with Cuda. %" PetscInt_FMT " nodes. Change new active set size %d --> %d (devCount=%d #nodes=%" PetscInt_FMT ")\n", ((PetscObject)pc)->prefix, r_nnodes, nactive, new_size, devCount, r_nnodes)); 114 } else { 115 PetscCall(PetscInfo(pc, "%s: With Cuda but no device. Use heuristics.\n", ((PetscObject)pc)->prefix)); 116 goto HEURISTIC; 117 } 118 #else 119 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "should not be here"); 120 #endif 121 } else if (pc_gamg->level_reduction_factors[pc_gamg->current_level] > 0) { 122 if (nactive < pc_gamg->level_reduction_factors[pc_gamg->current_level]) { 123 new_size = 1; 124 PetscCall(PetscInfo(pc, "%s: reduction factor too small for %d active processes: reduce to one process\n", ((PetscObject)pc)->prefix, new_size)); 125 } else { 126 PetscCheck(nactive % pc_gamg->level_reduction_factors[pc_gamg->current_level] == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "odd number of active process %d wrt reduction factor %" PetscInt_FMT, nactive, pc_gamg->level_reduction_factors[pc_gamg->current_level]); 127 new_size = nactive / pc_gamg->level_reduction_factors[pc_gamg->current_level]; 128 PetscCall(PetscInfo(pc, "%s: Manually setting reduction to %d active processes (%d/%" PetscInt_FMT ")\n", ((PetscObject)pc)->prefix, new_size, nactive, pc_gamg->level_reduction_factors[pc_gamg->current_level])); 129 } 130 } else if (is_last && !pc_gamg->use_parallel_coarse_grid_solver) { 131 new_size = 1; 132 PetscCall(PetscInfo(pc, "%s: Force coarsest grid reduction to %d active processes\n", ((PetscObject)pc)->prefix, new_size)); 133 } else { 134 PetscInt ncrs_eq_glob; 135 #if defined(PETSC_HAVE_CUDA) 136 HEURISTIC: 137 #endif 138 PetscCall(MatGetSize(Cmat, &ncrs_eq_glob, NULL)); 139 new_size = (PetscMPIInt)((float)ncrs_eq_glob / (float)pc_gamg->min_eq_proc + 0.5); /* hardwire min. number of eq/proc */ 140 if (!new_size) new_size = 1; /* not likely, possible? */ 141 else if (new_size >= nactive) new_size = nactive; /* no change, rare */ 142 PetscCall(PetscInfo(pc, "%s: Coarse grid reduction from %d to %d active processes\n", ((PetscObject)pc)->prefix, nactive, new_size)); 143 } 144 if (new_size == nactive) { 145 *a_Amat_crs = Cmat; /* output - no repartitioning or reduction - could bail here */ 146 if (new_size < size) { 147 /* odd case where multiple coarse grids are on one processor or no coarsening ... */ 148 PetscCall(PetscInfo(pc, "%s: reduced grid using same number of processors (%d) as last grid (use larger coarse grid)\n", ((PetscObject)pc)->prefix, nactive)); 149 if (pc_gamg->cpu_pin_coarse_grids) { 150 PetscCall(MatBindToCPU(*a_Amat_crs, PETSC_TRUE)); 151 PetscCall(MatBindToCPU(*a_P_inout, PETSC_TRUE)); 152 } 153 } 154 /* we know that the grid structure can be reused in MatPtAP */ 155 } else { /* reduce active processors - we know that the grid structure can NOT be reused in MatPtAP */ 156 PetscInt *counts, *newproc_idx, ii, jj, kk, strideNew, *tidx, ncrs_new, ncrs_eq_new, nloc_old, expand_factor = 1, rfactor = 1; 157 IS is_eq_newproc, is_eq_num, is_eq_num_prim, new_eq_indices; 158 PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_REDUCE], 0, 0, 0, 0)); 159 nloc_old = ncrs_eq / cr_bs; 160 PetscCheck(ncrs_eq % cr_bs == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "ncrs_eq %" PetscInt_FMT " not divisible by cr_bs %" PetscInt_FMT, ncrs_eq, cr_bs); 161 /* get new_size and rfactor */ 162 if (pc_gamg->layout_type == PCGAMG_LAYOUT_SPREAD || !pc_gamg->repart) { 163 /* find factor */ 164 if (new_size == 1) rfactor = size; /* don't modify */ 165 else { 166 PetscReal best_fact = 0.; 167 jj = -1; 168 for (kk = 1; kk <= size; kk++) { 169 if (!(size % kk)) { /* a candidate */ 170 PetscReal nactpe = (PetscReal)size / (PetscReal)kk, fact = nactpe / (PetscReal)new_size; 171 if (fact > 1.0) fact = 1. / fact; /* keep fact < 1 */ 172 if (fact > best_fact) { 173 best_fact = fact; 174 jj = kk; 175 } 176 } 177 } 178 if (jj != -1) rfactor = jj; 179 else rfactor = 1; /* a prime */ 180 if (pc_gamg->layout_type == PCGAMG_LAYOUT_COMPACT) expand_factor = 1; 181 else expand_factor = rfactor; 182 } 183 new_size = size / rfactor; /* make new size one that is factor */ 184 if (new_size == nactive) { /* no repartitioning or reduction, bail out because nested here (rare) */ 185 *a_Amat_crs = Cmat; 186 PetscCall(PetscInfo(pc, "%s: Finding factorable processor set stopped reduction: new_size=%d, neq(loc)=%" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, new_size, ncrs_eq)); 187 PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_REDUCE], 0, 0, 0, 0)); 188 PetscFunctionReturn(PETSC_SUCCESS); 189 } 190 } 191 /* make 'is_eq_newproc' */ 192 PetscCall(PetscMalloc1(size, &counts)); 193 if (pc_gamg->repart) { /* Repartition Cmat_{k} and move columns of P^{k}_{k-1} and coordinates of primal part accordingly */ 194 Mat adj; 195 PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_REPART], 0, 0, 0, 0)); 196 PetscCall(PetscInfo(pc, "%s: Repartition: size (active): %d --> %d, %" PetscInt_FMT " local equations, using %s process layout\n", ((PetscObject)pc)->prefix, *a_nactive_proc, new_size, ncrs_eq, (pc_gamg->layout_type == PCGAMG_LAYOUT_COMPACT) ? "compact" : "spread")); 197 /* get 'adj' */ 198 if (cr_bs == 1) { 199 PetscCall(MatConvert(Cmat, MATMPIADJ, MAT_INITIAL_MATRIX, &adj)); 200 } else { 201 /* make a scalar matrix to partition (no Stokes here) */ 202 Mat tMat; 203 PetscInt Istart_crs, Iend_crs, ncols, jj, Ii; 204 const PetscScalar *vals; 205 const PetscInt *idx; 206 PetscInt *d_nnz, *o_nnz, M, N; 207 static PetscInt llev = 0; /* ugly but just used for debugging */ 208 MatType mtype; 209 210 PetscCall(PetscMalloc2(ncrs, &d_nnz, ncrs, &o_nnz)); 211 PetscCall(MatGetOwnershipRange(Cmat, &Istart_crs, &Iend_crs)); 212 PetscCall(MatGetSize(Cmat, &M, &N)); 213 for (Ii = Istart_crs, jj = 0; Ii < Iend_crs; Ii += cr_bs, jj++) { 214 PetscCall(MatGetRow(Cmat, Ii, &ncols, NULL, NULL)); 215 d_nnz[jj] = ncols / cr_bs; 216 o_nnz[jj] = ncols / cr_bs; 217 PetscCall(MatRestoreRow(Cmat, Ii, &ncols, NULL, NULL)); 218 if (d_nnz[jj] > ncrs) d_nnz[jj] = ncrs; 219 if (o_nnz[jj] > (M / cr_bs - ncrs)) o_nnz[jj] = M / cr_bs - ncrs; 220 } 221 222 PetscCall(MatGetType(Amat_fine, &mtype)); 223 PetscCall(MatCreate(comm, &tMat)); 224 PetscCall(MatSetSizes(tMat, ncrs, ncrs, PETSC_DETERMINE, PETSC_DETERMINE)); 225 PetscCall(MatSetType(tMat, mtype)); 226 PetscCall(MatSeqAIJSetPreallocation(tMat, 0, d_nnz)); 227 PetscCall(MatMPIAIJSetPreallocation(tMat, 0, d_nnz, 0, o_nnz)); 228 PetscCall(PetscFree2(d_nnz, o_nnz)); 229 230 for (ii = Istart_crs; ii < Iend_crs; ii++) { 231 PetscInt dest_row = ii / cr_bs; 232 PetscCall(MatGetRow(Cmat, ii, &ncols, &idx, &vals)); 233 for (jj = 0; jj < ncols; jj++) { 234 PetscInt dest_col = idx[jj] / cr_bs; 235 PetscScalar v = 1.0; 236 PetscCall(MatSetValues(tMat, 1, &dest_row, 1, &dest_col, &v, ADD_VALUES)); 237 } 238 PetscCall(MatRestoreRow(Cmat, ii, &ncols, &idx, &vals)); 239 } 240 PetscCall(MatAssemblyBegin(tMat, MAT_FINAL_ASSEMBLY)); 241 PetscCall(MatAssemblyEnd(tMat, MAT_FINAL_ASSEMBLY)); 242 243 if (llev++ == -1) { 244 PetscViewer viewer; 245 char fname[32]; 246 PetscCall(PetscSNPrintf(fname, sizeof(fname), "part_mat_%" PetscInt_FMT ".mat", llev)); 247 PetscCall(PetscViewerBinaryOpen(comm, fname, FILE_MODE_WRITE, &viewer)); 248 PetscCall(MatView(tMat, viewer)); 249 PetscCall(PetscViewerDestroy(&viewer)); 250 } 251 PetscCall(MatConvert(tMat, MATMPIADJ, MAT_INITIAL_MATRIX, &adj)); 252 PetscCall(MatDestroy(&tMat)); 253 } /* create 'adj' */ 254 255 { /* partition: get newproc_idx */ 256 char prefix[256]; 257 const char *pcpre; 258 const PetscInt *is_idx; 259 MatPartitioning mpart; 260 IS proc_is; 261 262 PetscCall(MatPartitioningCreate(comm, &mpart)); 263 PetscCall(MatPartitioningSetAdjacency(mpart, adj)); 264 PetscCall(PCGetOptionsPrefix(pc, &pcpre)); 265 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_gamg_", pcpre ? pcpre : "")); 266 PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mpart, prefix)); 267 PetscCall(MatPartitioningSetFromOptions(mpart)); 268 PetscCall(MatPartitioningSetNParts(mpart, new_size)); 269 PetscCall(MatPartitioningApply(mpart, &proc_is)); 270 PetscCall(MatPartitioningDestroy(&mpart)); 271 272 /* collect IS info */ 273 PetscCall(PetscMalloc1(ncrs_eq, &newproc_idx)); 274 PetscCall(ISGetIndices(proc_is, &is_idx)); 275 for (kk = jj = 0; kk < nloc_old; kk++) { 276 for (ii = 0; ii < cr_bs; ii++, jj++) { newproc_idx[jj] = is_idx[kk] * expand_factor; /* distribution */ } 277 } 278 PetscCall(ISRestoreIndices(proc_is, &is_idx)); 279 PetscCall(ISDestroy(&proc_is)); 280 } 281 PetscCall(MatDestroy(&adj)); 282 283 PetscCall(ISCreateGeneral(comm, ncrs_eq, newproc_idx, PETSC_COPY_VALUES, &is_eq_newproc)); 284 PetscCall(PetscFree(newproc_idx)); 285 PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_REPART], 0, 0, 0, 0)); 286 } else { /* simple aggregation of parts -- 'is_eq_newproc' */ 287 PetscInt targetPE; 288 PetscCheck(new_size != nactive, PETSC_COMM_SELF, PETSC_ERR_PLIB, "new_size==nactive. Should not happen"); 289 PetscCall(PetscInfo(pc, "%s: Number of equations (loc) %" PetscInt_FMT " with simple aggregation\n", ((PetscObject)pc)->prefix, ncrs_eq)); 290 targetPE = (rank / rfactor) * expand_factor; 291 PetscCall(ISCreateStride(comm, ncrs_eq, targetPE, 0, &is_eq_newproc)); 292 } /* end simple 'is_eq_newproc' */ 293 294 /* 295 Create an index set from the is_eq_newproc index set to indicate the mapping TO 296 */ 297 PetscCall(ISPartitioningToNumbering(is_eq_newproc, &is_eq_num)); 298 is_eq_num_prim = is_eq_num; 299 /* 300 Determine how many equations/vertices are assigned to each processor 301 */ 302 PetscCall(ISPartitioningCount(is_eq_newproc, size, counts)); 303 ncrs_eq_new = counts[rank]; 304 PetscCall(ISDestroy(&is_eq_newproc)); 305 ncrs_new = ncrs_eq_new / cr_bs; 306 307 PetscCall(PetscFree(counts)); 308 /* data movement scope -- this could be moved to subclasses so that we don't try to cram all auxiliary data into some complex abstracted thing */ 309 { 310 Vec src_crd, dest_crd; 311 const PetscInt *idx, ndata_rows = pc_gamg->data_cell_rows, ndata_cols = pc_gamg->data_cell_cols, node_data_sz = ndata_rows * ndata_cols; 312 VecScatter vecscat; 313 PetscScalar *array; 314 IS isscat; 315 /* move data (for primal equations only) */ 316 /* Create a vector to contain the newly ordered element information */ 317 PetscCall(VecCreate(comm, &dest_crd)); 318 PetscCall(VecSetSizes(dest_crd, node_data_sz * ncrs_new, PETSC_DECIDE)); 319 PetscCall(VecSetType(dest_crd, VECSTANDARD)); /* this is needed! */ 320 /* 321 There are 'ndata_rows*ndata_cols' data items per node, (one can think of the vectors of having 322 a block size of ...). Note, ISs are expanded into equation space by 'cr_bs'. 323 */ 324 PetscCall(PetscMalloc1(ncrs * node_data_sz, &tidx)); 325 PetscCall(ISGetIndices(is_eq_num_prim, &idx)); 326 for (ii = 0, jj = 0; ii < ncrs; ii++) { 327 PetscInt id = idx[ii * cr_bs] / cr_bs; /* get node back */ 328 for (kk = 0; kk < node_data_sz; kk++, jj++) tidx[jj] = id * node_data_sz + kk; 329 } 330 PetscCall(ISRestoreIndices(is_eq_num_prim, &idx)); 331 PetscCall(ISCreateGeneral(comm, node_data_sz * ncrs, tidx, PETSC_COPY_VALUES, &isscat)); 332 PetscCall(PetscFree(tidx)); 333 /* 334 Create a vector to contain the original vertex information for each element 335 */ 336 PetscCall(VecCreateSeq(PETSC_COMM_SELF, node_data_sz * ncrs, &src_crd)); 337 for (jj = 0; jj < ndata_cols; jj++) { 338 const PetscInt stride0 = ncrs * pc_gamg->data_cell_rows; 339 for (ii = 0; ii < ncrs; ii++) { 340 for (kk = 0; kk < ndata_rows; kk++) { 341 PetscInt ix = ii * ndata_rows + kk + jj * stride0, jx = ii * node_data_sz + kk * ndata_cols + jj; 342 PetscScalar tt = (PetscScalar)pc_gamg->data[ix]; 343 PetscCall(VecSetValues(src_crd, 1, &jx, &tt, INSERT_VALUES)); 344 } 345 } 346 } 347 PetscCall(VecAssemblyBegin(src_crd)); 348 PetscCall(VecAssemblyEnd(src_crd)); 349 /* 350 Scatter the element vertex information (still in the original vertex ordering) 351 to the correct processor 352 */ 353 PetscCall(VecScatterCreate(src_crd, NULL, dest_crd, isscat, &vecscat)); 354 PetscCall(ISDestroy(&isscat)); 355 PetscCall(VecScatterBegin(vecscat, src_crd, dest_crd, INSERT_VALUES, SCATTER_FORWARD)); 356 PetscCall(VecScatterEnd(vecscat, src_crd, dest_crd, INSERT_VALUES, SCATTER_FORWARD)); 357 PetscCall(VecScatterDestroy(&vecscat)); 358 PetscCall(VecDestroy(&src_crd)); 359 /* 360 Put the element vertex data into a new allocation of the gdata->ele 361 */ 362 PetscCall(PetscFree(pc_gamg->data)); 363 PetscCall(PetscMalloc1(node_data_sz * ncrs_new, &pc_gamg->data)); 364 365 pc_gamg->data_sz = node_data_sz * ncrs_new; 366 strideNew = ncrs_new * ndata_rows; 367 368 PetscCall(VecGetArray(dest_crd, &array)); 369 for (jj = 0; jj < ndata_cols; jj++) { 370 for (ii = 0; ii < ncrs_new; ii++) { 371 for (kk = 0; kk < ndata_rows; kk++) { 372 PetscInt ix = ii * ndata_rows + kk + jj * strideNew, jx = ii * node_data_sz + kk * ndata_cols + jj; 373 pc_gamg->data[ix] = PetscRealPart(array[jx]); 374 } 375 } 376 } 377 PetscCall(VecRestoreArray(dest_crd, &array)); 378 PetscCall(VecDestroy(&dest_crd)); 379 } 380 /* move A and P (columns) with new layout */ 381 /* 382 Invert for MatCreateSubMatrix 383 */ 384 PetscCall(ISInvertPermutation(is_eq_num, ncrs_eq_new, &new_eq_indices)); 385 PetscCall(ISSort(new_eq_indices)); /* is this needed? */ 386 PetscCall(ISSetBlockSize(new_eq_indices, cr_bs)); 387 if (is_eq_num != is_eq_num_prim) { PetscCall(ISDestroy(&is_eq_num_prim)); /* could be same as 'is_eq_num' */ } 388 if (Pcolumnperm) { 389 PetscCall(PetscObjectReference((PetscObject)new_eq_indices)); 390 *Pcolumnperm = new_eq_indices; 391 } 392 PetscCall(ISDestroy(&is_eq_num)); 393 394 /* 'a_Amat_crs' output */ 395 { 396 Mat mat; 397 PetscBool isset, isspd, isher; 398 #if !defined(PETSC_USE_COMPLEX) 399 PetscBool issym; 400 #endif 401 402 PetscCall(MatCreateSubMatrix(Cmat, new_eq_indices, new_eq_indices, MAT_INITIAL_MATRIX, &mat)); 403 PetscCall(MatIsSPDKnown(Cmat, &isset, &isspd)); // like MatPropagateSymmetryOptions, but should set MAT_STRUCTURALLY_SYMMETRIC ? 404 if (isset) PetscCall(MatSetOption(mat, MAT_SPD, isspd)); 405 else { 406 PetscCall(MatIsHermitianKnown(Cmat, &isset, &isher)); 407 if (isset) PetscCall(MatSetOption(mat, MAT_HERMITIAN, isher)); 408 else { 409 #if !defined(PETSC_USE_COMPLEX) 410 PetscCall(MatIsSymmetricKnown(Cmat, &isset, &issym)); 411 if (isset) PetscCall(MatSetOption(mat, MAT_SYMMETRIC, issym)); 412 #endif 413 } 414 } 415 *a_Amat_crs = mat; 416 } 417 PetscCall(MatDestroy(&Cmat)); 418 419 /* prolongator */ 420 { 421 IS findices; 422 PetscInt Istart, Iend; 423 Mat Pnew; 424 425 PetscCall(MatGetOwnershipRange(Pold, &Istart, &Iend)); 426 PetscCall(ISCreateStride(comm, Iend - Istart, Istart, 1, &findices)); 427 PetscCall(ISSetBlockSize(findices, f_bs)); 428 PetscCall(MatCreateSubMatrix(Pold, findices, new_eq_indices, MAT_INITIAL_MATRIX, &Pnew)); 429 PetscCall(ISDestroy(&findices)); 430 PetscCall(MatSetOption(Pnew, MAT_FORM_EXPLICIT_TRANSPOSE, PETSC_TRUE)); 431 432 PetscCall(MatDestroy(a_P_inout)); 433 434 /* output - repartitioned */ 435 *a_P_inout = Pnew; 436 } 437 PetscCall(ISDestroy(&new_eq_indices)); 438 439 *a_nactive_proc = new_size; /* output */ 440 441 /* pinning on reduced grids, not a bad heuristic and optimization gets folded into process reduction optimization */ 442 if (pc_gamg->cpu_pin_coarse_grids) { 443 #if defined(PETSC_HAVE_VIENNACL) || defined(PETSC_HAVE_CUDA) 444 static PetscInt llev = 2; 445 PetscCall(PetscInfo(pc, "%s: Pinning level %" PetscInt_FMT " to the CPU\n", ((PetscObject)pc)->prefix, llev++)); 446 #endif 447 PetscCall(MatBindToCPU(*a_Amat_crs, PETSC_TRUE)); 448 PetscCall(MatBindToCPU(*a_P_inout, PETSC_TRUE)); 449 if (1) { /* HACK: move this to MatBindCPU_MPIAIJXXX; lvec is created, need to pin it, this is done in MatSetUpMultiply_MPIAIJ. Hack */ 450 Mat A = *a_Amat_crs, P = *a_P_inout; 451 PetscMPIInt size; 452 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size)); 453 if (size > 1) { 454 Mat_MPIAIJ *a = (Mat_MPIAIJ *)A->data, *p = (Mat_MPIAIJ *)P->data; 455 PetscCall(VecBindToCPU(a->lvec, PETSC_TRUE)); 456 PetscCall(VecBindToCPU(p->lvec, PETSC_TRUE)); 457 } 458 } 459 } 460 PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_REDUCE], 0, 0, 0, 0)); 461 } // processor reduce 462 PetscFunctionReturn(PETSC_SUCCESS); 463 } 464 465 // used in GEO 466 PetscErrorCode PCGAMGSquareGraph_GAMG(PC a_pc, Mat Gmat1, Mat *Gmat2) 467 { 468 const char *prefix; 469 char addp[32]; 470 PC_MG *mg = (PC_MG *)a_pc->data; 471 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 472 473 PetscFunctionBegin; 474 PetscCall(PCGetOptionsPrefix(a_pc, &prefix)); 475 PetscCall(PetscInfo(a_pc, "%s: Square Graph on level %" PetscInt_FMT "\n", ((PetscObject)a_pc)->prefix, pc_gamg->current_level + 1)); 476 PetscCall(MatProductCreate(Gmat1, Gmat1, NULL, Gmat2)); 477 PetscCall(MatSetOptionsPrefix(*Gmat2, prefix)); 478 PetscCall(PetscSNPrintf(addp, sizeof(addp), "pc_gamg_square_%" PetscInt_FMT "_", pc_gamg->current_level)); 479 PetscCall(MatAppendOptionsPrefix(*Gmat2, addp)); 480 if ((*Gmat2)->structurally_symmetric == PETSC_BOOL3_TRUE) { 481 PetscCall(MatProductSetType(*Gmat2, MATPRODUCT_AB)); 482 } else { 483 PetscCall(MatSetOption(Gmat1, MAT_FORM_EXPLICIT_TRANSPOSE, PETSC_TRUE)); 484 PetscCall(MatProductSetType(*Gmat2, MATPRODUCT_AtB)); 485 } 486 PetscCall(MatProductSetFromOptions(*Gmat2)); 487 PetscCall(PetscLogEventBegin(petsc_gamg_setup_matmat_events[pc_gamg->current_level][0], 0, 0, 0, 0)); 488 PetscCall(MatProductSymbolic(*Gmat2)); 489 PetscCall(PetscLogEventEnd(petsc_gamg_setup_matmat_events[pc_gamg->current_level][0], 0, 0, 0, 0)); 490 PetscCall(MatProductClear(*Gmat2)); 491 /* we only need the sparsity, cheat and tell PETSc the matrix has been assembled */ 492 (*Gmat2)->assembled = PETSC_TRUE; 493 PetscFunctionReturn(PETSC_SUCCESS); 494 } 495 496 /* 497 PCSetUp_GAMG - Prepares for the use of the GAMG preconditioner 498 by setting data structures and options. 499 500 Input Parameter: 501 . pc - the preconditioner context 502 503 */ 504 static PetscErrorCode PCSetUp_GAMG(PC pc) 505 { 506 PC_MG *mg = (PC_MG *)pc->data; 507 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 508 Mat Pmat = pc->pmat; 509 PetscInt fine_level, level, level1, bs, M, N, qq, lidx, nASMBlocksArr[PETSC_MG_MAXLEVELS]; 510 MPI_Comm comm; 511 PetscMPIInt rank, size, nactivepe; 512 Mat Aarr[PETSC_MG_MAXLEVELS], Parr[PETSC_MG_MAXLEVELS]; 513 IS *ASMLocalIDsArr[PETSC_MG_MAXLEVELS]; 514 PetscBool is_last = PETSC_FALSE; 515 #if defined(PETSC_USE_INFO) 516 PetscLogDouble nnz0 = 0., nnztot = 0.; 517 MatInfo info; 518 #endif 519 520 PetscFunctionBegin; 521 PetscCall(PetscObjectGetComm((PetscObject)pc, &comm)); 522 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 523 PetscCallMPI(MPI_Comm_size(comm, &size)); 524 PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_SETUP], 0, 0, 0, 0)); 525 if (pc->setupcalled) { 526 if (!pc_gamg->reuse_prol || pc->flag == DIFFERENT_NONZERO_PATTERN) { 527 /* reset everything */ 528 PetscCall(PCReset_MG(pc)); 529 pc->setupcalled = 0; 530 } else { 531 PC_MG_Levels **mglevels = mg->levels; 532 /* just do Galerkin grids */ 533 Mat B, dA, dB; 534 if (pc_gamg->Nlevels > 1) { 535 PetscInt gl; 536 /* currently only handle case where mat and pmat are the same on coarser levels */ 537 PetscCall(KSPGetOperators(mglevels[pc_gamg->Nlevels - 1]->smoothd, &dA, &dB)); 538 /* (re)set to get dirty flag */ 539 PetscCall(KSPSetOperators(mglevels[pc_gamg->Nlevels - 1]->smoothd, dA, dB)); 540 541 for (level = pc_gamg->Nlevels - 2, gl = 0; level >= 0; level--, gl++) { 542 MatReuse reuse = MAT_INITIAL_MATRIX; 543 #if defined(GAMG_STAGES) 544 PetscCall(PetscLogStagePush(gamg_stages[gl])); 545 #endif 546 /* matrix structure can change from repartitioning or process reduction but don't know if we have process reduction here. Should fix */ 547 PetscCall(KSPGetOperators(mglevels[level]->smoothd, NULL, &B)); 548 if (B->product) { 549 if (B->product->A == dB && B->product->B == mglevels[level + 1]->interpolate) reuse = MAT_REUSE_MATRIX; 550 } 551 if (reuse == MAT_INITIAL_MATRIX) PetscCall(MatDestroy(&mglevels[level]->A)); 552 if (reuse == MAT_REUSE_MATRIX) { 553 PetscCall(PetscInfo(pc, "%s: RAP after first solve, reuse matrix level %" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, level)); 554 } else { 555 PetscCall(PetscInfo(pc, "%s: RAP after first solve, new matrix level %" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, level)); 556 } 557 PetscCall(PetscLogEventBegin(petsc_gamg_setup_matmat_events[gl][1], 0, 0, 0, 0)); 558 PetscCall(MatPtAP(dB, mglevels[level + 1]->interpolate, reuse, PETSC_DEFAULT, &B)); 559 PetscCall(PetscLogEventEnd(petsc_gamg_setup_matmat_events[gl][1], 0, 0, 0, 0)); 560 if (reuse == MAT_INITIAL_MATRIX) mglevels[level]->A = B; 561 PetscCall(KSPSetOperators(mglevels[level]->smoothd, B, B)); 562 // check for redoing eigen estimates 563 if (pc_gamg->recompute_esteig) { 564 PetscBool ischeb; 565 KSP smoother; 566 PetscCall(PCMGGetSmoother(pc, level + 1, &smoother)); 567 PetscCall(PetscObjectTypeCompare((PetscObject)smoother, KSPCHEBYSHEV, &ischeb)); 568 if (ischeb) { 569 KSP_Chebyshev *cheb = (KSP_Chebyshev *)smoother->data; 570 cheb->emin_provided = 0; 571 cheb->emax_provided = 0; 572 } 573 /* we could call PetscCall(KSPChebyshevSetEigenvalues(smoother, 0, 0)); but the logic does not work properly */ 574 } 575 // inc 576 dB = B; 577 #if defined(GAMG_STAGES) 578 PetscCall(PetscLogStagePop()); 579 #endif 580 } 581 } 582 583 PetscCall(PCSetUp_MG(pc)); 584 PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_SETUP], 0, 0, 0, 0)); 585 PetscFunctionReturn(PETSC_SUCCESS); 586 } 587 } 588 589 if (!pc_gamg->data) { 590 if (pc_gamg->orig_data) { 591 PetscCall(MatGetBlockSize(Pmat, &bs)); 592 PetscCall(MatGetLocalSize(Pmat, &qq, NULL)); 593 594 pc_gamg->data_sz = (qq / bs) * pc_gamg->orig_data_cell_rows * pc_gamg->orig_data_cell_cols; 595 pc_gamg->data_cell_rows = pc_gamg->orig_data_cell_rows; 596 pc_gamg->data_cell_cols = pc_gamg->orig_data_cell_cols; 597 598 PetscCall(PetscMalloc1(pc_gamg->data_sz, &pc_gamg->data)); 599 for (qq = 0; qq < pc_gamg->data_sz; qq++) pc_gamg->data[qq] = pc_gamg->orig_data[qq]; 600 } else { 601 PetscCheck(pc_gamg->ops->createdefaultdata, comm, PETSC_ERR_PLIB, "'createdefaultdata' not set(?) need to support NULL data"); 602 PetscCall(pc_gamg->ops->createdefaultdata(pc, Pmat)); 603 } 604 } 605 606 /* cache original data for reuse */ 607 if (!pc_gamg->orig_data && (PetscBool)(!pc_gamg->reuse_prol)) { 608 PetscCall(PetscMalloc1(pc_gamg->data_sz, &pc_gamg->orig_data)); 609 for (qq = 0; qq < pc_gamg->data_sz; qq++) pc_gamg->orig_data[qq] = pc_gamg->data[qq]; 610 pc_gamg->orig_data_cell_rows = pc_gamg->data_cell_rows; 611 pc_gamg->orig_data_cell_cols = pc_gamg->data_cell_cols; 612 } 613 614 /* get basic dims */ 615 PetscCall(MatGetBlockSize(Pmat, &bs)); 616 PetscCall(MatGetSize(Pmat, &M, &N)); 617 618 #if defined(PETSC_USE_INFO) 619 PetscCall(MatGetInfo(Pmat, MAT_GLOBAL_SUM, &info)); /* global reduction */ 620 nnz0 = info.nz_used; 621 nnztot = info.nz_used; 622 #endif 623 PetscCall(PetscInfo(pc, "%s: level %d) N=%" PetscInt_FMT ", n data rows=%" PetscInt_FMT ", n data cols=%" PetscInt_FMT ", nnz/row (ave)=%" PetscInt_FMT ", np=%d\n", ((PetscObject)pc)->prefix, 0, M, pc_gamg->data_cell_rows, pc_gamg->data_cell_cols, (PetscInt)(nnz0 / (PetscReal)M + 0.5), size)); 624 625 /* Get A_i and R_i */ 626 for (level = 0, Aarr[0] = Pmat, nactivepe = size; level < (pc_gamg->Nlevels - 1) && (!level || M > pc_gamg->coarse_eq_limit); level++) { 627 pc_gamg->current_level = level; 628 PetscCheck(level < PETSC_MG_MAXLEVELS, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Too many levels %" PetscInt_FMT, level); 629 level1 = level + 1; 630 #if defined(GAMG_STAGES) 631 if (!gamg_stages[level]) { 632 char str[32]; 633 PetscCall(PetscSNPrintf(str, PETSC_STATIC_ARRAY_LENGTH(str), "GAMG Level %d", (int)level)); 634 PetscCall(PetscLogStageRegister(str, &gamg_stages[level])); 635 } 636 PetscCall(PetscLogStagePush(gamg_stages[level])); 637 #endif 638 { /* construct prolongator */ 639 Mat Gmat; 640 PetscCoarsenData *agg_lists; 641 Mat Prol11; 642 643 PetscCall(PCGAMGCreateGraph(pc, Aarr[level], &Gmat)); 644 PetscCall(pc_gamg->ops->coarsen(pc, &Gmat, &agg_lists)); 645 PetscCall(pc_gamg->ops->prolongator(pc, Aarr[level], Gmat, agg_lists, &Prol11)); 646 647 /* could have failed to create new level */ 648 if (Prol11) { 649 const char *prefix; 650 char addp[32]; 651 652 /* get new block size of coarse matrices */ 653 PetscCall(MatGetBlockSizes(Prol11, NULL, &bs)); 654 655 if (pc_gamg->ops->optprolongator) { 656 /* smooth */ 657 PetscCall(pc_gamg->ops->optprolongator(pc, Aarr[level], &Prol11)); 658 } 659 660 if (pc_gamg->use_aggs_in_asm) { 661 PetscInt bs; 662 PetscCall(MatGetBlockSizes(Prol11, &bs, NULL)); // not timed directly, ugly, could remove, but good ASM method 663 PetscCall(PetscCDGetASMBlocks(agg_lists, bs, Gmat, &nASMBlocksArr[level], &ASMLocalIDsArr[level])); 664 } 665 666 PetscCall(PCGetOptionsPrefix(pc, &prefix)); 667 PetscCall(MatSetOptionsPrefix(Prol11, prefix)); 668 PetscCall(PetscSNPrintf(addp, sizeof(addp), "pc_gamg_prolongator_%d_", (int)level)); 669 PetscCall(MatAppendOptionsPrefix(Prol11, addp)); 670 /* Always generate the transpose with CUDA 671 Such behaviour can be adapted with -pc_gamg_prolongator_ prefixed options */ 672 PetscCall(MatSetOption(Prol11, MAT_FORM_EXPLICIT_TRANSPOSE, PETSC_TRUE)); 673 PetscCall(MatSetFromOptions(Prol11)); 674 Parr[level1] = Prol11; 675 } else Parr[level1] = NULL; /* failed to coarsen */ 676 677 PetscCall(MatDestroy(&Gmat)); 678 PetscCall(PetscCDDestroy(agg_lists)); 679 } /* construct prolongator scope */ 680 if (!level) Aarr[0] = Pmat; /* use Pmat for finest level setup */ 681 if (!Parr[level1]) { /* failed to coarsen */ 682 PetscCall(PetscInfo(pc, "%s: Stop gridding, level %" PetscInt_FMT "\n", ((PetscObject)pc)->prefix, level)); 683 #if defined(GAMG_STAGES) 684 PetscCall(PetscLogStagePop()); 685 #endif 686 break; 687 } 688 PetscCall(MatGetSize(Parr[level1], &M, &N)); /* N is next M, a loop test variables */ 689 PetscCheck(!is_last, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Is last ?"); 690 if (N <= pc_gamg->coarse_eq_limit) is_last = PETSC_TRUE; 691 if (level1 == pc_gamg->Nlevels - 1) is_last = PETSC_TRUE; 692 PetscCall(PetscLogEventBegin(petsc_gamg_setup_events[GAMG_LEVEL], 0, 0, 0, 0)); 693 PetscCall(pc_gamg->ops->createlevel(pc, Aarr[level], bs, &Parr[level1], &Aarr[level1], &nactivepe, NULL, is_last)); 694 PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_LEVEL], 0, 0, 0, 0)); 695 696 PetscCall(MatGetSize(Aarr[level1], &M, &N)); /* M is loop test variables */ 697 #if defined(PETSC_USE_INFO) 698 PetscCall(MatGetInfo(Aarr[level1], MAT_GLOBAL_SUM, &info)); 699 nnztot += info.nz_used; 700 #endif 701 PetscCall(PetscInfo(pc, "%s: %d) N=%" PetscInt_FMT ", n data cols=%" PetscInt_FMT ", nnz/row (ave)=%" PetscInt_FMT ", %d active pes\n", ((PetscObject)pc)->prefix, (int)level1, M, pc_gamg->data_cell_cols, (PetscInt)(info.nz_used / (PetscReal)M), nactivepe)); 702 703 #if defined(GAMG_STAGES) 704 PetscCall(PetscLogStagePop()); 705 #endif 706 /* stop if one node or one proc -- could pull back for singular problems */ 707 if ((pc_gamg->data_cell_cols && M / pc_gamg->data_cell_cols < 2) || (!pc_gamg->data_cell_cols && M / bs < 2)) { 708 PetscCall(PetscInfo(pc, "%s: HARD stop of coarsening on level %" PetscInt_FMT ". Grid too small: %" PetscInt_FMT " block nodes\n", ((PetscObject)pc)->prefix, level, M / bs)); 709 level++; 710 break; 711 } 712 } /* levels */ 713 PetscCall(PetscFree(pc_gamg->data)); 714 715 PetscCall(PetscInfo(pc, "%s: %" PetscInt_FMT " levels, operator complexity = %g\n", ((PetscObject)pc)->prefix, level + 1, nnztot / nnz0)); 716 pc_gamg->Nlevels = level + 1; 717 fine_level = level; 718 PetscCall(PCMGSetLevels(pc, pc_gamg->Nlevels, NULL)); 719 720 if (pc_gamg->Nlevels > 1) { /* don't setup MG if one level */ 721 722 /* set default smoothers & set operators */ 723 for (lidx = 1, level = pc_gamg->Nlevels - 2; lidx <= fine_level; lidx++, level--) { 724 KSP smoother; 725 PC subpc; 726 727 PetscCall(PCMGGetSmoother(pc, lidx, &smoother)); 728 PetscCall(KSPGetPC(smoother, &subpc)); 729 730 PetscCall(KSPSetNormType(smoother, KSP_NORM_NONE)); 731 /* set ops */ 732 PetscCall(KSPSetOperators(smoother, Aarr[level], Aarr[level])); 733 PetscCall(PCMGSetInterpolation(pc, lidx, Parr[level + 1])); 734 735 /* set defaults */ 736 PetscCall(KSPSetType(smoother, KSPCHEBYSHEV)); 737 738 /* set blocks for ASM smoother that uses the 'aggregates' */ 739 if (pc_gamg->use_aggs_in_asm) { 740 PetscInt sz; 741 IS *iss; 742 743 sz = nASMBlocksArr[level]; 744 iss = ASMLocalIDsArr[level]; 745 PetscCall(PCSetType(subpc, PCASM)); 746 PetscCall(PCASMSetOverlap(subpc, 0)); 747 PetscCall(PCASMSetType(subpc, PC_ASM_BASIC)); 748 if (!sz) { 749 IS is; 750 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 0, NULL, PETSC_COPY_VALUES, &is)); 751 PetscCall(PCASMSetLocalSubdomains(subpc, 1, NULL, &is)); 752 PetscCall(ISDestroy(&is)); 753 } else { 754 PetscInt kk; 755 PetscCall(PCASMSetLocalSubdomains(subpc, sz, iss, NULL)); 756 for (kk = 0; kk < sz; kk++) PetscCall(ISDestroy(&iss[kk])); 757 PetscCall(PetscFree(iss)); 758 } 759 ASMLocalIDsArr[level] = NULL; 760 nASMBlocksArr[level] = 0; 761 } else { 762 PetscCall(PCSetType(subpc, PCJACOBI)); 763 } 764 } 765 { 766 /* coarse grid */ 767 KSP smoother, *k2; 768 PC subpc, pc2; 769 PetscInt ii, first; 770 Mat Lmat = Aarr[(level = pc_gamg->Nlevels - 1)]; 771 lidx = 0; 772 773 PetscCall(PCMGGetSmoother(pc, lidx, &smoother)); 774 PetscCall(KSPSetOperators(smoother, Lmat, Lmat)); 775 if (!pc_gamg->use_parallel_coarse_grid_solver) { 776 PetscCall(KSPSetNormType(smoother, KSP_NORM_NONE)); 777 PetscCall(KSPGetPC(smoother, &subpc)); 778 PetscCall(PCSetType(subpc, PCBJACOBI)); 779 PetscCall(PCSetUp(subpc)); 780 PetscCall(PCBJacobiGetSubKSP(subpc, &ii, &first, &k2)); 781 PetscCheck(ii == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "ii %" PetscInt_FMT " is not one", ii); 782 PetscCall(KSPGetPC(k2[0], &pc2)); 783 PetscCall(PCSetType(pc2, PCLU)); 784 PetscCall(PCFactorSetShiftType(pc2, MAT_SHIFT_INBLOCKS)); 785 PetscCall(KSPSetTolerances(k2[0], PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, 1)); 786 PetscCall(KSPSetType(k2[0], KSPPREONLY)); 787 } 788 } 789 790 /* should be called in PCSetFromOptions_GAMG(), but cannot be called prior to PCMGSetLevels() */ 791 PetscObjectOptionsBegin((PetscObject)pc); 792 PetscCall(PCSetFromOptions_MG(pc, PetscOptionsObject)); 793 PetscOptionsEnd(); 794 PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_EXTERNAL)); 795 796 /* set cheby eigen estimates from SA to use in the solver */ 797 if (pc_gamg->use_sa_esteig) { 798 for (lidx = 1, level = pc_gamg->Nlevels - 2; level >= 0; lidx++, level--) { 799 KSP smoother; 800 PetscBool ischeb; 801 802 PetscCall(PCMGGetSmoother(pc, lidx, &smoother)); 803 PetscCall(PetscObjectTypeCompare((PetscObject)smoother, KSPCHEBYSHEV, &ischeb)); 804 if (ischeb) { 805 KSP_Chebyshev *cheb = (KSP_Chebyshev *)smoother->data; 806 807 // The command line will override these settings because KSPSetFromOptions is called in PCSetUp_MG 808 if (mg->max_eigen_DinvA[level] > 0) { 809 // SA uses Jacobi for P; we use SA estimates if the smoother is also Jacobi or if the user explicitly requested it. 810 // TODO: This should test whether it's the same Jacobi variant (DIAG, ROWSUM, etc.) 811 PetscReal emax, emin; 812 813 emin = mg->min_eigen_DinvA[level]; 814 emax = mg->max_eigen_DinvA[level]; 815 PetscCall(PetscInfo(pc, "%s: PCSetUp_GAMG: call KSPChebyshevSetEigenvalues on level %" PetscInt_FMT " (N=%" PetscInt_FMT ") with emax = %g emin = %g\n", ((PetscObject)pc)->prefix, level, Aarr[level]->rmap->N, (double)emax, (double)emin)); 816 cheb->emin_provided = emin; 817 cheb->emax_provided = emax; 818 } 819 } 820 } 821 } 822 823 PetscCall(PCSetUp_MG(pc)); 824 825 /* clean up */ 826 for (level = 1; level < pc_gamg->Nlevels; level++) { 827 PetscCall(MatDestroy(&Parr[level])); 828 PetscCall(MatDestroy(&Aarr[level])); 829 } 830 } else { 831 KSP smoother; 832 833 PetscCall(PetscInfo(pc, "%s: One level solver used (system is seen as DD). Using default solver.\n", ((PetscObject)pc)->prefix)); 834 PetscCall(PCMGGetSmoother(pc, 0, &smoother)); 835 PetscCall(KSPSetOperators(smoother, Aarr[0], Aarr[0])); 836 PetscCall(KSPSetType(smoother, KSPPREONLY)); 837 PetscCall(PCSetUp_MG(pc)); 838 } 839 PetscCall(PetscLogEventEnd(petsc_gamg_setup_events[GAMG_SETUP], 0, 0, 0, 0)); 840 PetscFunctionReturn(PETSC_SUCCESS); 841 } 842 843 /* 844 PCDestroy_GAMG - Destroys the private context for the GAMG preconditioner 845 that was created with PCCreate_GAMG(). 846 847 Input Parameter: 848 . pc - the preconditioner context 849 850 Application Interface Routine: PCDestroy() 851 */ 852 PetscErrorCode PCDestroy_GAMG(PC pc) 853 { 854 PC_MG *mg = (PC_MG *)pc->data; 855 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 856 857 PetscFunctionBegin; 858 PetscCall(PCReset_GAMG(pc)); 859 if (pc_gamg->ops->destroy) PetscCall((*pc_gamg->ops->destroy)(pc)); 860 PetscCall(PetscFree(pc_gamg->ops)); 861 PetscCall(PetscFree(pc_gamg->gamg_type_name)); 862 PetscCall(PetscFree(pc_gamg)); 863 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetProcEqLim_C", NULL)); 864 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseEqLim_C", NULL)); 865 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRepartition_C", NULL)); 866 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetEigenvalues_C", NULL)); 867 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetUseSAEstEig_C", NULL)); 868 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRecomputeEstEig_C", NULL)); 869 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetReuseInterpolation_C", NULL)); 870 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGASMSetUseAggs_C", NULL)); 871 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetParallelCoarseGridSolve_C", NULL)); 872 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCpuPinCoarseGrids_C", NULL)); 873 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseGridLayoutType_C", NULL)); 874 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThreshold_C", NULL)); 875 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRankReductionFactors_C", NULL)); 876 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThresholdScale_C", NULL)); 877 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetType_C", NULL)); 878 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGGetType_C", NULL)); 879 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetNlevels_C", NULL)); 880 PetscCall(PCDestroy_MG(pc)); 881 PetscFunctionReturn(PETSC_SUCCESS); 882 } 883 884 /*@ 885 PCGAMGSetProcEqLim - Set number of equations to aim for per process on the coarse grids via processor reduction in `PCGAMG` 886 887 Logically Collective 888 889 Input Parameters: 890 + pc - the preconditioner context 891 - n - the number of equations 892 893 Options Database Key: 894 . -pc_gamg_process_eq_limit <limit> - set the limit 895 896 Level: intermediate 897 898 Note: 899 `PCGAMG` will reduce the number of MPI processes used directly on the coarse grids so that there are around <limit> equations on each process 900 that has degrees of freedom 901 902 .seealso: `PCGAMG`, `PCGAMGSetCoarseEqLim()`, `PCGAMGSetRankReductionFactors()`, `PCGAMGSetRepartition()` 903 @*/ 904 PetscErrorCode PCGAMGSetProcEqLim(PC pc, PetscInt n) 905 { 906 PetscFunctionBegin; 907 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 908 PetscTryMethod(pc, "PCGAMGSetProcEqLim_C", (PC, PetscInt), (pc, n)); 909 PetscFunctionReturn(PETSC_SUCCESS); 910 } 911 912 static PetscErrorCode PCGAMGSetProcEqLim_GAMG(PC pc, PetscInt n) 913 { 914 PC_MG *mg = (PC_MG *)pc->data; 915 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 916 917 PetscFunctionBegin; 918 if (n > 0) pc_gamg->min_eq_proc = n; 919 PetscFunctionReturn(PETSC_SUCCESS); 920 } 921 922 /*@ 923 PCGAMGSetCoarseEqLim - Set maximum number of equations on the coarsest grid of `PCGAMG` 924 925 Collective 926 927 Input Parameters: 928 + pc - the preconditioner context 929 - n - maximum number of equations to aim for 930 931 Options Database Key: 932 . -pc_gamg_coarse_eq_limit <limit> - set the limit 933 934 Level: intermediate 935 936 Note: 937 For example -pc_gamg_coarse_eq_limit 1000 will stop coarsening once the coarse grid 938 has less than 1000 unknowns. 939 940 .seealso: `PCGAMG`, `PCGAMGSetProcEqLim()`, `PCGAMGSetRankReductionFactors()`, `PCGAMGSetRepartition()` 941 @*/ 942 PetscErrorCode PCGAMGSetCoarseEqLim(PC pc, PetscInt n) 943 { 944 PetscFunctionBegin; 945 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 946 PetscTryMethod(pc, "PCGAMGSetCoarseEqLim_C", (PC, PetscInt), (pc, n)); 947 PetscFunctionReturn(PETSC_SUCCESS); 948 } 949 950 static PetscErrorCode PCGAMGSetCoarseEqLim_GAMG(PC pc, PetscInt n) 951 { 952 PC_MG *mg = (PC_MG *)pc->data; 953 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 954 955 PetscFunctionBegin; 956 if (n > 0) pc_gamg->coarse_eq_limit = n; 957 PetscFunctionReturn(PETSC_SUCCESS); 958 } 959 960 /*@ 961 PCGAMGSetRepartition - Repartition the degrees of freedom across the processors on the coarser grids when reducing the number of MPI ranks to use 962 963 Collective 964 965 Input Parameters: 966 + pc - the preconditioner context 967 - n - `PETSC_TRUE` or `PETSC_FALSE` 968 969 Options Database Key: 970 . -pc_gamg_repartition <true,false> - turn on the repartitioning 971 972 Level: intermediate 973 974 Note: 975 This will generally improve the loading balancing of the work on each level 976 977 .seealso: `PCGAMG`, `PCGAMGSetProcEqLim()`, `PCGAMGSetRankReductionFactors()` 978 @*/ 979 PetscErrorCode PCGAMGSetRepartition(PC pc, PetscBool n) 980 { 981 PetscFunctionBegin; 982 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 983 PetscTryMethod(pc, "PCGAMGSetRepartition_C", (PC, PetscBool), (pc, n)); 984 PetscFunctionReturn(PETSC_SUCCESS); 985 } 986 987 static PetscErrorCode PCGAMGSetRepartition_GAMG(PC pc, PetscBool n) 988 { 989 PC_MG *mg = (PC_MG *)pc->data; 990 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 991 992 PetscFunctionBegin; 993 pc_gamg->repart = n; 994 PetscFunctionReturn(PETSC_SUCCESS); 995 } 996 997 /*@ 998 PCGAMGSetUseSAEstEig - Use the eigen estimate from smoothed aggregation for the Chebyshev smoother during the solution process 999 1000 Collective 1001 1002 Input Parameters: 1003 + pc - the preconditioner context 1004 - b - flag 1005 1006 Options Database Key: 1007 . -pc_gamg_use_sa_esteig <true,false> - use the eigen estimate 1008 1009 Level: advanced 1010 1011 Notes: 1012 Smoothed aggregation constructs the smoothed prolongator $P = (I - \omega D^{-1} A) T$ where $T$ is the tentative prolongator and $D$ is the diagonal of $A$. 1013 Eigenvalue estimates (based on a few `PCCG` or `PCGMRES` iterations) are computed to choose $\omega$ so that this is a stable smoothing operation. 1014 If Chebyshev with Jacobi (diagonal) preconditioning is used for smoothing, then the eigenvalue estimates can be reused during the solution process 1015 This option is only used when the smoother uses Jacobi, and should be turned off if a different `PCJacobiType` is used. 1016 It became default in PETSc 3.17. 1017 1018 .seealso: `PCGAMG`, `KSPChebyshevSetEigenvalues()`, `KSPChebyshevEstEigSet()`, `PCGAMGSetRecomputeEstEig()` 1019 @*/ 1020 PetscErrorCode PCGAMGSetUseSAEstEig(PC pc, PetscBool b) 1021 { 1022 PetscFunctionBegin; 1023 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1024 PetscTryMethod(pc, "PCGAMGSetUseSAEstEig_C", (PC, PetscBool), (pc, b)); 1025 PetscFunctionReturn(PETSC_SUCCESS); 1026 } 1027 1028 static PetscErrorCode PCGAMGSetUseSAEstEig_GAMG(PC pc, PetscBool b) 1029 { 1030 PC_MG *mg = (PC_MG *)pc->data; 1031 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1032 1033 PetscFunctionBegin; 1034 pc_gamg->use_sa_esteig = b; 1035 PetscFunctionReturn(PETSC_SUCCESS); 1036 } 1037 1038 /*@ 1039 PCGAMGSetRecomputeEstEig - Set flag for Chebyshev smoothers to recompute the eigen estimates 1040 1041 Collective 1042 1043 Input Parameters: 1044 + pc - the preconditioner context 1045 - b - flag 1046 1047 Options Database Key: 1048 . -pc_gamg_recompute_esteig <true> - use the eigen estimate 1049 1050 Level: advanced 1051 1052 .seealso: `PCGAMG`, `KSPChebyshevSetEigenvalues()`, `KSPChebyshevEstEigSet()` 1053 @*/ 1054 PetscErrorCode PCGAMGSetRecomputeEstEig(PC pc, PetscBool b) 1055 { 1056 PetscFunctionBegin; 1057 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1058 PetscTryMethod(pc, "PCGAMGSetRecomputeEstEig_C", (PC, PetscBool), (pc, b)); 1059 PetscFunctionReturn(PETSC_SUCCESS); 1060 } 1061 1062 static PetscErrorCode PCGAMGSetRecomputeEstEig_GAMG(PC pc, PetscBool b) 1063 { 1064 PC_MG *mg = (PC_MG *)pc->data; 1065 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1066 1067 PetscFunctionBegin; 1068 pc_gamg->recompute_esteig = b; 1069 PetscFunctionReturn(PETSC_SUCCESS); 1070 } 1071 1072 /*@ 1073 PCGAMGSetEigenvalues - Set WHAT eigenvalues WHY? 1074 1075 Collective 1076 1077 Input Parameters: 1078 + pc - the preconditioner context 1079 . emax - max eigenvalue 1080 - emin - min eigenvalue 1081 1082 Options Database Key: 1083 . -pc_gamg_eigenvalues <emin,emax> - estimates of the eigenvalues 1084 1085 Level: intermediate 1086 1087 .seealso: `PCGAMG`, `PCGAMGSetUseSAEstEig()` 1088 @*/ 1089 PetscErrorCode PCGAMGSetEigenvalues(PC pc, PetscReal emax, PetscReal emin) 1090 { 1091 PetscFunctionBegin; 1092 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1093 PetscTryMethod(pc, "PCGAMGSetEigenvalues_C", (PC, PetscReal, PetscReal), (pc, emax, emin)); 1094 PetscFunctionReturn(PETSC_SUCCESS); 1095 } 1096 1097 static PetscErrorCode PCGAMGSetEigenvalues_GAMG(PC pc, PetscReal emax, PetscReal emin) 1098 { 1099 PC_MG *mg = (PC_MG *)pc->data; 1100 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1101 1102 PetscFunctionBegin; 1103 PetscCheck(emax > emin, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "Maximum eigenvalue must be larger than minimum: max %g min %g", (double)emax, (double)emin); 1104 PetscCheck(emax * emin > 0.0, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "Both eigenvalues must be of the same sign: max %g min %g", (double)emax, (double)emin); 1105 pc_gamg->emax = emax; 1106 pc_gamg->emin = emin; 1107 PetscFunctionReturn(PETSC_SUCCESS); 1108 } 1109 1110 /*@ 1111 PCGAMGSetReuseInterpolation - Reuse prolongation when rebuilding a `PCGAMG` algebraic multigrid preconditioner 1112 1113 Collective 1114 1115 Input Parameters: 1116 + pc - the preconditioner context 1117 - n - `PETSC_TRUE` or `PETSC_FALSE` 1118 1119 Options Database Key: 1120 . -pc_gamg_reuse_interpolation <true,false> - reuse the previous interpolation 1121 1122 Level: intermediate 1123 1124 Note: 1125 May negatively affect the convergence rate of the method on new matrices if the matrix entries change a great deal, but allows 1126 rebuilding the preconditioner quicker. 1127 1128 .seealso: `PCGAMG` 1129 @*/ 1130 PetscErrorCode PCGAMGSetReuseInterpolation(PC pc, PetscBool n) 1131 { 1132 PetscFunctionBegin; 1133 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1134 PetscTryMethod(pc, "PCGAMGSetReuseInterpolation_C", (PC, PetscBool), (pc, n)); 1135 PetscFunctionReturn(PETSC_SUCCESS); 1136 } 1137 1138 static PetscErrorCode PCGAMGSetReuseInterpolation_GAMG(PC pc, PetscBool n) 1139 { 1140 PC_MG *mg = (PC_MG *)pc->data; 1141 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1142 1143 PetscFunctionBegin; 1144 pc_gamg->reuse_prol = n; 1145 PetscFunctionReturn(PETSC_SUCCESS); 1146 } 1147 1148 /*@ 1149 PCGAMGASMSetUseAggs - Have the `PCGAMG` smoother on each level use the aggregates defined by the coarsening process as the subdomains for the additive Schwarz preconditioner 1150 used as the smoother 1151 1152 Collective 1153 1154 Input Parameters: 1155 + pc - the preconditioner context 1156 - flg - `PETSC_TRUE` to use aggregates, `PETSC_FALSE` to not 1157 1158 Options Database Key: 1159 . -pc_gamg_asm_use_agg <true,false> - use aggregates to define the additive Schwarz subdomains 1160 1161 Level: intermediate 1162 1163 .seealso: `PCGAMG`, `PCASM`, `PCSetType` 1164 @*/ 1165 PetscErrorCode PCGAMGASMSetUseAggs(PC pc, PetscBool flg) 1166 { 1167 PetscFunctionBegin; 1168 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1169 PetscTryMethod(pc, "PCGAMGASMSetUseAggs_C", (PC, PetscBool), (pc, flg)); 1170 PetscFunctionReturn(PETSC_SUCCESS); 1171 } 1172 1173 static PetscErrorCode PCGAMGASMSetUseAggs_GAMG(PC pc, PetscBool flg) 1174 { 1175 PC_MG *mg = (PC_MG *)pc->data; 1176 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1177 1178 PetscFunctionBegin; 1179 pc_gamg->use_aggs_in_asm = flg; 1180 PetscFunctionReturn(PETSC_SUCCESS); 1181 } 1182 1183 /*@ 1184 PCGAMGSetParallelCoarseGridSolve - allow a parallel coarse grid solver 1185 1186 Collective 1187 1188 Input Parameters: 1189 + pc - the preconditioner context 1190 - flg - `PETSC_TRUE` to not force coarse grid onto one processor 1191 1192 Options Database Key: 1193 . -pc_gamg_parallel_coarse_grid_solver - use a parallel coarse grid direct solver 1194 1195 Level: intermediate 1196 1197 .seealso: `PCGAMG`, `PCGAMGSetCoarseGridLayoutType()`, `PCGAMGSetCpuPinCoarseGrids()` 1198 @*/ 1199 PetscErrorCode PCGAMGSetParallelCoarseGridSolve(PC pc, PetscBool flg) 1200 { 1201 PetscFunctionBegin; 1202 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1203 PetscTryMethod(pc, "PCGAMGSetParallelCoarseGridSolve_C", (PC, PetscBool), (pc, flg)); 1204 PetscFunctionReturn(PETSC_SUCCESS); 1205 } 1206 1207 static PetscErrorCode PCGAMGSetParallelCoarseGridSolve_GAMG(PC pc, PetscBool flg) 1208 { 1209 PC_MG *mg = (PC_MG *)pc->data; 1210 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1211 1212 PetscFunctionBegin; 1213 pc_gamg->use_parallel_coarse_grid_solver = flg; 1214 PetscFunctionReturn(PETSC_SUCCESS); 1215 } 1216 1217 /*@ 1218 PCGAMGSetCpuPinCoarseGrids - pin the coarse grids created in `PCGAMG` to run only on the CPU since the problems may be too small to run efficiently on the GPUs 1219 1220 Collective 1221 1222 Input Parameters: 1223 + pc - the preconditioner context 1224 - flg - `PETSC_TRUE` to pin coarse grids to the CPU 1225 1226 Options Database Key: 1227 . -pc_gamg_cpu_pin_coarse_grids - pin the coarse grids to the CPU 1228 1229 Level: intermediate 1230 1231 .seealso: `PCGAMG`, `PCGAMGSetCoarseGridLayoutType()`, `PCGAMGSetParallelCoarseGridSolve()` 1232 @*/ 1233 PetscErrorCode PCGAMGSetCpuPinCoarseGrids(PC pc, PetscBool flg) 1234 { 1235 PetscFunctionBegin; 1236 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1237 PetscTryMethod(pc, "PCGAMGSetCpuPinCoarseGrids_C", (PC, PetscBool), (pc, flg)); 1238 PetscFunctionReturn(PETSC_SUCCESS); 1239 } 1240 1241 static PetscErrorCode PCGAMGSetCpuPinCoarseGrids_GAMG(PC pc, PetscBool flg) 1242 { 1243 PC_MG *mg = (PC_MG *)pc->data; 1244 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1245 1246 PetscFunctionBegin; 1247 pc_gamg->cpu_pin_coarse_grids = flg; 1248 PetscFunctionReturn(PETSC_SUCCESS); 1249 } 1250 1251 /*@ 1252 PCGAMGSetCoarseGridLayoutType - place coarse grids on processors with natural order (compact type) 1253 1254 Collective 1255 1256 Input Parameters: 1257 + pc - the preconditioner context 1258 - flg - `PCGAMGLayoutType` type, either `PCGAMG_LAYOUT_COMPACT` or `PCGAMG_LAYOUT_SPREAD` 1259 1260 Options Database Key: 1261 . -pc_gamg_coarse_grid_layout_type - place the coarse grids with natural ordering 1262 1263 Level: intermediate 1264 1265 .seealso: `PCGAMG`, `PCGAMGSetParallelCoarseGridSolve()`, `PCGAMGSetCpuPinCoarseGrids()`, `PCGAMGLayoutType`, `PCGAMG_LAYOUT_COMPACT`, `PCGAMG_LAYOUT_SPREAD` 1266 @*/ 1267 PetscErrorCode PCGAMGSetCoarseGridLayoutType(PC pc, PCGAMGLayoutType flg) 1268 { 1269 PetscFunctionBegin; 1270 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1271 PetscTryMethod(pc, "PCGAMGSetCoarseGridLayoutType_C", (PC, PCGAMGLayoutType), (pc, flg)); 1272 PetscFunctionReturn(PETSC_SUCCESS); 1273 } 1274 1275 static PetscErrorCode PCGAMGSetCoarseGridLayoutType_GAMG(PC pc, PCGAMGLayoutType flg) 1276 { 1277 PC_MG *mg = (PC_MG *)pc->data; 1278 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1279 1280 PetscFunctionBegin; 1281 pc_gamg->layout_type = flg; 1282 PetscFunctionReturn(PETSC_SUCCESS); 1283 } 1284 1285 /*@ 1286 PCGAMGSetNlevels - Sets the maximum number of levels `PCGAMG` will use 1287 1288 Not Collective 1289 1290 Input Parameters: 1291 + pc - the preconditioner 1292 - n - the maximum number of levels to use 1293 1294 Options Database Key: 1295 . -pc_mg_levels <n> - set the maximum number of levels to allow 1296 1297 Level: intermediate 1298 1299 Developer Notes: 1300 Should be called `PCGAMGSetMaximumNumberlevels()` and possible be shared with `PCMG` 1301 1302 .seealso: `PCGAMG` 1303 @*/ 1304 PetscErrorCode PCGAMGSetNlevels(PC pc, PetscInt n) 1305 { 1306 PetscFunctionBegin; 1307 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1308 PetscTryMethod(pc, "PCGAMGSetNlevels_C", (PC, PetscInt), (pc, n)); 1309 PetscFunctionReturn(PETSC_SUCCESS); 1310 } 1311 1312 static PetscErrorCode PCGAMGSetNlevels_GAMG(PC pc, PetscInt n) 1313 { 1314 PC_MG *mg = (PC_MG *)pc->data; 1315 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1316 1317 PetscFunctionBegin; 1318 pc_gamg->Nlevels = n; 1319 PetscFunctionReturn(PETSC_SUCCESS); 1320 } 1321 1322 /*@ 1323 PCGAMGSetThreshold - Relative threshold to use for dropping edges in aggregation graph 1324 1325 Not Collective 1326 1327 Input Parameters: 1328 + pc - the preconditioner context 1329 . v - array of threshold values for finest n levels; 0.0 means keep all nonzero entries in the graph; negative means keep even zero entries in the graph 1330 - n - number of threshold values provided in array 1331 1332 Options Database Key: 1333 . -pc_gamg_threshold <threshold> - the threshold to drop edges 1334 1335 Level: intermediate 1336 1337 Notes: 1338 Increasing the threshold decreases the rate of coarsening. Conversely reducing the threshold increases the rate of coarsening (aggressive coarsening) and thereby reduces the complexity of the coarse grids, and generally results in slower solver converge rates. Reducing coarse grid complexity reduced the complexity of Galerkin coarse grid construction considerably. 1339 Before coarsening or aggregating the graph, `PCGAMG` removes small values from the graph with this threshold, and thus reducing the coupling in the graph and a different (perhaps better) coarser set of points. 1340 1341 If `n` is less than the total number of coarsenings (see `PCGAMGSetNlevels()`), then threshold scaling (see `PCGAMGSetThresholdScale()`) is used for each successive coarsening. 1342 In this case, `PCGAMGSetThresholdScale()` must be called before `PCGAMGSetThreshold()`. 1343 If `n` is greater than the total number of levels, the excess entries in threshold will not be used. 1344 1345 .seealso: `PCGAMG`, `PCGAMGSetAggressiveLevels()`, `PCGAMGMISkSetAggressive()`, `PCGAMGSetMinDegreeOrderingMISk()`, `PCGAMGSetThresholdScale()` 1346 @*/ 1347 PetscErrorCode PCGAMGSetThreshold(PC pc, PetscReal v[], PetscInt n) 1348 { 1349 PetscFunctionBegin; 1350 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1351 if (n) PetscAssertPointer(v, 2); 1352 PetscTryMethod(pc, "PCGAMGSetThreshold_C", (PC, PetscReal[], PetscInt), (pc, v, n)); 1353 PetscFunctionReturn(PETSC_SUCCESS); 1354 } 1355 1356 static PetscErrorCode PCGAMGSetThreshold_GAMG(PC pc, PetscReal v[], PetscInt n) 1357 { 1358 PC_MG *mg = (PC_MG *)pc->data; 1359 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1360 PetscInt i; 1361 PetscFunctionBegin; 1362 for (i = 0; i < PetscMin(n, PETSC_MG_MAXLEVELS); i++) pc_gamg->threshold[i] = v[i]; 1363 for (; i < PETSC_MG_MAXLEVELS; i++) pc_gamg->threshold[i] = pc_gamg->threshold[i - 1] * pc_gamg->threshold_scale; 1364 PetscFunctionReturn(PETSC_SUCCESS); 1365 } 1366 1367 /*@ 1368 PCGAMGSetRankReductionFactors - Set a manual schedule for MPI rank reduction on coarse grids 1369 1370 Collective 1371 1372 Input Parameters: 1373 + pc - the preconditioner context 1374 . v - array of reduction factors. 0 for first value forces a reduction to one process/device on first level in CUDA 1375 - n - number of values provided in array 1376 1377 Options Database Key: 1378 . -pc_gamg_rank_reduction_factors <factors> - provide the schedule 1379 1380 Level: intermediate 1381 1382 .seealso: `PCGAMG`, `PCGAMGSetProcEqLim()`, `PCGAMGSetCoarseEqLim()` 1383 @*/ 1384 PetscErrorCode PCGAMGSetRankReductionFactors(PC pc, PetscInt v[], PetscInt n) 1385 { 1386 PetscFunctionBegin; 1387 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1388 if (n) PetscAssertPointer(v, 2); 1389 PetscTryMethod(pc, "PCGAMGSetRankReductionFactors_C", (PC, PetscInt[], PetscInt), (pc, v, n)); 1390 PetscFunctionReturn(PETSC_SUCCESS); 1391 } 1392 1393 static PetscErrorCode PCGAMGSetRankReductionFactors_GAMG(PC pc, PetscInt v[], PetscInt n) 1394 { 1395 PC_MG *mg = (PC_MG *)pc->data; 1396 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1397 PetscInt i; 1398 PetscFunctionBegin; 1399 for (i = 0; i < PetscMin(n, PETSC_MG_MAXLEVELS); i++) pc_gamg->level_reduction_factors[i] = v[i]; 1400 for (; i < PETSC_MG_MAXLEVELS; i++) pc_gamg->level_reduction_factors[i] = -1; /* 0 stop putting one process/device on first level */ 1401 PetscFunctionReturn(PETSC_SUCCESS); 1402 } 1403 1404 /*@ 1405 PCGAMGSetThresholdScale - Relative threshold reduction at each level 1406 1407 Not Collective 1408 1409 Input Parameters: 1410 + pc - the preconditioner context 1411 - v - the threshold value reduction, usually < 1.0 1412 1413 Options Database Key: 1414 . -pc_gamg_threshold_scale <v> - set the relative threshold reduction on each level 1415 1416 Level: advanced 1417 1418 Note: 1419 The initial threshold (for an arbitrary number of levels starting from the finest) can be set with `PCGAMGSetThreshold()`. 1420 This scaling is used for each subsequent coarsening, but must be called before `PCGAMGSetThreshold()`. 1421 1422 .seealso: `PCGAMG`, `PCGAMGSetThreshold()` 1423 @*/ 1424 PetscErrorCode PCGAMGSetThresholdScale(PC pc, PetscReal v) 1425 { 1426 PetscFunctionBegin; 1427 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1428 PetscTryMethod(pc, "PCGAMGSetThresholdScale_C", (PC, PetscReal), (pc, v)); 1429 PetscFunctionReturn(PETSC_SUCCESS); 1430 } 1431 1432 static PetscErrorCode PCGAMGSetThresholdScale_GAMG(PC pc, PetscReal v) 1433 { 1434 PC_MG *mg = (PC_MG *)pc->data; 1435 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1436 PetscFunctionBegin; 1437 pc_gamg->threshold_scale = v; 1438 PetscFunctionReturn(PETSC_SUCCESS); 1439 } 1440 1441 /*@C 1442 PCGAMGSetType - Set the type of algorithm `PCGAMG` should use 1443 1444 Collective 1445 1446 Input Parameters: 1447 + pc - the preconditioner context 1448 - type - `PCGAMGAGG`, `PCGAMGGEO`, or `PCGAMGCLASSICAL` 1449 1450 Options Database Key: 1451 . -pc_gamg_type <agg,geo,classical> - type of algebraic multigrid to apply - only agg supported 1452 1453 Level: intermediate 1454 1455 .seealso: `PCGAMGGetType()`, `PCGAMG`, `PCGAMGType` 1456 @*/ 1457 PetscErrorCode PCGAMGSetType(PC pc, PCGAMGType type) 1458 { 1459 PetscFunctionBegin; 1460 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1461 PetscTryMethod(pc, "PCGAMGSetType_C", (PC, PCGAMGType), (pc, type)); 1462 PetscFunctionReturn(PETSC_SUCCESS); 1463 } 1464 1465 /*@C 1466 PCGAMGGetType - Get the type of algorithm `PCGAMG` will use 1467 1468 Collective 1469 1470 Input Parameter: 1471 . pc - the preconditioner context 1472 1473 Output Parameter: 1474 . type - the type of algorithm used 1475 1476 Level: intermediate 1477 1478 .seealso: `PCGAMG`, `PCGAMGSetType()`, `PCGAMGType` 1479 @*/ 1480 PetscErrorCode PCGAMGGetType(PC pc, PCGAMGType *type) 1481 { 1482 PetscFunctionBegin; 1483 PetscValidHeaderSpecific(pc, PC_CLASSID, 1); 1484 PetscUseMethod(pc, "PCGAMGGetType_C", (PC, PCGAMGType *), (pc, type)); 1485 PetscFunctionReturn(PETSC_SUCCESS); 1486 } 1487 1488 static PetscErrorCode PCGAMGGetType_GAMG(PC pc, PCGAMGType *type) 1489 { 1490 PC_MG *mg = (PC_MG *)pc->data; 1491 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1492 1493 PetscFunctionBegin; 1494 *type = pc_gamg->type; 1495 PetscFunctionReturn(PETSC_SUCCESS); 1496 } 1497 1498 static PetscErrorCode PCGAMGSetType_GAMG(PC pc, PCGAMGType type) 1499 { 1500 PC_MG *mg = (PC_MG *)pc->data; 1501 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1502 PetscErrorCode (*r)(PC); 1503 1504 PetscFunctionBegin; 1505 pc_gamg->type = type; 1506 PetscCall(PetscFunctionListFind(GAMGList, type, &r)); 1507 PetscCheck(r, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown GAMG type %s given", type); 1508 if (pc_gamg->ops->destroy) { 1509 PetscCall((*pc_gamg->ops->destroy)(pc)); 1510 PetscCall(PetscMemzero(pc_gamg->ops, sizeof(struct _PCGAMGOps))); 1511 pc_gamg->ops->createlevel = PCGAMGCreateLevel_GAMG; 1512 /* cleaning up common data in pc_gamg - this should disappear someday */ 1513 pc_gamg->data_cell_cols = 0; 1514 pc_gamg->data_cell_rows = 0; 1515 pc_gamg->orig_data_cell_cols = 0; 1516 pc_gamg->orig_data_cell_rows = 0; 1517 PetscCall(PetscFree(pc_gamg->data)); 1518 pc_gamg->data_sz = 0; 1519 } 1520 PetscCall(PetscFree(pc_gamg->gamg_type_name)); 1521 PetscCall(PetscStrallocpy(type, &pc_gamg->gamg_type_name)); 1522 PetscCall((*r)(pc)); 1523 PetscFunctionReturn(PETSC_SUCCESS); 1524 } 1525 1526 static PetscErrorCode PCView_GAMG(PC pc, PetscViewer viewer) 1527 { 1528 PC_MG *mg = (PC_MG *)pc->data; 1529 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1530 PetscReal gc = 0, oc = 0; 1531 1532 PetscFunctionBegin; 1533 PetscCall(PetscViewerASCIIPrintf(viewer, " GAMG specific options\n")); 1534 PetscCall(PetscViewerASCIIPrintf(viewer, " Threshold for dropping small values in graph on each level =")); 1535 for (PetscInt i = 0; i < mg->nlevels; i++) PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)pc_gamg->threshold[i])); 1536 PetscCall(PetscViewerASCIIPrintf(viewer, "\n")); 1537 PetscCall(PetscViewerASCIIPrintf(viewer, " Threshold scaling factor for each level not specified = %g\n", (double)pc_gamg->threshold_scale)); 1538 if (pc_gamg->use_aggs_in_asm) PetscCall(PetscViewerASCIIPrintf(viewer, " Using aggregates from coarsening process to define subdomains for PCASM\n")); 1539 if (pc_gamg->use_parallel_coarse_grid_solver) PetscCall(PetscViewerASCIIPrintf(viewer, " Using parallel coarse grid solver (all coarse grid equations not put on one process)\n")); 1540 if (pc_gamg->ops->view) PetscCall((*pc_gamg->ops->view)(pc, viewer)); 1541 PetscCall(PCMGGetGridComplexity(pc, &gc, &oc)); 1542 PetscCall(PetscViewerASCIIPrintf(viewer, " Complexity: grid = %g operator = %g\n", (double)gc, (double)oc)); 1543 PetscFunctionReturn(PETSC_SUCCESS); 1544 } 1545 1546 static PetscErrorCode PCSetFromOptions_GAMG(PC pc, PetscOptionItems *PetscOptionsObject) 1547 { 1548 PC_MG *mg = (PC_MG *)pc->data; 1549 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1550 PetscBool flag; 1551 MPI_Comm comm; 1552 char prefix[256], tname[32]; 1553 PetscInt i, n; 1554 const char *pcpre; 1555 static const char *LayoutTypes[] = {"compact", "spread", "PCGAMGLayoutType", "PC_GAMG_LAYOUT", NULL}; 1556 PetscFunctionBegin; 1557 PetscCall(PetscObjectGetComm((PetscObject)pc, &comm)); 1558 PetscOptionsHeadBegin(PetscOptionsObject, "GAMG options"); 1559 PetscCall(PetscOptionsFList("-pc_gamg_type", "Type of AMG method (only 'agg' supported and useful)", "PCGAMGSetType", GAMGList, pc_gamg->gamg_type_name, tname, sizeof(tname), &flag)); 1560 if (flag) PetscCall(PCGAMGSetType(pc, tname)); 1561 PetscCall(PetscOptionsBool("-pc_gamg_repartition", "Repartion coarse grids", "PCGAMGSetRepartition", pc_gamg->repart, &pc_gamg->repart, NULL)); 1562 PetscCall(PetscOptionsBool("-pc_gamg_use_sa_esteig", "Use eigen estimate from smoothed aggregation for smoother", "PCGAMGSetUseSAEstEig", pc_gamg->use_sa_esteig, &pc_gamg->use_sa_esteig, NULL)); 1563 PetscCall(PetscOptionsBool("-pc_gamg_recompute_esteig", "Set flag to recompute eigen estimates for Chebyshev when matrix changes", "PCGAMGSetRecomputeEstEig", pc_gamg->recompute_esteig, &pc_gamg->recompute_esteig, NULL)); 1564 PetscCall(PetscOptionsBool("-pc_gamg_reuse_interpolation", "Reuse prolongation operator", "PCGAMGReuseInterpolation", pc_gamg->reuse_prol, &pc_gamg->reuse_prol, NULL)); 1565 PetscCall(PetscOptionsBool("-pc_gamg_asm_use_agg", "Use aggregation aggregates for ASM smoother", "PCGAMGASMSetUseAggs", pc_gamg->use_aggs_in_asm, &pc_gamg->use_aggs_in_asm, NULL)); 1566 PetscCall(PetscOptionsBool("-pc_gamg_parallel_coarse_grid_solver", "Use parallel coarse grid solver (otherwise put last grid on one process)", "PCGAMGSetParallelCoarseGridSolve", pc_gamg->use_parallel_coarse_grid_solver, &pc_gamg->use_parallel_coarse_grid_solver, NULL)); 1567 PetscCall(PetscOptionsBool("-pc_gamg_cpu_pin_coarse_grids", "Pin coarse grids to the CPU", "PCGAMGSetCpuPinCoarseGrids", pc_gamg->cpu_pin_coarse_grids, &pc_gamg->cpu_pin_coarse_grids, NULL)); 1568 PetscCall(PetscOptionsEnum("-pc_gamg_coarse_grid_layout_type", "compact: place reduced grids on processes in natural order; spread: distribute to whole machine for more memory bandwidth", "PCGAMGSetCoarseGridLayoutType", LayoutTypes, 1569 (PetscEnum)pc_gamg->layout_type, (PetscEnum *)&pc_gamg->layout_type, NULL)); 1570 PetscCall(PetscOptionsInt("-pc_gamg_process_eq_limit", "Limit (goal) on number of equations per process on coarse grids", "PCGAMGSetProcEqLim", pc_gamg->min_eq_proc, &pc_gamg->min_eq_proc, NULL)); 1571 PetscCall(PetscOptionsInt("-pc_gamg_coarse_eq_limit", "Limit on number of equations for the coarse grid", "PCGAMGSetCoarseEqLim", pc_gamg->coarse_eq_limit, &pc_gamg->coarse_eq_limit, NULL)); 1572 PetscCall(PetscOptionsReal("-pc_gamg_threshold_scale", "Scaling of threshold for each level not specified", "PCGAMGSetThresholdScale", pc_gamg->threshold_scale, &pc_gamg->threshold_scale, NULL)); 1573 n = PETSC_MG_MAXLEVELS; 1574 PetscCall(PetscOptionsRealArray("-pc_gamg_threshold", "Relative threshold to use for dropping edges in aggregation graph", "PCGAMGSetThreshold", pc_gamg->threshold, &n, &flag)); 1575 if (!flag || n < PETSC_MG_MAXLEVELS) { 1576 if (!flag) n = 1; 1577 i = n; 1578 do { 1579 pc_gamg->threshold[i] = pc_gamg->threshold[i - 1] * pc_gamg->threshold_scale; 1580 } while (++i < PETSC_MG_MAXLEVELS); 1581 } 1582 n = PETSC_MG_MAXLEVELS; 1583 PetscCall(PetscOptionsIntArray("-pc_gamg_rank_reduction_factors", "Manual schedule of coarse grid reduction factors that overrides internal heuristics (0 for first reduction puts one process/device)", "PCGAMGSetRankReductionFactors", pc_gamg->level_reduction_factors, &n, &flag)); 1584 if (!flag) i = 0; 1585 else i = n; 1586 do { 1587 pc_gamg->level_reduction_factors[i] = -1; 1588 } while (++i < PETSC_MG_MAXLEVELS); 1589 PetscCall(PetscOptionsInt("-pc_mg_levels", "Set number of MG levels", "PCGAMGSetNlevels", pc_gamg->Nlevels, &pc_gamg->Nlevels, NULL)); 1590 { 1591 PetscReal eminmax[2] = {0., 0.}; 1592 n = 2; 1593 PetscCall(PetscOptionsRealArray("-pc_gamg_eigenvalues", "extreme eigenvalues for smoothed aggregation", "PCGAMGSetEigenvalues", eminmax, &n, &flag)); 1594 if (flag) { 1595 PetscCheck(n == 2, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "-pc_gamg_eigenvalues: must specify 2 parameters, min and max eigenvalues"); 1596 PetscCall(PCGAMGSetEigenvalues(pc, eminmax[1], eminmax[0])); 1597 } 1598 } 1599 /* set options for subtype */ 1600 PetscCall((*pc_gamg->ops->setfromoptions)(pc, PetscOptionsObject)); 1601 1602 PetscCall(PCGetOptionsPrefix(pc, &pcpre)); 1603 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_gamg_", pcpre ? pcpre : "")); 1604 PetscOptionsHeadEnd(); 1605 PetscFunctionReturn(PETSC_SUCCESS); 1606 } 1607 1608 /*MC 1609 PCGAMG - Geometric algebraic multigrid (AMG) preconditioner 1610 1611 Options Database Keys: 1612 + -pc_gamg_type <type,default=agg> - one of agg, geo, or classical (only smoothed aggregation, agg, supported) 1613 . -pc_gamg_repartition <bool,default=false> - repartition the degrees of freedom across the coarse grids as they are determined 1614 . -pc_gamg_asm_use_agg <bool,default=false> - use the aggregates from the coasening process to defined the subdomains on each level for the PCASM smoother 1615 . -pc_gamg_process_eq_limit <limit, default=50> - `PCGAMG` will reduce the number of MPI ranks used directly on the coarse grids so that there are around <limit> 1616 equations on each process that has degrees of freedom 1617 . -pc_gamg_coarse_eq_limit <limit, default=50> - Set maximum number of equations on coarsest grid to aim for. 1618 . -pc_gamg_reuse_interpolation <bool,default=true> - when rebuilding the algebraic multigrid preconditioner reuse the previously computed interpolations (should always be true) 1619 . -pc_gamg_threshold[] <thresh,default=[-1,...]> - Before aggregating the graph `PCGAMG` will remove small values from the graph on each level (< 0 does no filtering) 1620 - -pc_gamg_threshold_scale <scale,default=1> - Scaling of threshold on each coarser grid if not specified 1621 1622 Options Database Keys for Aggregation: 1623 + -pc_gamg_agg_nsmooths <nsmooth, default=1> - number of smoothing steps to use with smooth aggregation 1624 . -pc_gamg_aggressive_coarsening <n,default=1> - number of aggressive coarsening (MIS-2) levels from finest. 1625 . -pc_gamg_aggressive_square_graph <bool,default=false> - Use square graph (A'A) or MIS-k (k=2) for aggressive coarsening 1626 . -pc_gamg_mis_k_minimum_degree_ordering <bool,default=true> - Use minimum degree ordering in greedy MIS algorithm 1627 - -pc_gamg_aggressive_mis_k <n,default=2> - Number (k) distance in MIS coarsening (>2 is 'aggressive') 1628 1629 Options Database Keys for Multigrid: 1630 + -pc_mg_cycle_type <v> - v or w, see `PCMGSetCycleType()` 1631 . -pc_mg_distinct_smoothup - configure the up and down (pre and post) smoothers separately, see PCMGSetDistinctSmoothUp() 1632 . -pc_mg_type <multiplicative> - (one of) additive multiplicative full kascade 1633 - -pc_mg_levels <levels> - Number of levels of multigrid to use. GAMG has a heuristic so pc_mg_levels is not usually used with GAMG 1634 1635 Level: intermediate 1636 1637 Notes: 1638 To obtain good performance for `PCGAMG` for vector valued problems you must 1639 call `MatSetBlockSize()` to indicate the number of degrees of freedom per grid point 1640 call `MatSetNearNullSpace()` (or `PCSetCoordinates()` if solving the equations of elasticity) to indicate the near null space of the operator 1641 1642 The many options for `PCMG` also work directly for `PCGAMG` such as controlling the smoothers on each level etc. 1643 1644 See [the Users Manual section on PCGAMG](sec_amg) and [the Users Manual section on PCMG](sec_mg)for more details. 1645 1646 .seealso: `PCCreate()`, `PCSetType()`, `MatSetBlockSize()`, `PCMGType`, `PCSetCoordinates()`, `MatSetNearNullSpace()`, `PCGAMGSetType()`, `PCGAMGAGG`, `PCGAMGGEO`, `PCGAMGCLASSICAL`, `PCGAMGSetProcEqLim()`, 1647 `PCGAMGSetCoarseEqLim()`, `PCGAMGSetRepartition()`, `PCGAMGRegister()`, `PCGAMGSetReuseInterpolation()`, `PCGAMGASMSetUseAggs()`, `PCGAMGSetParallelCoarseGridSolve()`, `PCGAMGSetNlevels()`, `PCGAMGSetThreshold()`, `PCGAMGGetType()`, `PCGAMGSetUseSAEstEig()` 1648 M*/ 1649 PETSC_EXTERN PetscErrorCode PCCreate_GAMG(PC pc) 1650 { 1651 PC_GAMG *pc_gamg; 1652 PC_MG *mg; 1653 1654 PetscFunctionBegin; 1655 /* register AMG type */ 1656 PetscCall(PCGAMGInitializePackage()); 1657 1658 /* PCGAMG is an inherited class of PCMG. Initialize pc as PCMG */ 1659 PetscCall(PCSetType(pc, PCMG)); 1660 PetscCall(PetscObjectChangeTypeName((PetscObject)pc, PCGAMG)); 1661 1662 /* create a supporting struct and attach it to pc */ 1663 PetscCall(PetscNew(&pc_gamg)); 1664 PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_EXTERNAL)); 1665 mg = (PC_MG *)pc->data; 1666 mg->innerctx = pc_gamg; 1667 1668 PetscCall(PetscNew(&pc_gamg->ops)); 1669 1670 /* these should be in subctx but repartitioning needs simple arrays */ 1671 pc_gamg->data_sz = 0; 1672 pc_gamg->data = NULL; 1673 1674 /* overwrite the pointers of PCMG by the functions of base class PCGAMG */ 1675 pc->ops->setfromoptions = PCSetFromOptions_GAMG; 1676 pc->ops->setup = PCSetUp_GAMG; 1677 pc->ops->reset = PCReset_GAMG; 1678 pc->ops->destroy = PCDestroy_GAMG; 1679 mg->view = PCView_GAMG; 1680 1681 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetProcEqLim_C", PCGAMGSetProcEqLim_GAMG)); 1682 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseEqLim_C", PCGAMGSetCoarseEqLim_GAMG)); 1683 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRepartition_C", PCGAMGSetRepartition_GAMG)); 1684 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetEigenvalues_C", PCGAMGSetEigenvalues_GAMG)); 1685 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetUseSAEstEig_C", PCGAMGSetUseSAEstEig_GAMG)); 1686 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRecomputeEstEig_C", PCGAMGSetRecomputeEstEig_GAMG)); 1687 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetReuseInterpolation_C", PCGAMGSetReuseInterpolation_GAMG)); 1688 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGASMSetUseAggs_C", PCGAMGASMSetUseAggs_GAMG)); 1689 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetParallelCoarseGridSolve_C", PCGAMGSetParallelCoarseGridSolve_GAMG)); 1690 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCpuPinCoarseGrids_C", PCGAMGSetCpuPinCoarseGrids_GAMG)); 1691 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetCoarseGridLayoutType_C", PCGAMGSetCoarseGridLayoutType_GAMG)); 1692 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThreshold_C", PCGAMGSetThreshold_GAMG)); 1693 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetRankReductionFactors_C", PCGAMGSetRankReductionFactors_GAMG)); 1694 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetThresholdScale_C", PCGAMGSetThresholdScale_GAMG)); 1695 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetType_C", PCGAMGSetType_GAMG)); 1696 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGGetType_C", PCGAMGGetType_GAMG)); 1697 PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGAMGSetNlevels_C", PCGAMGSetNlevels_GAMG)); 1698 pc_gamg->repart = PETSC_FALSE; 1699 pc_gamg->reuse_prol = PETSC_TRUE; 1700 pc_gamg->use_aggs_in_asm = PETSC_FALSE; 1701 pc_gamg->use_parallel_coarse_grid_solver = PETSC_FALSE; 1702 pc_gamg->cpu_pin_coarse_grids = PETSC_FALSE; 1703 pc_gamg->layout_type = PCGAMG_LAYOUT_SPREAD; 1704 pc_gamg->min_eq_proc = 50; 1705 pc_gamg->coarse_eq_limit = 50; 1706 for (int i = 0; i < PETSC_MG_MAXLEVELS; i++) pc_gamg->threshold[i] = -1; 1707 pc_gamg->threshold_scale = 1.; 1708 pc_gamg->Nlevels = PETSC_MG_MAXLEVELS; 1709 pc_gamg->current_level = 0; /* don't need to init really */ 1710 pc_gamg->use_sa_esteig = PETSC_TRUE; 1711 pc_gamg->recompute_esteig = PETSC_TRUE; 1712 pc_gamg->emin = 0; 1713 pc_gamg->emax = 0; 1714 1715 pc_gamg->ops->createlevel = PCGAMGCreateLevel_GAMG; 1716 1717 /* PCSetUp_GAMG assumes that the type has been set, so set it to the default now */ 1718 PetscCall(PCGAMGSetType(pc, PCGAMGAGG)); 1719 PetscFunctionReturn(PETSC_SUCCESS); 1720 } 1721 1722 /*@C 1723 PCGAMGInitializePackage - This function initializes everything in the `PCGAMG` package. It is called 1724 from `PCInitializePackage()`. 1725 1726 Level: developer 1727 1728 .seealso: `PetscInitialize()` 1729 @*/ 1730 PetscErrorCode PCGAMGInitializePackage(void) 1731 { 1732 PetscInt l; 1733 1734 PetscFunctionBegin; 1735 if (PCGAMGPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS); 1736 PCGAMGPackageInitialized = PETSC_TRUE; 1737 PetscCall(PetscFunctionListAdd(&GAMGList, PCGAMGGEO, PCCreateGAMG_GEO)); 1738 PetscCall(PetscFunctionListAdd(&GAMGList, PCGAMGAGG, PCCreateGAMG_AGG)); 1739 PetscCall(PetscFunctionListAdd(&GAMGList, PCGAMGCLASSICAL, PCCreateGAMG_Classical)); 1740 PetscCall(PetscRegisterFinalize(PCGAMGFinalizePackage)); 1741 1742 /* general events */ 1743 PetscCall(PetscLogEventRegister("PCSetUp_GAMG+", PC_CLASSID, &petsc_gamg_setup_events[GAMG_SETUP])); 1744 PetscCall(PetscLogEventRegister(" PCGAMGCreateG", PC_CLASSID, &petsc_gamg_setup_events[GAMG_GRAPH])); 1745 PetscCall(PetscLogEventRegister(" GAMG Coarsen", PC_CLASSID, &petsc_gamg_setup_events[GAMG_COARSEN])); 1746 PetscCall(PetscLogEventRegister(" GAMG MIS/Agg", PC_CLASSID, &petsc_gamg_setup_events[GAMG_MIS])); 1747 PetscCall(PetscLogEventRegister(" PCGAMGProl", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PROL])); 1748 PetscCall(PetscLogEventRegister(" GAMG Prol-col", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PROLA])); 1749 PetscCall(PetscLogEventRegister(" GAMG Prol-lift", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PROLB])); 1750 PetscCall(PetscLogEventRegister(" PCGAMGOptProl", PC_CLASSID, &petsc_gamg_setup_events[GAMG_OPT])); 1751 PetscCall(PetscLogEventRegister(" GAMG smooth", PC_CLASSID, &petsc_gamg_setup_events[GAMG_OPTSM])); 1752 PetscCall(PetscLogEventRegister(" PCGAMGCreateL", PC_CLASSID, &petsc_gamg_setup_events[GAMG_LEVEL])); 1753 PetscCall(PetscLogEventRegister(" GAMG PtAP", PC_CLASSID, &petsc_gamg_setup_events[GAMG_PTAP])); 1754 PetscCall(PetscLogEventRegister(" GAMG Reduce", PC_CLASSID, &petsc_gamg_setup_events[GAMG_REDUCE])); 1755 PetscCall(PetscLogEventRegister(" GAMG Repart", PC_CLASSID, &petsc_gamg_setup_events[GAMG_REPART])); 1756 /* PetscCall(PetscLogEventRegister(" GAMG Inv-Srt", PC_CLASSID, &petsc_gamg_setup_events[SET13])); */ 1757 /* PetscCall(PetscLogEventRegister(" GAMG Move A", PC_CLASSID, &petsc_gamg_setup_events[SET14])); */ 1758 /* PetscCall(PetscLogEventRegister(" GAMG Move P", PC_CLASSID, &petsc_gamg_setup_events[SET15])); */ 1759 for (l = 0; l < PETSC_MG_MAXLEVELS; l++) { 1760 char ename[32]; 1761 1762 PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCGAMG Squ l%02" PetscInt_FMT, l)); 1763 PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &petsc_gamg_setup_matmat_events[l][0])); 1764 PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCGAMG Gal l%02" PetscInt_FMT, l)); 1765 PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &petsc_gamg_setup_matmat_events[l][1])); 1766 PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCGAMG Opt l%02" PetscInt_FMT, l)); 1767 PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &petsc_gamg_setup_matmat_events[l][2])); 1768 } 1769 #if defined(GAMG_STAGES) 1770 { /* create timer stages */ 1771 char str[32]; 1772 PetscCall(PetscSNPrintf(str, PETSC_STATIC_ARRAY_LENGTH(str), "GAMG Level %d", 0)); 1773 PetscCall(PetscLogStageRegister(str, &gamg_stages[0])); 1774 } 1775 #endif 1776 PetscFunctionReturn(PETSC_SUCCESS); 1777 } 1778 1779 /*@C 1780 PCGAMGFinalizePackage - This function frees everything from the `PCGAMG` package. It is 1781 called from `PetscFinalize()` automatically. 1782 1783 Level: developer 1784 1785 .seealso: `PetscFinalize()` 1786 @*/ 1787 PetscErrorCode PCGAMGFinalizePackage(void) 1788 { 1789 PetscFunctionBegin; 1790 PCGAMGPackageInitialized = PETSC_FALSE; 1791 PetscCall(PetscFunctionListDestroy(&GAMGList)); 1792 PetscFunctionReturn(PETSC_SUCCESS); 1793 } 1794 1795 /*@C 1796 PCGAMGRegister - Register a `PCGAMG` implementation. 1797 1798 Input Parameters: 1799 + type - string that will be used as the name of the `PCGAMG` type. 1800 - create - function for creating the gamg context. 1801 1802 Level: developer 1803 1804 .seealso: `PCGAMGType`, `PCGAMG`, `PCGAMGSetType()` 1805 @*/ 1806 PetscErrorCode PCGAMGRegister(PCGAMGType type, PetscErrorCode (*create)(PC)) 1807 { 1808 PetscFunctionBegin; 1809 PetscCall(PCGAMGInitializePackage()); 1810 PetscCall(PetscFunctionListAdd(&GAMGList, type, create)); 1811 PetscFunctionReturn(PETSC_SUCCESS); 1812 } 1813 1814 /*@C 1815 PCGAMGCreateGraph - Creates a graph that is used by the ``PCGAMGType`` in the coarsening process 1816 1817 Input Parameters: 1818 + pc - the `PCGAMG` 1819 - A - the matrix, for any level 1820 1821 Output Parameter: 1822 . G - the graph 1823 1824 Level: advanced 1825 1826 .seealso: `PCGAMGType`, `PCGAMG`, `PCGAMGSetType()` 1827 @*/ 1828 PetscErrorCode PCGAMGCreateGraph(PC pc, Mat A, Mat *G) 1829 { 1830 PC_MG *mg = (PC_MG *)pc->data; 1831 PC_GAMG *pc_gamg = (PC_GAMG *)mg->innerctx; 1832 1833 PetscFunctionBegin; 1834 PetscCall(pc_gamg->ops->creategraph(pc, A, G)); 1835 PetscFunctionReturn(PETSC_SUCCESS); 1836 } 1837