xref: /petsc/src/vec/is/ao/impls/memscalable/aomemscalable.c (revision bd412c90fba8895e9763ccee76c7dd2e09a982d7)
1 
2 /*
3     The memory scalable AO application ordering routines. These store the
4   orderings on each processor for that processor's range of values
5 */
6 
7 #include <../src/vec/is/ao/aoimpl.h> /*I  "petscao.h"   I*/
8 
9 typedef struct {
10   PetscInt   *app_loc;   /* app_loc[i] is the partner for the ith local PETSc slot */
11   PetscInt   *petsc_loc; /* petsc_loc[j] is the partner for the jth local app slot */
12   PetscLayout map;       /* determines the local sizes of ao */
13 } AO_MemoryScalable;
14 
15 /*
16        All processors ship the data to process 0 to be printed; note that this is not scalable because
17        process 0 allocates space for all the orderings entry across all the processes
18 */
19 PetscErrorCode AOView_MemoryScalable(AO ao, PetscViewer viewer)
20 {
21   PetscMPIInt        rank, size;
22   AO_MemoryScalable *aomems = (AO_MemoryScalable *)ao->data;
23   PetscBool          iascii;
24   PetscMPIInt        tag_app, tag_petsc;
25   PetscLayout        map = aomems->map;
26   PetscInt          *app, *app_loc, *petsc, *petsc_loc, len, i, j;
27   MPI_Status         status;
28 
29   PetscFunctionBegin;
30   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
31   PetscCheck(iascii, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Viewer type %s not supported for AO MemoryScalable", ((PetscObject)viewer)->type_name);
32 
33   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)ao), &rank));
34   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)ao), &size));
35 
36   PetscCall(PetscObjectGetNewTag((PetscObject)ao, &tag_app));
37   PetscCall(PetscObjectGetNewTag((PetscObject)ao, &tag_petsc));
38 
39   if (rank == 0) {
40     PetscCall(PetscViewerASCIIPrintf(viewer, "Number of elements in ordering %" PetscInt_FMT "\n", ao->N));
41     PetscCall(PetscViewerASCIIPrintf(viewer, "PETSc->App  App->PETSc\n"));
42 
43     PetscCall(PetscMalloc2(map->N, &app, map->N, &petsc));
44     len = map->n;
45     /* print local AO */
46     PetscCall(PetscViewerASCIIPrintf(viewer, "Process [%d]\n", rank));
47     for (i = 0; i < len; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "%3" PetscInt_FMT "  %3" PetscInt_FMT "    %3" PetscInt_FMT "  %3" PetscInt_FMT "\n", i, aomems->app_loc[i], i, aomems->petsc_loc[i]));
48 
49     /* recv and print off-processor's AO */
50     for (i = 1; i < size; i++) {
51       len       = map->range[i + 1] - map->range[i];
52       app_loc   = app + map->range[i];
53       petsc_loc = petsc + map->range[i];
54       PetscCallMPI(MPI_Recv(app_loc, (PetscMPIInt)len, MPIU_INT, i, tag_app, PetscObjectComm((PetscObject)ao), &status));
55       PetscCallMPI(MPI_Recv(petsc_loc, (PetscMPIInt)len, MPIU_INT, i, tag_petsc, PetscObjectComm((PetscObject)ao), &status));
56       PetscCall(PetscViewerASCIIPrintf(viewer, "Process [%" PetscInt_FMT "]\n", i));
57       for (j = 0; j < len; j++) PetscCall(PetscViewerASCIIPrintf(viewer, "%3" PetscInt_FMT "  %3" PetscInt_FMT "    %3" PetscInt_FMT "  %3" PetscInt_FMT "\n", map->range[i] + j, app_loc[j], map->range[i] + j, petsc_loc[j]));
58     }
59     PetscCall(PetscFree2(app, petsc));
60 
61   } else {
62     /* send values */
63     PetscCallMPI(MPI_Send((void *)aomems->app_loc, map->n, MPIU_INT, 0, tag_app, PetscObjectComm((PetscObject)ao)));
64     PetscCallMPI(MPI_Send((void *)aomems->petsc_loc, map->n, MPIU_INT, 0, tag_petsc, PetscObjectComm((PetscObject)ao)));
65   }
66   PetscCall(PetscViewerFlush(viewer));
67   PetscFunctionReturn(0);
68 }
69 
70 PetscErrorCode AODestroy_MemoryScalable(AO ao)
71 {
72   AO_MemoryScalable *aomems = (AO_MemoryScalable *)ao->data;
73 
74   PetscFunctionBegin;
75   PetscCall(PetscFree2(aomems->app_loc, aomems->petsc_loc));
76   PetscCall(PetscLayoutDestroy(&aomems->map));
77   PetscCall(PetscFree(aomems));
78   PetscFunctionReturn(0);
79 }
80 
81 /*
82    Input Parameters:
83 +   ao - the application ordering context
84 .   n  - the number of integers in ia[]
85 .   ia - the integers; these are replaced with their mapped value
86 -   maploc - app_loc or petsc_loc in struct "AO_MemoryScalable"
87 
88    Output Parameter:
89 .   ia - the mapped interges
90  */
91 PetscErrorCode AOMap_MemoryScalable_private(AO ao, PetscInt n, PetscInt *ia, const PetscInt *maploc)
92 {
93   AO_MemoryScalable *aomems = (AO_MemoryScalable *)ao->data;
94   MPI_Comm           comm;
95   PetscMPIInt        rank, size, tag1, tag2;
96   PetscInt          *owner, *start, *sizes, nsends, nreceives;
97   PetscInt           nmax, count, *sindices, *rindices, i, j, idx, lastidx, *sindices2, *rindices2;
98   const PetscInt    *owners = aomems->map->range;
99   MPI_Request       *send_waits, *recv_waits, *send_waits2, *recv_waits2;
100   MPI_Status         recv_status;
101   PetscMPIInt        nindices, source, widx;
102   PetscInt          *rbuf, *sbuf;
103   MPI_Status        *send_status, *send_status2;
104 
105   PetscFunctionBegin;
106   PetscCall(PetscObjectGetComm((PetscObject)ao, &comm));
107   PetscCallMPI(MPI_Comm_rank(comm, &rank));
108   PetscCallMPI(MPI_Comm_size(comm, &size));
109 
110   /*  first count number of contributors to each processor */
111   PetscCall(PetscMalloc1(size, &start));
112   PetscCall(PetscCalloc2(2 * size, &sizes, n, &owner));
113 
114   j       = 0;
115   lastidx = -1;
116   for (i = 0; i < n; i++) {
117     if (ia[i] < 0) owner[i] = -1;      /* mark negative entries (which are not to be mapped) with a special negative value */
118     if (ia[i] >= ao->N) owner[i] = -2; /* mark out of range entries with special negative value */
119     else {
120       /* if indices are NOT locally sorted, need to start search at the beginning */
121       if (lastidx > (idx = ia[i])) j = 0;
122       lastidx = idx;
123       for (; j < size; j++) {
124         if (idx >= owners[j] && idx < owners[j + 1]) {
125           sizes[2 * j]++;       /* num of indices to be sent */
126           sizes[2 * j + 1] = 1; /* send to proc[j] */
127           owner[i]         = j;
128           break;
129         }
130       }
131     }
132   }
133   sizes[2 * rank] = sizes[2 * rank + 1] = 0; /* do not receive from self! */
134   nsends                                = 0;
135   for (i = 0; i < size; i++) nsends += sizes[2 * i + 1];
136 
137   /* inform other processors of number of messages and max length*/
138   PetscCall(PetscMaxSum(comm, sizes, &nmax, &nreceives));
139 
140   /* allocate arrays */
141   PetscCall(PetscObjectGetNewTag((PetscObject)ao, &tag1));
142   PetscCall(PetscObjectGetNewTag((PetscObject)ao, &tag2));
143 
144   PetscCall(PetscMalloc2(nreceives * nmax, &rindices, nreceives, &recv_waits));
145   PetscCall(PetscMalloc2(nsends * nmax, &rindices2, nsends, &recv_waits2));
146 
147   PetscCall(PetscMalloc3(n, &sindices, nsends, &send_waits, nsends, &send_status));
148   PetscCall(PetscMalloc3(n, &sindices2, nreceives, &send_waits2, nreceives, &send_status2));
149 
150   /* post 1st receives: receive others requests
151      since we don't know how long each individual message is we
152      allocate the largest needed buffer for each receive. Potentially
153      this is a lot of wasted space.
154   */
155   for (i = 0, count = 0; i < nreceives; i++) PetscCallMPI(MPI_Irecv(rindices + nmax * i, nmax, MPIU_INT, MPI_ANY_SOURCE, tag1, comm, recv_waits + count++));
156 
157   /* do 1st sends:
158       1) starts[i] gives the starting index in svalues for stuff going to
159          the ith processor
160   */
161   start[0] = 0;
162   for (i = 1; i < size; i++) start[i] = start[i - 1] + sizes[2 * i - 2];
163   for (i = 0; i < n; i++) {
164     j = owner[i];
165     if (j == -1) continue; /* do not remap negative entries in ia[] */
166     else if (j == -2) { /* out of range entries get mapped to -1 */ ia[i] = -1;
167       continue;
168     } else if (j != rank) {
169       sindices[start[j]++] = ia[i];
170     } else { /* compute my own map */
171       ia[i] = maploc[ia[i] - owners[rank]];
172     }
173   }
174 
175   start[0] = 0;
176   for (i = 1; i < size; i++) start[i] = start[i - 1] + sizes[2 * i - 2];
177   for (i = 0, count = 0; i < size; i++) {
178     if (sizes[2 * i + 1]) {
179       /* send my request to others */
180       PetscCallMPI(MPI_Isend(sindices + start[i], sizes[2 * i], MPIU_INT, i, tag1, comm, send_waits + count));
181       /* post receive for the answer of my request */
182       PetscCallMPI(MPI_Irecv(sindices2 + start[i], sizes[2 * i], MPIU_INT, i, tag2, comm, recv_waits2 + count));
183       count++;
184     }
185   }
186   PetscCheck(nsends == count, comm, PETSC_ERR_SUP, "nsends %" PetscInt_FMT " != count %" PetscInt_FMT, nsends, count);
187 
188   /* wait on 1st sends */
189   if (nsends) PetscCallMPI(MPI_Waitall(nsends, send_waits, send_status));
190 
191   /* 1st recvs: other's requests */
192   for (j = 0; j < nreceives; j++) {
193     PetscCallMPI(MPI_Waitany(nreceives, recv_waits, &widx, &recv_status)); /* idx: index of handle for operation that completed */
194     PetscCallMPI(MPI_Get_count(&recv_status, MPIU_INT, &nindices));
195     rbuf   = rindices + nmax * widx; /* global index */
196     source = recv_status.MPI_SOURCE;
197 
198     /* compute mapping */
199     sbuf = rbuf;
200     for (i = 0; i < nindices; i++) sbuf[i] = maploc[rbuf[i] - owners[rank]];
201 
202     /* send mapping back to the sender */
203     PetscCallMPI(MPI_Isend(sbuf, nindices, MPIU_INT, source, tag2, comm, send_waits2 + widx));
204   }
205 
206   /* wait on 2nd sends */
207   if (nreceives) PetscCallMPI(MPI_Waitall(nreceives, send_waits2, send_status2));
208 
209   /* 2nd recvs: for the answer of my request */
210   for (j = 0; j < nsends; j++) {
211     PetscCallMPI(MPI_Waitany(nsends, recv_waits2, &widx, &recv_status));
212     PetscCallMPI(MPI_Get_count(&recv_status, MPIU_INT, &nindices));
213     source = recv_status.MPI_SOURCE;
214     /* pack output ia[] */
215     rbuf  = sindices2 + start[source];
216     count = 0;
217     for (i = 0; i < n; i++) {
218       if (source == owner[i]) ia[i] = rbuf[count++];
219     }
220   }
221 
222   /* free arrays */
223   PetscCall(PetscFree(start));
224   PetscCall(PetscFree2(sizes, owner));
225   PetscCall(PetscFree2(rindices, recv_waits));
226   PetscCall(PetscFree2(rindices2, recv_waits2));
227   PetscCall(PetscFree3(sindices, send_waits, send_status));
228   PetscCall(PetscFree3(sindices2, send_waits2, send_status2));
229   PetscFunctionReturn(0);
230 }
231 
232 PetscErrorCode AOPetscToApplication_MemoryScalable(AO ao, PetscInt n, PetscInt *ia)
233 {
234   AO_MemoryScalable *aomems  = (AO_MemoryScalable *)ao->data;
235   PetscInt          *app_loc = aomems->app_loc;
236 
237   PetscFunctionBegin;
238   PetscCall(AOMap_MemoryScalable_private(ao, n, ia, app_loc));
239   PetscFunctionReturn(0);
240 }
241 
242 PetscErrorCode AOApplicationToPetsc_MemoryScalable(AO ao, PetscInt n, PetscInt *ia)
243 {
244   AO_MemoryScalable *aomems    = (AO_MemoryScalable *)ao->data;
245   PetscInt          *petsc_loc = aomems->petsc_loc;
246 
247   PetscFunctionBegin;
248   PetscCall(AOMap_MemoryScalable_private(ao, n, ia, petsc_loc));
249   PetscFunctionReturn(0);
250 }
251 
252 static struct _AOOps AOOps_MemoryScalable = {
253   PetscDesignatedInitializer(view, AOView_MemoryScalable),
254   PetscDesignatedInitializer(destroy, AODestroy_MemoryScalable),
255   PetscDesignatedInitializer(petsctoapplication, AOPetscToApplication_MemoryScalable),
256   PetscDesignatedInitializer(applicationtopetsc, AOApplicationToPetsc_MemoryScalable),
257 };
258 
259 PetscErrorCode AOCreateMemoryScalable_private(MPI_Comm comm, PetscInt napp, const PetscInt from_array[], const PetscInt to_array[], AO ao, PetscInt *aomap_loc)
260 {
261   AO_MemoryScalable *aomems  = (AO_MemoryScalable *)ao->data;
262   PetscLayout        map     = aomems->map;
263   PetscInt           n_local = map->n, i, j;
264   PetscMPIInt        rank, size, tag;
265   PetscInt          *owner, *start, *sizes, nsends, nreceives;
266   PetscInt           nmax, count, *sindices, *rindices, idx, lastidx;
267   PetscInt          *owners = aomems->map->range;
268   MPI_Request       *send_waits, *recv_waits;
269   MPI_Status         recv_status;
270   PetscMPIInt        nindices, widx;
271   PetscInt          *rbuf;
272   PetscInt           n = napp, ip, ia;
273   MPI_Status        *send_status;
274 
275   PetscFunctionBegin;
276   PetscCall(PetscArrayzero(aomap_loc, n_local));
277 
278   PetscCallMPI(MPI_Comm_rank(comm, &rank));
279   PetscCallMPI(MPI_Comm_size(comm, &size));
280 
281   /*  first count number of contributors (of from_array[]) to each processor */
282   PetscCall(PetscCalloc1(2 * size, &sizes));
283   PetscCall(PetscMalloc1(n, &owner));
284 
285   j       = 0;
286   lastidx = -1;
287   for (i = 0; i < n; i++) {
288     /* if indices are NOT locally sorted, need to start search at the beginning */
289     if (lastidx > (idx = from_array[i])) j = 0;
290     lastidx = idx;
291     for (; j < size; j++) {
292       if (idx >= owners[j] && idx < owners[j + 1]) {
293         sizes[2 * j] += 2;    /* num of indices to be sent - in pairs (ip,ia) */
294         sizes[2 * j + 1] = 1; /* send to proc[j] */
295         owner[i]         = j;
296         break;
297       }
298     }
299   }
300   sizes[2 * rank] = sizes[2 * rank + 1] = 0; /* do not receive from self! */
301   nsends                                = 0;
302   for (i = 0; i < size; i++) nsends += sizes[2 * i + 1];
303 
304   /* inform other processors of number of messages and max length*/
305   PetscCall(PetscMaxSum(comm, sizes, &nmax, &nreceives));
306 
307   /* allocate arrays */
308   PetscCall(PetscObjectGetNewTag((PetscObject)ao, &tag));
309   PetscCall(PetscMalloc2(nreceives * nmax, &rindices, nreceives, &recv_waits));
310   PetscCall(PetscMalloc3(2 * n, &sindices, nsends, &send_waits, nsends, &send_status));
311   PetscCall(PetscMalloc1(size, &start));
312 
313   /* post receives: */
314   for (i = 0; i < nreceives; i++) PetscCallMPI(MPI_Irecv(rindices + nmax * i, nmax, MPIU_INT, MPI_ANY_SOURCE, tag, comm, recv_waits + i));
315 
316   /* do sends:
317       1) starts[i] gives the starting index in svalues for stuff going to
318          the ith processor
319   */
320   start[0] = 0;
321   for (i = 1; i < size; i++) start[i] = start[i - 1] + sizes[2 * i - 2];
322   for (i = 0; i < n; i++) {
323     j = owner[i];
324     if (j != rank) {
325       ip                   = from_array[i];
326       ia                   = to_array[i];
327       sindices[start[j]++] = ip;
328       sindices[start[j]++] = ia;
329     } else { /* compute my own map */
330       ip            = from_array[i] - owners[rank];
331       ia            = to_array[i];
332       aomap_loc[ip] = ia;
333     }
334   }
335 
336   start[0] = 0;
337   for (i = 1; i < size; i++) start[i] = start[i - 1] + sizes[2 * i - 2];
338   for (i = 0, count = 0; i < size; i++) {
339     if (sizes[2 * i + 1]) {
340       PetscCallMPI(MPI_Isend(sindices + start[i], sizes[2 * i], MPIU_INT, i, tag, comm, send_waits + count));
341       count++;
342     }
343   }
344   PetscCheck(nsends == count, comm, PETSC_ERR_SUP, "nsends %" PetscInt_FMT " != count %" PetscInt_FMT, nsends, count);
345 
346   /* wait on sends */
347   if (nsends) PetscCallMPI(MPI_Waitall(nsends, send_waits, send_status));
348 
349   /* recvs */
350   count = 0;
351   for (j = nreceives; j > 0; j--) {
352     PetscCallMPI(MPI_Waitany(nreceives, recv_waits, &widx, &recv_status));
353     PetscCallMPI(MPI_Get_count(&recv_status, MPIU_INT, &nindices));
354     rbuf = rindices + nmax * widx; /* global index */
355 
356     /* compute local mapping */
357     for (i = 0; i < nindices; i += 2) {       /* pack aomap_loc */
358       ip            = rbuf[i] - owners[rank]; /* local index */
359       ia            = rbuf[i + 1];
360       aomap_loc[ip] = ia;
361     }
362     count++;
363   }
364 
365   PetscCall(PetscFree(start));
366   PetscCall(PetscFree3(sindices, send_waits, send_status));
367   PetscCall(PetscFree2(rindices, recv_waits));
368   PetscCall(PetscFree(owner));
369   PetscCall(PetscFree(sizes));
370   PetscFunctionReturn(0);
371 }
372 
373 PETSC_EXTERN PetscErrorCode AOCreate_MemoryScalable(AO ao)
374 {
375   IS                 isapp = ao->isapp, ispetsc = ao->ispetsc;
376   const PetscInt    *mypetsc, *myapp;
377   PetscInt           napp, n_local, N, i, start, *petsc, *lens, *disp;
378   MPI_Comm           comm;
379   AO_MemoryScalable *aomems;
380   PetscLayout        map;
381   PetscMPIInt        size, rank;
382 
383   PetscFunctionBegin;
384   PetscCheck(isapp, PetscObjectComm((PetscObject)ao), PETSC_ERR_ARG_WRONGSTATE, "AOSetIS() must be called before AOSetType()");
385   /* create special struct aomems */
386   PetscCall(PetscNew(&aomems));
387   ao->data = (void *)aomems;
388   PetscCall(PetscMemcpy(ao->ops, &AOOps_MemoryScalable, sizeof(struct _AOOps)));
389   PetscCall(PetscObjectChangeTypeName((PetscObject)ao, AOMEMORYSCALABLE));
390 
391   /* transmit all local lengths of isapp to all processors */
392   PetscCall(PetscObjectGetComm((PetscObject)isapp, &comm));
393   PetscCallMPI(MPI_Comm_size(comm, &size));
394   PetscCallMPI(MPI_Comm_rank(comm, &rank));
395   PetscCall(PetscMalloc2(size, &lens, size, &disp));
396   PetscCall(ISGetLocalSize(isapp, &napp));
397   PetscCallMPI(MPI_Allgather(&napp, 1, MPIU_INT, lens, 1, MPIU_INT, comm));
398 
399   N = 0;
400   for (i = 0; i < size; i++) {
401     disp[i] = N;
402     N += lens[i];
403   }
404 
405   /* If ispetsc is 0 then use "natural" numbering */
406   if (napp) {
407     if (!ispetsc) {
408       start = disp[rank];
409       PetscCall(PetscMalloc1(napp + 1, &petsc));
410       for (i = 0; i < napp; i++) petsc[i] = start + i;
411     } else {
412       PetscCall(ISGetIndices(ispetsc, &mypetsc));
413       petsc = (PetscInt *)mypetsc;
414     }
415   } else {
416     petsc = NULL;
417   }
418 
419   /* create a map with global size N - used to determine the local sizes of ao - shall we use local napp instead of N? */
420   PetscCall(PetscLayoutCreate(comm, &map));
421   map->bs = 1;
422   map->N  = N;
423   PetscCall(PetscLayoutSetUp(map));
424 
425   ao->N       = N;
426   ao->n       = map->n;
427   aomems->map = map;
428 
429   /* create distributed indices app_loc: petsc->app and petsc_loc: app->petsc */
430   n_local = map->n;
431   PetscCall(PetscCalloc2(n_local, &aomems->app_loc, n_local, &aomems->petsc_loc));
432   PetscCall(ISGetIndices(isapp, &myapp));
433 
434   PetscCall(AOCreateMemoryScalable_private(comm, napp, petsc, myapp, ao, aomems->app_loc));
435   PetscCall(AOCreateMemoryScalable_private(comm, napp, myapp, petsc, ao, aomems->petsc_loc));
436 
437   PetscCall(ISRestoreIndices(isapp, &myapp));
438   if (napp) {
439     if (ispetsc) {
440       PetscCall(ISRestoreIndices(ispetsc, &mypetsc));
441     } else {
442       PetscCall(PetscFree(petsc));
443     }
444   }
445   PetscCall(PetscFree2(lens, disp));
446   PetscFunctionReturn(0);
447 }
448 
449 /*@C
450    AOCreateMemoryScalable - Creates a memory scalable application ordering using two integer arrays.
451 
452    Collective
453 
454    Input Parameters:
455 +  comm - MPI communicator that is to share AO
456 .  napp - size of integer arrays
457 .  myapp - integer array that defines an ordering
458 -  mypetsc - integer array that defines another ordering (may be NULL to
459              indicate the natural ordering, that is 0,1,2,3,...)
460 
461    Output Parameter:
462 .  aoout - the new application ordering
463 
464    Level: beginner
465 
466     Notes:
467     The arrays myapp and mypetsc must contain the all the integers 0 to napp-1 with no duplicates; that is there cannot be any "holes"
468            in the indices. Use AOCreateMapping() or AOCreateMappingIS() if you wish to have "holes" in the indices.
469            Comparing with AOCreateBasic(), this routine trades memory with message communication.
470 
471 .seealso: `AOCreateMemoryScalableIS()`, `AODestroy()`, `AOPetscToApplication()`, `AOApplicationToPetsc()`
472 @*/
473 PetscErrorCode AOCreateMemoryScalable(MPI_Comm comm, PetscInt napp, const PetscInt myapp[], const PetscInt mypetsc[], AO *aoout)
474 {
475   IS              isapp, ispetsc;
476   const PetscInt *app = myapp, *petsc = mypetsc;
477 
478   PetscFunctionBegin;
479   PetscCall(ISCreateGeneral(comm, napp, app, PETSC_USE_POINTER, &isapp));
480   if (mypetsc) {
481     PetscCall(ISCreateGeneral(comm, napp, petsc, PETSC_USE_POINTER, &ispetsc));
482   } else {
483     ispetsc = NULL;
484   }
485   PetscCall(AOCreateMemoryScalableIS(isapp, ispetsc, aoout));
486   PetscCall(ISDestroy(&isapp));
487   if (mypetsc) PetscCall(ISDestroy(&ispetsc));
488   PetscFunctionReturn(0);
489 }
490 
491 /*@C
492    AOCreateMemoryScalableIS - Creates a memory scalable application ordering using two index sets.
493 
494    Collective on IS
495 
496    Input Parameters:
497 +  isapp - index set that defines an ordering
498 -  ispetsc - index set that defines another ordering (may be NULL to use the
499              natural ordering)
500 
501    Output Parameter:
502 .  aoout - the new application ordering
503 
504    Level: beginner
505 
506     Notes:
507     The index sets isapp and ispetsc must contain the all the integers 0 to napp-1 (where napp is the length of the index sets) with no duplicates;
508            that is there cannot be any "holes".
509            Comparing with AOCreateBasicIS(), this routine trades memory with message communication.
510 .seealso: `AOCreateMemoryScalable()`, `AODestroy()`
511 @*/
512 PetscErrorCode AOCreateMemoryScalableIS(IS isapp, IS ispetsc, AO *aoout)
513 {
514   MPI_Comm comm;
515   AO       ao;
516 
517   PetscFunctionBegin;
518   PetscCall(PetscObjectGetComm((PetscObject)isapp, &comm));
519   PetscCall(AOCreate(comm, &ao));
520   PetscCall(AOSetIS(ao, isapp, ispetsc));
521   PetscCall(AOSetType(ao, AOMEMORYSCALABLE));
522   PetscCall(AOViewFromOptions(ao, NULL, "-ao_view"));
523   *aoout = ao;
524   PetscFunctionReturn(0);
525 }
526