Actual source code: plexpartition.c

  1: #include <petsc/private/dmpleximpl.h>
  2: #include <petsc/private/partitionerimpl.h>
  3: #include <petsc/private/hashseti.h>

  5: const char *const DMPlexCSRAlgorithms[] = {"mat", "graph", "overlap", "DMPlexCSRAlgorithm", "DM_PLEX_CSR_", NULL};

  7: static inline PetscInt DMPlex_GlobalID(PetscInt point)
  8: {
  9:   return point >= 0 ? point : -(point + 1);
 10: }

 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;

 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;

 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];

 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: }

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;

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), &section));
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;

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;

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;

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;

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));

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));

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:   }

305:   if (offsets) *offsets = vOffsets;
306:   if (adjacency) *adjacency = graph;

308:   /* Cleanup */
309:   PetscCall(PetscSegBufferDestroy(&adjBuffer));
310:   PetscCall(PetscSectionDestroy(&section));
311:   PetscCall(ISRestoreIndices(cellNumbering, &cellNum));
312:   PetscCall(ISDestroy(&cellNumbering));
313:   PetscFunctionReturn(PETSC_SUCCESS);
314: }

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;

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 */

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));

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];

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));

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];

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));

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));

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;

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;

415:       row = rows[cone[f] - fStart];
416:       PetscCall(MatSetValues(conn, 1, &row, 1, &col, &v, INSERT_VALUES));

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];

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));

436:   /* Get parallel CSR by doing conn^T * conn */
437:   PetscCall(MatTransposeMatMult(conn, conn, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &CSR));
438:   PetscCall(MatDestroy(&conn));

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");

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];

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:   }

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: }

477: /*@C
478:   DMPlexCreatePartitionerGraph - Create a CSR graph of point connections for the partitioner

480:   Collective

482:   Input Parameters:
483: + dm     - The mesh `DM`
484: - height - Height of the strata from which to construct the graph

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.

492:   Options Database Key:
493: . -dm_plex_csr_alg <mat,graph,overlap> - Choose the algorithm for computing the CSR graph

495:   Level: developer

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().

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;

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: }

523: /*@C
524:   DMPlexCreateNeighborCSR - Create a mesh graph (cell-cell adjacency) in parallel CSR format.

526:   Collective

528:   Input Parameters:
529: + dm         - The `DMPLEX`
530: - cellHeight - The height of mesh points to treat as cells (default should be 0)

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

537:   Level: advanced

539:   Note:
540:   This is suitable for input to a mesh partitioner like ParMetis.

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;

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;

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;

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;

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;

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 */

624:     for (c = cStart; c < cEnd; ++c) {
625:       PetscInt corners;

627:       PetscCall(DMPlexGetConeSize(dm, c, &corners));
628:       if (!cornersSeen[corners]) {
629:         PetscInt nFV;

631:         PetscCheck(numFaceCases < maxFaceCases, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Exceeded maximum number of face recognition cases");
632:         cornersSeen[corners] = 1;

634:         PetscCall(DMPlexGetNumFaceVertices(dm, cellDim, corners, &nFV));

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;

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;

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;

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];

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;

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;

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;

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: }

722: /*@
723:   PetscPartitionerDMPlexPartition - Create a non-overlapping partition of the cells in the mesh

725:   Collective

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`

732:   Output Parameters:
733: + partSection - The `PetscSection` giving the division of points by partition
734: - partition   - The list of points by partition

736:   Level: developer

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.

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;

751:   PetscFunctionBegin;
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;

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;

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;

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;

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;

815:         /* skip cells in the overlap */
816:         if (gid && gid[p - pStart] < 0) continue;

818:         if (section) {
819:           PetscInt cl, clSize, clOff;

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 */

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];

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;

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;

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;

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: }

890: /*@
891:   DMPlexGetPartitioner - Get the mesh partitioner

893:   Not Collective

895:   Input Parameter:
896: . dm - The `DM`

898:   Output Parameter:
899: . part - The `PetscPartitioner`

901:   Level: developer

903:   Note:
904:   This gets a borrowed reference, so the user should not destroy this `PetscPartitioner`.

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;

912:   PetscFunctionBegin;
914:   PetscAssertPointer(part, 2);
915:   *part = mesh->partitioner;
916:   PetscFunctionReturn(PETSC_SUCCESS);
917: }

919: /*@
920:   DMPlexSetPartitioner - Set the mesh partitioner

922:   logically Collective

924:   Input Parameters:
925: + dm   - The `DM`
926: - part - The partitioner

928:   Level: developer

930:   Note:
931:   Any existing `PetscPartitioner` will be destroyed.

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;

939:   PetscFunctionBegin;
942:   PetscCall(PetscObjectReference((PetscObject)part));
943:   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
944:   mesh->partitioner = part;
945:   PetscFunctionReturn(PETSC_SUCCESS);
946: }

948: static PetscErrorCode DMPlexAddClosure_Private(DM dm, PetscHSetI ht, PetscInt point)
949: {
950:   const PetscInt *cone;
951:   PetscInt        coneSize, c;
952:   PetscBool       missing;

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: }

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;

970:     PetscCall(DMPlexGetTreeParent(dm, point, &parent, NULL));
971:     if (parent != point) {
972:       PetscInt closureSize, *closure = NULL, i;

974:       PetscCall(DMPlexGetTransitiveClosure(dm, parent, PETSC_TRUE, &closureSize, &closure));
975:       for (i = 0; i < closureSize; i++) {
976:         PetscInt cpoint = closure[2 * i];

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;

988:     PetscCall(DMPlexGetTreeChildren(dm, point, &numChildren, &children));
989:     if (numChildren) {
990:       PetscInt i;

992:       for (i = 0; i < numChildren; i++) {
993:         PetscInt cpoint = children[i];

995:         PetscCall(PetscHSetIAdd(ht, cpoint));
996:         PetscCall(DMPlexAddClosure_Tree(dm, ht, cpoint, PETSC_FALSE, PETSC_TRUE));
997:       }
998:     }
999:   }
1000:   PetscFunctionReturn(PETSC_SUCCESS);
1001: }

1003: static PetscErrorCode DMPlexAddClosureTree_Up_Private(DM dm, PetscHSetI ht, PetscInt point)
1004: {
1005:   PetscInt parent;

1007:   PetscFunctionBeginHot;
1008:   PetscCall(DMPlexGetTreeParent(dm, point, &parent, NULL));
1009:   if (point != parent) {
1010:     const PetscInt *cone;
1011:     PetscInt        coneSize, c;

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];

1020:       PetscCall(DMPlexAddClosureTree_Up_Private(dm, ht, cp));
1021:     }
1022:   }
1023:   PetscFunctionReturn(PETSC_SUCCESS);
1024: }

1026: static PetscErrorCode DMPlexAddClosureTree_Down_Private(DM dm, PetscHSetI ht, PetscInt point)
1027: {
1028:   PetscInt        i, numChildren;
1029:   const PetscInt *children;

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: }

1037: static PetscErrorCode DMPlexAddClosureTree_Private(DM dm, PetscHSetI ht, PetscInt point)
1038: {
1039:   const PetscInt *cone;
1040:   PetscInt        coneSize, c;

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: }

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;

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: }

1088: /*@
1089:   DMPlexPartitionLabelClosure - Add the closure of all points to the partition label

1091:   Input Parameters:
1092: + dm    - The `DM`
1093: - label - `DMLabel` assigning ranks to remote roots

1095:   Level: developer

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;

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: }

1125: /*@
1126:   DMPlexPartitionLabelAdjacency - Add one level of adjacent points to the partition label

1128:   Input Parameters:
1129: + dm    - The `DM`
1130: - label - `DMLabel` assigning ranks to remote roots

1132:   Level: developer

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;

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];

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: }

1167: /*@
1168:   DMPlexPartitionLabelPropagate - Propagate points in a partition label over the point `PetscSF`

1170:   Input Parameters:
1171: + dm    - The `DM`
1172: - label - `DMLabel` assigning ranks to remote roots

1174:   Level: developer

1176:   Note:
1177:   This is required when generating multi-level overlaps to capture
1178:   overlap points from non-neighbouring partitions.

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;

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: }

1229: /*@
1230:   DMPlexPartitionLabelInvert - Create a partition label of remote roots from a local root label

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

1237:   Output Parameter:
1238: . leafLabel - `DMLabel` assigning ranks to remote roots

1240:   Level: developer

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.

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;

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));

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;

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:   }

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;

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:   }

1343:   /* Communicate overlap using process star forest */
1344:   if (processSF || mpiOverflow) {
1345:     PetscSF      procSF;
1346:     PetscSection leafSection;

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:     }

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:   }

1365:   for (p = 0; p < leafSize; p++) PetscCall(DMLabelSetValue(leafLabel, leafPoints[p].index, leafPoints[p].rank));

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: }

1376: /*@
1377:   DMPlexPartitionLabelCreateSF - Create a star forest from a label that assigns ranks to points

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

1384:   Output Parameter:
1385: . sf - The star forest communication context encapsulating the defined mapping

1387:   Level: developer

1389:   Note:
1390:   The incoming label is a receiver mapping of remote points to their parent rank.

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;

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));

1408:   if (sortRanks) {
1409:     IS is;

1411:     PetscCall(ISDuplicate(neighborsIS, &is));
1412:     PetscCall(ISSort(is));
1413:     PetscCall(ISDestroy(&neighborsIS));
1414:     neighborsIS = is;
1415:   }
1416:   myRank = sortRanks ? -1 : rank;

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));

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:   }

1442:   /* Now add remote points */
1443:   for (n = 0; n < nNeighbors; ++n) {
1444:     PetscMPIInt nn;

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:   }

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: }

1469: #if defined(PETSC_HAVE_PARMETIS)
1470:   #include <parmetis.h>
1471: #endif

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 `PetsSF` of a `DM` (in place).

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()`.

1487:   Level: developer

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;

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));

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;

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) */

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));

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.");

1561:       newOwners[oldNumber]  = newOwner;
1562:       newNumbers[oldNumber] = newNumber;
1563:     }
1564:   }
1565:   PetscCall(PetscFree(cumSumDegrees));
1566:   PetscCall(PetscFree(locationsOfLeafs));
1567:   PetscCall(PetscFree(remoteLocalPointOfLeafs));

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));

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:   }

1584:   /* Now set up the new sf by creating the leaf arrays */
1585:   PetscCall(PetscMalloc1(leafCounter, &leafsNew));
1586:   PetscCall(PetscMalloc1(leafCounter, &leafLocationsNew));

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:   }

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: }

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;

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: }

1643: #endif

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.

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.

1654:   Output Parameter:
1655: . success - whether the graph partitioning was successful or not, optional. Unsuccessful simply means no change to the partitioning

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

1663:   Level: intermediate

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;

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));

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", &parallel));
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));

1722:   PetscCall(PetscLogEventBegin(DMPLEX_RebalanceSharedPoints, dm, 0, 0, 0));

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));

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);

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:   }

1750:   /* mark all the leafs */
1751:   for (i = 0; i < nleafs; i++) isLeaf[ilocal[i] - pStart] = PETSC_TRUE;

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, &degrees));
1756:   PetscCall(PetscSFComputeDegreeEnd(sf, &degrees));
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:   }

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:   }

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));

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));

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;

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:   }

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;

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;

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));

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));

1912:     PetscCall(PetscMalloc1(numRows, &partGlobal));
1913:     PetscCall(PetscLogEventBegin(DMPLEX_RebalPartition, 0, 0, 0, 0));
1914:     if (rank == 0) {
1915:       PetscInt *vtxwgt_g, numRows_g;

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:       }

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));

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));

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));

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:   }

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:   }

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));

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));

2018:   PetscCall(PetscLayoutDestroy(&layout));

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));

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: }