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