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