xref: /petsc/src/sys/objects/subcomm.c (revision 0baf8eba40dbc839082666f9f7396a225d6f663c)
1 /*
2      Provides utility routines for split MPI communicator.
3 */
4 #include <petscsys.h> /*I   "petscsys.h"    I*/
5 #include <petscviewer.h>
6 
7 const char *const PetscSubcommTypes[] = {"GENERAL", "CONTIGUOUS", "INTERLACED", "PetscSubcommType", "PETSC_SUBCOMM_", NULL};
8 
9 static PetscErrorCode PetscSubcommCreate_contiguous(PetscSubcomm);
10 static PetscErrorCode PetscSubcommCreate_interlaced(PetscSubcomm);
11 
12 /*@
13   PetscSubcommSetFromOptions - Allows setting options for a `PetscSubcomm`
14 
15   Collective
16 
17   Input Parameter:
18 . psubcomm - `PetscSubcomm` context
19 
20   Level: beginner
21 
22 .seealso: `PetscSubcomm`, `PetscSubcommCreate()`
23 @*/
24 PetscErrorCode PetscSubcommSetFromOptions(PetscSubcomm psubcomm)
25 {
26   PetscSubcommType type;
27   PetscBool        flg;
28 
29   PetscFunctionBegin;
30   PetscCheck(psubcomm, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Must call PetscSubcommCreate first");
31 
32   PetscOptionsBegin(psubcomm->parent, psubcomm->subcommprefix, "Options for PetscSubcomm", NULL);
33   PetscCall(PetscOptionsEnum("-psubcomm_type", NULL, NULL, PetscSubcommTypes, (PetscEnum)psubcomm->type, (PetscEnum *)&type, &flg));
34   if (flg && psubcomm->type != type) {
35     /* free old structures */
36     PetscCall(PetscCommDestroy(&(psubcomm)->dupparent));
37     PetscCall(PetscCommDestroy(&(psubcomm)->child));
38     PetscCall(PetscFree((psubcomm)->subsize));
39     switch (type) {
40     case PETSC_SUBCOMM_GENERAL:
41       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Runtime option PETSC_SUBCOMM_GENERAL is not supported, use PetscSubcommSetTypeGeneral()");
42     case PETSC_SUBCOMM_CONTIGUOUS:
43       PetscCall(PetscSubcommCreate_contiguous(psubcomm));
44       break;
45     case PETSC_SUBCOMM_INTERLACED:
46       PetscCall(PetscSubcommCreate_interlaced(psubcomm));
47       break;
48     default:
49       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "PetscSubcommType %s is not supported yet", PetscSubcommTypes[type]);
50     }
51   }
52 
53   PetscCall(PetscOptionsName("-psubcomm_view", "Triggers display of PetscSubcomm context", "PetscSubcommView", &flg));
54   if (flg) PetscCall(PetscSubcommView(psubcomm, PETSC_VIEWER_STDOUT_(psubcomm->parent)));
55   PetscOptionsEnd();
56   PetscFunctionReturn(PETSC_SUCCESS);
57 }
58 
59 /*@
60   PetscSubcommSetOptionsPrefix - Sets the prefix used for searching for options in the options database for this object
61 
62   Logically Collective
63 
64   Level: intermediate
65 
66   Input Parameters:
67 + psubcomm - `PetscSubcomm` context
68 - pre      - the prefix to prepend all `PetscSubcomm` item names with.
69 
70 .seealso: `PetscSubcomm`, `PetscSubcommCreate()`
71 @*/
72 PetscErrorCode PetscSubcommSetOptionsPrefix(PetscSubcomm psubcomm, const char pre[])
73 {
74   PetscFunctionBegin;
75   if (!pre) {
76     PetscCall(PetscFree(psubcomm->subcommprefix));
77   } else {
78     PetscCheck(pre[0] != '-', PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Options prefix should not begin with a hyphen");
79     PetscCall(PetscFree(psubcomm->subcommprefix));
80     PetscCall(PetscStrallocpy(pre, &psubcomm->subcommprefix));
81   }
82   PetscFunctionReturn(PETSC_SUCCESS);
83 }
84 
85 /*@
86   PetscSubcommView - Views a `PetscSubcomm`
87 
88   Collective
89 
90   Input Parameters:
91 + psubcomm - `PetscSubcomm` context
92 - viewer   - `PetscViewer` to display the information
93 
94   Level: beginner
95 
96 .seealso: `PetscSubcomm`, `PetscSubcommCreate()`, `PetscViewer`
97 @*/
98 PetscErrorCode PetscSubcommView(PetscSubcomm psubcomm, PetscViewer viewer)
99 {
100   PetscBool         iascii;
101   PetscViewerFormat format;
102 
103   PetscFunctionBegin;
104   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
105   if (iascii) {
106     PetscCall(PetscViewerGetFormat(viewer, &format));
107     if (format == PETSC_VIEWER_DEFAULT) {
108       MPI_Comm    comm = psubcomm->parent;
109       PetscMPIInt rank, size, subsize, subrank, duprank;
110 
111       PetscCallMPI(MPI_Comm_size(comm, &size));
112       PetscCall(PetscViewerASCIIPrintf(viewer, "PetscSubcomm type %s with total %d MPI processes:\n", PetscSubcommTypes[psubcomm->type], size));
113       PetscCallMPI(MPI_Comm_rank(comm, &rank));
114       PetscCallMPI(MPI_Comm_size(psubcomm->child, &subsize));
115       PetscCallMPI(MPI_Comm_rank(psubcomm->child, &subrank));
116       PetscCallMPI(MPI_Comm_rank(psubcomm->dupparent, &duprank));
117       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
118       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  [%d], color %d, sub-size %d, sub-rank %d, duprank %d\n", rank, psubcomm->color, subsize, subrank, duprank));
119       PetscCall(PetscViewerFlush(viewer));
120       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
121     }
122   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Not supported yet");
123   PetscFunctionReturn(PETSC_SUCCESS);
124 }
125 
126 /*@
127   PetscSubcommSetNumber - Set total number of subcommunicators desired in the given `PetscSubcomm`
128 
129   Collective
130 
131   Input Parameters:
132 + psubcomm - `PetscSubcomm` context
133 - nsubcomm - the total number of subcommunicators in psubcomm
134 
135   Level: advanced
136 
137 .seealso: `PetscSubcomm`, `PetscSubcommCreate()`, `PetscSubcommDestroy()`, `PetscSubcommSetType()`, `PetscSubcommSetTypeGeneral()`
138 @*/
139 PetscErrorCode PetscSubcommSetNumber(PetscSubcomm psubcomm, PetscInt nsubcomm)
140 {
141   MPI_Comm    comm = psubcomm->parent;
142   PetscMPIInt msub, size;
143 
144   PetscFunctionBegin;
145   PetscCheck(psubcomm, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "PetscSubcomm is not created. Call PetscSubcommCreate() first");
146   PetscCallMPI(MPI_Comm_size(comm, &size));
147   PetscCall(PetscMPIIntCast(nsubcomm, &msub));
148   PetscCheck(msub >= 1 && msub <= size, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Num of subcommunicators %d cannot be < 1 or > input comm size %d", msub, size);
149 
150   psubcomm->n = msub;
151   PetscFunctionReturn(PETSC_SUCCESS);
152 }
153 
154 /*@
155   PetscSubcommSetType - Set the way the original MPI communicator is divided up in the `PetscSubcomm`
156 
157   Collective
158 
159   Input Parameters:
160 + psubcomm    - `PetscSubcomm` context
161 - subcommtype - `PetscSubcommType` `PETSC_SUBCOMM_CONTIGUOUS` or `PETSC_SUBCOMM_INTERLACED`
162 
163   Level: advanced
164 
165 .seealso: `PetscSubcommType`, `PETSC_SUBCOMM_CONTIGUOUS`, `PETSC_SUBCOMM_INTERLACED`,
166           `PetscSubcommCreate()`, `PetscSubcommDestroy()`, `PetscSubcommSetNumber()`, `PetscSubcommSetTypeGeneral()`
167 @*/
168 PetscErrorCode PetscSubcommSetType(PetscSubcomm psubcomm, PetscSubcommType subcommtype)
169 {
170   PetscFunctionBegin;
171   PetscCheck(psubcomm, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "PetscSubcomm is not created. Call PetscSubcommCreate()");
172   PetscCheck(psubcomm->n >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "number of subcommunicators %d is incorrect. Call PetscSubcommSetNumber()", psubcomm->n);
173 
174   if (subcommtype == PETSC_SUBCOMM_CONTIGUOUS) {
175     PetscCall(PetscSubcommCreate_contiguous(psubcomm));
176   } else if (subcommtype == PETSC_SUBCOMM_INTERLACED) {
177     PetscCall(PetscSubcommCreate_interlaced(psubcomm));
178   } else SETERRQ(psubcomm->parent, PETSC_ERR_SUP, "PetscSubcommType %s is not supported yet", PetscSubcommTypes[subcommtype]);
179   PetscFunctionReturn(PETSC_SUCCESS);
180 }
181 
182 /*@
183   PetscSubcommSetTypeGeneral - Divides up a communicator based on a specific user's specification
184 
185   Collective
186 
187   Input Parameters:
188 + psubcomm - `PetscSubcomm` context
189 . color    - control of subset assignment (nonnegative integer). Processes with the same color are in the same subcommunicator.
190 - subrank  - rank in the subcommunicator
191 
192   Level: advanced
193 
194 .seealso: `PetscSubcommType`, `PETSC_SUBCOMM_CONTIGUOUS`, `PETSC_SUBCOMM_INTERLACED`, `PetscSubcommCreate()`, `PetscSubcommDestroy()`, `PetscSubcommSetNumber()`, `PetscSubcommSetType()`
195 @*/
196 PetscErrorCode PetscSubcommSetTypeGeneral(PetscSubcomm psubcomm, PetscMPIInt color, PetscMPIInt subrank)
197 {
198   MPI_Comm    subcomm = 0, dupcomm = 0, comm = psubcomm->parent;
199   PetscMPIInt size, icolor, duprank, *recvbuf, sendbuf[3], mysubsize, rank, *subsize;
200   PetscMPIInt i, nsubcomm = psubcomm->n;
201 
202   PetscFunctionBegin;
203   PetscCheck(psubcomm, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "PetscSubcomm is not created. Call PetscSubcommCreate()");
204   PetscCheck(nsubcomm >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "number of subcommunicators %d is incorrect. Call PetscSubcommSetNumber()", nsubcomm);
205 
206   PetscCallMPI(MPI_Comm_split(comm, color, subrank, &subcomm));
207 
208   /* create dupcomm with same size as comm, but its rank, duprank, maps subcomm's contiguously into dupcomm */
209   /* TODO: this can be done in an ostensibly scalale way (i.e., without allocating an array of size 'size') as is done in PetscObjectsCreateGlobalOrdering(). */
210   PetscCallMPI(MPI_Comm_size(comm, &size));
211   PetscCall(PetscMalloc1(2 * size, &recvbuf));
212 
213   PetscCallMPI(MPI_Comm_rank(comm, &rank));
214   PetscCallMPI(MPI_Comm_size(subcomm, &mysubsize));
215 
216   sendbuf[0] = color;
217   sendbuf[1] = mysubsize;
218   PetscCallMPI(MPI_Allgather(sendbuf, 2, MPI_INT, recvbuf, 2, MPI_INT, comm));
219 
220   PetscCall(PetscCalloc1(nsubcomm, &subsize));
221   for (i = 0; i < 2 * size; i += 2) subsize[recvbuf[i]] = recvbuf[i + 1];
222   PetscCall(PetscFree(recvbuf));
223 
224   duprank = 0;
225   for (icolor = 0; icolor < nsubcomm; icolor++) {
226     if (icolor != color) { /* not color of this process */
227       duprank += subsize[icolor];
228     } else {
229       duprank += subrank;
230       break;
231     }
232   }
233   PetscCallMPI(MPI_Comm_split(comm, 0, duprank, &dupcomm));
234 
235   PetscCall(PetscCommDuplicate(dupcomm, &psubcomm->dupparent, NULL));
236   PetscCall(PetscCommDuplicate(subcomm, &psubcomm->child, NULL));
237   PetscCallMPI(MPI_Comm_free(&dupcomm));
238   PetscCallMPI(MPI_Comm_free(&subcomm));
239 
240   psubcomm->color   = color;
241   psubcomm->subsize = subsize;
242   psubcomm->type    = PETSC_SUBCOMM_GENERAL;
243   PetscFunctionReturn(PETSC_SUCCESS);
244 }
245 
246 /*@
247   PetscSubcommDestroy - Destroys a `PetscSubcomm` object
248 
249   Collective
250 
251   Input Parameter:
252 . psubcomm - the `PetscSubcomm` context
253 
254   Level: advanced
255 
256 .seealso: `PetscSubcommCreate()`, `PetscSubcommSetType()`
257 @*/
258 PetscErrorCode PetscSubcommDestroy(PetscSubcomm *psubcomm)
259 {
260   PetscFunctionBegin;
261   if (!*psubcomm) PetscFunctionReturn(PETSC_SUCCESS);
262   PetscCall(PetscCommDestroy(&(*psubcomm)->dupparent));
263   PetscCall(PetscCommDestroy(&(*psubcomm)->child));
264   PetscCall(PetscFree((*psubcomm)->subsize));
265   if ((*psubcomm)->subcommprefix) PetscCall(PetscFree((*psubcomm)->subcommprefix));
266   PetscCall(PetscFree(*psubcomm));
267   PetscFunctionReturn(PETSC_SUCCESS);
268 }
269 
270 /*@
271   PetscSubcommCreate - Create a `PetscSubcomm` context. This object is used to manage the division of a `MPI_Comm` into subcommunicators
272 
273   Collective
274 
275   Input Parameter:
276 . comm - MPI communicator
277 
278   Output Parameter:
279 . psubcomm - location to store the `PetscSubcomm` context
280 
281   Level: advanced
282 
283 .seealso: `PetscSubcomm`, `PetscSubcommDestroy()`, `PetscSubcommSetTypeGeneral()`, `PetscSubcommSetFromOptions()`, `PetscSubcommSetType()`,
284           `PetscSubcommSetNumber()`
285 @*/
286 PetscErrorCode PetscSubcommCreate(MPI_Comm comm, PetscSubcomm *psubcomm)
287 {
288   PetscMPIInt rank, size;
289 
290   PetscFunctionBegin;
291   PetscCall(PetscNew(psubcomm));
292 
293   /* set defaults */
294   PetscCallMPI(MPI_Comm_rank(comm, &rank));
295   PetscCallMPI(MPI_Comm_size(comm, &size));
296 
297   (*psubcomm)->parent    = comm;
298   (*psubcomm)->dupparent = comm;
299   (*psubcomm)->child     = PETSC_COMM_SELF;
300   (*psubcomm)->n         = size;
301   (*psubcomm)->color     = rank;
302   (*psubcomm)->subsize   = NULL;
303   (*psubcomm)->type      = PETSC_SUBCOMM_INTERLACED;
304   PetscFunctionReturn(PETSC_SUCCESS);
305 }
306 
307 /*@C
308   PetscSubcommGetParent - Gets the communicator that was used to create the `PetscSubcomm`
309 
310   Collective
311 
312   Input Parameter:
313 . scomm - the `PetscSubcomm`
314 
315   Output Parameter:
316 . pcomm - location to store the parent communicator
317 
318   Level: intermediate
319 
320 .seealso: `PetscSubcommDestroy()`, `PetscSubcommSetTypeGeneral()`, `PetscSubcommSetFromOptions()`, `PetscSubcommSetType()`,
321           `PetscSubcommSetNumber()`, `PetscSubcommGetChild()`, `PetscSubcommContiguousParent()`
322 @*/
323 PetscErrorCode PetscSubcommGetParent(PetscSubcomm scomm, MPI_Comm *pcomm)
324 {
325   *pcomm = PetscSubcommParent(scomm);
326   return PETSC_SUCCESS;
327 }
328 
329 /*@C
330   PetscSubcommGetContiguousParent - Gets a communicator that is a duplicate of the parent but has the ranks
331   reordered by the order they are in the children
332 
333   Collective
334 
335   Input Parameter:
336 . scomm - the `PetscSubcomm`
337 
338   Output Parameter:
339 . pcomm - location to store the parent communicator
340 
341   Level: intermediate
342 
343 .seealso: `PetscSubcommDestroy()`, `PetscSubcommSetTypeGeneral()`, `PetscSubcommSetFromOptions()`, `PetscSubcommSetType()`,
344           `PetscSubcommSetNumber()`, `PetscSubcommGetChild()`, `PetscSubcommContiguousParent()`
345 @*/
346 PetscErrorCode PetscSubcommGetContiguousParent(PetscSubcomm scomm, MPI_Comm *pcomm)
347 {
348   *pcomm = PetscSubcommContiguousParent(scomm);
349   return PETSC_SUCCESS;
350 }
351 
352 /*@C
353   PetscSubcommGetChild - Gets the communicator created by the `PetscSubcomm`. This is part of one of the subcommunicators created by the `PetscSubcomm`
354 
355   Collective
356 
357   Input Parameter:
358 . scomm - the `PetscSubcomm`
359 
360   Output Parameter:
361 . ccomm - location to store the child communicator
362 
363   Level: intermediate
364 
365 .seealso: `PetscSubcommDestroy()`, `PetscSubcommSetTypeGeneral()`, `PetscSubcommSetFromOptions()`, `PetscSubcommSetType()`,
366           `PetscSubcommSetNumber()`, `PetscSubcommGetParent()`, `PetscSubcommContiguousParent()`
367 @*/
368 PetscErrorCode PetscSubcommGetChild(PetscSubcomm scomm, MPI_Comm *ccomm)
369 {
370   *ccomm = PetscSubcommChild(scomm);
371   return PETSC_SUCCESS;
372 }
373 
374 static PetscErrorCode PetscSubcommCreate_contiguous(PetscSubcomm psubcomm)
375 {
376   PetscMPIInt rank, size, *subsize, duprank = -1, subrank = -1;
377   PetscMPIInt np_subcomm, nleftover, i, color = -1, rankstart, nsubcomm = psubcomm->n;
378   MPI_Comm    subcomm = 0, dupcomm = 0, comm = psubcomm->parent;
379 
380   PetscFunctionBegin;
381   PetscCallMPI(MPI_Comm_rank(comm, &rank));
382   PetscCallMPI(MPI_Comm_size(comm, &size));
383 
384   /* get size of each subcommunicator */
385   PetscCall(PetscMalloc1(1 + nsubcomm, &subsize));
386 
387   np_subcomm = size / nsubcomm;
388   nleftover  = size - nsubcomm * np_subcomm;
389   for (i = 0; i < nsubcomm; i++) {
390     subsize[i] = np_subcomm;
391     if (i < nleftover) subsize[i]++;
392   }
393 
394   /* get color and subrank of this proc */
395   rankstart = 0;
396   for (i = 0; i < nsubcomm; i++) {
397     if (rank >= rankstart && rank < rankstart + subsize[i]) {
398       color   = i;
399       subrank = rank - rankstart;
400       duprank = rank;
401       break;
402     } else rankstart += subsize[i];
403   }
404 
405   PetscCallMPI(MPI_Comm_split(comm, color, subrank, &subcomm));
406 
407   /* create dupcomm with same size as comm, but its rank, duprank, maps subcomm's contiguously into dupcomm */
408   PetscCallMPI(MPI_Comm_split(comm, 0, duprank, &dupcomm));
409   PetscCall(PetscCommDuplicate(dupcomm, &psubcomm->dupparent, NULL));
410   PetscCall(PetscCommDuplicate(subcomm, &psubcomm->child, NULL));
411   PetscCallMPI(MPI_Comm_free(&dupcomm));
412   PetscCallMPI(MPI_Comm_free(&subcomm));
413 
414   psubcomm->color   = color;
415   psubcomm->subsize = subsize;
416   psubcomm->type    = PETSC_SUBCOMM_CONTIGUOUS;
417   PetscFunctionReturn(PETSC_SUCCESS);
418 }
419 
420 /*
421    Note:
422    In PCREDUNDANT, to avoid data scattering from subcomm back to original comm, we create subcommunicators
423    by iteratively taking a process into a subcommunicator.
424    Example: size=4, nsubcomm=(*psubcomm)->n=3
425      comm=(*psubcomm)->parent:
426       rank:     [0]  [1]  [2]  [3]
427       color:     0    1    2    0
428 
429      subcomm=(*psubcomm)->comm:
430       subrank:  [0]  [0]  [0]  [1]
431 
432      dupcomm=(*psubcomm)->dupparent:
433       duprank:  [0]  [2]  [3]  [1]
434 
435      Here, subcomm[color = 0] has subsize=2, owns process [0] and [3]
436            subcomm[color = 1] has subsize=1, owns process [1]
437            subcomm[color = 2] has subsize=1, owns process [2]
438            dupcomm has same number of processes as comm, and its duprank maps
439            processes in subcomm contiguously into a 1d array:
440             duprank: [0] [1]      [2]         [3]
441             rank:    [0] [3]      [1]         [2]
442                     subcomm[0] subcomm[1]  subcomm[2]
443 */
444 
445 static PetscErrorCode PetscSubcommCreate_interlaced(PetscSubcomm psubcomm)
446 {
447   PetscMPIInt rank, size, *subsize, duprank, subrank;
448   PetscMPIInt np_subcomm, nleftover, i, j, color, nsubcomm = psubcomm->n;
449   MPI_Comm    subcomm = 0, dupcomm = 0, comm = psubcomm->parent;
450 
451   PetscFunctionBegin;
452   PetscCallMPI(MPI_Comm_rank(comm, &rank));
453   PetscCallMPI(MPI_Comm_size(comm, &size));
454 
455   /* get size of each subcommunicator */
456   PetscCall(PetscMalloc1(1 + nsubcomm, &subsize));
457 
458   np_subcomm = size / nsubcomm;
459   nleftover  = size - nsubcomm * np_subcomm;
460   for (i = 0; i < nsubcomm; i++) {
461     subsize[i] = np_subcomm;
462     if (i < nleftover) subsize[i]++;
463   }
464 
465   /* find color for this proc */
466   color   = rank % nsubcomm;
467   subrank = rank / nsubcomm;
468 
469   PetscCallMPI(MPI_Comm_split(comm, color, subrank, &subcomm));
470 
471   j       = 0;
472   duprank = 0;
473   for (i = 0; i < nsubcomm; i++) {
474     if (j == color) {
475       duprank += subrank;
476       break;
477     }
478     duprank += subsize[i];
479     j++;
480   }
481 
482   /* create dupcomm with same size as comm, but its rank, duprank, maps subcomm's contiguously into dupcomm */
483   PetscCallMPI(MPI_Comm_split(comm, 0, duprank, &dupcomm));
484   PetscCall(PetscCommDuplicate(dupcomm, &psubcomm->dupparent, NULL));
485   PetscCall(PetscCommDuplicate(subcomm, &psubcomm->child, NULL));
486   PetscCallMPI(MPI_Comm_free(&dupcomm));
487   PetscCallMPI(MPI_Comm_free(&subcomm));
488 
489   psubcomm->color   = color;
490   psubcomm->subsize = subsize;
491   psubcomm->type    = PETSC_SUBCOMM_INTERLACED;
492   PetscFunctionReturn(PETSC_SUCCESS);
493 }
494