xref: /petsc/src/vec/is/ao/impls/mapping/aomapping.c (revision 98d129c30f3ee9fdddc40fdbc5a989b7be64f888)
1 /*
2   These AO application ordering routines do not require that the input
3   be a permutation, but merely a 1-1 mapping. This implementation still
4   keeps the entire ordering on each processor.
5 */
6 
7 #include <../src/vec/is/ao/aoimpl.h> /*I  "petscao.h" I*/
8 
9 typedef struct {
10   PetscInt  N;
11   PetscInt *app; /* app[i] is the partner for petsc[appPerm[i]] */
12   PetscInt *appPerm;
13   PetscInt *petsc; /* petsc[j] is the partner for app[petscPerm[j]] */
14   PetscInt *petscPerm;
15 } AO_Mapping;
16 
17 static PetscErrorCode AODestroy_Mapping(AO ao)
18 {
19   AO_Mapping *aomap = (AO_Mapping *)ao->data;
20 
21   PetscFunctionBegin;
22   PetscCall(PetscFree4(aomap->app, aomap->appPerm, aomap->petsc, aomap->petscPerm));
23   PetscCall(PetscFree(aomap));
24   PetscFunctionReturn(PETSC_SUCCESS);
25 }
26 
27 static PetscErrorCode AOView_Mapping(AO ao, PetscViewer viewer)
28 {
29   AO_Mapping *aomap = (AO_Mapping *)ao->data;
30   PetscMPIInt rank;
31   PetscInt    i;
32   PetscBool   iascii;
33 
34   PetscFunctionBegin;
35   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)ao), &rank));
36   if (rank) PetscFunctionReturn(PETSC_SUCCESS);
37   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
38   if (iascii) {
39     PetscCall(PetscViewerASCIIPrintf(viewer, "Number of elements in ordering %" PetscInt_FMT "\n", aomap->N));
40     PetscCall(PetscViewerASCIIPrintf(viewer, "   App.   PETSc\n"));
41     for (i = 0; i < aomap->N; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT "   %" PetscInt_FMT "    %" PetscInt_FMT "\n", i, aomap->app[i], aomap->petsc[aomap->appPerm[i]]));
42   }
43   PetscFunctionReturn(PETSC_SUCCESS);
44 }
45 
46 static PetscErrorCode AOPetscToApplication_Mapping(AO ao, PetscInt n, PetscInt *ia)
47 {
48   AO_Mapping *aomap = (AO_Mapping *)ao->data;
49   PetscInt   *app   = aomap->app;
50   PetscInt   *petsc = aomap->petsc;
51   PetscInt   *perm  = aomap->petscPerm;
52   PetscInt    N     = aomap->N;
53   PetscInt    low, high, mid = 0;
54   PetscInt    idex;
55   PetscInt    i;
56 
57   /* It would be possible to use a single bisection search, which
58      recursively divided the indices to be converted, and searched
59      partitions which contained an index. This would result in
60      better running times if indices are clustered.
61   */
62   PetscFunctionBegin;
63   for (i = 0; i < n; i++) {
64     idex = ia[i];
65     if (idex < 0) continue;
66     /* Use bisection since the array is sorted */
67     low  = 0;
68     high = N - 1;
69     while (low <= high) {
70       mid = (low + high) / 2;
71       if (idex == petsc[mid]) break;
72       else if (idex < petsc[mid]) high = mid - 1;
73       else low = mid + 1;
74     }
75     if (low > high) ia[i] = -1;
76     else ia[i] = app[perm[mid]];
77   }
78   PetscFunctionReturn(PETSC_SUCCESS);
79 }
80 
81 static PetscErrorCode AOApplicationToPetsc_Mapping(AO ao, PetscInt n, PetscInt *ia)
82 {
83   AO_Mapping *aomap = (AO_Mapping *)ao->data;
84   PetscInt   *app   = aomap->app;
85   PetscInt   *petsc = aomap->petsc;
86   PetscInt   *perm  = aomap->appPerm;
87   PetscInt    N     = aomap->N;
88   PetscInt    low, high, mid = 0;
89   PetscInt    idex;
90   PetscInt    i;
91 
92   /* It would be possible to use a single bisection search, which
93      recursively divided the indices to be converted, and searched
94      partitions which contained an index. This would result in
95      better running times if indices are clustered.
96   */
97   PetscFunctionBegin;
98   for (i = 0; i < n; i++) {
99     idex = ia[i];
100     if (idex < 0) continue;
101     /* Use bisection since the array is sorted */
102     low  = 0;
103     high = N - 1;
104     while (low <= high) {
105       mid = (low + high) / 2;
106       if (idex == app[mid]) break;
107       else if (idex < app[mid]) high = mid - 1;
108       else low = mid + 1;
109     }
110     if (low > high) ia[i] = -1;
111     else ia[i] = petsc[perm[mid]];
112   }
113   PetscFunctionReturn(PETSC_SUCCESS);
114 }
115 
116 static const struct _AOOps AOps = {
117   PetscDesignatedInitializer(view, AOView_Mapping),
118   PetscDesignatedInitializer(destroy, AODestroy_Mapping),
119   PetscDesignatedInitializer(petsctoapplication, AOPetscToApplication_Mapping),
120   PetscDesignatedInitializer(applicationtopetsc, AOApplicationToPetsc_Mapping),
121 };
122 
123 /*@C
124   AOMappingHasApplicationIndex - Checks if an `AO` has a requested application index.
125 
126   Not Collective
127 
128   Input Parameters:
129 + ao   - The `AO`
130 - idex - The application index
131 
132   Output Parameter:
133 . hasIndex - Flag is `PETSC_TRUE` if the index exists
134 
135   Level: intermediate
136 
137   Developer Notes:
138   The name of the function is wrong, it should be `AOHasApplicationIndex`
139 
140 .seealso: [](sec_ao), `AOMappingHasPetscIndex()`, `AOCreateMapping()`, `AO`
141 @*/
142 PetscErrorCode AOMappingHasApplicationIndex(AO ao, PetscInt idex, PetscBool *hasIndex)
143 {
144   AO_Mapping *aomap;
145   PetscInt   *app;
146   PetscInt    low, high, mid;
147 
148   PetscFunctionBegin;
149   PetscValidHeaderSpecific(ao, AO_CLASSID, 1);
150   PetscAssertPointer(hasIndex, 3);
151   aomap = (AO_Mapping *)ao->data;
152   app   = aomap->app;
153   /* Use bisection since the array is sorted */
154   low  = 0;
155   high = aomap->N - 1;
156   while (low <= high) {
157     mid = (low + high) / 2;
158     if (idex == app[mid]) break;
159     else if (idex < app[mid]) high = mid - 1;
160     else low = mid + 1;
161   }
162   if (low > high) *hasIndex = PETSC_FALSE;
163   else *hasIndex = PETSC_TRUE;
164   PetscFunctionReturn(PETSC_SUCCESS);
165 }
166 
167 /*@
168   AOMappingHasPetscIndex - checks if an `AO` has a requested petsc index.
169 
170   Not Collective
171 
172   Input Parameters:
173 + ao   - The `AO`
174 - idex - The petsc index
175 
176   Output Parameter:
177 . hasIndex - Flag is `PETSC_TRUE` if the index exists
178 
179   Level: intermediate
180 
181   Developer Notes:
182   The name of the function is wrong, it should be `AOHasPetscIndex`
183 
184 .seealso: [](sec_ao), `AOMappingHasApplicationIndex()`, `AOCreateMapping()`
185 @*/
186 PetscErrorCode AOMappingHasPetscIndex(AO ao, PetscInt idex, PetscBool *hasIndex)
187 {
188   AO_Mapping *aomap;
189   PetscInt   *petsc;
190   PetscInt    low, high, mid;
191 
192   PetscFunctionBegin;
193   PetscValidHeaderSpecific(ao, AO_CLASSID, 1);
194   PetscAssertPointer(hasIndex, 3);
195   aomap = (AO_Mapping *)ao->data;
196   petsc = aomap->petsc;
197   /* Use bisection since the array is sorted */
198   low  = 0;
199   high = aomap->N - 1;
200   while (low <= high) {
201     mid = (low + high) / 2;
202     if (idex == petsc[mid]) break;
203     else if (idex < petsc[mid]) high = mid - 1;
204     else low = mid + 1;
205   }
206   if (low > high) *hasIndex = PETSC_FALSE;
207   else *hasIndex = PETSC_TRUE;
208   PetscFunctionReturn(PETSC_SUCCESS);
209 }
210 
211 /*@C
212   AOCreateMapping - Creates an application mapping using two integer arrays.
213 
214   Input Parameters:
215 + comm    - MPI communicator that is to share the `AO`
216 . napp    - size of integer arrays
217 . myapp   - integer array that defines an ordering
218 - mypetsc - integer array that defines another ordering (may be `NULL` to indicate the identity ordering)
219 
220   Output Parameter:
221 . aoout - the new application mapping
222 
223   Options Database Key:
224 . -ao_view - call `AOView()` at the conclusion of `AOCreateMapping()`
225 
226   Level: beginner
227 
228   Note:
229   The arrays `myapp` and `mypetsc` need NOT contain the all the integers 0 to `napp`-1, that is there CAN be "holes"  in the indices.
230   Use `AOCreateBasic()` or `AOCreateBasicIS()` if they do not have holes for better performance.
231 
232 .seealso: [](sec_ao), `AOCreateBasic()`, `AOCreateMappingIS()`, `AODestroy()`
233 @*/
234 PetscErrorCode AOCreateMapping(MPI_Comm comm, PetscInt napp, const PetscInt myapp[], const PetscInt mypetsc[], AO *aoout)
235 {
236   AO          ao;
237   AO_Mapping *aomap;
238   PetscInt   *allpetsc, *allapp;
239   PetscInt   *petscPerm, *appPerm;
240   PetscInt   *petsc;
241   PetscMPIInt size, rank, *lens, *disp, nnapp;
242   PetscInt    N, start;
243   PetscInt    i;
244 
245   PetscFunctionBegin;
246   PetscAssertPointer(aoout, 5);
247   *aoout = NULL;
248   PetscCall(AOInitializePackage());
249 
250   PetscCall(PetscHeaderCreate(ao, AO_CLASSID, "AO", "Application Ordering", "AO", comm, AODestroy, AOView));
251   PetscCall(PetscNew(&aomap));
252   ao->ops[0] = AOps;
253   ao->data   = (void *)aomap;
254 
255   /* transmit all lengths to all processors */
256   PetscCallMPI(MPI_Comm_size(comm, &size));
257   PetscCallMPI(MPI_Comm_rank(comm, &rank));
258   PetscCall(PetscMalloc2(size, &lens, size, &disp));
259   nnapp = napp;
260   PetscCallMPI(MPI_Allgather(&nnapp, 1, MPI_INT, lens, 1, MPI_INT, comm));
261   N = 0;
262   for (i = 0; i < size; i++) {
263     disp[i] = N;
264     N += lens[i];
265   }
266   aomap->N = N;
267   ao->N    = N;
268   ao->n    = N;
269 
270   /* If mypetsc is 0 then use "natural" numbering */
271   if (!mypetsc) {
272     start = disp[rank];
273     PetscCall(PetscMalloc1(napp + 1, &petsc));
274     for (i = 0; i < napp; i++) petsc[i] = start + i;
275   } else {
276     petsc = (PetscInt *)mypetsc;
277   }
278 
279   /* get all indices on all processors */
280   PetscCall(PetscMalloc4(N, &allapp, N, &appPerm, N, &allpetsc, N, &petscPerm));
281   PetscCallMPI(MPI_Allgatherv((void *)myapp, napp, MPIU_INT, allapp, lens, disp, MPIU_INT, comm));
282   PetscCallMPI(MPI_Allgatherv((void *)petsc, napp, MPIU_INT, allpetsc, lens, disp, MPIU_INT, comm));
283   PetscCall(PetscFree2(lens, disp));
284 
285   /* generate a list of application and PETSc node numbers */
286   PetscCall(PetscMalloc4(N, &aomap->app, N, &aomap->appPerm, N, &aomap->petsc, N, &aomap->petscPerm));
287   for (i = 0; i < N; i++) {
288     appPerm[i]   = i;
289     petscPerm[i] = i;
290   }
291   PetscCall(PetscSortIntWithPermutation(N, allpetsc, petscPerm));
292   PetscCall(PetscSortIntWithPermutation(N, allapp, appPerm));
293   /* Form sorted arrays of indices */
294   for (i = 0; i < N; i++) {
295     aomap->app[i]   = allapp[appPerm[i]];
296     aomap->petsc[i] = allpetsc[petscPerm[i]];
297   }
298   /* Invert petscPerm[] into aomap->petscPerm[] */
299   for (i = 0; i < N; i++) aomap->petscPerm[petscPerm[i]] = i;
300 
301   /* Form map between aomap->app[] and aomap->petsc[] */
302   for (i = 0; i < N; i++) aomap->appPerm[i] = aomap->petscPerm[appPerm[i]];
303 
304   /* Invert appPerm[] into allapp[] */
305   for (i = 0; i < N; i++) allapp[appPerm[i]] = i;
306 
307   /* Form map between aomap->petsc[] and aomap->app[] */
308   for (i = 0; i < N; i++) aomap->petscPerm[i] = allapp[petscPerm[i]];
309 
310   if (PetscDefined(USE_DEBUG)) {
311     /* Check that the permutations are complementary */
312     for (i = 0; i < N; i++) PetscCheck(i == aomap->appPerm[aomap->petscPerm[i]], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid ordering");
313   }
314   /* Cleanup */
315   if (!mypetsc) PetscCall(PetscFree(petsc));
316   PetscCall(PetscFree4(allapp, appPerm, allpetsc, petscPerm));
317 
318   PetscCall(AOViewFromOptions(ao, NULL, "-ao_view"));
319 
320   *aoout = ao;
321   PetscFunctionReturn(PETSC_SUCCESS);
322 }
323 
324 /*@
325   AOCreateMappingIS - Creates an application mapping using two index sets.
326 
327   Input Parameters:
328 + isapp   - index set that defines an ordering
329 - ispetsc - index set that defines another ordering, maybe `NULL` for identity `IS`
330 
331   Output Parameter:
332 . aoout - the new application ordering
333 
334   Options Database Key:
335 . -ao_view - call `AOView()` at the conclusion of `AOCreateMappingIS()`
336 
337   Level: beginner
338 
339   Note:
340   The index sets `isapp` and `ispetsc` need NOT contain the all the integers 0 to N-1, that is there CAN be "holes"  in the indices.
341   Use `AOCreateBasic()` or `AOCreateBasicIS()` if they do not have holes for better performance.
342 
343 .seealso: [](sec_ao), [](sec_scatter), `AOCreateBasic()`, `AOCreateMapping()`, `AODestroy()`
344 @*/
345 PetscErrorCode AOCreateMappingIS(IS isapp, IS ispetsc, AO *aoout)
346 {
347   MPI_Comm        comm;
348   const PetscInt *mypetsc, *myapp;
349   PetscInt        napp, npetsc;
350 
351   PetscFunctionBegin;
352   PetscCall(PetscObjectGetComm((PetscObject)isapp, &comm));
353   PetscCall(ISGetLocalSize(isapp, &napp));
354   if (ispetsc) {
355     PetscCall(ISGetLocalSize(ispetsc, &npetsc));
356     PetscCheck(napp == npetsc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local IS lengths must match");
357     PetscCall(ISGetIndices(ispetsc, &mypetsc));
358   } else {
359     mypetsc = NULL;
360   }
361   PetscCall(ISGetIndices(isapp, &myapp));
362 
363   PetscCall(AOCreateMapping(comm, napp, myapp, mypetsc, aoout));
364 
365   PetscCall(ISRestoreIndices(isapp, &myapp));
366   if (ispetsc) PetscCall(ISRestoreIndices(ispetsc, &mypetsc));
367   PetscFunctionReturn(PETSC_SUCCESS);
368 }
369