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_Internal(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_Internal(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_Internal(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(DMGetPointSF(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 PetscCall(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_DEFAULT, &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; 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 PetscCall(PetscPartitionerPartition(part, size, numVertices, start, adjacency, vertSection, targetSection, partSection, partition)); 840 PetscCall(PetscFree(start)); 841 PetscCall(PetscFree(adjacency)); 842 if (globalNumbering) { /* partition is wrt global unique numbering: change this to be wrt local numbering */ 843 const PetscInt *globalNum; 844 const PetscInt *partIdx; 845 PetscInt *map, cStart, cEnd; 846 PetscInt *adjusted, i, localSize, offset; 847 IS newPartition; 848 849 PetscCall(ISGetLocalSize(*partition, &localSize)); 850 PetscCall(PetscMalloc1(localSize, &adjusted)); 851 PetscCall(ISGetIndices(globalNumbering, &globalNum)); 852 PetscCall(ISGetIndices(*partition, &partIdx)); 853 PetscCall(PetscMalloc1(localSize, &map)); 854 PetscCall(DMPlexGetHeightStratum(dm, part->height, &cStart, &cEnd)); 855 for (i = cStart, offset = 0; i < cEnd; i++) { 856 if (globalNum[i - cStart] >= 0) map[offset++] = i; 857 } 858 for (i = 0; i < localSize; i++) adjusted[i] = map[partIdx[i]]; 859 PetscCall(PetscFree(map)); 860 PetscCall(ISRestoreIndices(*partition, &partIdx)); 861 PetscCall(ISRestoreIndices(globalNumbering, &globalNum)); 862 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, localSize, adjusted, PETSC_OWN_POINTER, &newPartition)); 863 PetscCall(ISDestroy(&globalNumbering)); 864 PetscCall(ISDestroy(partition)); 865 *partition = newPartition; 866 } 867 } else SETERRQ(PetscObjectComm((PetscObject)part), PETSC_ERR_ARG_OUTOFRANGE, "Invalid height %" PetscInt_FMT " for points to partition", part->height); 868 PetscCall(PetscSectionDestroy(&vertSection)); 869 PetscFunctionReturn(PETSC_SUCCESS); 870 } 871 872 /*@ 873 DMPlexGetPartitioner - Get the mesh partitioner 874 875 Not Collective 876 877 Input Parameter: 878 . dm - The `DM` 879 880 Output Parameter: 881 . part - The `PetscPartitioner` 882 883 Level: developer 884 885 Note: 886 This gets a borrowed reference, so the user should not destroy this `PetscPartitioner`. 887 888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscPartitioner`, `PetscSection`, `DMPlexDistribute()`, `DMPlexSetPartitioner()`, `PetscPartitionerDMPlexPartition()`, `PetscPartitionerCreate()` 889 @*/ 890 PetscErrorCode DMPlexGetPartitioner(DM dm, PetscPartitioner *part) 891 { 892 DM_Plex *mesh = (DM_Plex *)dm->data; 893 894 PetscFunctionBegin; 895 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 896 PetscAssertPointer(part, 2); 897 *part = mesh->partitioner; 898 PetscFunctionReturn(PETSC_SUCCESS); 899 } 900 901 /*@ 902 DMPlexSetPartitioner - Set the mesh partitioner 903 904 logically Collective 905 906 Input Parameters: 907 + dm - The `DM` 908 - part - The partitioner 909 910 Level: developer 911 912 Note: 913 Any existing `PetscPartitioner` will be destroyed. 914 915 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscPartitioner`,`DMPlexDistribute()`, `DMPlexGetPartitioner()`, `PetscPartitionerCreate()` 916 @*/ 917 PetscErrorCode DMPlexSetPartitioner(DM dm, PetscPartitioner part) 918 { 919 DM_Plex *mesh = (DM_Plex *)dm->data; 920 921 PetscFunctionBegin; 922 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 923 PetscValidHeaderSpecific(part, PETSCPARTITIONER_CLASSID, 2); 924 PetscCall(PetscObjectReference((PetscObject)part)); 925 PetscCall(PetscPartitionerDestroy(&mesh->partitioner)); 926 mesh->partitioner = part; 927 PetscFunctionReturn(PETSC_SUCCESS); 928 } 929 930 static PetscErrorCode DMPlexAddClosure_Private(DM dm, PetscHSetI ht, PetscInt point) 931 { 932 const PetscInt *cone; 933 PetscInt coneSize, c; 934 PetscBool missing; 935 936 PetscFunctionBeginHot; 937 PetscCall(PetscHSetIQueryAdd(ht, point, &missing)); 938 if (missing) { 939 PetscCall(DMPlexGetCone(dm, point, &cone)); 940 PetscCall(DMPlexGetConeSize(dm, point, &coneSize)); 941 for (c = 0; c < coneSize; c++) PetscCall(DMPlexAddClosure_Private(dm, ht, cone[c])); 942 } 943 PetscFunctionReturn(PETSC_SUCCESS); 944 } 945 946 PETSC_UNUSED static PetscErrorCode DMPlexAddClosure_Tree(DM dm, PetscHSetI ht, PetscInt point, PetscBool up, PetscBool down) 947 { 948 PetscFunctionBegin; 949 if (up) { 950 PetscInt parent; 951 952 PetscCall(DMPlexGetTreeParent(dm, point, &parent, NULL)); 953 if (parent != point) { 954 PetscInt closureSize, *closure = NULL, i; 955 956 PetscCall(DMPlexGetTransitiveClosure(dm, parent, PETSC_TRUE, &closureSize, &closure)); 957 for (i = 0; i < closureSize; i++) { 958 PetscInt cpoint = closure[2 * i]; 959 960 PetscCall(PetscHSetIAdd(ht, cpoint)); 961 PetscCall(DMPlexAddClosure_Tree(dm, ht, cpoint, PETSC_TRUE, PETSC_FALSE)); 962 } 963 PetscCall(DMPlexRestoreTransitiveClosure(dm, parent, PETSC_TRUE, &closureSize, &closure)); 964 } 965 } 966 if (down) { 967 PetscInt numChildren; 968 const PetscInt *children; 969 970 PetscCall(DMPlexGetTreeChildren(dm, point, &numChildren, &children)); 971 if (numChildren) { 972 PetscInt i; 973 974 for (i = 0; i < numChildren; i++) { 975 PetscInt cpoint = children[i]; 976 977 PetscCall(PetscHSetIAdd(ht, cpoint)); 978 PetscCall(DMPlexAddClosure_Tree(dm, ht, cpoint, PETSC_FALSE, PETSC_TRUE)); 979 } 980 } 981 } 982 PetscFunctionReturn(PETSC_SUCCESS); 983 } 984 985 static PetscErrorCode DMPlexAddClosureTree_Up_Private(DM dm, PetscHSetI ht, PetscInt point) 986 { 987 PetscInt parent; 988 989 PetscFunctionBeginHot; 990 PetscCall(DMPlexGetTreeParent(dm, point, &parent, NULL)); 991 if (point != parent) { 992 const PetscInt *cone; 993 PetscInt coneSize, c; 994 995 PetscCall(DMPlexAddClosureTree_Up_Private(dm, ht, parent)); 996 PetscCall(DMPlexAddClosure_Private(dm, ht, parent)); 997 PetscCall(DMPlexGetCone(dm, parent, &cone)); 998 PetscCall(DMPlexGetConeSize(dm, parent, &coneSize)); 999 for (c = 0; c < coneSize; c++) { 1000 const PetscInt cp = cone[c]; 1001 1002 PetscCall(DMPlexAddClosureTree_Up_Private(dm, ht, cp)); 1003 } 1004 } 1005 PetscFunctionReturn(PETSC_SUCCESS); 1006 } 1007 1008 static PetscErrorCode DMPlexAddClosureTree_Down_Private(DM dm, PetscHSetI ht, PetscInt point) 1009 { 1010 PetscInt i, numChildren; 1011 const PetscInt *children; 1012 1013 PetscFunctionBeginHot; 1014 PetscCall(DMPlexGetTreeChildren(dm, point, &numChildren, &children)); 1015 for (i = 0; i < numChildren; i++) PetscCall(PetscHSetIAdd(ht, children[i])); 1016 PetscFunctionReturn(PETSC_SUCCESS); 1017 } 1018 1019 static PetscErrorCode DMPlexAddClosureTree_Private(DM dm, PetscHSetI ht, PetscInt point) 1020 { 1021 const PetscInt *cone; 1022 PetscInt coneSize, c; 1023 1024 PetscFunctionBeginHot; 1025 PetscCall(PetscHSetIAdd(ht, point)); 1026 PetscCall(DMPlexAddClosureTree_Up_Private(dm, ht, point)); 1027 PetscCall(DMPlexAddClosureTree_Down_Private(dm, ht, point)); 1028 PetscCall(DMPlexGetCone(dm, point, &cone)); 1029 PetscCall(DMPlexGetConeSize(dm, point, &coneSize)); 1030 for (c = 0; c < coneSize; c++) PetscCall(DMPlexAddClosureTree_Private(dm, ht, cone[c])); 1031 PetscFunctionReturn(PETSC_SUCCESS); 1032 } 1033 1034 PetscErrorCode DMPlexClosurePoints_Private(DM dm, PetscInt numPoints, const PetscInt points[], IS *closureIS) 1035 { 1036 DM_Plex *mesh = (DM_Plex *)dm->data; 1037 const PetscBool hasTree = (mesh->parentSection || mesh->childSection) ? PETSC_TRUE : PETSC_FALSE; 1038 PetscInt nelems, *elems, off = 0, p; 1039 PetscHSetI ht = NULL; 1040 1041 PetscFunctionBegin; 1042 PetscCall(PetscHSetICreate(&ht)); 1043 PetscCall(PetscHSetIResize(ht, numPoints * 16)); 1044 if (!hasTree) { 1045 for (p = 0; p < numPoints; ++p) PetscCall(DMPlexAddClosure_Private(dm, ht, points[p])); 1046 } else { 1047 #if 1 1048 for (p = 0; p < numPoints; ++p) PetscCall(DMPlexAddClosureTree_Private(dm, ht, points[p])); 1049 #else 1050 PetscInt *closure = NULL, closureSize, c; 1051 for (p = 0; p < numPoints; ++p) { 1052 PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closure)); 1053 for (c = 0; c < closureSize * 2; c += 2) { 1054 PetscCall(PetscHSetIAdd(ht, closure[c])); 1055 if (hasTree) PetscCall(DMPlexAddClosure_Tree(dm, ht, closure[c], PETSC_TRUE, PETSC_TRUE)); 1056 } 1057 } 1058 if (closure) PetscCall(DMPlexRestoreTransitiveClosure(dm, 0, PETSC_TRUE, NULL, &closure)); 1059 #endif 1060 } 1061 PetscCall(PetscHSetIGetSize(ht, &nelems)); 1062 PetscCall(PetscMalloc1(nelems, &elems)); 1063 PetscCall(PetscHSetIGetElems(ht, &off, elems)); 1064 PetscCall(PetscHSetIDestroy(&ht)); 1065 PetscCall(PetscSortInt(nelems, elems)); 1066 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nelems, elems, PETSC_OWN_POINTER, closureIS)); 1067 PetscFunctionReturn(PETSC_SUCCESS); 1068 } 1069 1070 /*@ 1071 DMPlexPartitionLabelClosure - Add the closure of all points to the partition label 1072 1073 Input Parameters: 1074 + dm - The `DM` 1075 - label - `DMLabel` assigning ranks to remote roots 1076 1077 Level: developer 1078 1079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1080 @*/ 1081 PetscErrorCode DMPlexPartitionLabelClosure(DM dm, DMLabel label) 1082 { 1083 IS rankIS, pointIS, closureIS; 1084 const PetscInt *ranks, *points; 1085 PetscInt numRanks, numPoints, r; 1086 1087 PetscFunctionBegin; 1088 PetscCall(DMLabelGetValueIS(label, &rankIS)); 1089 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1090 PetscCall(ISGetIndices(rankIS, &ranks)); 1091 for (r = 0; r < numRanks; ++r) { 1092 const PetscInt rank = ranks[r]; 1093 PetscCall(DMLabelGetStratumIS(label, rank, &pointIS)); 1094 PetscCall(ISGetLocalSize(pointIS, &numPoints)); 1095 PetscCall(ISGetIndices(pointIS, &points)); 1096 PetscCall(DMPlexClosurePoints_Private(dm, numPoints, points, &closureIS)); 1097 PetscCall(ISRestoreIndices(pointIS, &points)); 1098 PetscCall(ISDestroy(&pointIS)); 1099 PetscCall(DMLabelSetStratumIS(label, rank, closureIS)); 1100 PetscCall(ISDestroy(&closureIS)); 1101 } 1102 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1103 PetscCall(ISDestroy(&rankIS)); 1104 PetscFunctionReturn(PETSC_SUCCESS); 1105 } 1106 1107 /*@ 1108 DMPlexPartitionLabelAdjacency - Add one level of adjacent points to the partition label 1109 1110 Input Parameters: 1111 + dm - The `DM` 1112 - label - `DMLabel` assigning ranks to remote roots 1113 1114 Level: developer 1115 1116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1117 @*/ 1118 PetscErrorCode DMPlexPartitionLabelAdjacency(DM dm, DMLabel label) 1119 { 1120 IS rankIS, pointIS; 1121 const PetscInt *ranks, *points; 1122 PetscInt numRanks, numPoints, r, p, a, adjSize; 1123 PetscInt *adj = NULL; 1124 1125 PetscFunctionBegin; 1126 PetscCall(DMLabelGetValueIS(label, &rankIS)); 1127 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1128 PetscCall(ISGetIndices(rankIS, &ranks)); 1129 for (r = 0; r < numRanks; ++r) { 1130 const PetscInt rank = ranks[r]; 1131 1132 PetscCall(DMLabelGetStratumIS(label, rank, &pointIS)); 1133 PetscCall(ISGetLocalSize(pointIS, &numPoints)); 1134 PetscCall(ISGetIndices(pointIS, &points)); 1135 for (p = 0; p < numPoints; ++p) { 1136 adjSize = PETSC_DETERMINE; 1137 PetscCall(DMPlexGetAdjacency(dm, points[p], &adjSize, &adj)); 1138 for (a = 0; a < adjSize; ++a) PetscCall(DMLabelSetValue(label, adj[a], rank)); 1139 } 1140 PetscCall(ISRestoreIndices(pointIS, &points)); 1141 PetscCall(ISDestroy(&pointIS)); 1142 } 1143 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1144 PetscCall(ISDestroy(&rankIS)); 1145 PetscCall(PetscFree(adj)); 1146 PetscFunctionReturn(PETSC_SUCCESS); 1147 } 1148 1149 /*@ 1150 DMPlexPartitionLabelPropagate - Propagate points in a partition label over the point `PetscSF` 1151 1152 Input Parameters: 1153 + dm - The `DM` 1154 - label - `DMLabel` assigning ranks to remote roots 1155 1156 Level: developer 1157 1158 Note: 1159 This is required when generating multi-level overlaps to capture 1160 overlap points from non-neighbouring partitions. 1161 1162 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1163 @*/ 1164 PetscErrorCode DMPlexPartitionLabelPropagate(DM dm, DMLabel label) 1165 { 1166 MPI_Comm comm; 1167 PetscMPIInt rank; 1168 PetscSF sfPoint; 1169 DMLabel lblRoots, lblLeaves; 1170 IS rankIS, pointIS; 1171 const PetscInt *ranks; 1172 PetscInt numRanks, r; 1173 1174 PetscFunctionBegin; 1175 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1176 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1177 PetscCall(DMGetPointSF(dm, &sfPoint)); 1178 /* Pull point contributions from remote leaves into local roots */ 1179 PetscCall(DMLabelGather(label, sfPoint, &lblLeaves)); 1180 PetscCall(DMLabelGetValueIS(lblLeaves, &rankIS)); 1181 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1182 PetscCall(ISGetIndices(rankIS, &ranks)); 1183 for (r = 0; r < numRanks; ++r) { 1184 const PetscInt remoteRank = ranks[r]; 1185 if (remoteRank == rank) continue; 1186 PetscCall(DMLabelGetStratumIS(lblLeaves, remoteRank, &pointIS)); 1187 PetscCall(DMLabelInsertIS(label, pointIS, remoteRank)); 1188 PetscCall(ISDestroy(&pointIS)); 1189 } 1190 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1191 PetscCall(ISDestroy(&rankIS)); 1192 PetscCall(DMLabelDestroy(&lblLeaves)); 1193 /* Push point contributions from roots into remote leaves */ 1194 PetscCall(DMLabelDistribute(label, sfPoint, &lblRoots)); 1195 PetscCall(DMLabelGetValueIS(lblRoots, &rankIS)); 1196 PetscCall(ISGetLocalSize(rankIS, &numRanks)); 1197 PetscCall(ISGetIndices(rankIS, &ranks)); 1198 for (r = 0; r < numRanks; ++r) { 1199 const PetscInt remoteRank = ranks[r]; 1200 if (remoteRank == rank) continue; 1201 PetscCall(DMLabelGetStratumIS(lblRoots, remoteRank, &pointIS)); 1202 PetscCall(DMLabelInsertIS(label, pointIS, remoteRank)); 1203 PetscCall(ISDestroy(&pointIS)); 1204 } 1205 PetscCall(ISRestoreIndices(rankIS, &ranks)); 1206 PetscCall(ISDestroy(&rankIS)); 1207 PetscCall(DMLabelDestroy(&lblRoots)); 1208 PetscFunctionReturn(PETSC_SUCCESS); 1209 } 1210 1211 /*@ 1212 DMPlexPartitionLabelInvert - Create a partition label of remote roots from a local root label 1213 1214 Input Parameters: 1215 + dm - The `DM` 1216 . rootLabel - `DMLabel` assigning ranks to local roots 1217 - processSF - A star forest mapping into the local index on each remote rank 1218 1219 Output Parameter: 1220 . leafLabel - `DMLabel` assigning ranks to remote roots 1221 1222 Level: developer 1223 1224 Note: 1225 The rootLabel defines a send pattern by mapping local points to remote target ranks. The 1226 resulting leafLabel is a receiver mapping of remote roots to their parent rank. 1227 1228 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexPartitionLabelCreateSF()`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1229 @*/ 1230 PetscErrorCode DMPlexPartitionLabelInvert(DM dm, DMLabel rootLabel, PetscSF processSF, DMLabel leafLabel) 1231 { 1232 MPI_Comm comm; 1233 PetscMPIInt rank, size, r; 1234 PetscInt p, n, numNeighbors, numPoints, dof, off, rootSize, l, nleaves, leafSize; 1235 PetscSF sfPoint; 1236 PetscSection rootSection; 1237 PetscSFNode *rootPoints, *leafPoints; 1238 const PetscSFNode *remote; 1239 const PetscInt *local, *neighbors; 1240 IS valueIS; 1241 PetscBool mpiOverflow = PETSC_FALSE; 1242 1243 PetscFunctionBegin; 1244 PetscCall(PetscLogEventBegin(DMPLEX_PartLabelInvert, dm, 0, 0, 0)); 1245 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1246 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1247 PetscCallMPI(MPI_Comm_size(comm, &size)); 1248 PetscCall(DMGetPointSF(dm, &sfPoint)); 1249 1250 /* Convert to (point, rank) and use actual owners */ 1251 PetscCall(PetscSectionCreate(comm, &rootSection)); 1252 PetscCall(PetscSectionSetChart(rootSection, 0, size)); 1253 PetscCall(DMLabelGetValueIS(rootLabel, &valueIS)); 1254 PetscCall(ISGetLocalSize(valueIS, &numNeighbors)); 1255 PetscCall(ISGetIndices(valueIS, &neighbors)); 1256 for (n = 0; n < numNeighbors; ++n) { 1257 PetscCall(DMLabelGetStratumSize(rootLabel, neighbors[n], &numPoints)); 1258 PetscCall(PetscSectionAddDof(rootSection, neighbors[n], numPoints)); 1259 } 1260 PetscCall(PetscSectionSetUp(rootSection)); 1261 PetscCall(PetscSectionGetStorageSize(rootSection, &rootSize)); 1262 PetscCall(PetscMalloc1(rootSize, &rootPoints)); 1263 PetscCall(PetscSFGetGraph(sfPoint, NULL, &nleaves, &local, &remote)); 1264 for (n = 0; n < numNeighbors; ++n) { 1265 IS pointIS; 1266 const PetscInt *points; 1267 1268 PetscCall(PetscSectionGetOffset(rootSection, neighbors[n], &off)); 1269 PetscCall(DMLabelGetStratumIS(rootLabel, neighbors[n], &pointIS)); 1270 PetscCall(ISGetLocalSize(pointIS, &numPoints)); 1271 PetscCall(ISGetIndices(pointIS, &points)); 1272 for (p = 0; p < numPoints; ++p) { 1273 if (local) PetscCall(PetscFindInt(points[p], nleaves, local, &l)); 1274 else l = -1; 1275 if (l >= 0) { 1276 rootPoints[off + p] = remote[l]; 1277 } else { 1278 rootPoints[off + p].index = points[p]; 1279 rootPoints[off + p].rank = rank; 1280 } 1281 } 1282 PetscCall(ISRestoreIndices(pointIS, &points)); 1283 PetscCall(ISDestroy(&pointIS)); 1284 } 1285 1286 /* Try to communicate overlap using All-to-All */ 1287 if (!processSF) { 1288 PetscInt64 counter = 0; 1289 PetscBool locOverflow = PETSC_FALSE; 1290 PetscMPIInt *scounts, *sdispls, *rcounts, *rdispls; 1291 1292 PetscCall(PetscCalloc4(size, &scounts, size, &sdispls, size, &rcounts, size, &rdispls)); 1293 for (n = 0; n < numNeighbors; ++n) { 1294 PetscCall(PetscSectionGetDof(rootSection, neighbors[n], &dof)); 1295 PetscCall(PetscSectionGetOffset(rootSection, neighbors[n], &off)); 1296 #if defined(PETSC_USE_64BIT_INDICES) 1297 if (dof > PETSC_MPI_INT_MAX) { 1298 locOverflow = PETSC_TRUE; 1299 break; 1300 } 1301 if (off > PETSC_MPI_INT_MAX) { 1302 locOverflow = PETSC_TRUE; 1303 break; 1304 } 1305 #endif 1306 scounts[neighbors[n]] = (PetscMPIInt)dof; 1307 sdispls[neighbors[n]] = (PetscMPIInt)off; 1308 } 1309 PetscCallMPI(MPI_Alltoall(scounts, 1, MPI_INT, rcounts, 1, MPI_INT, comm)); 1310 for (r = 0; r < size; ++r) { 1311 rdispls[r] = (int)counter; 1312 counter += rcounts[r]; 1313 } 1314 if (counter > PETSC_MPI_INT_MAX) locOverflow = PETSC_TRUE; 1315 PetscCall(MPIU_Allreduce(&locOverflow, &mpiOverflow, 1, MPIU_BOOL, MPI_LOR, comm)); 1316 if (!mpiOverflow) { 1317 PetscCall(PetscInfo(dm, "Using Alltoallv for mesh distribution\n")); 1318 leafSize = (PetscInt)counter; 1319 PetscCall(PetscMalloc1(leafSize, &leafPoints)); 1320 PetscCallMPI(MPI_Alltoallv(rootPoints, scounts, sdispls, MPIU_2INT, leafPoints, rcounts, rdispls, MPIU_2INT, comm)); 1321 } 1322 PetscCall(PetscFree4(scounts, sdispls, rcounts, rdispls)); 1323 } 1324 1325 /* Communicate overlap using process star forest */ 1326 if (processSF || mpiOverflow) { 1327 PetscSF procSF; 1328 PetscSection leafSection; 1329 1330 if (processSF) { 1331 PetscCall(PetscInfo(dm, "Using processSF for mesh distribution\n")); 1332 PetscCall(PetscObjectReference((PetscObject)processSF)); 1333 procSF = processSF; 1334 } else { 1335 PetscCall(PetscInfo(dm, "Using processSF for mesh distribution (MPI overflow)\n")); 1336 PetscCall(PetscSFCreate(comm, &procSF)); 1337 PetscCall(PetscSFSetGraphWithPattern(procSF, NULL, PETSCSF_PATTERN_ALLTOALL)); 1338 } 1339 1340 PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &leafSection)); 1341 PetscCall(DMPlexDistributeData(dm, procSF, rootSection, MPIU_2INT, rootPoints, leafSection, (void **)&leafPoints)); 1342 PetscCall(PetscSectionGetStorageSize(leafSection, &leafSize)); 1343 PetscCall(PetscSectionDestroy(&leafSection)); 1344 PetscCall(PetscSFDestroy(&procSF)); 1345 } 1346 1347 for (p = 0; p < leafSize; p++) PetscCall(DMLabelSetValue(leafLabel, leafPoints[p].index, leafPoints[p].rank)); 1348 1349 PetscCall(ISRestoreIndices(valueIS, &neighbors)); 1350 PetscCall(ISDestroy(&valueIS)); 1351 PetscCall(PetscSectionDestroy(&rootSection)); 1352 PetscCall(PetscFree(rootPoints)); 1353 PetscCall(PetscFree(leafPoints)); 1354 PetscCall(PetscLogEventEnd(DMPLEX_PartLabelInvert, dm, 0, 0, 0)); 1355 PetscFunctionReturn(PETSC_SUCCESS); 1356 } 1357 1358 /*@ 1359 DMPlexPartitionLabelCreateSF - Create a star forest from a label that assigns ranks to points 1360 1361 Input Parameters: 1362 + dm - The `DM` 1363 - label - `DMLabel` assigning ranks to remote roots 1364 1365 Output Parameter: 1366 . sf - The star forest communication context encapsulating the defined mapping 1367 1368 Level: developer 1369 1370 Note: 1371 The incoming label is a receiver mapping of remote points to their parent rank. 1372 1373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `PetscSF`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1374 @*/ 1375 PetscErrorCode DMPlexPartitionLabelCreateSF(DM dm, DMLabel label, PetscSF *sf) 1376 { 1377 PetscMPIInt rank; 1378 PetscInt n, numRemote, p, numPoints, pStart, pEnd, idx = 0, nNeighbors; 1379 PetscSFNode *remotePoints; 1380 IS remoteRootIS, neighborsIS; 1381 const PetscInt *remoteRoots, *neighbors; 1382 1383 PetscFunctionBegin; 1384 PetscCall(PetscLogEventBegin(DMPLEX_PartLabelCreateSF, dm, 0, 0, 0)); 1385 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 1386 1387 PetscCall(DMLabelGetValueIS(label, &neighborsIS)); 1388 #if 0 1389 { 1390 IS is; 1391 PetscCall(ISDuplicate(neighborsIS, &is)); 1392 PetscCall(ISSort(is)); 1393 PetscCall(ISDestroy(&neighborsIS)); 1394 neighborsIS = is; 1395 } 1396 #endif 1397 PetscCall(ISGetLocalSize(neighborsIS, &nNeighbors)); 1398 PetscCall(ISGetIndices(neighborsIS, &neighbors)); 1399 for (numRemote = 0, n = 0; n < nNeighbors; ++n) { 1400 PetscCall(DMLabelGetStratumSize(label, neighbors[n], &numPoints)); 1401 numRemote += numPoints; 1402 } 1403 PetscCall(PetscMalloc1(numRemote, &remotePoints)); 1404 /* Put owned points first */ 1405 PetscCall(DMLabelGetStratumSize(label, rank, &numPoints)); 1406 if (numPoints > 0) { 1407 PetscCall(DMLabelGetStratumIS(label, rank, &remoteRootIS)); 1408 PetscCall(ISGetIndices(remoteRootIS, &remoteRoots)); 1409 for (p = 0; p < numPoints; p++) { 1410 remotePoints[idx].index = remoteRoots[p]; 1411 remotePoints[idx].rank = rank; 1412 idx++; 1413 } 1414 PetscCall(ISRestoreIndices(remoteRootIS, &remoteRoots)); 1415 PetscCall(ISDestroy(&remoteRootIS)); 1416 } 1417 /* Now add remote points */ 1418 for (n = 0; n < nNeighbors; ++n) { 1419 const PetscInt nn = neighbors[n]; 1420 1421 PetscCall(DMLabelGetStratumSize(label, nn, &numPoints)); 1422 if (nn == rank || numPoints <= 0) continue; 1423 PetscCall(DMLabelGetStratumIS(label, nn, &remoteRootIS)); 1424 PetscCall(ISGetIndices(remoteRootIS, &remoteRoots)); 1425 for (p = 0; p < numPoints; p++) { 1426 remotePoints[idx].index = remoteRoots[p]; 1427 remotePoints[idx].rank = nn; 1428 idx++; 1429 } 1430 PetscCall(ISRestoreIndices(remoteRootIS, &remoteRoots)); 1431 PetscCall(ISDestroy(&remoteRootIS)); 1432 } 1433 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), sf)); 1434 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 1435 PetscCall(PetscSFSetGraph(*sf, pEnd - pStart, numRemote, NULL, PETSC_OWN_POINTER, remotePoints, PETSC_OWN_POINTER)); 1436 PetscCall(PetscSFSetUp(*sf)); 1437 PetscCall(ISDestroy(&neighborsIS)); 1438 PetscCall(PetscLogEventEnd(DMPLEX_PartLabelCreateSF, dm, 0, 0, 0)); 1439 PetscFunctionReturn(PETSC_SUCCESS); 1440 } 1441 1442 #if defined(PETSC_HAVE_PARMETIS) 1443 #include <parmetis.h> 1444 #endif 1445 1446 /* The two functions below are used by DMPlexRebalanceSharedPoints which errors 1447 * when PETSc is built without ParMETIS. To avoid -Wunused-function, we take 1448 * them out in that case. */ 1449 #if defined(PETSC_HAVE_PARMETIS) 1450 /* 1451 DMPlexRewriteSF - Rewrites the ownership of the `PetsSF` of a `DM` (in place). 1452 1453 Input parameters: 1454 + dm - The `DMPLEX` object. 1455 . n - The number of points. 1456 . pointsToRewrite - The points in the `PetscSF` whose ownership will change. 1457 . targetOwners - New owner for each element in pointsToRewrite. 1458 - degrees - Degrees of the points in the `PetscSF` as obtained by `PetscSFComputeDegreeBegin()`/`PetscSFComputeDegreeEnd()`. 1459 1460 Level: developer 1461 1462 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `PetscSF`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1463 */ 1464 static PetscErrorCode DMPlexRewriteSF(DM dm, PetscInt n, PetscInt *pointsToRewrite, PetscInt *targetOwners, const PetscInt *degrees) 1465 { 1466 PetscInt pStart, pEnd, i, j, counter, leafCounter, sumDegrees, nroots, nleafs; 1467 PetscInt *cumSumDegrees, *newOwners, *newNumbers, *rankOnLeafs, *locationsOfLeafs, *remoteLocalPointOfLeafs, *points, *leafsNew; 1468 PetscSFNode *leafLocationsNew; 1469 const PetscSFNode *iremote; 1470 const PetscInt *ilocal; 1471 PetscBool *isLeaf; 1472 PetscSF sf; 1473 MPI_Comm comm; 1474 PetscMPIInt rank, size; 1475 1476 PetscFunctionBegin; 1477 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1478 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1479 PetscCallMPI(MPI_Comm_size(comm, &size)); 1480 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 1481 1482 PetscCall(DMGetPointSF(dm, &sf)); 1483 PetscCall(PetscSFGetGraph(sf, &nroots, &nleafs, &ilocal, &iremote)); 1484 PetscCall(PetscMalloc1(pEnd - pStart, &isLeaf)); 1485 for (i = 0; i < pEnd - pStart; i++) isLeaf[i] = PETSC_FALSE; 1486 for (i = 0; i < nleafs; i++) isLeaf[ilocal[i] - pStart] = PETSC_TRUE; 1487 1488 PetscCall(PetscMalloc1(pEnd - pStart + 1, &cumSumDegrees)); 1489 cumSumDegrees[0] = 0; 1490 for (i = 1; i <= pEnd - pStart; i++) cumSumDegrees[i] = cumSumDegrees[i - 1] + degrees[i - 1]; 1491 sumDegrees = cumSumDegrees[pEnd - pStart]; 1492 /* get the location of my leafs (we have sumDegrees many leafs pointing at our roots) */ 1493 1494 PetscCall(PetscMalloc1(sumDegrees, &locationsOfLeafs)); 1495 PetscCall(PetscMalloc1(pEnd - pStart, &rankOnLeafs)); 1496 for (i = 0; i < pEnd - pStart; i++) rankOnLeafs[i] = rank; 1497 PetscCall(PetscSFGatherBegin(sf, MPIU_INT, rankOnLeafs, locationsOfLeafs)); 1498 PetscCall(PetscSFGatherEnd(sf, MPIU_INT, rankOnLeafs, locationsOfLeafs)); 1499 PetscCall(PetscFree(rankOnLeafs)); 1500 1501 /* get the remote local points of my leaves */ 1502 PetscCall(PetscMalloc1(sumDegrees, &remoteLocalPointOfLeafs)); 1503 PetscCall(PetscMalloc1(pEnd - pStart, &points)); 1504 for (i = 0; i < pEnd - pStart; i++) points[i] = pStart + i; 1505 PetscCall(PetscSFGatherBegin(sf, MPIU_INT, points, remoteLocalPointOfLeafs)); 1506 PetscCall(PetscSFGatherEnd(sf, MPIU_INT, points, remoteLocalPointOfLeafs)); 1507 PetscCall(PetscFree(points)); 1508 /* Figure out the new owners of the vertices that are up for grabs and their numbers on the new owners */ 1509 PetscCall(PetscMalloc1(pEnd - pStart, &newOwners)); 1510 PetscCall(PetscMalloc1(pEnd - pStart, &newNumbers)); 1511 for (i = 0; i < pEnd - pStart; i++) { 1512 newOwners[i] = -1; 1513 newNumbers[i] = -1; 1514 } 1515 { 1516 PetscInt oldNumber, newNumber, oldOwner, newOwner; 1517 for (i = 0; i < n; i++) { 1518 oldNumber = pointsToRewrite[i]; 1519 newNumber = -1; 1520 oldOwner = rank; 1521 newOwner = targetOwners[i]; 1522 if (oldOwner == newOwner) { 1523 newNumber = oldNumber; 1524 } else { 1525 for (j = 0; j < degrees[oldNumber]; j++) { 1526 if (locationsOfLeafs[cumSumDegrees[oldNumber] + j] == newOwner) { 1527 newNumber = remoteLocalPointOfLeafs[cumSumDegrees[oldNumber] + j]; 1528 break; 1529 } 1530 } 1531 } 1532 PetscCheck(newNumber != -1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Couldn't find the new owner of vertex."); 1533 1534 newOwners[oldNumber] = newOwner; 1535 newNumbers[oldNumber] = newNumber; 1536 } 1537 } 1538 PetscCall(PetscFree(cumSumDegrees)); 1539 PetscCall(PetscFree(locationsOfLeafs)); 1540 PetscCall(PetscFree(remoteLocalPointOfLeafs)); 1541 1542 PetscCall(PetscSFBcastBegin(sf, MPIU_INT, newOwners, newOwners, MPI_REPLACE)); 1543 PetscCall(PetscSFBcastEnd(sf, MPIU_INT, newOwners, newOwners, MPI_REPLACE)); 1544 PetscCall(PetscSFBcastBegin(sf, MPIU_INT, newNumbers, newNumbers, MPI_REPLACE)); 1545 PetscCall(PetscSFBcastEnd(sf, MPIU_INT, newNumbers, newNumbers, MPI_REPLACE)); 1546 1547 /* Now count how many leafs we have on each processor. */ 1548 leafCounter = 0; 1549 for (i = 0; i < pEnd - pStart; i++) { 1550 if (newOwners[i] >= 0) { 1551 if (newOwners[i] != rank) leafCounter++; 1552 } else { 1553 if (isLeaf[i]) leafCounter++; 1554 } 1555 } 1556 1557 /* Now set up the new sf by creating the leaf arrays */ 1558 PetscCall(PetscMalloc1(leafCounter, &leafsNew)); 1559 PetscCall(PetscMalloc1(leafCounter, &leafLocationsNew)); 1560 1561 leafCounter = 0; 1562 counter = 0; 1563 for (i = 0; i < pEnd - pStart; i++) { 1564 if (newOwners[i] >= 0) { 1565 if (newOwners[i] != rank) { 1566 leafsNew[leafCounter] = i; 1567 leafLocationsNew[leafCounter].rank = newOwners[i]; 1568 leafLocationsNew[leafCounter].index = newNumbers[i]; 1569 leafCounter++; 1570 } 1571 } else { 1572 if (isLeaf[i]) { 1573 leafsNew[leafCounter] = i; 1574 leafLocationsNew[leafCounter].rank = iremote[counter].rank; 1575 leafLocationsNew[leafCounter].index = iremote[counter].index; 1576 leafCounter++; 1577 } 1578 } 1579 if (isLeaf[i]) counter++; 1580 } 1581 1582 PetscCall(PetscSFSetGraph(sf, nroots, leafCounter, leafsNew, PETSC_OWN_POINTER, leafLocationsNew, PETSC_OWN_POINTER)); 1583 PetscCall(PetscFree(newOwners)); 1584 PetscCall(PetscFree(newNumbers)); 1585 PetscCall(PetscFree(isLeaf)); 1586 PetscFunctionReturn(PETSC_SUCCESS); 1587 } 1588 1589 static PetscErrorCode DMPlexViewDistribution(MPI_Comm comm, PetscInt n, PetscInt skip, PetscInt *vtxwgt, PetscInt *part, PetscViewer viewer) 1590 { 1591 PetscInt *distribution, min, max, sum; 1592 PetscMPIInt rank, size; 1593 1594 PetscFunctionBegin; 1595 PetscCallMPI(MPI_Comm_size(comm, &size)); 1596 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1597 PetscCall(PetscCalloc1(size, &distribution)); 1598 for (PetscInt i = 0; i < n; i++) { 1599 if (part) distribution[part[i]] += vtxwgt[skip * i]; 1600 else distribution[rank] += vtxwgt[skip * i]; 1601 } 1602 PetscCall(MPIU_Allreduce(MPI_IN_PLACE, distribution, size, MPIU_INT, MPI_SUM, comm)); 1603 min = distribution[0]; 1604 max = distribution[0]; 1605 sum = distribution[0]; 1606 for (PetscInt i = 1; i < size; i++) { 1607 if (distribution[i] < min) min = distribution[i]; 1608 if (distribution[i] > max) max = distribution[i]; 1609 sum += distribution[i]; 1610 } 1611 PetscCall(PetscViewerASCIIPrintf(viewer, "Min: %" PetscInt_FMT ", Avg: %" PetscInt_FMT ", Max: %" PetscInt_FMT ", Balance: %f\n", min, sum / size, max, (max * 1. * size) / sum)); 1612 PetscCall(PetscFree(distribution)); 1613 PetscFunctionReturn(PETSC_SUCCESS); 1614 } 1615 1616 #endif 1617 1618 /*@ 1619 DMPlexRebalanceSharedPoints - Redistribute points in the plex that are shared in order to achieve better balancing. This routine updates the `PointSF` of the `DM` inplace. 1620 1621 Input Parameters: 1622 + dm - The `DMPLEX` object. 1623 . entityDepth - depth of the entity to balance (0 -> balance vertices). 1624 . useInitialGuess - whether to use the current distribution as initial guess (only used by ParMETIS). 1625 - parallel - whether to use ParMETIS and do the partition in parallel or whether to gather the graph onto a single process and use METIS. 1626 1627 Output Parameter: 1628 . success - whether the graph partitioning was successful or not, optional. Unsuccessful simply means no change to the partitioning 1629 1630 Options Database Keys: 1631 + -dm_plex_rebalance_shared_points_parmetis - Use ParMetis instead of Metis for the partitioner 1632 . -dm_plex_rebalance_shared_points_use_initial_guess - Use current partition to bootstrap ParMetis partition 1633 . -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_ 1634 - -dm_plex_rebalance_shared_points_monitor - Monitor the shared points rebalance process 1635 1636 Level: intermediate 1637 1638 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexDistribute()`, `DMPlexCreateOverlap()` 1639 @*/ 1640 PetscErrorCode DMPlexRebalanceSharedPoints(DM dm, PetscInt entityDepth, PetscBool useInitialGuess, PetscBool parallel, PetscBool *success) 1641 { 1642 #if defined(PETSC_HAVE_PARMETIS) 1643 PetscSF sf; 1644 PetscInt ierr, i, j, idx, jdx; 1645 PetscInt eBegin, eEnd, nroots, nleafs, pStart, pEnd; 1646 const PetscInt *degrees, *ilocal; 1647 const PetscSFNode *iremote; 1648 PetscBool *toBalance, *isLeaf, *isExclusivelyOwned, *isNonExclusivelyOwned; 1649 PetscInt numExclusivelyOwned, numNonExclusivelyOwned; 1650 PetscMPIInt rank, size; 1651 PetscInt *globalNumbersOfLocalOwnedVertices, *leafGlobalNumbers; 1652 const PetscInt *cumSumVertices; 1653 PetscInt offset, counter; 1654 PetscInt *vtxwgt; 1655 const PetscInt *xadj, *adjncy; 1656 PetscInt *part, *options; 1657 PetscInt nparts, wgtflag, numflag, ncon, edgecut; 1658 real_t *ubvec; 1659 PetscInt *firstVertices, *renumbering; 1660 PetscInt failed, failedGlobal; 1661 MPI_Comm comm; 1662 Mat A; 1663 PetscViewer viewer; 1664 PetscViewerFormat format; 1665 PetscLayout layout; 1666 real_t *tpwgts; 1667 PetscMPIInt *counts, *mpiCumSumVertices; 1668 PetscInt *pointsToRewrite; 1669 PetscInt numRows; 1670 PetscBool done, usematpartitioning = PETSC_FALSE; 1671 IS ispart = NULL; 1672 MatPartitioning mp; 1673 const char *prefix; 1674 1675 PetscFunctionBegin; 1676 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1677 PetscCallMPI(MPI_Comm_size(comm, &size)); 1678 if (size == 1) { 1679 if (success) *success = PETSC_TRUE; 1680 PetscFunctionReturn(PETSC_SUCCESS); 1681 } 1682 if (success) *success = PETSC_FALSE; 1683 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 1684 1685 parallel = PETSC_FALSE; 1686 useInitialGuess = PETSC_FALSE; 1687 PetscObjectOptionsBegin((PetscObject)dm); 1688 PetscCall(PetscOptionsName("-dm_plex_rebalance_shared_points_parmetis", "Use ParMetis instead of Metis for the partitioner", "DMPlexRebalanceSharedPoints", ¶llel)); 1689 PetscCall(PetscOptionsBool("-dm_plex_rebalance_shared_points_use_initial_guess", "Use current partition to bootstrap ParMetis partition", "DMPlexRebalanceSharedPoints", useInitialGuess, &useInitialGuess, NULL)); 1690 PetscCall(PetscOptionsBool("-dm_plex_rebalance_shared_points_use_mat_partitioning", "Use the MatPartitioning object to partition", "DMPlexRebalanceSharedPoints", usematpartitioning, &usematpartitioning, NULL)); 1691 PetscCall(PetscOptionsViewer("-dm_plex_rebalance_shared_points_monitor", "Monitor the shared points rebalance process", "DMPlexRebalanceSharedPoints", &viewer, &format, NULL)); 1692 PetscOptionsEnd(); 1693 if (viewer) PetscCall(PetscViewerPushFormat(viewer, format)); 1694 1695 PetscCall(PetscLogEventBegin(DMPLEX_RebalanceSharedPoints, dm, 0, 0, 0)); 1696 1697 PetscCall(DMGetOptionsPrefix(dm, &prefix)); 1698 PetscCall(PetscOptionsGetViewer(comm, ((PetscObject)dm)->options, prefix, "-dm_rebalance_partition_view", &viewer, &format, NULL)); 1699 if (viewer) PetscCall(PetscViewerPushFormat(viewer, format)); 1700 1701 /* Figure out all points in the plex that we are interested in balancing. */ 1702 PetscCall(DMPlexGetDepthStratum(dm, entityDepth, &eBegin, &eEnd)); 1703 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 1704 PetscCall(PetscMalloc1(pEnd - pStart, &toBalance)); 1705 for (i = 0; i < pEnd - pStart; i++) toBalance[i] = (PetscBool)(i >= eBegin && i < eEnd); 1706 1707 /* There are three types of points: 1708 * exclusivelyOwned: points that are owned by this process and only seen by this process 1709 * nonExclusivelyOwned: points that are owned by this process but seen by at least another process 1710 * leaf: a point that is seen by this process but owned by a different process 1711 */ 1712 PetscCall(DMGetPointSF(dm, &sf)); 1713 PetscCall(PetscSFGetGraph(sf, &nroots, &nleafs, &ilocal, &iremote)); 1714 PetscCall(PetscMalloc1(pEnd - pStart, &isLeaf)); 1715 PetscCall(PetscMalloc1(pEnd - pStart, &isNonExclusivelyOwned)); 1716 PetscCall(PetscMalloc1(pEnd - pStart, &isExclusivelyOwned)); 1717 for (i = 0; i < pEnd - pStart; i++) { 1718 isNonExclusivelyOwned[i] = PETSC_FALSE; 1719 isExclusivelyOwned[i] = PETSC_FALSE; 1720 isLeaf[i] = PETSC_FALSE; 1721 } 1722 1723 /* mark all the leafs */ 1724 for (i = 0; i < nleafs; i++) isLeaf[ilocal[i] - pStart] = PETSC_TRUE; 1725 1726 /* for an owned point, we can figure out whether another processor sees it or 1727 * not by calculating its degree */ 1728 PetscCall(PetscSFComputeDegreeBegin(sf, °rees)); 1729 PetscCall(PetscSFComputeDegreeEnd(sf, °rees)); 1730 numExclusivelyOwned = 0; 1731 numNonExclusivelyOwned = 0; 1732 for (i = 0; i < pEnd - pStart; i++) { 1733 if (toBalance[i]) { 1734 if (degrees[i] > 0) { 1735 isNonExclusivelyOwned[i] = PETSC_TRUE; 1736 numNonExclusivelyOwned += 1; 1737 } else { 1738 if (!isLeaf[i]) { 1739 isExclusivelyOwned[i] = PETSC_TRUE; 1740 numExclusivelyOwned += 1; 1741 } 1742 } 1743 } 1744 } 1745 1746 /* Build a graph with one vertex per core representing the 1747 * exclusively owned points and then one vertex per nonExclusively owned 1748 * point. */ 1749 PetscCall(PetscLayoutCreate(comm, &layout)); 1750 PetscCall(PetscLayoutSetLocalSize(layout, 1 + numNonExclusivelyOwned)); 1751 PetscCall(PetscLayoutSetUp(layout)); 1752 PetscCall(PetscLayoutGetRanges(layout, &cumSumVertices)); 1753 PetscCall(PetscMalloc1(pEnd - pStart, &globalNumbersOfLocalOwnedVertices)); 1754 for (i = 0; i < pEnd - pStart; i++) globalNumbersOfLocalOwnedVertices[i] = pStart - 1; 1755 offset = cumSumVertices[rank]; 1756 counter = 0; 1757 for (i = 0; i < pEnd - pStart; i++) { 1758 if (toBalance[i]) { 1759 if (degrees[i] > 0) { 1760 globalNumbersOfLocalOwnedVertices[i] = counter + 1 + offset; 1761 counter++; 1762 } 1763 } 1764 } 1765 1766 /* send the global numbers of vertices I own to the leafs so that they know to connect to it */ 1767 PetscCall(PetscMalloc1(pEnd - pStart, &leafGlobalNumbers)); 1768 PetscCall(PetscSFBcastBegin(sf, MPIU_INT, globalNumbersOfLocalOwnedVertices, leafGlobalNumbers, MPI_REPLACE)); 1769 PetscCall(PetscSFBcastEnd(sf, MPIU_INT, globalNumbersOfLocalOwnedVertices, leafGlobalNumbers, MPI_REPLACE)); 1770 1771 /* Build the graph for partitioning */ 1772 numRows = 1 + numNonExclusivelyOwned; 1773 PetscCall(PetscLogEventBegin(DMPLEX_RebalBuildGraph, dm, 0, 0, 0)); 1774 PetscCall(MatCreate(comm, &A)); 1775 PetscCall(MatSetType(A, MATMPIADJ)); 1776 PetscCall(MatSetSizes(A, numRows, numRows, cumSumVertices[size], cumSumVertices[size])); 1777 idx = cumSumVertices[rank]; 1778 for (i = 0; i < pEnd - pStart; i++) { 1779 if (toBalance[i]) { 1780 if (isNonExclusivelyOwned[i]) jdx = globalNumbersOfLocalOwnedVertices[i]; 1781 else if (isLeaf[i]) jdx = leafGlobalNumbers[i]; 1782 else continue; 1783 PetscCall(MatSetValue(A, idx, jdx, 1, INSERT_VALUES)); 1784 PetscCall(MatSetValue(A, jdx, idx, 1, INSERT_VALUES)); 1785 } 1786 } 1787 PetscCall(PetscFree(globalNumbersOfLocalOwnedVertices)); 1788 PetscCall(PetscFree(leafGlobalNumbers)); 1789 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 1790 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 1791 PetscCall(PetscLogEventEnd(DMPLEX_RebalBuildGraph, dm, 0, 0, 0)); 1792 1793 nparts = size; 1794 ncon = 1; 1795 PetscCall(PetscMalloc1(ncon * nparts, &tpwgts)); 1796 for (i = 0; i < ncon * nparts; i++) tpwgts[i] = 1. / (nparts); 1797 PetscCall(PetscMalloc1(ncon, &ubvec)); 1798 for (i = 0; i < ncon; i++) ubvec[i] = 1.05; 1799 1800 PetscCall(PetscMalloc1(ncon * (1 + numNonExclusivelyOwned), &vtxwgt)); 1801 if (ncon == 2) { 1802 vtxwgt[0] = numExclusivelyOwned; 1803 vtxwgt[1] = 1; 1804 for (i = 0; i < numNonExclusivelyOwned; i++) { 1805 vtxwgt[ncon * (i + 1)] = 1; 1806 vtxwgt[ncon * (i + 1) + 1] = 0; 1807 } 1808 } else { 1809 PetscInt base, ms; 1810 PetscCall(MPIU_Allreduce(&numExclusivelyOwned, &base, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm))); 1811 PetscCall(MatGetSize(A, &ms, NULL)); 1812 ms -= size; 1813 base = PetscMax(base, ms); 1814 vtxwgt[0] = base + numExclusivelyOwned; 1815 for (i = 0; i < numNonExclusivelyOwned; i++) vtxwgt[i + 1] = 1; 1816 } 1817 1818 if (viewer) { 1819 PetscCall(PetscViewerASCIIPrintf(viewer, "Attempt rebalancing of shared points of depth %" PetscInt_FMT " on interface of mesh distribution.\n", entityDepth)); 1820 PetscCall(PetscViewerASCIIPrintf(viewer, "Size of generated auxiliary graph: %" PetscInt_FMT "\n", cumSumVertices[size])); 1821 } 1822 /* TODO: Drop the parallel/sequential choice here and just use MatPartioner for much more flexibility */ 1823 if (usematpartitioning) { 1824 const char *prefix; 1825 1826 PetscCall(MatPartitioningCreate(PetscObjectComm((PetscObject)dm), &mp)); 1827 PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mp, "dm_plex_rebalance_shared_points_")); 1828 PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix)); 1829 PetscCall(PetscObjectPrependOptionsPrefix((PetscObject)mp, prefix)); 1830 PetscCall(MatPartitioningSetAdjacency(mp, A)); 1831 PetscCall(MatPartitioningSetNumberVertexWeights(mp, ncon)); 1832 PetscCall(MatPartitioningSetVertexWeights(mp, vtxwgt)); 1833 PetscCall(MatPartitioningSetFromOptions(mp)); 1834 PetscCall(MatPartitioningApply(mp, &ispart)); 1835 PetscCall(ISGetIndices(ispart, (const PetscInt **)&part)); 1836 } else if (parallel) { 1837 if (viewer) PetscCall(PetscViewerASCIIPrintf(viewer, "Using ParMETIS to partition graph.\n")); 1838 PetscCall(PetscMalloc1(cumSumVertices[rank + 1] - cumSumVertices[rank], &part)); 1839 PetscCall(MatGetRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, &xadj, &adjncy, &done)); 1840 PetscCheck(done, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Could not get adjacency information"); 1841 PetscCall(PetscMalloc1(4, &options)); 1842 options[0] = 1; 1843 options[1] = 0; /* Verbosity */ 1844 if (viewer) options[1] = 1; 1845 options[2] = 0; /* Seed */ 1846 options[3] = PARMETIS_PSR_COUPLED; /* Seed */ 1847 wgtflag = 2; 1848 numflag = 0; 1849 if (useInitialGuess) { 1850 PetscCall(PetscViewerASCIIPrintf(viewer, "THIS DOES NOT WORK! I don't know why. Using current distribution of points as initial guess.\n")); 1851 for (i = 0; i < numRows; i++) part[i] = rank; 1852 if (viewer) PetscCall(PetscViewerASCIIPrintf(viewer, "Using current distribution of points as initial guess.\n")); 1853 PetscStackPushExternal("ParMETIS_V3_RefineKway"); 1854 PetscCall(PetscLogEventBegin(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1855 ierr = ParMETIS_V3_RefineKway((PetscInt *)cumSumVertices, (idx_t *)xadj, (idx_t *)adjncy, vtxwgt, NULL, &wgtflag, &numflag, &ncon, &nparts, tpwgts, ubvec, options, &edgecut, part, &comm); 1856 PetscCall(PetscLogEventEnd(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1857 PetscStackPop; 1858 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in ParMETIS_V3_RefineKway()"); 1859 } else { 1860 PetscStackPushExternal("ParMETIS_V3_PartKway"); 1861 PetscCall(PetscLogEventBegin(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1862 ierr = ParMETIS_V3_PartKway((PetscInt *)cumSumVertices, (idx_t *)xadj, (idx_t *)adjncy, vtxwgt, NULL, &wgtflag, &numflag, &ncon, &nparts, tpwgts, ubvec, options, &edgecut, part, &comm); 1863 PetscCall(PetscLogEventEnd(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1864 PetscStackPop; 1865 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in ParMETIS_V3_PartKway()"); 1866 } 1867 PetscCall(MatRestoreRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, &xadj, &adjncy, &done)); 1868 PetscCall(PetscFree(options)); 1869 } else { 1870 if (viewer) PetscCall(PetscViewerASCIIPrintf(viewer, "Using METIS to partition graph.\n")); 1871 Mat As; 1872 PetscInt *partGlobal; 1873 PetscInt *numExclusivelyOwnedAll; 1874 1875 PetscCall(PetscMalloc1(cumSumVertices[rank + 1] - cumSumVertices[rank], &part)); 1876 PetscCall(MatGetSize(A, &numRows, NULL)); 1877 PetscCall(PetscLogEventBegin(DMPLEX_RebalGatherGraph, dm, 0, 0, 0)); 1878 PetscCall(MatMPIAdjToSeqRankZero(A, &As)); 1879 PetscCall(PetscLogEventEnd(DMPLEX_RebalGatherGraph, dm, 0, 0, 0)); 1880 1881 PetscCall(PetscMalloc1(size, &numExclusivelyOwnedAll)); 1882 numExclusivelyOwnedAll[rank] = numExclusivelyOwned; 1883 PetscCallMPI(MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, numExclusivelyOwnedAll, 1, MPIU_INT, comm)); 1884 1885 PetscCall(PetscMalloc1(numRows, &partGlobal)); 1886 PetscCall(PetscLogEventBegin(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1887 if (rank == 0) { 1888 PetscInt *vtxwgt_g, numRows_g; 1889 1890 PetscCall(MatGetRowIJ(As, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows_g, &xadj, &adjncy, &done)); 1891 PetscCall(PetscMalloc1(2 * numRows_g, &vtxwgt_g)); 1892 for (i = 0; i < size; i++) { 1893 vtxwgt_g[ncon * cumSumVertices[i]] = numExclusivelyOwnedAll[i]; 1894 if (ncon > 1) vtxwgt_g[ncon * cumSumVertices[i] + 1] = 1; 1895 for (j = cumSumVertices[i] + 1; j < cumSumVertices[i + 1]; j++) { 1896 vtxwgt_g[ncon * j] = 1; 1897 if (ncon > 1) vtxwgt_g[2 * j + 1] = 0; 1898 } 1899 } 1900 1901 PetscCall(PetscMalloc1(64, &options)); 1902 ierr = METIS_SetDefaultOptions(options); /* initialize all defaults */ 1903 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in METIS_SetDefaultOptions()"); 1904 options[METIS_OPTION_CONTIG] = 1; 1905 PetscStackPushExternal("METIS_PartGraphKway"); 1906 ierr = METIS_PartGraphKway(&numRows_g, &ncon, (idx_t *)xadj, (idx_t *)adjncy, vtxwgt_g, NULL, NULL, &nparts, tpwgts, ubvec, options, &edgecut, partGlobal); 1907 PetscStackPop; 1908 PetscCheck(ierr == METIS_OK, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in METIS_PartGraphKway()"); 1909 PetscCall(PetscFree(options)); 1910 PetscCall(PetscFree(vtxwgt_g)); 1911 PetscCall(MatRestoreRowIJ(As, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows_g, &xadj, &adjncy, &done)); 1912 PetscCall(MatDestroy(&As)); 1913 } 1914 PetscCall(PetscBarrier((PetscObject)dm)); 1915 PetscCall(PetscLogEventEnd(DMPLEX_RebalPartition, 0, 0, 0, 0)); 1916 PetscCall(PetscFree(numExclusivelyOwnedAll)); 1917 1918 /* scatter the partitioning information to ranks */ 1919 PetscCall(PetscLogEventBegin(DMPLEX_RebalScatterPart, 0, 0, 0, 0)); 1920 PetscCall(PetscMalloc1(size, &counts)); 1921 PetscCall(PetscMalloc1(size + 1, &mpiCumSumVertices)); 1922 for (i = 0; i < size; i++) PetscCall(PetscMPIIntCast(cumSumVertices[i + 1] - cumSumVertices[i], &(counts[i]))); 1923 for (i = 0; i <= size; i++) PetscCall(PetscMPIIntCast(cumSumVertices[i], &(mpiCumSumVertices[i]))); 1924 PetscCallMPI(MPI_Scatterv(partGlobal, counts, mpiCumSumVertices, MPIU_INT, part, counts[rank], MPIU_INT, 0, comm)); 1925 PetscCall(PetscFree(counts)); 1926 PetscCall(PetscFree(mpiCumSumVertices)); 1927 PetscCall(PetscFree(partGlobal)); 1928 PetscCall(PetscLogEventEnd(DMPLEX_RebalScatterPart, 0, 0, 0, 0)); 1929 } 1930 PetscCall(PetscFree(ubvec)); 1931 PetscCall(PetscFree(tpwgts)); 1932 1933 /* Rename the result so that the vertex resembling the exclusively owned points stays on the same rank */ 1934 PetscCall(PetscMalloc2(size, &firstVertices, size, &renumbering)); 1935 firstVertices[rank] = part[0]; 1936 PetscCallMPI(MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, firstVertices, 1, MPIU_INT, comm)); 1937 for (i = 0; i < size; i++) renumbering[firstVertices[i]] = i; 1938 for (i = 0; i < cumSumVertices[rank + 1] - cumSumVertices[rank]; i++) part[i] = renumbering[part[i]]; 1939 PetscCall(PetscFree2(firstVertices, renumbering)); 1940 1941 /* Check if the renumbering worked (this can fail when ParMETIS gives fewer partitions than there are processes) */ 1942 failed = (PetscInt)(part[0] != rank); 1943 PetscCall(MPIU_Allreduce(&failed, &failedGlobal, 1, MPIU_INT, MPI_SUM, comm)); 1944 if (failedGlobal > 0) { 1945 PetscCheck(failedGlobal <= 0, comm, PETSC_ERR_LIB, "Metis/Parmetis returned a bad partition"); 1946 PetscCall(PetscFree(vtxwgt)); 1947 PetscCall(PetscFree(toBalance)); 1948 PetscCall(PetscFree(isLeaf)); 1949 PetscCall(PetscFree(isNonExclusivelyOwned)); 1950 PetscCall(PetscFree(isExclusivelyOwned)); 1951 if (usematpartitioning) { 1952 PetscCall(ISRestoreIndices(ispart, (const PetscInt **)&part)); 1953 PetscCall(ISDestroy(&ispart)); 1954 } else PetscCall(PetscFree(part)); 1955 if (viewer) { 1956 PetscCall(PetscViewerPopFormat(viewer)); 1957 PetscCall(PetscViewerDestroy(&viewer)); 1958 } 1959 PetscCall(PetscLogEventEnd(DMPLEX_RebalanceSharedPoints, dm, 0, 0, 0)); 1960 PetscFunctionReturn(PETSC_SUCCESS); 1961 } 1962 1963 /* Check how well we did distributing points*/ 1964 if (viewer) { 1965 PetscCall(PetscViewerASCIIPrintf(viewer, "Number of owned entities of depth %" PetscInt_FMT ".\n", entityDepth)); 1966 PetscCall(PetscViewerASCIIPrintf(viewer, "Initial ")); 1967 PetscCall(DMPlexViewDistribution(comm, cumSumVertices[rank + 1] - cumSumVertices[rank], ncon, vtxwgt, NULL, viewer)); 1968 PetscCall(PetscViewerASCIIPrintf(viewer, "Rebalanced ")); 1969 PetscCall(DMPlexViewDistribution(comm, cumSumVertices[rank + 1] - cumSumVertices[rank], ncon, vtxwgt, part, viewer)); 1970 } 1971 1972 /* Check that every vertex is owned by a process that it is actually connected to. */ 1973 PetscCall(MatGetRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, (const PetscInt **)&xadj, (const PetscInt **)&adjncy, &done)); 1974 for (i = 1; i <= numNonExclusivelyOwned; i++) { 1975 PetscInt loc = 0; 1976 PetscCall(PetscFindInt(cumSumVertices[part[i]], xadj[i + 1] - xadj[i], &adjncy[xadj[i]], &loc)); 1977 /* If not, then just set the owner to the original owner (hopefully a rare event, it means that a vertex has been isolated) */ 1978 if (loc < 0) part[i] = rank; 1979 } 1980 PetscCall(MatRestoreRowIJ(A, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE, &numRows, (const PetscInt **)&xadj, (const PetscInt **)&adjncy, &done)); 1981 PetscCall(MatDestroy(&A)); 1982 1983 /* See how significant the influences of the previous fixing up step was.*/ 1984 if (viewer) { 1985 PetscCall(PetscViewerASCIIPrintf(viewer, "After fix ")); 1986 PetscCall(DMPlexViewDistribution(comm, cumSumVertices[rank + 1] - cumSumVertices[rank], ncon, vtxwgt, part, viewer)); 1987 } 1988 if (!usematpartitioning) PetscCall(PetscFree(vtxwgt)); 1989 else PetscCall(MatPartitioningDestroy(&mp)); 1990 1991 PetscCall(PetscLayoutDestroy(&layout)); 1992 1993 PetscCall(PetscLogEventBegin(DMPLEX_RebalRewriteSF, dm, 0, 0, 0)); 1994 /* Rewrite the SF to reflect the new ownership. */ 1995 PetscCall(PetscMalloc1(numNonExclusivelyOwned, &pointsToRewrite)); 1996 counter = 0; 1997 for (i = 0; i < pEnd - pStart; i++) { 1998 if (toBalance[i]) { 1999 if (isNonExclusivelyOwned[i]) { 2000 pointsToRewrite[counter] = i + pStart; 2001 counter++; 2002 } 2003 } 2004 } 2005 PetscCall(DMPlexRewriteSF(dm, numNonExclusivelyOwned, pointsToRewrite, part + 1, degrees)); 2006 PetscCall(PetscFree(pointsToRewrite)); 2007 PetscCall(PetscLogEventEnd(DMPLEX_RebalRewriteSF, dm, 0, 0, 0)); 2008 2009 PetscCall(PetscFree(toBalance)); 2010 PetscCall(PetscFree(isLeaf)); 2011 PetscCall(PetscFree(isNonExclusivelyOwned)); 2012 PetscCall(PetscFree(isExclusivelyOwned)); 2013 if (usematpartitioning) { 2014 PetscCall(ISRestoreIndices(ispart, (const PetscInt **)&part)); 2015 PetscCall(ISDestroy(&ispart)); 2016 } else PetscCall(PetscFree(part)); 2017 if (viewer) { 2018 PetscCall(PetscViewerPopFormat(viewer)); 2019 PetscCall(PetscViewerDestroy(&viewer)); 2020 } 2021 if (success) *success = PETSC_TRUE; 2022 PetscCall(PetscLogEventEnd(DMPLEX_RebalanceSharedPoints, dm, 0, 0, 0)); 2023 PetscFunctionReturn(PETSC_SUCCESS); 2024 #else 2025 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh partitioning needs external package support.\nPlease reconfigure with --download-parmetis."); 2026 #endif 2027 } 2028