1 #include <petsc/private/dmpleximpl.h> /*I "petscdmplex.h" I*/ 2 #include <petsc/private/partitionerimpl.h> 3 #include <petsc/private/hashseti.h> 4 5 const char *const DMPlexCSRAlgorithms[] = {"mat", "graph", "overlap", "DMPlexCSRAlgorithm", "DM_PLEX_CSR_", NULL}; 6 7 static inline PetscInt DMPlex_GlobalID(PetscInt point) 8 { 9 return point >= 0 ? point : -(point + 1); 10 } 11 12 static PetscErrorCode DMPlexCreatePartitionerGraph_Overlap(DM dm, PetscInt height, PetscInt *numVertices, PetscInt **offsets, PetscInt **adjacency, IS *globalNumbering) 13 { 14 DM ovdm; 15 PetscSF sfPoint; 16 IS cellNumbering; 17 const PetscInt *cellNum; 18 PetscInt *adj = NULL, *vOffsets = NULL, *vAdj = NULL; 19 PetscBool useCone, useClosure; 20 PetscInt dim, depth, overlap, cStart, cEnd, c, v; 21 PetscMPIInt rank, size; 22 23 PetscFunctionBegin; 24 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 25 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size)); 26 PetscCall(DMGetDimension(dm, &dim)); 27 PetscCall(DMPlexGetDepth(dm, &depth)); 28 if (dim != depth) { 29 /* We do not handle the uninterpolated case here */ 30 PetscCall(DMPlexCreateNeighborCSR(dm, height, numVertices, offsets, adjacency)); 31 /* DMPlexCreateNeighborCSR does not make a numbering */ 32 if (globalNumbering) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, globalNumbering)); 33 /* Different behavior for empty graphs */ 34 if (!*numVertices) { 35 PetscCall(PetscMalloc1(1, offsets)); 36 (*offsets)[0] = 0; 37 } 38 /* Broken in parallel */ 39 if (rank) PetscCheck(!*numVertices, PETSC_COMM_SELF, PETSC_ERR_SUP, "Parallel partitioning of uninterpolated meshes not supported"); 40 PetscFunctionReturn(PETSC_SUCCESS); 41 } 42 /* Always use FVM adjacency to create partitioner graph */ 43 PetscCall(DMGetBasicAdjacency(dm, &useCone, &useClosure)); 44 PetscCall(DMSetBasicAdjacency(dm, PETSC_TRUE, PETSC_FALSE)); 45 /* Need overlap >= 1 */ 46 PetscCall(DMPlexGetOverlap(dm, &overlap)); 47 if (size && overlap < 1) { 48 PetscCall(DMPlexDistributeOverlap(dm, 1, NULL, &ovdm)); 49 } else { 50 PetscCall(PetscObjectReference((PetscObject)dm)); 51 ovdm = dm; 52 } 53 PetscCall(DMGetPointSF(ovdm, &sfPoint)); 54 PetscCall(DMPlexGetHeightStratum(ovdm, height, &cStart, &cEnd)); 55 PetscCall(DMPlexCreateNumbering_Plex(ovdm, cStart, cEnd, 0, NULL, sfPoint, &cellNumbering)); 56 if (globalNumbering) { 57 PetscCall(PetscObjectReference((PetscObject)cellNumbering)); 58 *globalNumbering = cellNumbering; 59 } 60 PetscCall(ISGetIndices(cellNumbering, &cellNum)); 61 /* Determine sizes */ 62 for (*numVertices = 0, c = cStart; c < cEnd; ++c) { 63 /* Skip non-owned cells in parallel (ParMetis expects no overlap) */ 64 if (cellNum[c - cStart] < 0) continue; 65 (*numVertices)++; 66 } 67 PetscCall(PetscCalloc1(*numVertices + 1, &vOffsets)); 68 for (c = cStart, v = 0; c < cEnd; ++c) { 69 PetscInt adjSize = PETSC_DETERMINE, a, vsize = 0; 70 71 if (cellNum[c - cStart] < 0) continue; 72 PetscCall(DMPlexGetAdjacency(ovdm, c, &adjSize, &adj)); 73 for (a = 0; a < adjSize; ++a) { 74 const PetscInt point = adj[a]; 75 if (point != c && cStart <= point && point < cEnd) ++vsize; 76 } 77 vOffsets[v + 1] = vOffsets[v] + vsize; 78 ++v; 79 } 80 /* Determine adjacency */ 81 PetscCall(PetscMalloc1(vOffsets[*numVertices], &vAdj)); 82 for (c = cStart, v = 0; c < cEnd; ++c) { 83 PetscInt adjSize = PETSC_DETERMINE, a, off = vOffsets[v]; 84 85 /* Skip non-owned cells in parallel (ParMetis expects no overlap) */ 86 if (cellNum[c - cStart] < 0) continue; 87 PetscCall(DMPlexGetAdjacency(ovdm, c, &adjSize, &adj)); 88 for (a = 0; a < adjSize; ++a) { 89 const PetscInt point = adj[a]; 90 if (point != c && cStart <= point && point < cEnd) vAdj[off++] = DMPlex_GlobalID(cellNum[point - cStart]); 91 } 92 PetscCheck(off == vOffsets[v + 1], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Offsets %" PetscInt_FMT " should be %" PetscInt_FMT, off, vOffsets[v + 1]); 93 /* Sort adjacencies (not strictly necessary) */ 94 PetscCall(PetscSortInt(off - vOffsets[v], &vAdj[vOffsets[v]])); 95 ++v; 96 } 97 PetscCall(PetscFree(adj)); 98 PetscCall(ISRestoreIndices(cellNumbering, &cellNum)); 99 PetscCall(ISDestroy(&cellNumbering)); 100 PetscCall(DMSetBasicAdjacency(dm, useCone, useClosure)); 101 PetscCall(DMDestroy(&ovdm)); 102 if (offsets) { 103 *offsets = vOffsets; 104 } else PetscCall(PetscFree(vOffsets)); 105 if (adjacency) { 106 *adjacency = vAdj; 107 } else PetscCall(PetscFree(vAdj)); 108 PetscFunctionReturn(PETSC_SUCCESS); 109 } 110 111 static PetscErrorCode DMPlexCreatePartitionerGraph_Native(DM dm, PetscInt height, PetscInt *numVertices, PetscInt **offsets, PetscInt **adjacency, IS *globalNumbering) 112 { 113 PetscInt dim, depth, p, pStart, pEnd, a, adjSize, idx, size; 114 PetscInt *adj = NULL, *vOffsets = NULL, *graph = NULL; 115 IS cellNumbering; 116 const PetscInt *cellNum; 117 PetscBool useCone, useClosure; 118 PetscSection section; 119 PetscSegBuffer adjBuffer; 120 PetscSF sfPoint; 121 PetscInt *adjCells = NULL, *remoteCells = NULL; 122 const PetscInt *local; 123 PetscInt nroots, nleaves, l; 124 PetscMPIInt rank; 125 126 PetscFunctionBegin; 127 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 128 PetscCall(DMGetDimension(dm, &dim)); 129 PetscCall(DMPlexGetDepth(dm, &depth)); 130 if (dim != depth) { 131 /* We do not handle the uninterpolated case here */ 132 PetscCall(DMPlexCreateNeighborCSR(dm, height, numVertices, offsets, adjacency)); 133 /* DMPlexCreateNeighborCSR does not make a numbering */ 134 if (globalNumbering) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, globalNumbering)); 135 /* Different behavior for empty graphs */ 136 if (!*numVertices) { 137 PetscCall(PetscMalloc1(1, offsets)); 138 (*offsets)[0] = 0; 139 } 140 /* Broken in parallel */ 141 if (rank) PetscCheck(!*numVertices, PETSC_COMM_SELF, PETSC_ERR_SUP, "Parallel partitioning of uninterpolated meshes not supported"); 142 PetscFunctionReturn(PETSC_SUCCESS); 143 } 144 PetscCall(DMGetPointSF(dm, &sfPoint)); 145 PetscCall(DMPlexGetHeightStratum(dm, height, &pStart, &pEnd)); 146 /* Build adjacency graph via a section/segbuffer */ 147 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), §ion)); 148 PetscCall(PetscSectionSetChart(section, pStart, pEnd)); 149 PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 1000, &adjBuffer)); 150 /* Always use FVM adjacency to create partitioner graph */ 151 PetscCall(DMGetBasicAdjacency(dm, &useCone, &useClosure)); 152 PetscCall(DMSetBasicAdjacency(dm, PETSC_TRUE, PETSC_FALSE)); 153 PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, 0, NULL, sfPoint, &cellNumbering)); 154 if (globalNumbering) { 155 PetscCall(PetscObjectReference((PetscObject)cellNumbering)); 156 *globalNumbering = cellNumbering; 157 } 158 PetscCall(ISGetIndices(cellNumbering, &cellNum)); 159 /* For all boundary faces (including faces adjacent to a ghost cell), record the local cell in adjCells 160 Broadcast adjCells to remoteCells (to get cells from roots) and Reduce adjCells to remoteCells (to get cells from leaves) 161 */ 162 PetscCall(PetscSFGetGraph(sfPoint, &nroots, &nleaves, &local, NULL)); 163 if (nroots >= 0) { 164 PetscInt fStart, fEnd, f; 165 166 PetscCall(PetscCalloc2(nroots, &adjCells, nroots, &remoteCells)); 167 PetscCall(DMPlexGetHeightStratum(dm, height + 1, &fStart, &fEnd)); 168 for (l = 0; l < nroots; ++l) adjCells[l] = -3; 169 for (f = fStart; f < fEnd; ++f) { 170 const PetscInt *support; 171 PetscInt supportSize; 172 173 PetscCall(DMPlexGetSupport(dm, f, &support)); 174 PetscCall(DMPlexGetSupportSize(dm, f, &supportSize)); 175 if (supportSize == 1) adjCells[f] = DMPlex_GlobalID(cellNum[support[0] - pStart]); 176 else if (supportSize == 2) { 177 PetscCall(PetscFindInt(support[0], nleaves, local, &p)); 178 if (p >= 0) adjCells[f] = DMPlex_GlobalID(cellNum[support[1] - pStart]); 179 PetscCall(PetscFindInt(support[1], nleaves, local, &p)); 180 if (p >= 0) adjCells[f] = DMPlex_GlobalID(cellNum[support[0] - pStart]); 181 } 182 /* Handle non-conforming meshes */ 183 if (supportSize > 2) { 184 PetscInt numChildren, i; 185 const PetscInt *children; 186 187 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, &children)); 188 for (i = 0; i < numChildren; ++i) { 189 const PetscInt child = children[i]; 190 if (fStart <= child && child < fEnd) { 191 PetscCall(DMPlexGetSupport(dm, child, &support)); 192 PetscCall(DMPlexGetSupportSize(dm, child, &supportSize)); 193 if (supportSize == 1) adjCells[child] = DMPlex_GlobalID(cellNum[support[0] - pStart]); 194 else if (supportSize == 2) { 195 PetscCall(PetscFindInt(support[0], nleaves, local, &p)); 196 if (p >= 0) adjCells[child] = DMPlex_GlobalID(cellNum[support[1] - pStart]); 197 PetscCall(PetscFindInt(support[1], nleaves, local, &p)); 198 if (p >= 0) adjCells[child] = DMPlex_GlobalID(cellNum[support[0] - pStart]); 199 } 200 } 201 } 202 } 203 } 204 for (l = 0; l < nroots; ++l) remoteCells[l] = -1; 205 PetscCall(PetscSFBcastBegin(dm->sf, MPIU_INT, adjCells, remoteCells, MPI_REPLACE)); 206 PetscCall(PetscSFBcastEnd(dm->sf, MPIU_INT, adjCells, remoteCells, MPI_REPLACE)); 207 PetscCall(PetscSFReduceBegin(dm->sf, MPIU_INT, adjCells, remoteCells, MPI_MAX)); 208 PetscCall(PetscSFReduceEnd(dm->sf, MPIU_INT, adjCells, remoteCells, MPI_MAX)); 209 } 210 /* Combine local and global adjacencies */ 211 for (*numVertices = 0, p = pStart; p < pEnd; p++) { 212 /* Skip non-owned cells in parallel (ParMetis expects no overlap) */ 213 if (nroots > 0) { 214 if (cellNum[p - pStart] < 0) continue; 215 } 216 /* Add remote cells */ 217 if (remoteCells) { 218 const PetscInt gp = DMPlex_GlobalID(cellNum[p - pStart]); 219 PetscInt coneSize, numChildren, c, i; 220 const PetscInt *cone, *children; 221 222 PetscCall(DMPlexGetCone(dm, p, &cone)); 223 PetscCall(DMPlexGetConeSize(dm, p, &coneSize)); 224 for (c = 0; c < coneSize; ++c) { 225 const PetscInt point = cone[c]; 226 if (remoteCells[point] >= 0 && remoteCells[point] != gp) { 227 PetscInt *PETSC_RESTRICT pBuf; 228 PetscCall(PetscSectionAddDof(section, p, 1)); 229 PetscCall(PetscSegBufferGetInts(adjBuffer, 1, &pBuf)); 230 *pBuf = remoteCells[point]; 231 } 232 /* Handle non-conforming meshes */ 233 PetscCall(DMPlexGetTreeChildren(dm, point, &numChildren, &children)); 234 for (i = 0; i < numChildren; ++i) { 235 const PetscInt child = children[i]; 236 if (remoteCells[child] >= 0 && remoteCells[child] != gp) { 237 PetscInt *PETSC_RESTRICT pBuf; 238 PetscCall(PetscSectionAddDof(section, p, 1)); 239 PetscCall(PetscSegBufferGetInts(adjBuffer, 1, &pBuf)); 240 *pBuf = remoteCells[child]; 241 } 242 } 243 } 244 } 245 /* Add local cells */ 246 adjSize = PETSC_DETERMINE; 247 PetscCall(DMPlexGetAdjacency(dm, p, &adjSize, &adj)); 248 for (a = 0; a < adjSize; ++a) { 249 const PetscInt point = adj[a]; 250 if (point != p && pStart <= point && point < pEnd) { 251 PetscInt *PETSC_RESTRICT pBuf; 252 PetscCall(PetscSectionAddDof(section, p, 1)); 253 PetscCall(PetscSegBufferGetInts(adjBuffer, 1, &pBuf)); 254 *pBuf = DMPlex_GlobalID(cellNum[point - pStart]); 255 } 256 } 257 (*numVertices)++; 258 } 259 PetscCall(PetscFree(adj)); 260 PetscCall(PetscFree2(adjCells, remoteCells)); 261 PetscCall(DMSetBasicAdjacency(dm, useCone, useClosure)); 262 263 /* Derive CSR graph from section/segbuffer */ 264 PetscCall(PetscSectionSetUp(section)); 265 PetscCall(PetscSectionGetStorageSize(section, &size)); 266 PetscCall(PetscMalloc1(*numVertices + 1, &vOffsets)); 267 for (idx = 0, p = pStart; p < pEnd; p++) { 268 if (nroots > 0) { 269 if (cellNum[p - pStart] < 0) continue; 270 } 271 PetscCall(PetscSectionGetOffset(section, p, &vOffsets[idx++])); 272 } 273 vOffsets[*numVertices] = size; 274 PetscCall(PetscSegBufferExtractAlloc(adjBuffer, &graph)); 275 276 if (nroots >= 0) { 277 /* Filter out duplicate edges using section/segbuffer */ 278 PetscCall(PetscSectionReset(section)); 279 PetscCall(PetscSectionSetChart(section, 0, *numVertices)); 280 for (p = 0; p < *numVertices; p++) { 281 PetscInt start = vOffsets[p], end = vOffsets[p + 1]; 282 PetscInt numEdges = end - start, *PETSC_RESTRICT edges; 283 PetscCall(PetscSortRemoveDupsInt(&numEdges, &graph[start])); 284 PetscCall(PetscSectionSetDof(section, p, numEdges)); 285 PetscCall(PetscSegBufferGetInts(adjBuffer, numEdges, &edges)); 286 PetscCall(PetscArraycpy(edges, &graph[start], numEdges)); 287 } 288 PetscCall(PetscFree(vOffsets)); 289 PetscCall(PetscFree(graph)); 290 /* Derive CSR graph from section/segbuffer */ 291 PetscCall(PetscSectionSetUp(section)); 292 PetscCall(PetscSectionGetStorageSize(section, &size)); 293 PetscCall(PetscMalloc1(*numVertices + 1, &vOffsets)); 294 for (idx = 0, p = 0; p < *numVertices; p++) PetscCall(PetscSectionGetOffset(section, p, &vOffsets[idx++])); 295 vOffsets[*numVertices] = size; 296 PetscCall(PetscSegBufferExtractAlloc(adjBuffer, &graph)); 297 } else { 298 /* Sort adjacencies (not strictly necessary) */ 299 for (p = 0; p < *numVertices; p++) { 300 PetscInt start = vOffsets[p], end = vOffsets[p + 1]; 301 PetscCall(PetscSortInt(end - start, &graph[start])); 302 } 303 } 304 305 if (offsets) *offsets = vOffsets; 306 if (adjacency) *adjacency = graph; 307 308 /* Cleanup */ 309 PetscCall(PetscSegBufferDestroy(&adjBuffer)); 310 PetscCall(PetscSectionDestroy(§ion)); 311 PetscCall(ISRestoreIndices(cellNumbering, &cellNum)); 312 PetscCall(ISDestroy(&cellNumbering)); 313 PetscFunctionReturn(PETSC_SUCCESS); 314 } 315 316 static PetscErrorCode DMPlexCreatePartitionerGraph_ViaMat(DM dm, PetscInt height, PetscInt *numVertices, PetscInt **offsets, PetscInt **adjacency, IS *globalNumbering) 317 { 318 Mat conn, CSR; 319 IS fis, cis, cis_own; 320 PetscSF sfPoint; 321 const PetscInt *rows, *cols, *ii, *jj; 322 PetscInt *idxs, *idxs2; 323 PetscInt dim, depth, floc, cloc, i, M, N, c, lm, m, cStart, cEnd, fStart, fEnd; 324 PetscMPIInt rank; 325 PetscBool flg; 326 327 PetscFunctionBegin; 328 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 329 PetscCall(DMGetDimension(dm, &dim)); 330 PetscCall(DMPlexGetDepth(dm, &depth)); 331 if (dim != depth) { 332 /* We do not handle the uninterpolated case here */ 333 PetscCall(DMPlexCreateNeighborCSR(dm, height, numVertices, offsets, adjacency)); 334 /* DMPlexCreateNeighborCSR does not make a numbering */ 335 if (globalNumbering) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, globalNumbering)); 336 /* Different behavior for empty graphs */ 337 if (!*numVertices) { 338 PetscCall(PetscMalloc1(1, offsets)); 339 (*offsets)[0] = 0; 340 } 341 /* Broken in parallel */ 342 if (rank) PetscCheck(!*numVertices, PETSC_COMM_SELF, PETSC_ERR_SUP, "Parallel partitioning of uninterpolated meshes not supported"); 343 PetscFunctionReturn(PETSC_SUCCESS); 344 } 345 /* Interpolated and parallel case */ 346 347 /* numbering */ 348 PetscCall(DMGetIsoperiodicPointSF_Internal(dm, &sfPoint)); 349 PetscCall(DMPlexGetHeightStratum(dm, height, &cStart, &cEnd)); 350 PetscCall(DMPlexGetHeightStratum(dm, height + 1, &fStart, &fEnd)); 351 PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, &N, sfPoint, &cis)); 352 PetscCall(DMPlexCreateNumbering_Plex(dm, fStart, fEnd, 0, &M, sfPoint, &fis)); 353 if (globalNumbering) PetscCall(ISDuplicate(cis, globalNumbering)); 354 355 /* get positive global ids and local sizes for facets and cells */ 356 PetscCall(ISGetLocalSize(fis, &m)); 357 PetscCall(ISGetIndices(fis, &rows)); 358 PetscCall(PetscMalloc1(m, &idxs)); 359 for (i = 0, floc = 0; i < m; i++) { 360 const PetscInt p = rows[i]; 361 362 if (p < 0) { 363 idxs[i] = -(p + 1); 364 } else { 365 idxs[i] = p; 366 floc += 1; 367 } 368 } 369 PetscCall(ISRestoreIndices(fis, &rows)); 370 PetscCall(ISDestroy(&fis)); 371 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, idxs, PETSC_OWN_POINTER, &fis)); 372 373 PetscCall(ISGetLocalSize(cis, &m)); 374 PetscCall(ISGetIndices(cis, &cols)); 375 PetscCall(PetscMalloc1(m, &idxs)); 376 PetscCall(PetscMalloc1(m, &idxs2)); 377 for (i = 0, cloc = 0; i < m; i++) { 378 const PetscInt p = cols[i]; 379 380 if (p < 0) { 381 idxs[i] = -(p + 1); 382 } else { 383 idxs[i] = p; 384 idxs2[cloc++] = p; 385 } 386 } 387 PetscCall(ISRestoreIndices(cis, &cols)); 388 PetscCall(ISDestroy(&cis)); 389 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), m, idxs, PETSC_OWN_POINTER, &cis)); 390 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), cloc, idxs2, PETSC_OWN_POINTER, &cis_own)); 391 392 /* Create matrix to hold F-C connectivity (MatMatTranspose Mult not supported for MPIAIJ) */ 393 PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), &conn)); 394 PetscCall(MatSetSizes(conn, floc, cloc, M, N)); 395 PetscCall(MatSetType(conn, MATMPIAIJ)); 396 PetscCall(DMPlexGetMaxSizes(dm, NULL, &lm)); 397 PetscCallMPI(MPIU_Allreduce(&lm, &m, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm))); 398 PetscCall(MatMPIAIJSetPreallocation(conn, m, NULL, m, NULL)); 399 400 /* Assemble matrix */ 401 PetscCall(ISGetIndices(fis, &rows)); 402 PetscCall(ISGetIndices(cis, &cols)); 403 for (c = cStart; c < cEnd; c++) { 404 const PetscInt *cone; 405 PetscInt coneSize, row, col, f; 406 407 col = cols[c - cStart]; 408 PetscCall(DMPlexGetCone(dm, c, &cone)); 409 PetscCall(DMPlexGetConeSize(dm, c, &coneSize)); 410 for (f = 0; f < coneSize; f++) { 411 const PetscScalar v = 1.0; 412 const PetscInt *children; 413 PetscInt numChildren, ch; 414 415 row = rows[cone[f] - fStart]; 416 PetscCall(MatSetValues(conn, 1, &row, 1, &col, &v, INSERT_VALUES)); 417 418 /* non-conforming meshes */ 419 PetscCall(DMPlexGetTreeChildren(dm, cone[f], &numChildren, &children)); 420 for (ch = 0; ch < numChildren; ch++) { 421 const PetscInt child = children[ch]; 422 423 if (child < fStart || child >= fEnd) continue; 424 row = rows[child - fStart]; 425 PetscCall(MatSetValues(conn, 1, &row, 1, &col, &v, INSERT_VALUES)); 426 } 427 } 428 } 429 PetscCall(ISRestoreIndices(fis, &rows)); 430 PetscCall(ISRestoreIndices(cis, &cols)); 431 PetscCall(ISDestroy(&fis)); 432 PetscCall(ISDestroy(&cis)); 433 PetscCall(MatAssemblyBegin(conn, MAT_FINAL_ASSEMBLY)); 434 PetscCall(MatAssemblyEnd(conn, MAT_FINAL_ASSEMBLY)); 435 436 /* Get parallel CSR by doing conn^T * conn */ 437 PetscCall(MatTransposeMatMult(conn, conn, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &CSR)); 438 PetscCall(MatDestroy(&conn)); 439 440 /* extract local part of the CSR */ 441 PetscCall(MatMPIAIJGetLocalMat(CSR, MAT_INITIAL_MATRIX, &conn)); 442 PetscCall(MatDestroy(&CSR)); 443 PetscCall(MatGetRowIJ(conn, 0, PETSC_FALSE, PETSC_FALSE, &m, &ii, &jj, &flg)); 444 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_PLIB, "No IJ format"); 445 446 /* get back requested output */ 447 if (numVertices) *numVertices = m; 448 if (offsets) { 449 PetscCall(PetscCalloc1(m + 1, &idxs)); 450 for (i = 1; i < m + 1; i++) idxs[i] = ii[i] - i; /* ParMetis does not like self-connectivity */ 451 *offsets = idxs; 452 } 453 if (adjacency) { 454 PetscCall(PetscMalloc1(ii[m] - m, &idxs)); 455 PetscCall(ISGetIndices(cis_own, &rows)); 456 for (i = 0, c = 0; i < m; i++) { 457 PetscInt j, g = rows[i]; 458 459 for (j = ii[i]; j < ii[i + 1]; j++) { 460 if (jj[j] == g) continue; /* again, self-connectivity */ 461 idxs[c++] = jj[j]; 462 } 463 } 464 PetscCheck(c == ii[m] - m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, c, ii[m] - m); 465 PetscCall(ISRestoreIndices(cis_own, &rows)); 466 *adjacency = idxs; 467 } 468 469 /* cleanup */ 470 PetscCall(ISDestroy(&cis_own)); 471 PetscCall(MatRestoreRowIJ(conn, 0, PETSC_FALSE, PETSC_FALSE, &m, &ii, &jj, &flg)); 472 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_PLIB, "No IJ format"); 473 PetscCall(MatDestroy(&conn)); 474 PetscFunctionReturn(PETSC_SUCCESS); 475 } 476 477 /*@C 478 DMPlexCreatePartitionerGraph - Create a CSR graph of point connections for the partitioner 479 480 Collective 481 482 Input Parameters: 483 + dm - The mesh `DM` 484 - height - Height of the strata from which to construct the graph 485 486 Output Parameters: 487 + numVertices - Number of vertices in the graph 488 . offsets - Point offsets in the graph 489 . adjacency - Point connectivity in the graph 490 - globalNumbering - A map from the local cell numbering to the global numbering used in "adjacency". Negative indicates that the cell is a duplicate from another process. 491 492 Options Database Key: 493 . -dm_plex_csr_alg <mat,graph,overlap> - Choose the algorithm for computing the CSR graph 494 495 Level: developer 496 497 Note: 498 The user can control the definition of adjacency for the mesh using `DMSetAdjacency()`. They should choose the combination appropriate for the function 499 representation on the mesh. If requested, globalNumbering needs to be destroyed by the caller; offsets and adjacency need to be freed with PetscFree(). 500 501 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscPartitionerGetType()`, `PetscPartitionerCreate()`, `DMSetAdjacency()` 502 @*/ 503 PetscErrorCode DMPlexCreatePartitionerGraph(DM dm, PetscInt height, PetscInt *numVertices, PetscInt **offsets, PetscInt **adjacency, IS *globalNumbering) 504 { 505 DMPlexCSRAlgorithm alg = DM_PLEX_CSR_GRAPH; 506 507 PetscFunctionBegin; 508 PetscCall(PetscOptionsGetEnum(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_csr_alg", DMPlexCSRAlgorithms, (PetscEnum *)&alg, NULL)); 509 switch (alg) { 510 case DM_PLEX_CSR_MAT: 511 PetscCall(DMPlexCreatePartitionerGraph_ViaMat(dm, height, numVertices, offsets, adjacency, globalNumbering)); 512 break; 513 case DM_PLEX_CSR_GRAPH: 514 PetscCall(DMPlexCreatePartitionerGraph_Native(dm, height, numVertices, offsets, adjacency, globalNumbering)); 515 break; 516 case DM_PLEX_CSR_OVERLAP: 517 PetscCall(DMPlexCreatePartitionerGraph_Overlap(dm, height, numVertices, offsets, adjacency, globalNumbering)); 518 break; 519 } 520 PetscFunctionReturn(PETSC_SUCCESS); 521 } 522 523 /*@C 524 DMPlexCreateNeighborCSR - Create a mesh graph (cell-cell adjacency) in parallel CSR format. 525 526 Collective 527 528 Input Parameters: 529 + dm - The `DMPLEX` 530 - cellHeight - The height of mesh points to treat as cells (default should be 0) 531 532 Output Parameters: 533 + numVertices - The number of local vertices in the graph, or cells in the mesh. 534 . offsets - The offset to the adjacency list for each cell 535 - adjacency - The adjacency list for all cells 536 537 Level: advanced 538 539 Note: 540 This is suitable for input to a mesh partitioner like ParMetis. 541 542 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()` 543 @*/ 544 PetscErrorCode DMPlexCreateNeighborCSR(DM dm, PetscInt cellHeight, PetscInt *numVertices, PetscInt **offsets, PetscInt **adjacency) 545 { 546 const PetscInt maxFaceCases = 30; 547 PetscInt numFaceCases = 0; 548 PetscInt numFaceVertices[30]; /* maxFaceCases, C89 sucks sucks sucks */ 549 PetscInt *off, *adj; 550 PetscInt *neighborCells = NULL; 551 PetscInt dim, cellDim, depth = 0, faceDepth, cStart, cEnd, c, numCells, cell; 552 553 PetscFunctionBegin; 554 /* For parallel partitioning, I think you have to communicate supports */ 555 PetscCall(DMGetDimension(dm, &dim)); 556 cellDim = dim - cellHeight; 557 PetscCall(DMPlexGetDepth(dm, &depth)); 558 PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd)); 559 if (cEnd - cStart == 0) { 560 if (numVertices) *numVertices = 0; 561 if (offsets) *offsets = NULL; 562 if (adjacency) *adjacency = NULL; 563 PetscFunctionReturn(PETSC_SUCCESS); 564 } 565 numCells = cEnd - cStart; 566 faceDepth = depth - cellHeight; 567 if (dim == depth) { 568 PetscInt f, fStart, fEnd; 569 570 PetscCall(PetscCalloc1(numCells + 1, &off)); 571 /* Count neighboring cells */ 572 PetscCall(DMPlexGetHeightStratum(dm, cellHeight + 1, &fStart, &fEnd)); 573 for (f = fStart; f < fEnd; ++f) { 574 const PetscInt *support; 575 PetscInt supportSize; 576 PetscCall(DMPlexGetSupportSize(dm, f, &supportSize)); 577 PetscCall(DMPlexGetSupport(dm, f, &support)); 578 if (supportSize == 2) { 579 PetscInt numChildren; 580 581 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL)); 582 if (!numChildren) { 583 ++off[support[0] - cStart + 1]; 584 ++off[support[1] - cStart + 1]; 585 } 586 } 587 } 588 /* Prefix sum */ 589 for (c = 1; c <= numCells; ++c) off[c] += off[c - 1]; 590 if (adjacency) { 591 PetscInt *tmp; 592 593 PetscCall(PetscMalloc1(off[numCells], &adj)); 594 PetscCall(PetscMalloc1(numCells + 1, &tmp)); 595 PetscCall(PetscArraycpy(tmp, off, numCells + 1)); 596 /* Get neighboring cells */ 597 for (f = fStart; f < fEnd; ++f) { 598 const PetscInt *support; 599 PetscInt supportSize; 600 PetscCall(DMPlexGetSupportSize(dm, f, &supportSize)); 601 PetscCall(DMPlexGetSupport(dm, f, &support)); 602 if (supportSize == 2) { 603 PetscInt numChildren; 604 605 PetscCall(DMPlexGetTreeChildren(dm, f, &numChildren, NULL)); 606 if (!numChildren) { 607 adj[tmp[support[0] - cStart]++] = support[1]; 608 adj[tmp[support[1] - cStart]++] = support[0]; 609 } 610 } 611 } 612 for (c = 0; c < cEnd - cStart; ++c) PetscAssert(tmp[c] == off[c + 1], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Offset %" PetscInt_FMT " != %" PetscInt_FMT " for cell %" PetscInt_FMT, tmp[c], off[c], c + cStart); 613 PetscCall(PetscFree(tmp)); 614 } 615 if (numVertices) *numVertices = numCells; 616 if (offsets) *offsets = off; 617 if (adjacency) *adjacency = adj; 618 PetscFunctionReturn(PETSC_SUCCESS); 619 } 620 /* Setup face recognition */ 621 if (faceDepth == 1) { 622 PetscInt cornersSeen[30] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* Could use PetscBT */ 623 624 for (c = cStart; c < cEnd; ++c) { 625 PetscInt corners; 626 627 PetscCall(DMPlexGetConeSize(dm, c, &corners)); 628 if (!cornersSeen[corners]) { 629 PetscInt nFV; 630 631 PetscCheck(numFaceCases < maxFaceCases, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Exceeded maximum number of face recognition cases"); 632 cornersSeen[corners] = 1; 633 634 PetscCall(DMPlexGetNumFaceVertices(dm, cellDim, corners, &nFV)); 635 636 numFaceVertices[numFaceCases++] = nFV; 637 } 638 } 639 } 640 PetscCall(PetscCalloc1(numCells + 1, &off)); 641 /* Count neighboring cells */ 642 for (cell = cStart; cell < cEnd; ++cell) { 643 PetscInt numNeighbors = PETSC_DETERMINE, n; 644 645 PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &numNeighbors, &neighborCells)); 646 /* Get meet with each cell, and check with recognizer (could optimize to check each pair only once) */ 647 for (n = 0; n < numNeighbors; ++n) { 648 PetscInt cellPair[2]; 649 PetscBool found = faceDepth > 1 ? PETSC_TRUE : PETSC_FALSE; 650 PetscInt meetSize = 0; 651 const PetscInt *meet = NULL; 652 653 cellPair[0] = cell; 654 cellPair[1] = neighborCells[n]; 655 if (cellPair[0] == cellPair[1]) continue; 656 if (!found) { 657 PetscCall(DMPlexGetMeet(dm, 2, cellPair, &meetSize, &meet)); 658 if (meetSize) { 659 PetscInt f; 660 661 for (f = 0; f < numFaceCases; ++f) { 662 if (numFaceVertices[f] == meetSize) { 663 found = PETSC_TRUE; 664 break; 665 } 666 } 667 } 668 PetscCall(DMPlexRestoreMeet(dm, 2, cellPair, &meetSize, &meet)); 669 } 670 if (found) ++off[cell - cStart + 1]; 671 } 672 } 673 /* Prefix sum */ 674 for (cell = 1; cell <= numCells; ++cell) off[cell] += off[cell - 1]; 675 676 if (adjacency) { 677 PetscCall(PetscMalloc1(off[numCells], &adj)); 678 /* Get neighboring cells */ 679 for (cell = cStart; cell < cEnd; ++cell) { 680 PetscInt numNeighbors = PETSC_DETERMINE, n; 681 PetscInt cellOffset = 0; 682 683 PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &numNeighbors, &neighborCells)); 684 /* Get meet with each cell, and check with recognizer (could optimize to check each pair only once) */ 685 for (n = 0; n < numNeighbors; ++n) { 686 PetscInt cellPair[2]; 687 PetscBool found = faceDepth > 1 ? PETSC_TRUE : PETSC_FALSE; 688 PetscInt meetSize = 0; 689 const PetscInt *meet = NULL; 690 691 cellPair[0] = cell; 692 cellPair[1] = neighborCells[n]; 693 if (cellPair[0] == cellPair[1]) continue; 694 if (!found) { 695 PetscCall(DMPlexGetMeet(dm, 2, cellPair, &meetSize, &meet)); 696 if (meetSize) { 697 PetscInt f; 698 699 for (f = 0; f < numFaceCases; ++f) { 700 if (numFaceVertices[f] == meetSize) { 701 found = PETSC_TRUE; 702 break; 703 } 704 } 705 } 706 PetscCall(DMPlexRestoreMeet(dm, 2, cellPair, &meetSize, &meet)); 707 } 708 if (found) { 709 adj[off[cell - cStart] + cellOffset] = neighborCells[n]; 710 ++cellOffset; 711 } 712 } 713 } 714 } 715 PetscCall(PetscFree(neighborCells)); 716 if (numVertices) *numVertices = numCells; 717 if (offsets) *offsets = off; 718 if (adjacency) *adjacency = adj; 719 PetscFunctionReturn(PETSC_SUCCESS); 720 } 721 722 /*@ 723 PetscPartitionerDMPlexPartition - Create a non-overlapping partition of the cells in the mesh 724 725 Collective 726 727 Input Parameters: 728 + part - The `PetscPartitioner` 729 . targetSection - The `PetscSection` describing the absolute weight of each partition (can be `NULL`) 730 - dm - The mesh `DM` 731 732 Output Parameters: 733 + partSection - The `PetscSection` giving the division of points by partition 734 - partition - The list of points by partition 735 736 Level: developer 737 738 Note: 739 If the `DM` has a local section associated, each point to be partitioned will be weighted by the total number of dofs identified 740 by the section in the transitive closure of the point. 741 742 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscPartitioner`, `PetscSection`, `DMPlexDistribute()`, `PetscPartitionerCreate()`, `PetscSectionCreate()`, 743 `PetscSectionSetChart()`, `PetscPartitionerPartition()` 744 @*/ 745 PetscErrorCode PetscPartitionerDMPlexPartition(PetscPartitioner part, DM dm, PetscSection targetSection, PetscSection partSection, IS *partition) 746 { 747 PetscMPIInt size; 748 PetscBool isplex; 749 PetscSection vertSection = NULL, edgeSection = NULL; 750 751 PetscFunctionBegin; 752 PetscValidHeaderSpecific(part, PETSCPARTITIONER_CLASSID, 1); 753 PetscValidHeaderSpecific(dm, DM_CLASSID, 2); 754 if (targetSection) PetscValidHeaderSpecific(targetSection, PETSC_SECTION_CLASSID, 3); 755 PetscValidHeaderSpecific(partSection, PETSC_SECTION_CLASSID, 4); 756 PetscAssertPointer(partition, 5); 757 PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isplex)); 758 PetscCheck(isplex, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not for type %s", ((PetscObject)dm)->type_name); 759 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)part), &size)); 760 if (size == 1) { 761 PetscInt *points; 762 PetscInt cStart, cEnd, c; 763 764 PetscCall(DMPlexGetHeightStratum(dm, part->height, &cStart, &cEnd)); 765 PetscCall(PetscSectionReset(partSection)); 766 PetscCall(PetscSectionSetChart(partSection, 0, size)); 767 PetscCall(PetscSectionSetDof(partSection, 0, cEnd - cStart)); 768 PetscCall(PetscSectionSetUp(partSection)); 769 PetscCall(PetscMalloc1(cEnd - cStart, &points)); 770 for (c = cStart; c < cEnd; ++c) points[c] = c; 771 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)part), cEnd - cStart, points, PETSC_OWN_POINTER, partition)); 772 PetscFunctionReturn(PETSC_SUCCESS); 773 } 774 if (part->height == 0) { 775 PetscInt numVertices = 0; 776 PetscInt *start = NULL; 777 PetscInt *adjacency = NULL; 778 IS globalNumbering; 779 780 if (!part->noGraph || part->viewGraph) { 781 PetscCall(DMPlexCreatePartitionerGraph(dm, part->height, &numVertices, &start, &adjacency, &globalNumbering)); 782 } else { /* only compute the number of owned local vertices */ 783 const PetscInt *idxs; 784 PetscInt p, pStart, pEnd; 785 786 PetscCall(DMPlexGetHeightStratum(dm, part->height, &pStart, &pEnd)); 787 PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, 0, NULL, dm->sf, &globalNumbering)); 788 PetscCall(ISGetIndices(globalNumbering, &idxs)); 789 for (p = 0; p < pEnd - pStart; p++) numVertices += idxs[p] < 0 ? 0 : 1; 790 PetscCall(ISRestoreIndices(globalNumbering, &idxs)); 791 } 792 if (part->usevwgt) { 793 PetscSection section = dm->localSection, clSection = NULL; 794 IS clPoints = NULL; 795 const PetscInt *gid, *clIdx; 796 PetscInt v, p, pStart, pEnd; 797 798 /* dm->localSection encodes degrees of freedom per point, not per cell. We need to get the closure index to properly specify cell weights (aka dofs) */ 799 /* We do this only if the local section has been set */ 800 if (section) { 801 PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, &clSection, NULL)); 802 if (!clSection) PetscCall(DMPlexCreateClosureIndex(dm, NULL)); 803 PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, &clSection, &clPoints)); 804 PetscCall(ISGetIndices(clPoints, &clIdx)); 805 } 806 PetscCall(DMPlexGetHeightStratum(dm, part->height, &pStart, &pEnd)); 807 PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &vertSection)); 808 PetscCall(PetscSectionSetChart(vertSection, 0, numVertices)); 809 if (globalNumbering) { 810 PetscCall(ISGetIndices(globalNumbering, &gid)); 811 } else gid = NULL; 812 for (p = pStart, v = 0; p < pEnd; ++p) { 813 PetscInt dof = 1; 814 815 /* skip cells in the overlap */ 816 if (gid && gid[p - pStart] < 0) continue; 817 818 if (section) { 819 PetscInt cl, clSize, clOff; 820 821 dof = 0; 822 PetscCall(PetscSectionGetDof(clSection, p, &clSize)); 823 PetscCall(PetscSectionGetOffset(clSection, p, &clOff)); 824 for (cl = 0; cl < clSize; cl += 2) { 825 PetscInt clDof, clPoint = clIdx[clOff + cl]; /* odd indices are reserved for orientations */ 826 827 PetscCall(PetscSectionGetDof(section, clPoint, &clDof)); 828 dof += clDof; 829 } 830 } 831 PetscCheck(dof, PETSC_COMM_SELF, PETSC_ERR_SUP, "Number of dofs for point %" PetscInt_FMT " in the local section should be positive", p); 832 PetscCall(PetscSectionSetDof(vertSection, v, dof)); 833 v++; 834 } 835 if (globalNumbering) PetscCall(ISRestoreIndices(globalNumbering, &gid)); 836 if (clPoints) PetscCall(ISRestoreIndices(clPoints, &clIdx)); 837 PetscCall(PetscSectionSetUp(vertSection)); 838 } 839 if (part->useewgt) { 840 const PetscInt numEdges = start[numVertices]; 841 842 PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &edgeSection)); 843 PetscCall(PetscSectionSetChart(edgeSection, 0, numEdges)); 844 for (PetscInt e = 0; e < start[numVertices]; ++e) PetscCall(PetscSectionSetDof(edgeSection, e, 1)); 845 for (PetscInt v = 0; v < numVertices; ++v) { 846 DMPolytopeType ct; 847 848 // Assume v is the cell number 849 PetscCall(DMPlexGetCellType(dm, v, &ct)); 850 if (ct != DM_POLYTOPE_POINT_PRISM_TENSOR && ct != DM_POLYTOPE_SEG_PRISM_TENSOR && ct != DM_POLYTOPE_TRI_PRISM_TENSOR && ct != DM_POLYTOPE_QUAD_PRISM_TENSOR) continue; 851 852 for (PetscInt e = start[v]; e < start[v + 1]; ++e) PetscCall(PetscSectionSetDof(edgeSection, e, 3)); 853 } 854 PetscCall(PetscSectionSetUp(edgeSection)); 855 } 856 PetscCall(PetscPartitionerPartition(part, size, numVertices, start, adjacency, vertSection, edgeSection, targetSection, partSection, partition)); 857 PetscCall(PetscFree(start)); 858 PetscCall(PetscFree(adjacency)); 859 if (globalNumbering) { /* partition is wrt global unique numbering: change this to be wrt local numbering */ 860 const PetscInt *globalNum; 861 const PetscInt *partIdx; 862 PetscInt *map, cStart, cEnd; 863 PetscInt *adjusted, i, localSize, offset; 864 IS newPartition; 865 866 PetscCall(ISGetLocalSize(*partition, &localSize)); 867 PetscCall(PetscMalloc1(localSize, &adjusted)); 868 PetscCall(ISGetIndices(globalNumbering, &globalNum)); 869 PetscCall(ISGetIndices(*partition, &partIdx)); 870 PetscCall(PetscMalloc1(localSize, &map)); 871 PetscCall(DMPlexGetHeightStratum(dm, part->height, &cStart, &cEnd)); 872 for (i = cStart, offset = 0; i < cEnd; i++) { 873 if (globalNum[i - cStart] >= 0) map[offset++] = i; 874 } 875 for (i = 0; i < localSize; i++) adjusted[i] = map[partIdx[i]]; 876 PetscCall(PetscFree(map)); 877 PetscCall(ISRestoreIndices(*partition, &partIdx)); 878 PetscCall(ISRestoreIndices(globalNumbering, &globalNum)); 879 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, localSize, adjusted, PETSC_OWN_POINTER, &newPartition)); 880 PetscCall(ISDestroy(&globalNumbering)); 881 PetscCall(ISDestroy(partition)); 882 *partition = newPartition; 883 } 884 } else SETERRQ(PetscObjectComm((PetscObject)part), PETSC_ERR_ARG_OUTOFRANGE, "Invalid height %" PetscInt_FMT " for points to partition", part->height); 885 PetscCall(PetscSectionDestroy(&vertSection)); 886 PetscCall(PetscSectionDestroy(&edgeSection)); 887 PetscFunctionReturn(PETSC_SUCCESS); 888 } 889 890 /*@ 891 DMPlexGetPartitioner - Get the mesh partitioner 892 893 Not Collective 894 895 Input Parameter: 896 . dm - The `DM` 897 898 Output Parameter: 899 . part - The `PetscPartitioner` 900 901 Level: developer 902 903 Note: 904 This gets a borrowed reference, so the user should not destroy this `PetscPartitioner`. 905 906 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscPartitioner`, `PetscSection`, `DMPlexDistribute()`, `DMPlexSetPartitioner()`, `PetscPartitionerDMPlexPartition()`, `PetscPartitionerCreate()` 907 @*/ 908 PetscErrorCode DMPlexGetPartitioner(DM dm, PetscPartitioner *part) 909 { 910 DM_Plex *mesh = (DM_Plex *)dm->data; 911 912 PetscFunctionBegin; 913 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 914 PetscAssertPointer(part, 2); 915 *part = mesh->partitioner; 916 PetscFunctionReturn(PETSC_SUCCESS); 917 } 918 919 /*@ 920 DMPlexSetPartitioner - Set the mesh partitioner 921 922 logically Collective 923 924 Input Parameters: 925 + dm - The `DM` 926 - part - The partitioner 927 928 Level: developer 929 930 Note: 931 Any existing `PetscPartitioner` will be destroyed. 932 933 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscPartitioner`,`DMPlexDistribute()`, `DMPlexGetPartitioner()`, `PetscPartitionerCreate()` 934 @*/ 935 PetscErrorCode DMPlexSetPartitioner(DM dm, PetscPartitioner part) 936 { 937 DM_Plex *mesh = (DM_Plex *)dm->data; 938 939 PetscFunctionBegin; 940 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 941 PetscValidHeaderSpecific(part, PETSCPARTITIONER_CLASSID, 2); 942 PetscCall(PetscObjectReference((PetscObject)part)); 943 PetscCall(PetscPartitionerDestroy(&mesh->partitioner)); 944 mesh->partitioner = part; 945 PetscFunctionReturn(PETSC_SUCCESS); 946 } 947 948 static PetscErrorCode DMPlexAddClosure_Private(DM dm, PetscHSetI ht, PetscInt point) 949 { 950 const PetscInt *cone; 951 PetscInt coneSize, c; 952 PetscBool missing; 953 954 PetscFunctionBeginHot; 955 PetscCall(PetscHSetIQueryAdd(ht, point, &missing)); 956 if (missing) { 957 PetscCall(DMPlexGetCone(dm, point, &cone)); 958 PetscCall(DMPlexGetConeSize(dm, point, &coneSize)); 959 for (c = 0; c < coneSize; c++) PetscCall(DMPlexAddClosure_Private(dm, ht, cone[c])); 960 } 961 PetscFunctionReturn(PETSC_SUCCESS); 962 } 963 964 PETSC_UNUSED static PetscErrorCode DMPlexAddClosure_Tree(DM dm, PetscHSetI ht, PetscInt point, PetscBool up, PetscBool down) 965 { 966 PetscFunctionBegin; 967 if (up) { 968 PetscInt parent; 969 970 PetscCall(DMPlexGetTreeParent(dm, point, &parent, NULL)); 971 if (parent != point) { 972 PetscInt closureSize, *closure = NULL, i; 973 974 PetscCall(DMPlexGetTransitiveClosure(dm, parent, PETSC_TRUE, &closureSize, &closure)); 975 for (i = 0; i < closureSize; i++) { 976 PetscInt cpoint = closure[2 * i]; 977 978 PetscCall(PetscHSetIAdd(ht, cpoint)); 979 PetscCall(DMPlexAddClosure_Tree(dm, ht, cpoint, PETSC_TRUE, PETSC_FALSE)); 980 } 981 PetscCall(DMPlexRestoreTransitiveClosure(dm, parent, PETSC_TRUE, &closureSize, &closure)); 982 } 983 } 984 if (down) { 985 PetscInt numChildren; 986 const PetscInt *children; 987 988 PetscCall(DMPlexGetTreeChildren(dm, point, &numChildren, &children)); 989 if (numChildren) { 990 PetscInt i; 991 992 for (i = 0; i < numChildren; i++) { 993 PetscInt cpoint = children[i]; 994 995 PetscCall(PetscHSetIAdd(ht, cpoint)); 996 PetscCall(DMPlexAddClosure_Tree(dm, ht, cpoint, PETSC_FALSE, PETSC_TRUE)); 997 } 998 } 999 } 1000 PetscFunctionReturn(PETSC_SUCCESS); 1001 } 1002 1003 static PetscErrorCode DMPlexAddClosureTree_Up_Private(DM dm, PetscHSetI ht, PetscInt point) 1004 { 1005 PetscInt parent; 1006 1007 PetscFunctionBeginHot; 1008 PetscCall(DMPlexGetTreeParent(dm, point, &parent, NULL)); 1009 if (point != parent) { 1010 const PetscInt *cone; 1011 PetscInt coneSize, c; 1012 1013 PetscCall(DMPlexAddClosureTree_Up_Private(dm, ht, parent)); 1014 PetscCall(DMPlexAddClosure_Private(dm, ht, parent)); 1015 PetscCall(DMPlexGetCone(dm, parent, &cone)); 1016 PetscCall(DMPlexGetConeSize(dm, parent, &coneSize)); 1017 for (c = 0; c < coneSize; c++) { 1018 const PetscInt cp = cone[c]; 1019 1020 PetscCall(DMPlexAddClosureTree_Up_Private(dm, ht, cp)); 1021 } 1022 } 1023 PetscFunctionReturn(PETSC_SUCCESS); 1024 } 1025 1026 static PetscErrorCode DMPlexAddClosureTree_Down_Private(DM dm, PetscHSetI ht, PetscInt point) 1027 { 1028 PetscInt i, numChildren; 1029 const PetscInt *children; 1030 1031 PetscFunctionBeginHot; 1032 PetscCall(DMPlexGetTreeChildren(dm, point, &numChildren, &children)); 1033 for (i = 0; i < numChildren; i++) PetscCall(PetscHSetIAdd(ht, children[i])); 1034 PetscFunctionReturn(PETSC_SUCCESS); 1035 } 1036 1037 static PetscErrorCode DMPlexAddClosureTree_Private(DM dm, PetscHSetI ht, PetscInt point) 1038 { 1039 const PetscInt *cone; 1040 PetscInt coneSize, c; 1041 1042 PetscFunctionBeginHot; 1043 PetscCall(PetscHSetIAdd(ht, point)); 1044 PetscCall(DMPlexAddClosureTree_Up_Private(dm, ht, point)); 1045 PetscCall(DMPlexAddClosureTree_Down_Private(dm, ht, point)); 1046 PetscCall(DMPlexGetCone(dm, point, &cone)); 1047 PetscCall(DMPlexGetConeSize(dm, point, &coneSize)); 1048 for (c = 0; c < coneSize; c++) PetscCall(DMPlexAddClosureTree_Private(dm, ht, cone[c])); 1049 PetscFunctionReturn(PETSC_SUCCESS); 1050 } 1051 1052 PetscErrorCode DMPlexClosurePoints_Private(DM dm, PetscInt numPoints, const PetscInt points[], IS *closureIS) 1053 { 1054 DM_Plex *mesh = (DM_Plex *)dm->data; 1055 const PetscBool hasTree = (mesh->parentSection || mesh->childSection) ? PETSC_TRUE : PETSC_FALSE; 1056 PetscInt nelems, *elems, off = 0, p; 1057 PetscHSetI ht = NULL; 1058 1059 PetscFunctionBegin; 1060 PetscCall(PetscHSetICreate(&ht)); 1061 PetscCall(PetscHSetIResize(ht, numPoints * 16)); 1062 if (!hasTree) { 1063 for (p = 0; p < numPoints; ++p) PetscCall(DMPlexAddClosure_Private(dm, ht, points[p])); 1064 } else { 1065 #if 1 1066 for (p = 0; p < numPoints; ++p) PetscCall(DMPlexAddClosureTree_Private(dm, ht, points[p])); 1067 #else 1068 PetscInt *closure = NULL, closureSize, c; 1069 for (p = 0; p < numPoints; ++p) { 1070 PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closure)); 1071 for (c = 0; c < closureSize * 2; c += 2) { 1072 PetscCall(PetscHSetIAdd(ht, closure[c])); 1073 if (hasTree) PetscCall(DMPlexAddClosure_Tree(dm, ht, closure[c], PETSC_TRUE, PETSC_TRUE)); 1074 } 1075 } 1076 if (closure) PetscCall(DMPlexRestoreTransitiveClosure(dm, 0, PETSC_TRUE, NULL, &closure)); 1077 #endif 1078 } 1079 PetscCall(PetscHSetIGetSize(ht, &nelems)); 1080 PetscCall(PetscMalloc1(nelems, &elems)); 1081 PetscCall(PetscHSetIGetElems(ht, &off, elems)); 1082 PetscCall(PetscHSetIDestroy(&ht)); 1083 PetscCall(PetscSortInt(nelems, elems)); 1084 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nelems, elems, PETSC_OWN_POINTER, closureIS)); 1085 PetscFunctionReturn(PETSC_SUCCESS); 1086 } 1087 1088 /*@ 1089 DMPlexPartitionLabelClosure - Add the closure of all points to the partition label 1090 1091 Input Parameters: 1092 + dm - The `DM` 1093 - label - `DMLabel` assigning ranks to remote roots 1094 1095 Level: developer 1096 1097 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1098 @*/ 1099 PetscErrorCode DMPlexPartitionLabelClosure(DM dm, DMLabel label) 1100 { 1101 IS rankIS, pointIS, closureIS; 1102 const PetscInt *ranks, *points; 1103 PetscInt numRanks, numPoints, r; 1104 1105 PetscFunctionBegin; 1106 PetscCall(DMLabelGetValueIS(label, &rankIS)); 1107 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1108 PetscCall(ISGetIndices(rankIS, &ranks)); 1109 for (r = 0; r < numRanks; ++r) { 1110 const PetscInt rank = ranks[r]; 1111 PetscCall(DMLabelGetStratumIS(label, rank, &pointIS)); 1112 PetscCall(ISGetLocalSize(pointIS, &numPoints)); 1113 PetscCall(ISGetIndices(pointIS, &points)); 1114 PetscCall(DMPlexClosurePoints_Private(dm, numPoints, points, &closureIS)); 1115 PetscCall(ISRestoreIndices(pointIS, &points)); 1116 PetscCall(ISDestroy(&pointIS)); 1117 PetscCall(DMLabelSetStratumIS(label, rank, closureIS)); 1118 PetscCall(ISDestroy(&closureIS)); 1119 } 1120 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1121 PetscCall(ISDestroy(&rankIS)); 1122 PetscFunctionReturn(PETSC_SUCCESS); 1123 } 1124 1125 /*@ 1126 DMPlexPartitionLabelAdjacency - Add one level of adjacent points to the partition label 1127 1128 Input Parameters: 1129 + dm - The `DM` 1130 - label - `DMLabel` assigning ranks to remote roots 1131 1132 Level: developer 1133 1134 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1135 @*/ 1136 PetscErrorCode DMPlexPartitionLabelAdjacency(DM dm, DMLabel label) 1137 { 1138 IS rankIS, pointIS; 1139 const PetscInt *ranks, *points; 1140 PetscInt numRanks, numPoints, r, p, a, adjSize; 1141 PetscInt *adj = NULL; 1142 1143 PetscFunctionBegin; 1144 PetscCall(DMLabelGetValueIS(label, &rankIS)); 1145 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1146 PetscCall(ISGetIndices(rankIS, &ranks)); 1147 for (r = 0; r < numRanks; ++r) { 1148 const PetscInt rank = ranks[r]; 1149 1150 PetscCall(DMLabelGetStratumIS(label, rank, &pointIS)); 1151 PetscCall(ISGetLocalSize(pointIS, &numPoints)); 1152 PetscCall(ISGetIndices(pointIS, &points)); 1153 for (p = 0; p < numPoints; ++p) { 1154 adjSize = PETSC_DETERMINE; 1155 PetscCall(DMPlexGetAdjacency(dm, points[p], &adjSize, &adj)); 1156 for (a = 0; a < adjSize; ++a) PetscCall(DMLabelSetValue(label, adj[a], rank)); 1157 } 1158 PetscCall(ISRestoreIndices(pointIS, &points)); 1159 PetscCall(ISDestroy(&pointIS)); 1160 } 1161 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1162 PetscCall(ISDestroy(&rankIS)); 1163 PetscCall(PetscFree(adj)); 1164 PetscFunctionReturn(PETSC_SUCCESS); 1165 } 1166 1167 /*@ 1168 DMPlexPartitionLabelPropagate - Propagate points in a partition label over the point `PetscSF` 1169 1170 Input Parameters: 1171 + dm - The `DM` 1172 - label - `DMLabel` assigning ranks to remote roots 1173 1174 Level: developer 1175 1176 Note: 1177 This is required when generating multi-level overlaps to capture 1178 overlap points from non-neighbouring partitions. 1179 1180 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1181 @*/ 1182 PetscErrorCode DMPlexPartitionLabelPropagate(DM dm, DMLabel label) 1183 { 1184 MPI_Comm comm; 1185 PetscMPIInt rank; 1186 PetscSF sfPoint; 1187 DMLabel lblRoots, lblLeaves; 1188 IS rankIS, pointIS; 1189 const PetscInt *ranks; 1190 PetscInt numRanks, r; 1191 1192 PetscFunctionBegin; 1193 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1194 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1195 PetscCall(DMGetPointSF(dm, &sfPoint)); 1196 /* Pull point contributions from remote leaves into local roots */ 1197 PetscCall(DMLabelGather(label, sfPoint, &lblLeaves)); 1198 PetscCall(DMLabelGetValueIS(lblLeaves, &rankIS)); 1199 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1200 PetscCall(ISGetIndices(rankIS, &ranks)); 1201 for (r = 0; r < numRanks; ++r) { 1202 const PetscInt remoteRank = ranks[r]; 1203 if (remoteRank == rank) continue; 1204 PetscCall(DMLabelGetStratumIS(lblLeaves, remoteRank, &pointIS)); 1205 PetscCall(DMLabelInsertIS(label, pointIS, remoteRank)); 1206 PetscCall(ISDestroy(&pointIS)); 1207 } 1208 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1209 PetscCall(ISDestroy(&rankIS)); 1210 PetscCall(DMLabelDestroy(&lblLeaves)); 1211 /* Push point contributions from roots into remote leaves */ 1212 PetscCall(DMLabelDistribute(label, sfPoint, &lblRoots)); 1213 PetscCall(DMLabelGetValueIS(lblRoots, &rankIS)); 1214 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1215 PetscCall(ISGetIndices(rankIS, &ranks)); 1216 for (r = 0; r < numRanks; ++r) { 1217 const PetscInt remoteRank = ranks[r]; 1218 if (remoteRank == rank) continue; 1219 PetscCall(DMLabelGetStratumIS(lblRoots, remoteRank, &pointIS)); 1220 PetscCall(DMLabelInsertIS(label, pointIS, remoteRank)); 1221 PetscCall(ISDestroy(&pointIS)); 1222 } 1223 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1224 PetscCall(ISDestroy(&rankIS)); 1225 PetscCall(DMLabelDestroy(&lblRoots)); 1226 PetscFunctionReturn(PETSC_SUCCESS); 1227 } 1228 1229 /*@ 1230 DMPlexPartitionLabelInvert - Create a partition label of remote roots from a local root label 1231 1232 Input Parameters: 1233 + dm - The `DM` 1234 . rootLabel - `DMLabel` assigning ranks to local roots 1235 - processSF - A star forest mapping into the local index on each remote rank 1236 1237 Output Parameter: 1238 . leafLabel - `DMLabel` assigning ranks to remote roots 1239 1240 Level: developer 1241 1242 Note: 1243 The rootLabel defines a send pattern by mapping local points to remote target ranks. The 1244 resulting leafLabel is a receiver mapping of remote roots to their parent rank. 1245 1246 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1247 @*/ 1248 PetscErrorCode DMPlexPartitionLabelInvert(DM dm, DMLabel rootLabel, PetscSF processSF, DMLabel leafLabel) 1249 { 1250 MPI_Comm comm; 1251 PetscMPIInt rank, size, r; 1252 PetscInt p, n, numNeighbors, numPoints, dof, off, rootSize, l, nleaves, leafSize; 1253 PetscSF sfPoint; 1254 PetscSection rootSection; 1255 PetscSFNode *rootPoints, *leafPoints; 1256 const PetscSFNode *remote; 1257 const PetscInt *local, *neighbors; 1258 IS valueIS; 1259 PetscBool mpiOverflow = PETSC_FALSE; 1260 1261 PetscFunctionBegin; 1262 PetscCall(PetscLogEventBegin(DMPLEX_PartLabelInvert, dm, 0, 0, 0)); 1263 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1264 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1265 PetscCallMPI(MPI_Comm_size(comm, &size)); 1266 PetscCall(DMGetPointSF(dm, &sfPoint)); 1267 1268 /* Convert to (point, rank) and use actual owners */ 1269 PetscCall(PetscSectionCreate(comm, &rootSection)); 1270 PetscCall(PetscSectionSetChart(rootSection, 0, size)); 1271 PetscCall(DMLabelGetValueIS(rootLabel, &valueIS)); 1272 PetscCall(ISGetLocalSize(valueIS, &numNeighbors)); 1273 PetscCall(ISGetIndices(valueIS, &neighbors)); 1274 for (n = 0; n < numNeighbors; ++n) { 1275 PetscCall(DMLabelGetStratumSize(rootLabel, neighbors[n], &numPoints)); 1276 PetscCall(PetscSectionAddDof(rootSection, neighbors[n], numPoints)); 1277 } 1278 PetscCall(PetscSectionSetUp(rootSection)); 1279 PetscCall(PetscSectionGetStorageSize(rootSection, &rootSize)); 1280 PetscCall(PetscMalloc1(rootSize, &rootPoints)); 1281 PetscCall(PetscSFGetGraph(sfPoint, NULL, &nleaves, &local, &remote)); 1282 for (n = 0; n < numNeighbors; ++n) { 1283 IS pointIS; 1284 const PetscInt *points; 1285 1286 PetscCall(PetscSectionGetOffset(rootSection, neighbors[n], &off)); 1287 PetscCall(DMLabelGetStratumIS(rootLabel, neighbors[n], &pointIS)); 1288 PetscCall(ISGetLocalSize(pointIS, &numPoints)); 1289 PetscCall(ISGetIndices(pointIS, &points)); 1290 for (p = 0; p < numPoints; ++p) { 1291 if (local) PetscCall(PetscFindInt(points[p], nleaves, local, &l)); 1292 else l = -1; 1293 if (l >= 0) { 1294 rootPoints[off + p] = remote[l]; 1295 } else { 1296 rootPoints[off + p].index = points[p]; 1297 rootPoints[off + p].rank = rank; 1298 } 1299 } 1300 PetscCall(ISRestoreIndices(pointIS, &points)); 1301 PetscCall(ISDestroy(&pointIS)); 1302 } 1303 1304 /* Try to communicate overlap using All-to-All */ 1305 if (!processSF) { 1306 PetscCount counter = 0; 1307 PetscBool locOverflow = PETSC_FALSE; 1308 PetscMPIInt *scounts, *sdispls, *rcounts, *rdispls; 1309 1310 PetscCall(PetscCalloc4(size, &scounts, size, &sdispls, size, &rcounts, size, &rdispls)); 1311 for (n = 0; n < numNeighbors; ++n) { 1312 PetscCall(PetscSectionGetDof(rootSection, neighbors[n], &dof)); 1313 PetscCall(PetscSectionGetOffset(rootSection, neighbors[n], &off)); 1314 #if defined(PETSC_USE_64BIT_INDICES) 1315 if (dof > PETSC_MPI_INT_MAX) { 1316 locOverflow = PETSC_TRUE; 1317 break; 1318 } 1319 if (off > PETSC_MPI_INT_MAX) { 1320 locOverflow = PETSC_TRUE; 1321 break; 1322 } 1323 #endif 1324 PetscCall(PetscMPIIntCast(dof, &scounts[neighbors[n]])); 1325 PetscCall(PetscMPIIntCast(off, &sdispls[neighbors[n]])); 1326 } 1327 PetscCallMPI(MPI_Alltoall(scounts, 1, MPI_INT, rcounts, 1, MPI_INT, comm)); 1328 for (r = 0; r < size; ++r) { 1329 PetscCall(PetscMPIIntCast(counter, &rdispls[r])); 1330 counter += rcounts[r]; 1331 } 1332 if (counter > PETSC_MPI_INT_MAX) locOverflow = PETSC_TRUE; 1333 PetscCallMPI(MPIU_Allreduce(&locOverflow, &mpiOverflow, 1, MPIU_BOOL, MPI_LOR, comm)); 1334 if (!mpiOverflow) { 1335 PetscCall(PetscInfo(dm, "Using MPI_Alltoallv() for mesh distribution\n")); 1336 PetscCall(PetscIntCast(counter, &leafSize)); 1337 PetscCall(PetscMalloc1(leafSize, &leafPoints)); 1338 PetscCallMPI(MPI_Alltoallv(rootPoints, scounts, sdispls, MPIU_SF_NODE, leafPoints, rcounts, rdispls, MPIU_SF_NODE, comm)); 1339 } 1340 PetscCall(PetscFree4(scounts, sdispls, rcounts, rdispls)); 1341 } 1342 1343 /* Communicate overlap using process star forest */ 1344 if (processSF || mpiOverflow) { 1345 PetscSF procSF; 1346 PetscSection leafSection; 1347 1348 if (processSF) { 1349 PetscCall(PetscInfo(dm, "Using processSF for mesh distribution\n")); 1350 PetscCall(PetscObjectReference((PetscObject)processSF)); 1351 procSF = processSF; 1352 } else { 1353 PetscCall(PetscInfo(dm, "Using processSF for mesh distribution (MPI overflow)\n")); 1354 PetscCall(PetscSFCreate(comm, &procSF)); 1355 PetscCall(PetscSFSetGraphWithPattern(procSF, NULL, PETSCSF_PATTERN_ALLTOALL)); 1356 } 1357 1358 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &leafSection)); 1359 PetscCall(DMPlexDistributeData(dm, procSF, rootSection, MPIU_SF_NODE, rootPoints, leafSection, (void **)&leafPoints)); 1360 PetscCall(PetscSectionGetStorageSize(leafSection, &leafSize)); 1361 PetscCall(PetscSectionDestroy(&leafSection)); 1362 PetscCall(PetscSFDestroy(&procSF)); 1363 } 1364 1365 for (p = 0; p < leafSize; p++) PetscCall(DMLabelSetValue(leafLabel, leafPoints[p].index, leafPoints[p].rank)); 1366 1367 PetscCall(ISRestoreIndices(valueIS, &neighbors)); 1368 PetscCall(ISDestroy(&valueIS)); 1369 PetscCall(PetscSectionDestroy(&rootSection)); 1370 PetscCall(PetscFree(rootPoints)); 1371 PetscCall(PetscFree(leafPoints)); 1372 PetscCall(PetscLogEventEnd(DMPLEX_PartLabelInvert, dm, 0, 0, 0)); 1373 PetscFunctionReturn(PETSC_SUCCESS); 1374 } 1375 1376 /*@ 1377 DMPlexPartitionLabelCreateSF - Create a star forest from a label that assigns ranks to points 1378 1379 Input Parameters: 1380 + dm - The `DM` 1381 . label - `DMLabel` assigning ranks to remote roots 1382 - sortRanks - Whether or not to sort the `PetscSF` leaves by rank 1383 1384 Output Parameter: 1385 . sf - The star forest communication context encapsulating the defined mapping 1386 1387 Level: developer 1388 1389 Note: 1390 The incoming label is a receiver mapping of remote points to their parent rank. 1391 1392 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `PetscSF`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1393 @*/ 1394 PetscErrorCode DMPlexPartitionLabelCreateSF(DM dm, DMLabel label, PetscBool sortRanks, PetscSF *sf) 1395 { 1396 PetscMPIInt rank; 1397 PetscInt n, numRemote, p, numPoints, pStart, pEnd, idx = 0, nNeighbors; 1398 PetscSFNode *remotePoints; 1399 IS remoteRootIS, neighborsIS; 1400 const PetscInt *remoteRoots, *neighbors; 1401 PetscMPIInt myRank; 1402 1403 PetscFunctionBegin; 1404 PetscCall(PetscLogEventBegin(DMPLEX_PartLabelCreateSF, dm, 0, 0, 0)); 1405 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 1406 PetscCall(DMLabelGetValueIS(label, &neighborsIS)); 1407 1408 if (sortRanks) { 1409 IS is; 1410 1411 PetscCall(ISDuplicate(neighborsIS, &is)); 1412 PetscCall(ISSort(is)); 1413 PetscCall(ISDestroy(&neighborsIS)); 1414 neighborsIS = is; 1415 } 1416 myRank = sortRanks ? -1 : rank; 1417 1418 PetscCall(ISGetLocalSize(neighborsIS, &nNeighbors)); 1419 PetscCall(ISGetIndices(neighborsIS, &neighbors)); 1420 for (numRemote = 0, n = 0; n < nNeighbors; ++n) { 1421 PetscCall(DMLabelGetStratumSize(label, neighbors[n], &numPoints)); 1422 numRemote += numPoints; 1423 } 1424 PetscCall(PetscMalloc1(numRemote, &remotePoints)); 1425 1426 /* Put owned points first if not sorting the ranks */ 1427 if (!sortRanks) { 1428 PetscCall(DMLabelGetStratumSize(label, rank, &numPoints)); 1429 if (numPoints > 0) { 1430 PetscCall(DMLabelGetStratumIS(label, rank, &remoteRootIS)); 1431 PetscCall(ISGetIndices(remoteRootIS, &remoteRoots)); 1432 for (p = 0; p < numPoints; p++) { 1433 remotePoints[idx].index = remoteRoots[p]; 1434 remotePoints[idx].rank = rank; 1435 idx++; 1436 } 1437 PetscCall(ISRestoreIndices(remoteRootIS, &remoteRoots)); 1438 PetscCall(ISDestroy(&remoteRootIS)); 1439 } 1440 } 1441 1442 /* Now add remote points */ 1443 for (n = 0; n < nNeighbors; ++n) { 1444 PetscMPIInt nn; 1445 1446 PetscCall(PetscMPIIntCast(neighbors[n], &nn)); 1447 PetscCall(DMLabelGetStratumSize(label, nn, &numPoints)); 1448 if (nn == myRank || numPoints <= 0) continue; 1449 PetscCall(DMLabelGetStratumIS(label, nn, &remoteRootIS)); 1450 PetscCall(ISGetIndices(remoteRootIS, &remoteRoots)); 1451 for (p = 0; p < numPoints; p++) { 1452 remotePoints[idx].index = remoteRoots[p]; 1453 remotePoints[idx].rank = nn; 1454 idx++; 1455 } 1456 PetscCall(ISRestoreIndices(remoteRootIS, &remoteRoots)); 1457 PetscCall(ISDestroy(&remoteRootIS)); 1458 } 1459 1460 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), sf)); 1461 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 1462 PetscCall(PetscSFSetGraph(*sf, pEnd - pStart, numRemote, NULL, PETSC_OWN_POINTER, remotePoints, PETSC_OWN_POINTER)); 1463 PetscCall(PetscSFSetUp(*sf)); 1464 PetscCall(ISDestroy(&neighborsIS)); 1465 PetscCall(PetscLogEventEnd(DMPLEX_PartLabelCreateSF, dm, 0, 0, 0)); 1466 PetscFunctionReturn(PETSC_SUCCESS); 1467 } 1468 1469 #if defined(PETSC_HAVE_PARMETIS) 1470 #include <parmetis.h> 1471 #endif 1472 1473 /* The two functions below are used by DMPlexRebalanceSharedPoints which errors 1474 * when PETSc is built without ParMETIS. To avoid -Wunused-function, we take 1475 * them out in that case. */ 1476 #if defined(PETSC_HAVE_PARMETIS) 1477 /* 1478 DMPlexRewriteSF - Rewrites the ownership of the `PetscSF` of a `DM` (in place). 1479 1480 Input parameters:b 1481 + dm - The `DMPLEX` object. 1482 . n - The number of points. 1483 . pointsToRewrite - The points in the `PetscSF` whose ownership will change. 1484 . targetOwners - New owner for each element in pointsToRewrite. 1485 - degrees - Degrees of the points in the `PetscSF` as obtained by `PetscSFComputeDegreeBegin()`/`PetscSFComputeDegreeEnd()`. 1486 1487 Level: developer 1488 1489 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `PetscSF`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1490 */ 1491 static PetscErrorCode DMPlexRewriteSF(DM dm, PetscInt n, PetscInt *pointsToRewrite, PetscInt *targetOwners, const PetscInt *degrees) 1492 { 1493 PetscInt pStart, pEnd, i, j, counter, leafCounter, sumDegrees, nroots, nleafs; 1494 PetscInt *cumSumDegrees, *newOwners, *newNumbers, *rankOnLeafs, *locationsOfLeafs, *remoteLocalPointOfLeafs, *points, *leafsNew; 1495 PetscSFNode *leafLocationsNew; 1496 const PetscSFNode *iremote; 1497 const PetscInt *ilocal; 1498 PetscBool *isLeaf; 1499 PetscSF sf; 1500 MPI_Comm comm; 1501 PetscMPIInt rank, size; 1502 1503 PetscFunctionBegin; 1504 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1505 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1506 PetscCallMPI(MPI_Comm_size(comm, &size)); 1507 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 1508 1509 PetscCall(DMGetPointSF(dm, &sf)); 1510 PetscCall(PetscSFGetGraph(sf, &nroots, &nleafs, &ilocal, &iremote)); 1511 PetscCall(PetscMalloc1(pEnd - pStart, &isLeaf)); 1512 for (i = 0; i < pEnd - pStart; i++) isLeaf[i] = PETSC_FALSE; 1513 for (i = 0; i < nleafs; i++) isLeaf[ilocal[i] - pStart] = PETSC_TRUE; 1514 1515 PetscCall(PetscMalloc1(pEnd - pStart + 1, &cumSumDegrees)); 1516 cumSumDegrees[0] = 0; 1517 for (i = 1; i <= pEnd - pStart; i++) cumSumDegrees[i] = cumSumDegrees[i - 1] + degrees[i - 1]; 1518 sumDegrees = cumSumDegrees[pEnd - pStart]; 1519 /* get the location of my leafs (we have sumDegrees many leafs pointing at our roots) */ 1520 1521 PetscCall(PetscMalloc1(sumDegrees, &locationsOfLeafs)); 1522 PetscCall(PetscMalloc1(pEnd - pStart, &rankOnLeafs)); 1523 for (i = 0; i < pEnd - pStart; i++) rankOnLeafs[i] = rank; 1524 PetscCall(PetscSFGatherBegin(sf, MPIU_INT, rankOnLeafs, locationsOfLeafs)); 1525 PetscCall(PetscSFGatherEnd(sf, MPIU_INT, rankOnLeafs, locationsOfLeafs)); 1526 PetscCall(PetscFree(rankOnLeafs)); 1527 1528 /* get the remote local points of my leaves */ 1529 PetscCall(PetscMalloc1(sumDegrees, &remoteLocalPointOfLeafs)); 1530 PetscCall(PetscMalloc1(pEnd - pStart, &points)); 1531 for (i = 0; i < pEnd - pStart; i++) points[i] = pStart + i; 1532 PetscCall(PetscSFGatherBegin(sf, MPIU_INT, points, remoteLocalPointOfLeafs)); 1533 PetscCall(PetscSFGatherEnd(sf, MPIU_INT, points, remoteLocalPointOfLeafs)); 1534 PetscCall(PetscFree(points)); 1535 /* Figure out the new owners of the vertices that are up for grabs and their numbers on the new owners */ 1536 PetscCall(PetscMalloc1(pEnd - pStart, &newOwners)); 1537 PetscCall(PetscMalloc1(pEnd - pStart, &newNumbers)); 1538 for (i = 0; i < pEnd - pStart; i++) { 1539 newOwners[i] = -1; 1540 newNumbers[i] = -1; 1541 } 1542 { 1543 PetscInt oldNumber, newNumber, oldOwner, newOwner; 1544 for (i = 0; i < n; i++) { 1545 oldNumber = pointsToRewrite[i]; 1546 newNumber = -1; 1547 oldOwner = rank; 1548 newOwner = targetOwners[i]; 1549 if (oldOwner == newOwner) { 1550 newNumber = oldNumber; 1551 } else { 1552 for (j = 0; j < degrees[oldNumber]; j++) { 1553 if (locationsOfLeafs[cumSumDegrees[oldNumber] + j] == newOwner) { 1554 newNumber = remoteLocalPointOfLeafs[cumSumDegrees[oldNumber] + j]; 1555 break; 1556 } 1557 } 1558 } 1559 PetscCheck(newNumber != -1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Couldn't find the new owner of vertex."); 1560 1561 newOwners[oldNumber] = newOwner; 1562 newNumbers[oldNumber] = newNumber; 1563 } 1564 } 1565 PetscCall(PetscFree(cumSumDegrees)); 1566 PetscCall(PetscFree(locationsOfLeafs)); 1567 PetscCall(PetscFree(remoteLocalPointOfLeafs)); 1568 1569 PetscCall(PetscSFBcastBegin(sf, MPIU_INT, newOwners, newOwners, MPI_REPLACE)); 1570 PetscCall(PetscSFBcastEnd(sf, MPIU_INT, newOwners, newOwners, MPI_REPLACE)); 1571 PetscCall(PetscSFBcastBegin(sf, MPIU_INT, newNumbers, newNumbers, MPI_REPLACE)); 1572 PetscCall(PetscSFBcastEnd(sf, MPIU_INT, newNumbers, newNumbers, MPI_REPLACE)); 1573 1574 /* Now count how many leafs we have on each processor. */ 1575 leafCounter = 0; 1576 for (i = 0; i < pEnd - pStart; i++) { 1577 if (newOwners[i] >= 0) { 1578 if (newOwners[i] != rank) leafCounter++; 1579 } else { 1580 if (isLeaf[i]) leafCounter++; 1581 } 1582 } 1583 1584 /* Now set up the new sf by creating the leaf arrays */ 1585 PetscCall(PetscMalloc1(leafCounter, &leafsNew)); 1586 PetscCall(PetscMalloc1(leafCounter, &leafLocationsNew)); 1587 1588 leafCounter = 0; 1589 counter = 0; 1590 for (i = 0; i < pEnd - pStart; i++) { 1591 if (newOwners[i] >= 0) { 1592 if (newOwners[i] != rank) { 1593 leafsNew[leafCounter] = i; 1594 leafLocationsNew[leafCounter].rank = newOwners[i]; 1595 leafLocationsNew[leafCounter].index = newNumbers[i]; 1596 leafCounter++; 1597 } 1598 } else { 1599 if (isLeaf[i]) { 1600 leafsNew[leafCounter] = i; 1601 leafLocationsNew[leafCounter].rank = iremote[counter].rank; 1602 leafLocationsNew[leafCounter].index = iremote[counter].index; 1603 leafCounter++; 1604 } 1605 } 1606 if (isLeaf[i]) counter++; 1607 } 1608 1609 PetscCall(PetscSFSetGraph(sf, nroots, leafCounter, leafsNew, PETSC_OWN_POINTER, leafLocationsNew, PETSC_OWN_POINTER)); 1610 PetscCall(PetscFree(newOwners)); 1611 PetscCall(PetscFree(newNumbers)); 1612 PetscCall(PetscFree(isLeaf)); 1613 PetscFunctionReturn(PETSC_SUCCESS); 1614 } 1615 1616 static PetscErrorCode DMPlexViewDistribution(MPI_Comm comm, PetscInt n, PetscInt skip, PetscInt *vtxwgt, PetscInt *part, PetscViewer viewer) 1617 { 1618 PetscInt *distribution, min, max, sum; 1619 PetscMPIInt rank, size; 1620 1621 PetscFunctionBegin; 1622 PetscCallMPI(MPI_Comm_size(comm, &size)); 1623 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1624 PetscCall(PetscCalloc1(size, &distribution)); 1625 for (PetscInt i = 0; i < n; i++) { 1626 if (part) distribution[part[i]] += vtxwgt[skip * i]; 1627 else distribution[rank] += vtxwgt[skip * i]; 1628 } 1629 PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, distribution, size, MPIU_INT, MPI_SUM, comm)); 1630 min = distribution[0]; 1631 max = distribution[0]; 1632 sum = distribution[0]; 1633 for (PetscInt i = 1; i < size; i++) { 1634 if (distribution[i] < min) min = distribution[i]; 1635 if (distribution[i] > max) max = distribution[i]; 1636 sum += distribution[i]; 1637 } 1638 PetscCall(PetscViewerASCIIPrintf(viewer, "Min: %" PetscInt_FMT ", Avg: %" PetscInt_FMT ", Max: %" PetscInt_FMT ", Balance: %f\n", min, sum / size, max, (max * 1. * size) / sum)); 1639 PetscCall(PetscFree(distribution)); 1640 PetscFunctionReturn(PETSC_SUCCESS); 1641 } 1642 1643 #endif 1644 1645 /*@ 1646 DMPlexRebalanceSharedPoints - Redistribute points in the plex that are shared in order to achieve better balancing. This routine updates the `PointSF` of the `DM` inplace. 1647 1648 Input Parameters: 1649 + dm - The `DMPLEX` object. 1650 . entityDepth - depth of the entity to balance (0 -> balance vertices). 1651 . useInitialGuess - whether to use the current distribution as initial guess (only used by ParMETIS). 1652 - parallel - whether to use ParMETIS and do the partition in parallel or whether to gather the graph onto a single process and use METIS. 1653 1654 Output Parameter: 1655 . success - whether the graph partitioning was successful or not, optional. Unsuccessful simply means no change to the partitioning 1656 1657 Options Database Keys: 1658 + -dm_plex_rebalance_shared_points_parmetis - Use ParMetis instead of Metis for the partitioner 1659 . -dm_plex_rebalance_shared_points_use_initial_guess - Use current partition to bootstrap ParMetis partition 1660 . -dm_plex_rebalance_shared_points_use_mat_partitioning - Use the MatPartitioning object to perform the partition, the prefix for those operations is -dm_plex_rebalance_shared_points_ 1661 - -dm_plex_rebalance_shared_points_monitor - Monitor the shared points rebalance process 1662 1663 Level: intermediate 1664 1665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1666 @*/ 1667 PetscErrorCode DMPlexRebalanceSharedPoints(DM dm, PetscInt entityDepth, PetscBool useInitialGuess, PetscBool parallel, PetscBool *success) 1668 { 1669 #if defined(PETSC_HAVE_PARMETIS) 1670 PetscSF sf; 1671 PetscInt ierr, i, j, idx, jdx; 1672 PetscInt eBegin, eEnd, nroots, nleafs, pStart, pEnd; 1673 const PetscInt *degrees, *ilocal; 1674 const PetscSFNode *iremote; 1675 PetscBool *toBalance, *isLeaf, *isExclusivelyOwned, *isNonExclusivelyOwned; 1676 PetscInt numExclusivelyOwned, numNonExclusivelyOwned; 1677 PetscMPIInt rank, size; 1678 PetscInt *globalNumbersOfLocalOwnedVertices, *leafGlobalNumbers; 1679 const PetscInt *cumSumVertices; 1680 PetscInt offset, counter; 1681 PetscInt *vtxwgt; 1682 const PetscInt *xadj, *adjncy; 1683 PetscInt *part, *options; 1684 PetscInt nparts, wgtflag, numflag, ncon, edgecut; 1685 real_t *ubvec; 1686 PetscInt *firstVertices, *renumbering; 1687 PetscInt failed, failedGlobal; 1688 MPI_Comm comm; 1689 Mat A; 1690 PetscViewer viewer; 1691 PetscViewerFormat format; 1692 PetscLayout layout; 1693 real_t *tpwgts; 1694 PetscMPIInt *counts, *mpiCumSumVertices; 1695 PetscInt *pointsToRewrite; 1696 PetscInt numRows; 1697 PetscBool done, usematpartitioning = PETSC_FALSE; 1698 IS ispart = NULL; 1699 MatPartitioning mp; 1700 const char *prefix; 1701 1702 PetscFunctionBegin; 1703 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1704 PetscCallMPI(MPI_Comm_size(comm, &size)); 1705 if (size == 1) { 1706 if (success) *success = PETSC_TRUE; 1707 PetscFunctionReturn(PETSC_SUCCESS); 1708 } 1709 if (success) *success = PETSC_FALSE; 1710 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1711 1712 parallel = PETSC_FALSE; 1713 useInitialGuess = PETSC_FALSE; 1714 PetscObjectOptionsBegin((PetscObject)dm); 1715 PetscCall(PetscOptionsName("-dm_plex_rebalance_shared_points_parmetis", "Use ParMetis instead of Metis for the partitioner", "DMPlexRebalanceSharedPoints", ¶llel)); 1716 PetscCall(PetscOptionsBool("-dm_plex_rebalance_shared_points_use_initial_guess", "Use current partition to bootstrap ParMetis partition", "DMPlexRebalanceSharedPoints", useInitialGuess, &useInitialGuess, NULL)); 1717 PetscCall(PetscOptionsBool("-dm_plex_rebalance_shared_points_use_mat_partitioning", "Use the MatPartitioning object to partition", "DMPlexRebalanceSharedPoints", usematpartitioning, &usematpartitioning, NULL)); 1718 PetscCall(PetscOptionsViewer("-dm_plex_rebalance_shared_points_monitor", "Monitor the shared points rebalance process", "DMPlexRebalanceSharedPoints", &viewer, &format, NULL)); 1719 PetscOptionsEnd(); 1720 if (viewer) PetscCall(PetscViewerPushFormat(viewer, format)); 1721 1722 PetscCall(PetscLogEventBegin(DMPLEX_RebalanceSharedPoints, dm, 0, 0, 0)); 1723 1724 PetscCall(DMGetOptionsPrefix(dm, &prefix)); 1725 PetscCall(PetscOptionsCreateViewer(comm, ((PetscObject)dm)->options, prefix, "-dm_rebalance_partition_view", &viewer, &format, NULL)); 1726 if (viewer) PetscCall(PetscViewerPushFormat(viewer, format)); 1727 1728 /* Figure out all points in the plex that we are interested in balancing. */ 1729 PetscCall(DMPlexGetDepthStratum(dm, entityDepth, &eBegin, &eEnd)); 1730 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 1731 PetscCall(PetscMalloc1(pEnd - pStart, &toBalance)); 1732 for (i = 0; i < pEnd - pStart; i++) toBalance[i] = (PetscBool)(i >= eBegin && i < eEnd); 1733 1734 /* There are three types of points: 1735 * exclusivelyOwned: points that are owned by this process and only seen by this process 1736 * nonExclusivelyOwned: points that are owned by this process but seen by at least another process 1737 * leaf: a point that is seen by this process but owned by a different process 1738 */ 1739 PetscCall(DMGetPointSF(dm, &sf)); 1740 PetscCall(PetscSFGetGraph(sf, &nroots, &nleafs, &ilocal, &iremote)); 1741 PetscCall(PetscMalloc1(pEnd - pStart, &isLeaf)); 1742 PetscCall(PetscMalloc1(pEnd - pStart, &isNonExclusivelyOwned)); 1743 PetscCall(PetscMalloc1(pEnd - pStart, &isExclusivelyOwned)); 1744 for (i = 0; i < pEnd - pStart; i++) { 1745 isNonExclusivelyOwned[i] = PETSC_FALSE; 1746 isExclusivelyOwned[i] = PETSC_FALSE; 1747 isLeaf[i] = PETSC_FALSE; 1748 } 1749 1750 /* mark all the leafs */ 1751 for (i = 0; i < nleafs; i++) isLeaf[ilocal[i] - pStart] = PETSC_TRUE; 1752 1753 /* for an owned point, we can figure out whether another processor sees it or 1754 * not by calculating its degree */ 1755 PetscCall(PetscSFComputeDegreeBegin(sf, °rees)); 1756 PetscCall(PetscSFComputeDegreeEnd(sf, °rees)); 1757 numExclusivelyOwned = 0; 1758 numNonExclusivelyOwned = 0; 1759 for (i = 0; i < pEnd - pStart; i++) { 1760 if (toBalance[i]) { 1761 if (degrees[i] > 0) { 1762 isNonExclusivelyOwned[i] = PETSC_TRUE; 1763 numNonExclusivelyOwned += 1; 1764 } else { 1765 if (!isLeaf[i]) { 1766 isExclusivelyOwned[i] = PETSC_TRUE; 1767 numExclusivelyOwned += 1; 1768 } 1769 } 1770 } 1771 } 1772 1773 /* Build a graph with one vertex per core representing the 1774 * exclusively owned points and then one vertex per nonExclusively owned 1775 * point. */ 1776 PetscCall(PetscLayoutCreate(comm, &layout)); 1777 PetscCall(PetscLayoutSetLocalSize(layout, 1 + numNonExclusivelyOwned)); 1778 PetscCall(PetscLayoutSetUp(layout)); 1779 PetscCall(PetscLayoutGetRanges(layout, &cumSumVertices)); 1780 PetscCall(PetscMalloc1(pEnd - pStart, &globalNumbersOfLocalOwnedVertices)); 1781 for (i = 0; i < pEnd - pStart; i++) globalNumbersOfLocalOwnedVertices[i] = pStart - 1; 1782 offset = cumSumVertices[rank]; 1783 counter = 0; 1784 for (i = 0; i < pEnd - pStart; i++) { 1785 if (toBalance[i]) { 1786 if (degrees[i] > 0) { 1787 globalNumbersOfLocalOwnedVertices[i] = counter + 1 + offset; 1788 counter++; 1789 } 1790 } 1791 } 1792 1793 /* send the global numbers of vertices I own to the leafs so that they know to connect to it */ 1794 PetscCall(PetscMalloc1(pEnd - pStart, &leafGlobalNumbers)); 1795 PetscCall(PetscSFBcastBegin(sf, MPIU_INT, globalNumbersOfLocalOwnedVertices, leafGlobalNumbers, MPI_REPLACE)); 1796 PetscCall(PetscSFBcastEnd(sf, MPIU_INT, globalNumbersOfLocalOwnedVertices, leafGlobalNumbers, MPI_REPLACE)); 1797 1798 /* Build the graph for partitioning */ 1799 numRows = 1 + numNonExclusivelyOwned; 1800 PetscCall(PetscLogEventBegin(DMPLEX_RebalBuildGraph, dm, 0, 0, 0)); 1801 PetscCall(MatCreate(comm, &A)); 1802 PetscCall(MatSetType(A, MATMPIADJ)); 1803 PetscCall(MatSetSizes(A, numRows, numRows, cumSumVertices[size], cumSumVertices[size])); 1804 idx = cumSumVertices[rank]; 1805 for (i = 0; i < pEnd - pStart; i++) { 1806 if (toBalance[i]) { 1807 if (isNonExclusivelyOwned[i]) jdx = globalNumbersOfLocalOwnedVertices[i]; 1808 else if (isLeaf[i]) jdx = leafGlobalNumbers[i]; 1809 else continue; 1810 PetscCall(MatSetValue(A, idx, jdx, 1, INSERT_VALUES)); 1811 PetscCall(MatSetValue(A, jdx, idx, 1, INSERT_VALUES)); 1812 } 1813 } 1814 PetscCall(PetscFree(globalNumbersOfLocalOwnedVertices)); 1815 PetscCall(PetscFree(leafGlobalNumbers)); 1816 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 1817 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 1818 PetscCall(PetscLogEventEnd(DMPLEX_RebalBuildGraph, dm, 0, 0, 0)); 1819 1820 nparts = size; 1821 ncon = 1; 1822 PetscCall(PetscMalloc1(ncon * nparts, &tpwgts)); 1823 for (i = 0; i < ncon * nparts; i++) tpwgts[i] = (real_t)(1. / (nparts)); 1824 PetscCall(PetscMalloc1(ncon, &ubvec)); 1825 for (i = 0; i < ncon; i++) ubvec[i] = (real_t)1.05; 1826 1827 PetscCall(PetscMalloc1(ncon * (1 + numNonExclusivelyOwned), &vtxwgt)); 1828 if (ncon == 2) { 1829 vtxwgt[0] = numExclusivelyOwned; 1830 vtxwgt[1] = 1; 1831 for (i = 0; i < numNonExclusivelyOwned; i++) { 1832 vtxwgt[ncon * (i + 1)] = 1; 1833 vtxwgt[ncon * (i + 1) + 1] = 0; 1834 } 1835 } else { 1836 PetscInt base, ms; 1837 PetscCallMPI(MPIU_Allreduce(&numExclusivelyOwned, &base, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm))); 1838 PetscCall(MatGetSize(A, &ms, NULL)); 1839 ms -= size; 1840 base = PetscMax(base, ms); 1841 vtxwgt[0] = base + numExclusivelyOwned; 1842 for (i = 0; i < numNonExclusivelyOwned; i++) vtxwgt[i + 1] = 1; 1843 } 1844 1845 if (viewer) { 1846 PetscCall(PetscViewerASCIIPrintf(viewer, "Attempt rebalancing of shared points of depth %" PetscInt_FMT " on interface of mesh distribution.\n", entityDepth)); 1847 PetscCall(PetscViewerASCIIPrintf(viewer, "Size of generated auxiliary graph: %" PetscInt_FMT "\n", cumSumVertices[size])); 1848 } 1849 /* TODO: Drop the parallel/sequential choice here and just use MatPartioner for much more flexibility */ 1850 if (usematpartitioning) { 1851 const char *prefix; 1852 1853 PetscCall(MatPartitioningCreate(PetscObjectComm((PetscObject)dm), &mp)); 1854 PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mp, "dm_plex_rebalance_shared_points_")); 1855 PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix)); 1856 PetscCall(PetscObjectPrependOptionsPrefix((PetscObject)mp, prefix)); 1857 PetscCall(MatPartitioningSetAdjacency(mp, A)); 1858 PetscCall(MatPartitioningSetNumberVertexWeights(mp, ncon)); 1859 PetscCall(MatPartitioningSetVertexWeights(mp, vtxwgt)); 1860 PetscCall(MatPartitioningSetFromOptions(mp)); 1861 PetscCall(MatPartitioningApply(mp, &ispart)); 1862 PetscCall(ISGetIndices(ispart, (const PetscInt **)&part)); 1863 } else if (parallel) { 1864 if (viewer) PetscCall(PetscViewerASCIIPrintf(viewer, "Using ParMETIS to partition graph.\n")); 1865 PetscCall(PetscMalloc1(cumSumVertices[rank + 1] - cumSumVertices[rank], &part)); 1866 PetscCall(MatGetRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, &xadj, &adjncy, &done)); 1867 PetscCheck(done, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Could not get adjacency information"); 1868 PetscCall(PetscMalloc1(4, &options)); 1869 options[0] = 1; 1870 options[1] = 0; /* Verbosity */ 1871 if (viewer) options[1] = 1; 1872 options[2] = 0; /* Seed */ 1873 options[3] = PARMETIS_PSR_COUPLED; /* Seed */ 1874 wgtflag = 2; 1875 numflag = 0; 1876 if (useInitialGuess) { 1877 PetscCall(PetscViewerASCIIPrintf(viewer, "THIS DOES NOT WORK! I don't know why. Using current distribution of points as initial guess.\n")); 1878 for (i = 0; i < numRows; i++) part[i] = rank; 1879 if (viewer) PetscCall(PetscViewerASCIIPrintf(viewer, "Using current distribution of points as initial guess.\n")); 1880 PetscStackPushExternal("ParMETIS_V3_RefineKway"); 1881 PetscCall(PetscLogEventBegin(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1882 ierr = ParMETIS_V3_RefineKway((PetscInt *)cumSumVertices, (idx_t *)xadj, (idx_t *)adjncy, vtxwgt, NULL, &wgtflag, &numflag, &ncon, &nparts, tpwgts, ubvec, options, &edgecut, part, &comm); 1883 PetscCall(PetscLogEventEnd(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1884 PetscStackPop; 1885 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in ParMETIS_V3_RefineKway()"); 1886 } else { 1887 PetscStackPushExternal("ParMETIS_V3_PartKway"); 1888 PetscCall(PetscLogEventBegin(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1889 ierr = ParMETIS_V3_PartKway((PetscInt *)cumSumVertices, (idx_t *)xadj, (idx_t *)adjncy, vtxwgt, NULL, &wgtflag, &numflag, &ncon, &nparts, tpwgts, ubvec, options, &edgecut, part, &comm); 1890 PetscCall(PetscLogEventEnd(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1891 PetscStackPop; 1892 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in ParMETIS_V3_PartKway()"); 1893 } 1894 PetscCall(MatRestoreRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, &xadj, &adjncy, &done)); 1895 PetscCall(PetscFree(options)); 1896 } else { 1897 if (viewer) PetscCall(PetscViewerASCIIPrintf(viewer, "Using METIS to partition graph.\n")); 1898 Mat As; 1899 PetscInt *partGlobal; 1900 PetscInt *numExclusivelyOwnedAll; 1901 1902 PetscCall(PetscMalloc1(cumSumVertices[rank + 1] - cumSumVertices[rank], &part)); 1903 PetscCall(MatGetSize(A, &numRows, NULL)); 1904 PetscCall(PetscLogEventBegin(DMPLEX_RebalGatherGraph, dm, 0, 0, 0)); 1905 PetscCall(MatMPIAdjToSeqRankZero(A, &As)); 1906 PetscCall(PetscLogEventEnd(DMPLEX_RebalGatherGraph, dm, 0, 0, 0)); 1907 1908 PetscCall(PetscMalloc1(size, &numExclusivelyOwnedAll)); 1909 numExclusivelyOwnedAll[rank] = numExclusivelyOwned; 1910 PetscCallMPI(MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, numExclusivelyOwnedAll, 1, MPIU_INT, comm)); 1911 1912 PetscCall(PetscMalloc1(numRows, &partGlobal)); 1913 PetscCall(PetscLogEventBegin(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1914 if (rank == 0) { 1915 PetscInt *vtxwgt_g, numRows_g; 1916 1917 PetscCall(MatGetRowIJ(As, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows_g, &xadj, &adjncy, &done)); 1918 PetscCall(PetscMalloc1(2 * numRows_g, &vtxwgt_g)); 1919 for (i = 0; i < size; i++) { 1920 vtxwgt_g[ncon * cumSumVertices[i]] = numExclusivelyOwnedAll[i]; 1921 if (ncon > 1) vtxwgt_g[ncon * cumSumVertices[i] + 1] = 1; 1922 for (j = cumSumVertices[i] + 1; j < cumSumVertices[i + 1]; j++) { 1923 vtxwgt_g[ncon * j] = 1; 1924 if (ncon > 1) vtxwgt_g[2 * j + 1] = 0; 1925 } 1926 } 1927 1928 PetscCall(PetscMalloc1(64, &options)); 1929 ierr = METIS_SetDefaultOptions(options); /* initialize all defaults */ 1930 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in METIS_SetDefaultOptions()"); 1931 options[METIS_OPTION_CONTIG] = 1; 1932 PetscStackPushExternal("METIS_PartGraphKway"); 1933 ierr = METIS_PartGraphKway(&numRows_g, &ncon, (idx_t *)xadj, (idx_t *)adjncy, vtxwgt_g, NULL, NULL, &nparts, tpwgts, ubvec, options, &edgecut, partGlobal); 1934 PetscStackPop; 1935 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in METIS_PartGraphKway()"); 1936 PetscCall(PetscFree(options)); 1937 PetscCall(PetscFree(vtxwgt_g)); 1938 PetscCall(MatRestoreRowIJ(As, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows_g, &xadj, &adjncy, &done)); 1939 PetscCall(MatDestroy(&As)); 1940 } 1941 PetscCall(PetscBarrier((PetscObject)dm)); 1942 PetscCall(PetscLogEventEnd(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1943 PetscCall(PetscFree(numExclusivelyOwnedAll)); 1944 1945 /* scatter the partitioning information to ranks */ 1946 PetscCall(PetscLogEventBegin(DMPLEX_RebalScatterPart, 0, 0, 0, 0)); 1947 PetscCall(PetscMalloc1(size, &counts)); 1948 PetscCall(PetscMalloc1(size + 1, &mpiCumSumVertices)); 1949 for (i = 0; i < size; i++) PetscCall(PetscMPIIntCast(cumSumVertices[i + 1] - cumSumVertices[i], &counts[i])); 1950 for (i = 0; i <= size; i++) PetscCall(PetscMPIIntCast(cumSumVertices[i], &mpiCumSumVertices[i])); 1951 PetscCallMPI(MPI_Scatterv(partGlobal, counts, mpiCumSumVertices, MPIU_INT, part, counts[rank], MPIU_INT, 0, comm)); 1952 PetscCall(PetscFree(counts)); 1953 PetscCall(PetscFree(mpiCumSumVertices)); 1954 PetscCall(PetscFree(partGlobal)); 1955 PetscCall(PetscLogEventEnd(DMPLEX_RebalScatterPart, 0, 0, 0, 0)); 1956 } 1957 PetscCall(PetscFree(ubvec)); 1958 PetscCall(PetscFree(tpwgts)); 1959 1960 /* Rename the result so that the vertex resembling the exclusively owned points stays on the same rank */ 1961 PetscCall(PetscMalloc2(size, &firstVertices, size, &renumbering)); 1962 firstVertices[rank] = part[0]; 1963 PetscCallMPI(MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, firstVertices, 1, MPIU_INT, comm)); 1964 for (i = 0; i < size; i++) renumbering[firstVertices[i]] = i; 1965 for (i = 0; i < cumSumVertices[rank + 1] - cumSumVertices[rank]; i++) part[i] = renumbering[part[i]]; 1966 PetscCall(PetscFree2(firstVertices, renumbering)); 1967 1968 /* Check if the renumbering worked (this can fail when ParMETIS gives fewer partitions than there are processes) */ 1969 failed = (PetscInt)(part[0] != rank); 1970 PetscCallMPI(MPIU_Allreduce(&failed, &failedGlobal, 1, MPIU_INT, MPI_SUM, comm)); 1971 if (failedGlobal > 0) { 1972 PetscCheck(failedGlobal <= 0, comm, PETSC_ERR_LIB, "Metis/Parmetis returned a bad partition"); 1973 PetscCall(PetscFree(vtxwgt)); 1974 PetscCall(PetscFree(toBalance)); 1975 PetscCall(PetscFree(isLeaf)); 1976 PetscCall(PetscFree(isNonExclusivelyOwned)); 1977 PetscCall(PetscFree(isExclusivelyOwned)); 1978 if (usematpartitioning) { 1979 PetscCall(ISRestoreIndices(ispart, (const PetscInt **)&part)); 1980 PetscCall(ISDestroy(&ispart)); 1981 } else PetscCall(PetscFree(part)); 1982 if (viewer) { 1983 PetscCall(PetscViewerPopFormat(viewer)); 1984 PetscCall(PetscViewerDestroy(&viewer)); 1985 } 1986 PetscCall(PetscLogEventEnd(DMPLEX_RebalanceSharedPoints, dm, 0, 0, 0)); 1987 PetscFunctionReturn(PETSC_SUCCESS); 1988 } 1989 1990 /* Check how well we did distributing points*/ 1991 if (viewer) { 1992 PetscCall(PetscViewerASCIIPrintf(viewer, "Number of owned entities of depth %" PetscInt_FMT ".\n", entityDepth)); 1993 PetscCall(PetscViewerASCIIPrintf(viewer, "Initial ")); 1994 PetscCall(DMPlexViewDistribution(comm, cumSumVertices[rank + 1] - cumSumVertices[rank], ncon, vtxwgt, NULL, viewer)); 1995 PetscCall(PetscViewerASCIIPrintf(viewer, "Rebalanced ")); 1996 PetscCall(DMPlexViewDistribution(comm, cumSumVertices[rank + 1] - cumSumVertices[rank], ncon, vtxwgt, part, viewer)); 1997 } 1998 1999 /* Check that every vertex is owned by a process that it is actually connected to. */ 2000 PetscCall(MatGetRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, (const PetscInt **)&xadj, (const PetscInt **)&adjncy, &done)); 2001 for (i = 1; i <= numNonExclusivelyOwned; i++) { 2002 PetscInt loc = 0; 2003 PetscCall(PetscFindInt(cumSumVertices[part[i]], xadj[i + 1] - xadj[i], &adjncy[xadj[i]], &loc)); 2004 /* If not, then just set the owner to the original owner (hopefully a rare event, it means that a vertex has been isolated) */ 2005 if (loc < 0) part[i] = rank; 2006 } 2007 PetscCall(MatRestoreRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, (const PetscInt **)&xadj, (const PetscInt **)&adjncy, &done)); 2008 PetscCall(MatDestroy(&A)); 2009 2010 /* See how significant the influences of the previous fixing up step was.*/ 2011 if (viewer) { 2012 PetscCall(PetscViewerASCIIPrintf(viewer, "After fix ")); 2013 PetscCall(DMPlexViewDistribution(comm, cumSumVertices[rank + 1] - cumSumVertices[rank], ncon, vtxwgt, part, viewer)); 2014 } 2015 if (!usematpartitioning) PetscCall(PetscFree(vtxwgt)); 2016 else PetscCall(MatPartitioningDestroy(&mp)); 2017 2018 PetscCall(PetscLayoutDestroy(&layout)); 2019 2020 PetscCall(PetscLogEventBegin(DMPLEX_RebalRewriteSF, dm, 0, 0, 0)); 2021 /* Rewrite the SF to reflect the new ownership. */ 2022 PetscCall(PetscMalloc1(numNonExclusivelyOwned, &pointsToRewrite)); 2023 counter = 0; 2024 for (i = 0; i < pEnd - pStart; i++) { 2025 if (toBalance[i]) { 2026 if (isNonExclusivelyOwned[i]) { 2027 pointsToRewrite[counter] = i + pStart; 2028 counter++; 2029 } 2030 } 2031 } 2032 PetscCall(DMPlexRewriteSF(dm, numNonExclusivelyOwned, pointsToRewrite, part + 1, degrees)); 2033 PetscCall(PetscFree(pointsToRewrite)); 2034 PetscCall(PetscLogEventEnd(DMPLEX_RebalRewriteSF, dm, 0, 0, 0)); 2035 2036 PetscCall(PetscFree(toBalance)); 2037 PetscCall(PetscFree(isLeaf)); 2038 PetscCall(PetscFree(isNonExclusivelyOwned)); 2039 PetscCall(PetscFree(isExclusivelyOwned)); 2040 if (usematpartitioning) { 2041 PetscCall(ISRestoreIndices(ispart, (const PetscInt **)&part)); 2042 PetscCall(ISDestroy(&ispart)); 2043 } else PetscCall(PetscFree(part)); 2044 if (viewer) { 2045 PetscCall(PetscViewerPopFormat(viewer)); 2046 PetscCall(PetscViewerDestroy(&viewer)); 2047 } 2048 if (success) *success = PETSC_TRUE; 2049 PetscCall(PetscLogEventEnd(DMPLEX_RebalanceSharedPoints, dm, 0, 0, 0)); 2050 PetscFunctionReturn(PETSC_SUCCESS); 2051 #else 2052 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh partitioning needs external package support.\nPlease reconfigure with --download-parmetis."); 2053 #endif 2054 } 2055