xref: /petsc/src/vec/is/sf/utils/sfutils.c (revision 0619917b5a674bb687c64e7daba2ab22be99af31)
1 #include <petsc/private/sfimpl.h> /*I  "petscsf.h"   I*/
2 #include <petsc/private/sectionimpl.h>
3 
4 /*@
5   PetscSFSetGraphLayout - Set a parallel star forest via global indices and a `PetscLayout`
6 
7   Collective
8 
9   Input Parameters:
10 + sf        - star forest
11 . layout    - `PetscLayout` defining the global space for roots
12 . nleaves   - number of leaf vertices on the current process, each of these references a root on any process
13 . ilocal    - locations of leaves in leafdata buffers, pass NULL for contiguous storage
14 . localmode - copy mode for ilocal
15 - gremote   - root vertices in global numbering corresponding to leaves in ilocal
16 
17   Level: intermediate
18 
19   Note:
20   Global indices must lie in [0, N) where N is the global size of layout.
21   Leaf indices in ilocal get sorted; this means the user-provided array gets sorted if localmode is `PETSC_OWN_POINTER`.
22 
23   Developer Notes:
24   Local indices which are the identity permutation in the range [0,nleaves) are discarded as they
25   encode contiguous storage. In such case, if localmode is `PETSC_OWN_POINTER`, the memory is deallocated as it is not
26   needed
27 
28 .seealso: `PetscSF`, `PetscSFGetGraphLayout()`, `PetscSFCreate()`, `PetscSFView()`, `PetscSFSetGraph()`, `PetscSFGetGraph()`
29 @*/
30 PetscErrorCode PetscSFSetGraphLayout(PetscSF sf, PetscLayout layout, PetscInt nleaves, PetscInt *ilocal, PetscCopyMode localmode, const PetscInt *gremote)
31 {
32   const PetscInt *range;
33   PetscInt        i, nroots, ls = -1, ln = -1;
34   PetscMPIInt     lr = -1;
35   PetscSFNode    *remote;
36 
37   PetscFunctionBegin;
38   PetscCall(PetscLayoutSetUp(layout));
39   PetscCall(PetscLayoutGetLocalSize(layout, &nroots));
40   PetscCall(PetscLayoutGetRanges(layout, &range));
41   PetscCall(PetscMalloc1(nleaves, &remote));
42   if (nleaves) ls = gremote[0] + 1;
43   for (i = 0; i < nleaves; i++) {
44     const PetscInt idx = gremote[i] - ls;
45     if (idx < 0 || idx >= ln) { /* short-circuit the search */
46       PetscCall(PetscLayoutFindOwnerIndex(layout, gremote[i], &lr, &remote[i].index));
47       remote[i].rank = lr;
48       ls             = range[lr];
49       ln             = range[lr + 1] - ls;
50     } else {
51       remote[i].rank  = lr;
52       remote[i].index = idx;
53     }
54   }
55   PetscCall(PetscSFSetGraph(sf, nroots, nleaves, ilocal, localmode, remote, PETSC_OWN_POINTER));
56   PetscFunctionReturn(PETSC_SUCCESS);
57 }
58 
59 /*@C
60   PetscSFGetGraphLayout - Get the global indices and `PetscLayout` that describe this star forest
61 
62   Collective
63 
64   Input Parameter:
65 . sf - star forest
66 
67   Output Parameters:
68 + layout  - `PetscLayout` defining the global space for roots
69 . nleaves - number of leaf vertices on the current process, each of these references a root on any process
70 . ilocal  - locations of leaves in leafdata buffers, or NULL for contiguous storage
71 - gremote - root vertices in global numbering corresponding to leaves in ilocal
72 
73   Level: intermediate
74 
75   Notes:
76   The outputs are such that passing them as inputs to `PetscSFSetGraphLayout()` would lead to the same star forest.
77   The outputs layout and gremote are freshly created each time this function is called,
78   so they need to be freed by user and cannot be qualified as const.
79 
80 .seealso: `PetscSF`, `PetscSFSetGraphLayout()`, `PetscSFCreate()`, `PetscSFView()`, `PetscSFSetGraph()`, `PetscSFGetGraph()`
81 @*/
82 PetscErrorCode PetscSFGetGraphLayout(PetscSF sf, PetscLayout *layout, PetscInt *nleaves, const PetscInt *ilocal[], PetscInt *gremote[])
83 {
84   PetscInt           nr, nl;
85   const PetscSFNode *ir;
86   PetscLayout        lt;
87 
88   PetscFunctionBegin;
89   PetscCall(PetscSFGetGraph(sf, &nr, &nl, ilocal, &ir));
90   PetscCall(PetscLayoutCreateFromSizes(PetscObjectComm((PetscObject)sf), nr, PETSC_DECIDE, 1, &lt));
91   if (gremote) {
92     PetscInt        i;
93     const PetscInt *range;
94     PetscInt       *gr;
95 
96     PetscCall(PetscLayoutGetRanges(lt, &range));
97     PetscCall(PetscMalloc1(nl, &gr));
98     for (i = 0; i < nl; i++) gr[i] = range[ir[i].rank] + ir[i].index;
99     *gremote = gr;
100   }
101   if (nleaves) *nleaves = nl;
102   if (layout) *layout = lt;
103   else PetscCall(PetscLayoutDestroy(&lt));
104   PetscFunctionReturn(PETSC_SUCCESS);
105 }
106 
107 /*@
108   PetscSFSetGraphSection - Sets the `PetscSF` graph encoding the parallel dof overlap based upon the `PetscSection` describing the data layout.
109 
110   Input Parameters:
111 + sf            - The `PetscSF`
112 . localSection  - `PetscSection` describing the local data layout
113 - globalSection - `PetscSection` describing the global data layout
114 
115   Level: developer
116 
117 .seealso: `PetscSF`, `PetscSFSetGraph()`, `PetscSFSetGraphLayout()`
118 @*/
119 PetscErrorCode PetscSFSetGraphSection(PetscSF sf, PetscSection localSection, PetscSection globalSection)
120 {
121   MPI_Comm        comm;
122   PetscLayout     layout;
123   const PetscInt *ranges;
124   PetscInt       *local;
125   PetscSFNode    *remote;
126   PetscInt        pStart, pEnd, p, nroots, nleaves = 0, l;
127   PetscMPIInt     size, rank;
128 
129   PetscFunctionBegin;
130   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 1);
131   PetscValidHeaderSpecific(localSection, PETSC_SECTION_CLASSID, 2);
132   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
133 
134   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
135   PetscCallMPI(MPI_Comm_size(comm, &size));
136   PetscCallMPI(MPI_Comm_rank(comm, &rank));
137   PetscCall(PetscSectionGetChart(globalSection, &pStart, &pEnd));
138   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &nroots));
139   PetscCall(PetscLayoutCreate(comm, &layout));
140   PetscCall(PetscLayoutSetBlockSize(layout, 1));
141   PetscCall(PetscLayoutSetLocalSize(layout, nroots));
142   PetscCall(PetscLayoutSetUp(layout));
143   PetscCall(PetscLayoutGetRanges(layout, &ranges));
144   for (p = pStart; p < pEnd; ++p) {
145     PetscInt gdof, gcdof;
146 
147     PetscCall(PetscSectionGetDof(globalSection, p, &gdof));
148     PetscCall(PetscSectionGetConstraintDof(globalSection, p, &gcdof));
149     PetscCheck(gcdof <= (gdof < 0 ? -(gdof + 1) : gdof), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " has %" PetscInt_FMT " constraints > %" PetscInt_FMT " dof", p, gcdof, (gdof < 0 ? -(gdof + 1) : gdof));
150     nleaves += gdof < 0 ? -(gdof + 1) - gcdof : gdof - gcdof;
151   }
152   PetscCall(PetscMalloc1(nleaves, &local));
153   PetscCall(PetscMalloc1(nleaves, &remote));
154   for (p = pStart, l = 0; p < pEnd; ++p) {
155     const PetscInt *cind;
156     PetscInt        dof, cdof, off, gdof, gcdof, goff, gsize, d, c;
157 
158     PetscCall(PetscSectionGetDof(localSection, p, &dof));
159     PetscCall(PetscSectionGetOffset(localSection, p, &off));
160     PetscCall(PetscSectionGetConstraintDof(localSection, p, &cdof));
161     PetscCall(PetscSectionGetConstraintIndices(localSection, p, &cind));
162     PetscCall(PetscSectionGetDof(globalSection, p, &gdof));
163     PetscCall(PetscSectionGetConstraintDof(globalSection, p, &gcdof));
164     PetscCall(PetscSectionGetOffset(globalSection, p, &goff));
165     if (!gdof) continue; /* Censored point */
166     gsize = gdof < 0 ? -(gdof + 1) - gcdof : gdof - gcdof;
167     if (gsize != dof - cdof) {
168       PetscCheck(gsize == dof, comm, PETSC_ERR_ARG_WRONG, "Global dof %" PetscInt_FMT " for point %" PetscInt_FMT " is neither the constrained size %" PetscInt_FMT ", nor the unconstrained %" PetscInt_FMT, gsize, p, dof - cdof, dof);
169       cdof = 0; /* Ignore constraints */
170     }
171     for (d = 0, c = 0; d < dof; ++d) {
172       if ((c < cdof) && (cind[c] == d)) {
173         ++c;
174         continue;
175       }
176       local[l + d - c] = off + d;
177     }
178     PetscCheck(d - c == gsize, comm, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT ": Global dof %" PetscInt_FMT " != %" PetscInt_FMT " size - number of constraints", p, gsize, d - c);
179     if (gdof < 0) {
180       for (d = 0; d < gsize; ++d, ++l) {
181         PetscInt offset = -(goff + 1) + d, r;
182 
183         PetscCall(PetscFindInt(offset, size + 1, ranges, &r));
184         if (r < 0) r = -(r + 2);
185         PetscCheck(!(r < 0) && !(r >= size), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " mapped to invalid process %" PetscInt_FMT " (%" PetscInt_FMT ", %" PetscInt_FMT ")", p, r, gdof, goff);
186         remote[l].rank  = r;
187         remote[l].index = offset - ranges[r];
188       }
189     } else {
190       for (d = 0; d < gsize; ++d, ++l) {
191         remote[l].rank  = rank;
192         remote[l].index = goff + d - ranges[rank];
193       }
194     }
195   }
196   PetscCheck(l == nleaves, comm, PETSC_ERR_PLIB, "Iteration error, l %" PetscInt_FMT " != nleaves %" PetscInt_FMT, l, nleaves);
197   PetscCall(PetscLayoutDestroy(&layout));
198   PetscCall(PetscSFSetGraph(sf, nroots, nleaves, local, PETSC_OWN_POINTER, remote, PETSC_OWN_POINTER));
199   PetscFunctionReturn(PETSC_SUCCESS);
200 }
201 
202 /*@C
203   PetscSFDistributeSection - Create a new `PetscSection` reorganized, moving from the root to the leaves of the `PetscSF`
204 
205   Collective
206 
207   Input Parameters:
208 + sf          - The `PetscSF`
209 - rootSection - Section defined on root space
210 
211   Output Parameters:
212 + remoteOffsets - root offsets in leaf storage, or NULL
213 - leafSection   - Section defined on the leaf space
214 
215   Level: advanced
216 
217   Fortran Notes:
218   In Fortran, use PetscSFDistributeSectionF90()
219 
220 .seealso: `PetscSF`, `PetscSFCreate()`
221 @*/
222 PetscErrorCode PetscSFDistributeSection(PetscSF sf, PetscSection rootSection, PetscInt **remoteOffsets, PetscSection leafSection)
223 {
224   PetscSF         embedSF;
225   const PetscInt *indices;
226   IS              selected;
227   PetscInt        numFields, nroots, rpStart, rpEnd, lpStart = PETSC_MAX_INT, lpEnd = -1, f, c;
228   PetscBool      *sub, hasc;
229 
230   PetscFunctionBegin;
231   PetscCall(PetscLogEventBegin(PETSCSF_DistSect, sf, 0, 0, 0));
232   PetscCall(PetscSectionGetNumFields(rootSection, &numFields));
233   if (numFields) {
234     IS perm;
235 
236     /* PetscSectionSetNumFields() calls PetscSectionReset(), which destroys
237        leafSection->perm. To keep this permutation set by the user, we grab
238        the reference before calling PetscSectionSetNumFields() and set it
239        back after. */
240     PetscCall(PetscSectionGetPermutation(leafSection, &perm));
241     PetscCall(PetscObjectReference((PetscObject)perm));
242     PetscCall(PetscSectionSetNumFields(leafSection, numFields));
243     PetscCall(PetscSectionSetPermutation(leafSection, perm));
244     PetscCall(ISDestroy(&perm));
245   }
246   PetscCall(PetscMalloc1(numFields + 2, &sub));
247   sub[1] = rootSection->bc ? PETSC_TRUE : PETSC_FALSE;
248   for (f = 0; f < numFields; ++f) {
249     PetscSectionSym sym, dsym = NULL;
250     const char     *name    = NULL;
251     PetscInt        numComp = 0;
252 
253     sub[2 + f] = rootSection->field[f]->bc ? PETSC_TRUE : PETSC_FALSE;
254     PetscCall(PetscSectionGetFieldComponents(rootSection, f, &numComp));
255     PetscCall(PetscSectionGetFieldName(rootSection, f, &name));
256     PetscCall(PetscSectionGetFieldSym(rootSection, f, &sym));
257     if (sym) PetscCall(PetscSectionSymDistribute(sym, sf, &dsym));
258     PetscCall(PetscSectionSetFieldComponents(leafSection, f, numComp));
259     PetscCall(PetscSectionSetFieldName(leafSection, f, name));
260     PetscCall(PetscSectionSetFieldSym(leafSection, f, dsym));
261     PetscCall(PetscSectionSymDestroy(&dsym));
262     for (c = 0; c < rootSection->numFieldComponents[f]; ++c) {
263       PetscCall(PetscSectionGetComponentName(rootSection, f, c, &name));
264       PetscCall(PetscSectionSetComponentName(leafSection, f, c, name));
265     }
266   }
267   PetscCall(PetscSectionGetChart(rootSection, &rpStart, &rpEnd));
268   PetscCall(PetscSFGetGraph(sf, &nroots, NULL, NULL, NULL));
269   rpEnd = PetscMin(rpEnd, nroots);
270   rpEnd = PetscMax(rpStart, rpEnd);
271   /* see if we can avoid creating the embedded SF, since it can cost more than an allreduce */
272   sub[0] = (PetscBool)(nroots != rpEnd - rpStart);
273   PetscCall(MPIU_Allreduce(MPI_IN_PLACE, sub, 2 + numFields, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)sf)));
274   if (sub[0]) {
275     PetscCall(ISCreateStride(PETSC_COMM_SELF, rpEnd - rpStart, rpStart, 1, &selected));
276     PetscCall(ISGetIndices(selected, &indices));
277     PetscCall(PetscSFCreateEmbeddedRootSF(sf, rpEnd - rpStart, indices, &embedSF));
278     PetscCall(ISRestoreIndices(selected, &indices));
279     PetscCall(ISDestroy(&selected));
280   } else {
281     PetscCall(PetscObjectReference((PetscObject)sf));
282     embedSF = sf;
283   }
284   PetscCall(PetscSFGetLeafRange(embedSF, &lpStart, &lpEnd));
285   lpEnd++;
286 
287   PetscCall(PetscSectionSetChart(leafSection, lpStart, lpEnd));
288 
289   /* Constrained dof section */
290   hasc = sub[1];
291   for (f = 0; f < numFields; ++f) hasc = (PetscBool)(hasc || sub[2 + f]);
292 
293   /* Could fuse these at the cost of copies and extra allocation */
294   PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->atlasDof[-rpStart], &leafSection->atlasDof[-lpStart], MPI_REPLACE));
295   PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->atlasDof[-rpStart], &leafSection->atlasDof[-lpStart], MPI_REPLACE));
296   if (sub[1]) {
297     PetscCall(PetscSectionCheckConstraints_Private(rootSection));
298     PetscCall(PetscSectionCheckConstraints_Private(leafSection));
299     PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->bc->atlasDof[-rpStart], &leafSection->bc->atlasDof[-lpStart], MPI_REPLACE));
300     PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->bc->atlasDof[-rpStart], &leafSection->bc->atlasDof[-lpStart], MPI_REPLACE));
301   }
302   for (f = 0; f < numFields; ++f) {
303     PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->field[f]->atlasDof[-rpStart], &leafSection->field[f]->atlasDof[-lpStart], MPI_REPLACE));
304     PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->field[f]->atlasDof[-rpStart], &leafSection->field[f]->atlasDof[-lpStart], MPI_REPLACE));
305     if (sub[2 + f]) {
306       PetscCall(PetscSectionCheckConstraints_Private(rootSection->field[f]));
307       PetscCall(PetscSectionCheckConstraints_Private(leafSection->field[f]));
308       PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->field[f]->bc->atlasDof[-rpStart], &leafSection->field[f]->bc->atlasDof[-lpStart], MPI_REPLACE));
309       PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->field[f]->bc->atlasDof[-rpStart], &leafSection->field[f]->bc->atlasDof[-lpStart], MPI_REPLACE));
310     }
311   }
312   if (remoteOffsets) {
313     PetscCall(PetscMalloc1(lpEnd - lpStart, remoteOffsets));
314     PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->atlasOff[-rpStart], &(*remoteOffsets)[-lpStart], MPI_REPLACE));
315     PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->atlasOff[-rpStart], &(*remoteOffsets)[-lpStart], MPI_REPLACE));
316   }
317   PetscCall(PetscSectionInvalidateMaxDof_Internal(leafSection));
318   PetscCall(PetscSectionSetUp(leafSection));
319   if (hasc) { /* need to communicate bcIndices */
320     PetscSF   bcSF;
321     PetscInt *rOffBc;
322 
323     PetscCall(PetscMalloc1(lpEnd - lpStart, &rOffBc));
324     if (sub[1]) {
325       PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->bc->atlasOff[-rpStart], &rOffBc[-lpStart], MPI_REPLACE));
326       PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->bc->atlasOff[-rpStart], &rOffBc[-lpStart], MPI_REPLACE));
327       PetscCall(PetscSFCreateSectionSF(embedSF, rootSection->bc, rOffBc, leafSection->bc, &bcSF));
328       PetscCall(PetscSFBcastBegin(bcSF, MPIU_INT, rootSection->bcIndices, leafSection->bcIndices, MPI_REPLACE));
329       PetscCall(PetscSFBcastEnd(bcSF, MPIU_INT, rootSection->bcIndices, leafSection->bcIndices, MPI_REPLACE));
330       PetscCall(PetscSFDestroy(&bcSF));
331     }
332     for (f = 0; f < numFields; ++f) {
333       if (sub[2 + f]) {
334         PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->field[f]->bc->atlasOff[-rpStart], &rOffBc[-lpStart], MPI_REPLACE));
335         PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->field[f]->bc->atlasOff[-rpStart], &rOffBc[-lpStart], MPI_REPLACE));
336         PetscCall(PetscSFCreateSectionSF(embedSF, rootSection->field[f]->bc, rOffBc, leafSection->field[f]->bc, &bcSF));
337         PetscCall(PetscSFBcastBegin(bcSF, MPIU_INT, rootSection->field[f]->bcIndices, leafSection->field[f]->bcIndices, MPI_REPLACE));
338         PetscCall(PetscSFBcastEnd(bcSF, MPIU_INT, rootSection->field[f]->bcIndices, leafSection->field[f]->bcIndices, MPI_REPLACE));
339         PetscCall(PetscSFDestroy(&bcSF));
340       }
341     }
342     PetscCall(PetscFree(rOffBc));
343   }
344   PetscCall(PetscSFDestroy(&embedSF));
345   PetscCall(PetscFree(sub));
346   PetscCall(PetscLogEventEnd(PETSCSF_DistSect, sf, 0, 0, 0));
347   PetscFunctionReturn(PETSC_SUCCESS);
348 }
349 
350 /*@C
351   PetscSFCreateRemoteOffsets - Create offsets for point data on remote processes
352 
353   Collective
354 
355   Input Parameters:
356 + sf          - The `PetscSF`
357 . rootSection - Data layout of remote points for outgoing data (this is layout for SF roots)
358 - leafSection - Data layout of local points for incoming data  (this is layout for SF leaves)
359 
360   Output Parameter:
361 . remoteOffsets - Offsets for point data on remote processes (these are offsets from the root section), or NULL
362 
363   Level: developer
364 
365   Fortran Notes:
366   In Fortran, use PetscSFCreateRemoteOffsetsF90()
367 
368 .seealso: `PetscSF`, `PetscSFCreate()`
369 @*/
370 PetscErrorCode PetscSFCreateRemoteOffsets(PetscSF sf, PetscSection rootSection, PetscSection leafSection, PetscInt **remoteOffsets)
371 {
372   PetscSF         embedSF;
373   const PetscInt *indices;
374   IS              selected;
375   PetscInt        numRoots, rpStart = 0, rpEnd = 0, lpStart = 0, lpEnd = 0;
376 
377   PetscFunctionBegin;
378   *remoteOffsets = NULL;
379   PetscCall(PetscSFGetGraph(sf, &numRoots, NULL, NULL, NULL));
380   if (numRoots < 0) PetscFunctionReturn(PETSC_SUCCESS);
381   PetscCall(PetscLogEventBegin(PETSCSF_RemoteOff, sf, 0, 0, 0));
382   PetscCall(PetscSectionGetChart(rootSection, &rpStart, &rpEnd));
383   PetscCall(PetscSectionGetChart(leafSection, &lpStart, &lpEnd));
384   PetscCall(ISCreateStride(PETSC_COMM_SELF, rpEnd - rpStart, rpStart, 1, &selected));
385   PetscCall(ISGetIndices(selected, &indices));
386   PetscCall(PetscSFCreateEmbeddedRootSF(sf, rpEnd - rpStart, indices, &embedSF));
387   PetscCall(ISRestoreIndices(selected, &indices));
388   PetscCall(ISDestroy(&selected));
389   PetscCall(PetscCalloc1(lpEnd - lpStart, remoteOffsets));
390   PetscCall(PetscSFBcastBegin(embedSF, MPIU_INT, &rootSection->atlasOff[-rpStart], &(*remoteOffsets)[-lpStart], MPI_REPLACE));
391   PetscCall(PetscSFBcastEnd(embedSF, MPIU_INT, &rootSection->atlasOff[-rpStart], &(*remoteOffsets)[-lpStart], MPI_REPLACE));
392   PetscCall(PetscSFDestroy(&embedSF));
393   PetscCall(PetscLogEventEnd(PETSCSF_RemoteOff, sf, 0, 0, 0));
394   PetscFunctionReturn(PETSC_SUCCESS);
395 }
396 
397 /*@C
398   PetscSFCreateSectionSF - Create an expanded `PetscSF` of dofs, assuming the input `PetscSF` relates points
399 
400   Collective
401 
402   Input Parameters:
403 + sf            - The `PetscSF`
404 . rootSection   - Data layout of remote points for outgoing data (this is usually the serial section)
405 . remoteOffsets - Offsets for point data on remote processes (these are offsets from the root section), or NULL
406 - leafSection   - Data layout of local points for incoming data  (this is the distributed section)
407 
408   Output Parameter:
409 . sectionSF - The new `PetscSF`
410 
411   Level: advanced
412 
413   Notes:
414   Either rootSection or remoteOffsets can be specified
415 
416   Fortran Notes:
417   In Fortran, use PetscSFCreateSectionSFF90()
418 
419 .seealso: `PetscSF`, `PetscSFCreate()`
420 @*/
421 PetscErrorCode PetscSFCreateSectionSF(PetscSF sf, PetscSection rootSection, PetscInt remoteOffsets[], PetscSection leafSection, PetscSF *sectionSF)
422 {
423   MPI_Comm           comm;
424   const PetscInt    *localPoints;
425   const PetscSFNode *remotePoints;
426   PetscInt           lpStart, lpEnd;
427   PetscInt           numRoots, numSectionRoots, numPoints, numIndices = 0;
428   PetscInt          *localIndices;
429   PetscSFNode       *remoteIndices;
430   PetscInt           i, ind;
431 
432   PetscFunctionBegin;
433   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 1);
434   PetscAssertPointer(rootSection, 2);
435   /* Cannot check PetscAssertPointer(remoteOffsets,3) because it can be NULL if sf does not reference any points in leafSection */
436   PetscAssertPointer(leafSection, 4);
437   PetscAssertPointer(sectionSF, 5);
438   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
439   PetscCall(PetscSFCreate(comm, sectionSF));
440   PetscCall(PetscSectionGetChart(leafSection, &lpStart, &lpEnd));
441   PetscCall(PetscSectionGetStorageSize(rootSection, &numSectionRoots));
442   PetscCall(PetscSFGetGraph(sf, &numRoots, &numPoints, &localPoints, &remotePoints));
443   if (numRoots < 0) PetscFunctionReturn(PETSC_SUCCESS);
444   PetscCall(PetscLogEventBegin(PETSCSF_SectSF, sf, 0, 0, 0));
445   for (i = 0; i < numPoints; ++i) {
446     PetscInt localPoint = localPoints ? localPoints[i] : i;
447     PetscInt dof;
448 
449     if ((localPoint >= lpStart) && (localPoint < lpEnd)) {
450       PetscCall(PetscSectionGetDof(leafSection, localPoint, &dof));
451       numIndices += dof < 0 ? 0 : dof;
452     }
453   }
454   PetscCall(PetscMalloc1(numIndices, &localIndices));
455   PetscCall(PetscMalloc1(numIndices, &remoteIndices));
456   /* Create new index graph */
457   for (i = 0, ind = 0; i < numPoints; ++i) {
458     PetscInt localPoint = localPoints ? localPoints[i] : i;
459     PetscInt rank       = remotePoints[i].rank;
460 
461     if ((localPoint >= lpStart) && (localPoint < lpEnd)) {
462       PetscInt remoteOffset = remoteOffsets[localPoint - lpStart];
463       PetscInt loff, dof, d;
464 
465       PetscCall(PetscSectionGetOffset(leafSection, localPoint, &loff));
466       PetscCall(PetscSectionGetDof(leafSection, localPoint, &dof));
467       for (d = 0; d < dof; ++d, ++ind) {
468         localIndices[ind]        = loff + d;
469         remoteIndices[ind].rank  = rank;
470         remoteIndices[ind].index = remoteOffset + d;
471       }
472     }
473   }
474   PetscCheck(numIndices == ind, comm, PETSC_ERR_PLIB, "Inconsistency in indices, %" PetscInt_FMT " should be %" PetscInt_FMT, ind, numIndices);
475   PetscCall(PetscSFSetGraph(*sectionSF, numSectionRoots, numIndices, localIndices, PETSC_OWN_POINTER, remoteIndices, PETSC_OWN_POINTER));
476   PetscCall(PetscSFSetUp(*sectionSF));
477   PetscCall(PetscLogEventEnd(PETSCSF_SectSF, sf, 0, 0, 0));
478   PetscFunctionReturn(PETSC_SUCCESS);
479 }
480 
481 /*@C
482   PetscSFCreateFromLayouts - Creates a parallel star forest mapping two `PetscLayout` objects
483 
484   Collective
485 
486   Input Parameters:
487 + rmap - `PetscLayout` defining the global root space
488 - lmap - `PetscLayout` defining the global leaf space
489 
490   Output Parameter:
491 . sf - The parallel star forest
492 
493   Level: intermediate
494 
495 .seealso: `PetscSF`, `PetscSFCreate()`, `PetscLayoutCreate()`, `PetscSFSetGraphLayout()`
496 @*/
497 PetscErrorCode PetscSFCreateFromLayouts(PetscLayout rmap, PetscLayout lmap, PetscSF *sf)
498 {
499   PetscInt     i, nroots, nleaves = 0;
500   PetscInt     rN, lst, len;
501   PetscMPIInt  owner = -1;
502   PetscSFNode *remote;
503   MPI_Comm     rcomm = rmap->comm;
504   MPI_Comm     lcomm = lmap->comm;
505   PetscMPIInt  flg;
506 
507   PetscFunctionBegin;
508   PetscAssertPointer(sf, 3);
509   PetscCheck(rmap->setupcalled, rcomm, PETSC_ERR_ARG_WRONGSTATE, "Root layout not setup");
510   PetscCheck(lmap->setupcalled, lcomm, PETSC_ERR_ARG_WRONGSTATE, "Leaf layout not setup");
511   PetscCallMPI(MPI_Comm_compare(rcomm, lcomm, &flg));
512   PetscCheck(flg == MPI_CONGRUENT || flg == MPI_IDENT, rcomm, PETSC_ERR_SUP, "cannot map two layouts with non-matching communicators");
513   PetscCall(PetscSFCreate(rcomm, sf));
514   PetscCall(PetscLayoutGetLocalSize(rmap, &nroots));
515   PetscCall(PetscLayoutGetSize(rmap, &rN));
516   PetscCall(PetscLayoutGetRange(lmap, &lst, &len));
517   PetscCall(PetscMalloc1(len - lst, &remote));
518   for (i = lst; i < len && i < rN; i++) {
519     if (owner < -1 || i >= rmap->range[owner + 1]) PetscCall(PetscLayoutFindOwner(rmap, i, &owner));
520     remote[nleaves].rank  = owner;
521     remote[nleaves].index = i - rmap->range[owner];
522     nleaves++;
523   }
524   PetscCall(PetscSFSetGraph(*sf, nroots, nleaves, NULL, PETSC_OWN_POINTER, remote, PETSC_COPY_VALUES));
525   PetscCall(PetscFree(remote));
526   PetscFunctionReturn(PETSC_SUCCESS);
527 }
528 
529 /* TODO: handle nooffprocentries like MatZeroRowsMapLocal_Private, since this code is the same */
530 PetscErrorCode PetscLayoutMapLocal(PetscLayout map, PetscInt N, const PetscInt idxs[], PetscInt *on, PetscInt **oidxs, PetscInt **ogidxs)
531 {
532   PetscInt    *owners = map->range;
533   PetscInt     n      = map->n;
534   PetscSF      sf;
535   PetscInt    *lidxs, *work = NULL;
536   PetscSFNode *ridxs;
537   PetscMPIInt  rank, p = 0;
538   PetscInt     r, len  = 0;
539 
540   PetscFunctionBegin;
541   if (on) *on = 0; /* squelch -Wmaybe-uninitialized */
542   /* Create SF where leaves are input idxs and roots are owned idxs */
543   PetscCallMPI(MPI_Comm_rank(map->comm, &rank));
544   PetscCall(PetscMalloc1(n, &lidxs));
545   for (r = 0; r < n; ++r) lidxs[r] = -1;
546   PetscCall(PetscMalloc1(N, &ridxs));
547   for (r = 0; r < N; ++r) {
548     const PetscInt idx = idxs[r];
549     PetscCheck(idx >= 0 && idx < map->N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Index %" PetscInt_FMT " out of range [0,%" PetscInt_FMT ")", idx, map->N);
550     if (idx < owners[p] || owners[p + 1] <= idx) { /* short-circuit the search if the last p owns this idx too */
551       PetscCall(PetscLayoutFindOwner(map, idx, &p));
552     }
553     ridxs[r].rank  = p;
554     ridxs[r].index = idxs[r] - owners[p];
555   }
556   PetscCall(PetscSFCreate(map->comm, &sf));
557   PetscCall(PetscSFSetGraph(sf, n, N, NULL, PETSC_OWN_POINTER, ridxs, PETSC_OWN_POINTER));
558   PetscCall(PetscSFReduceBegin(sf, MPIU_INT, (PetscInt *)idxs, lidxs, MPI_LOR));
559   PetscCall(PetscSFReduceEnd(sf, MPIU_INT, (PetscInt *)idxs, lidxs, MPI_LOR));
560   if (ogidxs) { /* communicate global idxs */
561     PetscInt cum = 0, start, *work2;
562 
563     PetscCall(PetscMalloc1(n, &work));
564     PetscCall(PetscCalloc1(N, &work2));
565     for (r = 0; r < N; ++r)
566       if (idxs[r] >= 0) cum++;
567     PetscCallMPI(MPI_Scan(&cum, &start, 1, MPIU_INT, MPI_SUM, map->comm));
568     start -= cum;
569     cum = 0;
570     for (r = 0; r < N; ++r)
571       if (idxs[r] >= 0) work2[r] = start + cum++;
572     PetscCall(PetscSFReduceBegin(sf, MPIU_INT, work2, work, MPI_REPLACE));
573     PetscCall(PetscSFReduceEnd(sf, MPIU_INT, work2, work, MPI_REPLACE));
574     PetscCall(PetscFree(work2));
575   }
576   PetscCall(PetscSFDestroy(&sf));
577   /* Compress and put in indices */
578   for (r = 0; r < n; ++r)
579     if (lidxs[r] >= 0) {
580       if (work) work[len] = work[r];
581       lidxs[len++] = r;
582     }
583   if (on) *on = len;
584   if (oidxs) *oidxs = lidxs;
585   if (ogidxs) *ogidxs = work;
586   PetscFunctionReturn(PETSC_SUCCESS);
587 }
588 
589 /*@
590   PetscSFCreateByMatchingIndices - Create `PetscSF` by matching root and leaf indices
591 
592   Collective
593 
594   Input Parameters:
595 + layout           - `PetscLayout` defining the global index space and the rank that brokers each index
596 . numRootIndices   - size of rootIndices
597 . rootIndices      - `PetscInt` array of global indices of which this process requests ownership
598 . rootLocalIndices - root local index permutation (NULL if no permutation)
599 . rootLocalOffset  - offset to be added to root local indices
600 . numLeafIndices   - size of leafIndices
601 . leafIndices      - `PetscInt` array of global indices with which this process requires data associated
602 . leafLocalIndices - leaf local index permutation (NULL if no permutation)
603 - leafLocalOffset  - offset to be added to leaf local indices
604 
605   Output Parameters:
606 + sfA - star forest representing the communication pattern from the layout space to the leaf space (NULL if not needed)
607 - sf  - star forest representing the communication pattern from the root space to the leaf space
608 
609   Level: advanced
610 
611   Example 1:
612 .vb
613   rank             : 0            1            2
614   rootIndices      : [1 0 2]      [3]          [3]
615   rootLocalOffset  : 100          200          300
616   layout           : [0 1]        [2]          [3]
617   leafIndices      : [0]          [2]          [0 3]
618   leafLocalOffset  : 400          500          600
619 
620 would build the following PetscSF
621 
622   [0] 400 <- (0,101)
623   [1] 500 <- (0,102)
624   [2] 600 <- (0,101)
625   [2] 601 <- (2,300)
626 .ve
627 
628   Example 2:
629 .vb
630   rank             : 0               1               2
631   rootIndices      : [1 0 2]         [3]             [3]
632   rootLocalOffset  : 100             200             300
633   layout           : [0 1]           [2]             [3]
634   leafIndices      : rootIndices     rootIndices     rootIndices
635   leafLocalOffset  : rootLocalOffset rootLocalOffset rootLocalOffset
636 
637 would build the following PetscSF
638 
639   [1] 200 <- (2,300)
640 .ve
641 
642   Example 3:
643 .vb
644   No process requests ownership of global index 1, but no process needs it.
645 
646   rank             : 0            1            2
647   numRootIndices   : 2            1            1
648   rootIndices      : [0 2]        [3]          [3]
649   rootLocalOffset  : 100          200          300
650   layout           : [0 1]        [2]          [3]
651   numLeafIndices   : 1            1            2
652   leafIndices      : [0]          [2]          [0 3]
653   leafLocalOffset  : 400          500          600
654 
655 would build the following PetscSF
656 
657   [0] 400 <- (0,100)
658   [1] 500 <- (0,101)
659   [2] 600 <- (0,100)
660   [2] 601 <- (2,300)
661 .ve
662 
663   Notes:
664   The layout parameter represents any partitioning of [0, N), where N is the total number of global indices, and its
665   local size can be set to `PETSC_DECIDE`.
666 
667   If a global index x lies in the partition owned by process i, each process whose rootIndices contains x requests
668   ownership of x and sends its own rank and the local index of x to process i.
669   If multiple processes request ownership of x, the one with the highest rank is to own x.
670   Process i then broadcasts the ownership information, so that each process whose leafIndices contains x knows the
671   ownership information of x.
672   The output sf is constructed by associating each leaf point to a root point in this way.
673 
674   Suppose there is point data ordered according to the global indices and partitioned according to the given layout.
675   The optional output `PetscSF` object sfA can be used to push such data to leaf points.
676 
677   All indices in rootIndices and leafIndices must lie in the layout range. The union (over all processes) of rootIndices
678   must cover that of leafIndices, but need not cover the entire layout.
679 
680   If (leafIndices, leafLocalIndices, leafLocalOffset) == (rootIndices, rootLocalIndices, rootLocalOffset), the output
681   star forest is almost identity, so will only include non-trivial part of the map.
682 
683   Developer Notes:
684   Current approach of a process of the highest rank gaining the ownership may cause load imbalance; consider using
685   hash(rank, root_local_index) as the bid for the ownership determination.
686 
687 .seealso: `PetscSF`, `PetscSFCreate()`
688 @*/
689 PetscErrorCode PetscSFCreateByMatchingIndices(PetscLayout layout, PetscInt numRootIndices, const PetscInt *rootIndices, const PetscInt *rootLocalIndices, PetscInt rootLocalOffset, PetscInt numLeafIndices, const PetscInt *leafIndices, const PetscInt *leafLocalIndices, PetscInt leafLocalOffset, PetscSF *sfA, PetscSF *sf)
690 {
691   MPI_Comm     comm = layout->comm;
692   PetscMPIInt  size, rank;
693   PetscSF      sf1;
694   PetscSFNode *owners, *buffer, *iremote;
695   PetscInt    *ilocal, nleaves, N, n, i;
696 #if defined(PETSC_USE_DEBUG)
697   PetscInt N1;
698 #endif
699   PetscBool flag;
700 
701   PetscFunctionBegin;
702   if (rootIndices) PetscAssertPointer(rootIndices, 3);
703   if (rootLocalIndices) PetscAssertPointer(rootLocalIndices, 4);
704   if (leafIndices) PetscAssertPointer(leafIndices, 7);
705   if (leafLocalIndices) PetscAssertPointer(leafLocalIndices, 8);
706   if (sfA) PetscAssertPointer(sfA, 10);
707   PetscAssertPointer(sf, 11);
708   PetscCheck(numRootIndices >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "numRootIndices (%" PetscInt_FMT ") must be non-negative", numRootIndices);
709   PetscCheck(numLeafIndices >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "numLeafIndices (%" PetscInt_FMT ") must be non-negative", numLeafIndices);
710   PetscCallMPI(MPI_Comm_size(comm, &size));
711   PetscCallMPI(MPI_Comm_rank(comm, &rank));
712   PetscCall(PetscLayoutSetUp(layout));
713   PetscCall(PetscLayoutGetSize(layout, &N));
714   PetscCall(PetscLayoutGetLocalSize(layout, &n));
715   flag = (PetscBool)(leafIndices == rootIndices);
716   PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &flag, 1, MPIU_BOOL, MPI_LAND, comm));
717   PetscCheck(!flag || numLeafIndices == numRootIndices, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "leafIndices == rootIndices, but numLeafIndices (%" PetscInt_FMT ") != numRootIndices(%" PetscInt_FMT ")", numLeafIndices, numRootIndices);
718 #if defined(PETSC_USE_DEBUG)
719   N1 = PETSC_MIN_INT;
720   for (i = 0; i < numRootIndices; i++)
721     if (rootIndices[i] > N1) N1 = rootIndices[i];
722   PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &N1, 1, MPIU_INT, MPI_MAX, comm));
723   PetscCheck(N1 < N, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Max. root index (%" PetscInt_FMT ") out of layout range [0,%" PetscInt_FMT ")", N1, N);
724   if (!flag) {
725     N1 = PETSC_MIN_INT;
726     for (i = 0; i < numLeafIndices; i++)
727       if (leafIndices[i] > N1) N1 = leafIndices[i];
728     PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &N1, 1, MPIU_INT, MPI_MAX, comm));
729     PetscCheck(N1 < N, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Max. leaf index (%" PetscInt_FMT ") out of layout range [0,%" PetscInt_FMT ")", N1, N);
730   }
731 #endif
732   /* Reduce: owners -> buffer */
733   PetscCall(PetscMalloc1(n, &buffer));
734   PetscCall(PetscSFCreate(comm, &sf1));
735   PetscCall(PetscSFSetFromOptions(sf1));
736   PetscCall(PetscSFSetGraphLayout(sf1, layout, numRootIndices, NULL, PETSC_OWN_POINTER, rootIndices));
737   PetscCall(PetscMalloc1(numRootIndices, &owners));
738   for (i = 0; i < numRootIndices; ++i) {
739     owners[i].rank  = rank;
740     owners[i].index = rootLocalOffset + (rootLocalIndices ? rootLocalIndices[i] : i);
741   }
742   for (i = 0; i < n; ++i) {
743     buffer[i].index = -1;
744     buffer[i].rank  = -1;
745   }
746   PetscCall(PetscSFReduceBegin(sf1, MPIU_2INT, owners, buffer, MPI_MAXLOC));
747   PetscCall(PetscSFReduceEnd(sf1, MPIU_2INT, owners, buffer, MPI_MAXLOC));
748   /* Bcast: buffer -> owners */
749   if (!flag) {
750     /* leafIndices is different from rootIndices */
751     PetscCall(PetscFree(owners));
752     PetscCall(PetscSFSetGraphLayout(sf1, layout, numLeafIndices, NULL, PETSC_OWN_POINTER, leafIndices));
753     PetscCall(PetscMalloc1(numLeafIndices, &owners));
754   }
755   PetscCall(PetscSFBcastBegin(sf1, MPIU_2INT, buffer, owners, MPI_REPLACE));
756   PetscCall(PetscSFBcastEnd(sf1, MPIU_2INT, buffer, owners, MPI_REPLACE));
757   for (i = 0; i < numLeafIndices; ++i) PetscCheck(owners[i].rank >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Global point %" PetscInt_FMT " was unclaimed", leafIndices[i]);
758   PetscCall(PetscFree(buffer));
759   if (sfA) {
760     *sfA = sf1;
761   } else PetscCall(PetscSFDestroy(&sf1));
762   /* Create sf */
763   if (flag && rootLocalIndices == leafLocalIndices && leafLocalOffset == rootLocalOffset) {
764     /* leaf space == root space */
765     for (i = 0, nleaves = 0; i < numLeafIndices; ++i)
766       if (owners[i].rank != rank) ++nleaves;
767     PetscCall(PetscMalloc1(nleaves, &ilocal));
768     PetscCall(PetscMalloc1(nleaves, &iremote));
769     for (i = 0, nleaves = 0; i < numLeafIndices; ++i) {
770       if (owners[i].rank != rank) {
771         ilocal[nleaves]        = leafLocalOffset + i;
772         iremote[nleaves].rank  = owners[i].rank;
773         iremote[nleaves].index = owners[i].index;
774         ++nleaves;
775       }
776     }
777     PetscCall(PetscFree(owners));
778   } else {
779     nleaves = numLeafIndices;
780     PetscCall(PetscMalloc1(nleaves, &ilocal));
781     for (i = 0; i < nleaves; ++i) ilocal[i] = leafLocalOffset + (leafLocalIndices ? leafLocalIndices[i] : i);
782     iremote = owners;
783   }
784   PetscCall(PetscSFCreate(comm, sf));
785   PetscCall(PetscSFSetFromOptions(*sf));
786   PetscCall(PetscSFSetGraph(*sf, rootLocalOffset + numRootIndices, nleaves, ilocal, PETSC_OWN_POINTER, iremote, PETSC_OWN_POINTER));
787   PetscFunctionReturn(PETSC_SUCCESS);
788 }
789 
790 /*@
791   PetscSFMerge - append/merge indices of `sfb` into `sfa`, with preference for `sfb`
792 
793   Collective
794 
795   Input Parameters:
796 + sfa - default `PetscSF`
797 - sfb - additional edges to add/replace edges in sfa
798 
799   Output Parameter:
800 . merged - new `PetscSF` with combined edges
801 
802   Level: intermediate
803 
804 .seealso: `PetscSFCompose()`
805 @*/
806 PetscErrorCode PetscSFMerge(PetscSF sfa, PetscSF sfb, PetscSF *merged)
807 {
808   PetscInt maxleaf;
809 
810   PetscFunctionBegin;
811   PetscValidHeaderSpecific(sfa, PETSCSF_CLASSID, 1);
812   PetscValidHeaderSpecific(sfb, PETSCSF_CLASSID, 2);
813   PetscCheckSameComm(sfa, 1, sfb, 2);
814   PetscAssertPointer(merged, 3);
815   {
816     PetscInt aleaf, bleaf;
817     PetscCall(PetscSFGetLeafRange(sfa, NULL, &aleaf));
818     PetscCall(PetscSFGetLeafRange(sfb, NULL, &bleaf));
819     maxleaf = PetscMax(aleaf, bleaf) + 1; // One more than the last index
820   }
821   PetscInt          *clocal, aroots, aleaves, broots, bleaves;
822   PetscSFNode       *cremote;
823   const PetscInt    *alocal, *blocal;
824   const PetscSFNode *aremote, *bremote;
825   PetscCall(PetscMalloc2(maxleaf, &clocal, maxleaf, &cremote));
826   for (PetscInt i = 0; i < maxleaf; i++) clocal[i] = -1;
827   PetscCall(PetscSFGetGraph(sfa, &aroots, &aleaves, &alocal, &aremote));
828   PetscCall(PetscSFGetGraph(sfb, &broots, &bleaves, &blocal, &bremote));
829   PetscCheck(aroots == broots, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Both sfa and sfb must have the same root space");
830   for (PetscInt i = 0; i < aleaves; i++) {
831     PetscInt a = alocal ? alocal[i] : i;
832     clocal[a]  = a;
833     cremote[a] = aremote[i];
834   }
835   for (PetscInt i = 0; i < bleaves; i++) {
836     PetscInt b = blocal ? blocal[i] : i;
837     clocal[b]  = b;
838     cremote[b] = bremote[i];
839   }
840   PetscInt nleaves = 0;
841   for (PetscInt i = 0; i < maxleaf; i++) {
842     if (clocal[i] < 0) continue;
843     clocal[nleaves]  = clocal[i];
844     cremote[nleaves] = cremote[i];
845     nleaves++;
846   }
847   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)sfa), merged));
848   PetscCall(PetscSFSetGraph(*merged, aroots, nleaves, clocal, PETSC_COPY_VALUES, cremote, PETSC_COPY_VALUES));
849   PetscCall(PetscFree2(clocal, cremote));
850   PetscFunctionReturn(PETSC_SUCCESS);
851 }
852