xref: /petsc/src/dm/label/dmlabel.c (revision ffa8c5705e8ab2cf85ee1d14dbe507a6e2eb5283)
1 #include <petscdm.h>
2 #include <petsc/private/dmlabelimpl.h>   /*I      "petscdmlabel.h"   I*/
3 #include <petsc/private/sectionimpl.h>   /*I      "petscsection.h"   I*/
4 #include <petscsf.h>
5 #include <petscsection.h>
6 
7 /*@C
8   DMLabelCreate - Create a DMLabel object, which is a multimap
9 
10   Collective
11 
12   Input parameters:
13 + comm - The communicator, usually PETSC_COMM_SELF
14 - name - The label name
15 
16   Output parameter:
17 . label - The DMLabel
18 
19   Level: beginner
20 
21   Notes:
22   The label name is actually usual PetscObject name.
23   One can get/set it with PetscObjectGetName()/PetscObjectSetName().
24 
25 .seealso: DMLabelDestroy()
26 @*/
27 PetscErrorCode DMLabelCreate(MPI_Comm comm, const char name[], DMLabel *label)
28 {
29   PetscFunctionBegin;
30   PetscValidPointer(label,3);
31   PetscCall(DMInitializePackage());
32 
33   PetscCall(PetscHeaderCreate(*label,DMLABEL_CLASSID,"DMLabel","DMLabel","DM",comm,DMLabelDestroy,DMLabelView));
34 
35   (*label)->numStrata      = 0;
36   (*label)->defaultValue   = -1;
37   (*label)->stratumValues  = NULL;
38   (*label)->validIS        = NULL;
39   (*label)->stratumSizes   = NULL;
40   (*label)->points         = NULL;
41   (*label)->ht             = NULL;
42   (*label)->pStart         = -1;
43   (*label)->pEnd           = -1;
44   (*label)->bt             = NULL;
45   PetscCall(PetscHMapICreate(&(*label)->hmap));
46   PetscCall(PetscObjectSetName((PetscObject) *label, name));
47   PetscFunctionReturn(0);
48 }
49 
50 /*
51   DMLabelMakeValid_Private - Transfer stratum data from the hash format to the sorted list format
52 
53   Not collective
54 
55   Input parameter:
56 + label - The DMLabel
57 - v - The stratum value
58 
59   Output parameter:
60 . label - The DMLabel with stratum in sorted list format
61 
62   Level: developer
63 
64 .seealso: DMLabelCreate()
65 */
66 static PetscErrorCode DMLabelMakeValid_Private(DMLabel label, PetscInt v)
67 {
68   IS             is;
69   PetscInt       off = 0, *pointArray, p;
70 
71   if (PetscLikely(v >= 0 && v < label->numStrata) && label->validIS[v]) return 0;
72   PetscFunctionBegin;
73   PetscCheckFalse(v < 0 || v >= label->numStrata,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Trying to access invalid stratum %D in DMLabelMakeValid_Private", v);
74   PetscCall(PetscHSetIGetSize(label->ht[v], &label->stratumSizes[v]));
75   PetscCall(PetscMalloc1(label->stratumSizes[v], &pointArray));
76   PetscCall(PetscHSetIGetElems(label->ht[v], &off, pointArray));
77   PetscCall(PetscHSetIClear(label->ht[v]));
78   PetscCall(PetscSortInt(label->stratumSizes[v], pointArray));
79   if (label->bt) {
80     for (p = 0; p < label->stratumSizes[v]; ++p) {
81       const PetscInt point = pointArray[p];
82       PetscCheckFalse((point < label->pStart) || (point >= label->pEnd),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label point %D is not in [%D, %D)", point, label->pStart, label->pEnd);
83       PetscCall(PetscBTSet(label->bt, point - label->pStart));
84     }
85   }
86   if (label->stratumSizes[v] > 0 && pointArray[label->stratumSizes[v]-1] == pointArray[0] + label->stratumSizes[v]-1) {
87     PetscCall(ISCreateStride(PETSC_COMM_SELF, label->stratumSizes[v], pointArray[0], 1, &is));
88     PetscCall(PetscFree(pointArray));
89   } else {
90     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, label->stratumSizes[v], pointArray, PETSC_OWN_POINTER, &is));
91   }
92   PetscCall(PetscObjectSetName((PetscObject) is, "indices"));
93   label->points[v]  = is;
94   label->validIS[v] = PETSC_TRUE;
95   PetscCall(PetscObjectStateIncrease((PetscObject) label));
96   PetscFunctionReturn(0);
97 }
98 
99 /*
100   DMLabelMakeAllValid_Private - Transfer all strata from the hash format to the sorted list format
101 
102   Not collective
103 
104   Input parameter:
105 . label - The DMLabel
106 
107   Output parameter:
108 . label - The DMLabel with all strata in sorted list format
109 
110   Level: developer
111 
112 .seealso: DMLabelCreate()
113 */
114 static PetscErrorCode DMLabelMakeAllValid_Private(DMLabel label)
115 {
116   PetscInt       v;
117 
118   PetscFunctionBegin;
119   for (v = 0; v < label->numStrata; v++) {
120     PetscCall(DMLabelMakeValid_Private(label, v));
121   }
122   PetscFunctionReturn(0);
123 }
124 
125 /*
126   DMLabelMakeInvalid_Private - Transfer stratum data from the sorted list format to the hash format
127 
128   Not collective
129 
130   Input parameter:
131 + label - The DMLabel
132 - v - The stratum value
133 
134   Output parameter:
135 . label - The DMLabel with stratum in hash format
136 
137   Level: developer
138 
139 .seealso: DMLabelCreate()
140 */
141 static PetscErrorCode DMLabelMakeInvalid_Private(DMLabel label, PetscInt v)
142 {
143   PetscInt       p;
144   const PetscInt *points;
145 
146   if (PetscLikely(v >= 0 && v < label->numStrata) && !label->validIS[v]) return 0;
147   PetscFunctionBegin;
148   PetscCheckFalse(v < 0 || v >= label->numStrata,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Trying to access invalid stratum %D in DMLabelMakeInvalid_Private", v);
149   if (label->points[v]) {
150     PetscCall(ISGetIndices(label->points[v], &points));
151     for (p = 0; p < label->stratumSizes[v]; ++p) {
152       PetscCall(PetscHSetIAdd(label->ht[v], points[p]));
153     }
154     PetscCall(ISRestoreIndices(label->points[v],&points));
155     PetscCall(ISDestroy(&(label->points[v])));
156   }
157   label->validIS[v] = PETSC_FALSE;
158   PetscFunctionReturn(0);
159 }
160 
161 #if !defined(DMLABEL_LOOKUP_THRESHOLD)
162 #define DMLABEL_LOOKUP_THRESHOLD 16
163 #endif
164 
165 static inline PetscErrorCode DMLabelLookupStratum(DMLabel label, PetscInt value, PetscInt *index)
166 {
167   PetscInt       v;
168 
169   PetscFunctionBegin;
170   *index = -1;
171   if (label->numStrata <= DMLABEL_LOOKUP_THRESHOLD) {
172     for (v = 0; v < label->numStrata; ++v)
173       if (label->stratumValues[v] == value) {*index = v; break;}
174   } else {
175     PetscCall(PetscHMapIGet(label->hmap, value, index));
176   }
177   if (PetscDefined(USE_DEBUG)) { /* Check strata hash map consistency */
178     PetscInt len, loc = -1;
179     PetscCall(PetscHMapIGetSize(label->hmap, &len));
180     PetscCheckFalse(len != label->numStrata,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Inconsistent strata hash map size");
181     if (label->numStrata <= DMLABEL_LOOKUP_THRESHOLD) {
182       PetscCall(PetscHMapIGet(label->hmap, value, &loc));
183     } else {
184       for (v = 0; v < label->numStrata; ++v)
185         if (label->stratumValues[v] == value) {loc = v; break;}
186     }
187     PetscCheckFalse(loc != *index,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Inconsistent strata hash map lookup");
188   }
189   PetscFunctionReturn(0);
190 }
191 
192 static inline PetscErrorCode DMLabelNewStratum(DMLabel label, PetscInt value, PetscInt *index)
193 {
194   PetscInt       v;
195   PetscInt      *tmpV;
196   PetscInt      *tmpS;
197   PetscHSetI    *tmpH, ht;
198   IS            *tmpP, is;
199   PetscBool     *tmpB;
200   PetscHMapI     hmap = label->hmap;
201 
202   PetscFunctionBegin;
203   v    = label->numStrata;
204   tmpV = label->stratumValues;
205   tmpS = label->stratumSizes;
206   tmpH = label->ht;
207   tmpP = label->points;
208   tmpB = label->validIS;
209   { /* TODO: PetscRealloc() is broken, use malloc+memcpy+free  */
210     PetscInt   *oldV = tmpV;
211     PetscInt   *oldS = tmpS;
212     PetscHSetI *oldH = tmpH;
213     IS         *oldP = tmpP;
214     PetscBool  *oldB = tmpB;
215     PetscCall(PetscMalloc((v+1)*sizeof(*tmpV), &tmpV));
216     PetscCall(PetscMalloc((v+1)*sizeof(*tmpS), &tmpS));
217     PetscCall(PetscMalloc((v+1)*sizeof(*tmpH), &tmpH));
218     PetscCall(PetscMalloc((v+1)*sizeof(*tmpP), &tmpP));
219     PetscCall(PetscMalloc((v+1)*sizeof(*tmpB), &tmpB));
220     PetscCall(PetscArraycpy(tmpV, oldV, v));
221     PetscCall(PetscArraycpy(tmpS, oldS, v));
222     PetscCall(PetscArraycpy(tmpH, oldH, v));
223     PetscCall(PetscArraycpy(tmpP, oldP, v));
224     PetscCall(PetscArraycpy(tmpB, oldB, v));
225     PetscCall(PetscFree(oldV));
226     PetscCall(PetscFree(oldS));
227     PetscCall(PetscFree(oldH));
228     PetscCall(PetscFree(oldP));
229     PetscCall(PetscFree(oldB));
230   }
231   label->numStrata     = v+1;
232   label->stratumValues = tmpV;
233   label->stratumSizes  = tmpS;
234   label->ht            = tmpH;
235   label->points        = tmpP;
236   label->validIS       = tmpB;
237   PetscCall(PetscHSetICreate(&ht));
238   PetscCall(ISCreateStride(PETSC_COMM_SELF,0,0,1,&is));
239   PetscCall(PetscHMapISet(hmap, value, v));
240   tmpV[v] = value;
241   tmpS[v] = 0;
242   tmpH[v] = ht;
243   tmpP[v] = is;
244   tmpB[v] = PETSC_TRUE;
245   PetscCall(PetscObjectStateIncrease((PetscObject) label));
246   *index = v;
247   PetscFunctionReturn(0);
248 }
249 
250 static inline PetscErrorCode DMLabelLookupAddStratum(DMLabel label, PetscInt value, PetscInt *index)
251 {
252   PetscFunctionBegin;
253   PetscCall(DMLabelLookupStratum(label, value, index));
254   if (*index < 0) PetscCall(DMLabelNewStratum(label, value, index));
255   PetscFunctionReturn(0);
256 }
257 
258 static inline PetscErrorCode DMLabelGetStratumSize_Private(DMLabel label, PetscInt v, PetscInt *size)
259 {
260   PetscFunctionBegin;
261   *size = 0;
262   if (v < 0) PetscFunctionReturn(0);
263   if (label->validIS[v]) {
264     *size = label->stratumSizes[v];
265   } else {
266     PetscCall(PetscHSetIGetSize(label->ht[v], size));
267   }
268   PetscFunctionReturn(0);
269 }
270 
271 /*@
272   DMLabelAddStratum - Adds a new stratum value in a DMLabel
273 
274   Input Parameters:
275 + label - The DMLabel
276 - value - The stratum value
277 
278   Level: beginner
279 
280 .seealso:  DMLabelCreate(), DMLabelDestroy()
281 @*/
282 PetscErrorCode DMLabelAddStratum(DMLabel label, PetscInt value)
283 {
284   PetscInt       v;
285 
286   PetscFunctionBegin;
287   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
288   PetscCall(DMLabelLookupAddStratum(label, value, &v));
289   PetscFunctionReturn(0);
290 }
291 
292 /*@
293   DMLabelAddStrata - Adds new stratum values in a DMLabel
294 
295   Not collective
296 
297   Input Parameters:
298 + label - The DMLabel
299 . numStrata - The number of stratum values
300 - stratumValues - The stratum values
301 
302   Level: beginner
303 
304 .seealso:  DMLabelCreate(), DMLabelDestroy()
305 @*/
306 PetscErrorCode DMLabelAddStrata(DMLabel label, PetscInt numStrata, const PetscInt stratumValues[])
307 {
308   PetscInt       *values, v;
309 
310   PetscFunctionBegin;
311   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
312   if (numStrata) PetscValidIntPointer(stratumValues, 3);
313   PetscCall(PetscMalloc1(numStrata, &values));
314   PetscCall(PetscArraycpy(values, stratumValues, numStrata));
315   PetscCall(PetscSortRemoveDupsInt(&numStrata, values));
316   if (!label->numStrata) { /* Fast preallocation */
317     PetscInt   *tmpV;
318     PetscInt   *tmpS;
319     PetscHSetI *tmpH, ht;
320     IS         *tmpP, is;
321     PetscBool  *tmpB;
322     PetscHMapI  hmap = label->hmap;
323 
324     PetscCall(PetscMalloc1(numStrata, &tmpV));
325     PetscCall(PetscMalloc1(numStrata, &tmpS));
326     PetscCall(PetscMalloc1(numStrata, &tmpH));
327     PetscCall(PetscMalloc1(numStrata, &tmpP));
328     PetscCall(PetscMalloc1(numStrata, &tmpB));
329     label->numStrata     = numStrata;
330     label->stratumValues = tmpV;
331     label->stratumSizes  = tmpS;
332     label->ht            = tmpH;
333     label->points        = tmpP;
334     label->validIS       = tmpB;
335     for (v = 0; v < numStrata; ++v) {
336       PetscCall(PetscHSetICreate(&ht));
337       PetscCall(ISCreateStride(PETSC_COMM_SELF,0,0,1,&is));
338       PetscCall(PetscHMapISet(hmap, values[v], v));
339       tmpV[v] = values[v];
340       tmpS[v] = 0;
341       tmpH[v] = ht;
342       tmpP[v] = is;
343       tmpB[v] = PETSC_TRUE;
344     }
345     PetscCall(PetscObjectStateIncrease((PetscObject) label));
346   } else {
347     for (v = 0; v < numStrata; ++v) {
348       PetscCall(DMLabelAddStratum(label, values[v]));
349     }
350   }
351   PetscCall(PetscFree(values));
352   PetscFunctionReturn(0);
353 }
354 
355 /*@
356   DMLabelAddStrataIS - Adds new stratum values in a DMLabel
357 
358   Not collective
359 
360   Input Parameters:
361 + label - The DMLabel
362 - valueIS - Index set with stratum values
363 
364   Level: beginner
365 
366 .seealso:  DMLabelCreate(), DMLabelDestroy()
367 @*/
368 PetscErrorCode DMLabelAddStrataIS(DMLabel label, IS valueIS)
369 {
370   PetscInt       numStrata;
371   const PetscInt *stratumValues;
372 
373   PetscFunctionBegin;
374   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
375   PetscValidHeaderSpecific(valueIS, IS_CLASSID, 2);
376   PetscCall(ISGetLocalSize(valueIS, &numStrata));
377   PetscCall(ISGetIndices(valueIS, &stratumValues));
378   PetscCall(DMLabelAddStrata(label, numStrata, stratumValues));
379   PetscFunctionReturn(0);
380 }
381 
382 static PetscErrorCode DMLabelView_Ascii(DMLabel label, PetscViewer viewer)
383 {
384   PetscInt       v;
385   PetscMPIInt    rank;
386 
387   PetscFunctionBegin;
388   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
389   PetscCall(PetscViewerASCIIPushSynchronized(viewer));
390   if (label) {
391     const char *name;
392 
393     PetscCall(PetscObjectGetName((PetscObject) label, &name));
394     PetscCall(PetscViewerASCIIPrintf(viewer, "Label '%s':\n", name));
395     if (label->bt) PetscCall(PetscViewerASCIIPrintf(viewer, "  Index has been calculated in [%D, %D)\n", label->pStart, label->pEnd));
396     for (v = 0; v < label->numStrata; ++v) {
397       const PetscInt value = label->stratumValues[v];
398       const PetscInt *points;
399       PetscInt       p;
400 
401       PetscCall(ISGetIndices(label->points[v], &points));
402       for (p = 0; p < label->stratumSizes[v]; ++p) {
403         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %D (%D)\n", rank, points[p], value));
404       }
405       PetscCall(ISRestoreIndices(label->points[v],&points));
406     }
407   }
408   PetscCall(PetscViewerFlush(viewer));
409   PetscCall(PetscViewerASCIIPopSynchronized(viewer));
410   PetscFunctionReturn(0);
411 }
412 
413 /*@C
414   DMLabelView - View the label
415 
416   Collective on viewer
417 
418   Input Parameters:
419 + label - The DMLabel
420 - viewer - The PetscViewer
421 
422   Level: intermediate
423 
424 .seealso: DMLabelCreate(), DMLabelDestroy()
425 @*/
426 PetscErrorCode DMLabelView(DMLabel label, PetscViewer viewer)
427 {
428   PetscBool      iascii;
429 
430   PetscFunctionBegin;
431   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
432   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)label), &viewer));
433   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
434   if (label) PetscCall(DMLabelMakeAllValid_Private(label));
435   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii));
436   if (iascii) {
437     PetscCall(DMLabelView_Ascii(label, viewer));
438   }
439   PetscFunctionReturn(0);
440 }
441 
442 /*@
443   DMLabelReset - Destroys internal data structures in a DMLabel
444 
445   Not collective
446 
447   Input Parameter:
448 . label - The DMLabel
449 
450   Level: beginner
451 
452 .seealso: DMLabelDestroy(), DMLabelCreate()
453 @*/
454 PetscErrorCode DMLabelReset(DMLabel label)
455 {
456   PetscInt       v;
457 
458   PetscFunctionBegin;
459   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
460   for (v = 0; v < label->numStrata; ++v) {
461     PetscCall(PetscHSetIDestroy(&label->ht[v]));
462     PetscCall(ISDestroy(&label->points[v]));
463   }
464   label->numStrata = 0;
465   PetscCall(PetscFree(label->stratumValues));
466   PetscCall(PetscFree(label->stratumSizes));
467   PetscCall(PetscFree(label->ht));
468   PetscCall(PetscFree(label->points));
469   PetscCall(PetscFree(label->validIS));
470   label->stratumValues = NULL;
471   label->stratumSizes  = NULL;
472   label->ht            = NULL;
473   label->points        = NULL;
474   label->validIS       = NULL;
475   PetscCall(PetscHMapIReset(label->hmap));
476   label->pStart = -1;
477   label->pEnd   = -1;
478   PetscCall(PetscBTDestroy(&label->bt));
479   PetscFunctionReturn(0);
480 }
481 
482 /*@
483   DMLabelDestroy - Destroys a DMLabel
484 
485   Collective on label
486 
487   Input Parameter:
488 . label - The DMLabel
489 
490   Level: beginner
491 
492 .seealso: DMLabelReset(), DMLabelCreate()
493 @*/
494 PetscErrorCode DMLabelDestroy(DMLabel *label)
495 {
496   PetscFunctionBegin;
497   if (!*label) PetscFunctionReturn(0);
498   PetscValidHeaderSpecific((*label),DMLABEL_CLASSID,1);
499   if (--((PetscObject)(*label))->refct > 0) {*label = NULL; PetscFunctionReturn(0);}
500   PetscCall(DMLabelReset(*label));
501   PetscCall(PetscHMapIDestroy(&(*label)->hmap));
502   PetscCall(PetscHeaderDestroy(label));
503   PetscFunctionReturn(0);
504 }
505 
506 /*@
507   DMLabelDuplicate - Duplicates a DMLabel
508 
509   Collective on label
510 
511   Input Parameter:
512 . label - The DMLabel
513 
514   Output Parameter:
515 . labelnew - location to put new vector
516 
517   Level: intermediate
518 
519 .seealso: DMLabelCreate(), DMLabelDestroy()
520 @*/
521 PetscErrorCode DMLabelDuplicate(DMLabel label, DMLabel *labelnew)
522 {
523   const char    *name;
524   PetscInt       v;
525 
526   PetscFunctionBegin;
527   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
528   PetscCall(DMLabelMakeAllValid_Private(label));
529   PetscCall(PetscObjectGetName((PetscObject) label, &name));
530   PetscCall(DMLabelCreate(PetscObjectComm((PetscObject) label), name, labelnew));
531 
532   (*labelnew)->numStrata    = label->numStrata;
533   (*labelnew)->defaultValue = label->defaultValue;
534   PetscCall(PetscMalloc1(label->numStrata, &(*labelnew)->stratumValues));
535   PetscCall(PetscMalloc1(label->numStrata, &(*labelnew)->stratumSizes));
536   PetscCall(PetscMalloc1(label->numStrata, &(*labelnew)->ht));
537   PetscCall(PetscMalloc1(label->numStrata, &(*labelnew)->points));
538   PetscCall(PetscMalloc1(label->numStrata, &(*labelnew)->validIS));
539   for (v = 0; v < label->numStrata; ++v) {
540     PetscCall(PetscHSetICreate(&(*labelnew)->ht[v]));
541     (*labelnew)->stratumValues[v]  = label->stratumValues[v];
542     (*labelnew)->stratumSizes[v]   = label->stratumSizes[v];
543     PetscCall(PetscObjectReference((PetscObject) (label->points[v])));
544     (*labelnew)->points[v]         = label->points[v];
545     (*labelnew)->validIS[v]        = PETSC_TRUE;
546   }
547   PetscCall(PetscHMapIDestroy(&(*labelnew)->hmap));
548   PetscCall(PetscHMapIDuplicate(label->hmap,&(*labelnew)->hmap));
549   (*labelnew)->pStart = -1;
550   (*labelnew)->pEnd   = -1;
551   (*labelnew)->bt     = NULL;
552   PetscFunctionReturn(0);
553 }
554 
555 /*@C
556   DMLabelCompare - Compare two DMLabel objects
557 
558   Collective on comm
559 
560   Input Parameters:
561 + comm - Comm over which to compare labels
562 . l0 - First DMLabel
563 - l1 - Second DMLabel
564 
565   Output Parameters
566 + equal   - (Optional) Flag whether the two labels are equal
567 - message - (Optional) Message describing the difference
568 
569   Level: intermediate
570 
571   Notes:
572   The output flag equal is the same on all processes.
573   If it is passed as NULL and difference is found, an error is thrown on all processes.
574   Make sure to pass NULL on all processes.
575 
576   The output message is set independently on each rank.
577   It is set to NULL if no difference was found on the current rank. It must be freed by user.
578   If message is passed as NULL and difference is found, the difference description is printed to stderr in synchronized manner.
579   Make sure to pass NULL on all processes.
580 
581   For the comparison, we ignore the order of stratum values, and strata with no points.
582 
583   The communicator needs to be specified because currently DMLabel can live on PETSC_COMM_SELF even if the underlying DM is parallel.
584 
585   Fortran Notes:
586   This function is currently not available from Fortran.
587 
588 .seealso: DMCompareLabels(), DMLabelGetNumValues(), DMLabelGetDefaultValue(), DMLabelGetNonEmptyStratumValuesIS(), DMLabelGetStratumIS()
589 @*/
590 PetscErrorCode DMLabelCompare(MPI_Comm comm, DMLabel l0, DMLabel l1, PetscBool *equal, char **message)
591 {
592   const char     *name0, *name1;
593   char            msg[PETSC_MAX_PATH_LEN] = "";
594   PetscBool       eq;
595   PetscMPIInt     rank;
596 
597   PetscFunctionBegin;
598   PetscValidHeaderSpecific(l0, DMLABEL_CLASSID, 2);
599   PetscValidHeaderSpecific(l1, DMLABEL_CLASSID, 3);
600   if (equal) PetscValidBoolPointer(equal, 4);
601   if (message) PetscValidPointer(message, 5);
602   PetscCallMPI(MPI_Comm_rank(comm, &rank));
603   PetscCall(PetscObjectGetName((PetscObject)l0, &name0));
604   PetscCall(PetscObjectGetName((PetscObject)l1, &name1));
605   {
606     PetscInt v0, v1;
607 
608     PetscCall(DMLabelGetDefaultValue(l0, &v0));
609     PetscCall(DMLabelGetDefaultValue(l1, &v1));
610     eq = (PetscBool) (v0 == v1);
611     if (!eq) {
612       PetscCall(PetscSNPrintf(msg, sizeof(msg), "Default value of DMLabel l0 \"%s\" = %D != %D = Default value of DMLabel l1 \"%s\"", name0, v0, v1, name1));
613     }
614     PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, &eq, 1, MPIU_BOOL, MPI_LAND, comm));
615     if (!eq) goto finish;
616   }
617   {
618     IS              is0, is1;
619 
620     PetscCall(DMLabelGetNonEmptyStratumValuesIS(l0, &is0));
621     PetscCall(DMLabelGetNonEmptyStratumValuesIS(l1, &is1));
622     PetscCall(ISEqual(is0, is1, &eq));
623     PetscCall(ISDestroy(&is0));
624     PetscCall(ISDestroy(&is1));
625     if (!eq) {
626       PetscCall(PetscSNPrintf(msg, sizeof(msg), "Stratum values in DMLabel l0 \"%s\" are different than in DMLabel l1 \"%s\"", name0, name1));
627     }
628     PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, &eq, 1, MPIU_BOOL, MPI_LAND, comm));
629     if (!eq) goto finish;
630   }
631   {
632     PetscInt i, nValues;
633 
634     PetscCall(DMLabelGetNumValues(l0, &nValues));
635     for (i=0; i<nValues; i++) {
636       const PetscInt  v = l0->stratumValues[i];
637       PetscInt        n;
638       IS              is0, is1;
639 
640       PetscCall(DMLabelGetStratumSize_Private(l0, i, &n));
641       if (!n) continue;
642       PetscCall(DMLabelGetStratumIS(l0, v, &is0));
643       PetscCall(DMLabelGetStratumIS(l1, v, &is1));
644       PetscCall(ISEqualUnsorted(is0, is1, &eq));
645       PetscCall(ISDestroy(&is0));
646       PetscCall(ISDestroy(&is1));
647       if (!eq) {
648         PetscCall(PetscSNPrintf(msg, sizeof(msg), "Stratum #%D with value %D contains different points in DMLabel l0 \"%s\" and DMLabel l1 \"%s\"", i, v, name0, name1));
649         break;
650       }
651     }
652     PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, &eq, 1, MPIU_BOOL, MPI_LAND, comm));
653   }
654 finish:
655   /* If message output arg not set, print to stderr */
656   if (message) {
657     *message = NULL;
658     if (msg[0]) {
659       PetscCall(PetscStrallocpy(msg, message));
660     }
661   } else {
662     if (msg[0]) {
663       PetscCall(PetscSynchronizedFPrintf(comm, PETSC_STDERR, "[%d] %s\n", rank, msg));
664     }
665     PetscCall(PetscSynchronizedFlush(comm, PETSC_STDERR));
666   }
667   /* If same output arg not ser and labels are not equal, throw error */
668   if (equal) *equal = eq;
669   else PetscCheck(eq,comm, PETSC_ERR_ARG_INCOMP, "DMLabels l0 \"%s\" and l1 \"%s\" are not equal");
670   PetscFunctionReturn(0);
671 }
672 
673 /*@
674   DMLabelComputeIndex - Create an index structure for membership determination, automatically determining the bounds
675 
676   Not collective
677 
678   Input Parameter:
679 . label  - The DMLabel
680 
681   Level: intermediate
682 
683 .seealso: DMLabelHasPoint(), DMLabelCreateIndex(), DMLabelDestroyIndex(), DMLabelGetValue(), DMLabelSetValue()
684 @*/
685 PetscErrorCode DMLabelComputeIndex(DMLabel label)
686 {
687   PetscInt       pStart = PETSC_MAX_INT, pEnd = -1, v;
688 
689   PetscFunctionBegin;
690   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
691   PetscCall(DMLabelMakeAllValid_Private(label));
692   for (v = 0; v < label->numStrata; ++v) {
693     const PetscInt *points;
694     PetscInt       i;
695 
696     PetscCall(ISGetIndices(label->points[v], &points));
697     for (i = 0; i < label->stratumSizes[v]; ++i) {
698       const PetscInt point = points[i];
699 
700       pStart = PetscMin(point,   pStart);
701       pEnd   = PetscMax(point+1, pEnd);
702     }
703     PetscCall(ISRestoreIndices(label->points[v], &points));
704   }
705   label->pStart = pStart == PETSC_MAX_INT ? -1 : pStart;
706   label->pEnd   = pEnd;
707   PetscCall(DMLabelCreateIndex(label, label->pStart, label->pEnd));
708   PetscFunctionReturn(0);
709 }
710 
711 /*@
712   DMLabelCreateIndex - Create an index structure for membership determination
713 
714   Not collective
715 
716   Input Parameters:
717 + label  - The DMLabel
718 . pStart - The smallest point
719 - pEnd   - The largest point + 1
720 
721   Level: intermediate
722 
723 .seealso: DMLabelHasPoint(), DMLabelComputeIndex(), DMLabelDestroyIndex(), DMLabelGetValue(), DMLabelSetValue()
724 @*/
725 PetscErrorCode DMLabelCreateIndex(DMLabel label, PetscInt pStart, PetscInt pEnd)
726 {
727   PetscInt       v;
728 
729   PetscFunctionBegin;
730   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
731   PetscCall(DMLabelDestroyIndex(label));
732   PetscCall(DMLabelMakeAllValid_Private(label));
733   label->pStart = pStart;
734   label->pEnd   = pEnd;
735   /* This can be hooked into SetValue(),  ClearValue(), etc. for updating */
736   PetscCall(PetscBTCreate(pEnd - pStart, &label->bt));
737   for (v = 0; v < label->numStrata; ++v) {
738     const PetscInt *points;
739     PetscInt       i;
740 
741     PetscCall(ISGetIndices(label->points[v], &points));
742     for (i = 0; i < label->stratumSizes[v]; ++i) {
743       const PetscInt point = points[i];
744 
745       PetscCheckFalse((point < pStart) || (point >= pEnd),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label point %D is not in [%D, %D)", point, pStart, pEnd);
746       PetscCall(PetscBTSet(label->bt, point - pStart));
747     }
748     PetscCall(ISRestoreIndices(label->points[v], &points));
749   }
750   PetscFunctionReturn(0);
751 }
752 
753 /*@
754   DMLabelDestroyIndex - Destroy the index structure
755 
756   Not collective
757 
758   Input Parameter:
759 . label - the DMLabel
760 
761   Level: intermediate
762 
763 .seealso: DMLabelHasPoint(), DMLabelCreateIndex(), DMLabelGetValue(), DMLabelSetValue()
764 @*/
765 PetscErrorCode DMLabelDestroyIndex(DMLabel label)
766 {
767   PetscFunctionBegin;
768   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
769   label->pStart = -1;
770   label->pEnd   = -1;
771   PetscCall(PetscBTDestroy(&label->bt));
772   PetscFunctionReturn(0);
773 }
774 
775 /*@
776   DMLabelGetBounds - Return the smallest and largest point in the label
777 
778   Not collective
779 
780   Input Parameter:
781 . label - the DMLabel
782 
783   Output Parameters:
784 + pStart - The smallest point
785 - pEnd   - The largest point + 1
786 
787   Note: This will compute an index for the label if one does not exist.
788 
789   Level: intermediate
790 
791 .seealso: DMLabelHasPoint(), DMLabelCreateIndex(), DMLabelGetValue(), DMLabelSetValue()
792 @*/
793 PetscErrorCode DMLabelGetBounds(DMLabel label, PetscInt *pStart, PetscInt *pEnd)
794 {
795   PetscFunctionBegin;
796   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
797   if ((label->pStart == -1) && (label->pEnd == -1)) PetscCall(DMLabelComputeIndex(label));
798   if (pStart) {
799     PetscValidIntPointer(pStart, 2);
800     *pStart = label->pStart;
801   }
802   if (pEnd) {
803     PetscValidIntPointer(pEnd, 3);
804     *pEnd = label->pEnd;
805   }
806   PetscFunctionReturn(0);
807 }
808 
809 /*@
810   DMLabelHasValue - Determine whether a label assigns the value to any point
811 
812   Not collective
813 
814   Input Parameters:
815 + label - the DMLabel
816 - value - the value
817 
818   Output Parameter:
819 . contains - Flag indicating whether the label maps this value to any point
820 
821   Level: developer
822 
823 .seealso: DMLabelHasPoint(), DMLabelGetValue(), DMLabelSetValue()
824 @*/
825 PetscErrorCode DMLabelHasValue(DMLabel label, PetscInt value, PetscBool *contains)
826 {
827   PetscInt v;
828 
829   PetscFunctionBegin;
830   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
831   PetscValidBoolPointer(contains, 3);
832   PetscCall(DMLabelLookupStratum(label, value, &v));
833   *contains = v < 0 ? PETSC_FALSE : PETSC_TRUE;
834   PetscFunctionReturn(0);
835 }
836 
837 /*@
838   DMLabelHasPoint - Determine whether a label assigns a value to a point
839 
840   Not collective
841 
842   Input Parameters:
843 + label - the DMLabel
844 - point - the point
845 
846   Output Parameter:
847 . contains - Flag indicating whether the label maps this point to a value
848 
849   Note: The user must call DMLabelCreateIndex() before this function.
850 
851   Level: developer
852 
853 .seealso: DMLabelCreateIndex(), DMLabelGetValue(), DMLabelSetValue()
854 @*/
855 PetscErrorCode DMLabelHasPoint(DMLabel label, PetscInt point, PetscBool *contains)
856 {
857   PetscFunctionBeginHot;
858   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
859   PetscValidBoolPointer(contains, 3);
860   PetscCall(DMLabelMakeAllValid_Private(label));
861   if (PetscDefined(USE_DEBUG)) {
862     PetscCheck(label->bt,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must call DMLabelCreateIndex() before DMLabelHasPoint()");
863     PetscCheckFalse((point < label->pStart) || (point >= label->pEnd),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label point %D is not in [%D, %D)", point, label->pStart, label->pEnd);
864   }
865   *contains = PetscBTLookup(label->bt, point - label->pStart) ? PETSC_TRUE : PETSC_FALSE;
866   PetscFunctionReturn(0);
867 }
868 
869 /*@
870   DMLabelStratumHasPoint - Return true if the stratum contains a point
871 
872   Not collective
873 
874   Input Parameters:
875 + label - the DMLabel
876 . value - the stratum value
877 - point - the point
878 
879   Output Parameter:
880 . contains - true if the stratum contains the point
881 
882   Level: intermediate
883 
884 .seealso: DMLabelCreate(), DMLabelSetValue(), DMLabelClearValue()
885 @*/
886 PetscErrorCode DMLabelStratumHasPoint(DMLabel label, PetscInt value, PetscInt point, PetscBool *contains)
887 {
888   PetscInt       v;
889 
890   PetscFunctionBeginHot;
891   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
892   PetscValidBoolPointer(contains, 4);
893   *contains = PETSC_FALSE;
894   PetscCall(DMLabelLookupStratum(label, value, &v));
895   if (v < 0) PetscFunctionReturn(0);
896 
897   if (label->validIS[v]) {
898     PetscInt i;
899 
900     PetscCall(ISLocate(label->points[v], point, &i));
901     if (i >= 0) *contains = PETSC_TRUE;
902   } else {
903     PetscBool has;
904 
905     PetscCall(PetscHSetIHas(label->ht[v], point, &has));
906     if (has) *contains = PETSC_TRUE;
907   }
908   PetscFunctionReturn(0);
909 }
910 
911 /*@
912   DMLabelGetDefaultValue - Get the default value returned by DMLabelGetValue() if a point has not been explicitly given a value.
913   When a label is created, it is initialized to -1.
914 
915   Not collective
916 
917   Input parameter:
918 . label - a DMLabel object
919 
920   Output parameter:
921 . defaultValue - the default value
922 
923   Level: beginner
924 
925 .seealso: DMLabelSetDefaultValue(), DMLabelGetValue(), DMLabelSetValue()
926 @*/
927 PetscErrorCode DMLabelGetDefaultValue(DMLabel label, PetscInt *defaultValue)
928 {
929   PetscFunctionBegin;
930   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
931   *defaultValue = label->defaultValue;
932   PetscFunctionReturn(0);
933 }
934 
935 /*@
936   DMLabelSetDefaultValue - Set the default value returned by DMLabelGetValue() if a point has not been explicitly given a value.
937   When a label is created, it is initialized to -1.
938 
939   Not collective
940 
941   Input parameter:
942 . label - a DMLabel object
943 
944   Output parameter:
945 . defaultValue - the default value
946 
947   Level: beginner
948 
949 .seealso: DMLabelGetDefaultValue(), DMLabelGetValue(), DMLabelSetValue()
950 @*/
951 PetscErrorCode DMLabelSetDefaultValue(DMLabel label, PetscInt defaultValue)
952 {
953   PetscFunctionBegin;
954   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
955   label->defaultValue = defaultValue;
956   PetscFunctionReturn(0);
957 }
958 
959 /*@
960   DMLabelGetValue - Return the value a label assigns to a point, or the label's default value (which is initially -1, and can be changed with DMLabelSetDefaultValue())
961 
962   Not collective
963 
964   Input Parameters:
965 + label - the DMLabel
966 - point - the point
967 
968   Output Parameter:
969 . value - The point value, or the default value (-1 by default)
970 
971   Level: intermediate
972 
973 .seealso: DMLabelCreate(), DMLabelSetValue(), DMLabelClearValue(), DMLabelGetDefaultValue(), DMLabelSetDefaultValue()
974 @*/
975 PetscErrorCode DMLabelGetValue(DMLabel label, PetscInt point, PetscInt *value)
976 {
977   PetscInt       v;
978 
979   PetscFunctionBeginHot;
980   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
981   PetscValidIntPointer(value, 3);
982   *value = label->defaultValue;
983   for (v = 0; v < label->numStrata; ++v) {
984     if (label->validIS[v]) {
985       PetscInt i;
986 
987       PetscCall(ISLocate(label->points[v], point, &i));
988       if (i >= 0) {
989         *value = label->stratumValues[v];
990         break;
991       }
992     } else {
993       PetscBool has;
994 
995       PetscCall(PetscHSetIHas(label->ht[v], point, &has));
996       if (has) {
997         *value = label->stratumValues[v];
998         break;
999       }
1000     }
1001   }
1002   PetscFunctionReturn(0);
1003 }
1004 
1005 /*@
1006   DMLabelSetValue - Set the value a label assigns to a point.  If the value is the same as the label's default value (which is initially -1, and can be changed with DMLabelSetDefaultValue() to something different), then this function will do nothing.
1007 
1008   Not collective
1009 
1010   Input Parameters:
1011 + label - the DMLabel
1012 . point - the point
1013 - value - The point value
1014 
1015   Level: intermediate
1016 
1017 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelClearValue(), DMLabelGetDefaultValue(), DMLabelSetDefaultValue()
1018 @*/
1019 PetscErrorCode DMLabelSetValue(DMLabel label, PetscInt point, PetscInt value)
1020 {
1021   PetscInt       v;
1022 
1023   PetscFunctionBegin;
1024   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1025   /* Find label value, add new entry if needed */
1026   if (value == label->defaultValue) PetscFunctionReturn(0);
1027   PetscCall(DMLabelLookupAddStratum(label, value, &v));
1028   /* Set key */
1029   PetscCall(DMLabelMakeInvalid_Private(label, v));
1030   PetscCall(PetscHSetIAdd(label->ht[v], point));
1031   PetscFunctionReturn(0);
1032 }
1033 
1034 /*@
1035   DMLabelClearValue - Clear the value a label assigns to a point
1036 
1037   Not collective
1038 
1039   Input Parameters:
1040 + label - the DMLabel
1041 . point - the point
1042 - value - The point value
1043 
1044   Level: intermediate
1045 
1046 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue()
1047 @*/
1048 PetscErrorCode DMLabelClearValue(DMLabel label, PetscInt point, PetscInt value)
1049 {
1050   PetscInt       v;
1051 
1052   PetscFunctionBegin;
1053   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1054   /* Find label value */
1055   PetscCall(DMLabelLookupStratum(label, value, &v));
1056   if (v < 0) PetscFunctionReturn(0);
1057 
1058   if (label->bt) {
1059     PetscCheckFalse((point < label->pStart) || (point >= label->pEnd),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label point %D is not in [%D, %D)", point, label->pStart, label->pEnd);
1060     PetscCall(PetscBTClear(label->bt, point - label->pStart));
1061   }
1062 
1063   /* Delete key */
1064   PetscCall(DMLabelMakeInvalid_Private(label, v));
1065   PetscCall(PetscHSetIDel(label->ht[v], point));
1066   PetscFunctionReturn(0);
1067 }
1068 
1069 /*@
1070   DMLabelInsertIS - Set all points in the IS to a value
1071 
1072   Not collective
1073 
1074   Input Parameters:
1075 + label - the DMLabel
1076 . is    - the point IS
1077 - value - The point value
1078 
1079   Level: intermediate
1080 
1081 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1082 @*/
1083 PetscErrorCode DMLabelInsertIS(DMLabel label, IS is, PetscInt value)
1084 {
1085   PetscInt        v, n, p;
1086   const PetscInt *points;
1087 
1088   PetscFunctionBegin;
1089   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1090   PetscValidHeaderSpecific(is, IS_CLASSID, 2);
1091   /* Find label value, add new entry if needed */
1092   if (value == label->defaultValue) PetscFunctionReturn(0);
1093   PetscCall(DMLabelLookupAddStratum(label, value, &v));
1094   /* Set keys */
1095   PetscCall(DMLabelMakeInvalid_Private(label, v));
1096   PetscCall(ISGetLocalSize(is, &n));
1097   PetscCall(ISGetIndices(is, &points));
1098   for (p = 0; p < n; ++p) PetscCall(PetscHSetIAdd(label->ht[v], points[p]));
1099   PetscCall(ISRestoreIndices(is, &points));
1100   PetscFunctionReturn(0);
1101 }
1102 
1103 /*@
1104   DMLabelGetNumValues - Get the number of values that the DMLabel takes
1105 
1106   Not collective
1107 
1108   Input Parameter:
1109 . label - the DMLabel
1110 
1111   Output Parameter:
1112 . numValues - the number of values
1113 
1114   Level: intermediate
1115 
1116 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1117 @*/
1118 PetscErrorCode DMLabelGetNumValues(DMLabel label, PetscInt *numValues)
1119 {
1120   PetscFunctionBegin;
1121   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1122   PetscValidIntPointer(numValues, 2);
1123   *numValues = label->numStrata;
1124   PetscFunctionReturn(0);
1125 }
1126 
1127 /*@
1128   DMLabelGetValueIS - Get an IS of all values that the DMlabel takes
1129 
1130   Not collective
1131 
1132   Input Parameter:
1133 . label - the DMLabel
1134 
1135   Output Parameter:
1136 . is    - the value IS
1137 
1138   Level: intermediate
1139 
1140   Notes:
1141   The output IS should be destroyed when no longer needed.
1142   Strata which are allocated but empty [DMLabelGetStratumSize() yields 0] are counted.
1143   If you need to count only nonempty strata, use DMLabelGetNonEmptyStratumValuesIS().
1144 
1145 .seealso: DMLabelGetNonEmptyStratumValuesIS(), DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1146 @*/
1147 PetscErrorCode DMLabelGetValueIS(DMLabel label, IS *values)
1148 {
1149   PetscFunctionBegin;
1150   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1151   PetscValidPointer(values, 2);
1152   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, label->numStrata, label->stratumValues, PETSC_USE_POINTER, values));
1153   PetscFunctionReturn(0);
1154 }
1155 
1156 /*@
1157   DMLabelGetNonEmptyStratumValuesIS - Get an IS of all values that the DMlabel takes
1158 
1159   Not collective
1160 
1161   Input Parameter:
1162 . label - the DMLabel
1163 
1164   Output Paramater:
1165 . is    - the value IS
1166 
1167   Level: intermediate
1168 
1169   Notes:
1170   The output IS should be destroyed when no longer needed.
1171   This is similar to DMLabelGetValueIS() but counts only nonempty strata.
1172 
1173 .seealso: DMLabelGetValueIS(), DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1174 @*/
1175 PetscErrorCode DMLabelGetNonEmptyStratumValuesIS(DMLabel label, IS *values)
1176 {
1177   PetscInt        i, j;
1178   PetscInt       *valuesArr;
1179 
1180   PetscFunctionBegin;
1181   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1182   PetscValidPointer(values, 2);
1183   PetscCall(PetscMalloc1(label->numStrata, &valuesArr));
1184   for (i = 0, j = 0; i < label->numStrata; i++) {
1185     PetscInt        n;
1186 
1187     PetscCall(DMLabelGetStratumSize_Private(label, i, &n));
1188     if (n) valuesArr[j++] = label->stratumValues[i];
1189   }
1190   if (j == label->numStrata) {
1191     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, label->numStrata, label->stratumValues, PETSC_USE_POINTER, values));
1192   } else {
1193     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, j, valuesArr, PETSC_COPY_VALUES, values));
1194   }
1195   PetscCall(PetscFree(valuesArr));
1196   PetscFunctionReturn(0);
1197 }
1198 
1199 /*@
1200   DMLabelGetValueIndex - Get the index of a given value in the list of values for the DMlabel, or -1 if it is not present
1201 
1202   Not collective
1203 
1204   Input Parameters:
1205 + label - the DMLabel
1206 - value - the value
1207 
1208   Output Parameter:
1209 . index - the index of value in the list of values
1210 
1211   Level: intermediate
1212 
1213 .seealso: DMLabelGetValueIS(), DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1214 @*/
1215 PetscErrorCode DMLabelGetValueIndex(DMLabel label, PetscInt value, PetscInt *index)
1216 {
1217   PetscInt v;
1218 
1219   PetscFunctionBegin;
1220   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1221   PetscValidIntPointer(index, 3);
1222   /* Do not assume they are sorted */
1223   for (v = 0; v < label->numStrata; ++v) if (label->stratumValues[v] == value) break;
1224   if (v >= label->numStrata) *index = -1;
1225   else                       *index = v;
1226   PetscFunctionReturn(0);
1227 }
1228 
1229 /*@
1230   DMLabelHasStratum - Determine whether points exist with the given value
1231 
1232   Not collective
1233 
1234   Input Parameters:
1235 + label - the DMLabel
1236 - value - the stratum value
1237 
1238   Output Parameter:
1239 . exists - Flag saying whether points exist
1240 
1241   Level: intermediate
1242 
1243 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1244 @*/
1245 PetscErrorCode DMLabelHasStratum(DMLabel label, PetscInt value, PetscBool *exists)
1246 {
1247   PetscInt       v;
1248 
1249   PetscFunctionBegin;
1250   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1251   PetscValidBoolPointer(exists, 3);
1252   PetscCall(DMLabelLookupStratum(label, value, &v));
1253   *exists = v < 0 ? PETSC_FALSE : PETSC_TRUE;
1254   PetscFunctionReturn(0);
1255 }
1256 
1257 /*@
1258   DMLabelGetStratumSize - Get the size of a stratum
1259 
1260   Not collective
1261 
1262   Input Parameters:
1263 + label - the DMLabel
1264 - value - the stratum value
1265 
1266   Output Parameter:
1267 . size - The number of points in the stratum
1268 
1269   Level: intermediate
1270 
1271 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1272 @*/
1273 PetscErrorCode DMLabelGetStratumSize(DMLabel label, PetscInt value, PetscInt *size)
1274 {
1275   PetscInt       v;
1276 
1277   PetscFunctionBegin;
1278   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1279   PetscValidIntPointer(size, 3);
1280   PetscCall(DMLabelLookupStratum(label, value, &v));
1281   PetscCall(DMLabelGetStratumSize_Private(label, v, size));
1282   PetscFunctionReturn(0);
1283 }
1284 
1285 /*@
1286   DMLabelGetStratumBounds - Get the largest and smallest point of a stratum
1287 
1288   Not collective
1289 
1290   Input Parameters:
1291 + label - the DMLabel
1292 - value - the stratum value
1293 
1294   Output Parameters:
1295 + start - the smallest point in the stratum
1296 - end - the largest point in the stratum
1297 
1298   Level: intermediate
1299 
1300 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1301 @*/
1302 PetscErrorCode DMLabelGetStratumBounds(DMLabel label, PetscInt value, PetscInt *start, PetscInt *end)
1303 {
1304   PetscInt       v, min, max;
1305 
1306   PetscFunctionBegin;
1307   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1308   if (start) {PetscValidIntPointer(start, 3); *start = -1;}
1309   if (end)   {PetscValidIntPointer(end,   4); *end   = -1;}
1310   PetscCall(DMLabelLookupStratum(label, value, &v));
1311   if (v < 0) PetscFunctionReturn(0);
1312   PetscCall(DMLabelMakeValid_Private(label, v));
1313   if (label->stratumSizes[v] <= 0) PetscFunctionReturn(0);
1314   PetscCall(ISGetMinMax(label->points[v], &min, &max));
1315   if (start) *start = min;
1316   if (end)   *end   = max+1;
1317   PetscFunctionReturn(0);
1318 }
1319 
1320 /*@
1321   DMLabelGetStratumIS - Get an IS with the stratum points
1322 
1323   Not collective
1324 
1325   Input Parameters:
1326 + label - the DMLabel
1327 - value - the stratum value
1328 
1329   Output Parameter:
1330 . points - The stratum points
1331 
1332   Level: intermediate
1333 
1334   Notes:
1335   The output IS should be destroyed when no longer needed.
1336   Returns NULL if the stratum is empty.
1337 
1338 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1339 @*/
1340 PetscErrorCode DMLabelGetStratumIS(DMLabel label, PetscInt value, IS *points)
1341 {
1342   PetscInt       v;
1343 
1344   PetscFunctionBegin;
1345   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1346   PetscValidPointer(points, 3);
1347   *points = NULL;
1348   PetscCall(DMLabelLookupStratum(label, value, &v));
1349   if (v < 0) PetscFunctionReturn(0);
1350   PetscCall(DMLabelMakeValid_Private(label, v));
1351   PetscCall(PetscObjectReference((PetscObject) label->points[v]));
1352   *points = label->points[v];
1353   PetscFunctionReturn(0);
1354 }
1355 
1356 /*@
1357   DMLabelSetStratumIS - Set the stratum points using an IS
1358 
1359   Not collective
1360 
1361   Input Parameters:
1362 + label - the DMLabel
1363 . value - the stratum value
1364 - points - The stratum points
1365 
1366   Level: intermediate
1367 
1368 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1369 @*/
1370 PetscErrorCode DMLabelSetStratumIS(DMLabel label, PetscInt value, IS is)
1371 {
1372   PetscInt       v;
1373 
1374   PetscFunctionBegin;
1375   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1376   PetscValidHeaderSpecific(is, IS_CLASSID, 3);
1377   PetscCall(DMLabelLookupAddStratum(label, value, &v));
1378   if (is == label->points[v]) PetscFunctionReturn(0);
1379   PetscCall(DMLabelClearStratum(label, value));
1380   PetscCall(ISGetLocalSize(is, &(label->stratumSizes[v])));
1381   PetscCall(PetscObjectReference((PetscObject)is));
1382   PetscCall(ISDestroy(&(label->points[v])));
1383   label->points[v]  = is;
1384   label->validIS[v] = PETSC_TRUE;
1385   PetscCall(PetscObjectStateIncrease((PetscObject) label));
1386   if (label->bt) {
1387     const PetscInt *points;
1388     PetscInt p;
1389 
1390     PetscCall(ISGetIndices(is,&points));
1391     for (p = 0; p < label->stratumSizes[v]; ++p) {
1392       const PetscInt point = points[p];
1393 
1394       PetscCheckFalse((point < label->pStart) || (point >= label->pEnd),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label point %D is not in [%D, %D)", point, label->pStart, label->pEnd);
1395       PetscCall(PetscBTSet(label->bt, point - label->pStart));
1396     }
1397   }
1398   PetscFunctionReturn(0);
1399 }
1400 
1401 /*@
1402   DMLabelClearStratum - Remove a stratum
1403 
1404   Not collective
1405 
1406   Input Parameters:
1407 + label - the DMLabel
1408 - value - the stratum value
1409 
1410   Level: intermediate
1411 
1412 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1413 @*/
1414 PetscErrorCode DMLabelClearStratum(DMLabel label, PetscInt value)
1415 {
1416   PetscInt       v;
1417 
1418   PetscFunctionBegin;
1419   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1420   PetscCall(DMLabelLookupStratum(label, value, &v));
1421   if (v < 0) PetscFunctionReturn(0);
1422   if (label->validIS[v]) {
1423     if (label->bt) {
1424       PetscInt       i;
1425       const PetscInt *points;
1426 
1427       PetscCall(ISGetIndices(label->points[v], &points));
1428       for (i = 0; i < label->stratumSizes[v]; ++i) {
1429         const PetscInt point = points[i];
1430 
1431         PetscCheckFalse((point < label->pStart) || (point >= label->pEnd),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label point %D is not in [%D, %D)", point, label->pStart, label->pEnd);
1432         PetscCall(PetscBTClear(label->bt, point - label->pStart));
1433       }
1434       PetscCall(ISRestoreIndices(label->points[v], &points));
1435     }
1436     label->stratumSizes[v] = 0;
1437     PetscCall(ISDestroy(&label->points[v]));
1438     PetscCall(ISCreateStride(PETSC_COMM_SELF, 0, 0, 1, &label->points[v]));
1439     PetscCall(PetscObjectSetName((PetscObject) label->points[v], "indices"));
1440     PetscCall(PetscObjectStateIncrease((PetscObject) label));
1441   } else {
1442     PetscCall(PetscHSetIClear(label->ht[v]));
1443   }
1444   PetscFunctionReturn(0);
1445 }
1446 
1447 /*@
1448   DMLabelSetStratumBounds - Efficiently give a contiguous set of points a given label value
1449 
1450   Not collective
1451 
1452   Input Parameters:
1453 + label  - The DMLabel
1454 . value  - The label value for all points
1455 . pStart - The first point
1456 - pEnd   - A point beyond all marked points
1457 
1458   Note: The marks points are [pStart, pEnd), and only the bounds are stored.
1459 
1460   Level: intermediate
1461 
1462 .seealso: DMLabelCreate(), DMLabelSetStratumIS(), DMLabelGetStratumIS()
1463 @*/
1464 PetscErrorCode DMLabelSetStratumBounds(DMLabel label, PetscInt value, PetscInt pStart, PetscInt pEnd)
1465 {
1466   IS             pIS;
1467 
1468   PetscFunctionBegin;
1469   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &pIS));
1470   PetscCall(DMLabelSetStratumIS(label, value, pIS));
1471   PetscCall(ISDestroy(&pIS));
1472   PetscFunctionReturn(0);
1473 }
1474 
1475 /*@
1476   DMLabelGetStratumPointIndex - Get the index of a point in a given stratum
1477 
1478   Not collective
1479 
1480   Input Parameters:
1481 + label  - The DMLabel
1482 . value  - The label value
1483 - p      - A point with this value
1484 
1485   Output Parameter:
1486 . index  - The index of this point in the stratum, or -1 if the point is not in the stratum or the stratum does not exist
1487 
1488   Level: intermediate
1489 
1490 .seealso: DMLabelGetValueIndex(), DMLabelGetStratumIS(), DMLabelCreate()
1491 @*/
1492 PetscErrorCode DMLabelGetStratumPointIndex(DMLabel label, PetscInt value, PetscInt p, PetscInt *index)
1493 {
1494   const PetscInt *indices;
1495   PetscInt        v;
1496 
1497   PetscFunctionBegin;
1498   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1499   PetscValidIntPointer(index, 4);
1500   *index = -1;
1501   PetscCall(DMLabelLookupStratum(label, value, &v));
1502   if (v < 0) PetscFunctionReturn(0);
1503   PetscCall(DMLabelMakeValid_Private(label, v));
1504   PetscCall(ISGetIndices(label->points[v], &indices));
1505   PetscCall(PetscFindInt(p, label->stratumSizes[v], indices, index));
1506   PetscCall(ISRestoreIndices(label->points[v], &indices));
1507   PetscFunctionReturn(0);
1508 }
1509 
1510 /*@
1511   DMLabelFilter - Remove all points outside of [start, end)
1512 
1513   Not collective
1514 
1515   Input Parameters:
1516 + label - the DMLabel
1517 . start - the first point kept
1518 - end - one more than the last point kept
1519 
1520   Level: intermediate
1521 
1522 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1523 @*/
1524 PetscErrorCode DMLabelFilter(DMLabel label, PetscInt start, PetscInt end)
1525 {
1526   PetscInt       v;
1527 
1528   PetscFunctionBegin;
1529   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1530   PetscCall(DMLabelDestroyIndex(label));
1531   PetscCall(DMLabelMakeAllValid_Private(label));
1532   for (v = 0; v < label->numStrata; ++v) {
1533     PetscCall(ISGeneralFilter(label->points[v], start, end));
1534   }
1535   PetscCall(DMLabelCreateIndex(label, start, end));
1536   PetscFunctionReturn(0);
1537 }
1538 
1539 /*@
1540   DMLabelPermute - Create a new label with permuted points
1541 
1542   Not collective
1543 
1544   Input Parameters:
1545 + label - the DMLabel
1546 - permutation - the point permutation
1547 
1548   Output Parameter:
1549 . labelnew - the new label containing the permuted points
1550 
1551   Level: intermediate
1552 
1553 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1554 @*/
1555 PetscErrorCode DMLabelPermute(DMLabel label, IS permutation, DMLabel *labelNew)
1556 {
1557   const PetscInt *perm;
1558   PetscInt        numValues, numPoints, v, q;
1559 
1560   PetscFunctionBegin;
1561   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1562   PetscValidHeaderSpecific(permutation, IS_CLASSID, 2);
1563   PetscCall(DMLabelMakeAllValid_Private(label));
1564   PetscCall(DMLabelDuplicate(label, labelNew));
1565   PetscCall(DMLabelGetNumValues(*labelNew, &numValues));
1566   PetscCall(ISGetLocalSize(permutation, &numPoints));
1567   PetscCall(ISGetIndices(permutation, &perm));
1568   for (v = 0; v < numValues; ++v) {
1569     const PetscInt size   = (*labelNew)->stratumSizes[v];
1570     const PetscInt *points;
1571     PetscInt *pointsNew;
1572 
1573     PetscCall(ISGetIndices((*labelNew)->points[v],&points));
1574     PetscCall(PetscMalloc1(size,&pointsNew));
1575     for (q = 0; q < size; ++q) {
1576       const PetscInt point = points[q];
1577 
1578       PetscCheckFalse((point < 0) || (point >= numPoints),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label point %D is not in [0, %D) for the remapping", point, numPoints);
1579       pointsNew[q] = perm[point];
1580     }
1581     PetscCall(ISRestoreIndices((*labelNew)->points[v],&points));
1582     PetscCall(PetscSortInt(size, pointsNew));
1583     PetscCall(ISDestroy(&((*labelNew)->points[v])));
1584     if (size > 0 && pointsNew[size - 1] == pointsNew[0] + size - 1) {
1585       PetscCall(ISCreateStride(PETSC_COMM_SELF,size,pointsNew[0],1,&((*labelNew)->points[v])));
1586       PetscCall(PetscFree(pointsNew));
1587     } else {
1588       PetscCall(ISCreateGeneral(PETSC_COMM_SELF,size,pointsNew,PETSC_OWN_POINTER,&((*labelNew)->points[v])));
1589     }
1590     PetscCall(PetscObjectSetName((PetscObject) ((*labelNew)->points[v]), "indices"));
1591   }
1592   PetscCall(ISRestoreIndices(permutation, &perm));
1593   if (label->bt) {
1594     PetscCall(PetscBTDestroy(&label->bt));
1595     PetscCall(DMLabelCreateIndex(label, label->pStart, label->pEnd));
1596   }
1597   PetscFunctionReturn(0);
1598 }
1599 
1600 PetscErrorCode DMLabelDistribute_Internal(DMLabel label, PetscSF sf, PetscSection *leafSection, PetscInt **leafStrata)
1601 {
1602   MPI_Comm       comm;
1603   PetscInt       s, l, nroots, nleaves, dof, offset, size;
1604   PetscInt      *remoteOffsets, *rootStrata, *rootIdx;
1605   PetscSection   rootSection;
1606   PetscSF        labelSF;
1607 
1608   PetscFunctionBegin;
1609   if (label) PetscCall(DMLabelMakeAllValid_Private(label));
1610   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
1611   /* Build a section of stratum values per point, generate the according SF
1612      and distribute point-wise stratum values to leaves. */
1613   PetscCall(PetscSFGetGraph(sf, &nroots, &nleaves, NULL, NULL));
1614   PetscCall(PetscSectionCreate(comm, &rootSection));
1615   PetscCall(PetscSectionSetChart(rootSection, 0, nroots));
1616   if (label) {
1617     for (s = 0; s < label->numStrata; ++s) {
1618       const PetscInt *points;
1619 
1620       PetscCall(ISGetIndices(label->points[s], &points));
1621       for (l = 0; l < label->stratumSizes[s]; l++) {
1622         PetscCall(PetscSectionGetDof(rootSection, points[l], &dof));
1623         PetscCall(PetscSectionSetDof(rootSection, points[l], dof+1));
1624       }
1625       PetscCall(ISRestoreIndices(label->points[s], &points));
1626     }
1627   }
1628   PetscCall(PetscSectionSetUp(rootSection));
1629   /* Create a point-wise array of stratum values */
1630   PetscCall(PetscSectionGetStorageSize(rootSection, &size));
1631   PetscCall(PetscMalloc1(size, &rootStrata));
1632   PetscCall(PetscCalloc1(nroots, &rootIdx));
1633   if (label) {
1634     for (s = 0; s < label->numStrata; ++s) {
1635       const PetscInt *points;
1636 
1637       PetscCall(ISGetIndices(label->points[s], &points));
1638       for (l = 0; l < label->stratumSizes[s]; l++) {
1639         const PetscInt p = points[l];
1640         PetscCall(PetscSectionGetOffset(rootSection, p, &offset));
1641         rootStrata[offset+rootIdx[p]++] = label->stratumValues[s];
1642       }
1643       PetscCall(ISRestoreIndices(label->points[s], &points));
1644     }
1645   }
1646   /* Build SF that maps label points to remote processes */
1647   PetscCall(PetscSectionCreate(comm, leafSection));
1648   PetscCall(PetscSFDistributeSection(sf, rootSection, &remoteOffsets, *leafSection));
1649   PetscCall(PetscSFCreateSectionSF(sf, rootSection, remoteOffsets, *leafSection, &labelSF));
1650   PetscCall(PetscFree(remoteOffsets));
1651   /* Send the strata for each point over the derived SF */
1652   PetscCall(PetscSectionGetStorageSize(*leafSection, &size));
1653   PetscCall(PetscMalloc1(size, leafStrata));
1654   PetscCall(PetscSFBcastBegin(labelSF, MPIU_INT, rootStrata, *leafStrata,MPI_REPLACE));
1655   PetscCall(PetscSFBcastEnd(labelSF, MPIU_INT, rootStrata, *leafStrata,MPI_REPLACE));
1656   /* Clean up */
1657   PetscCall(PetscFree(rootStrata));
1658   PetscCall(PetscFree(rootIdx));
1659   PetscCall(PetscSectionDestroy(&rootSection));
1660   PetscCall(PetscSFDestroy(&labelSF));
1661   PetscFunctionReturn(0);
1662 }
1663 
1664 /*@
1665   DMLabelDistribute - Create a new label pushed forward over the PetscSF
1666 
1667   Collective on sf
1668 
1669   Input Parameters:
1670 + label - the DMLabel
1671 - sf    - the map from old to new distribution
1672 
1673   Output Parameter:
1674 . labelnew - the new redistributed label
1675 
1676   Level: intermediate
1677 
1678 .seealso: DMLabelCreate(), DMLabelGetValue(), DMLabelSetValue(), DMLabelClearValue()
1679 @*/
1680 PetscErrorCode DMLabelDistribute(DMLabel label, PetscSF sf, DMLabel *labelNew)
1681 {
1682   MPI_Comm       comm;
1683   PetscSection   leafSection;
1684   PetscInt       p, pStart, pEnd, s, size, dof, offset, stratum;
1685   PetscInt      *leafStrata, *strataIdx;
1686   PetscInt     **points;
1687   const char    *lname = NULL;
1688   char          *name;
1689   PetscInt       nameSize;
1690   PetscHSetI     stratumHash;
1691   size_t         len = 0;
1692   PetscMPIInt    rank;
1693 
1694   PetscFunctionBegin;
1695   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
1696   if (label) {
1697     PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1698     PetscCall(DMLabelMakeAllValid_Private(label));
1699   }
1700   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
1701   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1702   /* Bcast name */
1703   if (rank == 0) {
1704     PetscCall(PetscObjectGetName((PetscObject) label, &lname));
1705     PetscCall(PetscStrlen(lname, &len));
1706   }
1707   nameSize = len;
1708   PetscCallMPI(MPI_Bcast(&nameSize, 1, MPIU_INT, 0, comm));
1709   PetscCall(PetscMalloc1(nameSize+1, &name));
1710   if (rank == 0) PetscCall(PetscArraycpy(name, lname, nameSize+1));
1711   PetscCallMPI(MPI_Bcast(name, nameSize+1, MPI_CHAR, 0, comm));
1712   PetscCall(DMLabelCreate(PETSC_COMM_SELF, name, labelNew));
1713   PetscCall(PetscFree(name));
1714   /* Bcast defaultValue */
1715   if (rank == 0) (*labelNew)->defaultValue = label->defaultValue;
1716   PetscCallMPI(MPI_Bcast(&(*labelNew)->defaultValue, 1, MPIU_INT, 0, comm));
1717   /* Distribute stratum values over the SF and get the point mapping on the receiver */
1718   PetscCall(DMLabelDistribute_Internal(label, sf, &leafSection, &leafStrata));
1719   /* Determine received stratum values and initialise new label*/
1720   PetscCall(PetscHSetICreate(&stratumHash));
1721   PetscCall(PetscSectionGetStorageSize(leafSection, &size));
1722   for (p = 0; p < size; ++p) PetscCall(PetscHSetIAdd(stratumHash, leafStrata[p]));
1723   PetscCall(PetscHSetIGetSize(stratumHash, &(*labelNew)->numStrata));
1724   PetscCall(PetscMalloc1((*labelNew)->numStrata, &(*labelNew)->validIS));
1725   for (s = 0; s < (*labelNew)->numStrata; ++s) (*labelNew)->validIS[s] = PETSC_TRUE;
1726   PetscCall(PetscMalloc1((*labelNew)->numStrata, &(*labelNew)->stratumValues));
1727   /* Turn leafStrata into indices rather than stratum values */
1728   offset = 0;
1729   PetscCall(PetscHSetIGetElems(stratumHash, &offset, (*labelNew)->stratumValues));
1730   PetscCall(PetscSortInt((*labelNew)->numStrata,(*labelNew)->stratumValues));
1731   for (s = 0; s < (*labelNew)->numStrata; ++s) {
1732     PetscCall(PetscHMapISet((*labelNew)->hmap, (*labelNew)->stratumValues[s], s));
1733   }
1734   for (p = 0; p < size; ++p) {
1735     for (s = 0; s < (*labelNew)->numStrata; ++s) {
1736       if (leafStrata[p] == (*labelNew)->stratumValues[s]) {leafStrata[p] = s; break;}
1737     }
1738   }
1739   /* Rebuild the point strata on the receiver */
1740   PetscCall(PetscCalloc1((*labelNew)->numStrata,&(*labelNew)->stratumSizes));
1741   PetscCall(PetscSectionGetChart(leafSection, &pStart, &pEnd));
1742   for (p=pStart; p<pEnd; p++) {
1743     PetscCall(PetscSectionGetDof(leafSection, p, &dof));
1744     PetscCall(PetscSectionGetOffset(leafSection, p, &offset));
1745     for (s=0; s<dof; s++) {
1746       (*labelNew)->stratumSizes[leafStrata[offset+s]]++;
1747     }
1748   }
1749   PetscCall(PetscCalloc1((*labelNew)->numStrata,&(*labelNew)->ht));
1750   PetscCall(PetscMalloc1((*labelNew)->numStrata,&(*labelNew)->points));
1751   PetscCall(PetscMalloc1((*labelNew)->numStrata,&points));
1752   for (s = 0; s < (*labelNew)->numStrata; ++s) {
1753     PetscCall(PetscHSetICreate(&(*labelNew)->ht[s]));
1754     PetscCall(PetscMalloc1((*labelNew)->stratumSizes[s], &(points[s])));
1755   }
1756   /* Insert points into new strata */
1757   PetscCall(PetscCalloc1((*labelNew)->numStrata, &strataIdx));
1758   PetscCall(PetscSectionGetChart(leafSection, &pStart, &pEnd));
1759   for (p=pStart; p<pEnd; p++) {
1760     PetscCall(PetscSectionGetDof(leafSection, p, &dof));
1761     PetscCall(PetscSectionGetOffset(leafSection, p, &offset));
1762     for (s=0; s<dof; s++) {
1763       stratum = leafStrata[offset+s];
1764       points[stratum][strataIdx[stratum]++] = p;
1765     }
1766   }
1767   for (s = 0; s < (*labelNew)->numStrata; s++) {
1768     PetscCall(ISCreateGeneral(PETSC_COMM_SELF,(*labelNew)->stratumSizes[s],&(points[s][0]),PETSC_OWN_POINTER,&((*labelNew)->points[s])));
1769     PetscCall(PetscObjectSetName((PetscObject)((*labelNew)->points[s]),"indices"));
1770   }
1771   PetscCall(PetscFree(points));
1772   PetscCall(PetscHSetIDestroy(&stratumHash));
1773   PetscCall(PetscFree(leafStrata));
1774   PetscCall(PetscFree(strataIdx));
1775   PetscCall(PetscSectionDestroy(&leafSection));
1776   PetscFunctionReturn(0);
1777 }
1778 
1779 /*@
1780   DMLabelGather - Gather all label values from leafs into roots
1781 
1782   Collective on sf
1783 
1784   Input Parameters:
1785 + label - the DMLabel
1786 - sf - the Star Forest point communication map
1787 
1788   Output Parameters:
1789 . labelNew - the new DMLabel with localised leaf values
1790 
1791   Level: developer
1792 
1793   Note: This is the inverse operation to DMLabelDistribute.
1794 
1795 .seealso: DMLabelDistribute()
1796 @*/
1797 PetscErrorCode DMLabelGather(DMLabel label, PetscSF sf, DMLabel *labelNew)
1798 {
1799   MPI_Comm       comm;
1800   PetscSection   rootSection;
1801   PetscSF        sfLabel;
1802   PetscSFNode   *rootPoints, *leafPoints;
1803   PetscInt       p, s, d, nroots, nleaves, nmultiroots, idx, dof, offset;
1804   const PetscInt *rootDegree, *ilocal;
1805   PetscInt       *rootStrata;
1806   const char    *lname;
1807   char          *name;
1808   PetscInt       nameSize;
1809   size_t         len = 0;
1810   PetscMPIInt    rank, size;
1811 
1812   PetscFunctionBegin;
1813   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1814   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
1815   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
1816   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1817   PetscCallMPI(MPI_Comm_size(comm, &size));
1818   /* Bcast name */
1819   if (rank == 0) {
1820     PetscCall(PetscObjectGetName((PetscObject) label, &lname));
1821     PetscCall(PetscStrlen(lname, &len));
1822   }
1823   nameSize = len;
1824   PetscCallMPI(MPI_Bcast(&nameSize, 1, MPIU_INT, 0, comm));
1825   PetscCall(PetscMalloc1(nameSize+1, &name));
1826   if (rank == 0) PetscCall(PetscArraycpy(name, lname, nameSize+1));
1827   PetscCallMPI(MPI_Bcast(name, nameSize+1, MPI_CHAR, 0, comm));
1828   PetscCall(DMLabelCreate(PETSC_COMM_SELF, name, labelNew));
1829   PetscCall(PetscFree(name));
1830   /* Gather rank/index pairs of leaves into local roots to build
1831      an inverse, multi-rooted SF. Note that this ignores local leaf
1832      indexing due to the use of the multiSF in PetscSFGather. */
1833   PetscCall(PetscSFGetGraph(sf, &nroots, &nleaves, &ilocal, NULL));
1834   PetscCall(PetscMalloc1(nroots, &leafPoints));
1835   for (p = 0; p < nroots; ++p) leafPoints[p].rank = leafPoints[p].index = -1;
1836   for (p = 0; p < nleaves; p++) {
1837     PetscInt ilp = ilocal ? ilocal[p] : p;
1838 
1839     leafPoints[ilp].index = ilp;
1840     leafPoints[ilp].rank  = rank;
1841   }
1842   PetscCall(PetscSFComputeDegreeBegin(sf, &rootDegree));
1843   PetscCall(PetscSFComputeDegreeEnd(sf, &rootDegree));
1844   for (p = 0, nmultiroots = 0; p < nroots; ++p) nmultiroots += rootDegree[p];
1845   PetscCall(PetscMalloc1(nmultiroots, &rootPoints));
1846   PetscCall(PetscSFGatherBegin(sf, MPIU_2INT, leafPoints, rootPoints));
1847   PetscCall(PetscSFGatherEnd(sf, MPIU_2INT, leafPoints, rootPoints));
1848   PetscCall(PetscSFCreate(comm,& sfLabel));
1849   PetscCall(PetscSFSetGraph(sfLabel, nroots, nmultiroots, NULL, PETSC_OWN_POINTER, rootPoints, PETSC_OWN_POINTER));
1850   /* Migrate label over inverted SF to pull stratum values at leaves into roots. */
1851   PetscCall(DMLabelDistribute_Internal(label, sfLabel, &rootSection, &rootStrata));
1852   /* Rebuild the point strata on the receiver */
1853   for (p = 0, idx = 0; p < nroots; p++) {
1854     for (d = 0; d < rootDegree[p]; d++) {
1855       PetscCall(PetscSectionGetDof(rootSection, idx+d, &dof));
1856       PetscCall(PetscSectionGetOffset(rootSection, idx+d, &offset));
1857       for (s = 0; s < dof; s++) PetscCall(DMLabelSetValue(*labelNew, p, rootStrata[offset+s]));
1858     }
1859     idx += rootDegree[p];
1860   }
1861   PetscCall(PetscFree(leafPoints));
1862   PetscCall(PetscFree(rootStrata));
1863   PetscCall(PetscSectionDestroy(&rootSection));
1864   PetscCall(PetscSFDestroy(&sfLabel));
1865   PetscFunctionReturn(0);
1866 }
1867 
1868 /*@
1869   DMLabelConvertToSection - Make a PetscSection/IS pair that encodes the label
1870 
1871   Not collective
1872 
1873   Input Parameter:
1874 . label - the DMLabel
1875 
1876   Output Parameters:
1877 + section - the section giving offsets for each stratum
1878 - is - An IS containing all the label points
1879 
1880   Level: developer
1881 
1882 .seealso: DMLabelDistribute()
1883 @*/
1884 PetscErrorCode DMLabelConvertToSection(DMLabel label, PetscSection *section, IS *is)
1885 {
1886   IS              vIS;
1887   const PetscInt *values;
1888   PetscInt       *points;
1889   PetscInt        nV, vS = 0, vE = 0, v, N;
1890 
1891   PetscFunctionBegin;
1892   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 1);
1893   PetscCall(DMLabelGetNumValues(label, &nV));
1894   PetscCall(DMLabelGetValueIS(label, &vIS));
1895   PetscCall(ISGetIndices(vIS, &values));
1896   if (nV) {vS = values[0]; vE = values[0]+1;}
1897   for (v = 1; v < nV; ++v) {
1898     vS = PetscMin(vS, values[v]);
1899     vE = PetscMax(vE, values[v]+1);
1900   }
1901   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, section));
1902   PetscCall(PetscSectionSetChart(*section, vS, vE));
1903   for (v = 0; v < nV; ++v) {
1904     PetscInt n;
1905 
1906     PetscCall(DMLabelGetStratumSize(label, values[v], &n));
1907     PetscCall(PetscSectionSetDof(*section, values[v], n));
1908   }
1909   PetscCall(PetscSectionSetUp(*section));
1910   PetscCall(PetscSectionGetStorageSize(*section, &N));
1911   PetscCall(PetscMalloc1(N, &points));
1912   for (v = 0; v < nV; ++v) {
1913     IS              is;
1914     const PetscInt *spoints;
1915     PetscInt        dof, off, p;
1916 
1917     PetscCall(PetscSectionGetDof(*section, values[v], &dof));
1918     PetscCall(PetscSectionGetOffset(*section, values[v], &off));
1919     PetscCall(DMLabelGetStratumIS(label, values[v], &is));
1920     PetscCall(ISGetIndices(is, &spoints));
1921     for (p = 0; p < dof; ++p) points[off+p] = spoints[p];
1922     PetscCall(ISRestoreIndices(is, &spoints));
1923     PetscCall(ISDestroy(&is));
1924   }
1925   PetscCall(ISRestoreIndices(vIS, &values));
1926   PetscCall(ISDestroy(&vIS));
1927   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, N, points, PETSC_OWN_POINTER, is));
1928   PetscFunctionReturn(0);
1929 }
1930 
1931 /*@
1932   PetscSectionCreateGlobalSectionLabel - Create a section describing the global field layout using
1933   the local section and an SF describing the section point overlap.
1934 
1935   Collective on sf
1936 
1937   Input Parameters:
1938   + s - The PetscSection for the local field layout
1939   . sf - The SF describing parallel layout of the section points
1940   . includeConstraints - By default this is PETSC_FALSE, meaning that the global field vector will not possess constrained dofs
1941   . label - The label specifying the points
1942   - labelValue - The label stratum specifying the points
1943 
1944   Output Parameter:
1945   . gsection - The PetscSection for the global field layout
1946 
1947   Note: This gives negative sizes and offsets to points not owned by this process
1948 
1949   Level: developer
1950 
1951 .seealso: PetscSectionCreate()
1952 @*/
1953 PetscErrorCode PetscSectionCreateGlobalSectionLabel(PetscSection s, PetscSF sf, PetscBool includeConstraints, DMLabel label, PetscInt labelValue, PetscSection *gsection)
1954 {
1955   PetscInt      *neg = NULL, *tmpOff = NULL;
1956   PetscInt       pStart, pEnd, p, dof, cdof, off, globalOff = 0, nroots;
1957 
1958   PetscFunctionBegin;
1959   PetscValidHeaderSpecific(s, PETSC_SECTION_CLASSID, 1);
1960   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
1961   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 4);
1962   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject) s), gsection));
1963   PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
1964   PetscCall(PetscSectionSetChart(*gsection, pStart, pEnd));
1965   PetscCall(PetscSFGetGraph(sf, &nroots, NULL, NULL, NULL));
1966   if (nroots >= 0) {
1967     PetscCheckFalse(nroots < pEnd-pStart,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "PetscSF nroots %d < %d section size", nroots, pEnd-pStart);
1968     PetscCall(PetscCalloc1(nroots, &neg));
1969     if (nroots > pEnd-pStart) {
1970       PetscCall(PetscCalloc1(nroots, &tmpOff));
1971     } else {
1972       tmpOff = &(*gsection)->atlasDof[-pStart];
1973     }
1974   }
1975   /* Mark ghost points with negative dof */
1976   for (p = pStart; p < pEnd; ++p) {
1977     PetscInt value;
1978 
1979     PetscCall(DMLabelGetValue(label, p, &value));
1980     if (value != labelValue) continue;
1981     PetscCall(PetscSectionGetDof(s, p, &dof));
1982     PetscCall(PetscSectionSetDof(*gsection, p, dof));
1983     PetscCall(PetscSectionGetConstraintDof(s, p, &cdof));
1984     if (!includeConstraints && cdof > 0) PetscCall(PetscSectionSetConstraintDof(*gsection, p, cdof));
1985     if (neg) neg[p] = -(dof+1);
1986   }
1987   PetscCall(PetscSectionSetUpBC(*gsection));
1988   if (nroots >= 0) {
1989     PetscCall(PetscSFBcastBegin(sf, MPIU_INT, neg, tmpOff,MPI_REPLACE));
1990     PetscCall(PetscSFBcastEnd(sf, MPIU_INT, neg, tmpOff,MPI_REPLACE));
1991     if (nroots > pEnd-pStart) {
1992       for (p = pStart; p < pEnd; ++p) {if (tmpOff[p] < 0) (*gsection)->atlasDof[p-pStart] = tmpOff[p];}
1993     }
1994   }
1995   /* Calculate new sizes, get proccess offset, and calculate point offsets */
1996   for (p = 0, off = 0; p < pEnd-pStart; ++p) {
1997     cdof = (!includeConstraints && s->bc) ? s->bc->atlasDof[p] : 0;
1998     (*gsection)->atlasOff[p] = off;
1999     off += (*gsection)->atlasDof[p] > 0 ? (*gsection)->atlasDof[p]-cdof : 0;
2000   }
2001   PetscCallMPI(MPI_Scan(&off, &globalOff, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject) s)));
2002   globalOff -= off;
2003   for (p = 0, off = 0; p < pEnd-pStart; ++p) {
2004     (*gsection)->atlasOff[p] += globalOff;
2005     if (neg) neg[p] = -((*gsection)->atlasOff[p]+1);
2006   }
2007   /* Put in negative offsets for ghost points */
2008   if (nroots >= 0) {
2009     PetscCall(PetscSFBcastBegin(sf, MPIU_INT, neg, tmpOff,MPI_REPLACE));
2010     PetscCall(PetscSFBcastEnd(sf, MPIU_INT, neg, tmpOff,MPI_REPLACE));
2011     if (nroots > pEnd-pStart) {
2012       for (p = pStart; p < pEnd; ++p) {if (tmpOff[p] < 0) (*gsection)->atlasOff[p-pStart] = tmpOff[p];}
2013     }
2014   }
2015   if (nroots >= 0 && nroots > pEnd-pStart) PetscCall(PetscFree(tmpOff));
2016   PetscCall(PetscFree(neg));
2017   PetscFunctionReturn(0);
2018 }
2019 
2020 typedef struct _n_PetscSectionSym_Label
2021 {
2022   DMLabel           label;
2023   PetscCopyMode     *modes;
2024   PetscInt          *sizes;
2025   const PetscInt    ***perms;
2026   const PetscScalar ***rots;
2027   PetscInt          (*minMaxOrients)[2];
2028   PetscInt          numStrata; /* numStrata is only increasing, functions as a state */
2029 } PetscSectionSym_Label;
2030 
2031 static PetscErrorCode PetscSectionSymLabelReset(PetscSectionSym sym)
2032 {
2033   PetscInt              i, j;
2034   PetscSectionSym_Label *sl = (PetscSectionSym_Label *) sym->data;
2035 
2036   PetscFunctionBegin;
2037   for (i = 0; i <= sl->numStrata; i++) {
2038     if (sl->modes[i] == PETSC_OWN_POINTER || sl->modes[i] == PETSC_COPY_VALUES) {
2039       for (j = sl->minMaxOrients[i][0]; j < sl->minMaxOrients[i][1]; j++) {
2040         if (sl->perms[i]) PetscCall(PetscFree(sl->perms[i][j]));
2041         if (sl->rots[i]) PetscCall(PetscFree(sl->rots[i][j]));
2042       }
2043       if (sl->perms[i]) {
2044         const PetscInt **perms = &sl->perms[i][sl->minMaxOrients[i][0]];
2045 
2046         PetscCall(PetscFree(perms));
2047       }
2048       if (sl->rots[i]) {
2049         const PetscScalar **rots = &sl->rots[i][sl->minMaxOrients[i][0]];
2050 
2051         PetscCall(PetscFree(rots));
2052       }
2053     }
2054   }
2055   PetscCall(PetscFree5(sl->modes,sl->sizes,sl->perms,sl->rots,sl->minMaxOrients));
2056   PetscCall(DMLabelDestroy(&sl->label));
2057   sl->numStrata = 0;
2058   PetscFunctionReturn(0);
2059 }
2060 
2061 static PetscErrorCode PetscSectionSymDestroy_Label(PetscSectionSym sym)
2062 {
2063   PetscFunctionBegin;
2064   PetscCall(PetscSectionSymLabelReset(sym));
2065   PetscCall(PetscFree(sym->data));
2066   PetscFunctionReturn(0);
2067 }
2068 
2069 static PetscErrorCode PetscSectionSymView_Label(PetscSectionSym sym, PetscViewer viewer)
2070 {
2071   PetscSectionSym_Label *sl = (PetscSectionSym_Label *) sym->data;
2072   PetscBool             isAscii;
2073   DMLabel               label = sl->label;
2074   const char           *name;
2075 
2076   PetscFunctionBegin;
2077   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &isAscii));
2078   if (isAscii) {
2079     PetscInt          i, j, k;
2080     PetscViewerFormat format;
2081 
2082     PetscCall(PetscViewerGetFormat(viewer,&format));
2083     if (label) {
2084       PetscCall(PetscViewerGetFormat(viewer,&format));
2085       if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
2086         PetscCall(PetscViewerASCIIPushTab(viewer));
2087         PetscCall(DMLabelView(label, viewer));
2088         PetscCall(PetscViewerASCIIPopTab(viewer));
2089       } else {
2090         PetscCall(PetscObjectGetName((PetscObject) sl->label, &name));
2091         PetscCall(PetscViewerASCIIPrintf(viewer,"  Label '%s'\n",name));
2092       }
2093     } else {
2094       PetscCall(PetscViewerASCIIPrintf(viewer, "No label given\n"));
2095     }
2096     PetscCall(PetscViewerASCIIPushTab(viewer));
2097     for (i = 0; i <= sl->numStrata; i++) {
2098       PetscInt value = i < sl->numStrata ? label->stratumValues[i] : label->defaultValue;
2099 
2100       if (!(sl->perms[i] || sl->rots[i])) {
2101         PetscCall(PetscViewerASCIIPrintf(viewer, "Symmetry for stratum value %D (%D dofs per point): no symmetries\n", value, sl->sizes[i]));
2102       } else {
2103       PetscCall(PetscViewerASCIIPrintf(viewer, "Symmetry for stratum value %D (%D dofs per point):\n", value, sl->sizes[i]));
2104         PetscCall(PetscViewerASCIIPushTab(viewer));
2105         PetscCall(PetscViewerASCIIPrintf(viewer, "Orientation range: [%D, %D)\n", sl->minMaxOrients[i][0], sl->minMaxOrients[i][1]));
2106         if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
2107           PetscCall(PetscViewerASCIIPushTab(viewer));
2108           for (j = sl->minMaxOrients[i][0]; j < sl->minMaxOrients[i][1]; j++) {
2109             if (!((sl->perms[i] && sl->perms[i][j]) || (sl->rots[i] && sl->rots[i][j]))) {
2110               PetscCall(PetscViewerASCIIPrintf(viewer, "Orientation %D: identity\n",j));
2111             } else {
2112               PetscInt tab;
2113 
2114               PetscCall(PetscViewerASCIIPrintf(viewer, "Orientation %D:\n",j));
2115               PetscCall(PetscViewerASCIIPushTab(viewer));
2116               PetscCall(PetscViewerASCIIGetTab(viewer,&tab));
2117               if (sl->perms[i] && sl->perms[i][j]) {
2118                 PetscCall(PetscViewerASCIIPrintf(viewer,"Permutation:"));
2119                 PetscCall(PetscViewerASCIISetTab(viewer,0));
2120                 for (k = 0; k < sl->sizes[i]; k++) PetscCall(PetscViewerASCIIPrintf(viewer," %D",sl->perms[i][j][k]));
2121                 PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
2122                 PetscCall(PetscViewerASCIISetTab(viewer,tab));
2123               }
2124               if (sl->rots[i] && sl->rots[i][j]) {
2125                 PetscCall(PetscViewerASCIIPrintf(viewer,"Rotations:  "));
2126                 PetscCall(PetscViewerASCIISetTab(viewer,0));
2127 #if defined(PETSC_USE_COMPLEX)
2128                 for (k = 0; k < sl->sizes[i]; k++) PetscCall(PetscViewerASCIIPrintf(viewer," %+f+i*%+f",PetscRealPart(sl->rots[i][j][k]),PetscImaginaryPart(sl->rots[i][j][k])));
2129 #else
2130                 for (k = 0; k < sl->sizes[i]; k++) PetscCall(PetscViewerASCIIPrintf(viewer," %+f",sl->rots[i][j][k]));
2131 #endif
2132                 PetscCall(PetscViewerASCIIPrintf(viewer,"\n"));
2133                 PetscCall(PetscViewerASCIISetTab(viewer,tab));
2134               }
2135               PetscCall(PetscViewerASCIIPopTab(viewer));
2136             }
2137           }
2138           PetscCall(PetscViewerASCIIPopTab(viewer));
2139         }
2140         PetscCall(PetscViewerASCIIPopTab(viewer));
2141       }
2142     }
2143     PetscCall(PetscViewerASCIIPopTab(viewer));
2144   }
2145   PetscFunctionReturn(0);
2146 }
2147 
2148 /*@
2149   PetscSectionSymLabelSetLabel - set the label whose strata will define the points that receive symmetries
2150 
2151   Logically collective on sym
2152 
2153   Input parameters:
2154 + sym - the section symmetries
2155 - label - the DMLabel describing the types of points
2156 
2157   Level: developer:
2158 
2159 .seealso: PetscSectionSymLabelSetStratum(), PetscSectionSymCreateLabel(), PetscSectionGetPointSyms()
2160 @*/
2161 PetscErrorCode PetscSectionSymLabelSetLabel(PetscSectionSym sym, DMLabel label)
2162 {
2163   PetscSectionSym_Label *sl;
2164 
2165   PetscFunctionBegin;
2166   PetscValidHeaderSpecific(sym,PETSC_SECTION_SYM_CLASSID,1);
2167   sl = (PetscSectionSym_Label *) sym->data;
2168   if (sl->label && sl->label != label) PetscCall(PetscSectionSymLabelReset(sym));
2169   if (label) {
2170     sl->label = label;
2171     PetscCall(PetscObjectReference((PetscObject) label));
2172     PetscCall(DMLabelGetNumValues(label,&sl->numStrata));
2173     PetscCall(PetscMalloc5(sl->numStrata+1,&sl->modes,sl->numStrata+1,&sl->sizes,sl->numStrata+1,&sl->perms,sl->numStrata+1,&sl->rots,sl->numStrata+1,&sl->minMaxOrients));
2174     PetscCall(PetscMemzero((void *) sl->modes,(sl->numStrata+1)*sizeof(PetscCopyMode)));
2175     PetscCall(PetscMemzero((void *) sl->sizes,(sl->numStrata+1)*sizeof(PetscInt)));
2176     PetscCall(PetscMemzero((void *) sl->perms,(sl->numStrata+1)*sizeof(const PetscInt **)));
2177     PetscCall(PetscMemzero((void *) sl->rots,(sl->numStrata+1)*sizeof(const PetscScalar **)));
2178     PetscCall(PetscMemzero((void *) sl->minMaxOrients,(sl->numStrata+1)*sizeof(PetscInt[2])));
2179   }
2180   PetscFunctionReturn(0);
2181 }
2182 
2183 /*@C
2184   PetscSectionSymLabelGetStratum - get the symmetries for the orientations of a stratum
2185 
2186   Logically collective on sym
2187 
2188   Input Parameters:
2189 + sym       - the section symmetries
2190 - stratum   - the stratum value in the label that we are assigning symmetries for
2191 
2192   Output Parameters:
2193 + size      - the number of dofs for points in the stratum of the label
2194 . minOrient - the smallest orientation for a point in this stratum
2195 . maxOrient - one greater than the largest orientation for a ppoint in this stratum (i.e., orientations are in the range [minOrient, maxOrient))
2196 . perms     - NULL if there are no permutations, or (maxOrient - minOrient) permutations, one for each orientation.  A NULL permutation is the identity
2197 - rots      - NULL if there are no rotations, or (maxOrient - minOrient) sets of rotations, one for each orientation.  A NULL set of orientations is the identity
2198 
2199   Level: developer
2200 
2201 .seealso: PetscSectionSymLabelSetStratum(), PetscSectionSymCreate(), PetscSectionSetSym(), PetscSectionGetPointSyms(), PetscSectionSymCreateLabel()
2202 @*/
2203 PetscErrorCode PetscSectionSymLabelGetStratum(PetscSectionSym sym, PetscInt stratum, PetscInt *size, PetscInt *minOrient, PetscInt *maxOrient, const PetscInt ***perms, const PetscScalar ***rots)
2204 {
2205   PetscSectionSym_Label *sl;
2206   const char            *name;
2207   PetscInt               i;
2208 
2209   PetscFunctionBegin;
2210   PetscValidHeaderSpecific(sym,PETSC_SECTION_SYM_CLASSID,1);
2211   sl = (PetscSectionSym_Label *) sym->data;
2212   PetscCheck(sl->label, PetscObjectComm((PetscObject) sym), PETSC_ERR_ARG_WRONGSTATE, "No label set yet");
2213   for (i = 0; i <= sl->numStrata; i++) {
2214     PetscInt value = (i < sl->numStrata) ? sl->label->stratumValues[i] : sl->label->defaultValue;
2215 
2216     if (stratum == value) break;
2217   }
2218   PetscCall(PetscObjectGetName((PetscObject) sl->label, &name));
2219   PetscCheck(i <= sl->numStrata, PetscObjectComm((PetscObject) sym), PETSC_ERR_ARG_OUTOFRANGE, "Stratum %" PetscInt_FMT " not found in label %s", stratum, name);
2220   if (size)      {PetscValidIntPointer(size, 3);      *size      = sl->sizes[i];}
2221   if (minOrient) {PetscValidIntPointer(minOrient, 4); *minOrient = sl->minMaxOrients[i][0];}
2222   if (maxOrient) {PetscValidIntPointer(maxOrient, 5); *maxOrient = sl->minMaxOrients[i][1];}
2223   if (perms)     {PetscValidPointer(perms, 6);        *perms     = sl->perms[i] ? &sl->perms[i][sl->minMaxOrients[i][0]] : NULL;}
2224   if (rots)      {PetscValidPointer(rots, 7);         *rots      = sl->rots[i]  ? &sl->rots[i][sl->minMaxOrients[i][0]]  : NULL;}
2225   PetscFunctionReturn(0);
2226 }
2227 
2228 /*@C
2229   PetscSectionSymLabelSetStratum - set the symmetries for the orientations of a stratum
2230 
2231   Logically collective on sym
2232 
2233   InputParameters:
2234 + sym       - the section symmetries
2235 . stratum   - the stratum value in the label that we are assigning symmetries for
2236 . size      - the number of dofs for points in the stratum of the label
2237 . minOrient - the smallest orientation for a point in this stratum
2238 . maxOrient - one greater than the largest orientation for a ppoint in this stratum (i.e., orientations are in the range [minOrient, maxOrient))
2239 . mode      - how sym should copy the perms and rots arrays
2240 . perms     - NULL if there are no permutations, or (maxOrient - minOrient) permutations, one for each orientation.  A NULL permutation is the identity
2241 - rots      - NULL if there are no rotations, or (maxOrient - minOrient) sets of rotations, one for each orientation.  A NULL set of orientations is the identity
2242 
2243   Level: developer
2244 
2245 .seealso: PetscSectionSymLabelGetStratum(), PetscSectionSymCreate(), PetscSectionSetSym(), PetscSectionGetPointSyms(), PetscSectionSymCreateLabel()
2246 @*/
2247 PetscErrorCode PetscSectionSymLabelSetStratum(PetscSectionSym sym, PetscInt stratum, PetscInt size, PetscInt minOrient, PetscInt maxOrient, PetscCopyMode mode, const PetscInt **perms, const PetscScalar **rots)
2248 {
2249   PetscSectionSym_Label *sl;
2250   const char            *name;
2251   PetscInt               i, j, k;
2252 
2253   PetscFunctionBegin;
2254   PetscValidHeaderSpecific(sym,PETSC_SECTION_SYM_CLASSID,1);
2255   sl = (PetscSectionSym_Label *) sym->data;
2256   PetscCheck(sl->label, PetscObjectComm((PetscObject) sym), PETSC_ERR_ARG_WRONGSTATE, "No label set yet");
2257   for (i = 0; i <= sl->numStrata; i++) {
2258     PetscInt value = (i < sl->numStrata) ? sl->label->stratumValues[i] : sl->label->defaultValue;
2259 
2260     if (stratum == value) break;
2261   }
2262   PetscCall(PetscObjectGetName((PetscObject) sl->label, &name));
2263   PetscCheck(i <= sl->numStrata, PetscObjectComm((PetscObject) sym), PETSC_ERR_ARG_OUTOFRANGE, "Stratum %D not found in label %s", stratum, name);
2264   sl->sizes[i] = size;
2265   sl->modes[i] = mode;
2266   sl->minMaxOrients[i][0] = minOrient;
2267   sl->minMaxOrients[i][1] = maxOrient;
2268   if (mode == PETSC_COPY_VALUES) {
2269     if (perms) {
2270       PetscInt    **ownPerms;
2271 
2272       PetscCall(PetscCalloc1(maxOrient - minOrient,&ownPerms));
2273       for (j = 0; j < maxOrient-minOrient; j++) {
2274         if (perms[j]) {
2275           PetscCall(PetscMalloc1(size,&ownPerms[j]));
2276           for (k = 0; k < size; k++) {ownPerms[j][k] = perms[j][k];}
2277         }
2278       }
2279       sl->perms[i] = (const PetscInt **) &ownPerms[-minOrient];
2280     }
2281     if (rots) {
2282       PetscScalar **ownRots;
2283 
2284       PetscCall(PetscCalloc1(maxOrient - minOrient,&ownRots));
2285       for (j = 0; j < maxOrient-minOrient; j++) {
2286         if (rots[j]) {
2287           PetscCall(PetscMalloc1(size,&ownRots[j]));
2288           for (k = 0; k < size; k++) {ownRots[j][k] = rots[j][k];}
2289         }
2290       }
2291       sl->rots[i] = (const PetscScalar **) &ownRots[-minOrient];
2292     }
2293   } else {
2294     sl->perms[i] = perms ? &perms[-minOrient] : NULL;
2295     sl->rots[i]  = rots ? &rots[-minOrient] : NULL;
2296   }
2297   PetscFunctionReturn(0);
2298 }
2299 
2300 static PetscErrorCode PetscSectionSymGetPoints_Label(PetscSectionSym sym, PetscSection section, PetscInt numPoints, const PetscInt *points, const PetscInt **perms, const PetscScalar **rots)
2301 {
2302   PetscInt              i, j, numStrata;
2303   PetscSectionSym_Label *sl;
2304   DMLabel               label;
2305 
2306   PetscFunctionBegin;
2307   sl = (PetscSectionSym_Label *) sym->data;
2308   numStrata = sl->numStrata;
2309   label     = sl->label;
2310   for (i = 0; i < numPoints; i++) {
2311     PetscInt point = points[2*i];
2312     PetscInt ornt  = points[2*i+1];
2313 
2314     for (j = 0; j < numStrata; j++) {
2315       if (label->validIS[j]) {
2316         PetscInt k;
2317 
2318         PetscCall(ISLocate(label->points[j],point,&k));
2319         if (k >= 0) break;
2320       } else {
2321         PetscBool has;
2322 
2323         PetscCall(PetscHSetIHas(label->ht[j], point, &has));
2324         if (has) break;
2325       }
2326     }
2327     PetscCheckFalse((sl->minMaxOrients[j][1] > sl->minMaxOrients[j][0]) && (ornt < sl->minMaxOrients[j][0] || ornt >= sl->minMaxOrients[j][1]),PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"point %D orientation %D not in range [%D, %D) for stratum %D",point,ornt,sl->minMaxOrients[j][0],sl->minMaxOrients[j][1],j < numStrata ? label->stratumValues[j] : label->defaultValue);
2328     if (perms) {perms[i] = sl->perms[j] ? sl->perms[j][ornt] : NULL;}
2329     if (rots) {rots[i]  = sl->rots[j] ? sl->rots[j][ornt] : NULL;}
2330   }
2331   PetscFunctionReturn(0);
2332 }
2333 
2334 static PetscErrorCode PetscSectionSymCopy_Label(PetscSectionSym sym, PetscSectionSym nsym)
2335 {
2336   PetscSectionSym_Label *sl = (PetscSectionSym_Label *) nsym->data;
2337   IS                     valIS;
2338   const PetscInt        *values;
2339   PetscInt               Nv, v;
2340 
2341   PetscFunctionBegin;
2342   PetscCall(DMLabelGetNumValues(sl->label, &Nv));
2343   PetscCall(DMLabelGetValueIS(sl->label, &valIS));
2344   PetscCall(ISGetIndices(valIS, &values));
2345   for (v = 0; v < Nv; ++v) {
2346     const PetscInt      val = values[v];
2347     PetscInt            size, minOrient, maxOrient;
2348     const PetscInt    **perms;
2349     const PetscScalar **rots;
2350 
2351     PetscCall(PetscSectionSymLabelGetStratum(sym,  val, &size, &minOrient, &maxOrient, &perms, &rots));
2352     PetscCall(PetscSectionSymLabelSetStratum(nsym, val,  size,  minOrient,  maxOrient, PETSC_COPY_VALUES, perms, rots));
2353   }
2354   PetscCall(ISDestroy(&valIS));
2355   PetscFunctionReturn(0);
2356 }
2357 
2358 static PetscErrorCode PetscSectionSymDistribute_Label(PetscSectionSym sym, PetscSF migrationSF, PetscSectionSym *dsym)
2359 {
2360   PetscSectionSym_Label *sl = (PetscSectionSym_Label *) sym->data;
2361   DMLabel                dlabel;
2362 
2363   PetscFunctionBegin;
2364   PetscCall(DMLabelDistribute(sl->label, migrationSF, &dlabel));
2365   PetscCall(PetscSectionSymCreateLabel(PetscObjectComm((PetscObject) sym), dlabel, dsym));
2366   PetscCall(DMLabelDestroy(&dlabel));
2367   PetscCall(PetscSectionSymCopy(sym, *dsym));
2368   PetscFunctionReturn(0);
2369 }
2370 
2371 PetscErrorCode PetscSectionSymCreate_Label(PetscSectionSym sym)
2372 {
2373   PetscSectionSym_Label *sl;
2374 
2375   PetscFunctionBegin;
2376   PetscCall(PetscNewLog(sym,&sl));
2377   sym->ops->getpoints  = PetscSectionSymGetPoints_Label;
2378   sym->ops->distribute = PetscSectionSymDistribute_Label;
2379   sym->ops->copy       = PetscSectionSymCopy_Label;
2380   sym->ops->view       = PetscSectionSymView_Label;
2381   sym->ops->destroy    = PetscSectionSymDestroy_Label;
2382   sym->data            = (void *) sl;
2383   PetscFunctionReturn(0);
2384 }
2385 
2386 /*@
2387   PetscSectionSymCreateLabel - Create a section symmetry that assigns one symmetry to each stratum of a label
2388 
2389   Collective
2390 
2391   Input Parameters:
2392 + comm - the MPI communicator for the new symmetry
2393 - label - the label defining the strata
2394 
2395   Output Parameters:
2396 . sym - the section symmetries
2397 
2398   Level: developer
2399 
2400 .seealso: PetscSectionSymCreate(), PetscSectionSetSym(), PetscSectionGetSym(), PetscSectionSymLabelSetStratum(), PetscSectionGetPointSyms()
2401 @*/
2402 PetscErrorCode PetscSectionSymCreateLabel(MPI_Comm comm, DMLabel label, PetscSectionSym *sym)
2403 {
2404   PetscFunctionBegin;
2405   PetscCall(DMInitializePackage());
2406   PetscCall(PetscSectionSymCreate(comm,sym));
2407   PetscCall(PetscSectionSymSetType(*sym,PETSCSECTIONSYMLABEL));
2408   PetscCall(PetscSectionSymLabelSetLabel(*sym,label));
2409   PetscFunctionReturn(0);
2410 }
2411