xref: /petsc/src/vec/is/utils/isltog.c (revision fc47f7259de0629fb6058a3a6076517fd44721f2)
1 #include <petsc/private/isimpl.h> /*I "petscis.h"  I*/
2 #include <petsc/private/hashmapi.h>
3 #include <petscsf.h>
4 #include <petscviewer.h>
5 #include <petscbt.h>
6 
7 PetscClassId          IS_LTOGM_CLASSID;
8 static PetscErrorCode ISLocalToGlobalMappingSetUpBlockInfo_Private(ISLocalToGlobalMapping);
9 
10 typedef struct {
11   PetscInt *globals;
12 } ISLocalToGlobalMapping_Basic;
13 
14 typedef struct {
15   PetscHMapI globalht;
16 } ISLocalToGlobalMapping_Hash;
17 
18 /*@C
19   ISGetPointRange - Returns a description of the points in an `IS` suitable for traversal
20 
21   Not Collective
22 
23   Input Parameter:
24 . pointIS - The `IS` object
25 
26   Output Parameters:
27 + pStart - The first index, see notes
28 . pEnd   - One past the last index, see notes
29 - points - The indices, see notes
30 
31   Level: intermediate
32 
33   Notes:
34   If the `IS` contains contiguous indices in an `ISSTRIDE`, then the indices are contained in [pStart, pEnd) and points = `NULL`.
35   Otherwise, `pStart = 0`, `pEnd = numIndices`, and points is an array of the indices. This supports the following pattern
36 .vb
37   ISGetPointRange(is, &pStart, &pEnd, &points);
38   for (p = pStart; p < pEnd; ++p) {
39     const PetscInt point = points ? points[p] : p;
40     // use point
41   }
42   ISRestorePointRange(is, &pstart, &pEnd, &points);
43 .ve
44   Hence the same code can be written for `pointIS` being a `ISSTRIDE` or `ISGENERAL`
45 
46 .seealso: [](sec_scatter), `IS`, `ISRestorePointRange()`, `ISGetPointSubrange()`, `ISGetIndices()`, `ISCreateStride()`
47 @*/
48 PetscErrorCode ISGetPointRange(IS pointIS, PetscInt *pStart, PetscInt *pEnd, const PetscInt *points[])
49 {
50   PetscInt  numCells, step = 1;
51   PetscBool isStride;
52 
53   PetscFunctionBeginHot;
54   *pStart = 0;
55   *points = NULL;
56   PetscCall(ISGetLocalSize(pointIS, &numCells));
57   PetscCall(PetscObjectTypeCompare((PetscObject)pointIS, ISSTRIDE, &isStride));
58   if (isStride) PetscCall(ISStrideGetInfo(pointIS, pStart, &step));
59   *pEnd = *pStart + numCells;
60   if (!isStride || step != 1) PetscCall(ISGetIndices(pointIS, points));
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
64 /*@C
65   ISRestorePointRange - Destroys the traversal description created with `ISGetPointRange()`
66 
67   Not Collective
68 
69   Input Parameters:
70 + pointIS - The `IS` object
71 . pStart  - The first index, from `ISGetPointRange()`
72 . pEnd    - One past the last index, from `ISGetPointRange()`
73 - points  - The indices, from `ISGetPointRange()`
74 
75   Level: intermediate
76 
77 .seealso: [](sec_scatter), `IS`, `ISGetPointRange()`, `ISGetPointSubrange()`, `ISGetIndices()`, `ISCreateStride()`
78 @*/
79 PetscErrorCode ISRestorePointRange(IS pointIS, PetscInt *pStart, PetscInt *pEnd, const PetscInt *points[])
80 {
81   PetscInt  step = 1;
82   PetscBool isStride;
83 
84   PetscFunctionBeginHot;
85   PetscCall(PetscObjectTypeCompare((PetscObject)pointIS, ISSTRIDE, &isStride));
86   if (isStride) PetscCall(ISStrideGetInfo(pointIS, pStart, &step));
87   if (!isStride || step != 1) PetscCall(ISGetIndices(pointIS, points));
88   PetscFunctionReturn(PETSC_SUCCESS);
89 }
90 
91 /*@
92   ISGetPointSubrange - Configures the input `IS` to be a subrange for the traversal information given
93 
94   Not Collective
95 
96   Input Parameters:
97 + subpointIS - The `IS` object to be configured
98 . pStart     - The first index of the subrange
99 . pEnd       - One past the last index for the subrange
100 - points     - The indices for the entire range, from `ISGetPointRange()`
101 
102   Output Parameters:
103 . subpointIS - The `IS` object now configured to be a subrange
104 
105   Level: intermediate
106 
107   Note:
108   The input `IS` will now respond properly to calls to `ISGetPointRange()` and return the subrange.
109 
110 .seealso: [](sec_scatter), `IS`, `ISGetPointRange()`, `ISRestorePointRange()`, `ISGetIndices()`, `ISCreateStride()`
111 @*/
112 PetscErrorCode ISGetPointSubrange(IS subpointIS, PetscInt pStart, PetscInt pEnd, const PetscInt points[])
113 {
114   PetscFunctionBeginHot;
115   if (points) {
116     PetscCall(ISSetType(subpointIS, ISGENERAL));
117     PetscCall(ISGeneralSetIndices(subpointIS, pEnd - pStart, &points[pStart], PETSC_USE_POINTER));
118   } else {
119     PetscCall(ISSetType(subpointIS, ISSTRIDE));
120     PetscCall(ISStrideSetStride(subpointIS, pEnd - pStart, pStart, 1));
121   }
122   PetscFunctionReturn(PETSC_SUCCESS);
123 }
124 
125 /* -----------------------------------------------------------------------------------------*/
126 
127 /*
128     Creates the global mapping information in the ISLocalToGlobalMapping structure
129 
130     If the user has not selected how to handle the global to local mapping then use HASH for "large" problems
131 */
132 static PetscErrorCode ISGlobalToLocalMappingSetUp(ISLocalToGlobalMapping mapping)
133 {
134   PetscInt i, *idx = mapping->indices, n = mapping->n, end, start;
135 
136   PetscFunctionBegin;
137   if (mapping->data) PetscFunctionReturn(PETSC_SUCCESS);
138   end   = 0;
139   start = PETSC_INT_MAX;
140 
141   for (i = 0; i < n; i++) {
142     if (idx[i] < 0) continue;
143     if (idx[i] < start) start = idx[i];
144     if (idx[i] > end) end = idx[i];
145   }
146   if (start > end) {
147     start = 0;
148     end   = -1;
149   }
150   mapping->globalstart = start;
151   mapping->globalend   = end;
152   if (!((PetscObject)mapping)->type_name) {
153     if ((end - start) > PetscMax(4 * n, 1000000)) {
154       PetscCall(ISLocalToGlobalMappingSetType(mapping, ISLOCALTOGLOBALMAPPINGHASH));
155     } else {
156       PetscCall(ISLocalToGlobalMappingSetType(mapping, ISLOCALTOGLOBALMAPPINGBASIC));
157     }
158   }
159   PetscTryTypeMethod(mapping, globaltolocalmappingsetup);
160   PetscFunctionReturn(PETSC_SUCCESS);
161 }
162 
163 static PetscErrorCode ISGlobalToLocalMappingSetUp_Basic(ISLocalToGlobalMapping mapping)
164 {
165   PetscInt                      i, *idx = mapping->indices, n = mapping->n, end, start, *globals;
166   ISLocalToGlobalMapping_Basic *map;
167 
168   PetscFunctionBegin;
169   start = mapping->globalstart;
170   end   = mapping->globalend;
171   PetscCall(PetscNew(&map));
172   PetscCall(PetscMalloc1(end - start + 2, &globals));
173   map->globals = globals;
174   for (i = 0; i < end - start + 1; i++) globals[i] = -1;
175   for (i = 0; i < n; i++) {
176     if (idx[i] < 0) continue;
177     globals[idx[i] - start] = i;
178   }
179   mapping->data = (void *)map;
180   PetscFunctionReturn(PETSC_SUCCESS);
181 }
182 
183 static PetscErrorCode ISGlobalToLocalMappingSetUp_Hash(ISLocalToGlobalMapping mapping)
184 {
185   PetscInt                     i, *idx = mapping->indices, n = mapping->n;
186   ISLocalToGlobalMapping_Hash *map;
187 
188   PetscFunctionBegin;
189   PetscCall(PetscNew(&map));
190   PetscCall(PetscHMapICreate(&map->globalht));
191   for (i = 0; i < n; i++) {
192     if (idx[i] < 0) continue;
193     PetscCall(PetscHMapISet(map->globalht, idx[i], i));
194   }
195   mapping->data = (void *)map;
196   PetscFunctionReturn(PETSC_SUCCESS);
197 }
198 
199 static PetscErrorCode ISLocalToGlobalMappingDestroy_Basic(ISLocalToGlobalMapping mapping)
200 {
201   ISLocalToGlobalMapping_Basic *map = (ISLocalToGlobalMapping_Basic *)mapping->data;
202 
203   PetscFunctionBegin;
204   if (!map) PetscFunctionReturn(PETSC_SUCCESS);
205   PetscCall(PetscFree(map->globals));
206   PetscCall(PetscFree(mapping->data));
207   PetscFunctionReturn(PETSC_SUCCESS);
208 }
209 
210 static PetscErrorCode ISLocalToGlobalMappingDestroy_Hash(ISLocalToGlobalMapping mapping)
211 {
212   ISLocalToGlobalMapping_Hash *map = (ISLocalToGlobalMapping_Hash *)mapping->data;
213 
214   PetscFunctionBegin;
215   if (!map) PetscFunctionReturn(PETSC_SUCCESS);
216   PetscCall(PetscHMapIDestroy(&map->globalht));
217   PetscCall(PetscFree(mapping->data));
218   PetscFunctionReturn(PETSC_SUCCESS);
219 }
220 
221 static PetscErrorCode ISLocalToGlobalMappingResetBlockInfo_Private(ISLocalToGlobalMapping mapping)
222 {
223   PetscFunctionBegin;
224   PetscCall(PetscFree(mapping->info_procs));
225   PetscCall(PetscFree(mapping->info_numprocs));
226   if (mapping->info_indices) {
227     for (PetscInt i = 0; i < mapping->info_nproc; i++) PetscCall(PetscFree(mapping->info_indices[i]));
228     PetscCall(PetscFree(mapping->info_indices));
229   }
230   if (mapping->info_nodei) PetscCall(PetscFree(mapping->info_nodei[0]));
231   PetscCall(PetscFree2(mapping->info_nodec, mapping->info_nodei));
232   PetscCall(PetscSFDestroy(&mapping->multileaves_sf));
233   PetscFunctionReturn(PETSC_SUCCESS);
234 }
235 
236 #define GTOLTYPE _Basic
237 #define GTOLNAME _Basic
238 #define GTOLBS   mapping->bs
239 #define GTOL(g, local) \
240   do { \
241     local = map->globals[g / bs - start]; \
242     if (local >= 0) local = bs * local + (g % bs); \
243   } while (0)
244 
245 #include <../src/vec/is/utils/isltog.h>
246 
247 #define GTOLTYPE _Basic
248 #define GTOLNAME Block_Basic
249 #define GTOLBS   1
250 #define GTOL(g, local) \
251   do { \
252     local = map->globals[g - start]; \
253   } while (0)
254 #include <../src/vec/is/utils/isltog.h>
255 
256 #define GTOLTYPE _Hash
257 #define GTOLNAME _Hash
258 #define GTOLBS   mapping->bs
259 #define GTOL(g, local) \
260   do { \
261     (void)PetscHMapIGet(map->globalht, g / bs, &local); \
262     if (local >= 0) local = bs * local + (g % bs); \
263   } while (0)
264 #include <../src/vec/is/utils/isltog.h>
265 
266 #define GTOLTYPE _Hash
267 #define GTOLNAME Block_Hash
268 #define GTOLBS   1
269 #define GTOL(g, local) \
270   do { \
271     (void)PetscHMapIGet(map->globalht, g, &local); \
272   } while (0)
273 #include <../src/vec/is/utils/isltog.h>
274 
275 /*@
276   ISLocalToGlobalMappingDuplicate - Duplicates the local to global mapping object
277 
278   Not Collective
279 
280   Input Parameter:
281 . ltog - local to global mapping
282 
283   Output Parameter:
284 . nltog - the duplicated local to global mapping
285 
286   Level: advanced
287 
288 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreate()`
289 @*/
290 PetscErrorCode ISLocalToGlobalMappingDuplicate(ISLocalToGlobalMapping ltog, ISLocalToGlobalMapping *nltog)
291 {
292   ISLocalToGlobalMappingType l2gtype;
293 
294   PetscFunctionBegin;
295   PetscValidHeaderSpecific(ltog, IS_LTOGM_CLASSID, 1);
296   PetscCall(ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)ltog), ltog->bs, ltog->n, ltog->indices, PETSC_COPY_VALUES, nltog));
297   PetscCall(ISLocalToGlobalMappingGetType(ltog, &l2gtype));
298   PetscCall(ISLocalToGlobalMappingSetType(*nltog, l2gtype));
299   PetscFunctionReturn(PETSC_SUCCESS);
300 }
301 
302 /*@
303   ISLocalToGlobalMappingGetSize - Gets the local size of a local to global mapping
304 
305   Not Collective
306 
307   Input Parameter:
308 . mapping - local to global mapping
309 
310   Output Parameter:
311 . n - the number of entries in the local mapping, `ISLocalToGlobalMappingGetIndices()` returns an array of this length
312 
313   Level: advanced
314 
315 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreate()`
316 @*/
317 PetscErrorCode ISLocalToGlobalMappingGetSize(ISLocalToGlobalMapping mapping, PetscInt *n)
318 {
319   PetscFunctionBegin;
320   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
321   PetscAssertPointer(n, 2);
322   *n = mapping->bs * mapping->n;
323   PetscFunctionReturn(PETSC_SUCCESS);
324 }
325 
326 /*@
327   ISLocalToGlobalMappingViewFromOptions - View an `ISLocalToGlobalMapping` based on values in the options database
328 
329   Collective
330 
331   Input Parameters:
332 + A    - the local to global mapping object
333 . obj  - Optional object that provides the options prefix used for the options database query
334 - name - command line option
335 
336   Level: intermediate
337 
338   Note:
339   See `PetscObjectViewFromOptions()` for the available `PetscViewer` and `PetscViewerFormat`
340 
341 .seealso: [](sec_scatter), `PetscViewer`, `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingView`, `PetscObjectViewFromOptions()`, `ISLocalToGlobalMappingCreate()`
342 @*/
343 PetscErrorCode ISLocalToGlobalMappingViewFromOptions(ISLocalToGlobalMapping A, PetscObject obj, const char name[])
344 {
345   PetscFunctionBegin;
346   PetscValidHeaderSpecific(A, IS_LTOGM_CLASSID, 1);
347   PetscCall(PetscObjectViewFromOptions((PetscObject)A, obj, name));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 /*@
352   ISLocalToGlobalMappingView - View a local to global mapping
353 
354   Collective on viewer
355 
356   Input Parameters:
357 + mapping - local to global mapping
358 - viewer  - viewer
359 
360   Level: intermediate
361 
362 .seealso: [](sec_scatter), `PetscViewer`, `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreate()`
363 @*/
364 PetscErrorCode ISLocalToGlobalMappingView(ISLocalToGlobalMapping mapping, PetscViewer viewer)
365 {
366   PetscBool         iascii, isbinary;
367   PetscViewerFormat format;
368 
369   PetscFunctionBegin;
370   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
371   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)mapping), &viewer));
372   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
373 
374   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
375   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERBINARY, &isbinary));
376   PetscCall(PetscViewerGetFormat(viewer, &format));
377   if (iascii) {
378     if (format == PETSC_VIEWER_ASCII_MATLAB) {
379       const PetscInt *idxs;
380       IS              is;
381       const char     *name = ((PetscObject)mapping)->name;
382       char            iname[PETSC_MAX_PATH_LEN];
383 
384       PetscCall(PetscSNPrintf(iname, sizeof(iname), "%sl2g", name ? name : ""));
385       PetscCall(ISLocalToGlobalMappingGetIndices(mapping, &idxs));
386       PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)viewer), mapping->n * mapping->bs, idxs, PETSC_USE_POINTER, &is));
387       PetscCall(PetscObjectSetName((PetscObject)is, iname));
388       PetscCall(ISView(is, viewer));
389       PetscCall(ISLocalToGlobalMappingRestoreIndices(mapping, &idxs));
390       PetscCall(ISDestroy(&is));
391     } else {
392       PetscMPIInt rank;
393 
394       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)mapping), &rank));
395       PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)mapping, viewer));
396       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
397       for (PetscInt i = 0; i < mapping->n; i++) {
398         PetscInt bs = mapping->bs, g = mapping->indices[i];
399         if (bs == 1) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] %" PetscInt_FMT " %" PetscInt_FMT "\n", rank, i, g));
400         else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] %" PetscInt_FMT ":%" PetscInt_FMT " %" PetscInt_FMT ":%" PetscInt_FMT "\n", rank, i * bs, (i + 1) * bs, g * bs, (g + 1) * bs));
401       }
402       PetscCall(PetscViewerFlush(viewer));
403       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
404     }
405   } else if (isbinary) {
406     PetscBool skipHeader;
407 
408     PetscCall(PetscViewerSetUp(viewer));
409     PetscCall(PetscViewerBinaryGetSkipHeader(viewer, &skipHeader));
410     if (!skipHeader) {
411       PetscMPIInt size;
412       PetscInt    tr[3];
413 
414       PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)viewer), &size));
415       tr[0] = IS_LTOGM_FILE_CLASSID;
416       tr[1] = mapping->bs;
417       tr[2] = size;
418       PetscCall(PetscViewerBinaryWrite(viewer, tr, 3, PETSC_INT));
419       PetscCall(PetscViewerBinaryWriteAll(viewer, &mapping->n, 1, PETSC_DETERMINE, PETSC_DETERMINE, PETSC_INT));
420     }
421     /* write block indices */
422     PetscCall(PetscViewerBinaryWriteAll(viewer, mapping->indices, mapping->n, PETSC_DETERMINE, PETSC_DETERMINE, PETSC_INT));
423   }
424   PetscFunctionReturn(PETSC_SUCCESS);
425 }
426 
427 /*@
428   ISLocalToGlobalMappingLoad - Loads a local-to-global mapping that has been stored in binary format.
429 
430   Collective on viewer
431 
432   Input Parameters:
433 + mapping - the newly loaded map, this needs to have been created with `ISLocalToGlobalMappingCreate()` or some related function before a call to `ISLocalToGlobalMappingLoad()`
434 - viewer  - binary file viewer, obtained from `PetscViewerBinaryOpen()`
435 
436   Level: intermediate
437 
438 .seealso: [](sec_scatter), `PetscViewer`, `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingView()`, `ISLocalToGlobalMappingCreate()`
439 @*/
440 PetscErrorCode ISLocalToGlobalMappingLoad(ISLocalToGlobalMapping mapping, PetscViewer viewer)
441 {
442   PetscBool isbinary, skipHeader;
443 
444   PetscFunctionBegin;
445   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
446   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
447   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERBINARY, &isbinary));
448   PetscCheck(isbinary, PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Invalid viewer of type %s", ((PetscObject)viewer)->type_name);
449 
450   /* reset previous data */
451   PetscCall(ISLocalToGlobalMappingResetBlockInfo_Private(mapping));
452 
453   PetscCall(PetscViewerSetUp(viewer));
454   PetscCall(PetscViewerBinaryGetSkipHeader(viewer, &skipHeader));
455 
456   /* When skipping header, it assumes bs and n have been already set */
457   if (!skipHeader) {
458     MPI_Comm comm = PetscObjectComm((PetscObject)viewer);
459     PetscInt tr[3], nold = mapping->n, *sizes, nmaps = PETSC_DECIDE, st = 0;
460 
461     PetscCall(PetscViewerBinaryRead(viewer, tr, 3, NULL, PETSC_INT));
462     PetscCheck(tr[0] == IS_LTOGM_FILE_CLASSID, PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED, "Not a local-to-global map next in file");
463 
464     mapping->bs = tr[1];
465     PetscCall(PetscMalloc1(tr[2], &sizes));
466     PetscCall(PetscViewerBinaryRead(viewer, sizes, tr[2], NULL, PETSC_INT));
467 
468     /* consume the input, read multiple maps per process if needed */
469     PetscCall(PetscSplitOwnership(comm, &nmaps, &tr[2]));
470     PetscCallMPI(MPI_Exscan(&nmaps, &st, 1, MPIU_INT, MPI_SUM, comm));
471     mapping->n = 0;
472     for (PetscInt i = st; i < st + nmaps; i++) mapping->n += sizes[i];
473     PetscCall(PetscFree(sizes));
474 
475     if (nold != mapping->n) {
476       if (mapping->dealloc_indices) PetscCall(PetscFree(mapping->indices));
477       mapping->indices = NULL;
478     }
479   }
480 
481   /* read indices */
482   if (mapping->n && !mapping->indices) {
483     PetscCall(PetscMalloc1(mapping->n, &mapping->indices));
484     mapping->dealloc_indices = PETSC_TRUE;
485   }
486   PetscCall(PetscViewerBinaryReadAll(viewer, mapping->indices, mapping->n, PETSC_DETERMINE, PETSC_DETERMINE, PETSC_INT));
487   PetscFunctionReturn(PETSC_SUCCESS);
488 }
489 
490 /*@
491   ISLocalToGlobalMappingCreateIS - Creates a mapping between a local (0 to n)
492   ordering and a global parallel ordering.
493 
494   Not Collective
495 
496   Input Parameter:
497 . is - index set containing the global numbers for each local number
498 
499   Output Parameter:
500 . mapping - new mapping data structure
501 
502   Level: advanced
503 
504   Note:
505   the block size of the `IS` determines the block size of the mapping
506 
507 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingSetFromOptions()`
508 @*/
509 PetscErrorCode ISLocalToGlobalMappingCreateIS(IS is, ISLocalToGlobalMapping *mapping)
510 {
511   PetscInt        n, bs;
512   const PetscInt *indices;
513   MPI_Comm        comm;
514   PetscBool       isblock;
515 
516   PetscFunctionBegin;
517   PetscValidHeaderSpecific(is, IS_CLASSID, 1);
518   PetscAssertPointer(mapping, 2);
519 
520   PetscCall(PetscObjectGetComm((PetscObject)is, &comm));
521   PetscCall(ISGetLocalSize(is, &n));
522   PetscCall(PetscObjectTypeCompare((PetscObject)is, ISBLOCK, &isblock));
523   if (!isblock) {
524     PetscCall(ISGetIndices(is, &indices));
525     PetscCall(ISLocalToGlobalMappingCreate(comm, 1, n, indices, PETSC_COPY_VALUES, mapping));
526     PetscCall(ISRestoreIndices(is, &indices));
527   } else {
528     PetscCall(ISGetBlockSize(is, &bs));
529     PetscCall(ISBlockGetIndices(is, &indices));
530     PetscCall(ISLocalToGlobalMappingCreate(comm, bs, n / bs, indices, PETSC_COPY_VALUES, mapping));
531     PetscCall(ISBlockRestoreIndices(is, &indices));
532   }
533   PetscFunctionReturn(PETSC_SUCCESS);
534 }
535 
536 /*@
537   ISLocalToGlobalMappingCreateSF - Creates a mapping between a local (0 to n) ordering and a global parallel ordering induced by a star forest.
538 
539   Collective
540 
541   Input Parameters:
542 + sf    - star forest mapping contiguous local indices to (rank, offset)
543 - start - first global index on this process, or `PETSC_DECIDE` to compute contiguous global numbering automatically
544 
545   Output Parameter:
546 . mapping - new mapping data structure
547 
548   Level: advanced
549 
550   Note:
551   If a process calls this function with `start` = `PETSC_DECIDE` then all processes must, otherwise the program will hang.
552 
553 .seealso: [](sec_scatter), `PetscSF`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingSetFromOptions()`
554 @*/
555 PetscErrorCode ISLocalToGlobalMappingCreateSF(PetscSF sf, PetscInt start, ISLocalToGlobalMapping *mapping)
556 {
557   PetscInt i, maxlocal, nroots, nleaves, *globals, *ltog;
558   MPI_Comm comm;
559 
560   PetscFunctionBegin;
561   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 1);
562   PetscAssertPointer(mapping, 3);
563   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
564   PetscCall(PetscSFGetGraph(sf, &nroots, &nleaves, NULL, NULL));
565   if (start == PETSC_DECIDE) {
566     start = 0;
567     PetscCallMPI(MPI_Exscan(&nroots, &start, 1, MPIU_INT, MPI_SUM, comm));
568   } else PetscCheck(start >= 0, comm, PETSC_ERR_ARG_OUTOFRANGE, "start must be nonnegative or PETSC_DECIDE");
569   PetscCall(PetscSFGetLeafRange(sf, NULL, &maxlocal));
570   ++maxlocal;
571   PetscCall(PetscMalloc1(nroots, &globals));
572   PetscCall(PetscMalloc1(maxlocal, &ltog));
573   for (i = 0; i < nroots; i++) globals[i] = start + i;
574   for (i = 0; i < maxlocal; i++) ltog[i] = -1;
575   PetscCall(PetscSFBcastBegin(sf, MPIU_INT, globals, ltog, MPI_REPLACE));
576   PetscCall(PetscSFBcastEnd(sf, MPIU_INT, globals, ltog, MPI_REPLACE));
577   PetscCall(ISLocalToGlobalMappingCreate(comm, 1, maxlocal, ltog, PETSC_OWN_POINTER, mapping));
578   PetscCall(PetscFree(globals));
579   PetscFunctionReturn(PETSC_SUCCESS);
580 }
581 
582 /*@
583   ISLocalToGlobalMappingSetBlockSize - Sets the blocksize of the mapping
584 
585   Not Collective
586 
587   Input Parameters:
588 + mapping - mapping data structure
589 - bs      - the blocksize
590 
591   Level: advanced
592 
593 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`
594 @*/
595 PetscErrorCode ISLocalToGlobalMappingSetBlockSize(ISLocalToGlobalMapping mapping, PetscInt bs)
596 {
597   PetscInt       *nid;
598   const PetscInt *oid;
599   PetscInt        i, cn, on, obs, nn;
600 
601   PetscFunctionBegin;
602   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
603   PetscCheck(bs >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid block size %" PetscInt_FMT, bs);
604   if (bs == mapping->bs) PetscFunctionReturn(PETSC_SUCCESS);
605   on  = mapping->n;
606   obs = mapping->bs;
607   oid = mapping->indices;
608   nn  = (on * obs) / bs;
609   PetscCheck((on * obs) % bs == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Block size %" PetscInt_FMT " is inconsistent with block size %" PetscInt_FMT " and number of block indices %" PetscInt_FMT, bs, obs, on);
610 
611   PetscCall(PetscMalloc1(nn, &nid));
612   PetscCall(ISLocalToGlobalMappingGetIndices(mapping, &oid));
613   for (i = 0; i < nn; i++) {
614     PetscInt j;
615     for (j = 0, cn = 0; j < bs - 1; j++) {
616       if (oid[i * bs + j] < 0) {
617         cn++;
618         continue;
619       }
620       PetscCheck(oid[i * bs + j] == oid[i * bs + j + 1] - 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Block sizes %" PetscInt_FMT " and %" PetscInt_FMT " are incompatible with the block indices: non consecutive indices %" PetscInt_FMT " %" PetscInt_FMT, bs, obs, oid[i * bs + j], oid[i * bs + j + 1]);
621     }
622     if (oid[i * bs + j] < 0) cn++;
623     if (cn) {
624       PetscCheck(cn == bs, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Block sizes %" PetscInt_FMT " and %" PetscInt_FMT " are incompatible with the block indices: invalid number of negative entries in block %" PetscInt_FMT, bs, obs, cn);
625       nid[i] = -1;
626     } else {
627       nid[i] = oid[i * bs] / bs;
628     }
629   }
630   PetscCall(ISLocalToGlobalMappingRestoreIndices(mapping, &oid));
631 
632   mapping->n  = nn;
633   mapping->bs = bs;
634   PetscCall(PetscFree(mapping->indices));
635   mapping->indices     = nid;
636   mapping->globalstart = 0;
637   mapping->globalend   = 0;
638 
639   /* reset the cached information */
640   PetscCall(ISLocalToGlobalMappingResetBlockInfo_Private(mapping));
641   PetscTryTypeMethod(mapping, destroy);
642   PetscFunctionReturn(PETSC_SUCCESS);
643 }
644 
645 /*@
646   ISLocalToGlobalMappingGetBlockSize - Gets the blocksize of the mapping
647   ordering and a global parallel ordering.
648 
649   Not Collective
650 
651   Input Parameter:
652 . mapping - mapping data structure
653 
654   Output Parameter:
655 . bs - the blocksize
656 
657   Level: advanced
658 
659 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`
660 @*/
661 PetscErrorCode ISLocalToGlobalMappingGetBlockSize(ISLocalToGlobalMapping mapping, PetscInt *bs)
662 {
663   PetscFunctionBegin;
664   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
665   *bs = mapping->bs;
666   PetscFunctionReturn(PETSC_SUCCESS);
667 }
668 
669 /*@
670   ISLocalToGlobalMappingCreate - Creates a mapping between a local (0 to n)
671   ordering and a global parallel ordering.
672 
673   Not Collective, but communicator may have more than one process
674 
675   Input Parameters:
676 + comm    - MPI communicator
677 . bs      - the block size
678 . n       - the number of local elements divided by the block size, or equivalently the number of block indices
679 . indices - the global index for each local element, these do not need to be in increasing order (sorted), these values should not be scaled (i.e. multiplied) by the blocksize bs
680 - mode    - see PetscCopyMode
681 
682   Output Parameter:
683 . mapping - new mapping data structure
684 
685   Level: advanced
686 
687   Notes:
688   There is one integer value in indices per block and it represents the actual indices bs*idx + j, where j=0,..,bs-1
689 
690   For "small" problems when using `ISGlobalToLocalMappingApply()` and `ISGlobalToLocalMappingApplyBlock()`, the `ISLocalToGlobalMappingType`
691   of `ISLOCALTOGLOBALMAPPINGBASIC` will be used; this uses more memory but is faster; this approach is not scalable for extremely large mappings.
692 
693   For large problems `ISLOCALTOGLOBALMAPPINGHASH` is used, this is scalable.
694   Use `ISLocalToGlobalMappingSetType()` or call `ISLocalToGlobalMappingSetFromOptions()` with the option
695   `-islocaltoglobalmapping_type` <`basic`,`hash`> to control which is used.
696 
697 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingSetFromOptions()`,
698           `ISLOCALTOGLOBALMAPPINGBASIC`, `ISLOCALTOGLOBALMAPPINGHASH`
699           `ISLocalToGlobalMappingSetType()`, `ISLocalToGlobalMappingType`
700 @*/
701 PetscErrorCode ISLocalToGlobalMappingCreate(MPI_Comm comm, PetscInt bs, PetscInt n, const PetscInt indices[], PetscCopyMode mode, ISLocalToGlobalMapping *mapping)
702 {
703   PetscInt *in;
704 
705   PetscFunctionBegin;
706   if (n) PetscAssertPointer(indices, 4);
707   PetscAssertPointer(mapping, 6);
708 
709   *mapping = NULL;
710   PetscCall(ISInitializePackage());
711 
712   PetscCall(PetscHeaderCreate(*mapping, IS_LTOGM_CLASSID, "ISLocalToGlobalMapping", "Local to global mapping", "IS", comm, ISLocalToGlobalMappingDestroy, ISLocalToGlobalMappingView));
713   (*mapping)->n  = n;
714   (*mapping)->bs = bs;
715   if (mode == PETSC_COPY_VALUES) {
716     PetscCall(PetscMalloc1(n, &in));
717     PetscCall(PetscArraycpy(in, indices, n));
718     (*mapping)->indices         = in;
719     (*mapping)->dealloc_indices = PETSC_TRUE;
720   } else if (mode == PETSC_OWN_POINTER) {
721     (*mapping)->indices         = (PetscInt *)indices;
722     (*mapping)->dealloc_indices = PETSC_TRUE;
723   } else if (mode == PETSC_USE_POINTER) {
724     (*mapping)->indices = (PetscInt *)indices;
725   } else SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid mode %d", mode);
726   PetscFunctionReturn(PETSC_SUCCESS);
727 }
728 
729 PetscFunctionList ISLocalToGlobalMappingList = NULL;
730 
731 /*@
732   ISLocalToGlobalMappingSetFromOptions - Set mapping options from the options database.
733 
734   Not Collective
735 
736   Input Parameter:
737 . mapping - mapping data structure
738 
739   Options Database Key:
740 . -islocaltoglobalmapping_type - <basic,hash> nonscalable and scalable versions
741 
742   Level: advanced
743 
744 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingDestroy()`,
745 `ISLocalToGlobalMappingCreateIS()`, `ISLOCALTOGLOBALMAPPINGBASIC`,
746 `ISLOCALTOGLOBALMAPPINGHASH`, `ISLocalToGlobalMappingSetType()`, `ISLocalToGlobalMappingType`
747 @*/
748 PetscErrorCode ISLocalToGlobalMappingSetFromOptions(ISLocalToGlobalMapping mapping)
749 {
750   char                       type[256];
751   ISLocalToGlobalMappingType defaulttype = "Not set";
752   PetscBool                  flg;
753 
754   PetscFunctionBegin;
755   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
756   PetscCall(ISLocalToGlobalMappingRegisterAll());
757   PetscObjectOptionsBegin((PetscObject)mapping);
758   PetscCall(PetscOptionsFList("-islocaltoglobalmapping_type", "ISLocalToGlobalMapping method", "ISLocalToGlobalMappingSetType", ISLocalToGlobalMappingList, ((PetscObject)mapping)->type_name ? ((PetscObject)mapping)->type_name : defaulttype, type, 256, &flg));
759   if (flg) PetscCall(ISLocalToGlobalMappingSetType(mapping, type));
760   PetscOptionsEnd();
761   PetscFunctionReturn(PETSC_SUCCESS);
762 }
763 
764 /*@
765   ISLocalToGlobalMappingDestroy - Destroys a mapping between a local (0 to n)
766   ordering and a global parallel ordering.
767 
768   Not Collective
769 
770   Input Parameter:
771 . mapping - mapping data structure
772 
773   Level: advanced
774 
775 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingCreate()`
776 @*/
777 PetscErrorCode ISLocalToGlobalMappingDestroy(ISLocalToGlobalMapping *mapping)
778 {
779   PetscFunctionBegin;
780   if (!*mapping) PetscFunctionReturn(PETSC_SUCCESS);
781   PetscValidHeaderSpecific(*mapping, IS_LTOGM_CLASSID, 1);
782   if (--((PetscObject)*mapping)->refct > 0) {
783     *mapping = NULL;
784     PetscFunctionReturn(PETSC_SUCCESS);
785   }
786   if ((*mapping)->dealloc_indices) PetscCall(PetscFree((*mapping)->indices));
787   PetscCall(ISLocalToGlobalMappingResetBlockInfo_Private(*mapping));
788   PetscTryTypeMethod(*mapping, destroy);
789   PetscCall(PetscHeaderDestroy(mapping));
790   PetscFunctionReturn(PETSC_SUCCESS);
791 }
792 
793 /*@
794   ISLocalToGlobalMappingApplyIS - Creates from an `IS` in the local numbering
795   a new index set using the global numbering defined in an `ISLocalToGlobalMapping`
796   context.
797 
798   Collective
799 
800   Input Parameters:
801 + mapping - mapping between local and global numbering
802 - is      - index set in local numbering
803 
804   Output Parameter:
805 . newis - index set in global numbering
806 
807   Level: advanced
808 
809   Note:
810   The output `IS` will have the same communicator as the input `IS`.
811 
812 .seealso: [](sec_scatter), `ISLocalToGlobalMappingApply()`, `ISLocalToGlobalMappingCreate()`,
813           `ISLocalToGlobalMappingDestroy()`, `ISGlobalToLocalMappingApply()`
814 @*/
815 PetscErrorCode ISLocalToGlobalMappingApplyIS(ISLocalToGlobalMapping mapping, IS is, IS *newis)
816 {
817   PetscInt        n, *idxout;
818   const PetscInt *idxin;
819 
820   PetscFunctionBegin;
821   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
822   PetscValidHeaderSpecific(is, IS_CLASSID, 2);
823   PetscAssertPointer(newis, 3);
824 
825   PetscCall(ISGetLocalSize(is, &n));
826   PetscCall(ISGetIndices(is, &idxin));
827   PetscCall(PetscMalloc1(n, &idxout));
828   PetscCall(ISLocalToGlobalMappingApply(mapping, n, idxin, idxout));
829   PetscCall(ISRestoreIndices(is, &idxin));
830   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)is), n, idxout, PETSC_OWN_POINTER, newis));
831   PetscFunctionReturn(PETSC_SUCCESS);
832 }
833 
834 /*@
835   ISLocalToGlobalMappingApply - Takes a list of integers in a local numbering
836   and converts them to the global numbering.
837 
838   Not Collective
839 
840   Input Parameters:
841 + mapping - the local to global mapping context
842 . N       - number of integers
843 - in      - input indices in local numbering
844 
845   Output Parameter:
846 . out - indices in global numbering
847 
848   Level: advanced
849 
850   Note:
851   The `in` and `out` array parameters may be identical.
852 
853 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingApplyBlock()`, `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingDestroy()`,
854           `ISLocalToGlobalMappingApplyIS()`, `AOCreateBasic()`, `AOApplicationToPetsc()`,
855           `AOPetscToApplication()`, `ISGlobalToLocalMappingApply()`
856 @*/
857 PetscErrorCode ISLocalToGlobalMappingApply(ISLocalToGlobalMapping mapping, PetscInt N, const PetscInt in[], PetscInt out[])
858 {
859   PetscInt i, bs, Nmax;
860 
861   PetscFunctionBegin;
862   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
863   bs   = mapping->bs;
864   Nmax = bs * mapping->n;
865   if (bs == 1) {
866     const PetscInt *idx = mapping->indices;
867     for (i = 0; i < N; i++) {
868       if (in[i] < 0) {
869         out[i] = in[i];
870         continue;
871       }
872       PetscCheck(in[i] < Nmax, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Local index %" PetscInt_FMT " too large %" PetscInt_FMT " (max) at %" PetscInt_FMT, in[i], Nmax - 1, i);
873       out[i] = idx[in[i]];
874     }
875   } else {
876     const PetscInt *idx = mapping->indices;
877     for (i = 0; i < N; i++) {
878       if (in[i] < 0) {
879         out[i] = in[i];
880         continue;
881       }
882       PetscCheck(in[i] < Nmax, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Local index %" PetscInt_FMT " too large %" PetscInt_FMT " (max) at %" PetscInt_FMT, in[i], Nmax - 1, i);
883       out[i] = idx[in[i] / bs] * bs + (in[i] % bs);
884     }
885   }
886   PetscFunctionReturn(PETSC_SUCCESS);
887 }
888 
889 /*@
890   ISLocalToGlobalMappingApplyBlock - Takes a list of integers in a local block numbering and converts them to the global block numbering
891 
892   Not Collective
893 
894   Input Parameters:
895 + mapping - the local to global mapping context
896 . N       - number of integers
897 - in      - input indices in local block numbering
898 
899   Output Parameter:
900 . out - indices in global block numbering
901 
902   Example:
903   If the index values are {0,1,6,7} set with a call to `ISLocalToGlobalMappingCreate`(`PETSC_COMM_SELF`,2,2,{0,3}) then the mapping applied to 0
904   (the first block) would produce 0 and the mapping applied to 1 (the second block) would produce 3.
905 
906   Level: advanced
907 
908   Note:
909   The `in` and `out` array parameters may be identical.
910 
911 .seealso: [](sec_scatter), `ISLocalToGlobalMappingApply()`, `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingDestroy()`,
912           `ISLocalToGlobalMappingApplyIS()`, `AOCreateBasic()`, `AOApplicationToPetsc()`,
913           `AOPetscToApplication()`, `ISGlobalToLocalMappingApply()`
914 @*/
915 PetscErrorCode ISLocalToGlobalMappingApplyBlock(ISLocalToGlobalMapping mapping, PetscInt N, const PetscInt in[], PetscInt out[])
916 {
917   PetscInt        i, Nmax;
918   const PetscInt *idx;
919 
920   PetscFunctionBegin;
921   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
922   Nmax = mapping->n;
923   idx  = mapping->indices;
924   for (i = 0; i < N; i++) {
925     if (in[i] < 0) {
926       out[i] = in[i];
927       continue;
928     }
929     PetscCheck(in[i] < Nmax, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Local block index %" PetscInt_FMT " too large %" PetscInt_FMT " (max) at %" PetscInt_FMT, in[i], Nmax - 1, i);
930     out[i] = idx[in[i]];
931   }
932   PetscFunctionReturn(PETSC_SUCCESS);
933 }
934 
935 /*@
936   ISGlobalToLocalMappingApply - Provides the local numbering for a list of integers
937   specified with a global numbering.
938 
939   Not Collective
940 
941   Input Parameters:
942 + mapping - mapping between local and global numbering
943 . type    - `IS_GTOLM_MASK` - maps global indices with no local value to -1 in the output list (i.e., mask them)
944            `IS_GTOLM_DROP` - drops the indices with no local value from the output list
945 . n       - number of global indices to map
946 - idx     - global indices to map
947 
948   Output Parameters:
949 + nout   - number of indices in output array (if type == `IS_GTOLM_MASK` then nout = n)
950 - idxout - local index of each global index, one must pass in an array long enough
951              to hold all the indices. You can call `ISGlobalToLocalMappingApply()` with
952              idxout == NULL to determine the required length (returned in nout)
953              and then allocate the required space and call `ISGlobalToLocalMappingApply()`
954              a second time to set the values.
955 
956   Level: advanced
957 
958   Notes:
959   Either `nout` or `idxout` may be `NULL`. `idx` and `idxout` may be identical.
960 
961   For "small" problems when using `ISGlobalToLocalMappingApply()` and `ISGlobalToLocalMappingApplyBlock()`, the `ISLocalToGlobalMappingType` of
962   `ISLOCALTOGLOBALMAPPINGBASIC` will be used;
963   this uses more memory but is faster; this approach is not scalable for extremely large mappings. For large problems `ISLOCALTOGLOBALMAPPINGHASH` is used, this is scalable.
964   Use `ISLocalToGlobalMappingSetType()` or call `ISLocalToGlobalMappingSetFromOptions()` with the option -islocaltoglobalmapping_type <basic,hash> to control which is used.
965 
966   Developer Notes:
967   The manual page states that `idx` and `idxout` may be identical but the calling
968   sequence declares `idx` as const so it cannot be the same as `idxout`.
969 
970 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingApply()`, `ISGlobalToLocalMappingApplyBlock()`, `ISLocalToGlobalMappingCreate()`,
971           `ISLocalToGlobalMappingDestroy()`
972 @*/
973 PetscErrorCode ISGlobalToLocalMappingApply(ISLocalToGlobalMapping mapping, ISGlobalToLocalMappingMode type, PetscInt n, const PetscInt idx[], PetscInt *nout, PetscInt idxout[])
974 {
975   PetscFunctionBegin;
976   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
977   if (!mapping->data) PetscCall(ISGlobalToLocalMappingSetUp(mapping));
978   PetscUseTypeMethod(mapping, globaltolocalmappingapply, type, n, idx, nout, idxout);
979   PetscFunctionReturn(PETSC_SUCCESS);
980 }
981 
982 /*@
983   ISGlobalToLocalMappingApplyIS - Creates from an `IS` in the global numbering
984   a new index set using the local numbering defined in an `ISLocalToGlobalMapping`
985   context.
986 
987   Not Collective
988 
989   Input Parameters:
990 + mapping - mapping between local and global numbering
991 . type    - `IS_GTOLM_MASK` - maps global indices with no local value to -1 in the output list (i.e., mask them)
992            `IS_GTOLM_DROP` - drops the indices with no local value from the output list
993 - is      - index set in global numbering
994 
995   Output Parameter:
996 . newis - index set in local numbering
997 
998   Level: advanced
999 
1000   Note:
1001   The output `IS` will be sequential, as it encodes a purely local operation
1002 
1003 .seealso: [](sec_scatter), `ISGlobalToLocalMapping`, `ISGlobalToLocalMappingApply()`, `ISLocalToGlobalMappingCreate()`,
1004           `ISLocalToGlobalMappingDestroy()`
1005 @*/
1006 PetscErrorCode ISGlobalToLocalMappingApplyIS(ISLocalToGlobalMapping mapping, ISGlobalToLocalMappingMode type, IS is, IS *newis)
1007 {
1008   PetscInt        n, nout, *idxout;
1009   const PetscInt *idxin;
1010 
1011   PetscFunctionBegin;
1012   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1013   PetscValidHeaderSpecific(is, IS_CLASSID, 3);
1014   PetscAssertPointer(newis, 4);
1015 
1016   PetscCall(ISGetLocalSize(is, &n));
1017   PetscCall(ISGetIndices(is, &idxin));
1018   if (type == IS_GTOLM_MASK) {
1019     PetscCall(PetscMalloc1(n, &idxout));
1020   } else {
1021     PetscCall(ISGlobalToLocalMappingApply(mapping, type, n, idxin, &nout, NULL));
1022     PetscCall(PetscMalloc1(nout, &idxout));
1023   }
1024   PetscCall(ISGlobalToLocalMappingApply(mapping, type, n, idxin, &nout, idxout));
1025   PetscCall(ISRestoreIndices(is, &idxin));
1026   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nout, idxout, PETSC_OWN_POINTER, newis));
1027   PetscFunctionReturn(PETSC_SUCCESS);
1028 }
1029 
1030 /*@
1031   ISGlobalToLocalMappingApplyBlock - Provides the local block numbering for a list of integers
1032   specified with a block global numbering.
1033 
1034   Not Collective
1035 
1036   Input Parameters:
1037 + mapping - mapping between local and global numbering
1038 . type    - `IS_GTOLM_MASK` - maps global indices with no local value to -1 in the output list (i.e., mask them)
1039            `IS_GTOLM_DROP` - drops the indices with no local value from the output list
1040 . n       - number of global indices to map
1041 - idx     - global indices to map
1042 
1043   Output Parameters:
1044 + nout   - number of indices in output array (if type == `IS_GTOLM_MASK` then nout = n)
1045 - idxout - local index of each global index, one must pass in an array long enough
1046              to hold all the indices. You can call `ISGlobalToLocalMappingApplyBlock()` with
1047              idxout == NULL to determine the required length (returned in nout)
1048              and then allocate the required space and call `ISGlobalToLocalMappingApplyBlock()`
1049              a second time to set the values.
1050 
1051   Level: advanced
1052 
1053   Notes:
1054   Either `nout` or `idxout` may be `NULL`. `idx` and `idxout` may be identical.
1055 
1056   For "small" problems when using `ISGlobalToLocalMappingApply()` and `ISGlobalToLocalMappingApplyBlock()`, the `ISLocalToGlobalMappingType` of
1057   `ISLOCALTOGLOBALMAPPINGBASIC` will be used;
1058   this uses more memory but is faster; this approach is not scalable for extremely large mappings. For large problems `ISLOCALTOGLOBALMAPPINGHASH` is used, this is scalable.
1059   Use `ISLocalToGlobalMappingSetType()` or call `ISLocalToGlobalMappingSetFromOptions()` with the option -islocaltoglobalmapping_type <basic,hash> to control which is used.
1060 
1061   Developer Notes:
1062   The manual page states that `idx` and `idxout` may be identical but the calling
1063   sequence declares `idx` as const so it cannot be the same as `idxout`.
1064 
1065 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingApply()`, `ISGlobalToLocalMappingApply()`, `ISLocalToGlobalMappingCreate()`,
1066           `ISLocalToGlobalMappingDestroy()`
1067 @*/
1068 PetscErrorCode ISGlobalToLocalMappingApplyBlock(ISLocalToGlobalMapping mapping, ISGlobalToLocalMappingMode type, PetscInt n, const PetscInt idx[], PetscInt *nout, PetscInt idxout[])
1069 {
1070   PetscFunctionBegin;
1071   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1072   if (!mapping->data) PetscCall(ISGlobalToLocalMappingSetUp(mapping));
1073   PetscUseTypeMethod(mapping, globaltolocalmappingapplyblock, type, n, idx, nout, idxout);
1074   PetscFunctionReturn(PETSC_SUCCESS);
1075 }
1076 
1077 /*@C
1078   ISLocalToGlobalMappingGetBlockInfo - Gets the neighbor information
1079 
1080   Collective the first time it is called
1081 
1082   Input Parameter:
1083 . mapping - the mapping from local to global indexing
1084 
1085   Output Parameters:
1086 + nproc    - number of processes that are connected to the calling process
1087 . procs    - neighboring processes
1088 . numprocs - number of block indices for each process
1089 - indices  - block indices (in local numbering) shared with neighbors (sorted by global numbering)
1090 
1091   Level: advanced
1092 
1093 .seealso: [](sec_scatter), `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1094           `ISLocalToGlobalMappingRestoreBlockInfo()`, `ISLocalToGlobalMappingGetBlockMultiLeavesSF()`
1095 @*/
1096 PetscErrorCode ISLocalToGlobalMappingGetBlockInfo(ISLocalToGlobalMapping mapping, PetscInt *nproc, PetscInt *procs[], PetscInt *numprocs[], PetscInt **indices[])
1097 {
1098   PetscFunctionBegin;
1099   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1100   PetscCall(ISLocalToGlobalMappingSetUpBlockInfo_Private(mapping));
1101   if (nproc) *nproc = mapping->info_nproc;
1102   if (procs) *procs = mapping->info_procs;
1103   if (numprocs) *numprocs = mapping->info_numprocs;
1104   if (indices) *indices = mapping->info_indices;
1105   PetscFunctionReturn(PETSC_SUCCESS);
1106 }
1107 
1108 /*@C
1109   ISLocalToGlobalMappingGetBlockNodeInfo - Gets the neighbor information for each local block index
1110 
1111   Collective the first time it is called
1112 
1113   Input Parameter:
1114 . mapping - the mapping from local to global indexing
1115 
1116   Output Parameters:
1117 + n       - number of local block nodes
1118 . n_procs - an array storing the number of processes for each local block node (including self)
1119 - procs   - the processes' rank for each local block node (sorted, self is first)
1120 
1121   Level: advanced
1122 
1123   Notes:
1124   The user needs to call `ISLocalToGlobalMappingRestoreBlockNodeInfo()` when the data is no longer needed.
1125   The information returned by this function complements that of `ISLocalToGlobalMappingGetBlockInfo()`.
1126   The latter only provides local information, and the neighboring information
1127   cannot be inferred in the general case, unless the mapping is locally one-to-one on each process.
1128 
1129 .seealso: `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1130           `ISLocalToGlobalMappingGetBlockInfo()`, `ISLocalToGlobalMappingRestoreBlockNodeInfo()`, `ISLocalToGlobalMappingGetNodeInfo()`
1131 @*/
1132 PetscErrorCode ISLocalToGlobalMappingGetBlockNodeInfo(ISLocalToGlobalMapping mapping, PetscInt *n, PetscInt *n_procs[], PetscInt **procs[])
1133 {
1134   PetscFunctionBegin;
1135   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1136   PetscCall(ISLocalToGlobalMappingSetUpBlockInfo_Private(mapping));
1137   if (n) *n = mapping->n;
1138   if (n_procs) *n_procs = mapping->info_nodec;
1139   if (procs) *procs = mapping->info_nodei;
1140   PetscFunctionReturn(PETSC_SUCCESS);
1141 }
1142 
1143 /*@C
1144   ISLocalToGlobalMappingRestoreBlockNodeInfo - Frees the memory allocated by `ISLocalToGlobalMappingGetBlockNodeInfo()`
1145 
1146   Not Collective
1147 
1148   Input Parameters:
1149 + mapping - the mapping from local to global indexing
1150 . n       - number of local block nodes
1151 . n_procs - an array storing the number of processes for each local block nodes (including self)
1152 - procs   - the processes' rank for each local block node (sorted, self is first)
1153 
1154   Level: advanced
1155 
1156 .seealso: `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1157           `ISLocalToGlobalMappingGetBlockNodeInfo()`
1158 @*/
1159 PetscErrorCode ISLocalToGlobalMappingRestoreBlockNodeInfo(ISLocalToGlobalMapping mapping, PetscInt *n, PetscInt *n_procs[], PetscInt **procs[])
1160 {
1161   PetscFunctionBegin;
1162   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1163   if (n) *n = 0;
1164   if (n_procs) *n_procs = NULL;
1165   if (procs) *procs = NULL;
1166   PetscFunctionReturn(PETSC_SUCCESS);
1167 }
1168 
1169 /*@C
1170   ISLocalToGlobalMappingGetBlockMultiLeavesSF - Get the star-forest to communicate multi-leaf block data
1171 
1172   Collective the first time it is called
1173 
1174   Input Parameter:
1175 . mapping - the mapping from local to global indexing
1176 
1177   Output Parameter:
1178 . mlsf - the `PetscSF`
1179 
1180   Level: advanced
1181 
1182   Notes:
1183   The returned star forest is suitable to exchange local information with other processes sharing the same global block index.
1184   For example, suppose a mapping with two processes has been created with
1185 .vb
1186     rank 0 global block indices: [0, 1, 2]
1187     rank 1 global block indices: [2, 3, 4]
1188 .ve
1189   and we want to share the local information
1190 .vb
1191     rank 0 data: [-1, -2, -3]
1192     rank 1 data: [1, 2, 3]
1193 .ve
1194   then, the broadcasting action of `mlsf` will allow to collect
1195 .vb
1196     rank 0 mlleafdata: [-1, -2, -3, 3]
1197     rank 1 mlleafdata: [-3, 3, 1, 2]
1198 .ve
1199   Use ``ISLocalToGlobalMappingGetBlockNodeInfo()`` to index into the multi-leaf data.
1200 
1201 .seealso: [](sec_scatter), `ISLocalToGlobalMappingGetBlockNodeInfo()`, `PetscSF`
1202 @*/
1203 PetscErrorCode ISLocalToGlobalMappingGetBlockMultiLeavesSF(ISLocalToGlobalMapping mapping, PetscSF *mlsf)
1204 {
1205   PetscFunctionBegin;
1206   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1207   PetscAssertPointer(mlsf, 2);
1208   PetscCall(ISLocalToGlobalMappingSetUpBlockInfo_Private(mapping));
1209   *mlsf = mapping->multileaves_sf;
1210   PetscFunctionReturn(PETSC_SUCCESS);
1211 }
1212 
1213 static PetscErrorCode ISLocalToGlobalMappingSetUpBlockInfo_Private(ISLocalToGlobalMapping mapping)
1214 {
1215   PetscSF            sf, sf2, imsf, msf;
1216   MPI_Comm           comm;
1217   const PetscSFNode *sfnode;
1218   PetscSFNode       *newsfnode;
1219   PetscLayout        layout;
1220   PetscHMapI         neighs;
1221   PetscHashIter      iter;
1222   PetscBool          missing;
1223   const PetscInt    *gidxs, *rootdegree;
1224   PetscInt          *mask, *mrootdata, *leafdata, *newleafdata, *leafrd, *tmpg;
1225   PetscInt           nroots, nleaves, newnleaves, bs, i, j, m, mnroots, p;
1226   PetscMPIInt        rank, size;
1227 
1228   PetscFunctionBegin;
1229   if (mapping->multileaves_sf) PetscFunctionReturn(PETSC_SUCCESS);
1230   PetscCall(PetscObjectGetComm((PetscObject)mapping, &comm));
1231   PetscCallMPI(MPI_Comm_size(comm, &size));
1232   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1233 
1234   /* Get mapping indices */
1235   PetscCall(ISLocalToGlobalMappingGetBlockSize(mapping, &bs));
1236   PetscCall(ISLocalToGlobalMappingGetBlockIndices(mapping, &gidxs));
1237   PetscCall(ISLocalToGlobalMappingGetSize(mapping, &nleaves));
1238   nleaves /= bs;
1239 
1240   /* Create layout for global indices */
1241   for (i = 0, m = 0; i < nleaves; i++) m = PetscMax(m, gidxs[i]);
1242   PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &m, 1, MPIU_INT, MPI_MAX, comm));
1243   PetscCall(PetscLayoutCreate(comm, &layout));
1244   PetscCall(PetscLayoutSetSize(layout, m + 1));
1245   PetscCall(PetscLayoutSetUp(layout));
1246 
1247   /* Create SF to share global indices */
1248   PetscCall(PetscSFCreate(comm, &sf));
1249   PetscCall(PetscSFSetGraphLayout(sf, layout, nleaves, NULL, PETSC_OWN_POINTER, gidxs));
1250   PetscCall(PetscSFSetUp(sf));
1251   PetscCall(PetscLayoutDestroy(&layout));
1252 
1253   /* communicate root degree to leaves */
1254   PetscCall(PetscSFGetGraph(sf, &nroots, NULL, NULL, &sfnode));
1255   PetscCall(PetscSFComputeDegreeBegin(sf, &rootdegree));
1256   PetscCall(PetscSFComputeDegreeEnd(sf, &rootdegree));
1257   for (i = 0, mnroots = 0; i < nroots; i++) mnroots += rootdegree[i];
1258   PetscCall(PetscMalloc3(2 * PetscMax(mnroots, nroots), &mrootdata, 2 * nleaves, &leafdata, nleaves, &leafrd));
1259   for (i = 0, m = 0; i < nroots; i++) {
1260     mrootdata[2 * i + 0] = rootdegree[i];
1261     mrootdata[2 * i + 1] = m;
1262     m += rootdegree[i];
1263   }
1264   PetscCall(PetscSFBcastBegin(sf, MPIU_2INT, mrootdata, leafdata, MPI_REPLACE));
1265   PetscCall(PetscSFBcastEnd(sf, MPIU_2INT, mrootdata, leafdata, MPI_REPLACE));
1266 
1267   /* allocate enough space to store ranks */
1268   for (i = 0, newnleaves = 0; i < nleaves; i++) {
1269     newnleaves += leafdata[2 * i];
1270     leafrd[i] = leafdata[2 * i];
1271   }
1272 
1273   /* create new SF nodes to collect multi-root data at leaves */
1274   PetscCall(PetscMalloc1(newnleaves, &newsfnode));
1275   for (i = 0, m = 0; i < nleaves; i++) {
1276     for (j = 0; j < leafrd[i]; j++) {
1277       newsfnode[m].rank  = sfnode[i].rank;
1278       newsfnode[m].index = leafdata[2 * i + 1] + j;
1279       m++;
1280     }
1281   }
1282 
1283   /* gather ranks at multi roots */
1284   for (i = 0; i < mnroots; i++) mrootdata[i] = -1;
1285   for (i = 0; i < nleaves; i++) leafdata[i] = rank;
1286 
1287   PetscCall(PetscSFGatherBegin(sf, MPIU_INT, leafdata, mrootdata));
1288   PetscCall(PetscSFGatherEnd(sf, MPIU_INT, leafdata, mrootdata));
1289 
1290   /* from multi-roots to multi-leaves */
1291   PetscCall(PetscSFCreate(comm, &sf2));
1292   PetscCall(PetscSFSetGraph(sf2, mnroots, newnleaves, NULL, PETSC_OWN_POINTER, newsfnode, PETSC_OWN_POINTER));
1293   PetscCall(PetscSFSetUp(sf2));
1294 
1295   /* broadcast multi-root data to multi-leaves */
1296   PetscCall(PetscMalloc1(newnleaves, &newleafdata));
1297   PetscCall(PetscSFBcastBegin(sf2, MPIU_INT, mrootdata, newleafdata, MPI_REPLACE));
1298   PetscCall(PetscSFBcastEnd(sf2, MPIU_INT, mrootdata, newleafdata, MPI_REPLACE));
1299 
1300   /* sort sharing ranks */
1301   for (i = 0, m = 0; i < nleaves; i++) {
1302     PetscCall(PetscSortInt(leafrd[i], newleafdata + m));
1303     m += leafrd[i];
1304   }
1305 
1306   /* Number of neighbors and their ranks */
1307   PetscCall(PetscHMapICreate(&neighs));
1308   for (i = 0; i < newnleaves; i++) PetscCall(PetscHMapIPut(neighs, newleafdata[i], &iter, &missing));
1309   PetscCall(PetscHMapIGetSize(neighs, &mapping->info_nproc));
1310   PetscCall(PetscMalloc1(mapping->info_nproc + 1, &mapping->info_procs));
1311   PetscCall(PetscHMapIGetKeys(neighs, (i = 0, &i), mapping->info_procs));
1312   for (i = 0; i < mapping->info_nproc; i++) { /* put info for self first */
1313     if (mapping->info_procs[i] == rank) {
1314       PetscInt newr = mapping->info_procs[0];
1315 
1316       mapping->info_procs[0] = rank;
1317       mapping->info_procs[i] = newr;
1318       break;
1319     }
1320   }
1321   if (mapping->info_nproc) PetscCall(PetscSortInt(mapping->info_nproc - 1, mapping->info_procs + 1));
1322   PetscCall(PetscHMapIDestroy(&neighs));
1323 
1324   /* collect info data */
1325   PetscCall(PetscMalloc1(mapping->info_nproc, &mapping->info_numprocs));
1326   PetscCall(PetscMalloc1(mapping->info_nproc, &mapping->info_indices));
1327   for (i = 0; i < mapping->info_nproc; i++) mapping->info_indices[i] = NULL;
1328 
1329   PetscCall(PetscMalloc1(nleaves, &mask));
1330   PetscCall(PetscMalloc1(nleaves, &tmpg));
1331   for (p = 0; p < mapping->info_nproc; p++) {
1332     PetscInt *tmp, trank = mapping->info_procs[p];
1333 
1334     PetscCall(PetscMemzero(mask, nleaves * sizeof(*mask)));
1335     for (i = 0, m = 0; i < nleaves; i++) {
1336       for (j = 0; j < leafrd[i]; j++) {
1337         if (newleafdata[m] == trank) mask[i]++;
1338         if (!p && newleafdata[m] != rank) mask[i]++;
1339         m++;
1340       }
1341     }
1342     for (i = 0, m = 0; i < nleaves; i++)
1343       if (mask[i] > (!p ? 1 : 0)) m++;
1344 
1345     PetscCall(PetscMalloc1(m, &tmp));
1346     for (i = 0, m = 0; i < nleaves; i++)
1347       if (mask[i] > (!p ? 1 : 0)) {
1348         tmp[m]  = i;
1349         tmpg[m] = gidxs[i];
1350         m++;
1351       }
1352     PetscCall(PetscSortIntWithArray(m, tmpg, tmp));
1353     mapping->info_indices[p]  = tmp;
1354     mapping->info_numprocs[p] = m;
1355   }
1356 
1357   /* Node info */
1358   PetscCall(PetscMalloc2(nleaves, &mapping->info_nodec, nleaves + 1, &mapping->info_nodei));
1359   PetscCall(PetscArraycpy(mapping->info_nodec, leafrd, nleaves));
1360   PetscCall(PetscMalloc1(newnleaves, &mapping->info_nodei[0]));
1361   for (i = 0; i < nleaves - 1; i++) mapping->info_nodei[i + 1] = mapping->info_nodei[i] + mapping->info_nodec[i];
1362   PetscCall(PetscArraycpy(mapping->info_nodei[0], newleafdata, newnleaves));
1363 
1364   /* Create SF from leaves to multi-leaves */
1365   PetscCall(PetscSFGetMultiSF(sf, &msf));
1366   PetscCall(PetscSFCreateInverseSF(msf, &imsf));
1367   PetscCall(PetscSFCompose(imsf, sf2, &mapping->multileaves_sf));
1368   PetscCall(PetscSFDestroy(&imsf));
1369   PetscCall(PetscSFDestroy(&sf));
1370   PetscCall(PetscSFDestroy(&sf2));
1371 
1372   PetscCall(ISLocalToGlobalMappingRestoreBlockIndices(mapping, &gidxs));
1373   PetscCall(PetscFree(tmpg));
1374   PetscCall(PetscFree(mask));
1375   PetscCall(PetscFree3(mrootdata, leafdata, leafrd));
1376   PetscCall(PetscFree(newleafdata));
1377   PetscFunctionReturn(PETSC_SUCCESS);
1378 }
1379 
1380 /*@C
1381   ISLocalToGlobalMappingRestoreBlockInfo - Frees the memory allocated by `ISLocalToGlobalMappingGetBlockInfo()`
1382 
1383   Not Collective
1384 
1385   Input Parameters:
1386 + mapping  - the mapping from local to global indexing
1387 . nproc    - number of processes that are connected to the calling process
1388 . procs    - neighboring processes
1389 . numprocs - number of block indices for each process
1390 - indices  - block indices (in local numbering) shared with neighbors (sorted by global numbering)
1391 
1392   Level: advanced
1393 
1394 .seealso: [](sec_scatter), `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1395           `ISLocalToGlobalMappingGetInfo()`
1396 @*/
1397 PetscErrorCode ISLocalToGlobalMappingRestoreBlockInfo(ISLocalToGlobalMapping mapping, PetscInt *nproc, PetscInt *procs[], PetscInt *numprocs[], PetscInt **indices[])
1398 {
1399   PetscFunctionBegin;
1400   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1401   if (nproc) *nproc = 0;
1402   if (procs) *procs = NULL;
1403   if (numprocs) *numprocs = NULL;
1404   if (indices) *indices = NULL;
1405   PetscFunctionReturn(PETSC_SUCCESS);
1406 }
1407 
1408 /*@C
1409   ISLocalToGlobalMappingGetInfo - Gets the neighbor information for each process
1410 
1411   Collective the first time it is called
1412 
1413   Input Parameter:
1414 . mapping - the mapping from local to global indexing
1415 
1416   Output Parameters:
1417 + nproc    - number of processes that are connected to the calling process
1418 . procs    - neighboring processes
1419 . numprocs - number of indices for each process
1420 - indices  - indices (in local numbering) shared with neighbors (sorted by global numbering)
1421 
1422   Level: advanced
1423 
1424   Note:
1425   The user needs to call `ISLocalToGlobalMappingRestoreInfo()` when the data is no longer needed.
1426 
1427   Fortran Notes:
1428   There is no `ISLocalToGlobalMappingRestoreInfo()` in Fortran. You must make sure that
1429   `procs`[], `numprocs`[] and `indices`[][] are large enough arrays, either by allocating them
1430   dynamically or defining static ones large enough.
1431 
1432 .seealso: [](sec_scatter), `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1433           `ISLocalToGlobalMappingRestoreInfo()`, `ISLocalToGlobalMappingGetNodeInfo()`
1434 @*/
1435 PetscErrorCode ISLocalToGlobalMappingGetInfo(ISLocalToGlobalMapping mapping, PetscInt *nproc, PetscInt *procs[], PetscInt *numprocs[], PetscInt **indices[])
1436 {
1437   PetscInt **bindices = NULL, *bnumprocs = NULL, bs, i, j, k, n, *bprocs;
1438 
1439   PetscFunctionBegin;
1440   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1441   bs = mapping->bs;
1442   PetscCall(ISLocalToGlobalMappingGetBlockInfo(mapping, &n, &bprocs, &bnumprocs, &bindices));
1443   if (bs > 1) { /* we need to expand the cached info */
1444     if (indices) PetscCall(PetscCalloc1(n, indices));
1445     if (numprocs) PetscCall(PetscCalloc1(n, numprocs));
1446     if (indices || numprocs) {
1447       for (i = 0; i < n; i++) {
1448         if (indices) {
1449           PetscCall(PetscMalloc1(bs * bnumprocs[i], &(*indices)[i]));
1450           for (j = 0; j < bnumprocs[i]; j++) {
1451             for (k = 0; k < bs; k++) (*indices)[i][j * bs + k] = bs * bindices[i][j] + k;
1452           }
1453         }
1454         if (numprocs) (*numprocs)[i] = bnumprocs[i] * bs;
1455       }
1456     }
1457   } else {
1458     if (numprocs) *numprocs = bnumprocs;
1459     if (indices) *indices = bindices;
1460   }
1461   if (nproc) *nproc = n;
1462   if (procs) *procs = bprocs;
1463   PetscFunctionReturn(PETSC_SUCCESS);
1464 }
1465 
1466 /*@C
1467   ISLocalToGlobalMappingRestoreInfo - Frees the memory allocated by `ISLocalToGlobalMappingGetInfo()`
1468 
1469   Not Collective
1470 
1471   Input Parameters:
1472 + mapping  - the mapping from local to global indexing
1473 . nproc    - number of processes that are connected to the calling process
1474 . procs    - neighboring processes
1475 . numprocs - number of indices for each process
1476 - indices  - indices (in local numbering) shared with neighbors (sorted by global numbering)
1477 
1478   Level: advanced
1479 
1480 .seealso: [](sec_scatter), `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1481           `ISLocalToGlobalMappingGetInfo()`
1482 @*/
1483 PetscErrorCode ISLocalToGlobalMappingRestoreInfo(ISLocalToGlobalMapping mapping, PetscInt *nproc, PetscInt *procs[], PetscInt *numprocs[], PetscInt **indices[])
1484 {
1485   PetscFunctionBegin;
1486   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1487   if (mapping->bs > 1) {
1488     if (numprocs) PetscCall(PetscFree(*numprocs));
1489     if (indices) {
1490       if (*indices)
1491         for (PetscInt i = 0; i < *nproc; i++) PetscCall(PetscFree((*indices)[i]));
1492       PetscCall(PetscFree(*indices));
1493     }
1494   }
1495   PetscFunctionReturn(PETSC_SUCCESS);
1496 }
1497 
1498 /*@C
1499   ISLocalToGlobalMappingGetNodeInfo - Gets the neighbor information of local nodes
1500 
1501   Collective the first time it is called
1502 
1503   Input Parameter:
1504 . mapping - the mapping from local to global indexing
1505 
1506   Output Parameters:
1507 + n       - number of local nodes
1508 . n_procs - an array storing the number of processes for each local node (including self)
1509 - procs   - the processes' rank for each local node (sorted, self is first)
1510 
1511   Level: advanced
1512 
1513   Note:
1514   The user needs to call `ISLocalToGlobalMappingRestoreNodeInfo()` when the data is no longer needed.
1515 
1516 .seealso: [](sec_scatter), `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1517           `ISLocalToGlobalMappingGetInfo()`, `ISLocalToGlobalMappingRestoreNodeInfo()`, `ISLocalToGlobalMappingGetBlockNodeInfo()`
1518 @*/
1519 PetscErrorCode ISLocalToGlobalMappingGetNodeInfo(ISLocalToGlobalMapping mapping, PetscInt *n, PetscInt *n_procs[], PetscInt **procs[])
1520 {
1521   PetscInt **bprocs = NULL, *bn_procs = NULL, bs, i, j, k, bn;
1522 
1523   PetscFunctionBegin;
1524   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1525   bs = mapping->bs;
1526   PetscCall(ISLocalToGlobalMappingGetBlockNodeInfo(mapping, &bn, &bn_procs, &bprocs));
1527   if (bs > 1) { /* we need to expand the cached info */
1528     PetscInt *tn_procs;
1529     PetscInt  c;
1530 
1531     PetscCall(PetscMalloc1(bn * bs, &tn_procs));
1532     for (i = 0, c = 0; i < bn; i++) {
1533       for (k = 0; k < bs; k++) tn_procs[i * bs + k] = bn_procs[i];
1534       c += bs * bn_procs[i];
1535     }
1536     if (n) *n = bn * bs;
1537     if (procs) {
1538       PetscInt **tprocs;
1539       PetscInt   tn = bn * bs;
1540 
1541       PetscCall(PetscMalloc1(tn, &tprocs));
1542       if (tn) PetscCall(PetscMalloc1(c, &tprocs[0]));
1543       for (i = 0; i < tn - 1; i++) tprocs[i + 1] = tprocs[i] + tn_procs[i];
1544       for (i = 0; i < bn; i++) {
1545         for (k = 0; k < bs; k++) {
1546           for (j = 0; j < bn_procs[i]; j++) tprocs[i * bs + k][j] = bprocs[i][j];
1547         }
1548       }
1549       *procs = tprocs;
1550     }
1551     if (n_procs) *n_procs = tn_procs;
1552     else PetscCall(PetscFree(tn_procs));
1553   } else {
1554     if (n) *n = bn;
1555     if (n_procs) *n_procs = bn_procs;
1556     if (procs) *procs = bprocs;
1557   }
1558   PetscFunctionReturn(PETSC_SUCCESS);
1559 }
1560 
1561 /*@C
1562   ISLocalToGlobalMappingRestoreNodeInfo - Frees the memory allocated by `ISLocalToGlobalMappingGetNodeInfo()`
1563 
1564   Not Collective
1565 
1566   Input Parameters:
1567 + mapping - the mapping from local to global indexing
1568 . n       - number of local nodes
1569 . n_procs - an array storing the number of processes for each local node (including self)
1570 - procs   - the processes' rank for each local node (sorted, self is first)
1571 
1572   Level: advanced
1573 
1574 .seealso: [](sec_scatter), `ISLocalToGlobalMappingDestroy()`, `ISLocalToGlobalMappingCreateIS()`, `ISLocalToGlobalMappingCreate()`,
1575           `ISLocalToGlobalMappingGetInfo()`
1576 @*/
1577 PetscErrorCode ISLocalToGlobalMappingRestoreNodeInfo(ISLocalToGlobalMapping mapping, PetscInt *n, PetscInt *n_procs[], PetscInt **procs[])
1578 {
1579   PetscFunctionBegin;
1580   PetscValidHeaderSpecific(mapping, IS_LTOGM_CLASSID, 1);
1581   if (mapping->bs > 1) {
1582     if (n_procs) PetscCall(PetscFree(*n_procs));
1583     if (procs) {
1584       if (*procs) PetscCall(PetscFree((*procs)[0]));
1585       PetscCall(PetscFree(*procs));
1586     }
1587   }
1588   PetscCall(ISLocalToGlobalMappingRestoreBlockNodeInfo(mapping, n, n_procs, procs));
1589   PetscFunctionReturn(PETSC_SUCCESS);
1590 }
1591 
1592 /*@C
1593   ISLocalToGlobalMappingGetIndices - Get global indices for every local point that is mapped
1594 
1595   Not Collective
1596 
1597   Input Parameter:
1598 . ltog - local to global mapping
1599 
1600   Output Parameter:
1601 . array - array of indices, the length of this array may be obtained with `ISLocalToGlobalMappingGetSize()`
1602 
1603   Level: advanced
1604 
1605   Note:
1606   `ISLocalToGlobalMappingGetSize()` returns the length the this array
1607 
1608 .seealso: [](sec_scatter), `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingApply()`, `ISLocalToGlobalMappingRestoreIndices()`,
1609           `ISLocalToGlobalMappingGetBlockIndices()`, `ISLocalToGlobalMappingRestoreBlockIndices()`
1610 @*/
1611 PetscErrorCode ISLocalToGlobalMappingGetIndices(ISLocalToGlobalMapping ltog, const PetscInt *array[])
1612 {
1613   PetscFunctionBegin;
1614   PetscValidHeaderSpecific(ltog, IS_LTOGM_CLASSID, 1);
1615   PetscAssertPointer(array, 2);
1616   if (ltog->bs == 1) {
1617     *array = ltog->indices;
1618   } else {
1619     PetscInt       *jj, k, i, j, n = ltog->n, bs = ltog->bs;
1620     const PetscInt *ii;
1621 
1622     PetscCall(PetscMalloc1(bs * n, &jj));
1623     *array = jj;
1624     k      = 0;
1625     ii     = ltog->indices;
1626     for (i = 0; i < n; i++)
1627       for (j = 0; j < bs; j++) jj[k++] = bs * ii[i] + j;
1628   }
1629   PetscFunctionReturn(PETSC_SUCCESS);
1630 }
1631 
1632 /*@C
1633   ISLocalToGlobalMappingRestoreIndices - Restore indices obtained with `ISLocalToGlobalMappingGetIndices()`
1634 
1635   Not Collective
1636 
1637   Input Parameters:
1638 + ltog  - local to global mapping
1639 - array - array of indices
1640 
1641   Level: advanced
1642 
1643 .seealso: [](sec_scatter), `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingApply()`, `ISLocalToGlobalMappingGetIndices()`
1644 @*/
1645 PetscErrorCode ISLocalToGlobalMappingRestoreIndices(ISLocalToGlobalMapping ltog, const PetscInt *array[])
1646 {
1647   PetscFunctionBegin;
1648   PetscValidHeaderSpecific(ltog, IS_LTOGM_CLASSID, 1);
1649   PetscAssertPointer(array, 2);
1650   PetscCheck(ltog->bs != 1 || *array == ltog->indices, PETSC_COMM_SELF, PETSC_ERR_ARG_BADPTR, "Trying to return mismatched pointer");
1651   if (ltog->bs > 1) PetscCall(PetscFree(*(void **)array));
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 /*@C
1656   ISLocalToGlobalMappingGetBlockIndices - Get global indices for every local block in a `ISLocalToGlobalMapping`
1657 
1658   Not Collective
1659 
1660   Input Parameter:
1661 . ltog - local to global mapping
1662 
1663   Output Parameter:
1664 . array - array of indices
1665 
1666   Level: advanced
1667 
1668 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingApply()`,
1669           `ISLocalToGlobalMappingRestoreBlockIndices()`
1670 @*/
1671 PetscErrorCode ISLocalToGlobalMappingGetBlockIndices(ISLocalToGlobalMapping ltog, const PetscInt *array[])
1672 {
1673   PetscFunctionBegin;
1674   PetscValidHeaderSpecific(ltog, IS_LTOGM_CLASSID, 1);
1675   PetscAssertPointer(array, 2);
1676   *array = ltog->indices;
1677   PetscFunctionReturn(PETSC_SUCCESS);
1678 }
1679 
1680 /*@C
1681   ISLocalToGlobalMappingRestoreBlockIndices - Restore indices obtained with `ISLocalToGlobalMappingGetBlockIndices()`
1682 
1683   Not Collective
1684 
1685   Input Parameters:
1686 + ltog  - local to global mapping
1687 - array - array of indices
1688 
1689   Level: advanced
1690 
1691 .seealso: [](sec_scatter), `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingApply()`, `ISLocalToGlobalMappingGetIndices()`
1692 @*/
1693 PetscErrorCode ISLocalToGlobalMappingRestoreBlockIndices(ISLocalToGlobalMapping ltog, const PetscInt *array[])
1694 {
1695   PetscFunctionBegin;
1696   PetscValidHeaderSpecific(ltog, IS_LTOGM_CLASSID, 1);
1697   PetscAssertPointer(array, 2);
1698   PetscCheck(*array == ltog->indices, PETSC_COMM_SELF, PETSC_ERR_ARG_BADPTR, "Trying to return mismatched pointer");
1699   *array = NULL;
1700   PetscFunctionReturn(PETSC_SUCCESS);
1701 }
1702 
1703 /*@
1704   ISLocalToGlobalMappingConcatenate - Create a new mapping that concatenates a list of mappings
1705 
1706   Not Collective
1707 
1708   Input Parameters:
1709 + comm  - communicator for the new mapping, must contain the communicator of every mapping to concatenate
1710 . n     - number of mappings to concatenate
1711 - ltogs - local to global mappings
1712 
1713   Output Parameter:
1714 . ltogcat - new mapping
1715 
1716   Level: advanced
1717 
1718   Note:
1719   This currently always returns a mapping with block size of 1
1720 
1721   Developer Notes:
1722   If all the input mapping have the same block size we could easily handle that as a special case
1723 
1724 .seealso: [](sec_scatter), `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingCreate()`
1725 @*/
1726 PetscErrorCode ISLocalToGlobalMappingConcatenate(MPI_Comm comm, PetscInt n, const ISLocalToGlobalMapping ltogs[], ISLocalToGlobalMapping *ltogcat)
1727 {
1728   PetscInt i, cnt, m, *idx;
1729 
1730   PetscFunctionBegin;
1731   PetscCheck(n >= 0, comm, PETSC_ERR_ARG_OUTOFRANGE, "Must have a non-negative number of mappings, given %" PetscInt_FMT, n);
1732   if (n > 0) PetscAssertPointer(ltogs, 3);
1733   for (i = 0; i < n; i++) PetscValidHeaderSpecific(ltogs[i], IS_LTOGM_CLASSID, 3);
1734   PetscAssertPointer(ltogcat, 4);
1735   for (cnt = 0, i = 0; i < n; i++) {
1736     PetscCall(ISLocalToGlobalMappingGetSize(ltogs[i], &m));
1737     cnt += m;
1738   }
1739   PetscCall(PetscMalloc1(cnt, &idx));
1740   for (cnt = 0, i = 0; i < n; i++) {
1741     const PetscInt *subidx;
1742     PetscCall(ISLocalToGlobalMappingGetSize(ltogs[i], &m));
1743     PetscCall(ISLocalToGlobalMappingGetIndices(ltogs[i], &subidx));
1744     PetscCall(PetscArraycpy(&idx[cnt], subidx, m));
1745     PetscCall(ISLocalToGlobalMappingRestoreIndices(ltogs[i], &subidx));
1746     cnt += m;
1747   }
1748   PetscCall(ISLocalToGlobalMappingCreate(comm, 1, cnt, idx, PETSC_OWN_POINTER, ltogcat));
1749   PetscFunctionReturn(PETSC_SUCCESS);
1750 }
1751 
1752 /*MC
1753       ISLOCALTOGLOBALMAPPINGBASIC - basic implementation of the `ISLocalToGlobalMapping` object. When `ISGlobalToLocalMappingApply()` is
1754                                     used this is good for only small and moderate size problems.
1755 
1756    Options Database Key:
1757 .   -islocaltoglobalmapping_type basic - select this method
1758 
1759    Level: beginner
1760 
1761    Developer Note:
1762    This stores all the mapping information on each MPI rank.
1763 
1764 .seealso: [](sec_scatter), `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingSetType()`, `ISLOCALTOGLOBALMAPPINGHASH`
1765 M*/
1766 PETSC_EXTERN PetscErrorCode ISLocalToGlobalMappingCreate_Basic(ISLocalToGlobalMapping ltog)
1767 {
1768   PetscFunctionBegin;
1769   ltog->ops->globaltolocalmappingapply      = ISGlobalToLocalMappingApply_Basic;
1770   ltog->ops->globaltolocalmappingsetup      = ISGlobalToLocalMappingSetUp_Basic;
1771   ltog->ops->globaltolocalmappingapplyblock = ISGlobalToLocalMappingApplyBlock_Basic;
1772   ltog->ops->destroy                        = ISLocalToGlobalMappingDestroy_Basic;
1773   PetscFunctionReturn(PETSC_SUCCESS);
1774 }
1775 
1776 /*MC
1777       ISLOCALTOGLOBALMAPPINGHASH - hash implementation of the `ISLocalToGlobalMapping` object. When `ISGlobalToLocalMappingApply()` is
1778                                     used this is good for large memory problems.
1779 
1780    Options Database Key:
1781 .   -islocaltoglobalmapping_type hash - select this method
1782 
1783    Level: beginner
1784 
1785    Note:
1786     This is selected automatically for large problems if the user does not set the type.
1787 
1788 .seealso: [](sec_scatter), `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingSetType()`, `ISLOCALTOGLOBALMAPPINGBASIC`
1789 M*/
1790 PETSC_EXTERN PetscErrorCode ISLocalToGlobalMappingCreate_Hash(ISLocalToGlobalMapping ltog)
1791 {
1792   PetscFunctionBegin;
1793   ltog->ops->globaltolocalmappingapply      = ISGlobalToLocalMappingApply_Hash;
1794   ltog->ops->globaltolocalmappingsetup      = ISGlobalToLocalMappingSetUp_Hash;
1795   ltog->ops->globaltolocalmappingapplyblock = ISGlobalToLocalMappingApplyBlock_Hash;
1796   ltog->ops->destroy                        = ISLocalToGlobalMappingDestroy_Hash;
1797   PetscFunctionReturn(PETSC_SUCCESS);
1798 }
1799 
1800 /*@C
1801   ISLocalToGlobalMappingRegister -  Registers a method for applying a global to local mapping with an `ISLocalToGlobalMapping`
1802 
1803   Not Collective, No Fortran Support
1804 
1805   Input Parameters:
1806 + sname    - name of a new method
1807 - function - routine to create method context
1808 
1809   Example Usage:
1810 .vb
1811    ISLocalToGlobalMappingRegister("my_mapper", MyCreate);
1812 .ve
1813 
1814   Then, your mapping can be chosen with the procedural interface via
1815 .vb
1816   ISLocalToGlobalMappingSetType(ltog, "my_mapper")
1817 .ve
1818   or at runtime via the option
1819 .vb
1820   -islocaltoglobalmapping_type my_mapper
1821 .ve
1822 
1823   Level: advanced
1824 
1825   Note:
1826   `ISLocalToGlobalMappingRegister()` may be called multiple times to add several user-defined mappings.
1827 
1828 .seealso: [](sec_scatter), `ISLocalToGlobalMappingRegisterAll()`, `ISLocalToGlobalMappingRegisterDestroy()`, `ISLOCALTOGLOBALMAPPINGBASIC`,
1829           `ISLOCALTOGLOBALMAPPINGHASH`, `ISLocalToGlobalMapping`, `ISLocalToGlobalMappingApply()`
1830 @*/
1831 PetscErrorCode ISLocalToGlobalMappingRegister(const char sname[], PetscErrorCode (*function)(ISLocalToGlobalMapping))
1832 {
1833   PetscFunctionBegin;
1834   PetscCall(ISInitializePackage());
1835   PetscCall(PetscFunctionListAdd(&ISLocalToGlobalMappingList, sname, function));
1836   PetscFunctionReturn(PETSC_SUCCESS);
1837 }
1838 
1839 /*@
1840   ISLocalToGlobalMappingSetType - Sets the implementation type `ISLocalToGlobalMapping` will use
1841 
1842   Logically Collective
1843 
1844   Input Parameters:
1845 + ltog - the `ISLocalToGlobalMapping` object
1846 - type - a known method
1847 
1848   Options Database Key:
1849 . -islocaltoglobalmapping_type  <method> - Sets the method; use -help for a list of available methods (for instance, basic or hash)
1850 
1851   Level: intermediate
1852 
1853   Notes:
1854   See `ISLocalToGlobalMappingType` for available methods
1855 
1856   Normally, it is best to use the `ISLocalToGlobalMappingSetFromOptions()` command and
1857   then set the `ISLocalToGlobalMappingType` from the options database rather than by using
1858   this routine.
1859 
1860   Developer Notes:
1861   `ISLocalToGlobalMappingRegister()` is used to add new types to `ISLocalToGlobalMappingList` from which they
1862   are accessed by `ISLocalToGlobalMappingSetType()`.
1863 
1864 .seealso: [](sec_scatter), `ISLocalToGlobalMappingType`, `ISLocalToGlobalMappingRegister()`, `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingGetType()`
1865 @*/
1866 PetscErrorCode ISLocalToGlobalMappingSetType(ISLocalToGlobalMapping ltog, ISLocalToGlobalMappingType type)
1867 {
1868   PetscBool match;
1869   PetscErrorCode (*r)(ISLocalToGlobalMapping) = NULL;
1870 
1871   PetscFunctionBegin;
1872   PetscValidHeaderSpecific(ltog, IS_LTOGM_CLASSID, 1);
1873   if (type) PetscAssertPointer(type, 2);
1874 
1875   PetscCall(PetscObjectTypeCompare((PetscObject)ltog, type, &match));
1876   if (match) PetscFunctionReturn(PETSC_SUCCESS);
1877 
1878   /* L2G maps defer type setup at globaltolocal calls, allow passing NULL here */
1879   if (type) {
1880     PetscCall(PetscFunctionListFind(ISLocalToGlobalMappingList, type, &r));
1881     PetscCheck(r, PetscObjectComm((PetscObject)ltog), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unable to find requested ISLocalToGlobalMapping type %s", type);
1882   }
1883   /* Destroy the previous private LTOG context */
1884   PetscTryTypeMethod(ltog, destroy);
1885   ltog->ops->destroy = NULL;
1886 
1887   PetscCall(PetscObjectChangeTypeName((PetscObject)ltog, type));
1888   if (r) PetscCall((*r)(ltog));
1889   PetscFunctionReturn(PETSC_SUCCESS);
1890 }
1891 
1892 /*@
1893   ISLocalToGlobalMappingGetType - Get the type of the `ISLocalToGlobalMapping`
1894 
1895   Not Collective
1896 
1897   Input Parameter:
1898 . ltog - the `ISLocalToGlobalMapping` object
1899 
1900   Output Parameter:
1901 . type - the type
1902 
1903   Level: intermediate
1904 
1905 .seealso: [](sec_scatter), `ISLocalToGlobalMappingType`, `ISLocalToGlobalMappingRegister()`, `ISLocalToGlobalMappingCreate()`, `ISLocalToGlobalMappingSetType()`
1906 @*/
1907 PetscErrorCode ISLocalToGlobalMappingGetType(ISLocalToGlobalMapping ltog, ISLocalToGlobalMappingType *type)
1908 {
1909   PetscFunctionBegin;
1910   PetscValidHeaderSpecific(ltog, IS_LTOGM_CLASSID, 1);
1911   PetscAssertPointer(type, 2);
1912   *type = ((PetscObject)ltog)->type_name;
1913   PetscFunctionReturn(PETSC_SUCCESS);
1914 }
1915 
1916 PetscBool ISLocalToGlobalMappingRegisterAllCalled = PETSC_FALSE;
1917 
1918 /*@C
1919   ISLocalToGlobalMappingRegisterAll - Registers all of the local to global mapping components in the `IS` package.
1920 
1921   Not Collective
1922 
1923   Level: advanced
1924 
1925 .seealso: [](sec_scatter), `ISRegister()`, `ISLocalToGlobalRegister()`
1926 @*/
1927 PetscErrorCode ISLocalToGlobalMappingRegisterAll(void)
1928 {
1929   PetscFunctionBegin;
1930   if (ISLocalToGlobalMappingRegisterAllCalled) PetscFunctionReturn(PETSC_SUCCESS);
1931   ISLocalToGlobalMappingRegisterAllCalled = PETSC_TRUE;
1932   PetscCall(ISLocalToGlobalMappingRegister(ISLOCALTOGLOBALMAPPINGBASIC, ISLocalToGlobalMappingCreate_Basic));
1933   PetscCall(ISLocalToGlobalMappingRegister(ISLOCALTOGLOBALMAPPINGHASH, ISLocalToGlobalMappingCreate_Hash));
1934   PetscFunctionReturn(PETSC_SUCCESS);
1935 }
1936