xref: /petsc/src/dm/interface/dm.c (revision a3f1d042deeee8d591d0e166df91c7782e45ac59)
1 #include <petscvec.h>
2 #include <petsc/private/dmimpl.h>      /*I      "petscdm.h"          I*/
3 #include <petsc/private/dmlabelimpl.h> /*I      "petscdmlabel.h"     I*/
4 #include <petsc/private/petscdsimpl.h> /*I      "petscds.h"     I*/
5 #include <petscdmplex.h>
6 #include <petscdmceed.h>
7 #include <petscdmfield.h>
8 #include <petscsf.h>
9 #include <petscds.h>
10 
11 #ifdef PETSC_HAVE_LIBCEED
12   #include <petscfeceed.h>
13 #endif
14 
15 #if !defined(PETSC_HAVE_WINDOWS_COMPILERS)
16   #include <petsc/private/valgrind/memcheck.h>
17 #endif
18 
19 PetscClassId DM_CLASSID;
20 PetscClassId DMLABEL_CLASSID;
21 PetscLogEvent DM_Convert, DM_GlobalToLocal, DM_LocalToGlobal, DM_LocalToLocal, DM_LocatePoints, DM_Coarsen, DM_Refine, DM_CreateInterpolation, DM_CreateRestriction, DM_CreateInjection, DM_CreateMatrix, DM_CreateMassMatrix, DM_Load, DM_AdaptInterpolator, DM_ProjectFunction;
22 
23 const char *const DMBoundaryTypes[]          = {"NONE", "GHOSTED", "MIRROR", "PERIODIC", "TWIST", "DMBoundaryType", "DM_BOUNDARY_", NULL};
24 const char *const DMBoundaryConditionTypes[] = {"INVALID", "ESSENTIAL", "NATURAL", "INVALID", "INVALID", "ESSENTIAL_FIELD", "NATURAL_FIELD", "INVALID", "INVALID", "ESSENTIAL_BD_FIELD", "NATURAL_RIEMANN", "DMBoundaryConditionType", "DM_BC_", NULL};
25 const char *const DMBlockingTypes[]          = {"TOPOLOGICAL_POINT", "FIELD_NODE", "DMBlockingType", "DM_BLOCKING_", NULL};
26 const char *const DMPolytopeTypes[] =
27   {"vertex",  "segment",      "tensor_segment", "triangle", "quadrilateral",  "tensor_quad",  "tetrahedron", "hexahedron", "triangular_prism", "tensor_triangular_prism", "tensor_quadrilateral_prism", "pyramid", "FV_ghost_cell", "interior_ghost_cell",
28    "unknown", "unknown_cell", "unknown_face",   "invalid",  "DMPolytopeType", "DM_POLYTOPE_", NULL};
29 const char *const DMCopyLabelsModes[] = {"replace", "keep", "fail", "DMCopyLabelsMode", "DM_COPY_LABELS_", NULL};
30 
31 /*@
32   DMCreate - Creates an empty `DM` object. `DM`s are the abstract objects in PETSc that mediate between meshes and discretizations and the
33   algebraic solvers, time integrators, and optimization algorithms.
34 
35   Collective
36 
37   Input Parameter:
38 . comm - The communicator for the `DM` object
39 
40   Output Parameter:
41 . dm - The `DM` object
42 
43   Level: beginner
44 
45   Notes:
46   See `DMType` for a brief summary of available `DM`.
47 
48   The type must then be set with `DMSetType()`. If you never call `DMSetType()` it will generate an
49   error when you try to use the dm.
50 
51 .seealso: [](ch_dmbase), `DM`, `DMSetType()`, `DMType`, `DMDACreate()`, `DMDA`, `DMSLICED`, `DMCOMPOSITE`, `DMPLEX`, `DMMOAB`, `DMNETWORK`
52 @*/
53 PetscErrorCode DMCreate(MPI_Comm comm, DM *dm)
54 {
55   DM      v;
56   PetscDS ds;
57 
58   PetscFunctionBegin;
59   PetscAssertPointer(dm, 2);
60 
61   PetscCall(DMInitializePackage());
62   PetscCall(PetscHeaderCreate(v, DM_CLASSID, "DM", "Distribution Manager", "DM", comm, DMDestroy, DMView));
63   ((PetscObject)v)->non_cyclic_references = &DMCountNonCyclicReferences;
64   v->setupcalled                          = PETSC_FALSE;
65   v->setfromoptionscalled                 = PETSC_FALSE;
66   v->ltogmap                              = NULL;
67   v->bind_below                           = 0;
68   v->bs                                   = 1;
69   v->coloringtype                         = IS_COLORING_GLOBAL;
70   PetscCall(PetscSFCreate(comm, &v->sf));
71   PetscCall(PetscSFCreate(comm, &v->sectionSF));
72   v->labels                    = NULL;
73   v->adjacency[0]              = PETSC_FALSE;
74   v->adjacency[1]              = PETSC_TRUE;
75   v->depthLabel                = NULL;
76   v->celltypeLabel             = NULL;
77   v->localSection              = NULL;
78   v->globalSection             = NULL;
79   v->defaultConstraint.section = NULL;
80   v->defaultConstraint.mat     = NULL;
81   v->defaultConstraint.bias    = NULL;
82   v->coordinates[0].dim        = PETSC_DEFAULT;
83   v->coordinates[1].dim        = PETSC_DEFAULT;
84   v->sparseLocalize            = PETSC_TRUE;
85   v->dim                       = PETSC_DETERMINE;
86   {
87     PetscInt i;
88     for (i = 0; i < 10; ++i) {
89       v->nullspaceConstructors[i]     = NULL;
90       v->nearnullspaceConstructors[i] = NULL;
91     }
92   }
93   PetscCall(PetscDSCreate(PETSC_COMM_SELF, &ds));
94   PetscCall(DMSetRegionDS(v, NULL, NULL, ds, NULL));
95   PetscCall(PetscDSDestroy(&ds));
96   PetscCall(PetscHMapAuxCreate(&v->auxData));
97   v->dmBC              = NULL;
98   v->coarseMesh        = NULL;
99   v->outputSequenceNum = -1;
100   v->outputSequenceVal = 0.0;
101   PetscCall(DMSetVecType(v, VECSTANDARD));
102   PetscCall(DMSetMatType(v, MATAIJ));
103 
104   *dm = v;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 /*@
109   DMClone - Creates a `DM` object with the same topology as the original.
110 
111   Collective
112 
113   Input Parameter:
114 . dm - The original `DM` object
115 
116   Output Parameter:
117 . newdm - The new `DM` object
118 
119   Level: beginner
120 
121   Notes:
122   For some `DM` implementations this is a shallow clone, the result of which may share (reference counted) information with its parent. For example,
123   `DMClone()` applied to a `DMPLEX` object will result in a new `DMPLEX` that shares the topology with the original `DMPLEX`. It does not
124   share the `PetscSection` of the original `DM`.
125 
126   The clone is considered set up if the original has been set up.
127 
128   Use `DMConvert()` for a general way to create new `DM` from a given `DM`
129 
130 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMCreate()`, `DMSetType()`, `DMSetLocalSection()`, `DMSetGlobalSection()`, `DMPLEX`, `DMConvert()`
131 @*/
132 PetscErrorCode DMClone(DM dm, DM *newdm)
133 {
134   PetscSF              sf;
135   Vec                  coords;
136   void                *ctx;
137   MatOrderingType      otype;
138   DMReorderDefaultFlag flg;
139   PetscInt             dim, cdim, i;
140 
141   PetscFunctionBegin;
142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
143   PetscAssertPointer(newdm, 2);
144   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), newdm));
145   PetscCall(DMCopyLabels(dm, *newdm, PETSC_COPY_VALUES, PETSC_TRUE, DM_COPY_LABELS_FAIL));
146   (*newdm)->leveldown     = dm->leveldown;
147   (*newdm)->levelup       = dm->levelup;
148   (*newdm)->prealloc_only = dm->prealloc_only;
149   (*newdm)->prealloc_skip = dm->prealloc_skip;
150   PetscCall(PetscFree((*newdm)->vectype));
151   PetscCall(PetscStrallocpy(dm->vectype, (char **)&(*newdm)->vectype));
152   PetscCall(PetscFree((*newdm)->mattype));
153   PetscCall(PetscStrallocpy(dm->mattype, (char **)&(*newdm)->mattype));
154   PetscCall(DMGetDimension(dm, &dim));
155   PetscCall(DMSetDimension(*newdm, dim));
156   PetscTryTypeMethod(dm, clone, newdm);
157   (*newdm)->setupcalled = dm->setupcalled;
158   PetscCall(DMGetPointSF(dm, &sf));
159   PetscCall(DMSetPointSF(*newdm, sf));
160   PetscCall(DMGetApplicationContext(dm, &ctx));
161   PetscCall(DMSetApplicationContext(*newdm, ctx));
162   PetscCall(DMReorderSectionGetDefault(dm, &flg));
163   PetscCall(DMReorderSectionSetDefault(*newdm, flg));
164   PetscCall(DMReorderSectionGetType(dm, &otype));
165   PetscCall(DMReorderSectionSetType(*newdm, otype));
166   for (i = 0; i < 2; ++i) {
167     if (dm->coordinates[i].dm) {
168       DM           ncdm;
169       PetscSection cs;
170       PetscInt     pEnd = -1, pEndMax = -1;
171 
172       PetscCall(DMGetLocalSection(dm->coordinates[i].dm, &cs));
173       if (cs) PetscCall(PetscSectionGetChart(cs, NULL, &pEnd));
174       PetscCallMPI(MPIU_Allreduce(&pEnd, &pEndMax, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
175       if (pEndMax >= 0) {
176         PetscCall(DMClone(dm->coordinates[i].dm, &ncdm));
177         PetscCall(DMCopyDisc(dm->coordinates[i].dm, ncdm));
178         PetscCall(DMSetLocalSection(ncdm, cs));
179         if (dm->coordinates[i].dm->periodic.setup) {
180           ncdm->periodic.setup = dm->coordinates[i].dm->periodic.setup;
181           PetscCall(ncdm->periodic.setup(ncdm));
182         }
183         if (i) PetscCall(DMSetCellCoordinateDM(*newdm, ncdm));
184         else PetscCall(DMSetCoordinateDM(*newdm, ncdm));
185         PetscCall(DMDestroy(&ncdm));
186       }
187     }
188   }
189   PetscCall(DMGetCoordinateDim(dm, &cdim));
190   PetscCall(DMSetCoordinateDim(*newdm, cdim));
191   PetscCall(DMGetCoordinatesLocal(dm, &coords));
192   if (coords) {
193     PetscCall(DMSetCoordinatesLocal(*newdm, coords));
194   } else {
195     PetscCall(DMGetCoordinates(dm, &coords));
196     if (coords) PetscCall(DMSetCoordinates(*newdm, coords));
197   }
198   PetscCall(DMGetCellCoordinatesLocal(dm, &coords));
199   if (coords) {
200     PetscCall(DMSetCellCoordinatesLocal(*newdm, coords));
201   } else {
202     PetscCall(DMGetCellCoordinates(dm, &coords));
203     if (coords) PetscCall(DMSetCellCoordinates(*newdm, coords));
204   }
205   {
206     const PetscReal *maxCell, *Lstart, *L;
207 
208     PetscCall(DMGetPeriodicity(dm, &maxCell, &Lstart, &L));
209     PetscCall(DMSetPeriodicity(*newdm, maxCell, Lstart, L));
210   }
211   {
212     PetscBool useCone, useClosure;
213 
214     PetscCall(DMGetAdjacency(dm, PETSC_DEFAULT, &useCone, &useClosure));
215     PetscCall(DMSetAdjacency(*newdm, PETSC_DEFAULT, useCone, useClosure));
216   }
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 /*@
221   DMSetVecType - Sets the type of vector to be created with `DMCreateLocalVector()` and `DMCreateGlobalVector()`
222 
223   Logically Collective
224 
225   Input Parameters:
226 + dm    - initial distributed array
227 - ctype - the vector type, for example `VECSTANDARD`, `VECCUDA`, or `VECVIENNACL`
228 
229   Options Database Key:
230 . -dm_vec_type ctype - the type of vector to create
231 
232   Level: intermediate
233 
234 .seealso: [](ch_dmbase), `DM`, `DMCreate()`, `DMDestroy()`, `DMDAInterpolationType`, `VecType`, `DMGetVecType()`, `DMSetMatType()`, `DMGetMatType()`,
235           `VECSTANDARD`, `VECCUDA`, `VECVIENNACL`, `DMCreateLocalVector()`, `DMCreateGlobalVector()`
236 @*/
237 PetscErrorCode DMSetVecType(DM dm, VecType ctype)
238 {
239   char *tmp;
240 
241   PetscFunctionBegin;
242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
243   PetscAssertPointer(ctype, 2);
244   tmp = (char *)dm->vectype;
245   PetscCall(PetscStrallocpy(ctype, (char **)&dm->vectype));
246   PetscCall(PetscFree(tmp));
247   PetscFunctionReturn(PETSC_SUCCESS);
248 }
249 
250 /*@
251   DMGetVecType - Gets the type of vector created with `DMCreateLocalVector()` and `DMCreateGlobalVector()`
252 
253   Logically Collective
254 
255   Input Parameter:
256 . da - initial distributed array
257 
258   Output Parameter:
259 . ctype - the vector type
260 
261   Level: intermediate
262 
263 .seealso: [](ch_dmbase), `DM`, `DMCreate()`, `DMDestroy()`, `DMDAInterpolationType`, `VecType`, `DMSetMatType()`, `DMGetMatType()`, `DMSetVecType()`
264 @*/
265 PetscErrorCode DMGetVecType(DM da, VecType *ctype)
266 {
267   PetscFunctionBegin;
268   PetscValidHeaderSpecific(da, DM_CLASSID, 1);
269   *ctype = da->vectype;
270   PetscFunctionReturn(PETSC_SUCCESS);
271 }
272 
273 /*@
274   VecGetDM - Gets the `DM` defining the data layout of the vector
275 
276   Not Collective
277 
278   Input Parameter:
279 . v - The `Vec`
280 
281   Output Parameter:
282 . dm - The `DM`
283 
284   Level: intermediate
285 
286   Note:
287   A `Vec` may not have a `DM` associated with it.
288 
289 .seealso: [](ch_dmbase), `DM`, `VecSetDM()`, `DMGetLocalVector()`, `DMGetGlobalVector()`, `DMSetVecType()`
290 @*/
291 PetscErrorCode VecGetDM(Vec v, DM *dm)
292 {
293   PetscFunctionBegin;
294   PetscValidHeaderSpecific(v, VEC_CLASSID, 1);
295   PetscAssertPointer(dm, 2);
296   PetscCall(PetscObjectQuery((PetscObject)v, "__PETSc_dm", (PetscObject *)dm));
297   PetscFunctionReturn(PETSC_SUCCESS);
298 }
299 
300 /*@
301   VecSetDM - Sets the `DM` defining the data layout of the vector.
302 
303   Not Collective
304 
305   Input Parameters:
306 + v  - The `Vec`
307 - dm - The `DM`
308 
309   Level: developer
310 
311   Notes:
312   This is rarely used, generally one uses `DMGetLocalVector()` or  `DMGetGlobalVector()` to create a vector associated with a given `DM`
313 
314   This is NOT the same as `DMCreateGlobalVector()` since it does not change the view methods or perform other customization, but merely sets the `DM` member.
315 
316 .seealso: [](ch_dmbase), `DM`, `VecGetDM()`, `DMGetLocalVector()`, `DMGetGlobalVector()`, `DMSetVecType()`
317 @*/
318 PetscErrorCode VecSetDM(Vec v, DM dm)
319 {
320   PetscFunctionBegin;
321   PetscValidHeaderSpecific(v, VEC_CLASSID, 1);
322   if (dm) PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
323   PetscCall(PetscObjectCompose((PetscObject)v, "__PETSc_dm", (PetscObject)dm));
324   PetscFunctionReturn(PETSC_SUCCESS);
325 }
326 
327 /*@
328   DMSetISColoringType - Sets the type of coloring, `IS_COLORING_GLOBAL` or `IS_COLORING_LOCAL` that is created by the `DM`
329 
330   Logically Collective
331 
332   Input Parameters:
333 + dm    - the `DM` context
334 - ctype - the matrix type
335 
336   Options Database Key:
337 . -dm_is_coloring_type - global or local
338 
339   Level: intermediate
340 
341 .seealso: [](ch_dmbase), `DM`, `DMDACreate1d()`, `DMDACreate2d()`, `DMDACreate3d()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatrixPreallocateOnly()`, `MatType`, `DMGetMatType()`,
342           `DMGetISColoringType()`, `ISColoringType`, `IS_COLORING_GLOBAL`, `IS_COLORING_LOCAL`
343 @*/
344 PetscErrorCode DMSetISColoringType(DM dm, ISColoringType ctype)
345 {
346   PetscFunctionBegin;
347   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
348   dm->coloringtype = ctype;
349   PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 
352 /*@
353   DMGetISColoringType - Gets the type of coloring, `IS_COLORING_GLOBAL` or `IS_COLORING_LOCAL` that is created by the `DM`
354 
355   Logically Collective
356 
357   Input Parameter:
358 . dm - the `DM` context
359 
360   Output Parameter:
361 . ctype - the matrix type
362 
363   Options Database Key:
364 . -dm_is_coloring_type - global or local
365 
366   Level: intermediate
367 
368 .seealso: [](ch_dmbase), `DM`, `DMDACreate1d()`, `DMDACreate2d()`, `DMDACreate3d()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatrixPreallocateOnly()`, `MatType`, `DMGetMatType()`,
369           `ISColoringType`, `IS_COLORING_GLOBAL`, `IS_COLORING_LOCAL`
370 @*/
371 PetscErrorCode DMGetISColoringType(DM dm, ISColoringType *ctype)
372 {
373   PetscFunctionBegin;
374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
375   *ctype = dm->coloringtype;
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 /*@
380   DMSetMatType - Sets the type of matrix created with `DMCreateMatrix()`
381 
382   Logically Collective
383 
384   Input Parameters:
385 + dm    - the `DM` context
386 - ctype - the matrix type, for example `MATMPIAIJ`
387 
388   Options Database Key:
389 . -dm_mat_type ctype - the type of the matrix to create, for example mpiaij
390 
391   Level: intermediate
392 
393 .seealso: [](ch_dmbase), `DM`, `MatType`, `DMDACreate1d()`, `DMDACreate2d()`, `DMDACreate3d()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatrixPreallocateOnly()`, `DMGetMatType()`, `DMCreateGlobalVector()`, `DMCreateLocalVector()`
394 @*/
395 PetscErrorCode DMSetMatType(DM dm, MatType ctype)
396 {
397   char *tmp;
398 
399   PetscFunctionBegin;
400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
401   PetscAssertPointer(ctype, 2);
402   tmp = (char *)dm->mattype;
403   PetscCall(PetscStrallocpy(ctype, (char **)&dm->mattype));
404   PetscCall(PetscFree(tmp));
405   PetscFunctionReturn(PETSC_SUCCESS);
406 }
407 
408 /*@
409   DMGetMatType - Gets the type of matrix that would be created with `DMCreateMatrix()`
410 
411   Logically Collective
412 
413   Input Parameter:
414 . dm - the `DM` context
415 
416   Output Parameter:
417 . ctype - the matrix type
418 
419   Level: intermediate
420 
421 .seealso: [](ch_dmbase), `DM`, `DMDACreate1d()`, `DMDACreate2d()`, `DMDACreate3d()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatrixPreallocateOnly()`, `MatType`, `DMSetMatType()`
422 @*/
423 PetscErrorCode DMGetMatType(DM dm, MatType *ctype)
424 {
425   PetscFunctionBegin;
426   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
427   *ctype = dm->mattype;
428   PetscFunctionReturn(PETSC_SUCCESS);
429 }
430 
431 /*@
432   MatGetDM - Gets the `DM` defining the data layout of the matrix
433 
434   Not Collective
435 
436   Input Parameter:
437 . A - The `Mat`
438 
439   Output Parameter:
440 . dm - The `DM`
441 
442   Level: intermediate
443 
444   Note:
445   A matrix may not have a `DM` associated with it
446 
447   Developer Note:
448   Since the `Mat` class doesn't know about the `DM` class the `DM` object is associated with the `Mat` through a `PetscObjectCompose()` operation
449 
450 .seealso: [](ch_dmbase), `DM`, `MatSetDM()`, `DMCreateMatrix()`, `DMSetMatType()`
451 @*/
452 PetscErrorCode MatGetDM(Mat A, DM *dm)
453 {
454   PetscFunctionBegin;
455   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
456   PetscAssertPointer(dm, 2);
457   PetscCall(PetscObjectQuery((PetscObject)A, "__PETSc_dm", (PetscObject *)dm));
458   PetscFunctionReturn(PETSC_SUCCESS);
459 }
460 
461 /*@
462   MatSetDM - Sets the `DM` defining the data layout of the matrix
463 
464   Not Collective
465 
466   Input Parameters:
467 + A  - The `Mat`
468 - dm - The `DM`
469 
470   Level: developer
471 
472   Note:
473   This is rarely used in practice, rather `DMCreateMatrix()` is used to create a matrix associated with a particular `DM`
474 
475   Developer Note:
476   Since the `Mat` class doesn't know about the `DM` class the `DM` object is associated with
477   the `Mat` through a `PetscObjectCompose()` operation
478 
479 .seealso: [](ch_dmbase), `DM`, `MatGetDM()`, `DMCreateMatrix()`, `DMSetMatType()`
480 @*/
481 PetscErrorCode MatSetDM(Mat A, DM dm)
482 {
483   PetscFunctionBegin;
484   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
485   if (dm) PetscValidHeaderSpecific(dm, DM_CLASSID, 2);
486   PetscCall(PetscObjectCompose((PetscObject)A, "__PETSc_dm", (PetscObject)dm));
487   PetscFunctionReturn(PETSC_SUCCESS);
488 }
489 
490 /*@
491   DMSetOptionsPrefix - Sets the prefix prepended to all option names when searching through the options database
492 
493   Logically Collective
494 
495   Input Parameters:
496 + dm     - the `DM` context
497 - prefix - the prefix to prepend
498 
499   Level: advanced
500 
501   Note:
502   A hyphen (-) must NOT be given at the beginning of the prefix name.
503   The first character of all runtime options is AUTOMATICALLY the hyphen.
504 
505 .seealso: [](ch_dmbase), `DM`, `PetscObjectSetOptionsPrefix()`, `DMSetFromOptions()`
506 @*/
507 PetscErrorCode DMSetOptionsPrefix(DM dm, const char prefix[])
508 {
509   PetscFunctionBegin;
510   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
511   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, prefix));
512   if (dm->sf) PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm->sf, prefix));
513   if (dm->sectionSF) PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm->sectionSF, prefix));
514   PetscFunctionReturn(PETSC_SUCCESS);
515 }
516 
517 /*@
518   DMAppendOptionsPrefix - Appends an additional string to an already existing prefix used for searching for
519   `DM` options in the options database.
520 
521   Logically Collective
522 
523   Input Parameters:
524 + dm     - the `DM` context
525 - prefix - the string to append to the current prefix
526 
527   Level: advanced
528 
529   Note:
530   If the `DM` does not currently have an options prefix then this value is used alone as the prefix as if `DMSetOptionsPrefix()` had been called.
531   A hyphen (-) must NOT be given at the beginning of the prefix name.
532   The first character of all runtime options is AUTOMATICALLY the hyphen.
533 
534 .seealso: [](ch_dmbase), `DM`, `DMSetOptionsPrefix()`, `DMGetOptionsPrefix()`, `PetscObjectAppendOptionsPrefix()`, `DMSetFromOptions()`
535 @*/
536 PetscErrorCode DMAppendOptionsPrefix(DM dm, const char prefix[])
537 {
538   PetscFunctionBegin;
539   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
540   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)dm, prefix));
541   PetscFunctionReturn(PETSC_SUCCESS);
542 }
543 
544 /*@
545   DMGetOptionsPrefix - Gets the prefix used for searching for all
546   DM options in the options database.
547 
548   Not Collective
549 
550   Input Parameter:
551 . dm - the `DM` context
552 
553   Output Parameter:
554 . prefix - pointer to the prefix string used is returned
555 
556   Level: advanced
557 
558   Fortran Note:
559   Pass in a string 'prefix' of
560   sufficient length to hold the prefix.
561 
562 .seealso: [](ch_dmbase), `DM`, `DMSetOptionsPrefix()`, `DMAppendOptionsPrefix()`, `DMSetFromOptions()`
563 @*/
564 PetscErrorCode DMGetOptionsPrefix(DM dm, const char *prefix[])
565 {
566   PetscFunctionBegin;
567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
568   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, prefix));
569   PetscFunctionReturn(PETSC_SUCCESS);
570 }
571 
572 static PetscErrorCode DMCountNonCyclicReferences_Internal(DM dm, PetscBool recurseCoarse, PetscBool recurseFine, PetscInt *ncrefct)
573 {
574   PetscInt refct = ((PetscObject)dm)->refct;
575 
576   PetscFunctionBegin;
577   *ncrefct = 0;
578   if (dm->coarseMesh && dm->coarseMesh->fineMesh == dm) {
579     refct--;
580     if (recurseCoarse) {
581       PetscInt coarseCount;
582 
583       PetscCall(DMCountNonCyclicReferences_Internal(dm->coarseMesh, PETSC_TRUE, PETSC_FALSE, &coarseCount));
584       refct += coarseCount;
585     }
586   }
587   if (dm->fineMesh && dm->fineMesh->coarseMesh == dm) {
588     refct--;
589     if (recurseFine) {
590       PetscInt fineCount;
591 
592       PetscCall(DMCountNonCyclicReferences_Internal(dm->fineMesh, PETSC_FALSE, PETSC_TRUE, &fineCount));
593       refct += fineCount;
594     }
595   }
596   *ncrefct = refct;
597   PetscFunctionReturn(PETSC_SUCCESS);
598 }
599 
600 /* Generic wrapper for DMCountNonCyclicReferences_Internal() */
601 PetscErrorCode DMCountNonCyclicReferences(PetscObject dm, PetscInt *ncrefct)
602 {
603   PetscFunctionBegin;
604   PetscCall(DMCountNonCyclicReferences_Internal((DM)dm, PETSC_TRUE, PETSC_TRUE, ncrefct));
605   PetscFunctionReturn(PETSC_SUCCESS);
606 }
607 
608 PetscErrorCode DMDestroyLabelLinkList_Internal(DM dm)
609 {
610   DMLabelLink next = dm->labels;
611 
612   PetscFunctionBegin;
613   /* destroy the labels */
614   while (next) {
615     DMLabelLink tmp = next->next;
616 
617     if (next->label == dm->depthLabel) dm->depthLabel = NULL;
618     if (next->label == dm->celltypeLabel) dm->celltypeLabel = NULL;
619     PetscCall(DMLabelDestroy(&next->label));
620     PetscCall(PetscFree(next));
621     next = tmp;
622   }
623   dm->labels = NULL;
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 static PetscErrorCode DMDestroyCoordinates_Private(DMCoordinates *c)
628 {
629   PetscFunctionBegin;
630   c->dim = PETSC_DEFAULT;
631   PetscCall(DMDestroy(&c->dm));
632   PetscCall(VecDestroy(&c->x));
633   PetscCall(VecDestroy(&c->xl));
634   PetscCall(DMFieldDestroy(&c->field));
635   PetscFunctionReturn(PETSC_SUCCESS);
636 }
637 
638 /*@
639   DMDestroy - Destroys a `DM`.
640 
641   Collective
642 
643   Input Parameter:
644 . dm - the `DM` object to destroy
645 
646   Level: developer
647 
648 .seealso: [](ch_dmbase), `DM`, `DMCreate()`, `DMType`, `DMSetType()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`
649 @*/
650 PetscErrorCode DMDestroy(DM *dm)
651 {
652   PetscInt cnt;
653 
654   PetscFunctionBegin;
655   if (!*dm) PetscFunctionReturn(PETSC_SUCCESS);
656   PetscValidHeaderSpecific(*dm, DM_CLASSID, 1);
657 
658   /* count all non-cyclic references in the doubly-linked list of coarse<->fine meshes */
659   PetscCall(DMCountNonCyclicReferences_Internal(*dm, PETSC_TRUE, PETSC_TRUE, &cnt));
660   --((PetscObject)*dm)->refct;
661   if (--cnt > 0) {
662     *dm = NULL;
663     PetscFunctionReturn(PETSC_SUCCESS);
664   }
665   if (((PetscObject)*dm)->refct < 0) PetscFunctionReturn(PETSC_SUCCESS);
666   ((PetscObject)*dm)->refct = 0;
667 
668   PetscCall(DMClearGlobalVectors(*dm));
669   PetscCall(DMClearLocalVectors(*dm));
670   PetscCall(DMClearNamedGlobalVectors(*dm));
671   PetscCall(DMClearNamedLocalVectors(*dm));
672 
673   /* Destroy the list of hooks */
674   {
675     DMCoarsenHookLink link, next;
676     for (link = (*dm)->coarsenhook; link; link = next) {
677       next = link->next;
678       PetscCall(PetscFree(link));
679     }
680     (*dm)->coarsenhook = NULL;
681   }
682   {
683     DMRefineHookLink link, next;
684     for (link = (*dm)->refinehook; link; link = next) {
685       next = link->next;
686       PetscCall(PetscFree(link));
687     }
688     (*dm)->refinehook = NULL;
689   }
690   {
691     DMSubDomainHookLink link, next;
692     for (link = (*dm)->subdomainhook; link; link = next) {
693       next = link->next;
694       PetscCall(PetscFree(link));
695     }
696     (*dm)->subdomainhook = NULL;
697   }
698   {
699     DMGlobalToLocalHookLink link, next;
700     for (link = (*dm)->gtolhook; link; link = next) {
701       next = link->next;
702       PetscCall(PetscFree(link));
703     }
704     (*dm)->gtolhook = NULL;
705   }
706   {
707     DMLocalToGlobalHookLink link, next;
708     for (link = (*dm)->ltoghook; link; link = next) {
709       next = link->next;
710       PetscCall(PetscFree(link));
711     }
712     (*dm)->ltoghook = NULL;
713   }
714   /* Destroy the work arrays */
715   {
716     DMWorkLink link, next;
717     PetscCheck(!(*dm)->workout, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Work array still checked out %p %p", (void *)(*dm)->workout, (void *)(*dm)->workout->mem);
718     for (link = (*dm)->workin; link; link = next) {
719       next = link->next;
720       PetscCall(PetscFree(link->mem));
721       PetscCall(PetscFree(link));
722     }
723     (*dm)->workin = NULL;
724   }
725   /* destroy the labels */
726   PetscCall(DMDestroyLabelLinkList_Internal(*dm));
727   /* destroy the fields */
728   PetscCall(DMClearFields(*dm));
729   /* destroy the boundaries */
730   {
731     DMBoundary next = (*dm)->boundary;
732     while (next) {
733       DMBoundary b = next;
734 
735       next = b->next;
736       PetscCall(PetscFree(b));
737     }
738   }
739 
740   PetscCall(PetscObjectDestroy(&(*dm)->dmksp));
741   PetscCall(PetscObjectDestroy(&(*dm)->dmsnes));
742   PetscCall(PetscObjectDestroy(&(*dm)->dmts));
743 
744   if ((*dm)->ctx && (*dm)->ctxdestroy) PetscCall((*(*dm)->ctxdestroy)(&(*dm)->ctx));
745   PetscCall(MatFDColoringDestroy(&(*dm)->fd));
746   PetscCall(ISLocalToGlobalMappingDestroy(&(*dm)->ltogmap));
747   PetscCall(PetscFree((*dm)->vectype));
748   PetscCall(PetscFree((*dm)->mattype));
749 
750   PetscCall(PetscSectionDestroy(&(*dm)->localSection));
751   PetscCall(PetscSectionDestroy(&(*dm)->globalSection));
752   PetscCall(PetscFree((*dm)->reorderSectionType));
753   PetscCall(PetscLayoutDestroy(&(*dm)->map));
754   PetscCall(PetscSectionDestroy(&(*dm)->defaultConstraint.section));
755   PetscCall(MatDestroy(&(*dm)->defaultConstraint.mat));
756   PetscCall(PetscSFDestroy(&(*dm)->sf));
757   PetscCall(PetscSFDestroy(&(*dm)->sectionSF));
758   if ((*dm)->sfNatural) PetscCall(PetscSFDestroy(&(*dm)->sfNatural));
759   PetscCall(PetscObjectDereference((PetscObject)(*dm)->sfMigration));
760   PetscCall(DMClearAuxiliaryVec(*dm));
761   PetscCall(PetscHMapAuxDestroy(&(*dm)->auxData));
762   if ((*dm)->coarseMesh && (*dm)->coarseMesh->fineMesh == *dm) PetscCall(DMSetFineDM((*dm)->coarseMesh, NULL));
763 
764   PetscCall(DMDestroy(&(*dm)->coarseMesh));
765   if ((*dm)->fineMesh && (*dm)->fineMesh->coarseMesh == *dm) PetscCall(DMSetCoarseDM((*dm)->fineMesh, NULL));
766   PetscCall(DMDestroy(&(*dm)->fineMesh));
767   PetscCall(PetscFree((*dm)->Lstart));
768   PetscCall(PetscFree((*dm)->L));
769   PetscCall(PetscFree((*dm)->maxCell));
770   PetscCall(DMDestroyCoordinates_Private(&(*dm)->coordinates[0]));
771   PetscCall(DMDestroyCoordinates_Private(&(*dm)->coordinates[1]));
772   if ((*dm)->transformDestroy) PetscCall((*(*dm)->transformDestroy)(*dm, (*dm)->transformCtx));
773   PetscCall(DMDestroy(&(*dm)->transformDM));
774   PetscCall(VecDestroy(&(*dm)->transform));
775   for (PetscInt i = 0; i < (*dm)->periodic.num_affines; i++) {
776     PetscCall(VecScatterDestroy(&(*dm)->periodic.affine_to_local[i]));
777     PetscCall(VecDestroy(&(*dm)->periodic.affine[i]));
778   }
779   if ((*dm)->periodic.num_affines > 0) PetscCall(PetscFree2((*dm)->periodic.affine_to_local, (*dm)->periodic.affine));
780 
781   PetscCall(DMClearDS(*dm));
782   PetscCall(DMDestroy(&(*dm)->dmBC));
783   /* if memory was published with SAWs then destroy it */
784   PetscCall(PetscObjectSAWsViewOff((PetscObject)*dm));
785 
786   PetscTryTypeMethod(*dm, destroy);
787   PetscCall(DMMonitorCancel(*dm));
788   PetscCall(DMCeedDestroy(&(*dm)->dmceed));
789 #ifdef PETSC_HAVE_LIBCEED
790   PetscCallCEED(CeedElemRestrictionDestroy(&(*dm)->ceedERestrict));
791   PetscCallCEED(CeedDestroy(&(*dm)->ceed));
792 #endif
793   /* We do not destroy (*dm)->data here so that we can reference count backend objects */
794   PetscCall(PetscHeaderDestroy(dm));
795   PetscFunctionReturn(PETSC_SUCCESS);
796 }
797 
798 /*@
799   DMSetUp - sets up the data structures inside a `DM` object
800 
801   Collective
802 
803   Input Parameter:
804 . dm - the `DM` object to setup
805 
806   Level: intermediate
807 
808   Note:
809   This is usually called after various parameter setting operations and `DMSetFromOptions()` are called on the `DM`
810 
811 .seealso: [](ch_dmbase), `DM`, `DMCreate()`, `DMSetType()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`
812 @*/
813 PetscErrorCode DMSetUp(DM dm)
814 {
815   PetscFunctionBegin;
816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
817   if (dm->setupcalled) PetscFunctionReturn(PETSC_SUCCESS);
818   PetscTryTypeMethod(dm, setup);
819   dm->setupcalled = PETSC_TRUE;
820   PetscFunctionReturn(PETSC_SUCCESS);
821 }
822 
823 /*@
824   DMSetFromOptions - sets parameters in a `DM` from the options database
825 
826   Collective
827 
828   Input Parameter:
829 . dm - the `DM` object to set options for
830 
831   Options Database Keys:
832 + -dm_preallocate_only                               - Only preallocate the matrix for `DMCreateMatrix()` and `DMCreateMassMatrix()`, but do not fill it with zeros
833 . -dm_vec_type <type>                                - type of vector to create inside `DM`
834 . -dm_mat_type <type>                                - type of matrix to create inside `DM`
835 . -dm_is_coloring_type                               - <global or local>
836 . -dm_bind_below <n>                                 - bind (force execution on CPU) for `Vec` and `Mat` objects with local size (number of vector entries or matrix rows) below n; currently only supported for `DMDA`
837 . -dm_plex_filename <str>                            - File containing a mesh
838 . -dm_plex_boundary_filename <str>                   - File containing a mesh boundary
839 . -dm_plex_name <str>                                - Name of the mesh in the file
840 . -dm_plex_shape <shape>                             - The domain shape, such as `BOX`, `SPHERE`, etc.
841 . -dm_plex_cell <ct>                                 - Cell shape
842 . -dm_plex_reference_cell_domain <bool>              - Use a reference cell domain
843 . -dm_plex_dim <dim>                                 - Set the topological dimension
844 . -dm_plex_simplex <bool>                            - `PETSC_TRUE` for simplex elements, `PETSC_FALSE` for tensor elements
845 . -dm_plex_interpolate <bool>                        - `PETSC_TRUE` turns on topological interpolation (creating edges and faces)
846 . -dm_plex_scale <sc>                                - Scale factor for mesh coordinates
847 . -dm_coord_remap <bool>                             - Map coordinates using a function
848 . -dm_coord_map <mapname>                            - Select a builtin coordinate map
849 . -dm_coord_map_params <p0,p1,p2,...>                - Set coordinate mapping parameters
850 . -dm_plex_box_faces <m,n,p>                         - Number of faces along each dimension
851 . -dm_plex_box_lower <x,y,z>                         - Specify lower-left-bottom coordinates for the box
852 . -dm_plex_box_upper <x,y,z>                         - Specify upper-right-top coordinates for the box
853 . -dm_plex_box_bd <bx,by,bz>                         - Specify the `DMBoundaryType` for each direction
854 . -dm_plex_sphere_radius <r>                         - The sphere radius
855 . -dm_plex_ball_radius <r>                           - Radius of the ball
856 . -dm_plex_cylinder_bd <bz>                          - Boundary type in the z direction
857 . -dm_plex_cylinder_num_wedges <n>                   - Number of wedges around the cylinder
858 . -dm_plex_reorder <order>                           - Reorder the mesh using the specified algorithm
859 . -dm_refine_pre <n>                                 - The number of refinements before distribution
860 . -dm_refine_uniform_pre <bool>                      - Flag for uniform refinement before distribution
861 . -dm_refine_volume_limit_pre <v>                    - The maximum cell volume after refinement before distribution
862 . -dm_refine <n>                                     - The number of refinements after distribution
863 . -dm_extrude <l>                                    - Activate extrusion and specify the number of layers to extrude
864 . -dm_plex_transform_extrude_thickness <t>           - The total thickness of extruded layers
865 . -dm_plex_transform_extrude_use_tensor <bool>       - Use tensor cells when extruding
866 . -dm_plex_transform_extrude_symmetric <bool>        - Extrude layers symmetrically about the surface
867 . -dm_plex_transform_extrude_normal <n0,...,nd>      - Specify the extrusion direction
868 . -dm_plex_transform_extrude_thicknesses <t0,...,tl> - Specify thickness of each layer
869 . -dm_plex_create_fv_ghost_cells                     - Flag to create finite volume ghost cells on the boundary
870 . -dm_plex_fv_ghost_cells_label <name>               - Label name for ghost cells boundary
871 . -dm_distribute <bool>                              - Flag to redistribute a mesh among processes
872 . -dm_distribute_overlap <n>                         - The size of the overlap halo
873 . -dm_plex_adj_cone <bool>                           - Set adjacency direction
874 . -dm_plex_adj_closure <bool>                        - Set adjacency size
875 . -dm_plex_use_ceed <bool>                           - Use LibCEED as the FEM backend
876 . -dm_plex_check_symmetry                            - Check that the adjacency information in the mesh is symmetric - `DMPlexCheckSymmetry()`
877 . -dm_plex_check_skeleton                            - Check that each cell has the correct number of vertices (only for homogeneous simplex or tensor meshes) - `DMPlexCheckSkeleton()`
878 . -dm_plex_check_faces                               - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type - `DMPlexCheckFaces()`
879 . -dm_plex_check_geometry                            - Check that cells have positive volume - `DMPlexCheckGeometry()`
880 . -dm_plex_check_pointsf                             - Check some necessary conditions for `PointSF` - `DMPlexCheckPointSF()`
881 . -dm_plex_check_interface_cones                     - Check points on inter-partition interfaces have conforming order of cone points - `DMPlexCheckInterfaceCones()`
882 - -dm_plex_check_all                                 - Perform all the checks above
883 
884   Level: intermediate
885 
886   Note:
887   For some `DMType` such as `DMDA` this cannot be called after `DMSetUp()` has been called.
888 
889 .seealso: [](ch_dmbase), `DM`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`,
890          `DMPlexCheckSymmetry()`, `DMPlexCheckSkeleton()`, `DMPlexCheckFaces()`, `DMPlexCheckGeometry()`, `DMPlexCheckPointSF()`, `DMPlexCheckInterfaceCones()`,
891          `DMSetOptionsPrefix()`, `DMType`, `DMPLEX`, `DMDA`, `DMSetUp()`
892 @*/
893 PetscErrorCode DMSetFromOptions(DM dm)
894 {
895   char      typeName[256];
896   PetscBool flg;
897 
898   PetscFunctionBegin;
899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
900   dm->setfromoptionscalled = PETSC_TRUE;
901   if (dm->sf) PetscCall(PetscSFSetFromOptions(dm->sf));
902   if (dm->sectionSF) PetscCall(PetscSFSetFromOptions(dm->sectionSF));
903   if (dm->coordinates[0].dm) PetscCall(DMSetFromOptions(dm->coordinates[0].dm));
904   PetscObjectOptionsBegin((PetscObject)dm);
905   PetscCall(PetscOptionsBool("-dm_preallocate_only", "only preallocate matrix, but do not set column indices", "DMSetMatrixPreallocateOnly", dm->prealloc_only, &dm->prealloc_only, NULL));
906   PetscCall(PetscOptionsFList("-dm_vec_type", "Vector type used for created vectors", "DMSetVecType", VecList, dm->vectype, typeName, 256, &flg));
907   if (flg) PetscCall(DMSetVecType(dm, typeName));
908   PetscCall(PetscOptionsFList("-dm_mat_type", "Matrix type used for created matrices", "DMSetMatType", MatList, dm->mattype ? dm->mattype : typeName, typeName, sizeof(typeName), &flg));
909   if (flg) PetscCall(DMSetMatType(dm, typeName));
910   PetscCall(PetscOptionsEnum("-dm_blocking_type", "Topological point or field node blocking", "DMSetBlockingType", DMBlockingTypes, (PetscEnum)dm->blocking_type, (PetscEnum *)&dm->blocking_type, NULL));
911   PetscCall(PetscOptionsEnum("-dm_is_coloring_type", "Global or local coloring of Jacobian", "DMSetISColoringType", ISColoringTypes, (PetscEnum)dm->coloringtype, (PetscEnum *)&dm->coloringtype, NULL));
912   PetscCall(PetscOptionsInt("-dm_bind_below", "Set the size threshold (in entries) below which the Vec is bound to the CPU", "VecBindToCPU", dm->bind_below, &dm->bind_below, &flg));
913   PetscCall(PetscOptionsBool("-dm_ignore_perm_output", "Ignore the local section permutation on output", "DMGetOutputDM", dm->ignorePermOutput, &dm->ignorePermOutput, NULL));
914   PetscTryTypeMethod(dm, setfromoptions, PetscOptionsObject);
915   /* process any options handlers added with PetscObjectAddOptionsHandler() */
916   PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)dm, PetscOptionsObject));
917   PetscOptionsEnd();
918   PetscFunctionReturn(PETSC_SUCCESS);
919 }
920 
921 /*@
922   DMViewFromOptions - View a `DM` in a particular way based on a request in the options database
923 
924   Collective
925 
926   Input Parameters:
927 + dm   - the `DM` object
928 . obj  - optional object that provides the prefix for the options database (if `NULL` then the prefix in obj is used)
929 - name - option string that is used to activate viewing
930 
931   Level: intermediate
932 
933   Note:
934   See `PetscObjectViewFromOptions()` for a list of values that can be provided in the options database to determine how the `DM` is viewed
935 
936 .seealso: [](ch_dmbase), `DM`, `DMView()`, `PetscObjectViewFromOptions()`, `DMCreate()`
937 @*/
938 PetscErrorCode DMViewFromOptions(DM dm, PetscObject obj, const char name[])
939 {
940   PetscFunctionBegin;
941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
942   PetscCall(PetscObjectViewFromOptions((PetscObject)dm, obj, name));
943   PetscFunctionReturn(PETSC_SUCCESS);
944 }
945 
946 /*@
947   DMView - Views a `DM`. Depending on the `PetscViewer` and its `PetscViewerFormat` it may print some ASCII information about the `DM` to the screen or a file or
948   save the `DM` in a binary file to be loaded later or create a visualization of the `DM`
949 
950   Collective
951 
952   Input Parameters:
953 + dm - the `DM` object to view
954 - v  - the viewer
955 
956   Level: beginner
957 
958   Notes:
959 
960   `PetscViewer` = `PETSCVIEWERHDF5` i.e. HDF5 format can be used with `PETSC_VIEWER_HDF5_PETSC` as the `PetscViewerFormat` to save multiple `DMPLEX`
961   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
962   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
963 
964   `PetscViewer` = `PETSCVIEWEREXODUSII` i.e. ExodusII format assumes that element blocks (mapped to "Cell sets" labels)
965   consists of sequentially numbered cells.
966 
967   If `dm` has been distributed, only the part of the `DM` on MPI rank 0 (including "ghost" cells and vertices) will be written.
968 
969   Only TRI, TET, QUAD, and HEX cells are supported.
970 
971   `DMPLEX` only represents geometry while most post-processing software expect that a mesh also provides information on the discretization space. This function assumes that the file represents Lagrange finite elements of order 1 or 2.
972   The order of the mesh shall be set using `PetscViewerExodusIISetOrder()`
973 
974   Variable names can be set and querried using `PetscViewerExodusII[Set/Get][Nodal/Zonal]VariableNames[s]`.
975 
976 .seealso: [](ch_dmbase), `DM`, `PetscViewer`, `PetscViewerFormat`, `PetscViewerSetFormat()`, `DMDestroy()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMLoad()`, `PetscObjectSetName()`
977 @*/
978 PetscErrorCode DMView(DM dm, PetscViewer v)
979 {
980   PetscBool         isbinary;
981   PetscMPIInt       size;
982   PetscViewerFormat format;
983 
984   PetscFunctionBegin;
985   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
986   if (!v) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)dm), &v));
987   PetscValidHeaderSpecific(v, PETSC_VIEWER_CLASSID, 2);
988   /* Ideally, we would like to have this test on.
989      However, it currently breaks socket viz via GLVis.
990      During DMView(parallel_mesh,glvis_viewer), each
991      process opens a sequential ASCII socket to visualize
992      the local mesh, and PetscObjectView(dm,local_socket)
993      is internally called inside VecView_GLVis, incurring
994      in an error here */
995   /* PetscCheckSameComm(dm,1,v,2); */
996   PetscCall(PetscViewerCheckWritable(v));
997 
998   PetscCall(PetscViewerGetFormat(v, &format));
999   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1000   if (size == 1 && format == PETSC_VIEWER_LOAD_BALANCE) PetscFunctionReturn(PETSC_SUCCESS);
1001   PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)dm, v));
1002   PetscCall(PetscObjectTypeCompare((PetscObject)v, PETSCVIEWERBINARY, &isbinary));
1003   if (isbinary) {
1004     PetscInt classid = DM_FILE_CLASSID;
1005     char     type[256];
1006 
1007     PetscCall(PetscViewerBinaryWrite(v, &classid, 1, PETSC_INT));
1008     PetscCall(PetscStrncpy(type, ((PetscObject)dm)->type_name, sizeof(type)));
1009     PetscCall(PetscViewerBinaryWrite(v, type, 256, PETSC_CHAR));
1010   }
1011   PetscTryTypeMethod(dm, view, v);
1012   PetscFunctionReturn(PETSC_SUCCESS);
1013 }
1014 
1015 /*@
1016   DMCreateGlobalVector - Creates a global vector from a `DM` object. A global vector is a parallel vector that has no duplicate values shared between MPI ranks,
1017   that is it has no ghost locations.
1018 
1019   Collective
1020 
1021   Input Parameter:
1022 . dm - the `DM` object
1023 
1024   Output Parameter:
1025 . vec - the global vector
1026 
1027   Level: beginner
1028 
1029 .seealso: [](ch_dmbase), `DM`, `Vec`, `DMCreateLocalVector()`, `DMGetGlobalVector()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`,
1030          `DMGlobalToLocalBegin()`, `DMGlobalToLocalEnd()`
1031 @*/
1032 PetscErrorCode DMCreateGlobalVector(DM dm, Vec *vec)
1033 {
1034   PetscFunctionBegin;
1035   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1036   PetscAssertPointer(vec, 2);
1037   PetscUseTypeMethod(dm, createglobalvector, vec);
1038   if (PetscDefined(USE_DEBUG)) {
1039     DM vdm;
1040 
1041     PetscCall(VecGetDM(*vec, &vdm));
1042     PetscCheck(vdm, PETSC_COMM_SELF, PETSC_ERR_PLIB, "DM type '%s' did not attach the DM to the vector", ((PetscObject)dm)->type_name);
1043   }
1044   PetscFunctionReturn(PETSC_SUCCESS);
1045 }
1046 
1047 /*@
1048   DMCreateLocalVector - Creates a local vector from a `DM` object.
1049 
1050   Not Collective
1051 
1052   Input Parameter:
1053 . dm - the `DM` object
1054 
1055   Output Parameter:
1056 . vec - the local vector
1057 
1058   Level: beginner
1059 
1060   Note:
1061   A local vector usually has ghost locations that contain values that are owned by different MPI ranks. A global vector has no ghost locations.
1062 
1063 .seealso: [](ch_dmbase), `DM`, `Vec`, `DMCreateGlobalVector()`, `DMGetLocalVector()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`
1064          `DMGlobalToLocalBegin()`, `DMGlobalToLocalEnd()`
1065 @*/
1066 PetscErrorCode DMCreateLocalVector(DM dm, Vec *vec)
1067 {
1068   PetscFunctionBegin;
1069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1070   PetscAssertPointer(vec, 2);
1071   PetscUseTypeMethod(dm, createlocalvector, vec);
1072   if (PetscDefined(USE_DEBUG)) {
1073     DM vdm;
1074 
1075     PetscCall(VecGetDM(*vec, &vdm));
1076     PetscCheck(vdm, PETSC_COMM_SELF, PETSC_ERR_LIB, "DM type '%s' did not attach the DM to the vector", ((PetscObject)dm)->type_name);
1077   }
1078   PetscFunctionReturn(PETSC_SUCCESS);
1079 }
1080 
1081 /*@
1082   DMGetLocalToGlobalMapping - Accesses the local-to-global mapping in a `DM`.
1083 
1084   Collective
1085 
1086   Input Parameter:
1087 . dm - the `DM` that provides the mapping
1088 
1089   Output Parameter:
1090 . ltog - the mapping
1091 
1092   Level: advanced
1093 
1094   Notes:
1095   The global to local mapping allows one to set values into the global vector or matrix using `VecSetValuesLocal()` and `MatSetValuesLocal()`
1096 
1097   Vectors obtained with  `DMCreateGlobalVector()` and matrices obtained with `DMCreateMatrix()` already contain the global mapping so you do
1098   need to use this function with those objects.
1099 
1100   This mapping can then be used by `VecSetLocalToGlobalMapping()` or `MatSetLocalToGlobalMapping()`.
1101 
1102 .seealso: [](ch_dmbase), `DM`, `DMCreateLocalVector()`, `DMCreateGlobalVector()`, `VecSetLocalToGlobalMapping()`, `MatSetLocalToGlobalMapping()`,
1103           `DMCreateMatrix()`
1104 @*/
1105 PetscErrorCode DMGetLocalToGlobalMapping(DM dm, ISLocalToGlobalMapping *ltog)
1106 {
1107   PetscInt bs = -1, bsLocal[2], bsMinMax[2];
1108 
1109   PetscFunctionBegin;
1110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1111   PetscAssertPointer(ltog, 2);
1112   if (!dm->ltogmap) {
1113     PetscSection section, sectionGlobal;
1114 
1115     PetscCall(DMGetLocalSection(dm, &section));
1116     if (section) {
1117       const PetscInt *cdofs;
1118       PetscInt       *ltog;
1119       PetscInt        pStart, pEnd, n, p, k, l;
1120 
1121       PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
1122       PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
1123       PetscCall(PetscSectionGetStorageSize(section, &n));
1124       PetscCall(PetscMalloc1(n, &ltog)); /* We want the local+overlap size */
1125       for (p = pStart, l = 0; p < pEnd; ++p) {
1126         PetscInt bdof, cdof, dof, off, c, cind;
1127 
1128         /* Should probably use constrained dofs */
1129         PetscCall(PetscSectionGetDof(section, p, &dof));
1130         PetscCall(PetscSectionGetConstraintDof(section, p, &cdof));
1131         PetscCall(PetscSectionGetConstraintIndices(section, p, &cdofs));
1132         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &off));
1133         /* If you have dofs, and constraints, and they are unequal, we set the blocksize to 1 */
1134         bdof = cdof && (dof - cdof) ? 1 : dof;
1135         if (dof) bs = bs < 0 ? bdof : PetscGCD(bs, bdof);
1136 
1137         for (c = 0, cind = 0; c < dof; ++c, ++l) {
1138           if (cind < cdof && c == cdofs[cind]) {
1139             ltog[l] = off < 0 ? off - c : -(off + c + 1);
1140             cind++;
1141           } else {
1142             ltog[l] = (off < 0 ? -(off + 1) : off) + c - cind;
1143           }
1144         }
1145       }
1146       /* Must have same blocksize on all procs (some might have no points) */
1147       bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
1148       bsLocal[1] = bs;
1149       PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
1150       if (bsMinMax[0] != bsMinMax[1]) {
1151         bs = 1;
1152       } else {
1153         bs = bsMinMax[0];
1154       }
1155       bs = bs < 0 ? 1 : bs;
1156       /* Must reduce indices by blocksize */
1157       if (bs > 1) {
1158         for (l = 0, k = 0; l < n; l += bs, ++k) {
1159           // Integer division of negative values truncates toward zero(!), not toward negative infinity
1160           ltog[k] = ltog[l] >= 0 ? ltog[l] / bs : -(-(ltog[l] + 1) / bs + 1);
1161         }
1162         n /= bs;
1163       }
1164       PetscCall(ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)dm), bs, n, ltog, PETSC_OWN_POINTER, &dm->ltogmap));
1165     } else PetscUseTypeMethod(dm, getlocaltoglobalmapping);
1166   }
1167   *ltog = dm->ltogmap;
1168   PetscFunctionReturn(PETSC_SUCCESS);
1169 }
1170 
1171 /*@
1172   DMGetBlockSize - Gets the inherent block size associated with a `DM`
1173 
1174   Not Collective
1175 
1176   Input Parameter:
1177 . dm - the `DM` with block structure
1178 
1179   Output Parameter:
1180 . bs - the block size, 1 implies no exploitable block structure
1181 
1182   Level: intermediate
1183 
1184   Notes:
1185   This might be the number of degrees of freedom at each grid point for a structured grid.
1186 
1187   Complex `DM` that represent multiphysics or staggered grids or mixed-methods do not generally have a single inherent block size, but
1188   rather different locations in the vectors may have a different block size.
1189 
1190 .seealso: [](ch_dmbase), `DM`, `ISCreateBlock()`, `VecSetBlockSize()`, `MatSetBlockSize()`, `DMGetLocalToGlobalMapping()`
1191 @*/
1192 PetscErrorCode DMGetBlockSize(DM dm, PetscInt *bs)
1193 {
1194   PetscFunctionBegin;
1195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1196   PetscAssertPointer(bs, 2);
1197   PetscCheck(dm->bs >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM does not have enough information to provide a block size yet");
1198   *bs = dm->bs;
1199   PetscFunctionReturn(PETSC_SUCCESS);
1200 }
1201 
1202 /*@
1203   DMCreateInterpolation - Gets the interpolation matrix between two `DM` objects. The resulting matrix map degrees of freedom in the vector obtained by
1204   `DMCreateGlobalVector()` on the coarse `DM` to similar vectors on the fine grid `DM`.
1205 
1206   Collective
1207 
1208   Input Parameters:
1209 + dmc - the `DM` object
1210 - dmf - the second, finer `DM` object
1211 
1212   Output Parameters:
1213 + mat - the interpolation
1214 - vec - the scaling (optional, pass `NULL` if not needed), see `DMCreateInterpolationScale()`
1215 
1216   Level: developer
1217 
1218   Notes:
1219   For `DMDA` objects this only works for "uniform refinement", that is the refined mesh was obtained `DMRefine()` or the coarse mesh was obtained by
1220   DMCoarsen(). The coordinates set into the `DMDA` are completely ignored in computing the interpolation.
1221 
1222   For `DMDA` objects you can use this interpolation (more precisely the interpolation from the `DMGetCoordinateDM()`) to interpolate the mesh coordinate
1223   vectors EXCEPT in the periodic case where it does not make sense since the coordinate vectors are not periodic.
1224 
1225 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateRestriction()`, `DMCreateInterpolationScale()`
1226 @*/
1227 PetscErrorCode DMCreateInterpolation(DM dmc, DM dmf, Mat *mat, Vec *vec)
1228 {
1229   PetscFunctionBegin;
1230   PetscValidHeaderSpecific(dmc, DM_CLASSID, 1);
1231   PetscValidHeaderSpecific(dmf, DM_CLASSID, 2);
1232   PetscAssertPointer(mat, 3);
1233   PetscCall(PetscLogEventBegin(DM_CreateInterpolation, dmc, dmf, 0, 0));
1234   PetscUseTypeMethod(dmc, createinterpolation, dmf, mat, vec);
1235   PetscCall(PetscLogEventEnd(DM_CreateInterpolation, dmc, dmf, 0, 0));
1236   PetscFunctionReturn(PETSC_SUCCESS);
1237 }
1238 
1239 /*@
1240   DMCreateInterpolationScale - Forms L = 1/(R*1) where 1 is the vector of all ones, and R is
1241   the transpose of the interpolation between the `DM`.
1242 
1243   Input Parameters:
1244 + dac - `DM` that defines a coarse mesh
1245 . daf - `DM` that defines a fine mesh
1246 - mat - the restriction (or interpolation operator) from fine to coarse
1247 
1248   Output Parameter:
1249 . scale - the scaled vector
1250 
1251   Level: advanced
1252 
1253   Note:
1254   xcoarse = diag(L)*R*xfine preserves scale and is thus suitable for state (versus residual)
1255   restriction. In other words xcoarse is the coarse representation of xfine.
1256 
1257   Developer Note:
1258   If the fine-scale `DMDA` has the -dm_bind_below option set to true, then `DMCreateInterpolationScale()` calls `MatSetBindingPropagates()`
1259   on the restriction/interpolation operator to set the bindingpropagates flag to true.
1260 
1261 .seealso: [](ch_dmbase), `DM`, `MatRestrict()`, `MatInterpolate()`, `DMCreateInterpolation()`, `DMCreateRestriction()`, `DMCreateGlobalVector()`
1262 @*/
1263 PetscErrorCode DMCreateInterpolationScale(DM dac, DM daf, Mat mat, Vec *scale)
1264 {
1265   Vec         fine;
1266   PetscScalar one = 1.0;
1267 #if defined(PETSC_HAVE_CUDA)
1268   PetscBool bindingpropagates, isbound;
1269 #endif
1270 
1271   PetscFunctionBegin;
1272   PetscCall(DMCreateGlobalVector(daf, &fine));
1273   PetscCall(DMCreateGlobalVector(dac, scale));
1274   PetscCall(VecSet(fine, one));
1275 #if defined(PETSC_HAVE_CUDA)
1276   /* If the 'fine' Vec is bound to the CPU, it makes sense to bind 'mat' as well.
1277    * Note that we only do this for the CUDA case, right now, but if we add support for MatMultTranspose() via ViennaCL,
1278    * we'll need to do it for that case, too.*/
1279   PetscCall(VecGetBindingPropagates(fine, &bindingpropagates));
1280   if (bindingpropagates) {
1281     PetscCall(MatSetBindingPropagates(mat, PETSC_TRUE));
1282     PetscCall(VecBoundToCPU(fine, &isbound));
1283     PetscCall(MatBindToCPU(mat, isbound));
1284   }
1285 #endif
1286   PetscCall(MatRestrict(mat, fine, *scale));
1287   PetscCall(VecDestroy(&fine));
1288   PetscCall(VecReciprocal(*scale));
1289   PetscFunctionReturn(PETSC_SUCCESS);
1290 }
1291 
1292 /*@
1293   DMCreateRestriction - Gets restriction matrix between two `DM` objects. The resulting matrix map degrees of freedom in the vector obtained by
1294   `DMCreateGlobalVector()` on the fine `DM` to similar vectors on the coarse grid `DM`.
1295 
1296   Collective
1297 
1298   Input Parameters:
1299 + dmc - the `DM` object
1300 - dmf - the second, finer `DM` object
1301 
1302   Output Parameter:
1303 . mat - the restriction
1304 
1305   Level: developer
1306 
1307   Note:
1308   This only works for `DMSTAG`. For many situations either the transpose of the operator obtained with `DMCreateInterpolation()` or that
1309   matrix multiplied by the vector obtained with `DMCreateInterpolationScale()` provides the desired object.
1310 
1311 .seealso: [](ch_dmbase), `DM`, `DMRestrict()`, `DMInterpolate()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateInterpolation()`
1312 @*/
1313 PetscErrorCode DMCreateRestriction(DM dmc, DM dmf, Mat *mat)
1314 {
1315   PetscFunctionBegin;
1316   PetscValidHeaderSpecific(dmc, DM_CLASSID, 1);
1317   PetscValidHeaderSpecific(dmf, DM_CLASSID, 2);
1318   PetscAssertPointer(mat, 3);
1319   PetscCall(PetscLogEventBegin(DM_CreateRestriction, dmc, dmf, 0, 0));
1320   PetscUseTypeMethod(dmc, createrestriction, dmf, mat);
1321   PetscCall(PetscLogEventEnd(DM_CreateRestriction, dmc, dmf, 0, 0));
1322   PetscFunctionReturn(PETSC_SUCCESS);
1323 }
1324 
1325 /*@
1326   DMCreateInjection - Gets injection matrix between two `DM` objects.
1327 
1328   Collective
1329 
1330   Input Parameters:
1331 + dac - the `DM` object
1332 - daf - the second, finer `DM` object
1333 
1334   Output Parameter:
1335 . mat - the injection
1336 
1337   Level: developer
1338 
1339   Notes:
1340   This is an operator that applied to a vector obtained with `DMCreateGlobalVector()` on the
1341   fine grid maps the values to a vector on the vector on the coarse `DM` by simply selecting
1342   the values on the coarse grid points. This compares to the operator obtained by
1343   `DMCreateRestriction()` or the transpose of the operator obtained by
1344   `DMCreateInterpolation()` that uses a "local weighted average" of the values around the
1345   coarse grid point as the coarse grid value.
1346 
1347   For `DMDA` objects this only works for "uniform refinement", that is the refined mesh was obtained `DMRefine()` or the coarse mesh was obtained by
1348   `DMCoarsen()`. The coordinates set into the `DMDA` are completely ignored in computing the injection.
1349 
1350 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMCreateInterpolation()`,
1351           `DMCreateRestriction()`, `MatRestrict()`, `MatInterpolate()`
1352 @*/
1353 PetscErrorCode DMCreateInjection(DM dac, DM daf, Mat *mat)
1354 {
1355   PetscFunctionBegin;
1356   PetscValidHeaderSpecific(dac, DM_CLASSID, 1);
1357   PetscValidHeaderSpecific(daf, DM_CLASSID, 2);
1358   PetscAssertPointer(mat, 3);
1359   PetscCall(PetscLogEventBegin(DM_CreateInjection, dac, daf, 0, 0));
1360   PetscUseTypeMethod(dac, createinjection, daf, mat);
1361   PetscCall(PetscLogEventEnd(DM_CreateInjection, dac, daf, 0, 0));
1362   PetscFunctionReturn(PETSC_SUCCESS);
1363 }
1364 
1365 /*@
1366   DMCreateMassMatrix - Gets the mass matrix between two `DM` objects, M_ij = \int \phi_i \psi_j where the \phi are Galerkin basis functions for a
1367   a Galerkin finite element model on the `DM`
1368 
1369   Collective
1370 
1371   Input Parameters:
1372 + dmc - the target `DM` object
1373 - dmf - the source `DM` object, can be `NULL`
1374 
1375   Output Parameter:
1376 . mat - the mass matrix
1377 
1378   Level: developer
1379 
1380   Notes:
1381   For `DMPLEX` the finite element model for the `DM` must have been already provided.
1382 
1383   if `dmc` is `dmf` or `NULL`, then x^t M x is an approximation to the L2 norm of the vector x which is obtained by `DMCreateGlobalVector()`
1384 
1385 .seealso: [](ch_dmbase), `DM`, `DMCreateMassMatrixLumped()`, `DMCreateMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateRestriction()`, `DMCreateInterpolation()`, `DMCreateInjection()`
1386 @*/
1387 PetscErrorCode DMCreateMassMatrix(DM dmc, DM dmf, Mat *mat)
1388 {
1389   PetscFunctionBegin;
1390   PetscValidHeaderSpecific(dmc, DM_CLASSID, 1);
1391   if (!dmf) dmf = dmc;
1392   PetscValidHeaderSpecific(dmf, DM_CLASSID, 2);
1393   PetscAssertPointer(mat, 3);
1394   PetscCall(PetscLogEventBegin(DM_CreateMassMatrix, dmc, dmf, 0, 0));
1395   PetscUseTypeMethod(dmc, createmassmatrix, dmf, mat);
1396   PetscCall(PetscLogEventEnd(DM_CreateMassMatrix, dmc, dmf, 0, 0));
1397   PetscFunctionReturn(PETSC_SUCCESS);
1398 }
1399 
1400 /*@
1401   DMCreateMassMatrixLumped - Gets the lumped mass matrix for a given `DM`
1402 
1403   Collective
1404 
1405   Input Parameter:
1406 . dm - the `DM` object
1407 
1408   Output Parameter:
1409 + llm - the local lumped mass matrix, which is a diagonal matrix, represented as a vector
1410 - lm  - the global lumped mass matrix, which is a diagonal matrix, represented as a vector
1411 
1412   Level: developer
1413 
1414   Note:
1415   See `DMCreateMassMatrix()` for how to create the non-lumped version of the mass matrix.
1416 
1417 .seealso: [](ch_dmbase), `DM`, `DMCreateMassMatrix()`, `DMCreateMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateRestriction()`, `DMCreateInterpolation()`, `DMCreateInjection()`
1418 @*/
1419 PetscErrorCode DMCreateMassMatrixLumped(DM dm, Vec *llm, Vec *lm)
1420 {
1421   PetscFunctionBegin;
1422   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1423   if (llm) PetscAssertPointer(llm, 2);
1424   if (lm) PetscAssertPointer(lm, 3);
1425   if (llm || lm) PetscUseTypeMethod(dm, createmassmatrixlumped, llm, lm);
1426   PetscFunctionReturn(PETSC_SUCCESS);
1427 }
1428 
1429 /*@
1430   DMCreateColoring - Gets coloring of a graph associated with the `DM`. Often the graph represents the operator matrix associated with the discretization
1431   of a PDE on the `DM`.
1432 
1433   Collective
1434 
1435   Input Parameters:
1436 + dm    - the `DM` object
1437 - ctype - `IS_COLORING_LOCAL` or `IS_COLORING_GLOBAL`
1438 
1439   Output Parameter:
1440 . coloring - the coloring
1441 
1442   Level: developer
1443 
1444   Notes:
1445   Coloring of matrices can also be computed directly from the sparse matrix nonzero structure via the `MatColoring` object or from the mesh from which the
1446   matrix comes from (what this function provides). In general using the mesh produces a more optimal coloring (fewer colors).
1447 
1448   This produces a coloring with the distance of 2, see `MatSetColoringDistance()` which can be used for efficiently computing Jacobians with `MatFDColoringCreate()`
1449   For `DMDA` in three dimensions with periodic boundary conditions the number of grid points in each dimension must be divisible by 2*stencil_width + 1,
1450   otherwise an error will be generated.
1451 
1452 .seealso: [](ch_dmbase), `DM`, `ISColoring`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatType()`, `MatColoring`, `MatFDColoringCreate()`
1453 @*/
1454 PetscErrorCode DMCreateColoring(DM dm, ISColoringType ctype, ISColoring *coloring)
1455 {
1456   PetscFunctionBegin;
1457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1458   PetscAssertPointer(coloring, 3);
1459   PetscUseTypeMethod(dm, getcoloring, ctype, coloring);
1460   PetscFunctionReturn(PETSC_SUCCESS);
1461 }
1462 
1463 /*@
1464   DMCreateMatrix - Gets an empty matrix for a `DM` that is most commonly used to store the Jacobian of a discrete PDE operator.
1465 
1466   Collective
1467 
1468   Input Parameter:
1469 . dm - the `DM` object
1470 
1471   Output Parameter:
1472 . mat - the empty Jacobian
1473 
1474   Options Database Key:
1475 . -dm_preallocate_only - Only preallocate the matrix for `DMCreateMatrix()` and `DMCreateMassMatrix()`, but do not fill it with zeros
1476 
1477   Level: beginner
1478 
1479   Notes:
1480   This properly preallocates the number of nonzeros in the sparse matrix so you
1481   do not need to do it yourself.
1482 
1483   By default it also sets the nonzero structure and puts in the zero entries. To prevent setting
1484   the nonzero pattern call `DMSetMatrixPreallocateOnly()`
1485 
1486   For `DMDA`, when you call `MatView()` on this matrix it is displayed using the global natural ordering, NOT in the ordering used
1487   internally by PETSc.
1488 
1489   For `DMDA`, in general it is easiest to use `MatSetValuesStencil()` or `MatSetValuesLocal()` to put values into the matrix because
1490   `MatSetValues()` requires the indices for the global numbering for the `DMDA` which is complic`ated to compute
1491 
1492 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMSetMatType()`, `DMCreateMassMatrix()`
1493 @*/
1494 PetscErrorCode DMCreateMatrix(DM dm, Mat *mat)
1495 {
1496   PetscFunctionBegin;
1497   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1498   PetscAssertPointer(mat, 2);
1499   PetscCall(MatInitializePackage());
1500   PetscCall(PetscLogEventBegin(DM_CreateMatrix, 0, 0, 0, 0));
1501   PetscUseTypeMethod(dm, creatematrix, mat);
1502   if (PetscDefined(USE_DEBUG)) {
1503     DM mdm;
1504 
1505     PetscCall(MatGetDM(*mat, &mdm));
1506     PetscCheck(mdm, PETSC_COMM_SELF, PETSC_ERR_PLIB, "DM type '%s' did not attach the DM to the matrix", ((PetscObject)dm)->type_name);
1507   }
1508   /* Handle nullspace and near nullspace */
1509   if (dm->Nf) {
1510     MatNullSpace nullSpace;
1511     PetscInt     Nf, f;
1512 
1513     PetscCall(DMGetNumFields(dm, &Nf));
1514     for (f = 0; f < Nf; ++f) {
1515       if (dm->nullspaceConstructors[f]) {
1516         PetscCall((*dm->nullspaceConstructors[f])(dm, f, f, &nullSpace));
1517         PetscCall(MatSetNullSpace(*mat, nullSpace));
1518         PetscCall(MatNullSpaceDestroy(&nullSpace));
1519         break;
1520       }
1521     }
1522     for (f = 0; f < Nf; ++f) {
1523       if (dm->nearnullspaceConstructors[f]) {
1524         PetscCall((*dm->nearnullspaceConstructors[f])(dm, f, f, &nullSpace));
1525         PetscCall(MatSetNearNullSpace(*mat, nullSpace));
1526         PetscCall(MatNullSpaceDestroy(&nullSpace));
1527       }
1528     }
1529   }
1530   PetscCall(PetscLogEventEnd(DM_CreateMatrix, 0, 0, 0, 0));
1531   PetscFunctionReturn(PETSC_SUCCESS);
1532 }
1533 
1534 /*@
1535   DMSetMatrixPreallocateSkip - When `DMCreateMatrix()` is called the matrix sizes and
1536   `ISLocalToGlobalMapping` will be properly set, but the data structures to store values in the
1537   matrices will not be preallocated.
1538 
1539   Logically Collective
1540 
1541   Input Parameters:
1542 + dm   - the `DM`
1543 - skip - `PETSC_TRUE` to skip preallocation
1544 
1545   Level: developer
1546 
1547   Note:
1548   This is most useful to reduce initialization costs when `MatSetPreallocationCOO()` and
1549   `MatSetValuesCOO()` will be used.
1550 
1551 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `DMSetMatrixStructureOnly()`, `DMSetMatrixPreallocateOnly()`
1552 @*/
1553 PetscErrorCode DMSetMatrixPreallocateSkip(DM dm, PetscBool skip)
1554 {
1555   PetscFunctionBegin;
1556   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1557   dm->prealloc_skip = skip;
1558   PetscFunctionReturn(PETSC_SUCCESS);
1559 }
1560 
1561 /*@
1562   DMSetMatrixPreallocateOnly - When `DMCreateMatrix()` is called the matrix will be properly
1563   preallocated but the nonzero structure and zero values will not be set.
1564 
1565   Logically Collective
1566 
1567   Input Parameters:
1568 + dm   - the `DM`
1569 - only - `PETSC_TRUE` if only want preallocation
1570 
1571   Options Database Key:
1572 . -dm_preallocate_only - Only preallocate the matrix for `DMCreateMatrix()`, `DMCreateMassMatrix()`, but do not fill it with zeros
1573 
1574   Level: developer
1575 
1576 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatrixStructureOnly()`, `DMSetMatrixPreallocateSkip()`
1577 @*/
1578 PetscErrorCode DMSetMatrixPreallocateOnly(DM dm, PetscBool only)
1579 {
1580   PetscFunctionBegin;
1581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1582   dm->prealloc_only = only;
1583   PetscFunctionReturn(PETSC_SUCCESS);
1584 }
1585 
1586 /*@
1587   DMSetMatrixStructureOnly - When `DMCreateMatrix()` is called, the matrix structure will be created
1588   but the array for numerical values will not be allocated.
1589 
1590   Logically Collective
1591 
1592   Input Parameters:
1593 + dm   - the `DM`
1594 - only - `PETSC_TRUE` if you only want matrix structure
1595 
1596   Level: developer
1597 
1598 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `DMSetMatrixPreallocateOnly()`, `DMSetMatrixPreallocateSkip()`
1599 @*/
1600 PetscErrorCode DMSetMatrixStructureOnly(DM dm, PetscBool only)
1601 {
1602   PetscFunctionBegin;
1603   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1604   dm->structure_only = only;
1605   PetscFunctionReturn(PETSC_SUCCESS);
1606 }
1607 
1608 /*@
1609   DMSetBlockingType - set the blocking granularity to be used for variable block size `DMCreateMatrix()` is called
1610 
1611   Logically Collective
1612 
1613   Input Parameters:
1614 + dm    - the `DM`
1615 - btype - block by topological point or field node
1616 
1617   Options Database Key:
1618 . -dm_blocking_type [topological_point, field_node] - use topological point blocking or field node blocking
1619 
1620   Level: advanced
1621 
1622 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `MatSetVariableBlockSizes()`
1623 @*/
1624 PetscErrorCode DMSetBlockingType(DM dm, DMBlockingType btype)
1625 {
1626   PetscFunctionBegin;
1627   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1628   dm->blocking_type = btype;
1629   PetscFunctionReturn(PETSC_SUCCESS);
1630 }
1631 
1632 /*@
1633   DMGetBlockingType - get the blocking granularity to be used for variable block size `DMCreateMatrix()` is called
1634 
1635   Not Collective
1636 
1637   Input Parameter:
1638 . dm - the `DM`
1639 
1640   Output Parameter:
1641 . btype - block by topological point or field node
1642 
1643   Level: advanced
1644 
1645 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `MatSetVariableBlockSizes()`
1646 @*/
1647 PetscErrorCode DMGetBlockingType(DM dm, DMBlockingType *btype)
1648 {
1649   PetscFunctionBegin;
1650   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1651   PetscAssertPointer(btype, 2);
1652   *btype = dm->blocking_type;
1653   PetscFunctionReturn(PETSC_SUCCESS);
1654 }
1655 
1656 /*@C
1657   DMGetWorkArray - Gets a work array guaranteed to be at least the input size, restore with `DMRestoreWorkArray()`
1658 
1659   Not Collective
1660 
1661   Input Parameters:
1662 + dm    - the `DM` object
1663 . count - The minimum size
1664 - dtype - MPI data type, often `MPIU_REAL`, `MPIU_SCALAR`, or `MPIU_INT`)
1665 
1666   Output Parameter:
1667 . mem - the work array
1668 
1669   Level: developer
1670 
1671   Notes:
1672   A `DM` may stash the array between instantiations so using this routine may be more efficient than calling `PetscMalloc()`
1673 
1674   The array may contain nonzero values
1675 
1676 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMCreate()`, `DMRestoreWorkArray()`, `PetscMalloc()`
1677 @*/
1678 PetscErrorCode DMGetWorkArray(DM dm, PetscInt count, MPI_Datatype dtype, void *mem)
1679 {
1680   DMWorkLink  link;
1681   PetscMPIInt dsize;
1682 
1683   PetscFunctionBegin;
1684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1685   PetscAssertPointer(mem, 4);
1686   if (!count) {
1687     *(void **)mem = NULL;
1688     PetscFunctionReturn(PETSC_SUCCESS);
1689   }
1690   if (dm->workin) {
1691     link       = dm->workin;
1692     dm->workin = dm->workin->next;
1693   } else {
1694     PetscCall(PetscNew(&link));
1695   }
1696   /* Avoid MPI_Type_size for most used datatypes
1697      Get size directly */
1698   if (dtype == MPIU_INT) dsize = sizeof(PetscInt);
1699   else if (dtype == MPIU_REAL) dsize = sizeof(PetscReal);
1700 #if defined(PETSC_USE_64BIT_INDICES)
1701   else if (dtype == MPI_INT) dsize = sizeof(int);
1702 #endif
1703 #if defined(PETSC_USE_COMPLEX)
1704   else if (dtype == MPIU_SCALAR) dsize = sizeof(PetscScalar);
1705 #endif
1706   else PetscCallMPI(MPI_Type_size(dtype, &dsize));
1707 
1708   if (((size_t)dsize * count) > link->bytes) {
1709     PetscCall(PetscFree(link->mem));
1710     PetscCall(PetscMalloc(dsize * count, &link->mem));
1711     link->bytes = dsize * count;
1712   }
1713   link->next  = dm->workout;
1714   dm->workout = link;
1715 #if defined(__MEMCHECK_H) && (defined(PLAT_amd64_linux) || defined(PLAT_x86_linux) || defined(PLAT_amd64_darwin))
1716   VALGRIND_MAKE_MEM_NOACCESS((char *)link->mem + (size_t)dsize * count, link->bytes - (size_t)dsize * count);
1717   VALGRIND_MAKE_MEM_UNDEFINED(link->mem, (size_t)dsize * count);
1718 #endif
1719   *(void **)mem = link->mem;
1720   PetscFunctionReturn(PETSC_SUCCESS);
1721 }
1722 
1723 /*@C
1724   DMRestoreWorkArray - Restores a work array obtained with `DMCreateWorkArray()`
1725 
1726   Not Collective
1727 
1728   Input Parameters:
1729 + dm    - the `DM` object
1730 . count - The minimum size
1731 - dtype - MPI data type, often `MPIU_REAL`, `MPIU_SCALAR`, `MPIU_INT`
1732 
1733   Output Parameter:
1734 . mem - the work array
1735 
1736   Level: developer
1737 
1738   Developer Note:
1739   count and dtype are ignored, they are only needed for `DMGetWorkArray()`
1740 
1741 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMCreate()`, `DMGetWorkArray()`
1742 @*/
1743 PetscErrorCode DMRestoreWorkArray(DM dm, PetscInt count, MPI_Datatype dtype, void *mem)
1744 {
1745   DMWorkLink *p, link;
1746 
1747   PetscFunctionBegin;
1748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1749   PetscAssertPointer(mem, 4);
1750   (void)count;
1751   (void)dtype;
1752   if (!*(void **)mem) PetscFunctionReturn(PETSC_SUCCESS);
1753   for (p = &dm->workout; (link = *p); p = &link->next) {
1754     if (link->mem == *(void **)mem) {
1755       *p            = link->next;
1756       link->next    = dm->workin;
1757       dm->workin    = link;
1758       *(void **)mem = NULL;
1759       PetscFunctionReturn(PETSC_SUCCESS);
1760     }
1761   }
1762   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Array was not checked out");
1763 }
1764 
1765 /*@C
1766   DMSetNullSpaceConstructor - Provide a callback function which constructs the nullspace for a given field, defined with `DMAddField()`, when function spaces
1767   are joined or split, such as in `DMCreateSubDM()`
1768 
1769   Logically Collective; No Fortran Support
1770 
1771   Input Parameters:
1772 + dm     - The `DM`
1773 . field  - The field number for the nullspace
1774 - nullsp - A callback to create the nullspace
1775 
1776   Calling sequence of `nullsp`:
1777 + dm        - The present `DM`
1778 . origField - The field number given above, in the original `DM`
1779 . field     - The field number in dm
1780 - nullSpace - The nullspace for the given field
1781 
1782   Level: intermediate
1783 
1784 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMGetNullSpaceConstructor()`, `DMSetNearNullSpaceConstructor()`, `DMGetNearNullSpaceConstructor()`, `DMCreateSubDM()`, `DMCreateSuperDM()`
1785 @*/
1786 PetscErrorCode DMSetNullSpaceConstructor(DM dm, PetscInt field, PetscErrorCode (*nullsp)(DM dm, PetscInt origField, PetscInt field, MatNullSpace *nullSpace))
1787 {
1788   PetscFunctionBegin;
1789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1790   PetscCheck(field < 10, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " >= 10 fields", field);
1791   dm->nullspaceConstructors[field] = nullsp;
1792   PetscFunctionReturn(PETSC_SUCCESS);
1793 }
1794 
1795 /*@C
1796   DMGetNullSpaceConstructor - Return the callback function which constructs the nullspace for a given field, defined with `DMAddField()`
1797 
1798   Not Collective; No Fortran Support
1799 
1800   Input Parameters:
1801 + dm    - The `DM`
1802 - field - The field number for the nullspace
1803 
1804   Output Parameter:
1805 . nullsp - A callback to create the nullspace
1806 
1807   Calling sequence of `nullsp`:
1808 + dm        - The present DM
1809 . origField - The field number given above, in the original DM
1810 . field     - The field number in dm
1811 - nullSpace - The nullspace for the given field
1812 
1813   Level: intermediate
1814 
1815 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMGetField()`, `DMSetNullSpaceConstructor()`, `DMSetNearNullSpaceConstructor()`, `DMGetNearNullSpaceConstructor()`, `DMCreateSubDM()`, `DMCreateSuperDM()`
1816 @*/
1817 PetscErrorCode DMGetNullSpaceConstructor(DM dm, PetscInt field, PetscErrorCode (**nullsp)(DM dm, PetscInt origField, PetscInt field, MatNullSpace *nullSpace))
1818 {
1819   PetscFunctionBegin;
1820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1821   PetscAssertPointer(nullsp, 3);
1822   PetscCheck(field < 10, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " >= 10 fields", field);
1823   *nullsp = dm->nullspaceConstructors[field];
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@C
1828   DMSetNearNullSpaceConstructor - Provide a callback function which constructs the near-nullspace for a given field, defined with `DMAddField()`
1829 
1830   Logically Collective; No Fortran Support
1831 
1832   Input Parameters:
1833 + dm     - The `DM`
1834 . field  - The field number for the nullspace
1835 - nullsp - A callback to create the near-nullspace
1836 
1837   Calling sequence of `nullsp`:
1838 + dm        - The present `DM`
1839 . origField - The field number given above, in the original `DM`
1840 . field     - The field number in dm
1841 - nullSpace - The nullspace for the given field
1842 
1843   Level: intermediate
1844 
1845 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMGetNearNullSpaceConstructor()`, `DMSetNullSpaceConstructor()`, `DMGetNullSpaceConstructor()`, `DMCreateSubDM()`, `DMCreateSuperDM()`,
1846           `MatNullSpace`
1847 @*/
1848 PetscErrorCode DMSetNearNullSpaceConstructor(DM dm, PetscInt field, PetscErrorCode (*nullsp)(DM dm, PetscInt origField, PetscInt field, MatNullSpace *nullSpace))
1849 {
1850   PetscFunctionBegin;
1851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1852   PetscCheck(field < 10, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " >= 10 fields", field);
1853   dm->nearnullspaceConstructors[field] = nullsp;
1854   PetscFunctionReturn(PETSC_SUCCESS);
1855 }
1856 
1857 /*@C
1858   DMGetNearNullSpaceConstructor - Return the callback function which constructs the near-nullspace for a given field, defined with `DMAddField()`
1859 
1860   Not Collective; No Fortran Support
1861 
1862   Input Parameters:
1863 + dm    - The `DM`
1864 - field - The field number for the nullspace
1865 
1866   Output Parameter:
1867 . nullsp - A callback to create the near-nullspace
1868 
1869   Calling sequence of `nullsp`:
1870 + dm        - The present `DM`
1871 . origField - The field number given above, in the original `DM`
1872 . field     - The field number in dm
1873 - nullSpace - The nullspace for the given field
1874 
1875   Level: intermediate
1876 
1877 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMGetField()`, `DMSetNearNullSpaceConstructor()`, `DMSetNullSpaceConstructor()`, `DMGetNullSpaceConstructor()`, `DMCreateSubDM()`,
1878           `MatNullSpace`, `DMCreateSuperDM()`
1879 @*/
1880 PetscErrorCode DMGetNearNullSpaceConstructor(DM dm, PetscInt field, PetscErrorCode (**nullsp)(DM dm, PetscInt origField, PetscInt field, MatNullSpace *nullSpace))
1881 {
1882   PetscFunctionBegin;
1883   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1884   PetscAssertPointer(nullsp, 3);
1885   PetscCheck(field < 10, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cannot handle %" PetscInt_FMT " >= 10 fields", field);
1886   *nullsp = dm->nearnullspaceConstructors[field];
1887   PetscFunctionReturn(PETSC_SUCCESS);
1888 }
1889 
1890 /*@C
1891   DMCreateFieldIS - Creates a set of `IS` objects with the global indices of dofs for each field defined with `DMAddField()`
1892 
1893   Not Collective; No Fortran Support
1894 
1895   Input Parameter:
1896 . dm - the `DM` object
1897 
1898   Output Parameters:
1899 + numFields  - The number of fields (or `NULL` if not requested)
1900 . fieldNames - The number of each field (or `NULL` if not requested)
1901 - fields     - The global indices for each field (or `NULL` if not requested)
1902 
1903   Level: intermediate
1904 
1905   Note:
1906   The user is responsible for freeing all requested arrays. In particular, every entry of `fieldNames` should be freed with
1907   `PetscFree()`, every entry of `fields` should be destroyed with `ISDestroy()`, and both arrays should be freed with
1908   `PetscFree()`.
1909 
1910   Developer Note:
1911   It is not clear why both this function and `DMCreateFieldDecomposition()` exist. Having two seems redundant and confusing. This function should
1912   likely be removed.
1913 
1914 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMGetField()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`,
1915           `DMCreateFieldDecomposition()`
1916 @*/
1917 PetscErrorCode DMCreateFieldIS(DM dm, PetscInt *numFields, char ***fieldNames, IS **fields)
1918 {
1919   PetscSection section, sectionGlobal;
1920 
1921   PetscFunctionBegin;
1922   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1923   if (numFields) {
1924     PetscAssertPointer(numFields, 2);
1925     *numFields = 0;
1926   }
1927   if (fieldNames) {
1928     PetscAssertPointer(fieldNames, 3);
1929     *fieldNames = NULL;
1930   }
1931   if (fields) {
1932     PetscAssertPointer(fields, 4);
1933     *fields = NULL;
1934   }
1935   PetscCall(DMGetLocalSection(dm, &section));
1936   if (section) {
1937     PetscInt *fieldSizes, *fieldNc, **fieldIndices;
1938     PetscInt  nF, f, pStart, pEnd, p;
1939 
1940     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
1941     PetscCall(PetscSectionGetNumFields(section, &nF));
1942     PetscCall(PetscMalloc3(nF, &fieldSizes, nF, &fieldNc, nF, &fieldIndices));
1943     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
1944     for (f = 0; f < nF; ++f) {
1945       fieldSizes[f] = 0;
1946       PetscCall(PetscSectionGetFieldComponents(section, f, &fieldNc[f]));
1947     }
1948     for (p = pStart; p < pEnd; ++p) {
1949       PetscInt gdof;
1950 
1951       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
1952       if (gdof > 0) {
1953         for (f = 0; f < nF; ++f) {
1954           PetscInt fdof, fcdof, fpdof;
1955 
1956           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
1957           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
1958           fpdof = fdof - fcdof;
1959           if (fpdof && fpdof != fieldNc[f]) {
1960             /* Layout does not admit a pointwise block size */
1961             fieldNc[f] = 1;
1962           }
1963           fieldSizes[f] += fpdof;
1964         }
1965       }
1966     }
1967     for (f = 0; f < nF; ++f) {
1968       PetscCall(PetscMalloc1(fieldSizes[f], &fieldIndices[f]));
1969       fieldSizes[f] = 0;
1970     }
1971     for (p = pStart; p < pEnd; ++p) {
1972       PetscInt gdof, goff;
1973 
1974       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
1975       if (gdof > 0) {
1976         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &goff));
1977         for (f = 0; f < nF; ++f) {
1978           PetscInt fdof, fcdof, fc;
1979 
1980           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
1981           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
1982           for (fc = 0; fc < fdof - fcdof; ++fc, ++fieldSizes[f]) fieldIndices[f][fieldSizes[f]] = goff++;
1983         }
1984       }
1985     }
1986     if (numFields) *numFields = nF;
1987     if (fieldNames) {
1988       PetscCall(PetscMalloc1(nF, fieldNames));
1989       for (f = 0; f < nF; ++f) {
1990         const char *fieldName;
1991 
1992         PetscCall(PetscSectionGetFieldName(section, f, &fieldName));
1993         PetscCall(PetscStrallocpy(fieldName, (char **)&(*fieldNames)[f]));
1994       }
1995     }
1996     if (fields) {
1997       PetscCall(PetscMalloc1(nF, fields));
1998       for (f = 0; f < nF; ++f) {
1999         PetscInt bs, in[2], out[2];
2000 
2001         PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), fieldSizes[f], fieldIndices[f], PETSC_OWN_POINTER, &(*fields)[f]));
2002         in[0] = -fieldNc[f];
2003         in[1] = fieldNc[f];
2004         PetscCallMPI(MPIU_Allreduce(in, out, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
2005         bs = (-out[0] == out[1]) ? out[1] : 1;
2006         PetscCall(ISSetBlockSize((*fields)[f], bs));
2007       }
2008     }
2009     PetscCall(PetscFree3(fieldSizes, fieldNc, fieldIndices));
2010   } else PetscTryTypeMethod(dm, createfieldis, numFields, fieldNames, fields);
2011   PetscFunctionReturn(PETSC_SUCCESS);
2012 }
2013 
2014 /*@C
2015   DMCreateFieldDecomposition - Returns a list of `IS` objects defining a decomposition of a problem into subproblems
2016   corresponding to different fields.
2017 
2018   Not Collective; No Fortran Support
2019 
2020   Input Parameter:
2021 . dm - the `DM` object
2022 
2023   Output Parameters:
2024 + len      - The number of fields (or `NULL` if not requested)
2025 . namelist - The name for each field (or `NULL` if not requested)
2026 . islist   - The global indices for each field (or `NULL` if not requested)
2027 - dmlist   - The `DM`s for each field subproblem (or `NULL`, if not requested; if `NULL` is returned, no `DM`s are defined)
2028 
2029   Level: intermediate
2030 
2031   Notes:
2032   Each `IS` contains the global indices of the dofs of the corresponding field, defined by
2033   `DMAddField()`. The optional list of `DM`s define the `DM` for each subproblem.
2034 
2035   The same as `DMCreateFieldIS()` but also returns a `DM` for each field.
2036 
2037   The user is responsible for freeing all requested arrays. In particular, every entry of `namelist` should be freed with
2038   `PetscFree()`, every entry of `islist` should be destroyed with `ISDestroy()`, every entry of `dmlist` should be destroyed with `DMDestroy()`,
2039   and all of the arrays should be freed with `PetscFree()`.
2040 
2041   Developer Notes:
2042   It is not clear why this function and `DMCreateFieldIS()` exist. Having two seems redundant and confusing.
2043 
2044   Unlike  `DMRefine()`, `DMCoarsen()`, and `DMCreateDomainDecomposition()` this provides no mechanism to provide hooks that are called after the
2045   decomposition is computed.
2046 
2047 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMCreateFieldIS()`, `DMCreateSubDM()`, `DMCreateDomainDecomposition()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMRefine()`, `DMCoarsen()`
2048 @*/
2049 PetscErrorCode DMCreateFieldDecomposition(DM dm, PetscInt *len, char ***namelist, IS **islist, DM **dmlist)
2050 {
2051   PetscFunctionBegin;
2052   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2053   if (len) {
2054     PetscAssertPointer(len, 2);
2055     *len = 0;
2056   }
2057   if (namelist) {
2058     PetscAssertPointer(namelist, 3);
2059     *namelist = NULL;
2060   }
2061   if (islist) {
2062     PetscAssertPointer(islist, 4);
2063     *islist = NULL;
2064   }
2065   if (dmlist) {
2066     PetscAssertPointer(dmlist, 5);
2067     *dmlist = NULL;
2068   }
2069   /*
2070    Is it a good idea to apply the following check across all impls?
2071    Perhaps some impls can have a well-defined decomposition before DMSetUp?
2072    This, however, follows the general principle that accessors are not well-behaved until the object is set up.
2073    */
2074   PetscCheck(dm->setupcalled, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Decomposition defined only after DMSetUp");
2075   if (!dm->ops->createfielddecomposition) {
2076     PetscSection section;
2077     PetscInt     numFields, f;
2078 
2079     PetscCall(DMGetLocalSection(dm, &section));
2080     if (section) PetscCall(PetscSectionGetNumFields(section, &numFields));
2081     if (section && numFields && dm->ops->createsubdm) {
2082       if (len) *len = numFields;
2083       if (namelist) PetscCall(PetscMalloc1(numFields, namelist));
2084       if (islist) PetscCall(PetscMalloc1(numFields, islist));
2085       if (dmlist) PetscCall(PetscMalloc1(numFields, dmlist));
2086       for (f = 0; f < numFields; ++f) {
2087         const char *fieldName;
2088 
2089         PetscCall(DMCreateSubDM(dm, 1, &f, islist ? &(*islist)[f] : NULL, dmlist ? &(*dmlist)[f] : NULL));
2090         if (namelist) {
2091           PetscCall(PetscSectionGetFieldName(section, f, &fieldName));
2092           PetscCall(PetscStrallocpy(fieldName, (char **)&(*namelist)[f]));
2093         }
2094       }
2095     } else {
2096       PetscCall(DMCreateFieldIS(dm, len, namelist, islist));
2097       /* By default there are no DMs associated with subproblems. */
2098       if (dmlist) *dmlist = NULL;
2099     }
2100   } else PetscUseTypeMethod(dm, createfielddecomposition, len, namelist, islist, dmlist);
2101   PetscFunctionReturn(PETSC_SUCCESS);
2102 }
2103 
2104 /*@
2105   DMCreateSubDM - Returns an `IS` and `DM` encapsulating a subproblem defined by the fields passed in.
2106   The fields are defined by `DMCreateFieldIS()`.
2107 
2108   Not collective
2109 
2110   Input Parameters:
2111 + dm        - The `DM` object
2112 . numFields - The number of fields to select
2113 - fields    - The field numbers of the selected fields
2114 
2115   Output Parameters:
2116 + is    - The global indices for all the degrees of freedom in the new sub `DM`, use `NULL` if not needed
2117 - subdm - The `DM` for the subproblem, use `NULL` if not needed
2118 
2119   Level: intermediate
2120 
2121   Note:
2122   You need to call `DMPlexSetMigrationSF()` on the original `DM` if you want the Global-To-Natural map to be automatically constructed
2123 
2124 .seealso: [](ch_dmbase), `DM`, `DMCreateFieldIS()`, `DMCreateFieldDecomposition()`, `DMAddField()`, `DMCreateSuperDM()`, `IS`, `DMPlexSetMigrationSF()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`
2125 @*/
2126 PetscErrorCode DMCreateSubDM(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
2127 {
2128   PetscFunctionBegin;
2129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2130   PetscAssertPointer(fields, 3);
2131   if (is) PetscAssertPointer(is, 4);
2132   if (subdm) PetscAssertPointer(subdm, 5);
2133   PetscUseTypeMethod(dm, createsubdm, numFields, fields, is, subdm);
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 /*@C
2138   DMCreateSuperDM - Returns an arrays of `IS` and `DM` encapsulating a superproblem defined by multiple `DM`s passed in.
2139 
2140   Not collective
2141 
2142   Input Parameters:
2143 + dms - The `DM` objects
2144 - n   - The number of `DM`s
2145 
2146   Output Parameters:
2147 + is      - The global indices for each of subproblem within the super `DM`, or NULL
2148 - superdm - The `DM` for the superproblem
2149 
2150   Level: intermediate
2151 
2152   Note:
2153   You need to call `DMPlexSetMigrationSF()` on the original `DM` if you want the Global-To-Natural map to be automatically constructed
2154 
2155 .seealso: [](ch_dmbase), `DM`, `DMCreateSubDM()`, `DMPlexSetMigrationSF()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMCreateFieldIS()`, `DMCreateDomainDecomposition()`
2156 @*/
2157 PetscErrorCode DMCreateSuperDM(DM dms[], PetscInt n, IS *is[], DM *superdm)
2158 {
2159   PetscInt i;
2160 
2161   PetscFunctionBegin;
2162   PetscAssertPointer(dms, 1);
2163   for (i = 0; i < n; ++i) PetscValidHeaderSpecific(dms[i], DM_CLASSID, 1);
2164   if (is) PetscAssertPointer(is, 3);
2165   PetscAssertPointer(superdm, 4);
2166   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of DMs must be nonnegative: %" PetscInt_FMT, n);
2167   if (n) {
2168     DM dm = dms[0];
2169     PetscCheck(dm->ops->createsuperdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "No method createsuperdm for DM of type %s", ((PetscObject)dm)->type_name);
2170     PetscCall((*dm->ops->createsuperdm)(dms, n, is, superdm));
2171   }
2172   PetscFunctionReturn(PETSC_SUCCESS);
2173 }
2174 
2175 /*@C
2176   DMCreateDomainDecomposition - Returns lists of `IS` objects defining a decomposition of a
2177   problem into subproblems corresponding to restrictions to pairs of nested subdomains.
2178 
2179   Not Collective
2180 
2181   Input Parameter:
2182 . dm - the `DM` object
2183 
2184   Output Parameters:
2185 + n           - The number of subproblems in the domain decomposition (or `NULL` if not requested)
2186 . namelist    - The name for each subdomain (or `NULL` if not requested)
2187 . innerislist - The global indices for each inner subdomain (or `NULL`, if not requested)
2188 . outerislist - The global indices for each outer subdomain (or `NULL`, if not requested)
2189 - dmlist      - The `DM`s for each subdomain subproblem (or `NULL`, if not requested; if `NULL` is returned, no `DM`s are defined)
2190 
2191   Level: intermediate
2192 
2193   Notes:
2194   Each `IS` contains the global indices of the dofs of the corresponding subdomains with in the
2195   dofs of the original `DM`. The inner subdomains conceptually define a nonoverlapping
2196   covering, while outer subdomains can overlap.
2197 
2198   The optional list of `DM`s define a `DM` for each subproblem.
2199 
2200   The user is responsible for freeing all requested arrays. In particular, every entry of `namelist` should be freed with
2201   `PetscFree()`, every entry of `innerislist` and `outerislist` should be destroyed with `ISDestroy()`, every entry of `dmlist` should be destroyed with `DMDestroy()`,
2202   and all of the arrays should be freed with `PetscFree()`.
2203 
2204   Developer Notes:
2205   The `dmlist` is for the inner subdomains or the outer subdomains or all subdomains?
2206 
2207   The names are inconsistent, the hooks use `DMSubDomainHook` which is nothing like `DMCreateDomainDecomposition()` while `DMRefineHook` is used for `DMRefine()`.
2208 
2209 .seealso: [](ch_dmbase), `DM`, `DMCreateFieldDecomposition()`, `DMDestroy()`, `DMCreateDomainDecompositionScatters()`, `DMView()`, `DMCreateInterpolation()`,
2210           `DMSubDomainHookAdd()`, `DMSubDomainHookRemove()`,`DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMRefine()`, `DMCoarsen()`
2211 @*/
2212 PetscErrorCode DMCreateDomainDecomposition(DM dm, PetscInt *n, char ***namelist, IS **innerislist, IS **outerislist, DM **dmlist)
2213 {
2214   DMSubDomainHookLink link;
2215   PetscInt            i, l;
2216 
2217   PetscFunctionBegin;
2218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2219   if (n) {
2220     PetscAssertPointer(n, 2);
2221     *n = 0;
2222   }
2223   if (namelist) {
2224     PetscAssertPointer(namelist, 3);
2225     *namelist = NULL;
2226   }
2227   if (innerislist) {
2228     PetscAssertPointer(innerislist, 4);
2229     *innerislist = NULL;
2230   }
2231   if (outerislist) {
2232     PetscAssertPointer(outerislist, 5);
2233     *outerislist = NULL;
2234   }
2235   if (dmlist) {
2236     PetscAssertPointer(dmlist, 6);
2237     *dmlist = NULL;
2238   }
2239   /*
2240    Is it a good idea to apply the following check across all impls?
2241    Perhaps some impls can have a well-defined decomposition before DMSetUp?
2242    This, however, follows the general principle that accessors are not well-behaved until the object is set up.
2243    */
2244   PetscCheck(dm->setupcalled, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Decomposition defined only after DMSetUp");
2245   if (dm->ops->createdomaindecomposition) {
2246     PetscUseTypeMethod(dm, createdomaindecomposition, &l, namelist, innerislist, outerislist, dmlist);
2247     /* copy subdomain hooks and context over to the subdomain DMs */
2248     if (dmlist && *dmlist) {
2249       for (i = 0; i < l; i++) {
2250         for (link = dm->subdomainhook; link; link = link->next) {
2251           if (link->ddhook) PetscCall((*link->ddhook)(dm, (*dmlist)[i], link->ctx));
2252         }
2253         if (dm->ctx) (*dmlist)[i]->ctx = dm->ctx;
2254       }
2255     }
2256     if (n) *n = l;
2257   }
2258   PetscFunctionReturn(PETSC_SUCCESS);
2259 }
2260 
2261 /*@C
2262   DMCreateDomainDecompositionScatters - Returns scatters to the subdomain vectors from the global vector for subdomains created with
2263   `DMCreateDomainDecomposition()`
2264 
2265   Not Collective
2266 
2267   Input Parameters:
2268 + dm     - the `DM` object
2269 . n      - the number of subdomains
2270 - subdms - the local subdomains
2271 
2272   Output Parameters:
2273 + iscat - scatter from global vector to nonoverlapping global vector entries on subdomain
2274 . oscat - scatter from global vector to overlapping global vector entries on subdomain
2275 - gscat - scatter from global vector to local vector on subdomain (fills in ghosts)
2276 
2277   Level: developer
2278 
2279   Note:
2280   This is an alternative to the iis and ois arguments in `DMCreateDomainDecomposition()` that allow for the solution
2281   of general nonlinear problems with overlapping subdomain methods.  While merely having index sets that enable subsets
2282   of the residual equations to be created is fine for linear problems, nonlinear problems require local assembly of
2283   solution and residual data.
2284 
2285   Developer Note:
2286   Can the subdms input be anything or are they exactly the `DM` obtained from
2287   `DMCreateDomainDecomposition()`?
2288 
2289 .seealso: [](ch_dmbase), `DM`, `DMCreateDomainDecomposition()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMCreateFieldIS()`
2290 @*/
2291 PetscErrorCode DMCreateDomainDecompositionScatters(DM dm, PetscInt n, DM *subdms, VecScatter *iscat[], VecScatter *oscat[], VecScatter *gscat[])
2292 {
2293   PetscFunctionBegin;
2294   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2295   PetscAssertPointer(subdms, 3);
2296   PetscUseTypeMethod(dm, createddscatters, n, subdms, iscat, oscat, gscat);
2297   PetscFunctionReturn(PETSC_SUCCESS);
2298 }
2299 
2300 /*@
2301   DMRefine - Refines a `DM` object using a standard nonadaptive refinement of the underlying mesh
2302 
2303   Collective
2304 
2305   Input Parameters:
2306 + dm   - the `DM` object
2307 - comm - the communicator to contain the new `DM` object (or `MPI_COMM_NULL`)
2308 
2309   Output Parameter:
2310 . dmf - the refined `DM`, or `NULL`
2311 
2312   Options Database Key:
2313 . -dm_plex_cell_refiner <strategy> - chooses the refinement strategy, e.g. regular, tohex
2314 
2315   Level: developer
2316 
2317   Note:
2318   If no refinement was done, the return value is `NULL`
2319 
2320 .seealso: [](ch_dmbase), `DM`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateDomainDecomposition()`,
2321           `DMRefineHookAdd()`, `DMRefineHookRemove()`
2322 @*/
2323 PetscErrorCode DMRefine(DM dm, MPI_Comm comm, DM *dmf)
2324 {
2325   DMRefineHookLink link;
2326 
2327   PetscFunctionBegin;
2328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2329   PetscCall(PetscLogEventBegin(DM_Refine, dm, 0, 0, 0));
2330   PetscUseTypeMethod(dm, refine, comm, dmf);
2331   if (*dmf) {
2332     (*dmf)->ops->creatematrix = dm->ops->creatematrix;
2333 
2334     PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)dm, (PetscObject)*dmf));
2335 
2336     (*dmf)->ctx       = dm->ctx;
2337     (*dmf)->leveldown = dm->leveldown;
2338     (*dmf)->levelup   = dm->levelup + 1;
2339 
2340     PetscCall(DMSetMatType(*dmf, dm->mattype));
2341     for (link = dm->refinehook; link; link = link->next) {
2342       if (link->refinehook) PetscCall((*link->refinehook)(dm, *dmf, link->ctx));
2343     }
2344   }
2345   PetscCall(PetscLogEventEnd(DM_Refine, dm, 0, 0, 0));
2346   PetscFunctionReturn(PETSC_SUCCESS);
2347 }
2348 
2349 /*@C
2350   DMRefineHookAdd - adds a callback to be run when interpolating a nonlinear problem to a finer grid
2351 
2352   Logically Collective; No Fortran Support
2353 
2354   Input Parameters:
2355 + coarse     - `DM` on which to run a hook when interpolating to a finer level
2356 . refinehook - function to run when setting up the finer level
2357 . interphook - function to run to update data on finer levels (once per `SNESSolve()`)
2358 - ctx        - [optional] user-defined context for provide data for the hooks (may be `NULL`)
2359 
2360   Calling sequence of `refinehook`:
2361 + coarse - coarse level `DM`
2362 . fine   - fine level `DM` to interpolate problem to
2363 - ctx    - optional user-defined function context
2364 
2365   Calling sequence of `interphook`:
2366 + coarse - coarse level `DM`
2367 . interp - matrix interpolating a coarse-level solution to the finer grid
2368 . fine   - fine level `DM` to update
2369 - ctx    - optional user-defined function context
2370 
2371   Level: advanced
2372 
2373   Notes:
2374   This function is only needed if auxiliary data that is attached to the `DM`s via, for example, `PetscObjectCompose()`, needs to be
2375   passed to fine grids while grid sequencing.
2376 
2377   The actual interpolation is done when `DMInterpolate()` is called.
2378 
2379   If this function is called multiple times, the hooks will be run in the order they are added.
2380 
2381 .seealso: [](ch_dmbase), `DM`, `DMCoarsenHookAdd()`, `DMInterpolate()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`
2382 @*/
2383 PetscErrorCode DMRefineHookAdd(DM coarse, PetscErrorCode (*refinehook)(DM coarse, DM fine, void *ctx), PetscErrorCode (*interphook)(DM coarse, Mat interp, DM fine, void *ctx), void *ctx)
2384 {
2385   DMRefineHookLink link, *p;
2386 
2387   PetscFunctionBegin;
2388   PetscValidHeaderSpecific(coarse, DM_CLASSID, 1);
2389   for (p = &coarse->refinehook; *p; p = &(*p)->next) { /* Scan to the end of the current list of hooks */
2390     if ((*p)->refinehook == refinehook && (*p)->interphook == interphook && (*p)->ctx == ctx) PetscFunctionReturn(PETSC_SUCCESS);
2391   }
2392   PetscCall(PetscNew(&link));
2393   link->refinehook = refinehook;
2394   link->interphook = interphook;
2395   link->ctx        = ctx;
2396   link->next       = NULL;
2397   *p               = link;
2398   PetscFunctionReturn(PETSC_SUCCESS);
2399 }
2400 
2401 /*@C
2402   DMRefineHookRemove - remove a callback from the list of hooks, that have been set with `DMRefineHookAdd()`, to be run when interpolating
2403   a nonlinear problem to a finer grid
2404 
2405   Logically Collective; No Fortran Support
2406 
2407   Input Parameters:
2408 + coarse     - the `DM` on which to run a hook when restricting to a coarser level
2409 . refinehook - function to run when setting up a finer level
2410 . interphook - function to run to update data on finer levels
2411 - ctx        - [optional] user-defined context for provide data for the hooks (may be `NULL`)
2412 
2413   Level: advanced
2414 
2415   Note:
2416   This function does nothing if the hook is not in the list.
2417 
2418 .seealso: [](ch_dmbase), `DM`, `DMRefineHookAdd()`, `DMCoarsenHookRemove()`, `DMInterpolate()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`
2419 @*/
2420 PetscErrorCode DMRefineHookRemove(DM coarse, PetscErrorCode (*refinehook)(DM, DM, void *), PetscErrorCode (*interphook)(DM, Mat, DM, void *), void *ctx)
2421 {
2422   DMRefineHookLink link, *p;
2423 
2424   PetscFunctionBegin;
2425   PetscValidHeaderSpecific(coarse, DM_CLASSID, 1);
2426   for (p = &coarse->refinehook; *p; p = &(*p)->next) { /* Search the list of current hooks */
2427     if ((*p)->refinehook == refinehook && (*p)->interphook == interphook && (*p)->ctx == ctx) {
2428       link = *p;
2429       *p   = link->next;
2430       PetscCall(PetscFree(link));
2431       break;
2432     }
2433   }
2434   PetscFunctionReturn(PETSC_SUCCESS);
2435 }
2436 
2437 /*@
2438   DMInterpolate - interpolates user-defined problem data attached to a `DM` to a finer `DM` by running hooks registered by `DMRefineHookAdd()`
2439 
2440   Collective if any hooks are
2441 
2442   Input Parameters:
2443 + coarse - coarser `DM` to use as a base
2444 . interp - interpolation matrix, apply using `MatInterpolate()`
2445 - fine   - finer `DM` to update
2446 
2447   Level: developer
2448 
2449   Developer Note:
2450   This routine is called `DMInterpolate()` while the hook is called `DMRefineHookAdd()`. It would be better to have an
2451   an API with consistent terminology.
2452 
2453 .seealso: [](ch_dmbase), `DM`, `DMRefineHookAdd()`, `MatInterpolate()`
2454 @*/
2455 PetscErrorCode DMInterpolate(DM coarse, Mat interp, DM fine)
2456 {
2457   DMRefineHookLink link;
2458 
2459   PetscFunctionBegin;
2460   for (link = fine->refinehook; link; link = link->next) {
2461     if (link->interphook) PetscCall((*link->interphook)(coarse, interp, fine, link->ctx));
2462   }
2463   PetscFunctionReturn(PETSC_SUCCESS);
2464 }
2465 
2466 /*@
2467   DMInterpolateSolution - Interpolates a solution from a coarse mesh to a fine mesh.
2468 
2469   Collective
2470 
2471   Input Parameters:
2472 + coarse    - coarse `DM`
2473 . fine      - fine `DM`
2474 . interp    - (optional) the matrix computed by `DMCreateInterpolation()`.  Implementations may not need this, but if it
2475             is available it can avoid some recomputation.  If it is provided, `MatInterpolate()` will be used if
2476             the coarse `DM` does not have a specialized implementation.
2477 - coarseSol - solution on the coarse mesh
2478 
2479   Output Parameter:
2480 . fineSol - the interpolation of coarseSol to the fine mesh
2481 
2482   Level: developer
2483 
2484   Note:
2485   This function exists because the interpolation of a solution vector between meshes is not always a linear
2486   map.  For example, if a boundary value problem has an inhomogeneous Dirichlet boundary condition that is compressed
2487   out of the solution vector.  Or if interpolation is inherently a nonlinear operation, such as a method using
2488   slope-limiting reconstruction.
2489 
2490   Developer Note:
2491   This doesn't just interpolate "solutions" so its API name is questionable.
2492 
2493 .seealso: [](ch_dmbase), `DM`, `DMInterpolate()`, `DMCreateInterpolation()`
2494 @*/
2495 PetscErrorCode DMInterpolateSolution(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
2496 {
2497   PetscErrorCode (*interpsol)(DM, DM, Mat, Vec, Vec) = NULL;
2498 
2499   PetscFunctionBegin;
2500   PetscValidHeaderSpecific(coarse, DM_CLASSID, 1);
2501   if (interp) PetscValidHeaderSpecific(interp, MAT_CLASSID, 3);
2502   PetscValidHeaderSpecific(coarseSol, VEC_CLASSID, 4);
2503   PetscValidHeaderSpecific(fineSol, VEC_CLASSID, 5);
2504 
2505   PetscCall(PetscObjectQueryFunction((PetscObject)coarse, "DMInterpolateSolution_C", &interpsol));
2506   if (interpsol) {
2507     PetscCall((*interpsol)(coarse, fine, interp, coarseSol, fineSol));
2508   } else if (interp) {
2509     PetscCall(MatInterpolate(interp, coarseSol, fineSol));
2510   } else SETERRQ(PetscObjectComm((PetscObject)coarse), PETSC_ERR_SUP, "DM %s does not implement DMInterpolateSolution()", ((PetscObject)coarse)->type_name);
2511   PetscFunctionReturn(PETSC_SUCCESS);
2512 }
2513 
2514 /*@
2515   DMGetRefineLevel - Gets the number of refinements that have generated this `DM` from some initial `DM`.
2516 
2517   Not Collective
2518 
2519   Input Parameter:
2520 . dm - the `DM` object
2521 
2522   Output Parameter:
2523 . level - number of refinements
2524 
2525   Level: developer
2526 
2527   Note:
2528   This can be used, by example, to set the number of coarser levels associated with this `DM` for a multigrid solver.
2529 
2530 .seealso: [](ch_dmbase), `DM`, `DMRefine()`, `DMCoarsen()`, `DMGetCoarsenLevel()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`
2531 @*/
2532 PetscErrorCode DMGetRefineLevel(DM dm, PetscInt *level)
2533 {
2534   PetscFunctionBegin;
2535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2536   *level = dm->levelup;
2537   PetscFunctionReturn(PETSC_SUCCESS);
2538 }
2539 
2540 /*@
2541   DMSetRefineLevel - Sets the number of refinements that have generated this `DM`.
2542 
2543   Not Collective
2544 
2545   Input Parameters:
2546 + dm    - the `DM` object
2547 - level - number of refinements
2548 
2549   Level: advanced
2550 
2551   Notes:
2552   This value is used by `PCMG` to determine how many multigrid levels to use
2553 
2554   The values are usually set automatically by the process that is causing the refinements of an initial `DM` by calling this routine.
2555 
2556 .seealso: [](ch_dmbase), `DM`, `DMGetRefineLevel()`, `DMCoarsen()`, `DMGetCoarsenLevel()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`
2557 @*/
2558 PetscErrorCode DMSetRefineLevel(DM dm, PetscInt level)
2559 {
2560   PetscFunctionBegin;
2561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2562   dm->levelup = level;
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 /*@
2567   DMExtrude - Extrude a `DM` object from a surface
2568 
2569   Collective
2570 
2571   Input Parameters:
2572 + dm     - the `DM` object
2573 - layers - the number of extruded cell layers
2574 
2575   Output Parameter:
2576 . dme - the extruded `DM`, or `NULL`
2577 
2578   Level: developer
2579 
2580   Note:
2581   If no extrusion was done, the return value is `NULL`
2582 
2583 .seealso: [](ch_dmbase), `DM`, `DMRefine()`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`
2584 @*/
2585 PetscErrorCode DMExtrude(DM dm, PetscInt layers, DM *dme)
2586 {
2587   PetscFunctionBegin;
2588   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2589   PetscUseTypeMethod(dm, extrude, layers, dme);
2590   if (*dme) {
2591     (*dme)->ops->creatematrix = dm->ops->creatematrix;
2592     PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)dm, (PetscObject)*dme));
2593     (*dme)->ctx = dm->ctx;
2594     PetscCall(DMSetMatType(*dme, dm->mattype));
2595   }
2596   PetscFunctionReturn(PETSC_SUCCESS);
2597 }
2598 
2599 PetscErrorCode DMGetBasisTransformDM_Internal(DM dm, DM *tdm)
2600 {
2601   PetscFunctionBegin;
2602   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2603   PetscAssertPointer(tdm, 2);
2604   *tdm = dm->transformDM;
2605   PetscFunctionReturn(PETSC_SUCCESS);
2606 }
2607 
2608 PetscErrorCode DMGetBasisTransformVec_Internal(DM dm, Vec *tv)
2609 {
2610   PetscFunctionBegin;
2611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2612   PetscAssertPointer(tv, 2);
2613   *tv = dm->transform;
2614   PetscFunctionReturn(PETSC_SUCCESS);
2615 }
2616 
2617 /*@
2618   DMHasBasisTransform - Whether the `DM` employs a basis transformation from functions in global vectors to functions in local vectors
2619 
2620   Input Parameter:
2621 . dm - The `DM`
2622 
2623   Output Parameter:
2624 . flg - `PETSC_TRUE` if a basis transformation should be done
2625 
2626   Level: developer
2627 
2628 .seealso: [](ch_dmbase), `DM`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`, `DMPlexCreateBasisRotation()`
2629 @*/
2630 PetscErrorCode DMHasBasisTransform(DM dm, PetscBool *flg)
2631 {
2632   Vec tv;
2633 
2634   PetscFunctionBegin;
2635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2636   PetscAssertPointer(flg, 2);
2637   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
2638   *flg = tv ? PETSC_TRUE : PETSC_FALSE;
2639   PetscFunctionReturn(PETSC_SUCCESS);
2640 }
2641 
2642 PetscErrorCode DMConstructBasisTransform_Internal(DM dm)
2643 {
2644   PetscSection s, ts;
2645   PetscScalar *ta;
2646   PetscInt     cdim, pStart, pEnd, p, Nf, f, Nc, dof;
2647 
2648   PetscFunctionBegin;
2649   PetscCall(DMGetCoordinateDim(dm, &cdim));
2650   PetscCall(DMGetLocalSection(dm, &s));
2651   PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
2652   PetscCall(PetscSectionGetNumFields(s, &Nf));
2653   PetscCall(DMClone(dm, &dm->transformDM));
2654   PetscCall(DMGetLocalSection(dm->transformDM, &ts));
2655   PetscCall(PetscSectionSetNumFields(ts, Nf));
2656   PetscCall(PetscSectionSetChart(ts, pStart, pEnd));
2657   for (f = 0; f < Nf; ++f) {
2658     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
2659     /* We could start to label fields by their transformation properties */
2660     if (Nc != cdim) continue;
2661     for (p = pStart; p < pEnd; ++p) {
2662       PetscCall(PetscSectionGetFieldDof(s, p, f, &dof));
2663       if (!dof) continue;
2664       PetscCall(PetscSectionSetFieldDof(ts, p, f, PetscSqr(cdim)));
2665       PetscCall(PetscSectionAddDof(ts, p, PetscSqr(cdim)));
2666     }
2667   }
2668   PetscCall(PetscSectionSetUp(ts));
2669   PetscCall(DMCreateLocalVector(dm->transformDM, &dm->transform));
2670   PetscCall(VecGetArray(dm->transform, &ta));
2671   for (p = pStart; p < pEnd; ++p) {
2672     for (f = 0; f < Nf; ++f) {
2673       PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
2674       if (dof) {
2675         PetscReal          x[3] = {0.0, 0.0, 0.0};
2676         PetscScalar       *tva;
2677         const PetscScalar *A;
2678 
2679         /* TODO Get quadrature point for this dual basis vector for coordinate */
2680         PetscCall((*dm->transformGetMatrix)(dm, x, PETSC_TRUE, &A, dm->transformCtx));
2681         PetscCall(DMPlexPointLocalFieldRef(dm->transformDM, p, f, ta, (void *)&tva));
2682         PetscCall(PetscArraycpy(tva, A, PetscSqr(cdim)));
2683       }
2684     }
2685   }
2686   PetscCall(VecRestoreArray(dm->transform, &ta));
2687   PetscFunctionReturn(PETSC_SUCCESS);
2688 }
2689 
2690 PetscErrorCode DMCopyTransform(DM dm, DM newdm)
2691 {
2692   PetscFunctionBegin;
2693   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2694   PetscValidHeaderSpecific(newdm, DM_CLASSID, 2);
2695   newdm->transformCtx       = dm->transformCtx;
2696   newdm->transformSetUp     = dm->transformSetUp;
2697   newdm->transformDestroy   = NULL;
2698   newdm->transformGetMatrix = dm->transformGetMatrix;
2699   if (newdm->transformSetUp) PetscCall(DMConstructBasisTransform_Internal(newdm));
2700   PetscFunctionReturn(PETSC_SUCCESS);
2701 }
2702 
2703 /*@C
2704   DMGlobalToLocalHookAdd - adds a callback to be run when `DMGlobalToLocal()` is called
2705 
2706   Logically Collective
2707 
2708   Input Parameters:
2709 + dm        - the `DM`
2710 . beginhook - function to run at the beginning of `DMGlobalToLocalBegin()`
2711 . endhook   - function to run after `DMGlobalToLocalEnd()` has completed
2712 - ctx       - [optional] user-defined context for provide data for the hooks (may be `NULL`)
2713 
2714   Calling sequence of `beginhook`:
2715 + dm   - global `DM`
2716 . g    - global vector
2717 . mode - mode
2718 . l    - local vector
2719 - ctx  - optional user-defined function context
2720 
2721   Calling sequence of `endhook`:
2722 + dm   - global `DM`
2723 . g    - global vector
2724 . mode - mode
2725 . l    - local vector
2726 - ctx  - optional user-defined function context
2727 
2728   Level: advanced
2729 
2730   Note:
2731   The hook may be used to provide, for example, values that represent boundary conditions in the local vectors that do not exist on the global vector.
2732 
2733 .seealso: [](ch_dmbase), `DM`, `DMGlobalToLocal()`, `DMRefineHookAdd()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`
2734 @*/
2735 PetscErrorCode DMGlobalToLocalHookAdd(DM dm, PetscErrorCode (*beginhook)(DM dm, Vec g, InsertMode mode, Vec l, void *ctx), PetscErrorCode (*endhook)(DM dm, Vec g, InsertMode mode, Vec l, void *ctx), void *ctx)
2736 {
2737   DMGlobalToLocalHookLink link, *p;
2738 
2739   PetscFunctionBegin;
2740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2741   for (p = &dm->gtolhook; *p; p = &(*p)->next) { } /* Scan to the end of the current list of hooks */
2742   PetscCall(PetscNew(&link));
2743   link->beginhook = beginhook;
2744   link->endhook   = endhook;
2745   link->ctx       = ctx;
2746   link->next      = NULL;
2747   *p              = link;
2748   PetscFunctionReturn(PETSC_SUCCESS);
2749 }
2750 
2751 static PetscErrorCode DMGlobalToLocalHook_Constraints(DM dm, Vec g, InsertMode mode, Vec l, void *ctx)
2752 {
2753   Mat          cMat;
2754   Vec          cVec, cBias;
2755   PetscSection section, cSec;
2756   PetscInt     pStart, pEnd, p, dof;
2757 
2758   PetscFunctionBegin;
2759   (void)g;
2760   (void)ctx;
2761   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2762   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, &cBias));
2763   if (cMat && (mode == INSERT_VALUES || mode == INSERT_ALL_VALUES || mode == INSERT_BC_VALUES)) {
2764     PetscInt nRows;
2765 
2766     PetscCall(MatGetSize(cMat, &nRows, NULL));
2767     if (nRows <= 0) PetscFunctionReturn(PETSC_SUCCESS);
2768     PetscCall(DMGetLocalSection(dm, &section));
2769     PetscCall(MatCreateVecs(cMat, NULL, &cVec));
2770     PetscCall(MatMult(cMat, l, cVec));
2771     if (cBias) PetscCall(VecAXPY(cVec, 1., cBias));
2772     PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
2773     for (p = pStart; p < pEnd; p++) {
2774       PetscCall(PetscSectionGetDof(cSec, p, &dof));
2775       if (dof) {
2776         PetscScalar *vals;
2777         PetscCall(VecGetValuesSection(cVec, cSec, p, &vals));
2778         PetscCall(VecSetValuesSection(l, section, p, vals, INSERT_ALL_VALUES));
2779       }
2780     }
2781     PetscCall(VecDestroy(&cVec));
2782   }
2783   PetscFunctionReturn(PETSC_SUCCESS);
2784 }
2785 
2786 /*@
2787   DMGlobalToLocal - update local vectors from global vector
2788 
2789   Neighbor-wise Collective
2790 
2791   Input Parameters:
2792 + dm   - the `DM` object
2793 . g    - the global vector
2794 . mode - `INSERT_VALUES` or `ADD_VALUES`
2795 - l    - the local vector
2796 
2797   Level: beginner
2798 
2799   Notes:
2800   The communication involved in this update can be overlapped with computation by instead using
2801   `DMGlobalToLocalBegin()` and `DMGlobalToLocalEnd()`.
2802 
2803   `DMGlobalToLocalHookAdd()` may be used to provide additional operations that are performed during the update process.
2804 
2805 .seealso: [](ch_dmbase), `DM`, `DMGlobalToLocalHookAdd()`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`,
2806           `DMGlobalToLocalEnd()`, `DMLocalToGlobalBegin()`, `DMLocalToGlobal()`, `DMLocalToGlobalEnd()`,
2807           `DMGlobalToLocalBegin()` `DMGlobalToLocalEnd()`
2808 @*/
2809 PetscErrorCode DMGlobalToLocal(DM dm, Vec g, InsertMode mode, Vec l)
2810 {
2811   PetscFunctionBegin;
2812   PetscCall(DMGlobalToLocalBegin(dm, g, mode, l));
2813   PetscCall(DMGlobalToLocalEnd(dm, g, mode, l));
2814   PetscFunctionReturn(PETSC_SUCCESS);
2815 }
2816 
2817 /*@
2818   DMGlobalToLocalBegin - Begins updating local vectors from global vector
2819 
2820   Neighbor-wise Collective
2821 
2822   Input Parameters:
2823 + dm   - the `DM` object
2824 . g    - the global vector
2825 . mode - `INSERT_VALUES` or `ADD_VALUES`
2826 - l    - the local vector
2827 
2828   Level: intermediate
2829 
2830   Notes:
2831   The operation is completed with `DMGlobalToLocalEnd()`
2832 
2833   One can perform local computations between the `DMGlobalToLocalBegin()` and  `DMGlobalToLocalEnd()` to overlap communication and computation
2834 
2835   `DMGlobalToLocal()` is a short form of  `DMGlobalToLocalBegin()` and  `DMGlobalToLocalEnd()`
2836 
2837   `DMGlobalToLocalHookAdd()` may be used to provide additional operations that are performed during the update process.
2838 
2839 .seealso: [](ch_dmbase), `DM`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMGlobalToLocal()`, `DMGlobalToLocalEnd()`, `DMLocalToGlobalBegin()`, `DMLocalToGlobal()`, `DMLocalToGlobalEnd()`
2840 @*/
2841 PetscErrorCode DMGlobalToLocalBegin(DM dm, Vec g, InsertMode mode, Vec l)
2842 {
2843   PetscSF                 sf;
2844   DMGlobalToLocalHookLink link;
2845 
2846   PetscFunctionBegin;
2847   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2848   for (link = dm->gtolhook; link; link = link->next) {
2849     if (link->beginhook) PetscCall((*link->beginhook)(dm, g, mode, l, link->ctx));
2850   }
2851   PetscCall(DMGetSectionSF(dm, &sf));
2852   if (sf) {
2853     const PetscScalar *gArray;
2854     PetscScalar       *lArray;
2855     PetscMemType       lmtype, gmtype;
2856 
2857     PetscCheck(mode != ADD_VALUES, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insertion mode %d", (int)mode);
2858     PetscCall(VecGetArrayAndMemType(l, &lArray, &lmtype));
2859     PetscCall(VecGetArrayReadAndMemType(g, &gArray, &gmtype));
2860     PetscCall(PetscSFBcastWithMemTypeBegin(sf, MPIU_SCALAR, gmtype, gArray, lmtype, lArray, MPI_REPLACE));
2861     PetscCall(VecRestoreArrayAndMemType(l, &lArray));
2862     PetscCall(VecRestoreArrayReadAndMemType(g, &gArray));
2863   } else {
2864     PetscUseTypeMethod(dm, globaltolocalbegin, g, mode == INSERT_ALL_VALUES ? INSERT_VALUES : (mode == ADD_ALL_VALUES ? ADD_VALUES : mode), l);
2865   }
2866   PetscFunctionReturn(PETSC_SUCCESS);
2867 }
2868 
2869 /*@
2870   DMGlobalToLocalEnd - Ends updating local vectors from global vector
2871 
2872   Neighbor-wise Collective
2873 
2874   Input Parameters:
2875 + dm   - the `DM` object
2876 . g    - the global vector
2877 . mode - `INSERT_VALUES` or `ADD_VALUES`
2878 - l    - the local vector
2879 
2880   Level: intermediate
2881 
2882   Note:
2883   See `DMGlobalToLocalBegin()` for details.
2884 
2885 .seealso: [](ch_dmbase), `DM`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMGlobalToLocal()`, `DMLocalToGlobalBegin()`, `DMLocalToGlobal()`, `DMLocalToGlobalEnd()`
2886 @*/
2887 PetscErrorCode DMGlobalToLocalEnd(DM dm, Vec g, InsertMode mode, Vec l)
2888 {
2889   PetscSF                 sf;
2890   const PetscScalar      *gArray;
2891   PetscScalar            *lArray;
2892   PetscBool               transform;
2893   DMGlobalToLocalHookLink link;
2894   PetscMemType            lmtype, gmtype;
2895 
2896   PetscFunctionBegin;
2897   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2898   PetscCall(DMGetSectionSF(dm, &sf));
2899   PetscCall(DMHasBasisTransform(dm, &transform));
2900   if (sf) {
2901     PetscCheck(mode != ADD_VALUES, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insertion mode %d", (int)mode);
2902 
2903     PetscCall(VecGetArrayAndMemType(l, &lArray, &lmtype));
2904     PetscCall(VecGetArrayReadAndMemType(g, &gArray, &gmtype));
2905     PetscCall(PetscSFBcastEnd(sf, MPIU_SCALAR, gArray, lArray, MPI_REPLACE));
2906     PetscCall(VecRestoreArrayAndMemType(l, &lArray));
2907     PetscCall(VecRestoreArrayReadAndMemType(g, &gArray));
2908     if (transform) PetscCall(DMPlexGlobalToLocalBasis(dm, l));
2909   } else {
2910     PetscUseTypeMethod(dm, globaltolocalend, g, mode == INSERT_ALL_VALUES ? INSERT_VALUES : (mode == ADD_ALL_VALUES ? ADD_VALUES : mode), l);
2911   }
2912   PetscCall(DMGlobalToLocalHook_Constraints(dm, g, mode, l, NULL));
2913   for (link = dm->gtolhook; link; link = link->next) {
2914     if (link->endhook) PetscCall((*link->endhook)(dm, g, mode, l, link->ctx));
2915   }
2916   PetscFunctionReturn(PETSC_SUCCESS);
2917 }
2918 
2919 /*@C
2920   DMLocalToGlobalHookAdd - adds a callback to be run when a local to global is called
2921 
2922   Logically Collective
2923 
2924   Input Parameters:
2925 + dm        - the `DM`
2926 . beginhook - function to run at the beginning of `DMLocalToGlobalBegin()`
2927 . endhook   - function to run after `DMLocalToGlobalEnd()` has completed
2928 - ctx       - [optional] user-defined context for provide data for the hooks (may be `NULL`)
2929 
2930   Calling sequence of `beginhook`:
2931 + global - global `DM`
2932 . l      - local vector
2933 . mode   - mode
2934 . g      - global vector
2935 - ctx    - optional user-defined function context
2936 
2937   Calling sequence of `endhook`:
2938 + global - global `DM`
2939 . l      - local vector
2940 . mode   - mode
2941 . g      - global vector
2942 - ctx    - optional user-defined function context
2943 
2944   Level: advanced
2945 
2946 .seealso: [](ch_dmbase), `DM`, `DMLocalToGlobal()`, `DMRefineHookAdd()`, `DMGlobalToLocalHookAdd()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`
2947 @*/
2948 PetscErrorCode DMLocalToGlobalHookAdd(DM dm, PetscErrorCode (*beginhook)(DM global, Vec l, InsertMode mode, Vec g, void *ctx), PetscErrorCode (*endhook)(DM global, Vec l, InsertMode mode, Vec g, void *ctx), void *ctx)
2949 {
2950   DMLocalToGlobalHookLink link, *p;
2951 
2952   PetscFunctionBegin;
2953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2954   for (p = &dm->ltoghook; *p; p = &(*p)->next) { } /* Scan to the end of the current list of hooks */
2955   PetscCall(PetscNew(&link));
2956   link->beginhook = beginhook;
2957   link->endhook   = endhook;
2958   link->ctx       = ctx;
2959   link->next      = NULL;
2960   *p              = link;
2961   PetscFunctionReturn(PETSC_SUCCESS);
2962 }
2963 
2964 static PetscErrorCode DMLocalToGlobalHook_Constraints(DM dm, Vec l, InsertMode mode, Vec g, void *ctx)
2965 {
2966   PetscFunctionBegin;
2967   (void)g;
2968   (void)ctx;
2969   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2970   if (mode == ADD_VALUES || mode == ADD_ALL_VALUES || mode == ADD_BC_VALUES) {
2971     Mat          cMat;
2972     Vec          cVec;
2973     PetscInt     nRows;
2974     PetscSection section, cSec;
2975     PetscInt     pStart, pEnd, p, dof;
2976 
2977     PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
2978     if (!cMat) PetscFunctionReturn(PETSC_SUCCESS);
2979 
2980     PetscCall(MatGetSize(cMat, &nRows, NULL));
2981     if (nRows <= 0) PetscFunctionReturn(PETSC_SUCCESS);
2982     PetscCall(DMGetLocalSection(dm, &section));
2983     PetscCall(MatCreateVecs(cMat, NULL, &cVec));
2984     PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
2985     for (p = pStart; p < pEnd; p++) {
2986       PetscCall(PetscSectionGetDof(cSec, p, &dof));
2987       if (dof) {
2988         PetscInt     d;
2989         PetscScalar *vals;
2990         PetscCall(VecGetValuesSection(l, section, p, &vals));
2991         PetscCall(VecSetValuesSection(cVec, cSec, p, vals, mode));
2992         /* for this to be the true transpose, we have to zero the values that
2993          * we just extracted */
2994         for (d = 0; d < dof; d++) vals[d] = 0.;
2995       }
2996     }
2997     PetscCall(MatMultTransposeAdd(cMat, cVec, l, l));
2998     PetscCall(VecDestroy(&cVec));
2999   }
3000   PetscFunctionReturn(PETSC_SUCCESS);
3001 }
3002 /*@
3003   DMLocalToGlobal - updates global vectors from local vectors
3004 
3005   Neighbor-wise Collective
3006 
3007   Input Parameters:
3008 + dm   - the `DM` object
3009 . l    - the local vector
3010 . mode - if `INSERT_VALUES` then no parallel communication is used, if `ADD_VALUES` then all ghost points from the same base point accumulate into that base point.
3011 - g    - the global vector
3012 
3013   Level: beginner
3014 
3015   Notes:
3016   The communication involved in this update can be overlapped with computation by using
3017   `DMLocalToGlobalBegin()` and `DMLocalToGlobalEnd()`.
3018 
3019   In the `ADD_VALUES` case you normally would zero the receiving vector before beginning this operation.
3020 
3021   `INSERT_VALUES` is not supported for `DMDA`; in that case simply compute the values directly into a global vector instead of a local one.
3022 
3023   Use `DMLocalToGlobalHookAdd()` to add additional operations that are performed on the data during the update process
3024 
3025 .seealso: [](ch_dmbase), `DM`, `DMLocalToGlobalBegin()`, `DMLocalToGlobalEnd()`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMGlobalToLocal()`, `DMGlobalToLocalEnd()`, `DMGlobalToLocalBegin()`, `DMLocalToGlobalHookAdd()`, `DMGlobaToLocallHookAdd()`
3026 @*/
3027 PetscErrorCode DMLocalToGlobal(DM dm, Vec l, InsertMode mode, Vec g)
3028 {
3029   PetscFunctionBegin;
3030   PetscCall(DMLocalToGlobalBegin(dm, l, mode, g));
3031   PetscCall(DMLocalToGlobalEnd(dm, l, mode, g));
3032   PetscFunctionReturn(PETSC_SUCCESS);
3033 }
3034 
3035 /*@
3036   DMLocalToGlobalBegin - begins updating global vectors from local vectors
3037 
3038   Neighbor-wise Collective
3039 
3040   Input Parameters:
3041 + dm   - the `DM` object
3042 . l    - the local vector
3043 . mode - if `INSERT_VALUES` then no parallel communication is used, if `ADD_VALUES` then all ghost points from the same base point accumulate into that base point.
3044 - g    - the global vector
3045 
3046   Level: intermediate
3047 
3048   Notes:
3049   In the `ADD_VALUES` case you normally would zero the receiving vector before beginning this operation.
3050 
3051   `INSERT_VALUES is` not supported for `DMDA`, in that case simply compute the values directly into a global vector instead of a local one.
3052 
3053   Use `DMLocalToGlobalEnd()` to complete the communication process.
3054 
3055   `DMLocalToGlobal()` is a short form of  `DMLocalToGlobalBegin()` and  `DMLocalToGlobalEnd()`
3056 
3057   `DMLocalToGlobalHookAdd()` may be used to provide additional operations that are performed during the update process.
3058 
3059 .seealso: [](ch_dmbase), `DM`, `DMLocalToGlobal()`, `DMLocalToGlobalEnd()`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMGlobalToLocal()`, `DMGlobalToLocalEnd()`, `DMGlobalToLocalBegin()`
3060 @*/
3061 PetscErrorCode DMLocalToGlobalBegin(DM dm, Vec l, InsertMode mode, Vec g)
3062 {
3063   PetscSF                 sf;
3064   PetscSection            s, gs;
3065   DMLocalToGlobalHookLink link;
3066   Vec                     tmpl;
3067   const PetscScalar      *lArray;
3068   PetscScalar            *gArray;
3069   PetscBool               isInsert, transform, l_inplace = PETSC_FALSE, g_inplace = PETSC_FALSE;
3070   PetscMemType            lmtype = PETSC_MEMTYPE_HOST, gmtype = PETSC_MEMTYPE_HOST;
3071 
3072   PetscFunctionBegin;
3073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3074   for (link = dm->ltoghook; link; link = link->next) {
3075     if (link->beginhook) PetscCall((*link->beginhook)(dm, l, mode, g, link->ctx));
3076   }
3077   PetscCall(DMLocalToGlobalHook_Constraints(dm, l, mode, g, NULL));
3078   PetscCall(DMGetSectionSF(dm, &sf));
3079   PetscCall(DMGetLocalSection(dm, &s));
3080   switch (mode) {
3081   case INSERT_VALUES:
3082   case INSERT_ALL_VALUES:
3083   case INSERT_BC_VALUES:
3084     isInsert = PETSC_TRUE;
3085     break;
3086   case ADD_VALUES:
3087   case ADD_ALL_VALUES:
3088   case ADD_BC_VALUES:
3089     isInsert = PETSC_FALSE;
3090     break;
3091   default:
3092     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insertion mode %d", mode);
3093   }
3094   if ((sf && !isInsert) || (s && isInsert)) {
3095     PetscCall(DMHasBasisTransform(dm, &transform));
3096     if (transform) {
3097       PetscCall(DMGetNamedLocalVector(dm, "__petsc_dm_transform_local_copy", &tmpl));
3098       PetscCall(VecCopy(l, tmpl));
3099       PetscCall(DMPlexLocalToGlobalBasis(dm, tmpl));
3100       PetscCall(VecGetArrayRead(tmpl, &lArray));
3101     } else if (isInsert) {
3102       PetscCall(VecGetArrayRead(l, &lArray));
3103     } else {
3104       PetscCall(VecGetArrayReadAndMemType(l, &lArray, &lmtype));
3105       l_inplace = PETSC_TRUE;
3106     }
3107     if (s && isInsert) {
3108       PetscCall(VecGetArray(g, &gArray));
3109     } else {
3110       PetscCall(VecGetArrayAndMemType(g, &gArray, &gmtype));
3111       g_inplace = PETSC_TRUE;
3112     }
3113     if (sf && !isInsert) {
3114       PetscCall(PetscSFReduceWithMemTypeBegin(sf, MPIU_SCALAR, lmtype, lArray, gmtype, gArray, MPIU_SUM));
3115     } else if (s && isInsert) {
3116       PetscInt gStart, pStart, pEnd, p;
3117 
3118       PetscCall(DMGetGlobalSection(dm, &gs));
3119       PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
3120       PetscCall(VecGetOwnershipRange(g, &gStart, NULL));
3121       for (p = pStart; p < pEnd; ++p) {
3122         PetscInt dof, gdof, cdof, gcdof, off, goff, d, e;
3123 
3124         PetscCall(PetscSectionGetDof(s, p, &dof));
3125         PetscCall(PetscSectionGetDof(gs, p, &gdof));
3126         PetscCall(PetscSectionGetConstraintDof(s, p, &cdof));
3127         PetscCall(PetscSectionGetConstraintDof(gs, p, &gcdof));
3128         PetscCall(PetscSectionGetOffset(s, p, &off));
3129         PetscCall(PetscSectionGetOffset(gs, p, &goff));
3130         /* Ignore off-process data and points with no global data */
3131         if (!gdof || goff < 0) continue;
3132         PetscCheck(dof == gdof, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Inconsistent sizes, p: %" PetscInt_FMT " dof: %" PetscInt_FMT " gdof: %" PetscInt_FMT " cdof: %" PetscInt_FMT " gcdof: %" PetscInt_FMT, p, dof, gdof, cdof, gcdof);
3133         /* If no constraints are enforced in the global vector */
3134         if (!gcdof) {
3135           for (d = 0; d < dof; ++d) gArray[goff - gStart + d] = lArray[off + d];
3136           /* If constraints are enforced in the global vector */
3137         } else if (cdof == gcdof) {
3138           const PetscInt *cdofs;
3139           PetscInt        cind = 0;
3140 
3141           PetscCall(PetscSectionGetConstraintIndices(s, p, &cdofs));
3142           for (d = 0, e = 0; d < dof; ++d) {
3143             if ((cind < cdof) && (d == cdofs[cind])) {
3144               ++cind;
3145               continue;
3146             }
3147             gArray[goff - gStart + e++] = lArray[off + d];
3148           }
3149         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Inconsistent sizes, p: %" PetscInt_FMT " dof: %" PetscInt_FMT " gdof: %" PetscInt_FMT " cdof: %" PetscInt_FMT " gcdof: %" PetscInt_FMT, p, dof, gdof, cdof, gcdof);
3150       }
3151     }
3152     if (g_inplace) {
3153       PetscCall(VecRestoreArrayAndMemType(g, &gArray));
3154     } else {
3155       PetscCall(VecRestoreArray(g, &gArray));
3156     }
3157     if (transform) {
3158       PetscCall(VecRestoreArrayRead(tmpl, &lArray));
3159       PetscCall(DMRestoreNamedLocalVector(dm, "__petsc_dm_transform_local_copy", &tmpl));
3160     } else if (l_inplace) {
3161       PetscCall(VecRestoreArrayReadAndMemType(l, &lArray));
3162     } else {
3163       PetscCall(VecRestoreArrayRead(l, &lArray));
3164     }
3165   } else {
3166     PetscUseTypeMethod(dm, localtoglobalbegin, l, mode == INSERT_ALL_VALUES ? INSERT_VALUES : (mode == ADD_ALL_VALUES ? ADD_VALUES : mode), g);
3167   }
3168   PetscFunctionReturn(PETSC_SUCCESS);
3169 }
3170 
3171 /*@
3172   DMLocalToGlobalEnd - updates global vectors from local vectors
3173 
3174   Neighbor-wise Collective
3175 
3176   Input Parameters:
3177 + dm   - the `DM` object
3178 . l    - the local vector
3179 . mode - `INSERT_VALUES` or `ADD_VALUES`
3180 - g    - the global vector
3181 
3182   Level: intermediate
3183 
3184   Note:
3185   See `DMLocalToGlobalBegin()` for full details
3186 
3187 .seealso: [](ch_dmbase), `DM`, `DMLocalToGlobalBegin()`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMGlobalToLocalEnd()`
3188 @*/
3189 PetscErrorCode DMLocalToGlobalEnd(DM dm, Vec l, InsertMode mode, Vec g)
3190 {
3191   PetscSF                 sf;
3192   PetscSection            s;
3193   DMLocalToGlobalHookLink link;
3194   PetscBool               isInsert, transform;
3195 
3196   PetscFunctionBegin;
3197   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3198   PetscCall(DMGetSectionSF(dm, &sf));
3199   PetscCall(DMGetLocalSection(dm, &s));
3200   switch (mode) {
3201   case INSERT_VALUES:
3202   case INSERT_ALL_VALUES:
3203     isInsert = PETSC_TRUE;
3204     break;
3205   case ADD_VALUES:
3206   case ADD_ALL_VALUES:
3207     isInsert = PETSC_FALSE;
3208     break;
3209   default:
3210     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insertion mode %d", mode);
3211   }
3212   if (sf && !isInsert) {
3213     const PetscScalar *lArray;
3214     PetscScalar       *gArray;
3215     Vec                tmpl;
3216 
3217     PetscCall(DMHasBasisTransform(dm, &transform));
3218     if (transform) {
3219       PetscCall(DMGetNamedLocalVector(dm, "__petsc_dm_transform_local_copy", &tmpl));
3220       PetscCall(VecGetArrayRead(tmpl, &lArray));
3221     } else {
3222       PetscCall(VecGetArrayReadAndMemType(l, &lArray, NULL));
3223     }
3224     PetscCall(VecGetArrayAndMemType(g, &gArray, NULL));
3225     PetscCall(PetscSFReduceEnd(sf, MPIU_SCALAR, lArray, gArray, MPIU_SUM));
3226     if (transform) {
3227       PetscCall(VecRestoreArrayRead(tmpl, &lArray));
3228       PetscCall(DMRestoreNamedLocalVector(dm, "__petsc_dm_transform_local_copy", &tmpl));
3229     } else {
3230       PetscCall(VecRestoreArrayReadAndMemType(l, &lArray));
3231     }
3232     PetscCall(VecRestoreArrayAndMemType(g, &gArray));
3233   } else if (s && isInsert) {
3234   } else {
3235     PetscUseTypeMethod(dm, localtoglobalend, l, mode == INSERT_ALL_VALUES ? INSERT_VALUES : (mode == ADD_ALL_VALUES ? ADD_VALUES : mode), g);
3236   }
3237   for (link = dm->ltoghook; link; link = link->next) {
3238     if (link->endhook) PetscCall((*link->endhook)(dm, g, mode, l, link->ctx));
3239   }
3240   PetscFunctionReturn(PETSC_SUCCESS);
3241 }
3242 
3243 /*@
3244   DMLocalToLocalBegin - Begins the process of mapping values from a local vector (that include
3245   ghost points that contain irrelevant values) to another local vector where the ghost points
3246   in the second are set correctly from values on other MPI ranks.
3247 
3248   Neighbor-wise Collective
3249 
3250   Input Parameters:
3251 + dm   - the `DM` object
3252 . g    - the original local vector
3253 - mode - one of `INSERT_VALUES` or `ADD_VALUES`
3254 
3255   Output Parameter:
3256 . l - the local vector with correct ghost values
3257 
3258   Level: intermediate
3259 
3260   Note:
3261   Must be followed by `DMLocalToLocalEnd()`.
3262 
3263 .seealso: [](ch_dmbase), `DM`, `DMLocalToLocalEnd()`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateLocalVector()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMGlobalToLocalEnd()`, `DMLocalToGlobalBegin()`
3264 @*/
3265 PetscErrorCode DMLocalToLocalBegin(DM dm, Vec g, InsertMode mode, Vec l)
3266 {
3267   PetscFunctionBegin;
3268   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3269   PetscValidHeaderSpecific(g, VEC_CLASSID, 2);
3270   PetscValidHeaderSpecific(l, VEC_CLASSID, 4);
3271   PetscUseTypeMethod(dm, localtolocalbegin, g, mode == INSERT_ALL_VALUES ? INSERT_VALUES : (mode == ADD_ALL_VALUES ? ADD_VALUES : mode), l);
3272   PetscFunctionReturn(PETSC_SUCCESS);
3273 }
3274 
3275 /*@
3276   DMLocalToLocalEnd - Maps from a local vector to another local vector where the ghost
3277   points in the second are set correctly. Must be preceded by `DMLocalToLocalBegin()`.
3278 
3279   Neighbor-wise Collective
3280 
3281   Input Parameters:
3282 + dm   - the `DM` object
3283 . g    - the original local vector
3284 - mode - one of `INSERT_VALUES` or `ADD_VALUES`
3285 
3286   Output Parameter:
3287 . l - the local vector with correct ghost values
3288 
3289   Level: intermediate
3290 
3291 .seealso: [](ch_dmbase), `DM`, `DMLocalToLocalBegin()`, `DMCoarsen()`, `DMDestroy()`, `DMView()`, `DMCreateLocalVector()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMGlobalToLocalEnd()`, `DMLocalToGlobalBegin()`
3292 @*/
3293 PetscErrorCode DMLocalToLocalEnd(DM dm, Vec g, InsertMode mode, Vec l)
3294 {
3295   PetscFunctionBegin;
3296   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3297   PetscValidHeaderSpecific(g, VEC_CLASSID, 2);
3298   PetscValidHeaderSpecific(l, VEC_CLASSID, 4);
3299   PetscUseTypeMethod(dm, localtolocalend, g, mode == INSERT_ALL_VALUES ? INSERT_VALUES : (mode == ADD_ALL_VALUES ? ADD_VALUES : mode), l);
3300   PetscFunctionReturn(PETSC_SUCCESS);
3301 }
3302 
3303 /*@
3304   DMCoarsen - Coarsens a `DM` object using a standard, non-adaptive coarsening of the underlying mesh
3305 
3306   Collective
3307 
3308   Input Parameters:
3309 + dm   - the `DM` object
3310 - comm - the communicator to contain the new `DM` object (or `MPI_COMM_NULL`)
3311 
3312   Output Parameter:
3313 . dmc - the coarsened `DM`
3314 
3315   Level: developer
3316 
3317 .seealso: [](ch_dmbase), `DM`, `DMRefine()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateDomainDecomposition()`,
3318           `DMCoarsenHookAdd()`, `DMCoarsenHookRemove()`
3319 @*/
3320 PetscErrorCode DMCoarsen(DM dm, MPI_Comm comm, DM *dmc)
3321 {
3322   DMCoarsenHookLink link;
3323 
3324   PetscFunctionBegin;
3325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3326   PetscCall(PetscLogEventBegin(DM_Coarsen, dm, 0, 0, 0));
3327   PetscUseTypeMethod(dm, coarsen, comm, dmc);
3328   if (*dmc) {
3329     (*dmc)->bind_below = dm->bind_below; /* Propagate this from parent DM; otherwise -dm_bind_below will be useless for multigrid cases. */
3330     PetscCall(DMSetCoarseDM(dm, *dmc));
3331     (*dmc)->ops->creatematrix = dm->ops->creatematrix;
3332     PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)dm, (PetscObject)*dmc));
3333     (*dmc)->ctx       = dm->ctx;
3334     (*dmc)->levelup   = dm->levelup;
3335     (*dmc)->leveldown = dm->leveldown + 1;
3336     PetscCall(DMSetMatType(*dmc, dm->mattype));
3337     for (link = dm->coarsenhook; link; link = link->next) {
3338       if (link->coarsenhook) PetscCall((*link->coarsenhook)(dm, *dmc, link->ctx));
3339     }
3340   }
3341   PetscCall(PetscLogEventEnd(DM_Coarsen, dm, 0, 0, 0));
3342   PetscCheck(*dmc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "NULL coarse mesh produced");
3343   PetscFunctionReturn(PETSC_SUCCESS);
3344 }
3345 
3346 /*@C
3347   DMCoarsenHookAdd - adds a callback to be run when restricting a nonlinear problem to the coarse grid
3348 
3349   Logically Collective; No Fortran Support
3350 
3351   Input Parameters:
3352 + fine         - `DM` on which to run a hook when restricting to a coarser level
3353 . coarsenhook  - function to run when setting up a coarser level
3354 . restricthook - function to run to update data on coarser levels (called once per `SNESSolve()`)
3355 - ctx          - [optional] user-defined context for provide data for the hooks (may be `NULL`)
3356 
3357   Calling sequence of `coarsenhook`:
3358 + fine   - fine level `DM`
3359 . coarse - coarse level `DM` to restrict problem to
3360 - ctx    - optional user-defined function context
3361 
3362   Calling sequence of `restricthook`:
3363 + fine      - fine level `DM`
3364 . mrestrict - matrix restricting a fine-level solution to the coarse grid, usually the transpose of the interpolation
3365 . rscale    - scaling vector for restriction
3366 . inject    - matrix restricting by injection
3367 . coarse    - coarse level DM to update
3368 - ctx       - optional user-defined function context
3369 
3370   Level: advanced
3371 
3372   Notes:
3373   This function is only needed if auxiliary data, attached to the `DM` with `PetscObjectCompose()`, needs to be set up or passed from the fine `DM` to the coarse `DM`.
3374 
3375   If this function is called multiple times, the hooks will be run in the order they are added.
3376 
3377   In order to compose with nonlinear preconditioning without duplicating storage, the hook should be implemented to
3378   extract the finest level information from its context (instead of from the `SNES`).
3379 
3380   The hooks are automatically called by `DMRestrict()`
3381 
3382 .seealso: [](ch_dmbase), `DM`, `DMCoarsenHookRemove()`, `DMRefineHookAdd()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`
3383 @*/
3384 PetscErrorCode DMCoarsenHookAdd(DM fine, PetscErrorCode (*coarsenhook)(DM fine, DM coarse, void *ctx), PetscErrorCode (*restricthook)(DM fine, Mat mrestrict, Vec rscale, Mat inject, DM coarse, void *ctx), void *ctx)
3385 {
3386   DMCoarsenHookLink link, *p;
3387 
3388   PetscFunctionBegin;
3389   PetscValidHeaderSpecific(fine, DM_CLASSID, 1);
3390   for (p = &fine->coarsenhook; *p; p = &(*p)->next) { /* Scan to the end of the current list of hooks */
3391     if ((*p)->coarsenhook == coarsenhook && (*p)->restricthook == restricthook && (*p)->ctx == ctx) PetscFunctionReturn(PETSC_SUCCESS);
3392   }
3393   PetscCall(PetscNew(&link));
3394   link->coarsenhook  = coarsenhook;
3395   link->restricthook = restricthook;
3396   link->ctx          = ctx;
3397   link->next         = NULL;
3398   *p                 = link;
3399   PetscFunctionReturn(PETSC_SUCCESS);
3400 }
3401 
3402 /*@C
3403   DMCoarsenHookRemove - remove a callback set with `DMCoarsenHookAdd()`
3404 
3405   Logically Collective; No Fortran Support
3406 
3407   Input Parameters:
3408 + fine         - `DM` on which to run a hook when restricting to a coarser level
3409 . coarsenhook  - function to run when setting up a coarser level
3410 . restricthook - function to run to update data on coarser levels
3411 - ctx          - [optional] user-defined context for provide data for the hooks (may be `NULL`)
3412 
3413   Level: advanced
3414 
3415   Notes:
3416   This function does nothing if the `coarsenhook` is not in the list.
3417 
3418   See `DMCoarsenHookAdd()` for the calling sequence of `coarsenhook` and `restricthook`
3419 
3420 .seealso: [](ch_dmbase), `DM`, `DMCoarsenHookAdd()`, `DMRefineHookAdd()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`
3421 @*/
3422 PetscErrorCode DMCoarsenHookRemove(DM fine, PetscErrorCode (*coarsenhook)(DM, DM, void *), PetscErrorCode (*restricthook)(DM, Mat, Vec, Mat, DM, void *), void *ctx)
3423 {
3424   DMCoarsenHookLink link, *p;
3425 
3426   PetscFunctionBegin;
3427   PetscValidHeaderSpecific(fine, DM_CLASSID, 1);
3428   for (p = &fine->coarsenhook; *p; p = &(*p)->next) { /* Search the list of current hooks */
3429     if ((*p)->coarsenhook == coarsenhook && (*p)->restricthook == restricthook && (*p)->ctx == ctx) {
3430       link = *p;
3431       *p   = link->next;
3432       PetscCall(PetscFree(link));
3433       break;
3434     }
3435   }
3436   PetscFunctionReturn(PETSC_SUCCESS);
3437 }
3438 
3439 /*@
3440   DMRestrict - restricts user-defined problem data to a coarser `DM` by running hooks registered by `DMCoarsenHookAdd()`
3441 
3442   Collective if any hooks are
3443 
3444   Input Parameters:
3445 + fine    - finer `DM` from which the data is obtained
3446 . restrct - restriction matrix, apply using `MatRestrict()`, usually the transpose of the interpolation
3447 . rscale  - scaling vector for restriction
3448 . inject  - injection matrix, also use `MatRestrict()`
3449 - coarse  - coarser `DM` to update
3450 
3451   Level: developer
3452 
3453   Developer Note:
3454   Though this routine is called `DMRestrict()` the hooks are added with `DMCoarsenHookAdd()`, a consistent terminology would be better
3455 
3456 .seealso: [](ch_dmbase), `DM`, `DMCoarsenHookAdd()`, `MatRestrict()`, `DMInterpolate()`, `DMRefineHookAdd()`
3457 @*/
3458 PetscErrorCode DMRestrict(DM fine, Mat restrct, Vec rscale, Mat inject, DM coarse)
3459 {
3460   DMCoarsenHookLink link;
3461 
3462   PetscFunctionBegin;
3463   for (link = fine->coarsenhook; link; link = link->next) {
3464     if (link->restricthook) PetscCall((*link->restricthook)(fine, restrct, rscale, inject, coarse, link->ctx));
3465   }
3466   PetscFunctionReturn(PETSC_SUCCESS);
3467 }
3468 
3469 /*@C
3470   DMSubDomainHookAdd - adds a callback to be run when restricting a problem to subdomain `DM`s with `DMCreateDomainDecomposition()`
3471 
3472   Logically Collective; No Fortran Support
3473 
3474   Input Parameters:
3475 + global       - global `DM`
3476 . ddhook       - function to run to pass data to the decomposition `DM` upon its creation
3477 . restricthook - function to run to update data on block solve (at the beginning of the block solve)
3478 - ctx          - [optional] user-defined context for provide data for the hooks (may be `NULL`)
3479 
3480   Calling sequence of `ddhook`:
3481 + global - global `DM`
3482 . block  - subdomain `DM`
3483 - ctx    - optional user-defined function context
3484 
3485   Calling sequence of `restricthook`:
3486 + global - global `DM`
3487 . out    - scatter to the outer (with ghost and overlap points) sub vector
3488 . in     - scatter to sub vector values only owned locally
3489 . block  - subdomain `DM`
3490 - ctx    - optional user-defined function context
3491 
3492   Level: advanced
3493 
3494   Notes:
3495   This function can be used if auxiliary data needs to be set up on subdomain `DM`s.
3496 
3497   If this function is called multiple times, the hooks will be run in the order they are added.
3498 
3499   In order to compose with nonlinear preconditioning without duplicating storage, the hook should be implemented to
3500   extract the global information from its context (instead of from the `SNES`).
3501 
3502   Developer Note:
3503   It is unclear what "block solve" means within the definition of `restricthook`
3504 
3505 .seealso: [](ch_dmbase), `DM`, `DMSubDomainHookRemove()`, `DMRefineHookAdd()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`, `DMCreateDomainDecomposition()`
3506 @*/
3507 PetscErrorCode DMSubDomainHookAdd(DM global, PetscErrorCode (*ddhook)(DM global, DM block, void *ctx), PetscErrorCode (*restricthook)(DM global, VecScatter out, VecScatter in, DM block, void *ctx), void *ctx)
3508 {
3509   DMSubDomainHookLink link, *p;
3510 
3511   PetscFunctionBegin;
3512   PetscValidHeaderSpecific(global, DM_CLASSID, 1);
3513   for (p = &global->subdomainhook; *p; p = &(*p)->next) { /* Scan to the end of the current list of hooks */
3514     if ((*p)->ddhook == ddhook && (*p)->restricthook == restricthook && (*p)->ctx == ctx) PetscFunctionReturn(PETSC_SUCCESS);
3515   }
3516   PetscCall(PetscNew(&link));
3517   link->restricthook = restricthook;
3518   link->ddhook       = ddhook;
3519   link->ctx          = ctx;
3520   link->next         = NULL;
3521   *p                 = link;
3522   PetscFunctionReturn(PETSC_SUCCESS);
3523 }
3524 
3525 /*@C
3526   DMSubDomainHookRemove - remove a callback from the list to be run when restricting a problem to subdomain `DM`s with `DMCreateDomainDecomposition()`
3527 
3528   Logically Collective; No Fortran Support
3529 
3530   Input Parameters:
3531 + global       - global `DM`
3532 . ddhook       - function to run to pass data to the decomposition `DM` upon its creation
3533 . restricthook - function to run to update data on block solve (at the beginning of the block solve)
3534 - ctx          - [optional] user-defined context for provide data for the hooks (may be `NULL`)
3535 
3536   Level: advanced
3537 
3538   Note:
3539   See `DMSubDomainHookAdd()` for the calling sequences of `ddhook` and `restricthook`
3540 
3541 .seealso: [](ch_dmbase), `DM`, `DMSubDomainHookAdd()`, `SNESFASGetInterpolation()`, `SNESFASGetInjection()`, `PetscObjectCompose()`, `PetscContainerCreate()`,
3542           `DMCreateDomainDecomposition()`
3543 @*/
3544 PetscErrorCode DMSubDomainHookRemove(DM global, PetscErrorCode (*ddhook)(DM, DM, void *), PetscErrorCode (*restricthook)(DM, VecScatter, VecScatter, DM, void *), void *ctx)
3545 {
3546   DMSubDomainHookLink link, *p;
3547 
3548   PetscFunctionBegin;
3549   PetscValidHeaderSpecific(global, DM_CLASSID, 1);
3550   for (p = &global->subdomainhook; *p; p = &(*p)->next) { /* Search the list of current hooks */
3551     if ((*p)->ddhook == ddhook && (*p)->restricthook == restricthook && (*p)->ctx == ctx) {
3552       link = *p;
3553       *p   = link->next;
3554       PetscCall(PetscFree(link));
3555       break;
3556     }
3557   }
3558   PetscFunctionReturn(PETSC_SUCCESS);
3559 }
3560 
3561 /*@
3562   DMSubDomainRestrict - restricts user-defined problem data to a subdomain `DM` by running hooks registered by `DMSubDomainHookAdd()`
3563 
3564   Collective if any hooks are
3565 
3566   Input Parameters:
3567 + global   - The global `DM` to use as a base
3568 . oscatter - The scatter from domain global vector filling subdomain global vector with overlap
3569 . gscatter - The scatter from domain global vector filling subdomain local vector with ghosts
3570 - subdm    - The subdomain `DM` to update
3571 
3572   Level: developer
3573 
3574 .seealso: [](ch_dmbase), `DM`, `DMCoarsenHookAdd()`, `MatRestrict()`, `DMCreateDomainDecomposition()`
3575 @*/
3576 PetscErrorCode DMSubDomainRestrict(DM global, VecScatter oscatter, VecScatter gscatter, DM subdm)
3577 {
3578   DMSubDomainHookLink link;
3579 
3580   PetscFunctionBegin;
3581   for (link = global->subdomainhook; link; link = link->next) {
3582     if (link->restricthook) PetscCall((*link->restricthook)(global, oscatter, gscatter, subdm, link->ctx));
3583   }
3584   PetscFunctionReturn(PETSC_SUCCESS);
3585 }
3586 
3587 /*@
3588   DMGetCoarsenLevel - Gets the number of coarsenings that have generated this `DM`.
3589 
3590   Not Collective
3591 
3592   Input Parameter:
3593 . dm - the `DM` object
3594 
3595   Output Parameter:
3596 . level - number of coarsenings
3597 
3598   Level: developer
3599 
3600 .seealso: [](ch_dmbase), `DM`, `DMCoarsen()`, `DMSetCoarsenLevel()`, `DMGetRefineLevel()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`
3601 @*/
3602 PetscErrorCode DMGetCoarsenLevel(DM dm, PetscInt *level)
3603 {
3604   PetscFunctionBegin;
3605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3606   PetscAssertPointer(level, 2);
3607   *level = dm->leveldown;
3608   PetscFunctionReturn(PETSC_SUCCESS);
3609 }
3610 
3611 /*@
3612   DMSetCoarsenLevel - Sets the number of coarsenings that have generated this `DM`.
3613 
3614   Collective
3615 
3616   Input Parameters:
3617 + dm    - the `DM` object
3618 - level - number of coarsenings
3619 
3620   Level: developer
3621 
3622   Note:
3623   This is rarely used directly, the information is automatically set when a `DM` is created with `DMCoarsen()`
3624 
3625 .seealso: [](ch_dmbase), `DM`, `DMCoarsen()`, `DMGetCoarsenLevel()`, `DMGetRefineLevel()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`
3626 @*/
3627 PetscErrorCode DMSetCoarsenLevel(DM dm, PetscInt level)
3628 {
3629   PetscFunctionBegin;
3630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3631   dm->leveldown = level;
3632   PetscFunctionReturn(PETSC_SUCCESS);
3633 }
3634 
3635 /*@
3636   DMRefineHierarchy - Refines a `DM` object, all levels at once
3637 
3638   Collective
3639 
3640   Input Parameters:
3641 + dm      - the `DM` object
3642 - nlevels - the number of levels of refinement
3643 
3644   Output Parameter:
3645 . dmf - the refined `DM` hierarchy
3646 
3647   Level: developer
3648 
3649 .seealso: [](ch_dmbase), `DM`, `DMCoarsen()`, `DMCoarsenHierarchy()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`
3650 @*/
3651 PetscErrorCode DMRefineHierarchy(DM dm, PetscInt nlevels, DM dmf[])
3652 {
3653   PetscFunctionBegin;
3654   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3655   PetscCheck(nlevels >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "nlevels cannot be negative");
3656   if (nlevels == 0) PetscFunctionReturn(PETSC_SUCCESS);
3657   PetscAssertPointer(dmf, 3);
3658   if (dm->ops->refine && !dm->ops->refinehierarchy) {
3659     PetscInt i;
3660 
3661     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &dmf[0]));
3662     for (i = 1; i < nlevels; i++) PetscCall(DMRefine(dmf[i - 1], PetscObjectComm((PetscObject)dm), &dmf[i]));
3663   } else PetscUseTypeMethod(dm, refinehierarchy, nlevels, dmf);
3664   PetscFunctionReturn(PETSC_SUCCESS);
3665 }
3666 
3667 /*@
3668   DMCoarsenHierarchy - Coarsens a `DM` object, all levels at once
3669 
3670   Collective
3671 
3672   Input Parameters:
3673 + dm      - the `DM` object
3674 - nlevels - the number of levels of coarsening
3675 
3676   Output Parameter:
3677 . dmc - the coarsened `DM` hierarchy
3678 
3679   Level: developer
3680 
3681 .seealso: [](ch_dmbase), `DM`, `DMCoarsen()`, `DMRefineHierarchy()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`
3682 @*/
3683 PetscErrorCode DMCoarsenHierarchy(DM dm, PetscInt nlevels, DM dmc[])
3684 {
3685   PetscFunctionBegin;
3686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3687   PetscCheck(nlevels >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "nlevels cannot be negative");
3688   if (nlevels == 0) PetscFunctionReturn(PETSC_SUCCESS);
3689   PetscAssertPointer(dmc, 3);
3690   if (dm->ops->coarsen && !dm->ops->coarsenhierarchy) {
3691     PetscInt i;
3692 
3693     PetscCall(DMCoarsen(dm, PetscObjectComm((PetscObject)dm), &dmc[0]));
3694     for (i = 1; i < nlevels; i++) PetscCall(DMCoarsen(dmc[i - 1], PetscObjectComm((PetscObject)dm), &dmc[i]));
3695   } else PetscUseTypeMethod(dm, coarsenhierarchy, nlevels, dmc);
3696   PetscFunctionReturn(PETSC_SUCCESS);
3697 }
3698 
3699 /*@C
3700   DMSetApplicationContextDestroy - Sets a user function that will be called to destroy the application context when the `DM` is destroyed
3701 
3702   Logically Collective if the function is collective
3703 
3704   Input Parameters:
3705 + dm      - the `DM` object
3706 - destroy - the destroy function
3707 
3708   Level: intermediate
3709 
3710 .seealso: [](ch_dmbase), `DM`, `DMSetApplicationContext()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMGetApplicationContext()`
3711 @*/
3712 PetscErrorCode DMSetApplicationContextDestroy(DM dm, PetscErrorCode (*destroy)(void **))
3713 {
3714   PetscFunctionBegin;
3715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3716   dm->ctxdestroy = destroy;
3717   PetscFunctionReturn(PETSC_SUCCESS);
3718 }
3719 
3720 /*@
3721   DMSetApplicationContext - Set a user context into a `DM` object
3722 
3723   Not Collective
3724 
3725   Input Parameters:
3726 + dm  - the `DM` object
3727 - ctx - the user context
3728 
3729   Level: intermediate
3730 
3731   Notes:
3732   A user context is a way to pass problem specific information that is accessible whenever the `DM` is available
3733   In a multilevel solver, the user context is shared by all the `DM` in the hierarchy; it is thus not advisable
3734   to store objects that represent discretized quantities inside the context.
3735 
3736 .seealso: [](ch_dmbase), `DM`, `DMGetApplicationContext()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`
3737 @*/
3738 PetscErrorCode DMSetApplicationContext(DM dm, void *ctx)
3739 {
3740   PetscFunctionBegin;
3741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3742   dm->ctx = ctx;
3743   PetscFunctionReturn(PETSC_SUCCESS);
3744 }
3745 
3746 /*@
3747   DMGetApplicationContext - Gets a user context from a `DM` object
3748 
3749   Not Collective
3750 
3751   Input Parameter:
3752 . dm - the `DM` object
3753 
3754   Output Parameter:
3755 . ctx - the user context
3756 
3757   Level: intermediate
3758 
3759   Note:
3760   A user context is a way to pass problem specific information that is accessible whenever the `DM` is available
3761 
3762 .seealso: [](ch_dmbase), `DM`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`
3763 @*/
3764 PetscErrorCode DMGetApplicationContext(DM dm, void *ctx)
3765 {
3766   PetscFunctionBegin;
3767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3768   *(void **)ctx = dm->ctx;
3769   PetscFunctionReturn(PETSC_SUCCESS);
3770 }
3771 
3772 /*@C
3773   DMSetVariableBounds - sets a function to compute the lower and upper bound vectors for `SNESVI`.
3774 
3775   Logically Collective
3776 
3777   Input Parameters:
3778 + dm - the DM object
3779 - f  - the function that computes variable bounds used by SNESVI (use `NULL` to cancel a previous function that was set)
3780 
3781   Level: intermediate
3782 
3783 .seealso: [](ch_dmbase), `DM`, `DMComputeVariableBounds()`, `DMHasVariableBounds()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMGetApplicationContext()`,
3784          `DMSetJacobian()`
3785 @*/
3786 PetscErrorCode DMSetVariableBounds(DM dm, PetscErrorCode (*f)(DM, Vec, Vec))
3787 {
3788   PetscFunctionBegin;
3789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3790   dm->ops->computevariablebounds = f;
3791   PetscFunctionReturn(PETSC_SUCCESS);
3792 }
3793 
3794 /*@
3795   DMHasVariableBounds - does the `DM` object have a variable bounds function?
3796 
3797   Not Collective
3798 
3799   Input Parameter:
3800 . dm - the `DM` object to destroy
3801 
3802   Output Parameter:
3803 . flg - `PETSC_TRUE` if the variable bounds function exists
3804 
3805   Level: developer
3806 
3807 .seealso: [](ch_dmbase), `DM`, `DMComputeVariableBounds()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMGetApplicationContext()`
3808 @*/
3809 PetscErrorCode DMHasVariableBounds(DM dm, PetscBool *flg)
3810 {
3811   PetscFunctionBegin;
3812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3813   PetscAssertPointer(flg, 2);
3814   *flg = (dm->ops->computevariablebounds) ? PETSC_TRUE : PETSC_FALSE;
3815   PetscFunctionReturn(PETSC_SUCCESS);
3816 }
3817 
3818 /*@
3819   DMComputeVariableBounds - compute variable bounds used by `SNESVI`.
3820 
3821   Logically Collective
3822 
3823   Input Parameter:
3824 . dm - the `DM` object
3825 
3826   Output Parameters:
3827 + xl - lower bound
3828 - xu - upper bound
3829 
3830   Level: advanced
3831 
3832   Note:
3833   This is generally not called by users. It calls the function provided by the user with DMSetVariableBounds()
3834 
3835 .seealso: [](ch_dmbase), `DM`, `DMHasVariableBounds()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMGetApplicationContext()`
3836 @*/
3837 PetscErrorCode DMComputeVariableBounds(DM dm, Vec xl, Vec xu)
3838 {
3839   PetscFunctionBegin;
3840   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3841   PetscValidHeaderSpecific(xl, VEC_CLASSID, 2);
3842   PetscValidHeaderSpecific(xu, VEC_CLASSID, 3);
3843   PetscUseTypeMethod(dm, computevariablebounds, xl, xu);
3844   PetscFunctionReturn(PETSC_SUCCESS);
3845 }
3846 
3847 /*@
3848   DMHasColoring - does the `DM` object have a method of providing a coloring?
3849 
3850   Not Collective
3851 
3852   Input Parameter:
3853 . dm - the DM object
3854 
3855   Output Parameter:
3856 . flg - `PETSC_TRUE` if the `DM` has facilities for `DMCreateColoring()`.
3857 
3858   Level: developer
3859 
3860 .seealso: [](ch_dmbase), `DM`, `DMCreateColoring()`
3861 @*/
3862 PetscErrorCode DMHasColoring(DM dm, PetscBool *flg)
3863 {
3864   PetscFunctionBegin;
3865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3866   PetscAssertPointer(flg, 2);
3867   *flg = (dm->ops->getcoloring) ? PETSC_TRUE : PETSC_FALSE;
3868   PetscFunctionReturn(PETSC_SUCCESS);
3869 }
3870 
3871 /*@
3872   DMHasCreateRestriction - does the `DM` object have a method of providing a restriction?
3873 
3874   Not Collective
3875 
3876   Input Parameter:
3877 . dm - the `DM` object
3878 
3879   Output Parameter:
3880 . flg - `PETSC_TRUE` if the `DM` has facilities for `DMCreateRestriction()`.
3881 
3882   Level: developer
3883 
3884 .seealso: [](ch_dmbase), `DM`, `DMCreateRestriction()`, `DMHasCreateInterpolation()`, `DMHasCreateInjection()`
3885 @*/
3886 PetscErrorCode DMHasCreateRestriction(DM dm, PetscBool *flg)
3887 {
3888   PetscFunctionBegin;
3889   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3890   PetscAssertPointer(flg, 2);
3891   *flg = (dm->ops->createrestriction) ? PETSC_TRUE : PETSC_FALSE;
3892   PetscFunctionReturn(PETSC_SUCCESS);
3893 }
3894 
3895 /*@
3896   DMHasCreateInjection - does the `DM` object have a method of providing an injection?
3897 
3898   Not Collective
3899 
3900   Input Parameter:
3901 . dm - the `DM` object
3902 
3903   Output Parameter:
3904 . flg - `PETSC_TRUE` if the `DM` has facilities for `DMCreateInjection()`.
3905 
3906   Level: developer
3907 
3908 .seealso: [](ch_dmbase), `DM`, `DMCreateInjection()`, `DMHasCreateRestriction()`, `DMHasCreateInterpolation()`
3909 @*/
3910 PetscErrorCode DMHasCreateInjection(DM dm, PetscBool *flg)
3911 {
3912   PetscFunctionBegin;
3913   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3914   PetscAssertPointer(flg, 2);
3915   if (dm->ops->hascreateinjection) PetscUseTypeMethod(dm, hascreateinjection, flg);
3916   else *flg = (dm->ops->createinjection) ? PETSC_TRUE : PETSC_FALSE;
3917   PetscFunctionReturn(PETSC_SUCCESS);
3918 }
3919 
3920 PetscFunctionList DMList              = NULL;
3921 PetscBool         DMRegisterAllCalled = PETSC_FALSE;
3922 
3923 /*@
3924   DMSetType - Builds a `DM`, for a particular `DM` implementation.
3925 
3926   Collective
3927 
3928   Input Parameters:
3929 + dm     - The `DM` object
3930 - method - The name of the `DMType`, for example `DMDA`, `DMPLEX`
3931 
3932   Options Database Key:
3933 . -dm_type <type> - Sets the `DM` type; use -help for a list of available types
3934 
3935   Level: intermediate
3936 
3937   Note:
3938   Of the `DM` is constructed by directly calling a function to construct a particular `DM`, for example, `DMDACreate2d()` or `DMPlexCreateBoxMesh()`
3939 
3940 .seealso: [](ch_dmbase), `DM`, `DMType`, `DMDA`, `DMPLEX`, `DMGetType()`, `DMCreate()`, `DMDACreate2d()`
3941 @*/
3942 PetscErrorCode DMSetType(DM dm, DMType method)
3943 {
3944   PetscErrorCode (*r)(DM);
3945   PetscBool match;
3946 
3947   PetscFunctionBegin;
3948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3949   PetscCall(PetscObjectTypeCompare((PetscObject)dm, method, &match));
3950   if (match) PetscFunctionReturn(PETSC_SUCCESS);
3951 
3952   PetscCall(DMRegisterAll());
3953   PetscCall(PetscFunctionListFind(DMList, method, &r));
3954   PetscCheck(r, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown DM type: %s", method);
3955 
3956   PetscTryTypeMethod(dm, destroy);
3957   PetscCall(PetscMemzero(dm->ops, sizeof(*dm->ops)));
3958   PetscCall(PetscObjectChangeTypeName((PetscObject)dm, method));
3959   PetscCall((*r)(dm));
3960   PetscFunctionReturn(PETSC_SUCCESS);
3961 }
3962 
3963 /*@
3964   DMGetType - Gets the `DM` type name (as a string) from the `DM`.
3965 
3966   Not Collective
3967 
3968   Input Parameter:
3969 . dm - The `DM`
3970 
3971   Output Parameter:
3972 . type - The `DMType` name
3973 
3974   Level: intermediate
3975 
3976 .seealso: [](ch_dmbase), `DM`, `DMType`, `DMDA`, `DMPLEX`, `DMSetType()`, `DMCreate()`
3977 @*/
3978 PetscErrorCode DMGetType(DM dm, DMType *type)
3979 {
3980   PetscFunctionBegin;
3981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3982   PetscAssertPointer(type, 2);
3983   PetscCall(DMRegisterAll());
3984   *type = ((PetscObject)dm)->type_name;
3985   PetscFunctionReturn(PETSC_SUCCESS);
3986 }
3987 
3988 /*@
3989   DMConvert - Converts a `DM` to another `DM`, either of the same or different type.
3990 
3991   Collective
3992 
3993   Input Parameters:
3994 + dm      - the `DM`
3995 - newtype - new `DM` type (use "same" for the same type)
3996 
3997   Output Parameter:
3998 . M - pointer to new `DM`
3999 
4000   Level: intermediate
4001 
4002   Note:
4003   Cannot be used to convert a sequential `DM` to a parallel or a parallel to sequential,
4004   the MPI communicator of the generated `DM` is always the same as the communicator
4005   of the input `DM`.
4006 
4007 .seealso: [](ch_dmbase), `DM`, `DMSetType()`, `DMCreate()`, `DMClone()`
4008 @*/
4009 PetscErrorCode DMConvert(DM dm, DMType newtype, DM *M)
4010 {
4011   DM        B;
4012   char      convname[256];
4013   PetscBool sametype /*, issame */;
4014 
4015   PetscFunctionBegin;
4016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4017   PetscValidType(dm, 1);
4018   PetscAssertPointer(M, 3);
4019   PetscCall(PetscObjectTypeCompare((PetscObject)dm, newtype, &sametype));
4020   /* PetscCall(PetscStrcmp(newtype, "same", &issame)); */
4021   if (sametype) {
4022     *M = dm;
4023     PetscCall(PetscObjectReference((PetscObject)dm));
4024     PetscFunctionReturn(PETSC_SUCCESS);
4025   } else {
4026     PetscErrorCode (*conv)(DM, DMType, DM *) = NULL;
4027 
4028     /*
4029        Order of precedence:
4030        1) See if a specialized converter is known to the current DM.
4031        2) See if a specialized converter is known to the desired DM class.
4032        3) See if a good general converter is registered for the desired class
4033        4) See if a good general converter is known for the current matrix.
4034        5) Use a really basic converter.
4035     */
4036 
4037     /* 1) See if a specialized converter is known to the current DM and the desired class */
4038     PetscCall(PetscStrncpy(convname, "DMConvert_", sizeof(convname)));
4039     PetscCall(PetscStrlcat(convname, ((PetscObject)dm)->type_name, sizeof(convname)));
4040     PetscCall(PetscStrlcat(convname, "_", sizeof(convname)));
4041     PetscCall(PetscStrlcat(convname, newtype, sizeof(convname)));
4042     PetscCall(PetscStrlcat(convname, "_C", sizeof(convname)));
4043     PetscCall(PetscObjectQueryFunction((PetscObject)dm, convname, &conv));
4044     if (conv) goto foundconv;
4045 
4046     /* 2)  See if a specialized converter is known to the desired DM class. */
4047     PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &B));
4048     PetscCall(DMSetType(B, newtype));
4049     PetscCall(PetscStrncpy(convname, "DMConvert_", sizeof(convname)));
4050     PetscCall(PetscStrlcat(convname, ((PetscObject)dm)->type_name, sizeof(convname)));
4051     PetscCall(PetscStrlcat(convname, "_", sizeof(convname)));
4052     PetscCall(PetscStrlcat(convname, newtype, sizeof(convname)));
4053     PetscCall(PetscStrlcat(convname, "_C", sizeof(convname)));
4054     PetscCall(PetscObjectQueryFunction((PetscObject)B, convname, &conv));
4055     if (conv) {
4056       PetscCall(DMDestroy(&B));
4057       goto foundconv;
4058     }
4059 
4060 #if 0
4061     /* 3) See if a good general converter is registered for the desired class */
4062     conv = B->ops->convertfrom;
4063     PetscCall(DMDestroy(&B));
4064     if (conv) goto foundconv;
4065 
4066     /* 4) See if a good general converter is known for the current matrix */
4067     if (dm->ops->convert) {
4068       conv = dm->ops->convert;
4069     }
4070     if (conv) goto foundconv;
4071 #endif
4072 
4073     /* 5) Use a really basic converter. */
4074     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "No conversion possible between DM types %s and %s", ((PetscObject)dm)->type_name, newtype);
4075 
4076   foundconv:
4077     PetscCall(PetscLogEventBegin(DM_Convert, dm, 0, 0, 0));
4078     PetscCall((*conv)(dm, newtype, M));
4079     /* Things that are independent of DM type: We should consult DMClone() here */
4080     {
4081       const PetscReal *maxCell, *Lstart, *L;
4082 
4083       PetscCall(DMGetPeriodicity(dm, &maxCell, &Lstart, &L));
4084       PetscCall(DMSetPeriodicity(*M, maxCell, Lstart, L));
4085       (*M)->prealloc_only = dm->prealloc_only;
4086       PetscCall(PetscFree((*M)->vectype));
4087       PetscCall(PetscStrallocpy(dm->vectype, (char **)&(*M)->vectype));
4088       PetscCall(PetscFree((*M)->mattype));
4089       PetscCall(PetscStrallocpy(dm->mattype, (char **)&(*M)->mattype));
4090     }
4091     PetscCall(PetscLogEventEnd(DM_Convert, dm, 0, 0, 0));
4092   }
4093   PetscCall(PetscObjectStateIncrease((PetscObject)*M));
4094   PetscFunctionReturn(PETSC_SUCCESS);
4095 }
4096 
4097 /*--------------------------------------------------------------------------------------------------------------------*/
4098 
4099 /*@C
4100   DMRegister -  Adds a new `DM` type implementation
4101 
4102   Not Collective, No Fortran Support
4103 
4104   Input Parameters:
4105 + sname    - The name of a new user-defined creation routine
4106 - function - The creation routine itself
4107 
4108   Level: advanced
4109 
4110   Note:
4111   `DMRegister()` may be called multiple times to add several user-defined `DM`s
4112 
4113   Example Usage:
4114 .vb
4115     DMRegister("my_da", MyDMCreate);
4116 .ve
4117 
4118   Then, your `DM` type can be chosen with the procedural interface via
4119 .vb
4120     DMCreate(MPI_Comm, DM *);
4121     DMSetType(DM,"my_da");
4122 .ve
4123   or at runtime via the option
4124 .vb
4125     -da_type my_da
4126 .ve
4127 
4128 .seealso: [](ch_dmbase), `DM`, `DMType`, `DMSetType()`, `DMRegisterAll()`, `DMRegisterDestroy()`
4129 @*/
4130 PetscErrorCode DMRegister(const char sname[], PetscErrorCode (*function)(DM))
4131 {
4132   PetscFunctionBegin;
4133   PetscCall(DMInitializePackage());
4134   PetscCall(PetscFunctionListAdd(&DMList, sname, function));
4135   PetscFunctionReturn(PETSC_SUCCESS);
4136 }
4137 
4138 /*@
4139   DMLoad - Loads a DM that has been stored in binary  with `DMView()`.
4140 
4141   Collective
4142 
4143   Input Parameters:
4144 + newdm  - the newly loaded `DM`, this needs to have been created with `DMCreate()` or
4145            some related function before a call to `DMLoad()`.
4146 - viewer - binary file viewer, obtained from `PetscViewerBinaryOpen()` or
4147            `PETSCVIEWERHDF5` file viewer, obtained from `PetscViewerHDF5Open()`
4148 
4149   Level: intermediate
4150 
4151   Notes:
4152   The type is determined by the data in the file, any type set into the DM before this call is ignored.
4153 
4154   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
4155   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
4156   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
4157 
4158 .seealso: [](ch_dmbase), `DM`, `PetscViewerBinaryOpen()`, `DMView()`, `MatLoad()`, `VecLoad()`
4159 @*/
4160 PetscErrorCode DMLoad(DM newdm, PetscViewer viewer)
4161 {
4162   PetscBool isbinary, ishdf5;
4163 
4164   PetscFunctionBegin;
4165   PetscValidHeaderSpecific(newdm, DM_CLASSID, 1);
4166   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
4167   PetscCall(PetscViewerCheckReadable(viewer));
4168   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERBINARY, &isbinary));
4169   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
4170   PetscCall(PetscLogEventBegin(DM_Load, viewer, 0, 0, 0));
4171   if (isbinary) {
4172     PetscInt classid;
4173     char     type[256];
4174 
4175     PetscCall(PetscViewerBinaryRead(viewer, &classid, 1, NULL, PETSC_INT));
4176     PetscCheck(classid == DM_FILE_CLASSID, PetscObjectComm((PetscObject)newdm), PETSC_ERR_ARG_WRONG, "Not DM next in file, classid found %d", (int)classid);
4177     PetscCall(PetscViewerBinaryRead(viewer, type, 256, NULL, PETSC_CHAR));
4178     PetscCall(DMSetType(newdm, type));
4179     PetscTryTypeMethod(newdm, load, viewer);
4180   } else if (ishdf5) {
4181     PetscTryTypeMethod(newdm, load, viewer);
4182   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid viewer; open viewer with PetscViewerBinaryOpen() or PetscViewerHDF5Open()");
4183   PetscCall(PetscLogEventEnd(DM_Load, viewer, 0, 0, 0));
4184   PetscFunctionReturn(PETSC_SUCCESS);
4185 }
4186 
4187 /* FEM Support */
4188 
4189 PetscErrorCode DMPrintCellIndices(PetscInt c, const char name[], PetscInt len, const PetscInt x[])
4190 {
4191   PetscInt f;
4192 
4193   PetscFunctionBegin;
4194   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4195   for (f = 0; f < len; ++f) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  | %" PetscInt_FMT " |\n", x[f]));
4196   PetscFunctionReturn(PETSC_SUCCESS);
4197 }
4198 
4199 PetscErrorCode DMPrintCellVector(PetscInt c, const char name[], PetscInt len, const PetscScalar x[])
4200 {
4201   PetscInt f;
4202 
4203   PetscFunctionBegin;
4204   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4205   for (f = 0; f < len; ++f) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  | %g |\n", (double)PetscRealPart(x[f])));
4206   PetscFunctionReturn(PETSC_SUCCESS);
4207 }
4208 
4209 PetscErrorCode DMPrintCellVectorReal(PetscInt c, const char name[], PetscInt len, const PetscReal x[])
4210 {
4211   PetscInt f;
4212 
4213   PetscFunctionBegin;
4214   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4215   for (f = 0; f < len; ++f) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  | %g |\n", (double)x[f]));
4216   PetscFunctionReturn(PETSC_SUCCESS);
4217 }
4218 
4219 PetscErrorCode DMPrintCellMatrix(PetscInt c, const char name[], PetscInt rows, PetscInt cols, const PetscScalar A[])
4220 {
4221   PetscInt f, g;
4222 
4223   PetscFunctionBegin;
4224   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4225   for (f = 0; f < rows; ++f) {
4226     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  |"));
4227     for (g = 0; g < cols; ++g) PetscCall(PetscPrintf(PETSC_COMM_SELF, " % 9.5g", (double)PetscRealPart(A[f * cols + g])));
4228     PetscCall(PetscPrintf(PETSC_COMM_SELF, " |\n"));
4229   }
4230   PetscFunctionReturn(PETSC_SUCCESS);
4231 }
4232 
4233 PetscErrorCode DMPrintLocalVec(DM dm, const char name[], PetscReal tol, Vec X)
4234 {
4235   PetscInt           localSize, bs;
4236   PetscMPIInt        size;
4237   Vec                x, xglob;
4238   const PetscScalar *xarray;
4239 
4240   PetscFunctionBegin;
4241   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
4242   PetscCall(VecDuplicate(X, &x));
4243   PetscCall(VecCopy(X, x));
4244   PetscCall(VecFilter(x, tol));
4245   PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "%s:\n", name));
4246   if (size > 1) {
4247     PetscCall(VecGetLocalSize(x, &localSize));
4248     PetscCall(VecGetArrayRead(x, &xarray));
4249     PetscCall(VecGetBlockSize(x, &bs));
4250     PetscCall(VecCreateMPIWithArray(PetscObjectComm((PetscObject)dm), bs, localSize, PETSC_DETERMINE, xarray, &xglob));
4251   } else {
4252     xglob = x;
4253   }
4254   PetscCall(VecView(xglob, PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)dm))));
4255   if (size > 1) {
4256     PetscCall(VecDestroy(&xglob));
4257     PetscCall(VecRestoreArrayRead(x, &xarray));
4258   }
4259   PetscCall(VecDestroy(&x));
4260   PetscFunctionReturn(PETSC_SUCCESS);
4261 }
4262 
4263 /*@
4264   DMGetSection - Get the `PetscSection` encoding the local data layout for the `DM`.   This is equivalent to `DMGetLocalSection()`. Deprecated in v3.12
4265 
4266   Input Parameter:
4267 . dm - The `DM`
4268 
4269   Output Parameter:
4270 . section - The `PetscSection`
4271 
4272   Options Database Key:
4273 . -dm_petscsection_view - View the `PetscSection` created by the `DM`
4274 
4275   Level: advanced
4276 
4277   Notes:
4278   Use `DMGetLocalSection()` in new code.
4279 
4280   This gets a borrowed reference, so the user should not destroy this `PetscSection`.
4281 
4282 .seealso: [](ch_dmbase), `DM`, `DMGetLocalSection()`, `DMSetLocalSection()`, `DMGetGlobalSection()`
4283 @*/
4284 PetscErrorCode DMGetSection(DM dm, PetscSection *section)
4285 {
4286   PetscFunctionBegin;
4287   PetscCall(DMGetLocalSection(dm, section));
4288   PetscFunctionReturn(PETSC_SUCCESS);
4289 }
4290 
4291 /*@
4292   DMGetLocalSection - Get the `PetscSection` encoding the local data layout for the `DM`.
4293 
4294   Input Parameter:
4295 . dm - The `DM`
4296 
4297   Output Parameter:
4298 . section - The `PetscSection`
4299 
4300   Options Database Key:
4301 . -dm_petscsection_view - View the section created by the `DM`
4302 
4303   Level: intermediate
4304 
4305   Note:
4306   This gets a borrowed reference, so the user should not destroy this `PetscSection`.
4307 
4308 .seealso: [](ch_dmbase), `DM`, `DMSetLocalSection()`, `DMGetGlobalSection()`
4309 @*/
4310 PetscErrorCode DMGetLocalSection(DM dm, PetscSection *section)
4311 {
4312   PetscFunctionBegin;
4313   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4314   PetscAssertPointer(section, 2);
4315   if (!dm->localSection && dm->ops->createlocalsection) {
4316     PetscInt d;
4317 
4318     if (dm->setfromoptionscalled) {
4319       PetscObject       obj = (PetscObject)dm;
4320       PetscViewer       viewer;
4321       PetscViewerFormat format;
4322       PetscBool         flg;
4323 
4324       PetscCall(PetscOptionsCreateViewer(PetscObjectComm(obj), obj->options, obj->prefix, "-dm_petscds_view", &viewer, &format, &flg));
4325       if (flg) PetscCall(PetscViewerPushFormat(viewer, format));
4326       for (d = 0; d < dm->Nds; ++d) {
4327         PetscCall(PetscDSSetFromOptions(dm->probs[d].ds));
4328         if (flg) PetscCall(PetscDSView(dm->probs[d].ds, viewer));
4329       }
4330       if (flg) {
4331         PetscCall(PetscViewerFlush(viewer));
4332         PetscCall(PetscViewerPopFormat(viewer));
4333         PetscCall(PetscViewerDestroy(&viewer));
4334       }
4335     }
4336     PetscUseTypeMethod(dm, createlocalsection);
4337     if (dm->localSection) PetscCall(PetscObjectViewFromOptions((PetscObject)dm->localSection, NULL, "-dm_petscsection_view"));
4338   }
4339   *section = dm->localSection;
4340   PetscFunctionReturn(PETSC_SUCCESS);
4341 }
4342 
4343 /*@
4344   DMSetSection - Set the `PetscSection` encoding the local data layout for the `DM`.  This is equivalent to `DMSetLocalSection()`. Deprecated in v3.12
4345 
4346   Input Parameters:
4347 + dm      - The `DM`
4348 - section - The `PetscSection`
4349 
4350   Level: advanced
4351 
4352   Notes:
4353   Use `DMSetLocalSection()` in new code.
4354 
4355   Any existing `PetscSection` will be destroyed
4356 
4357 .seealso: [](ch_dmbase), `DM`, `DMSetLocalSection()`, `DMGetLocalSection()`, `DMSetGlobalSection()`
4358 @*/
4359 PetscErrorCode DMSetSection(DM dm, PetscSection section)
4360 {
4361   PetscFunctionBegin;
4362   PetscCall(DMSetLocalSection(dm, section));
4363   PetscFunctionReturn(PETSC_SUCCESS);
4364 }
4365 
4366 /*@
4367   DMSetLocalSection - Set the `PetscSection` encoding the local data layout for the `DM`.
4368 
4369   Input Parameters:
4370 + dm      - The `DM`
4371 - section - The `PetscSection`
4372 
4373   Level: intermediate
4374 
4375   Note:
4376   Any existing Section will be destroyed
4377 
4378 .seealso: [](ch_dmbase), `DM`, `PetscSection`, `DMGetLocalSection()`, `DMSetGlobalSection()`
4379 @*/
4380 PetscErrorCode DMSetLocalSection(DM dm, PetscSection section)
4381 {
4382   PetscInt numFields = 0;
4383   PetscInt f;
4384 
4385   PetscFunctionBegin;
4386   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4387   if (section) PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
4388   PetscCall(PetscObjectReference((PetscObject)section));
4389   PetscCall(PetscSectionDestroy(&dm->localSection));
4390   dm->localSection = section;
4391   if (section) PetscCall(PetscSectionGetNumFields(dm->localSection, &numFields));
4392   if (numFields) {
4393     PetscCall(DMSetNumFields(dm, numFields));
4394     for (f = 0; f < numFields; ++f) {
4395       PetscObject disc;
4396       const char *name;
4397 
4398       PetscCall(PetscSectionGetFieldName(dm->localSection, f, &name));
4399       PetscCall(DMGetField(dm, f, NULL, &disc));
4400       PetscCall(PetscObjectSetName(disc, name));
4401     }
4402   }
4403   /* The global section and the SectionSF will be rebuilt
4404      in the next call to DMGetGlobalSection() and DMGetSectionSF(). */
4405   PetscCall(PetscSectionDestroy(&dm->globalSection));
4406   PetscCall(PetscSFDestroy(&dm->sectionSF));
4407   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &dm->sectionSF));
4408 
4409   /* Clear scratch vectors */
4410   PetscCall(DMClearGlobalVectors(dm));
4411   PetscCall(DMClearLocalVectors(dm));
4412   PetscCall(DMClearNamedGlobalVectors(dm));
4413   PetscCall(DMClearNamedLocalVectors(dm));
4414   PetscFunctionReturn(PETSC_SUCCESS);
4415 }
4416 
4417 /*@C
4418   DMCreateSectionPermutation - Create a permutation of the `PetscSection` chart and optionally a block structure.
4419 
4420   Input Parameter:
4421 . dm - The `DM`
4422 
4423   Output Parameters:
4424 + perm        - A permutation of the mesh points in the chart
4425 - blockStarts - A high bit is set for the point that begins every block, or `NULL` for default blocking
4426 
4427   Level: developer
4428 
4429 .seealso: [](ch_dmbase), `DM`, `PetscSection`, `DMGetLocalSection()`, `DMGetGlobalSection()`
4430 @*/
4431 PetscErrorCode DMCreateSectionPermutation(DM dm, IS *perm, PetscBT *blockStarts)
4432 {
4433   PetscFunctionBegin;
4434   *perm        = NULL;
4435   *blockStarts = NULL;
4436   PetscTryTypeMethod(dm, createsectionpermutation, perm, blockStarts);
4437   PetscFunctionReturn(PETSC_SUCCESS);
4438 }
4439 
4440 /*@
4441   DMGetDefaultConstraints - Get the `PetscSection` and `Mat` that specify the local constraint interpolation. See `DMSetDefaultConstraints()` for a description of the purpose of constraint interpolation.
4442 
4443   not Collective
4444 
4445   Input Parameter:
4446 . dm - The `DM`
4447 
4448   Output Parameters:
4449 + section - The `PetscSection` describing the range of the constraint matrix: relates rows of the constraint matrix to dofs of the default section.  Returns `NULL` if there are no local constraints.
4450 . mat     - The `Mat` that interpolates local constraints: its width should be the layout size of the default section.  Returns `NULL` if there are no local constraints.
4451 - bias    - Vector containing bias to be added to constrained dofs
4452 
4453   Level: advanced
4454 
4455   Note:
4456   This gets borrowed references, so the user should not destroy the `PetscSection`, `Mat`, or `Vec`.
4457 
4458 .seealso: [](ch_dmbase), `DM`, `DMSetDefaultConstraints()`
4459 @*/
4460 PetscErrorCode DMGetDefaultConstraints(DM dm, PetscSection *section, Mat *mat, Vec *bias)
4461 {
4462   PetscFunctionBegin;
4463   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4464   if (!dm->defaultConstraint.section && !dm->defaultConstraint.mat && dm->ops->createdefaultconstraints) PetscUseTypeMethod(dm, createdefaultconstraints);
4465   if (section) *section = dm->defaultConstraint.section;
4466   if (mat) *mat = dm->defaultConstraint.mat;
4467   if (bias) *bias = dm->defaultConstraint.bias;
4468   PetscFunctionReturn(PETSC_SUCCESS);
4469 }
4470 
4471 /*@
4472   DMSetDefaultConstraints - Set the `PetscSection` and `Mat` that specify the local constraint interpolation.
4473 
4474   Collective
4475 
4476   Input Parameters:
4477 + dm      - The `DM`
4478 . section - The `PetscSection` describing the range of the constraint matrix: relates rows of the constraint matrix to dofs of the default section.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
4479 . mat     - The `Mat` that interpolates local constraints: its width should be the layout size of the default section:  `NULL` indicates no constraints.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
4480 - bias    - A bias vector to be added to constrained values in the local vector.  `NULL` indicates no bias.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
4481 
4482   Level: advanced
4483 
4484   Notes:
4485   If a constraint matrix is specified, then it is applied during `DMGlobalToLocalEnd()` when mode is `INSERT_VALUES`, `INSERT_BC_VALUES`, or `INSERT_ALL_VALUES`.  Without a constraint matrix, the local vector l returned by `DMGlobalToLocalEnd()` contains values that have been scattered from a global vector without modification; with a constraint matrix A, l is modified by computing c = A * l + bias, l[s[i]] = c[i], where the scatter s is defined by the `PetscSection` returned by `DMGetDefaultConstraints()`.
4486 
4487   If a constraint matrix is specified, then its adjoint is applied during `DMLocalToGlobalBegin()` when mode is `ADD_VALUES`, `ADD_BC_VALUES`, or `ADD_ALL_VALUES`.  Without a constraint matrix, the local vector l is accumulated into a global vector without modification; with a constraint matrix A, l is first modified by computing c[i] = l[s[i]], l[s[i]] = 0, l = l + A'*c, which is the adjoint of the operation described above.  Any bias, if specified, is ignored when accumulating.
4488 
4489   This increments the references of the `PetscSection`, `Mat`, and `Vec`, so they user can destroy them.
4490 
4491 .seealso: [](ch_dmbase), `DM`, `DMGetDefaultConstraints()`
4492 @*/
4493 PetscErrorCode DMSetDefaultConstraints(DM dm, PetscSection section, Mat mat, Vec bias)
4494 {
4495   PetscMPIInt result;
4496 
4497   PetscFunctionBegin;
4498   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4499   if (section) {
4500     PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
4501     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)section), &result));
4502     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "constraint section must have local communicator");
4503   }
4504   if (mat) {
4505     PetscValidHeaderSpecific(mat, MAT_CLASSID, 3);
4506     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)mat), &result));
4507     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "constraint matrix must have local communicator");
4508   }
4509   if (bias) {
4510     PetscValidHeaderSpecific(bias, VEC_CLASSID, 4);
4511     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)bias), &result));
4512     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "constraint bias must have local communicator");
4513   }
4514   PetscCall(PetscObjectReference((PetscObject)section));
4515   PetscCall(PetscSectionDestroy(&dm->defaultConstraint.section));
4516   dm->defaultConstraint.section = section;
4517   PetscCall(PetscObjectReference((PetscObject)mat));
4518   PetscCall(MatDestroy(&dm->defaultConstraint.mat));
4519   dm->defaultConstraint.mat = mat;
4520   PetscCall(PetscObjectReference((PetscObject)bias));
4521   PetscCall(VecDestroy(&dm->defaultConstraint.bias));
4522   dm->defaultConstraint.bias = bias;
4523   PetscFunctionReturn(PETSC_SUCCESS);
4524 }
4525 
4526 #if defined(PETSC_USE_DEBUG)
4527 /*
4528   DMDefaultSectionCheckConsistency - Check the consistentcy of the global and local sections. Generates and error if they are not consistent.
4529 
4530   Input Parameters:
4531 + dm - The `DM`
4532 . localSection - `PetscSection` describing the local data layout
4533 - globalSection - `PetscSection` describing the global data layout
4534 
4535   Level: intermediate
4536 
4537 .seealso: [](ch_dmbase), `DM`, `DMGetSectionSF()`, `DMSetSectionSF()`
4538 */
4539 static PetscErrorCode DMDefaultSectionCheckConsistency_Internal(DM dm, PetscSection localSection, PetscSection globalSection)
4540 {
4541   MPI_Comm        comm;
4542   PetscLayout     layout;
4543   const PetscInt *ranges;
4544   PetscInt        pStart, pEnd, p, nroots;
4545   PetscMPIInt     size, rank;
4546   PetscBool       valid = PETSC_TRUE, gvalid;
4547 
4548   PetscFunctionBegin;
4549   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4550   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4551   PetscCallMPI(MPI_Comm_size(comm, &size));
4552   PetscCallMPI(MPI_Comm_rank(comm, &rank));
4553   PetscCall(PetscSectionGetChart(globalSection, &pStart, &pEnd));
4554   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &nroots));
4555   PetscCall(PetscLayoutCreate(comm, &layout));
4556   PetscCall(PetscLayoutSetBlockSize(layout, 1));
4557   PetscCall(PetscLayoutSetLocalSize(layout, nroots));
4558   PetscCall(PetscLayoutSetUp(layout));
4559   PetscCall(PetscLayoutGetRanges(layout, &ranges));
4560   for (p = pStart; p < pEnd; ++p) {
4561     PetscInt dof, cdof, off, gdof, gcdof, goff, gsize, d;
4562 
4563     PetscCall(PetscSectionGetDof(localSection, p, &dof));
4564     PetscCall(PetscSectionGetOffset(localSection, p, &off));
4565     PetscCall(PetscSectionGetConstraintDof(localSection, p, &cdof));
4566     PetscCall(PetscSectionGetDof(globalSection, p, &gdof));
4567     PetscCall(PetscSectionGetConstraintDof(globalSection, p, &gcdof));
4568     PetscCall(PetscSectionGetOffset(globalSection, p, &goff));
4569     if (!gdof) continue; /* Censored point */
4570     if ((gdof < 0 ? -(gdof + 1) : gdof) != dof) {
4571       PetscCall(PetscSynchronizedPrintf(comm, "[%d]Global dof %" PetscInt_FMT " for point %" PetscInt_FMT " not equal to local dof %" PetscInt_FMT "\n", rank, gdof, p, dof));
4572       valid = PETSC_FALSE;
4573     }
4574     if (gcdof && (gcdof != cdof)) {
4575       PetscCall(PetscSynchronizedPrintf(comm, "[%d]Global constraints %" PetscInt_FMT " for point %" PetscInt_FMT " not equal to local constraints %" PetscInt_FMT "\n", rank, gcdof, p, cdof));
4576       valid = PETSC_FALSE;
4577     }
4578     if (gdof < 0) {
4579       gsize = gdof < 0 ? -(gdof + 1) - gcdof : gdof - gcdof;
4580       for (d = 0; d < gsize; ++d) {
4581         PetscInt offset = -(goff + 1) + d, r;
4582 
4583         PetscCall(PetscFindInt(offset, size + 1, ranges, &r));
4584         if (r < 0) r = -(r + 2);
4585         if ((r < 0) || (r >= size)) {
4586           PetscCall(PetscSynchronizedPrintf(comm, "[%d]Point %" PetscInt_FMT " mapped to invalid process %" PetscInt_FMT " (%" PetscInt_FMT ", %" PetscInt_FMT ")\n", rank, p, r, gdof, goff));
4587           valid = PETSC_FALSE;
4588           break;
4589         }
4590       }
4591     }
4592   }
4593   PetscCall(PetscLayoutDestroy(&layout));
4594   PetscCall(PetscSynchronizedFlush(comm, NULL));
4595   PetscCallMPI(MPIU_Allreduce(&valid, &gvalid, 1, MPIU_BOOL, MPI_LAND, comm));
4596   if (!gvalid) {
4597     PetscCall(DMView(dm, NULL));
4598     SETERRQ(comm, PETSC_ERR_ARG_WRONG, "Inconsistent local and global sections");
4599   }
4600   PetscFunctionReturn(PETSC_SUCCESS);
4601 }
4602 #endif
4603 
4604 static PetscErrorCode DMGetIsoperiodicPointSF_Internal(DM dm, PetscSF *sf)
4605 {
4606   PetscErrorCode (*f)(DM, PetscSF *);
4607 
4608   PetscFunctionBegin;
4609   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4610   PetscAssertPointer(sf, 2);
4611   PetscCall(PetscObjectQueryFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", &f));
4612   if (f) PetscCall(f(dm, sf));
4613   else *sf = dm->sf;
4614   PetscFunctionReturn(PETSC_SUCCESS);
4615 }
4616 
4617 /*@
4618   DMGetGlobalSection - Get the `PetscSection` encoding the global data layout for the `DM`.
4619 
4620   Collective
4621 
4622   Input Parameter:
4623 . dm - The `DM`
4624 
4625   Output Parameter:
4626 . section - The `PetscSection`
4627 
4628   Level: intermediate
4629 
4630   Note:
4631   This gets a borrowed reference, so the user should not destroy this `PetscSection`.
4632 
4633 .seealso: [](ch_dmbase), `DM`, `DMSetLocalSection()`, `DMGetLocalSection()`
4634 @*/
4635 PetscErrorCode DMGetGlobalSection(DM dm, PetscSection *section)
4636 {
4637   PetscFunctionBegin;
4638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4639   PetscAssertPointer(section, 2);
4640   if (!dm->globalSection) {
4641     PetscSection s;
4642     PetscSF      sf;
4643 
4644     PetscCall(DMGetLocalSection(dm, &s));
4645     PetscCheck(s, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "DM must have a default PetscSection in order to create a global PetscSection");
4646     PetscCheck(dm->sf, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "DM must have a point PetscSF in order to create a global PetscSection");
4647     PetscCall(DMGetIsoperiodicPointSF_Internal(dm, &sf));
4648     PetscCall(PetscSectionCreateGlobalSection(s, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &dm->globalSection));
4649     PetscCall(PetscLayoutDestroy(&dm->map));
4650     PetscCall(PetscSectionGetValueLayout(PetscObjectComm((PetscObject)dm), dm->globalSection, &dm->map));
4651     PetscCall(PetscSectionViewFromOptions(dm->globalSection, NULL, "-global_section_view"));
4652   }
4653   *section = dm->globalSection;
4654   PetscFunctionReturn(PETSC_SUCCESS);
4655 }
4656 
4657 /*@
4658   DMSetGlobalSection - Set the `PetscSection` encoding the global data layout for the `DM`.
4659 
4660   Input Parameters:
4661 + dm      - The `DM`
4662 - section - The PetscSection, or `NULL`
4663 
4664   Level: intermediate
4665 
4666   Note:
4667   Any existing `PetscSection` will be destroyed
4668 
4669 .seealso: [](ch_dmbase), `DM`, `DMGetGlobalSection()`, `DMSetLocalSection()`
4670 @*/
4671 PetscErrorCode DMSetGlobalSection(DM dm, PetscSection section)
4672 {
4673   PetscFunctionBegin;
4674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4675   if (section) PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
4676   PetscCall(PetscObjectReference((PetscObject)section));
4677   PetscCall(PetscSectionDestroy(&dm->globalSection));
4678   dm->globalSection = section;
4679 #if defined(PETSC_USE_DEBUG)
4680   if (section) PetscCall(DMDefaultSectionCheckConsistency_Internal(dm, dm->localSection, section));
4681 #endif
4682   /* Clear global scratch vectors and sectionSF */
4683   PetscCall(PetscSFDestroy(&dm->sectionSF));
4684   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &dm->sectionSF));
4685   PetscCall(DMClearGlobalVectors(dm));
4686   PetscCall(DMClearNamedGlobalVectors(dm));
4687   PetscFunctionReturn(PETSC_SUCCESS);
4688 }
4689 
4690 /*@
4691   DMGetSectionSF - Get the `PetscSF` encoding the parallel dof overlap for the `DM`. If it has not been set,
4692   it is created from the default `PetscSection` layouts in the `DM`.
4693 
4694   Input Parameter:
4695 . dm - The `DM`
4696 
4697   Output Parameter:
4698 . sf - The `PetscSF`
4699 
4700   Level: intermediate
4701 
4702   Note:
4703   This gets a borrowed reference, so the user should not destroy this `PetscSF`.
4704 
4705 .seealso: [](ch_dmbase), `DM`, `DMSetSectionSF()`, `DMCreateSectionSF()`
4706 @*/
4707 PetscErrorCode DMGetSectionSF(DM dm, PetscSF *sf)
4708 {
4709   PetscInt nroots;
4710 
4711   PetscFunctionBegin;
4712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4713   PetscAssertPointer(sf, 2);
4714   if (!dm->sectionSF) PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &dm->sectionSF));
4715   PetscCall(PetscSFGetGraph(dm->sectionSF, &nroots, NULL, NULL, NULL));
4716   if (nroots < 0) {
4717     PetscSection section, gSection;
4718 
4719     PetscCall(DMGetLocalSection(dm, &section));
4720     if (section) {
4721       PetscCall(DMGetGlobalSection(dm, &gSection));
4722       PetscCall(DMCreateSectionSF(dm, section, gSection));
4723     } else {
4724       *sf = NULL;
4725       PetscFunctionReturn(PETSC_SUCCESS);
4726     }
4727   }
4728   *sf = dm->sectionSF;
4729   PetscFunctionReturn(PETSC_SUCCESS);
4730 }
4731 
4732 /*@
4733   DMSetSectionSF - Set the `PetscSF` encoding the parallel dof overlap for the `DM`
4734 
4735   Input Parameters:
4736 + dm - The `DM`
4737 - sf - The `PetscSF`
4738 
4739   Level: intermediate
4740 
4741   Note:
4742   Any previous `PetscSF` is destroyed
4743 
4744 .seealso: [](ch_dmbase), `DM`, `DMGetSectionSF()`, `DMCreateSectionSF()`
4745 @*/
4746 PetscErrorCode DMSetSectionSF(DM dm, PetscSF sf)
4747 {
4748   PetscFunctionBegin;
4749   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4750   if (sf) PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
4751   PetscCall(PetscObjectReference((PetscObject)sf));
4752   PetscCall(PetscSFDestroy(&dm->sectionSF));
4753   dm->sectionSF = sf;
4754   PetscFunctionReturn(PETSC_SUCCESS);
4755 }
4756 
4757 /*@
4758   DMCreateSectionSF - Create the `PetscSF` encoding the parallel dof overlap for the `DM` based upon the `PetscSection`s
4759   describing the data layout.
4760 
4761   Input Parameters:
4762 + dm            - The `DM`
4763 . localSection  - `PetscSection` describing the local data layout
4764 - globalSection - `PetscSection` describing the global data layout
4765 
4766   Level: developer
4767 
4768   Note:
4769   One usually uses `DMGetSectionSF()` to obtain the `PetscSF`
4770 
4771   Developer Note:
4772   Since this routine has for arguments the two sections from the `DM` and puts the resulting `PetscSF`
4773   directly into the `DM`, perhaps this function should not take the local and global sections as
4774   input and should just obtain them from the `DM`? Plus PETSc creation functions return the thing
4775   they create, this returns nothing
4776 
4777 .seealso: [](ch_dmbase), `DM`, `DMGetSectionSF()`, `DMSetSectionSF()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
4778 @*/
4779 PetscErrorCode DMCreateSectionSF(DM dm, PetscSection localSection, PetscSection globalSection)
4780 {
4781   PetscFunctionBegin;
4782   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4783   PetscCall(PetscSFSetGraphSection(dm->sectionSF, localSection, globalSection));
4784   PetscFunctionReturn(PETSC_SUCCESS);
4785 }
4786 
4787 /*@
4788   DMGetPointSF - Get the `PetscSF` encoding the parallel section point overlap for the `DM`.
4789 
4790   Not collective but the resulting `PetscSF` is collective
4791 
4792   Input Parameter:
4793 . dm - The `DM`
4794 
4795   Output Parameter:
4796 . sf - The `PetscSF`
4797 
4798   Level: intermediate
4799 
4800   Note:
4801   This gets a borrowed reference, so the user should not destroy this `PetscSF`.
4802 
4803 .seealso: [](ch_dmbase), `DM`, `DMSetPointSF()`, `DMGetSectionSF()`, `DMSetSectionSF()`, `DMCreateSectionSF()`
4804 @*/
4805 PetscErrorCode DMGetPointSF(DM dm, PetscSF *sf)
4806 {
4807   PetscFunctionBegin;
4808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4809   PetscAssertPointer(sf, 2);
4810   *sf = dm->sf;
4811   PetscFunctionReturn(PETSC_SUCCESS);
4812 }
4813 
4814 /*@
4815   DMSetPointSF - Set the `PetscSF` encoding the parallel section point overlap for the `DM`.
4816 
4817   Collective
4818 
4819   Input Parameters:
4820 + dm - The `DM`
4821 - sf - The `PetscSF`
4822 
4823   Level: intermediate
4824 
4825 .seealso: [](ch_dmbase), `DM`, `DMGetPointSF()`, `DMGetSectionSF()`, `DMSetSectionSF()`, `DMCreateSectionSF()`
4826 @*/
4827 PetscErrorCode DMSetPointSF(DM dm, PetscSF sf)
4828 {
4829   PetscFunctionBegin;
4830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4831   if (sf) PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
4832   PetscCall(PetscObjectReference((PetscObject)sf));
4833   PetscCall(PetscSFDestroy(&dm->sf));
4834   dm->sf = sf;
4835   PetscFunctionReturn(PETSC_SUCCESS);
4836 }
4837 
4838 /*@
4839   DMGetNaturalSF - Get the `PetscSF` encoding the map back to the original mesh ordering
4840 
4841   Input Parameter:
4842 . dm - The `DM`
4843 
4844   Output Parameter:
4845 . sf - The `PetscSF`
4846 
4847   Level: intermediate
4848 
4849   Note:
4850   This gets a borrowed reference, so the user should not destroy this `PetscSF`.
4851 
4852 .seealso: [](ch_dmbase), `DM`, `DMSetNaturalSF()`, `DMSetUseNatural()`, `DMGetUseNatural()`, `DMPlexCreateGlobalToNaturalSF()`, `DMPlexDistribute()`
4853 @*/
4854 PetscErrorCode DMGetNaturalSF(DM dm, PetscSF *sf)
4855 {
4856   PetscFunctionBegin;
4857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4858   PetscAssertPointer(sf, 2);
4859   *sf = dm->sfNatural;
4860   PetscFunctionReturn(PETSC_SUCCESS);
4861 }
4862 
4863 /*@
4864   DMSetNaturalSF - Set the PetscSF encoding the map back to the original mesh ordering
4865 
4866   Input Parameters:
4867 + dm - The DM
4868 - sf - The PetscSF
4869 
4870   Level: intermediate
4871 
4872 .seealso: [](ch_dmbase), `DM`, `DMGetNaturalSF()`, `DMSetUseNatural()`, `DMGetUseNatural()`, `DMPlexCreateGlobalToNaturalSF()`, `DMPlexDistribute()`
4873 @*/
4874 PetscErrorCode DMSetNaturalSF(DM dm, PetscSF sf)
4875 {
4876   PetscFunctionBegin;
4877   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4878   if (sf) PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
4879   PetscCall(PetscObjectReference((PetscObject)sf));
4880   PetscCall(PetscSFDestroy(&dm->sfNatural));
4881   dm->sfNatural = sf;
4882   PetscFunctionReturn(PETSC_SUCCESS);
4883 }
4884 
4885 static PetscErrorCode DMSetDefaultAdjacency_Private(DM dm, PetscInt f, PetscObject disc)
4886 {
4887   PetscClassId id;
4888 
4889   PetscFunctionBegin;
4890   PetscCall(PetscObjectGetClassId(disc, &id));
4891   if (id == PETSCFE_CLASSID) {
4892     PetscCall(DMSetAdjacency(dm, f, PETSC_FALSE, PETSC_TRUE));
4893   } else if (id == PETSCFV_CLASSID) {
4894     PetscCall(DMSetAdjacency(dm, f, PETSC_TRUE, PETSC_FALSE));
4895   } else {
4896     PetscCall(DMSetAdjacency(dm, f, PETSC_FALSE, PETSC_TRUE));
4897   }
4898   PetscFunctionReturn(PETSC_SUCCESS);
4899 }
4900 
4901 static PetscErrorCode DMFieldEnlarge_Static(DM dm, PetscInt NfNew)
4902 {
4903   RegionField *tmpr;
4904   PetscInt     Nf = dm->Nf, f;
4905 
4906   PetscFunctionBegin;
4907   if (Nf >= NfNew) PetscFunctionReturn(PETSC_SUCCESS);
4908   PetscCall(PetscMalloc1(NfNew, &tmpr));
4909   for (f = 0; f < Nf; ++f) tmpr[f] = dm->fields[f];
4910   for (f = Nf; f < NfNew; ++f) {
4911     tmpr[f].disc        = NULL;
4912     tmpr[f].label       = NULL;
4913     tmpr[f].avoidTensor = PETSC_FALSE;
4914   }
4915   PetscCall(PetscFree(dm->fields));
4916   dm->Nf     = NfNew;
4917   dm->fields = tmpr;
4918   PetscFunctionReturn(PETSC_SUCCESS);
4919 }
4920 
4921 /*@
4922   DMClearFields - Remove all fields from the `DM`
4923 
4924   Logically Collective
4925 
4926   Input Parameter:
4927 . dm - The `DM`
4928 
4929   Level: intermediate
4930 
4931 .seealso: [](ch_dmbase), `DM`, `DMGetNumFields()`, `DMSetNumFields()`, `DMSetField()`
4932 @*/
4933 PetscErrorCode DMClearFields(DM dm)
4934 {
4935   PetscInt f;
4936 
4937   PetscFunctionBegin;
4938   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4939   for (f = 0; f < dm->Nf; ++f) {
4940     PetscCall(PetscObjectDestroy(&dm->fields[f].disc));
4941     PetscCall(DMLabelDestroy(&dm->fields[f].label));
4942   }
4943   PetscCall(PetscFree(dm->fields));
4944   dm->fields = NULL;
4945   dm->Nf     = 0;
4946   PetscFunctionReturn(PETSC_SUCCESS);
4947 }
4948 
4949 /*@
4950   DMGetNumFields - Get the number of fields in the `DM`
4951 
4952   Not Collective
4953 
4954   Input Parameter:
4955 . dm - The `DM`
4956 
4957   Output Parameter:
4958 . numFields - The number of fields
4959 
4960   Level: intermediate
4961 
4962 .seealso: [](ch_dmbase), `DM`, `DMSetNumFields()`, `DMSetField()`
4963 @*/
4964 PetscErrorCode DMGetNumFields(DM dm, PetscInt *numFields)
4965 {
4966   PetscFunctionBegin;
4967   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4968   PetscAssertPointer(numFields, 2);
4969   *numFields = dm->Nf;
4970   PetscFunctionReturn(PETSC_SUCCESS);
4971 }
4972 
4973 /*@
4974   DMSetNumFields - Set the number of fields in the `DM`
4975 
4976   Logically Collective
4977 
4978   Input Parameters:
4979 + dm        - The `DM`
4980 - numFields - The number of fields
4981 
4982   Level: intermediate
4983 
4984 .seealso: [](ch_dmbase), `DM`, `DMGetNumFields()`, `DMSetField()`
4985 @*/
4986 PetscErrorCode DMSetNumFields(DM dm, PetscInt numFields)
4987 {
4988   PetscInt Nf, f;
4989 
4990   PetscFunctionBegin;
4991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4992   PetscCall(DMGetNumFields(dm, &Nf));
4993   for (f = Nf; f < numFields; ++f) {
4994     PetscContainer obj;
4995 
4996     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)dm), &obj));
4997     PetscCall(DMAddField(dm, NULL, (PetscObject)obj));
4998     PetscCall(PetscContainerDestroy(&obj));
4999   }
5000   PetscFunctionReturn(PETSC_SUCCESS);
5001 }
5002 
5003 /*@
5004   DMGetField - Return the `DMLabel` and discretization object for a given `DM` field
5005 
5006   Not Collective
5007 
5008   Input Parameters:
5009 + dm - The `DM`
5010 - f  - The field number
5011 
5012   Output Parameters:
5013 + label - The label indicating the support of the field, or `NULL` for the entire mesh (pass in `NULL` if not needed)
5014 - disc  - The discretization object (pass in `NULL` if not needed)
5015 
5016   Level: intermediate
5017 
5018 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMSetField()`
5019 @*/
5020 PetscErrorCode DMGetField(DM dm, PetscInt f, DMLabel *label, PetscObject *disc)
5021 {
5022   PetscFunctionBegin;
5023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5024   PetscAssertPointer(disc, 4);
5025   PetscCheck((f >= 0) && (f < dm->Nf), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field number %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", f, dm->Nf);
5026   if (label) *label = dm->fields[f].label;
5027   if (disc) *disc = dm->fields[f].disc;
5028   PetscFunctionReturn(PETSC_SUCCESS);
5029 }
5030 
5031 /* Does not clear the DS */
5032 PetscErrorCode DMSetField_Internal(DM dm, PetscInt f, DMLabel label, PetscObject disc)
5033 {
5034   PetscFunctionBegin;
5035   PetscCall(DMFieldEnlarge_Static(dm, f + 1));
5036   PetscCall(DMLabelDestroy(&dm->fields[f].label));
5037   PetscCall(PetscObjectDestroy(&dm->fields[f].disc));
5038   dm->fields[f].label = label;
5039   dm->fields[f].disc  = disc;
5040   PetscCall(PetscObjectReference((PetscObject)label));
5041   PetscCall(PetscObjectReference((PetscObject)disc));
5042   PetscFunctionReturn(PETSC_SUCCESS);
5043 }
5044 
5045 /*@
5046   DMSetField - Set the discretization object for a given `DM` field. Usually one would call `DMAddField()` which automatically handles
5047   the field numbering.
5048 
5049   Logically Collective
5050 
5051   Input Parameters:
5052 + dm    - The `DM`
5053 . f     - The field number
5054 . label - The label indicating the support of the field, or `NULL` for the entire mesh
5055 - disc  - The discretization object
5056 
5057   Level: intermediate
5058 
5059 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMGetField()`
5060 @*/
5061 PetscErrorCode DMSetField(DM dm, PetscInt f, DMLabel label, PetscObject disc)
5062 {
5063   PetscFunctionBegin;
5064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5065   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3);
5066   PetscValidHeader(disc, 4);
5067   PetscCheck(f >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field number %" PetscInt_FMT " must be non-negative", f);
5068   PetscCall(DMSetField_Internal(dm, f, label, disc));
5069   PetscCall(DMSetDefaultAdjacency_Private(dm, f, disc));
5070   PetscCall(DMClearDS(dm));
5071   PetscFunctionReturn(PETSC_SUCCESS);
5072 }
5073 
5074 /*@
5075   DMAddField - Add a field to a `DM` object. A field is a function space defined by of a set of discretization points (geometric entities)
5076   and a discretization object that defines the function space associated with those points.
5077 
5078   Logically Collective
5079 
5080   Input Parameters:
5081 + dm    - The `DM`
5082 . label - The label indicating the support of the field, or `NULL` for the entire mesh
5083 - disc  - The discretization object
5084 
5085   Level: intermediate
5086 
5087   Notes:
5088   The label already exists or will be added to the `DM` with `DMSetLabel()`.
5089 
5090   For example, a piecewise continuous pressure field can be defined by coefficients at the cell centers of a mesh and piecewise constant functions
5091   within each cell. Thus a specific function in the space is defined by the combination of a `Vec` containing the coefficients, a `DM` defining the
5092   geometry entities, a `DMLabel` indicating a subset of those geometric entities, and a discretization object, such as a `PetscFE`.
5093 
5094 .seealso: [](ch_dmbase), `DM`, `DMSetLabel()`, `DMSetField()`, `DMGetField()`, `PetscFE`
5095 @*/
5096 PetscErrorCode DMAddField(DM dm, DMLabel label, PetscObject disc)
5097 {
5098   PetscInt Nf = dm->Nf;
5099 
5100   PetscFunctionBegin;
5101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5102   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
5103   PetscValidHeader(disc, 3);
5104   PetscCall(DMFieldEnlarge_Static(dm, Nf + 1));
5105   dm->fields[Nf].label = label;
5106   dm->fields[Nf].disc  = disc;
5107   PetscCall(PetscObjectReference((PetscObject)label));
5108   PetscCall(PetscObjectReference((PetscObject)disc));
5109   PetscCall(DMSetDefaultAdjacency_Private(dm, Nf, disc));
5110   PetscCall(DMClearDS(dm));
5111   PetscFunctionReturn(PETSC_SUCCESS);
5112 }
5113 
5114 /*@
5115   DMSetFieldAvoidTensor - Set flag to avoid defining the field on tensor cells
5116 
5117   Logically Collective
5118 
5119   Input Parameters:
5120 + dm          - The `DM`
5121 . f           - The field index
5122 - avoidTensor - `PETSC_TRUE` to skip defining the field on tensor cells
5123 
5124   Level: intermediate
5125 
5126 .seealso: [](ch_dmbase), `DM`, `DMGetFieldAvoidTensor()`, `DMSetField()`, `DMGetField()`
5127 @*/
5128 PetscErrorCode DMSetFieldAvoidTensor(DM dm, PetscInt f, PetscBool avoidTensor)
5129 {
5130   PetscFunctionBegin;
5131   PetscCheck((f >= 0) && (f < dm->Nf), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", f, dm->Nf);
5132   dm->fields[f].avoidTensor = avoidTensor;
5133   PetscFunctionReturn(PETSC_SUCCESS);
5134 }
5135 
5136 /*@
5137   DMGetFieldAvoidTensor - Get flag to avoid defining the field on tensor cells
5138 
5139   Not Collective
5140 
5141   Input Parameters:
5142 + dm - The `DM`
5143 - f  - The field index
5144 
5145   Output Parameter:
5146 . avoidTensor - The flag to avoid defining the field on tensor cells
5147 
5148   Level: intermediate
5149 
5150 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMSetField()`, `DMGetField()`, `DMSetFieldAvoidTensor()`
5151 @*/
5152 PetscErrorCode DMGetFieldAvoidTensor(DM dm, PetscInt f, PetscBool *avoidTensor)
5153 {
5154   PetscFunctionBegin;
5155   PetscCheck((f >= 0) && (f < dm->Nf), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", f, dm->Nf);
5156   *avoidTensor = dm->fields[f].avoidTensor;
5157   PetscFunctionReturn(PETSC_SUCCESS);
5158 }
5159 
5160 /*@
5161   DMCopyFields - Copy the discretizations for the `DM` into another `DM`
5162 
5163   Collective
5164 
5165   Input Parameters:
5166 + dm        - The `DM`
5167 . minDegree - Minimum degree for a discretization, or `PETSC_DETERMINE` for no limit
5168 - maxDegree - Maximum degree for a discretization, or `PETSC_DETERMINE` for no limit
5169 
5170   Output Parameter:
5171 . newdm - The `DM`
5172 
5173   Level: advanced
5174 
5175 .seealso: [](ch_dmbase), `DM`, `DMGetField()`, `DMSetField()`, `DMAddField()`, `DMCopyDS()`, `DMGetDS()`, `DMGetCellDS()`
5176 @*/
5177 PetscErrorCode DMCopyFields(DM dm, PetscInt minDegree, PetscInt maxDegree, DM newdm)
5178 {
5179   PetscInt Nf, f;
5180 
5181   PetscFunctionBegin;
5182   if (dm == newdm) PetscFunctionReturn(PETSC_SUCCESS);
5183   PetscCall(DMGetNumFields(dm, &Nf));
5184   PetscCall(DMClearFields(newdm));
5185   for (f = 0; f < Nf; ++f) {
5186     DMLabel      label;
5187     PetscObject  field;
5188     PetscClassId id;
5189     PetscBool    useCone, useClosure;
5190 
5191     PetscCall(DMGetField(dm, f, &label, &field));
5192     PetscCall(PetscObjectGetClassId(field, &id));
5193     if (id == PETSCFE_CLASSID) {
5194       PetscFE newfe;
5195 
5196       PetscCall(PetscFELimitDegree((PetscFE)field, minDegree, maxDegree, &newfe));
5197       PetscCall(DMSetField(newdm, f, label, (PetscObject)newfe));
5198       PetscCall(PetscFEDestroy(&newfe));
5199     } else {
5200       PetscCall(DMSetField(newdm, f, label, field));
5201     }
5202     PetscCall(DMGetAdjacency(dm, f, &useCone, &useClosure));
5203     PetscCall(DMSetAdjacency(newdm, f, useCone, useClosure));
5204   }
5205   PetscFunctionReturn(PETSC_SUCCESS);
5206 }
5207 
5208 /*@
5209   DMGetAdjacency - Returns the flags for determining variable influence
5210 
5211   Not Collective
5212 
5213   Input Parameters:
5214 + dm - The `DM` object
5215 - f  - The field number, or `PETSC_DEFAULT` for the default adjacency
5216 
5217   Output Parameters:
5218 + useCone    - Flag for variable influence starting with the cone operation
5219 - useClosure - Flag for variable influence using transitive closure
5220 
5221   Level: developer
5222 
5223   Notes:
5224 .vb
5225      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5226      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5227      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5228 .ve
5229   Further explanation can be found in the User's Manual Section on the Influence of Variables on One Another.
5230 
5231 .seealso: [](ch_dmbase), `DM`, `DMSetAdjacency()`, `DMGetField()`, `DMSetField()`
5232 @*/
5233 PetscErrorCode DMGetAdjacency(DM dm, PetscInt f, PetscBool *useCone, PetscBool *useClosure)
5234 {
5235   PetscFunctionBegin;
5236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5237   if (useCone) PetscAssertPointer(useCone, 3);
5238   if (useClosure) PetscAssertPointer(useClosure, 4);
5239   if (f < 0) {
5240     if (useCone) *useCone = dm->adjacency[0];
5241     if (useClosure) *useClosure = dm->adjacency[1];
5242   } else {
5243     PetscInt Nf;
5244 
5245     PetscCall(DMGetNumFields(dm, &Nf));
5246     PetscCheck(f < Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field number %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", f, Nf);
5247     if (useCone) *useCone = dm->fields[f].adjacency[0];
5248     if (useClosure) *useClosure = dm->fields[f].adjacency[1];
5249   }
5250   PetscFunctionReturn(PETSC_SUCCESS);
5251 }
5252 
5253 /*@
5254   DMSetAdjacency - Set the flags for determining variable influence
5255 
5256   Not Collective
5257 
5258   Input Parameters:
5259 + dm         - The `DM` object
5260 . f          - The field number
5261 . useCone    - Flag for variable influence starting with the cone operation
5262 - useClosure - Flag for variable influence using transitive closure
5263 
5264   Level: developer
5265 
5266   Notes:
5267 .vb
5268      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5269      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5270      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5271 .ve
5272   Further explanation can be found in the User's Manual Section on the Influence of Variables on One Another.
5273 
5274 .seealso: [](ch_dmbase), `DM`, `DMGetAdjacency()`, `DMGetField()`, `DMSetField()`
5275 @*/
5276 PetscErrorCode DMSetAdjacency(DM dm, PetscInt f, PetscBool useCone, PetscBool useClosure)
5277 {
5278   PetscFunctionBegin;
5279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5280   if (f < 0) {
5281     dm->adjacency[0] = useCone;
5282     dm->adjacency[1] = useClosure;
5283   } else {
5284     PetscInt Nf;
5285 
5286     PetscCall(DMGetNumFields(dm, &Nf));
5287     PetscCheck(f < Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field number %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", f, Nf);
5288     dm->fields[f].adjacency[0] = useCone;
5289     dm->fields[f].adjacency[1] = useClosure;
5290   }
5291   PetscFunctionReturn(PETSC_SUCCESS);
5292 }
5293 
5294 /*@
5295   DMGetBasicAdjacency - Returns the flags for determining variable influence, using either the default or field 0 if it is defined
5296 
5297   Not collective
5298 
5299   Input Parameter:
5300 . dm - The `DM` object
5301 
5302   Output Parameters:
5303 + useCone    - Flag for variable influence starting with the cone operation
5304 - useClosure - Flag for variable influence using transitive closure
5305 
5306   Level: developer
5307 
5308   Notes:
5309 .vb
5310      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5311      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5312      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5313 .ve
5314 
5315 .seealso: [](ch_dmbase), `DM`, `DMSetBasicAdjacency()`, `DMGetField()`, `DMSetField()`
5316 @*/
5317 PetscErrorCode DMGetBasicAdjacency(DM dm, PetscBool *useCone, PetscBool *useClosure)
5318 {
5319   PetscInt Nf;
5320 
5321   PetscFunctionBegin;
5322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5323   if (useCone) PetscAssertPointer(useCone, 2);
5324   if (useClosure) PetscAssertPointer(useClosure, 3);
5325   PetscCall(DMGetNumFields(dm, &Nf));
5326   if (!Nf) {
5327     PetscCall(DMGetAdjacency(dm, PETSC_DEFAULT, useCone, useClosure));
5328   } else {
5329     PetscCall(DMGetAdjacency(dm, 0, useCone, useClosure));
5330   }
5331   PetscFunctionReturn(PETSC_SUCCESS);
5332 }
5333 
5334 /*@
5335   DMSetBasicAdjacency - Set the flags for determining variable influence, using either the default or field 0 if it is defined
5336 
5337   Not Collective
5338 
5339   Input Parameters:
5340 + dm         - The `DM` object
5341 . useCone    - Flag for variable influence starting with the cone operation
5342 - useClosure - Flag for variable influence using transitive closure
5343 
5344   Level: developer
5345 
5346   Notes:
5347 .vb
5348      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5349      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5350      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5351 .ve
5352 
5353 .seealso: [](ch_dmbase), `DM`, `DMGetBasicAdjacency()`, `DMGetField()`, `DMSetField()`
5354 @*/
5355 PetscErrorCode DMSetBasicAdjacency(DM dm, PetscBool useCone, PetscBool useClosure)
5356 {
5357   PetscInt Nf;
5358 
5359   PetscFunctionBegin;
5360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5361   PetscCall(DMGetNumFields(dm, &Nf));
5362   if (!Nf) {
5363     PetscCall(DMSetAdjacency(dm, PETSC_DEFAULT, useCone, useClosure));
5364   } else {
5365     PetscCall(DMSetAdjacency(dm, 0, useCone, useClosure));
5366   }
5367   PetscFunctionReturn(PETSC_SUCCESS);
5368 }
5369 
5370 PetscErrorCode DMCompleteBCLabels_Internal(DM dm)
5371 {
5372   DM           plex;
5373   DMLabel     *labels, *glabels;
5374   const char **names;
5375   char        *sendNames, *recvNames;
5376   PetscInt     Nds, s, maxLabels = 0, maxLen = 0, gmaxLen, Nl = 0, gNl, l, gl, m;
5377   size_t       len;
5378   MPI_Comm     comm;
5379   PetscMPIInt  rank, size, p, *counts, *displs;
5380 
5381   PetscFunctionBegin;
5382   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5383   PetscCallMPI(MPI_Comm_size(comm, &size));
5384   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5385   PetscCall(DMGetNumDS(dm, &Nds));
5386   for (s = 0; s < Nds; ++s) {
5387     PetscDS  dsBC;
5388     PetscInt numBd;
5389 
5390     PetscCall(DMGetRegionNumDS(dm, s, NULL, NULL, &dsBC, NULL));
5391     PetscCall(PetscDSGetNumBoundary(dsBC, &numBd));
5392     maxLabels += numBd;
5393   }
5394   PetscCall(PetscCalloc1(maxLabels, &labels));
5395   /* Get list of labels to be completed */
5396   for (s = 0; s < Nds; ++s) {
5397     PetscDS  dsBC;
5398     PetscInt numBd, bd;
5399 
5400     PetscCall(DMGetRegionNumDS(dm, s, NULL, NULL, &dsBC, NULL));
5401     PetscCall(PetscDSGetNumBoundary(dsBC, &numBd));
5402     for (bd = 0; bd < numBd; ++bd) {
5403       DMLabel      label;
5404       PetscInt     field;
5405       PetscObject  obj;
5406       PetscClassId id;
5407 
5408       PetscCall(PetscDSGetBoundary(dsBC, bd, NULL, NULL, NULL, &label, NULL, NULL, &field, NULL, NULL, NULL, NULL, NULL));
5409       PetscCall(DMGetField(dm, field, NULL, &obj));
5410       PetscCall(PetscObjectGetClassId(obj, &id));
5411       if (!(id == PETSCFE_CLASSID) || !label) continue;
5412       for (l = 0; l < Nl; ++l)
5413         if (labels[l] == label) break;
5414       if (l == Nl) labels[Nl++] = label;
5415     }
5416   }
5417   /* Get label names */
5418   PetscCall(PetscMalloc1(Nl, &names));
5419   for (l = 0; l < Nl; ++l) PetscCall(PetscObjectGetName((PetscObject)labels[l], &names[l]));
5420   for (l = 0; l < Nl; ++l) {
5421     PetscCall(PetscStrlen(names[l], &len));
5422     maxLen = PetscMax(maxLen, (PetscInt)len + 2);
5423   }
5424   PetscCall(PetscFree(labels));
5425   PetscCallMPI(MPIU_Allreduce(&maxLen, &gmaxLen, 1, MPIU_INT, MPI_MAX, comm));
5426   PetscCall(PetscCalloc1(Nl * gmaxLen, &sendNames));
5427   for (l = 0; l < Nl; ++l) PetscCall(PetscStrncpy(&sendNames[gmaxLen * l], names[l], gmaxLen));
5428   PetscCall(PetscFree(names));
5429   /* Put all names on all processes */
5430   PetscCall(PetscCalloc2(size, &counts, size + 1, &displs));
5431   PetscCallMPI(MPI_Allgather(&Nl, 1, MPI_INT, counts, 1, MPI_INT, comm));
5432   for (p = 0; p < size; ++p) displs[p + 1] = displs[p] + counts[p];
5433   gNl = displs[size];
5434   for (p = 0; p < size; ++p) {
5435     counts[p] *= gmaxLen;
5436     displs[p] *= gmaxLen;
5437   }
5438   PetscCall(PetscCalloc2(gNl * gmaxLen, &recvNames, gNl, &glabels));
5439   PetscCallMPI(MPI_Allgatherv(sendNames, counts[rank], MPI_CHAR, recvNames, counts, displs, MPI_CHAR, comm));
5440   PetscCall(PetscFree2(counts, displs));
5441   PetscCall(PetscFree(sendNames));
5442   for (l = 0, gl = 0; l < gNl; ++l) {
5443     PetscCall(DMGetLabel(dm, &recvNames[l * gmaxLen], &glabels[gl]));
5444     PetscCheck(glabels[gl], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Label %s missing on rank %d", &recvNames[l * gmaxLen], rank);
5445     for (m = 0; m < gl; ++m)
5446       if (glabels[m] == glabels[gl]) continue;
5447     PetscCall(DMConvert(dm, DMPLEX, &plex));
5448     PetscCall(DMPlexLabelComplete(plex, glabels[gl]));
5449     PetscCall(DMDestroy(&plex));
5450     ++gl;
5451   }
5452   PetscCall(PetscFree2(recvNames, glabels));
5453   PetscFunctionReturn(PETSC_SUCCESS);
5454 }
5455 
5456 static PetscErrorCode DMDSEnlarge_Static(DM dm, PetscInt NdsNew)
5457 {
5458   DMSpace *tmpd;
5459   PetscInt Nds = dm->Nds, s;
5460 
5461   PetscFunctionBegin;
5462   if (Nds >= NdsNew) PetscFunctionReturn(PETSC_SUCCESS);
5463   PetscCall(PetscMalloc1(NdsNew, &tmpd));
5464   for (s = 0; s < Nds; ++s) tmpd[s] = dm->probs[s];
5465   for (s = Nds; s < NdsNew; ++s) {
5466     tmpd[s].ds     = NULL;
5467     tmpd[s].label  = NULL;
5468     tmpd[s].fields = NULL;
5469   }
5470   PetscCall(PetscFree(dm->probs));
5471   dm->Nds   = NdsNew;
5472   dm->probs = tmpd;
5473   PetscFunctionReturn(PETSC_SUCCESS);
5474 }
5475 
5476 /*@
5477   DMGetNumDS - Get the number of discrete systems in the `DM`
5478 
5479   Not Collective
5480 
5481   Input Parameter:
5482 . dm - The `DM`
5483 
5484   Output Parameter:
5485 . Nds - The number of `PetscDS` objects
5486 
5487   Level: intermediate
5488 
5489 .seealso: [](ch_dmbase), `DM`, `DMGetDS()`, `DMGetCellDS()`
5490 @*/
5491 PetscErrorCode DMGetNumDS(DM dm, PetscInt *Nds)
5492 {
5493   PetscFunctionBegin;
5494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5495   PetscAssertPointer(Nds, 2);
5496   *Nds = dm->Nds;
5497   PetscFunctionReturn(PETSC_SUCCESS);
5498 }
5499 
5500 /*@
5501   DMClearDS - Remove all discrete systems from the `DM`
5502 
5503   Logically Collective
5504 
5505   Input Parameter:
5506 . dm - The `DM`
5507 
5508   Level: intermediate
5509 
5510 .seealso: [](ch_dmbase), `DM`, `DMGetNumDS()`, `DMGetDS()`, `DMSetField()`
5511 @*/
5512 PetscErrorCode DMClearDS(DM dm)
5513 {
5514   PetscInt s;
5515 
5516   PetscFunctionBegin;
5517   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5518   for (s = 0; s < dm->Nds; ++s) {
5519     PetscCall(PetscDSDestroy(&dm->probs[s].ds));
5520     PetscCall(PetscDSDestroy(&dm->probs[s].dsIn));
5521     PetscCall(DMLabelDestroy(&dm->probs[s].label));
5522     PetscCall(ISDestroy(&dm->probs[s].fields));
5523   }
5524   PetscCall(PetscFree(dm->probs));
5525   dm->probs = NULL;
5526   dm->Nds   = 0;
5527   PetscFunctionReturn(PETSC_SUCCESS);
5528 }
5529 
5530 /*@
5531   DMGetDS - Get the default `PetscDS`
5532 
5533   Not Collective
5534 
5535   Input Parameter:
5536 . dm - The `DM`
5537 
5538   Output Parameter:
5539 . ds - The default `PetscDS`
5540 
5541   Level: intermediate
5542 
5543 .seealso: [](ch_dmbase), `DM`, `DMGetCellDS()`, `DMGetRegionDS()`
5544 @*/
5545 PetscErrorCode DMGetDS(DM dm, PetscDS *ds)
5546 {
5547   PetscFunctionBeginHot;
5548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5549   PetscAssertPointer(ds, 2);
5550   PetscCheck(dm->Nds > 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Need to call DMCreateDS() before calling DMGetDS()");
5551   *ds = dm->probs[0].ds;
5552   PetscFunctionReturn(PETSC_SUCCESS);
5553 }
5554 
5555 /*@
5556   DMGetCellDS - Get the `PetscDS` defined on a given cell
5557 
5558   Not Collective
5559 
5560   Input Parameters:
5561 + dm    - The `DM`
5562 - point - Cell for the `PetscDS`
5563 
5564   Output Parameters:
5565 + ds   - The `PetscDS` defined on the given cell
5566 - dsIn - The `PetscDS` for input on the given cell, or NULL if the same ds
5567 
5568   Level: developer
5569 
5570 .seealso: [](ch_dmbase), `DM`, `DMGetDS()`, `DMSetRegionDS()`
5571 @*/
5572 PetscErrorCode DMGetCellDS(DM dm, PetscInt point, PetscDS *ds, PetscDS *dsIn)
5573 {
5574   PetscDS  dsDef = NULL;
5575   PetscInt s;
5576 
5577   PetscFunctionBeginHot;
5578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5579   if (ds) PetscAssertPointer(ds, 3);
5580   if (dsIn) PetscAssertPointer(dsIn, 4);
5581   PetscCheck(point >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Mesh point cannot be negative: %" PetscInt_FMT, point);
5582   if (ds) *ds = NULL;
5583   if (dsIn) *dsIn = NULL;
5584   for (s = 0; s < dm->Nds; ++s) {
5585     PetscInt val;
5586 
5587     if (!dm->probs[s].label) {
5588       dsDef = dm->probs[s].ds;
5589     } else {
5590       PetscCall(DMLabelGetValue(dm->probs[s].label, point, &val));
5591       if (val >= 0) {
5592         if (ds) *ds = dm->probs[s].ds;
5593         if (dsIn) *dsIn = dm->probs[s].dsIn;
5594         break;
5595       }
5596     }
5597   }
5598   if (ds && !*ds) *ds = dsDef;
5599   PetscFunctionReturn(PETSC_SUCCESS);
5600 }
5601 
5602 /*@
5603   DMGetRegionDS - Get the `PetscDS` for a given mesh region, defined by a `DMLabel`
5604 
5605   Not Collective
5606 
5607   Input Parameters:
5608 + dm    - The `DM`
5609 - label - The `DMLabel` defining the mesh region, or `NULL` for the entire mesh
5610 
5611   Output Parameters:
5612 + fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL`
5613 . ds     - The `PetscDS` defined on the given region, or `NULL`
5614 - dsIn   - The `PetscDS` for input in the given region, or `NULL`
5615 
5616   Level: advanced
5617 
5618   Note:
5619   If a non-`NULL` label is given, but there is no `PetscDS` on that specific label,
5620   the `PetscDS` for the full domain (if present) is returned. Returns with
5621   fields = `NULL` and ds = `NULL` if there is no `PetscDS` for the full domain.
5622 
5623 .seealso: [](ch_dmbase), `DM`, `DMGetRegionNumDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5624 @*/
5625 PetscErrorCode DMGetRegionDS(DM dm, DMLabel label, IS *fields, PetscDS *ds, PetscDS *dsIn)
5626 {
5627   PetscInt Nds = dm->Nds, s;
5628 
5629   PetscFunctionBegin;
5630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5631   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
5632   if (fields) {
5633     PetscAssertPointer(fields, 3);
5634     *fields = NULL;
5635   }
5636   if (ds) {
5637     PetscAssertPointer(ds, 4);
5638     *ds = NULL;
5639   }
5640   if (dsIn) {
5641     PetscAssertPointer(dsIn, 5);
5642     *dsIn = NULL;
5643   }
5644   for (s = 0; s < Nds; ++s) {
5645     if (dm->probs[s].label == label || !dm->probs[s].label) {
5646       if (fields) *fields = dm->probs[s].fields;
5647       if (ds) *ds = dm->probs[s].ds;
5648       if (dsIn) *dsIn = dm->probs[s].dsIn;
5649       if (dm->probs[s].label) PetscFunctionReturn(PETSC_SUCCESS);
5650     }
5651   }
5652   PetscFunctionReturn(PETSC_SUCCESS);
5653 }
5654 
5655 /*@
5656   DMSetRegionDS - Set the `PetscDS` for a given mesh region, defined by a `DMLabel`
5657 
5658   Collective
5659 
5660   Input Parameters:
5661 + dm     - The `DM`
5662 . label  - The `DMLabel` defining the mesh region, or `NULL` for the entire mesh
5663 . fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL` for all fields
5664 . ds     - The `PetscDS` defined on the given region
5665 - dsIn   - The `PetscDS` for input on the given cell, or `NULL` if it is the same `PetscDS`
5666 
5667   Level: advanced
5668 
5669   Note:
5670   If the label has a `PetscDS` defined, it will be replaced. Otherwise, it will be added to the `DM`. If the `PetscDS` is replaced,
5671   the fields argument is ignored.
5672 
5673 .seealso: [](ch_dmbase), `DM`, `DMGetRegionDS()`, `DMSetRegionNumDS()`, `DMGetDS()`, `DMGetCellDS()`
5674 @*/
5675 PetscErrorCode DMSetRegionDS(DM dm, DMLabel label, IS fields, PetscDS ds, PetscDS dsIn)
5676 {
5677   PetscInt Nds = dm->Nds, s;
5678 
5679   PetscFunctionBegin;
5680   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5681   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
5682   if (fields) PetscValidHeaderSpecific(fields, IS_CLASSID, 3);
5683   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 4);
5684   if (dsIn) PetscValidHeaderSpecific(dsIn, PETSCDS_CLASSID, 5);
5685   for (s = 0; s < Nds; ++s) {
5686     if (dm->probs[s].label == label) {
5687       PetscCall(PetscDSDestroy(&dm->probs[s].ds));
5688       PetscCall(PetscDSDestroy(&dm->probs[s].dsIn));
5689       dm->probs[s].ds   = ds;
5690       dm->probs[s].dsIn = dsIn;
5691       PetscFunctionReturn(PETSC_SUCCESS);
5692     }
5693   }
5694   PetscCall(DMDSEnlarge_Static(dm, Nds + 1));
5695   PetscCall(PetscObjectReference((PetscObject)label));
5696   PetscCall(PetscObjectReference((PetscObject)fields));
5697   PetscCall(PetscObjectReference((PetscObject)ds));
5698   PetscCall(PetscObjectReference((PetscObject)dsIn));
5699   if (!label) {
5700     /* Put the NULL label at the front, so it is returned as the default */
5701     for (s = Nds - 1; s >= 0; --s) dm->probs[s + 1] = dm->probs[s];
5702     Nds = 0;
5703   }
5704   dm->probs[Nds].label  = label;
5705   dm->probs[Nds].fields = fields;
5706   dm->probs[Nds].ds     = ds;
5707   dm->probs[Nds].dsIn   = dsIn;
5708   PetscFunctionReturn(PETSC_SUCCESS);
5709 }
5710 
5711 /*@
5712   DMGetRegionNumDS - Get the `PetscDS` for a given mesh region, defined by the region number
5713 
5714   Not Collective
5715 
5716   Input Parameters:
5717 + dm  - The `DM`
5718 - num - The region number, in [0, Nds)
5719 
5720   Output Parameters:
5721 + label  - The region label, or `NULL`
5722 . fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL`
5723 . ds     - The `PetscDS` defined on the given region, or `NULL`
5724 - dsIn   - The `PetscDS` for input in the given region, or `NULL`
5725 
5726   Level: advanced
5727 
5728 .seealso: [](ch_dmbase), `DM`, `DMGetRegionDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5729 @*/
5730 PetscErrorCode DMGetRegionNumDS(DM dm, PetscInt num, DMLabel *label, IS *fields, PetscDS *ds, PetscDS *dsIn)
5731 {
5732   PetscInt Nds;
5733 
5734   PetscFunctionBegin;
5735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5736   PetscCall(DMGetNumDS(dm, &Nds));
5737   PetscCheck((num >= 0) && (num < Nds), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Region number %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", num, Nds);
5738   if (label) {
5739     PetscAssertPointer(label, 3);
5740     *label = dm->probs[num].label;
5741   }
5742   if (fields) {
5743     PetscAssertPointer(fields, 4);
5744     *fields = dm->probs[num].fields;
5745   }
5746   if (ds) {
5747     PetscAssertPointer(ds, 5);
5748     *ds = dm->probs[num].ds;
5749   }
5750   if (dsIn) {
5751     PetscAssertPointer(dsIn, 6);
5752     *dsIn = dm->probs[num].dsIn;
5753   }
5754   PetscFunctionReturn(PETSC_SUCCESS);
5755 }
5756 
5757 /*@
5758   DMSetRegionNumDS - Set the `PetscDS` for a given mesh region, defined by the region number
5759 
5760   Not Collective
5761 
5762   Input Parameters:
5763 + dm     - The `DM`
5764 . num    - The region number, in [0, Nds)
5765 . label  - The region label, or `NULL`
5766 . fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL` to prevent setting
5767 . ds     - The `PetscDS` defined on the given region, or `NULL` to prevent setting
5768 - dsIn   - The `PetscDS` for input on the given cell, or `NULL` if it is the same `PetscDS`
5769 
5770   Level: advanced
5771 
5772 .seealso: [](ch_dmbase), `DM`, `DMGetRegionDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5773 @*/
5774 PetscErrorCode DMSetRegionNumDS(DM dm, PetscInt num, DMLabel label, IS fields, PetscDS ds, PetscDS dsIn)
5775 {
5776   PetscInt Nds;
5777 
5778   PetscFunctionBegin;
5779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5780   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3);
5781   PetscCall(DMGetNumDS(dm, &Nds));
5782   PetscCheck((num >= 0) && (num < Nds), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Region number %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", num, Nds);
5783   PetscCall(PetscObjectReference((PetscObject)label));
5784   PetscCall(DMLabelDestroy(&dm->probs[num].label));
5785   dm->probs[num].label = label;
5786   if (fields) {
5787     PetscValidHeaderSpecific(fields, IS_CLASSID, 4);
5788     PetscCall(PetscObjectReference((PetscObject)fields));
5789     PetscCall(ISDestroy(&dm->probs[num].fields));
5790     dm->probs[num].fields = fields;
5791   }
5792   if (ds) {
5793     PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 5);
5794     PetscCall(PetscObjectReference((PetscObject)ds));
5795     PetscCall(PetscDSDestroy(&dm->probs[num].ds));
5796     dm->probs[num].ds = ds;
5797   }
5798   if (dsIn) {
5799     PetscValidHeaderSpecific(dsIn, PETSCDS_CLASSID, 6);
5800     PetscCall(PetscObjectReference((PetscObject)dsIn));
5801     PetscCall(PetscDSDestroy(&dm->probs[num].dsIn));
5802     dm->probs[num].dsIn = dsIn;
5803   }
5804   PetscFunctionReturn(PETSC_SUCCESS);
5805 }
5806 
5807 /*@
5808   DMFindRegionNum - Find the region number for a given `PetscDS`, or -1 if it is not found.
5809 
5810   Not Collective
5811 
5812   Input Parameters:
5813 + dm - The `DM`
5814 - ds - The `PetscDS` defined on the given region
5815 
5816   Output Parameter:
5817 . num - The region number, in [0, Nds), or -1 if not found
5818 
5819   Level: advanced
5820 
5821 .seealso: [](ch_dmbase), `DM`, `DMGetRegionNumDS()`, `DMGetRegionDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5822 @*/
5823 PetscErrorCode DMFindRegionNum(DM dm, PetscDS ds, PetscInt *num)
5824 {
5825   PetscInt Nds, n;
5826 
5827   PetscFunctionBegin;
5828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5829   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 2);
5830   PetscAssertPointer(num, 3);
5831   PetscCall(DMGetNumDS(dm, &Nds));
5832   for (n = 0; n < Nds; ++n)
5833     if (ds == dm->probs[n].ds) break;
5834   if (n >= Nds) *num = -1;
5835   else *num = n;
5836   PetscFunctionReturn(PETSC_SUCCESS);
5837 }
5838 
5839 /*@
5840   DMCreateFEDefault - Create a `PetscFE` based on the celltype for the mesh
5841 
5842   Not Collective
5843 
5844   Input Parameters:
5845 + dm     - The `DM`
5846 . Nc     - The number of components for the field
5847 . prefix - The options prefix for the output `PetscFE`, or `NULL`
5848 - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
5849 
5850   Output Parameter:
5851 . fem - The `PetscFE`
5852 
5853   Level: intermediate
5854 
5855   Note:
5856   This is a convenience method that just calls `PetscFECreateByCell()` underneath.
5857 
5858 .seealso: [](ch_dmbase), `DM`, `PetscFECreateByCell()`, `DMAddField()`, `DMCreateDS()`, `DMGetCellDS()`, `DMGetRegionDS()`
5859 @*/
5860 PetscErrorCode DMCreateFEDefault(DM dm, PetscInt Nc, const char prefix[], PetscInt qorder, PetscFE *fem)
5861 {
5862   DMPolytopeType ct;
5863   PetscInt       dim, cStart;
5864 
5865   PetscFunctionBegin;
5866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5867   PetscValidLogicalCollectiveInt(dm, Nc, 2);
5868   if (prefix) PetscAssertPointer(prefix, 3);
5869   PetscValidLogicalCollectiveInt(dm, qorder, 4);
5870   PetscAssertPointer(fem, 5);
5871   PetscCall(DMGetDimension(dm, &dim));
5872   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
5873   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
5874   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, Nc, ct, prefix, qorder, fem));
5875   PetscFunctionReturn(PETSC_SUCCESS);
5876 }
5877 
5878 /*@
5879   DMCreateDS - Create the discrete systems for the `DM` based upon the fields added to the `DM`
5880 
5881   Collective
5882 
5883   Input Parameter:
5884 . dm - The `DM`
5885 
5886   Options Database Key:
5887 . -dm_petscds_view - View all the `PetscDS` objects in this `DM`
5888 
5889   Level: intermediate
5890 
5891 .seealso: [](ch_dmbase), `DM`, `DMSetField`, `DMAddField()`, `DMGetDS()`, `DMGetCellDS()`, `DMGetRegionDS()`, `DMSetRegionDS()`
5892 @*/
5893 PetscErrorCode DMCreateDS(DM dm)
5894 {
5895   MPI_Comm  comm;
5896   PetscDS   dsDef;
5897   DMLabel  *labelSet;
5898   PetscInt  dE, Nf = dm->Nf, f, s, Nl, l, Ndef, k;
5899   PetscBool doSetup = PETSC_TRUE, flg;
5900 
5901   PetscFunctionBegin;
5902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5903   if (!dm->fields) PetscFunctionReturn(PETSC_SUCCESS);
5904   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5905   PetscCall(DMGetCoordinateDim(dm, &dE));
5906   /* Determine how many regions we have */
5907   PetscCall(PetscMalloc1(Nf, &labelSet));
5908   Nl   = 0;
5909   Ndef = 0;
5910   for (f = 0; f < Nf; ++f) {
5911     DMLabel  label = dm->fields[f].label;
5912     PetscInt l;
5913 
5914 #ifdef PETSC_HAVE_LIBCEED
5915     /* Move CEED context to discretizations */
5916     {
5917       PetscClassId id;
5918 
5919       PetscCall(PetscObjectGetClassId(dm->fields[f].disc, &id));
5920       if (id == PETSCFE_CLASSID) {
5921         Ceed ceed;
5922 
5923         PetscCall(DMGetCeed(dm, &ceed));
5924         PetscCall(PetscFESetCeed((PetscFE)dm->fields[f].disc, ceed));
5925       }
5926     }
5927 #endif
5928     if (!label) {
5929       ++Ndef;
5930       continue;
5931     }
5932     for (l = 0; l < Nl; ++l)
5933       if (label == labelSet[l]) break;
5934     if (l < Nl) continue;
5935     labelSet[Nl++] = label;
5936   }
5937   /* Create default DS if there are no labels to intersect with */
5938   PetscCall(DMGetRegionDS(dm, NULL, NULL, &dsDef, NULL));
5939   if (!dsDef && Ndef && !Nl) {
5940     IS        fields;
5941     PetscInt *fld, nf;
5942 
5943     for (f = 0, nf = 0; f < Nf; ++f)
5944       if (!dm->fields[f].label) ++nf;
5945     PetscCheck(nf, comm, PETSC_ERR_PLIB, "All fields have labels, but we are trying to create a default DS");
5946     PetscCall(PetscMalloc1(nf, &fld));
5947     for (f = 0, nf = 0; f < Nf; ++f)
5948       if (!dm->fields[f].label) fld[nf++] = f;
5949     PetscCall(ISCreate(PETSC_COMM_SELF, &fields));
5950     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fields, "dm_fields_"));
5951     PetscCall(ISSetType(fields, ISGENERAL));
5952     PetscCall(ISGeneralSetIndices(fields, nf, fld, PETSC_OWN_POINTER));
5953 
5954     PetscCall(PetscDSCreate(PETSC_COMM_SELF, &dsDef));
5955     PetscCall(DMSetRegionDS(dm, NULL, fields, dsDef, NULL));
5956     PetscCall(PetscDSDestroy(&dsDef));
5957     PetscCall(ISDestroy(&fields));
5958   }
5959   PetscCall(DMGetRegionDS(dm, NULL, NULL, &dsDef, NULL));
5960   if (dsDef) PetscCall(PetscDSSetCoordinateDimension(dsDef, dE));
5961   /* Intersect labels with default fields */
5962   if (Ndef && Nl) {
5963     DM              plex;
5964     DMLabel         cellLabel;
5965     IS              fieldIS, allcellIS, defcellIS = NULL;
5966     PetscInt       *fields;
5967     const PetscInt *cells;
5968     PetscInt        depth, nf = 0, n, c;
5969 
5970     PetscCall(DMConvert(dm, DMPLEX, &plex));
5971     PetscCall(DMPlexGetDepth(plex, &depth));
5972     PetscCall(DMGetStratumIS(plex, "dim", depth, &allcellIS));
5973     if (!allcellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, &allcellIS));
5974     /* TODO This looks like it only works for one label */
5975     for (l = 0; l < Nl; ++l) {
5976       DMLabel label = labelSet[l];
5977       IS      pointIS;
5978 
5979       PetscCall(ISDestroy(&defcellIS));
5980       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
5981       PetscCall(ISDifference(allcellIS, pointIS, &defcellIS));
5982       PetscCall(ISDestroy(&pointIS));
5983     }
5984     PetscCall(ISDestroy(&allcellIS));
5985 
5986     PetscCall(DMLabelCreate(PETSC_COMM_SELF, "defaultCells", &cellLabel));
5987     PetscCall(ISGetLocalSize(defcellIS, &n));
5988     PetscCall(ISGetIndices(defcellIS, &cells));
5989     for (c = 0; c < n; ++c) PetscCall(DMLabelSetValue(cellLabel, cells[c], 1));
5990     PetscCall(ISRestoreIndices(defcellIS, &cells));
5991     PetscCall(ISDestroy(&defcellIS));
5992     PetscCall(DMPlexLabelComplete(plex, cellLabel));
5993 
5994     PetscCall(PetscMalloc1(Ndef, &fields));
5995     for (f = 0; f < Nf; ++f)
5996       if (!dm->fields[f].label) fields[nf++] = f;
5997     PetscCall(ISCreate(PETSC_COMM_SELF, &fieldIS));
5998     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fieldIS, "dm_fields_"));
5999     PetscCall(ISSetType(fieldIS, ISGENERAL));
6000     PetscCall(ISGeneralSetIndices(fieldIS, nf, fields, PETSC_OWN_POINTER));
6001 
6002     PetscCall(PetscDSCreate(PETSC_COMM_SELF, &dsDef));
6003     PetscCall(DMSetRegionDS(dm, cellLabel, fieldIS, dsDef, NULL));
6004     PetscCall(PetscDSSetCoordinateDimension(dsDef, dE));
6005     PetscCall(DMLabelDestroy(&cellLabel));
6006     PetscCall(PetscDSDestroy(&dsDef));
6007     PetscCall(ISDestroy(&fieldIS));
6008     PetscCall(DMDestroy(&plex));
6009   }
6010   /* Create label DSes
6011      - WE ONLY SUPPORT IDENTICAL OR DISJOINT LABELS
6012   */
6013   /* TODO Should check that labels are disjoint */
6014   for (l = 0; l < Nl; ++l) {
6015     DMLabel   label = labelSet[l];
6016     PetscDS   ds, dsIn = NULL;
6017     IS        fields;
6018     PetscInt *fld, nf;
6019 
6020     PetscCall(PetscDSCreate(PETSC_COMM_SELF, &ds));
6021     for (f = 0, nf = 0; f < Nf; ++f)
6022       if (label == dm->fields[f].label || !dm->fields[f].label) ++nf;
6023     PetscCall(PetscMalloc1(nf, &fld));
6024     for (f = 0, nf = 0; f < Nf; ++f)
6025       if (label == dm->fields[f].label || !dm->fields[f].label) fld[nf++] = f;
6026     PetscCall(ISCreate(PETSC_COMM_SELF, &fields));
6027     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fields, "dm_fields_"));
6028     PetscCall(ISSetType(fields, ISGENERAL));
6029     PetscCall(ISGeneralSetIndices(fields, nf, fld, PETSC_OWN_POINTER));
6030     PetscCall(PetscDSSetCoordinateDimension(ds, dE));
6031     {
6032       DMPolytopeType ct;
6033       PetscInt       lStart, lEnd;
6034       PetscBool      isCohesiveLocal = PETSC_FALSE, isCohesive;
6035 
6036       PetscCall(DMLabelGetBounds(label, &lStart, &lEnd));
6037       if (lStart >= 0) {
6038         PetscCall(DMPlexGetCellType(dm, lStart, &ct));
6039         switch (ct) {
6040         case DM_POLYTOPE_POINT_PRISM_TENSOR:
6041         case DM_POLYTOPE_SEG_PRISM_TENSOR:
6042         case DM_POLYTOPE_TRI_PRISM_TENSOR:
6043         case DM_POLYTOPE_QUAD_PRISM_TENSOR:
6044           isCohesiveLocal = PETSC_TRUE;
6045           break;
6046         default:
6047           break;
6048         }
6049       }
6050       PetscCallMPI(MPIU_Allreduce(&isCohesiveLocal, &isCohesive, 1, MPIU_BOOL, MPI_LOR, comm));
6051       if (isCohesive) {
6052         PetscCall(PetscDSCreate(PETSC_COMM_SELF, &dsIn));
6053         PetscCall(PetscDSSetCoordinateDimension(dsIn, dE));
6054       }
6055       for (f = 0, nf = 0; f < Nf; ++f) {
6056         if (label == dm->fields[f].label || !dm->fields[f].label) {
6057           if (label == dm->fields[f].label) {
6058             PetscCall(PetscDSSetDiscretization(ds, nf, NULL));
6059             PetscCall(PetscDSSetCohesive(ds, nf, isCohesive));
6060             if (dsIn) {
6061               PetscCall(PetscDSSetDiscretization(dsIn, nf, NULL));
6062               PetscCall(PetscDSSetCohesive(dsIn, nf, isCohesive));
6063             }
6064           }
6065           ++nf;
6066         }
6067       }
6068     }
6069     PetscCall(DMSetRegionDS(dm, label, fields, ds, dsIn));
6070     PetscCall(ISDestroy(&fields));
6071     PetscCall(PetscDSDestroy(&ds));
6072     PetscCall(PetscDSDestroy(&dsIn));
6073   }
6074   PetscCall(PetscFree(labelSet));
6075   /* Set fields in DSes */
6076   for (s = 0; s < dm->Nds; ++s) {
6077     PetscDS         ds     = dm->probs[s].ds;
6078     PetscDS         dsIn   = dm->probs[s].dsIn;
6079     IS              fields = dm->probs[s].fields;
6080     const PetscInt *fld;
6081     PetscInt        nf, dsnf;
6082     PetscBool       isCohesive;
6083 
6084     PetscCall(PetscDSGetNumFields(ds, &dsnf));
6085     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
6086     PetscCall(ISGetLocalSize(fields, &nf));
6087     PetscCall(ISGetIndices(fields, &fld));
6088     for (f = 0; f < nf; ++f) {
6089       PetscObject  disc = dm->fields[fld[f]].disc;
6090       PetscBool    isCohesiveField;
6091       PetscClassId id;
6092 
6093       /* Handle DS with no fields */
6094       if (dsnf) PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
6095       /* If this is a cohesive cell, then regular fields need the lower dimensional discretization */
6096       if (isCohesive) {
6097         if (!isCohesiveField) {
6098           PetscObject bdDisc;
6099 
6100           PetscCall(PetscFEGetHeightSubspace((PetscFE)disc, 1, (PetscFE *)&bdDisc));
6101           PetscCall(PetscDSSetDiscretization(ds, f, bdDisc));
6102           PetscCall(PetscDSSetDiscretization(dsIn, f, disc));
6103         } else {
6104           PetscCall(PetscDSSetDiscretization(ds, f, disc));
6105           PetscCall(PetscDSSetDiscretization(dsIn, f, disc));
6106         }
6107       } else {
6108         PetscCall(PetscDSSetDiscretization(ds, f, disc));
6109       }
6110       /* We allow people to have placeholder fields and construct the Section by hand */
6111       PetscCall(PetscObjectGetClassId(disc, &id));
6112       if ((id != PETSCFE_CLASSID) && (id != PETSCFV_CLASSID)) doSetup = PETSC_FALSE;
6113     }
6114     PetscCall(ISRestoreIndices(fields, &fld));
6115   }
6116   /* Allow k-jet tabulation */
6117   PetscCall(PetscOptionsGetInt(NULL, ((PetscObject)dm)->prefix, "-dm_ds_jet_degree", &k, &flg));
6118   if (flg) {
6119     for (s = 0; s < dm->Nds; ++s) {
6120       PetscDS  ds   = dm->probs[s].ds;
6121       PetscDS  dsIn = dm->probs[s].dsIn;
6122       PetscInt Nf, f;
6123 
6124       PetscCall(PetscDSGetNumFields(ds, &Nf));
6125       for (f = 0; f < Nf; ++f) {
6126         PetscCall(PetscDSSetJetDegree(ds, f, k));
6127         if (dsIn) PetscCall(PetscDSSetJetDegree(dsIn, f, k));
6128       }
6129     }
6130   }
6131   /* Setup DSes */
6132   if (doSetup) {
6133     for (s = 0; s < dm->Nds; ++s) {
6134       if (dm->setfromoptionscalled) {
6135         PetscCall(PetscDSSetFromOptions(dm->probs[s].ds));
6136         if (dm->probs[s].dsIn) PetscCall(PetscDSSetFromOptions(dm->probs[s].dsIn));
6137       }
6138       PetscCall(PetscDSSetUp(dm->probs[s].ds));
6139       if (dm->probs[s].dsIn) PetscCall(PetscDSSetUp(dm->probs[s].dsIn));
6140     }
6141   }
6142   PetscFunctionReturn(PETSC_SUCCESS);
6143 }
6144 
6145 /*@
6146   DMUseTensorOrder - Use a tensor product closure ordering for the default section
6147 
6148   Input Parameters:
6149 + dm     - The DM
6150 - tensor - Flag for tensor order
6151 
6152   Level: developer
6153 
6154 .seealso: `DMPlexSetClosurePermutationTensor()`, `PetscSectionResetClosurePermutation()`
6155 @*/
6156 PetscErrorCode DMUseTensorOrder(DM dm, PetscBool tensor)
6157 {
6158   PetscInt  Nf;
6159   PetscBool reorder = PETSC_TRUE, isPlex;
6160 
6161   PetscFunctionBegin;
6162   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
6163   PetscCall(DMGetNumFields(dm, &Nf));
6164   for (PetscInt f = 0; f < Nf; ++f) {
6165     PetscObject  obj;
6166     PetscClassId id;
6167 
6168     PetscCall(DMGetField(dm, f, NULL, &obj));
6169     PetscCall(PetscObjectGetClassId(obj, &id));
6170     if (id == PETSCFE_CLASSID) {
6171       PetscSpace sp;
6172       PetscBool  tensor;
6173 
6174       PetscCall(PetscFEGetBasisSpace((PetscFE)obj, &sp));
6175       PetscCall(PetscSpacePolynomialGetTensor(sp, &tensor));
6176       reorder = reorder && tensor ? PETSC_TRUE : PETSC_FALSE;
6177     } else reorder = PETSC_FALSE;
6178   }
6179   if (tensor) {
6180     if (reorder && isPlex) PetscCall(DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL));
6181   } else {
6182     PetscSection s;
6183 
6184     PetscCall(DMGetLocalSection(dm, &s));
6185     if (s) PetscCall(PetscSectionResetClosurePermutation(s));
6186   }
6187   PetscFunctionReturn(PETSC_SUCCESS);
6188 }
6189 
6190 /*@
6191   DMComputeExactSolution - Compute the exact solution for a given `DM`, using the `PetscDS` information.
6192 
6193   Collective
6194 
6195   Input Parameters:
6196 + dm   - The `DM`
6197 - time - The time
6198 
6199   Output Parameters:
6200 + u   - The vector will be filled with exact solution values, or `NULL`
6201 - u_t - The vector will be filled with the time derivative of exact solution values, or `NULL`
6202 
6203   Level: developer
6204 
6205   Note:
6206   The user must call `PetscDSSetExactSolution()` before using this routine
6207 
6208 .seealso: [](ch_dmbase), `DM`, `PetscDSSetExactSolution()`
6209 @*/
6210 PetscErrorCode DMComputeExactSolution(DM dm, PetscReal time, Vec u, Vec u_t)
6211 {
6212   PetscErrorCode (**exacts)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
6213   void   **ectxs;
6214   Vec      locu, locu_t;
6215   PetscInt Nf, Nds, s;
6216 
6217   PetscFunctionBegin;
6218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6219   if (u) {
6220     PetscValidHeaderSpecific(u, VEC_CLASSID, 3);
6221     PetscCall(DMGetLocalVector(dm, &locu));
6222     PetscCall(VecSet(locu, 0.));
6223   }
6224   if (u_t) {
6225     PetscValidHeaderSpecific(u_t, VEC_CLASSID, 4);
6226     PetscCall(DMGetLocalVector(dm, &locu_t));
6227     PetscCall(VecSet(locu_t, 0.));
6228   }
6229   PetscCall(DMGetNumFields(dm, &Nf));
6230   PetscCall(PetscMalloc2(Nf, &exacts, Nf, &ectxs));
6231   PetscCall(DMGetNumDS(dm, &Nds));
6232   for (s = 0; s < Nds; ++s) {
6233     PetscDS         ds;
6234     DMLabel         label;
6235     IS              fieldIS;
6236     const PetscInt *fields, id = 1;
6237     PetscInt        dsNf, f;
6238 
6239     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
6240     PetscCall(PetscDSGetNumFields(ds, &dsNf));
6241     PetscCall(ISGetIndices(fieldIS, &fields));
6242     PetscCall(PetscArrayzero(exacts, Nf));
6243     PetscCall(PetscArrayzero(ectxs, Nf));
6244     if (u) {
6245       for (f = 0; f < dsNf; ++f) PetscCall(PetscDSGetExactSolution(ds, fields[f], &exacts[fields[f]], &ectxs[fields[f]]));
6246       if (label) PetscCall(DMProjectFunctionLabelLocal(dm, time, label, 1, &id, 0, NULL, exacts, ectxs, INSERT_ALL_VALUES, locu));
6247       else PetscCall(DMProjectFunctionLocal(dm, time, exacts, ectxs, INSERT_ALL_VALUES, locu));
6248     }
6249     if (u_t) {
6250       PetscCall(PetscArrayzero(exacts, Nf));
6251       PetscCall(PetscArrayzero(ectxs, Nf));
6252       for (f = 0; f < dsNf; ++f) PetscCall(PetscDSGetExactSolutionTimeDerivative(ds, fields[f], &exacts[fields[f]], &ectxs[fields[f]]));
6253       if (label) PetscCall(DMProjectFunctionLabelLocal(dm, time, label, 1, &id, 0, NULL, exacts, ectxs, INSERT_ALL_VALUES, locu_t));
6254       else PetscCall(DMProjectFunctionLocal(dm, time, exacts, ectxs, INSERT_ALL_VALUES, locu_t));
6255     }
6256     PetscCall(ISRestoreIndices(fieldIS, &fields));
6257   }
6258   if (u) {
6259     PetscCall(PetscObjectSetName((PetscObject)u, "Exact Solution"));
6260     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)u, "exact_"));
6261   }
6262   if (u_t) {
6263     PetscCall(PetscObjectSetName((PetscObject)u, "Exact Solution Time Derivative"));
6264     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)u_t, "exact_t_"));
6265   }
6266   PetscCall(PetscFree2(exacts, ectxs));
6267   if (u) {
6268     PetscCall(DMLocalToGlobalBegin(dm, locu, INSERT_ALL_VALUES, u));
6269     PetscCall(DMLocalToGlobalEnd(dm, locu, INSERT_ALL_VALUES, u));
6270     PetscCall(DMRestoreLocalVector(dm, &locu));
6271   }
6272   if (u_t) {
6273     PetscCall(DMLocalToGlobalBegin(dm, locu_t, INSERT_ALL_VALUES, u_t));
6274     PetscCall(DMLocalToGlobalEnd(dm, locu_t, INSERT_ALL_VALUES, u_t));
6275     PetscCall(DMRestoreLocalVector(dm, &locu_t));
6276   }
6277   PetscFunctionReturn(PETSC_SUCCESS);
6278 }
6279 
6280 static PetscErrorCode DMTransferDS_Internal(DM dm, DMLabel label, IS fields, PetscInt minDegree, PetscInt maxDegree, PetscDS ds, PetscDS dsIn)
6281 {
6282   PetscDS dsNew, dsInNew = NULL;
6283 
6284   PetscFunctionBegin;
6285   PetscCall(PetscDSCreate(PetscObjectComm((PetscObject)ds), &dsNew));
6286   PetscCall(PetscDSCopy(ds, minDegree, maxDegree, dm, dsNew));
6287   if (dsIn) {
6288     PetscCall(PetscDSCreate(PetscObjectComm((PetscObject)dsIn), &dsInNew));
6289     PetscCall(PetscDSCopy(dsIn, minDegree, maxDegree, dm, dsInNew));
6290   }
6291   PetscCall(DMSetRegionDS(dm, label, fields, dsNew, dsInNew));
6292   PetscCall(PetscDSDestroy(&dsNew));
6293   PetscCall(PetscDSDestroy(&dsInNew));
6294   PetscFunctionReturn(PETSC_SUCCESS);
6295 }
6296 
6297 /*@
6298   DMCopyDS - Copy the discrete systems for the `DM` into another `DM`
6299 
6300   Collective
6301 
6302   Input Parameters:
6303 + dm        - The `DM`
6304 . minDegree - Minimum degree for a discretization, or `PETSC_DETERMINE` for no limit
6305 - maxDegree - Maximum degree for a discretization, or `PETSC_DETERMINE` for no limit
6306 
6307   Output Parameter:
6308 . newdm - The `DM`
6309 
6310   Level: advanced
6311 
6312 .seealso: [](ch_dmbase), `DM`, `DMCopyFields()`, `DMAddField()`, `DMGetDS()`, `DMGetCellDS()`, `DMGetRegionDS()`, `DMSetRegionDS()`
6313 @*/
6314 PetscErrorCode DMCopyDS(DM dm, PetscInt minDegree, PetscInt maxDegree, DM newdm)
6315 {
6316   PetscInt Nds, s;
6317 
6318   PetscFunctionBegin;
6319   if (dm == newdm) PetscFunctionReturn(PETSC_SUCCESS);
6320   PetscCall(DMGetNumDS(dm, &Nds));
6321   PetscCall(DMClearDS(newdm));
6322   for (s = 0; s < Nds; ++s) {
6323     DMLabel  label;
6324     IS       fields;
6325     PetscDS  ds, dsIn, newds;
6326     PetscInt Nbd, bd;
6327 
6328     PetscCall(DMGetRegionNumDS(dm, s, &label, &fields, &ds, &dsIn));
6329     /* TODO: We need to change all keys from labels in the old DM to labels in the new DM */
6330     PetscCall(DMTransferDS_Internal(newdm, label, fields, minDegree, maxDegree, ds, dsIn));
6331     /* Complete new labels in the new DS */
6332     PetscCall(DMGetRegionDS(newdm, label, NULL, &newds, NULL));
6333     PetscCall(PetscDSGetNumBoundary(newds, &Nbd));
6334     for (bd = 0; bd < Nbd; ++bd) {
6335       PetscWeakForm wf;
6336       DMLabel       label;
6337       PetscInt      field;
6338 
6339       PetscCall(PetscDSGetBoundary(newds, bd, &wf, NULL, NULL, &label, NULL, NULL, &field, NULL, NULL, NULL, NULL, NULL));
6340       PetscCall(PetscWeakFormReplaceLabel(wf, label));
6341     }
6342   }
6343   PetscCall(DMCompleteBCLabels_Internal(newdm));
6344   PetscFunctionReturn(PETSC_SUCCESS);
6345 }
6346 
6347 /*@
6348   DMCopyDisc - Copy the fields and discrete systems for the `DM` into another `DM`
6349 
6350   Collective
6351 
6352   Input Parameter:
6353 . dm - The `DM`
6354 
6355   Output Parameter:
6356 . newdm - The `DM`
6357 
6358   Level: advanced
6359 
6360   Developer Note:
6361   Really ugly name, nothing in PETSc is called a `Disc` plus it is an ugly abbreviation
6362 
6363 .seealso: [](ch_dmbase), `DM`, `DMCopyFields()`, `DMCopyDS()`
6364 @*/
6365 PetscErrorCode DMCopyDisc(DM dm, DM newdm)
6366 {
6367   PetscFunctionBegin;
6368   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, newdm));
6369   PetscCall(DMCopyDS(dm, PETSC_DETERMINE, PETSC_DETERMINE, newdm));
6370   PetscFunctionReturn(PETSC_SUCCESS);
6371 }
6372 
6373 /*@
6374   DMGetDimension - Return the topological dimension of the `DM`
6375 
6376   Not Collective
6377 
6378   Input Parameter:
6379 . dm - The `DM`
6380 
6381   Output Parameter:
6382 . dim - The topological dimension
6383 
6384   Level: beginner
6385 
6386 .seealso: [](ch_dmbase), `DM`, `DMSetDimension()`, `DMCreate()`
6387 @*/
6388 PetscErrorCode DMGetDimension(DM dm, PetscInt *dim)
6389 {
6390   PetscFunctionBegin;
6391   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6392   PetscAssertPointer(dim, 2);
6393   *dim = dm->dim;
6394   PetscFunctionReturn(PETSC_SUCCESS);
6395 }
6396 
6397 /*@
6398   DMSetDimension - Set the topological dimension of the `DM`
6399 
6400   Collective
6401 
6402   Input Parameters:
6403 + dm  - The `DM`
6404 - dim - The topological dimension
6405 
6406   Level: beginner
6407 
6408 .seealso: [](ch_dmbase), `DM`, `DMGetDimension()`, `DMCreate()`
6409 @*/
6410 PetscErrorCode DMSetDimension(DM dm, PetscInt dim)
6411 {
6412   PetscDS  ds;
6413   PetscInt Nds, n;
6414 
6415   PetscFunctionBegin;
6416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6417   PetscValidLogicalCollectiveInt(dm, dim, 2);
6418   dm->dim = dim;
6419   if (dm->dim >= 0) {
6420     PetscCall(DMGetNumDS(dm, &Nds));
6421     for (n = 0; n < Nds; ++n) {
6422       PetscCall(DMGetRegionNumDS(dm, n, NULL, NULL, &ds, NULL));
6423       if (ds->dimEmbed < 0) PetscCall(PetscDSSetCoordinateDimension(ds, dim));
6424     }
6425   }
6426   PetscFunctionReturn(PETSC_SUCCESS);
6427 }
6428 
6429 /*@
6430   DMGetDimPoints - Get the half-open interval for all points of a given dimension
6431 
6432   Collective
6433 
6434   Input Parameters:
6435 + dm  - the `DM`
6436 - dim - the dimension
6437 
6438   Output Parameters:
6439 + pStart - The first point of the given dimension
6440 - pEnd   - The first point following points of the given dimension
6441 
6442   Level: intermediate
6443 
6444   Note:
6445   The points are vertices in the Hasse diagram encoding the topology. This is explained in
6446   https://arxiv.org/abs/0908.4427. If no points exist of this dimension in the storage scheme,
6447   then the interval is empty.
6448 
6449 .seealso: [](ch_dmbase), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
6450 @*/
6451 PetscErrorCode DMGetDimPoints(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
6452 {
6453   PetscInt d;
6454 
6455   PetscFunctionBegin;
6456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6457   PetscCall(DMGetDimension(dm, &d));
6458   PetscCheck((dim >= 0) && (dim <= d), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension %" PetscInt_FMT, dim);
6459   PetscUseTypeMethod(dm, getdimpoints, dim, pStart, pEnd);
6460   PetscFunctionReturn(PETSC_SUCCESS);
6461 }
6462 
6463 /*@
6464   DMGetOutputDM - Retrieve the `DM` associated with the layout for output
6465 
6466   Collective
6467 
6468   Input Parameter:
6469 . dm - The original `DM`
6470 
6471   Output Parameter:
6472 . odm - The `DM` which provides the layout for output
6473 
6474   Level: intermediate
6475 
6476   Note:
6477   In some situations the vector obtained with `DMCreateGlobalVector()` excludes points for degrees of freedom that are associated with fixed (Dirichelet) boundary
6478   conditions since the algebraic solver does not solve for those variables. The output `DM` includes these excluded points and its global vector contains the
6479   locations for those dof so that they can be output to a file or other viewer along with the unconstrained dof.
6480 
6481 .seealso: [](ch_dmbase), `DM`, `VecView()`, `DMGetGlobalSection()`, `DMCreateGlobalVector()`, `PetscSectionHasConstraints()`, `DMSetGlobalSection()`
6482 @*/
6483 PetscErrorCode DMGetOutputDM(DM dm, DM *odm)
6484 {
6485   PetscSection section;
6486   IS           perm;
6487   PetscBool    hasConstraints, newDM, gnewDM;
6488 
6489   PetscFunctionBegin;
6490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6491   PetscAssertPointer(odm, 2);
6492   PetscCall(DMGetLocalSection(dm, &section));
6493   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
6494   PetscCall(PetscSectionGetPermutation(section, &perm));
6495   newDM = hasConstraints || perm ? PETSC_TRUE : PETSC_FALSE;
6496   PetscCallMPI(MPIU_Allreduce(&newDM, &gnewDM, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
6497   if (!gnewDM) {
6498     *odm = dm;
6499     PetscFunctionReturn(PETSC_SUCCESS);
6500   }
6501   if (!dm->dmBC) {
6502     PetscSection newSection, gsection;
6503     PetscSF      sf;
6504     PetscBool    usePerm = dm->ignorePermOutput ? PETSC_FALSE : PETSC_TRUE;
6505 
6506     PetscCall(DMClone(dm, &dm->dmBC));
6507     PetscCall(DMCopyDisc(dm, dm->dmBC));
6508     PetscCall(PetscSectionClone(section, &newSection));
6509     PetscCall(DMSetLocalSection(dm->dmBC, newSection));
6510     PetscCall(PetscSectionDestroy(&newSection));
6511     PetscCall(DMGetPointSF(dm->dmBC, &sf));
6512     PetscCall(PetscSectionCreateGlobalSection(section, sf, usePerm, PETSC_TRUE, PETSC_FALSE, &gsection));
6513     PetscCall(DMSetGlobalSection(dm->dmBC, gsection));
6514     PetscCall(PetscSectionDestroy(&gsection));
6515   }
6516   *odm = dm->dmBC;
6517   PetscFunctionReturn(PETSC_SUCCESS);
6518 }
6519 
6520 /*@
6521   DMGetOutputSequenceNumber - Retrieve the sequence number/value for output
6522 
6523   Input Parameter:
6524 . dm - The original `DM`
6525 
6526   Output Parameters:
6527 + num - The output sequence number
6528 - val - The output sequence value
6529 
6530   Level: intermediate
6531 
6532   Note:
6533   This is intended for output that should appear in sequence, for instance
6534   a set of timesteps in an `PETSCVIEWERHDF5` file, or a set of realizations of a stochastic system.
6535 
6536   Developer Note:
6537   The `DM` serves as a convenient place to store the current iteration value. The iteration is not
6538   not directly related to the `DM`.
6539 
6540 .seealso: [](ch_dmbase), `DM`, `VecView()`
6541 @*/
6542 PetscErrorCode DMGetOutputSequenceNumber(DM dm, PetscInt *num, PetscReal *val)
6543 {
6544   PetscFunctionBegin;
6545   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6546   if (num) {
6547     PetscAssertPointer(num, 2);
6548     *num = dm->outputSequenceNum;
6549   }
6550   if (val) {
6551     PetscAssertPointer(val, 3);
6552     *val = dm->outputSequenceVal;
6553   }
6554   PetscFunctionReturn(PETSC_SUCCESS);
6555 }
6556 
6557 /*@
6558   DMSetOutputSequenceNumber - Set the sequence number/value for output
6559 
6560   Input Parameters:
6561 + dm  - The original `DM`
6562 . num - The output sequence number
6563 - val - The output sequence value
6564 
6565   Level: intermediate
6566 
6567   Note:
6568   This is intended for output that should appear in sequence, for instance
6569   a set of timesteps in an `PETSCVIEWERHDF5` file, or a set of realizations of a stochastic system.
6570 
6571 .seealso: [](ch_dmbase), `DM`, `VecView()`
6572 @*/
6573 PetscErrorCode DMSetOutputSequenceNumber(DM dm, PetscInt num, PetscReal val)
6574 {
6575   PetscFunctionBegin;
6576   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6577   dm->outputSequenceNum = num;
6578   dm->outputSequenceVal = val;
6579   PetscFunctionReturn(PETSC_SUCCESS);
6580 }
6581 
6582 /*@
6583   DMOutputSequenceLoad - Retrieve the sequence value from a `PetscViewer`
6584 
6585   Input Parameters:
6586 + dm     - The original `DM`
6587 . viewer - The `PetscViewer` to get it from
6588 . name   - The sequence name
6589 - num    - The output sequence number
6590 
6591   Output Parameter:
6592 . val - The output sequence value
6593 
6594   Level: intermediate
6595 
6596   Note:
6597   This is intended for output that should appear in sequence, for instance
6598   a set of timesteps in an `PETSCVIEWERHDF5` file, or a set of realizations of a stochastic system.
6599 
6600   Developer Note:
6601   It is unclear at the user API level why a `DM` is needed as input
6602 
6603 .seealso: [](ch_dmbase), `DM`, `DMGetOutputSequenceNumber()`, `DMSetOutputSequenceNumber()`, `VecView()`
6604 @*/
6605 PetscErrorCode DMOutputSequenceLoad(DM dm, PetscViewer viewer, const char name[], PetscInt num, PetscReal *val)
6606 {
6607   PetscBool ishdf5;
6608 
6609   PetscFunctionBegin;
6610   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6611   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
6612   PetscAssertPointer(name, 3);
6613   PetscAssertPointer(val, 5);
6614   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
6615   if (ishdf5) {
6616 #if defined(PETSC_HAVE_HDF5)
6617     PetscScalar value;
6618 
6619     PetscCall(DMSequenceLoad_HDF5_Internal(dm, name, num, &value, viewer));
6620     *val = PetscRealPart(value);
6621 #endif
6622   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid viewer; open viewer with PetscViewerHDF5Open()");
6623   PetscFunctionReturn(PETSC_SUCCESS);
6624 }
6625 
6626 /*@
6627   DMGetOutputSequenceLength - Retrieve the number of sequence values from a `PetscViewer`
6628 
6629   Input Parameters:
6630 + dm     - The original `DM`
6631 . viewer - The `PetscViewer` to get it from
6632 - name   - The sequence name
6633 
6634   Output Parameter:
6635 . len - The length of the output sequence
6636 
6637   Level: intermediate
6638 
6639   Note:
6640   This is intended for output that should appear in sequence, for instance
6641   a set of timesteps in an `PETSCVIEWERHDF5` file, or a set of realizations of a stochastic system.
6642 
6643   Developer Note:
6644   It is unclear at the user API level why a `DM` is needed as input
6645 
6646 .seealso: [](ch_dmbase), `DM`, `DMGetOutputSequenceNumber()`, `DMSetOutputSequenceNumber()`, `VecView()`
6647 @*/
6648 PetscErrorCode DMGetOutputSequenceLength(DM dm, PetscViewer viewer, const char name[], PetscInt *len)
6649 {
6650   PetscBool ishdf5;
6651 
6652   PetscFunctionBegin;
6653   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6654   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
6655   PetscAssertPointer(name, 3);
6656   PetscAssertPointer(len, 4);
6657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
6658   if (ishdf5) {
6659 #if defined(PETSC_HAVE_HDF5)
6660     PetscCall(DMSequenceGetLength_HDF5_Internal(dm, name, len, viewer));
6661 #endif
6662   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid viewer; open viewer with PetscViewerHDF5Open()");
6663   PetscFunctionReturn(PETSC_SUCCESS);
6664 }
6665 
6666 /*@
6667   DMGetUseNatural - Get the flag for creating a mapping to the natural order when a `DM` is (re)distributed in parallel
6668 
6669   Not Collective
6670 
6671   Input Parameter:
6672 . dm - The `DM`
6673 
6674   Output Parameter:
6675 . useNatural - `PETSC_TRUE` to build the mapping to a natural order during distribution
6676 
6677   Level: beginner
6678 
6679 .seealso: [](ch_dmbase), `DM`, `DMSetUseNatural()`, `DMCreate()`
6680 @*/
6681 PetscErrorCode DMGetUseNatural(DM dm, PetscBool *useNatural)
6682 {
6683   PetscFunctionBegin;
6684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6685   PetscAssertPointer(useNatural, 2);
6686   *useNatural = dm->useNatural;
6687   PetscFunctionReturn(PETSC_SUCCESS);
6688 }
6689 
6690 /*@
6691   DMSetUseNatural - Set the flag for creating a mapping to the natural order when a `DM` is (re)distributed in parallel
6692 
6693   Collective
6694 
6695   Input Parameters:
6696 + dm         - The `DM`
6697 - useNatural - `PETSC_TRUE` to build the mapping to a natural order during distribution
6698 
6699   Level: beginner
6700 
6701   Note:
6702   This also causes the map to be build after `DMCreateSubDM()` and `DMCreateSuperDM()`
6703 
6704 .seealso: [](ch_dmbase), `DM`, `DMGetUseNatural()`, `DMCreate()`, `DMPlexDistribute()`, `DMCreateSubDM()`, `DMCreateSuperDM()`
6705 @*/
6706 PetscErrorCode DMSetUseNatural(DM dm, PetscBool useNatural)
6707 {
6708   PetscFunctionBegin;
6709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6710   PetscValidLogicalCollectiveBool(dm, useNatural, 2);
6711   dm->useNatural = useNatural;
6712   PetscFunctionReturn(PETSC_SUCCESS);
6713 }
6714 
6715 /*@
6716   DMCreateLabel - Create a label of the given name if it does not already exist in the `DM`
6717 
6718   Not Collective
6719 
6720   Input Parameters:
6721 + dm   - The `DM` object
6722 - name - The label name
6723 
6724   Level: intermediate
6725 
6726 .seealso: [](ch_dmbase), `DM`, `DMLabelCreate()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6727 @*/
6728 PetscErrorCode DMCreateLabel(DM dm, const char name[])
6729 {
6730   PetscBool flg;
6731   DMLabel   label;
6732 
6733   PetscFunctionBegin;
6734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6735   PetscAssertPointer(name, 2);
6736   PetscCall(DMHasLabel(dm, name, &flg));
6737   if (!flg) {
6738     PetscCall(DMLabelCreate(PETSC_COMM_SELF, name, &label));
6739     PetscCall(DMAddLabel(dm, label));
6740     PetscCall(DMLabelDestroy(&label));
6741   }
6742   PetscFunctionReturn(PETSC_SUCCESS);
6743 }
6744 
6745 /*@
6746   DMCreateLabelAtIndex - Create a label of the given name at the given index. If it already exists in the `DM`, move it to this index.
6747 
6748   Not Collective
6749 
6750   Input Parameters:
6751 + dm   - The `DM` object
6752 . l    - The index for the label
6753 - name - The label name
6754 
6755   Level: intermediate
6756 
6757 .seealso: [](ch_dmbase), `DM`, `DMCreateLabel()`, `DMLabelCreate()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6758 @*/
6759 PetscErrorCode DMCreateLabelAtIndex(DM dm, PetscInt l, const char name[])
6760 {
6761   DMLabelLink orig, prev = NULL;
6762   DMLabel     label;
6763   PetscInt    Nl, m;
6764   PetscBool   flg, match;
6765   const char *lname;
6766 
6767   PetscFunctionBegin;
6768   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6769   PetscAssertPointer(name, 3);
6770   PetscCall(DMHasLabel(dm, name, &flg));
6771   if (!flg) {
6772     PetscCall(DMLabelCreate(PETSC_COMM_SELF, name, &label));
6773     PetscCall(DMAddLabel(dm, label));
6774     PetscCall(DMLabelDestroy(&label));
6775   }
6776   PetscCall(DMGetNumLabels(dm, &Nl));
6777   PetscCheck(l < Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label index %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", l, Nl);
6778   for (m = 0, orig = dm->labels; m < Nl; ++m, prev = orig, orig = orig->next) {
6779     PetscCall(PetscObjectGetName((PetscObject)orig->label, &lname));
6780     PetscCall(PetscStrcmp(name, lname, &match));
6781     if (match) break;
6782   }
6783   if (m == l) PetscFunctionReturn(PETSC_SUCCESS);
6784   if (!m) dm->labels = orig->next;
6785   else prev->next = orig->next;
6786   if (!l) {
6787     orig->next = dm->labels;
6788     dm->labels = orig;
6789   } else {
6790     for (m = 0, prev = dm->labels; m < l - 1; ++m, prev = prev->next);
6791     orig->next = prev->next;
6792     prev->next = orig;
6793   }
6794   PetscFunctionReturn(PETSC_SUCCESS);
6795 }
6796 
6797 /*@
6798   DMGetLabelValue - Get the value in a `DMLabel` for the given point, with -1 as the default
6799 
6800   Not Collective
6801 
6802   Input Parameters:
6803 + dm    - The `DM` object
6804 . name  - The label name
6805 - point - The mesh point
6806 
6807   Output Parameter:
6808 . value - The label value for this point, or -1 if the point is not in the label
6809 
6810   Level: beginner
6811 
6812 .seealso: [](ch_dmbase), `DM`, `DMLabelGetValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6813 @*/
6814 PetscErrorCode DMGetLabelValue(DM dm, const char name[], PetscInt point, PetscInt *value)
6815 {
6816   DMLabel label;
6817 
6818   PetscFunctionBegin;
6819   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6820   PetscAssertPointer(name, 2);
6821   PetscCall(DMGetLabel(dm, name, &label));
6822   PetscCheck(label, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "No label named %s was found", name);
6823   PetscCall(DMLabelGetValue(label, point, value));
6824   PetscFunctionReturn(PETSC_SUCCESS);
6825 }
6826 
6827 /*@
6828   DMSetLabelValue - Add a point to a `DMLabel` with given value
6829 
6830   Not Collective
6831 
6832   Input Parameters:
6833 + dm    - The `DM` object
6834 . name  - The label name
6835 . point - The mesh point
6836 - value - The label value for this point
6837 
6838   Output Parameter:
6839 
6840   Level: beginner
6841 
6842 .seealso: [](ch_dmbase), `DM`, `DMLabelSetValue()`, `DMGetStratumIS()`, `DMClearLabelValue()`
6843 @*/
6844 PetscErrorCode DMSetLabelValue(DM dm, const char name[], PetscInt point, PetscInt value)
6845 {
6846   DMLabel label;
6847 
6848   PetscFunctionBegin;
6849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6850   PetscAssertPointer(name, 2);
6851   PetscCall(DMGetLabel(dm, name, &label));
6852   if (!label) {
6853     PetscCall(DMCreateLabel(dm, name));
6854     PetscCall(DMGetLabel(dm, name, &label));
6855   }
6856   PetscCall(DMLabelSetValue(label, point, value));
6857   PetscFunctionReturn(PETSC_SUCCESS);
6858 }
6859 
6860 /*@
6861   DMClearLabelValue - Remove a point from a `DMLabel` with given value
6862 
6863   Not Collective
6864 
6865   Input Parameters:
6866 + dm    - The `DM` object
6867 . name  - The label name
6868 . point - The mesh point
6869 - value - The label value for this point
6870 
6871   Level: beginner
6872 
6873 .seealso: [](ch_dmbase), `DM`, `DMLabelClearValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6874 @*/
6875 PetscErrorCode DMClearLabelValue(DM dm, const char name[], PetscInt point, PetscInt value)
6876 {
6877   DMLabel label;
6878 
6879   PetscFunctionBegin;
6880   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6881   PetscAssertPointer(name, 2);
6882   PetscCall(DMGetLabel(dm, name, &label));
6883   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
6884   PetscCall(DMLabelClearValue(label, point, value));
6885   PetscFunctionReturn(PETSC_SUCCESS);
6886 }
6887 
6888 /*@
6889   DMGetLabelSize - Get the value of `DMLabelGetNumValues()` of a `DMLabel` in the `DM`
6890 
6891   Not Collective
6892 
6893   Input Parameters:
6894 + dm   - The `DM` object
6895 - name - The label name
6896 
6897   Output Parameter:
6898 . size - The number of different integer ids, or 0 if the label does not exist
6899 
6900   Level: beginner
6901 
6902   Developer Note:
6903   This should be renamed to something like `DMGetLabelNumValues()` or removed.
6904 
6905 .seealso: [](ch_dmbase), `DM`, `DMLabelGetNumValues()`, `DMSetLabelValue()`, `DMGetLabel()`
6906 @*/
6907 PetscErrorCode DMGetLabelSize(DM dm, const char name[], PetscInt *size)
6908 {
6909   DMLabel label;
6910 
6911   PetscFunctionBegin;
6912   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6913   PetscAssertPointer(name, 2);
6914   PetscAssertPointer(size, 3);
6915   PetscCall(DMGetLabel(dm, name, &label));
6916   *size = 0;
6917   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
6918   PetscCall(DMLabelGetNumValues(label, size));
6919   PetscFunctionReturn(PETSC_SUCCESS);
6920 }
6921 
6922 /*@
6923   DMGetLabelIdIS - Get the `DMLabelGetValueIS()` from a `DMLabel` in the `DM`
6924 
6925   Not Collective
6926 
6927   Input Parameters:
6928 + dm   - The `DM` object
6929 - name - The label name
6930 
6931   Output Parameter:
6932 . ids - The integer ids, or `NULL` if the label does not exist
6933 
6934   Level: beginner
6935 
6936 .seealso: [](ch_dmbase), `DM`, `DMLabelGetValueIS()`, `DMGetLabelSize()`
6937 @*/
6938 PetscErrorCode DMGetLabelIdIS(DM dm, const char name[], IS *ids)
6939 {
6940   DMLabel label;
6941 
6942   PetscFunctionBegin;
6943   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6944   PetscAssertPointer(name, 2);
6945   PetscAssertPointer(ids, 3);
6946   PetscCall(DMGetLabel(dm, name, &label));
6947   *ids = NULL;
6948   if (label) {
6949     PetscCall(DMLabelGetValueIS(label, ids));
6950   } else {
6951     /* returning an empty IS */
6952     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 0, NULL, PETSC_USE_POINTER, ids));
6953   }
6954   PetscFunctionReturn(PETSC_SUCCESS);
6955 }
6956 
6957 /*@
6958   DMGetStratumSize - Get the number of points in a label stratum
6959 
6960   Not Collective
6961 
6962   Input Parameters:
6963 + dm    - The `DM` object
6964 . name  - The label name of the stratum
6965 - value - The stratum value
6966 
6967   Output Parameter:
6968 . size - The number of points, also called the stratum size
6969 
6970   Level: beginner
6971 
6972 .seealso: [](ch_dmbase), `DM`, `DMLabelGetStratumSize()`, `DMGetLabelSize()`, `DMGetLabelIds()`
6973 @*/
6974 PetscErrorCode DMGetStratumSize(DM dm, const char name[], PetscInt value, PetscInt *size)
6975 {
6976   DMLabel label;
6977 
6978   PetscFunctionBegin;
6979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6980   PetscAssertPointer(name, 2);
6981   PetscAssertPointer(size, 4);
6982   PetscCall(DMGetLabel(dm, name, &label));
6983   *size = 0;
6984   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
6985   PetscCall(DMLabelGetStratumSize(label, value, size));
6986   PetscFunctionReturn(PETSC_SUCCESS);
6987 }
6988 
6989 /*@
6990   DMGetStratumIS - Get the points in a label stratum
6991 
6992   Not Collective
6993 
6994   Input Parameters:
6995 + dm    - The `DM` object
6996 . name  - The label name
6997 - value - The stratum value
6998 
6999   Output Parameter:
7000 . points - The stratum points, or `NULL` if the label does not exist or does not have that value
7001 
7002   Level: beginner
7003 
7004 .seealso: [](ch_dmbase), `DM`, `DMLabelGetStratumIS()`, `DMGetStratumSize()`
7005 @*/
7006 PetscErrorCode DMGetStratumIS(DM dm, const char name[], PetscInt value, IS *points)
7007 {
7008   DMLabel label;
7009 
7010   PetscFunctionBegin;
7011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7012   PetscAssertPointer(name, 2);
7013   PetscAssertPointer(points, 4);
7014   PetscCall(DMGetLabel(dm, name, &label));
7015   *points = NULL;
7016   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
7017   PetscCall(DMLabelGetStratumIS(label, value, points));
7018   PetscFunctionReturn(PETSC_SUCCESS);
7019 }
7020 
7021 /*@
7022   DMSetStratumIS - Set the points in a label stratum
7023 
7024   Not Collective
7025 
7026   Input Parameters:
7027 + dm     - The `DM` object
7028 . name   - The label name
7029 . value  - The stratum value
7030 - points - The stratum points
7031 
7032   Level: beginner
7033 
7034 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMClearLabelStratum()`, `DMLabelClearStratum()`, `DMLabelSetStratumIS()`, `DMGetStratumSize()`
7035 @*/
7036 PetscErrorCode DMSetStratumIS(DM dm, const char name[], PetscInt value, IS points)
7037 {
7038   DMLabel label;
7039 
7040   PetscFunctionBegin;
7041   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7042   PetscAssertPointer(name, 2);
7043   PetscValidHeaderSpecific(points, IS_CLASSID, 4);
7044   PetscCall(DMGetLabel(dm, name, &label));
7045   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
7046   PetscCall(DMLabelSetStratumIS(label, value, points));
7047   PetscFunctionReturn(PETSC_SUCCESS);
7048 }
7049 
7050 /*@
7051   DMClearLabelStratum - Remove all points from a stratum from a `DMLabel`
7052 
7053   Not Collective
7054 
7055   Input Parameters:
7056 + dm    - The `DM` object
7057 . name  - The label name
7058 - value - The label value for this point
7059 
7060   Output Parameter:
7061 
7062   Level: beginner
7063 
7064 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMLabelClearStratum()`, `DMSetLabelValue()`, `DMGetStratumIS()`, `DMClearLabelValue()`
7065 @*/
7066 PetscErrorCode DMClearLabelStratum(DM dm, const char name[], PetscInt value)
7067 {
7068   DMLabel label;
7069 
7070   PetscFunctionBegin;
7071   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7072   PetscAssertPointer(name, 2);
7073   PetscCall(DMGetLabel(dm, name, &label));
7074   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
7075   PetscCall(DMLabelClearStratum(label, value));
7076   PetscFunctionReturn(PETSC_SUCCESS);
7077 }
7078 
7079 /*@
7080   DMGetNumLabels - Return the number of labels defined by on the `DM`
7081 
7082   Not Collective
7083 
7084   Input Parameter:
7085 . dm - The `DM` object
7086 
7087   Output Parameter:
7088 . numLabels - the number of Labels
7089 
7090   Level: intermediate
7091 
7092 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetLabelByNum()`, `DMGetLabelName()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7093 @*/
7094 PetscErrorCode DMGetNumLabels(DM dm, PetscInt *numLabels)
7095 {
7096   DMLabelLink next = dm->labels;
7097   PetscInt    n    = 0;
7098 
7099   PetscFunctionBegin;
7100   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7101   PetscAssertPointer(numLabels, 2);
7102   while (next) {
7103     ++n;
7104     next = next->next;
7105   }
7106   *numLabels = n;
7107   PetscFunctionReturn(PETSC_SUCCESS);
7108 }
7109 
7110 /*@
7111   DMGetLabelName - Return the name of nth label
7112 
7113   Not Collective
7114 
7115   Input Parameters:
7116 + dm - The `DM` object
7117 - n  - the label number
7118 
7119   Output Parameter:
7120 . name - the label name
7121 
7122   Level: intermediate
7123 
7124   Developer Note:
7125   Some of the functions that appropriate on labels using their number have the suffix ByNum, others do not.
7126 
7127 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetLabelByNum()`, `DMGetLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7128 @*/
7129 PetscErrorCode DMGetLabelName(DM dm, PetscInt n, const char *name[])
7130 {
7131   DMLabelLink next = dm->labels;
7132   PetscInt    l    = 0;
7133 
7134   PetscFunctionBegin;
7135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7136   PetscAssertPointer(name, 3);
7137   while (next) {
7138     if (l == n) {
7139       PetscCall(PetscObjectGetName((PetscObject)next->label, name));
7140       PetscFunctionReturn(PETSC_SUCCESS);
7141     }
7142     ++l;
7143     next = next->next;
7144   }
7145   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label %" PetscInt_FMT " does not exist in this DM", n);
7146 }
7147 
7148 /*@
7149   DMHasLabel - Determine whether the `DM` has a label of a given name
7150 
7151   Not Collective
7152 
7153   Input Parameters:
7154 + dm   - The `DM` object
7155 - name - The label name
7156 
7157   Output Parameter:
7158 . hasLabel - `PETSC_TRUE` if the label is present
7159 
7160   Level: intermediate
7161 
7162 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetLabel()`, `DMGetLabelByNum()`, `DMCreateLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7163 @*/
7164 PetscErrorCode DMHasLabel(DM dm, const char name[], PetscBool *hasLabel)
7165 {
7166   DMLabelLink next = dm->labels;
7167   const char *lname;
7168 
7169   PetscFunctionBegin;
7170   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7171   PetscAssertPointer(name, 2);
7172   PetscAssertPointer(hasLabel, 3);
7173   *hasLabel = PETSC_FALSE;
7174   while (next) {
7175     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7176     PetscCall(PetscStrcmp(name, lname, hasLabel));
7177     if (*hasLabel) break;
7178     next = next->next;
7179   }
7180   PetscFunctionReturn(PETSC_SUCCESS);
7181 }
7182 
7183 // PetscClangLinter pragma ignore: -fdoc-section-header-unknown
7184 /*@
7185   DMGetLabel - Return the label of a given name, or `NULL`, from a `DM`
7186 
7187   Not Collective
7188 
7189   Input Parameters:
7190 + dm   - The `DM` object
7191 - name - The label name
7192 
7193   Output Parameter:
7194 . label - The `DMLabel`, or `NULL` if the label is absent
7195 
7196   Default labels in a `DMPLEX`:
7197 + "depth"       - Holds the depth (co-dimension) of each mesh point
7198 . "celltype"    - Holds the topological type of each cell
7199 . "ghost"       - If the DM is distributed with overlap, this marks the cells and faces in the overlap
7200 . "Cell Sets"   - Mirrors the cell sets defined by GMsh and ExodusII
7201 . "Face Sets"   - Mirrors the face sets defined by GMsh and ExodusII
7202 - "Vertex Sets" - Mirrors the vertex sets defined by GMsh
7203 
7204   Level: intermediate
7205 
7206 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMHasLabel()`, `DMGetLabelByNum()`, `DMAddLabel()`, `DMCreateLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetCellType()`
7207 @*/
7208 PetscErrorCode DMGetLabel(DM dm, const char name[], DMLabel *label)
7209 {
7210   DMLabelLink next = dm->labels;
7211   PetscBool   hasLabel;
7212   const char *lname;
7213 
7214   PetscFunctionBegin;
7215   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7216   PetscAssertPointer(name, 2);
7217   PetscAssertPointer(label, 3);
7218   *label = NULL;
7219   while (next) {
7220     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7221     PetscCall(PetscStrcmp(name, lname, &hasLabel));
7222     if (hasLabel) {
7223       *label = next->label;
7224       break;
7225     }
7226     next = next->next;
7227   }
7228   PetscFunctionReturn(PETSC_SUCCESS);
7229 }
7230 
7231 /*@
7232   DMGetLabelByNum - Return the nth label on a `DM`
7233 
7234   Not Collective
7235 
7236   Input Parameters:
7237 + dm - The `DM` object
7238 - n  - the label number
7239 
7240   Output Parameter:
7241 . label - the label
7242 
7243   Level: intermediate
7244 
7245 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMAddLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7246 @*/
7247 PetscErrorCode DMGetLabelByNum(DM dm, PetscInt n, DMLabel *label)
7248 {
7249   DMLabelLink next = dm->labels;
7250   PetscInt    l    = 0;
7251 
7252   PetscFunctionBegin;
7253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7254   PetscAssertPointer(label, 3);
7255   while (next) {
7256     if (l == n) {
7257       *label = next->label;
7258       PetscFunctionReturn(PETSC_SUCCESS);
7259     }
7260     ++l;
7261     next = next->next;
7262   }
7263   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label %" PetscInt_FMT " does not exist in this DM", n);
7264 }
7265 
7266 /*@
7267   DMAddLabel - Add the label to this `DM`
7268 
7269   Not Collective
7270 
7271   Input Parameters:
7272 + dm    - The `DM` object
7273 - label - The `DMLabel`
7274 
7275   Level: developer
7276 
7277 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7278 @*/
7279 PetscErrorCode DMAddLabel(DM dm, DMLabel label)
7280 {
7281   DMLabelLink l, *p, tmpLabel;
7282   PetscBool   hasLabel;
7283   const char *lname;
7284   PetscBool   flg;
7285 
7286   PetscFunctionBegin;
7287   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7288   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
7289   PetscCall(DMHasLabel(dm, lname, &hasLabel));
7290   PetscCheck(!hasLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label %s already exists in this DM", lname);
7291   PetscCall(PetscCalloc1(1, &tmpLabel));
7292   tmpLabel->label  = label;
7293   tmpLabel->output = PETSC_TRUE;
7294   for (p = &dm->labels; (l = *p); p = &l->next) { }
7295   *p = tmpLabel;
7296   PetscCall(PetscObjectReference((PetscObject)label));
7297   PetscCall(PetscStrcmp(lname, "depth", &flg));
7298   if (flg) dm->depthLabel = label;
7299   PetscCall(PetscStrcmp(lname, "celltype", &flg));
7300   if (flg) dm->celltypeLabel = label;
7301   PetscFunctionReturn(PETSC_SUCCESS);
7302 }
7303 
7304 // PetscClangLinter pragma ignore: -fdoc-section-header-unknown
7305 /*@
7306   DMSetLabel - Replaces the label of a given name, or ignores it if the name is not present
7307 
7308   Not Collective
7309 
7310   Input Parameters:
7311 + dm    - The `DM` object
7312 - label - The `DMLabel`, having the same name, to substitute
7313 
7314   Default labels in a `DMPLEX`:
7315 + "depth"       - Holds the depth (co-dimension) of each mesh point
7316 . "celltype"    - Holds the topological type of each cell
7317 . "ghost"       - If the DM is distributed with overlap, this marks the cells and faces in the overlap
7318 . "Cell Sets"   - Mirrors the cell sets defined by GMsh and ExodusII
7319 . "Face Sets"   - Mirrors the face sets defined by GMsh and ExodusII
7320 - "Vertex Sets" - Mirrors the vertex sets defined by GMsh
7321 
7322   Level: intermediate
7323 
7324 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetCellType()`
7325 @*/
7326 PetscErrorCode DMSetLabel(DM dm, DMLabel label)
7327 {
7328   DMLabelLink next = dm->labels;
7329   PetscBool   hasLabel, flg;
7330   const char *name, *lname;
7331 
7332   PetscFunctionBegin;
7333   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7334   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
7335   PetscCall(PetscObjectGetName((PetscObject)label, &name));
7336   while (next) {
7337     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7338     PetscCall(PetscStrcmp(name, lname, &hasLabel));
7339     if (hasLabel) {
7340       PetscCall(PetscObjectReference((PetscObject)label));
7341       PetscCall(PetscStrcmp(lname, "depth", &flg));
7342       if (flg) dm->depthLabel = label;
7343       PetscCall(PetscStrcmp(lname, "celltype", &flg));
7344       if (flg) dm->celltypeLabel = label;
7345       PetscCall(DMLabelDestroy(&next->label));
7346       next->label = label;
7347       break;
7348     }
7349     next = next->next;
7350   }
7351   PetscFunctionReturn(PETSC_SUCCESS);
7352 }
7353 
7354 /*@
7355   DMRemoveLabel - Remove the label given by name from this `DM`
7356 
7357   Not Collective
7358 
7359   Input Parameters:
7360 + dm   - The `DM` object
7361 - name - The label name
7362 
7363   Output Parameter:
7364 . label - The `DMLabel`, or `NULL` if the label is absent. Pass in `NULL` to call `DMLabelDestroy()` on the label, otherwise the
7365           caller is responsible for calling `DMLabelDestroy()`.
7366 
7367   Level: developer
7368 
7369 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMLabelDestroy()`, `DMRemoveLabelBySelf()`
7370 @*/
7371 PetscErrorCode DMRemoveLabel(DM dm, const char name[], DMLabel *label)
7372 {
7373   DMLabelLink link, *pnext;
7374   PetscBool   hasLabel;
7375   const char *lname;
7376 
7377   PetscFunctionBegin;
7378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7379   PetscAssertPointer(name, 2);
7380   if (label) {
7381     PetscAssertPointer(label, 3);
7382     *label = NULL;
7383   }
7384   for (pnext = &dm->labels; (link = *pnext); pnext = &link->next) {
7385     PetscCall(PetscObjectGetName((PetscObject)link->label, &lname));
7386     PetscCall(PetscStrcmp(name, lname, &hasLabel));
7387     if (hasLabel) {
7388       *pnext = link->next; /* Remove from list */
7389       PetscCall(PetscStrcmp(name, "depth", &hasLabel));
7390       if (hasLabel) dm->depthLabel = NULL;
7391       PetscCall(PetscStrcmp(name, "celltype", &hasLabel));
7392       if (hasLabel) dm->celltypeLabel = NULL;
7393       if (label) *label = link->label;
7394       else PetscCall(DMLabelDestroy(&link->label));
7395       PetscCall(PetscFree(link));
7396       break;
7397     }
7398   }
7399   PetscFunctionReturn(PETSC_SUCCESS);
7400 }
7401 
7402 /*@
7403   DMRemoveLabelBySelf - Remove the label from this `DM`
7404 
7405   Not Collective
7406 
7407   Input Parameters:
7408 + dm           - The `DM` object
7409 . label        - The `DMLabel` to be removed from the `DM`
7410 - failNotFound - Should it fail if the label is not found in the `DM`?
7411 
7412   Level: developer
7413 
7414   Note:
7415   Only exactly the same instance is removed if found, name match is ignored.
7416   If the `DM` has an exclusive reference to the label, the label gets destroyed and
7417   *label nullified.
7418 
7419 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabel()` `DMGetLabelValue()`, `DMSetLabelValue()`, `DMLabelDestroy()`, `DMRemoveLabel()`
7420 @*/
7421 PetscErrorCode DMRemoveLabelBySelf(DM dm, DMLabel *label, PetscBool failNotFound)
7422 {
7423   DMLabelLink link, *pnext;
7424   PetscBool   hasLabel = PETSC_FALSE;
7425 
7426   PetscFunctionBegin;
7427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7428   PetscAssertPointer(label, 2);
7429   if (!*label && !failNotFound) PetscFunctionReturn(PETSC_SUCCESS);
7430   PetscValidHeaderSpecific(*label, DMLABEL_CLASSID, 2);
7431   PetscValidLogicalCollectiveBool(dm, failNotFound, 3);
7432   for (pnext = &dm->labels; (link = *pnext); pnext = &link->next) {
7433     if (*label == link->label) {
7434       hasLabel = PETSC_TRUE;
7435       *pnext   = link->next; /* Remove from list */
7436       if (*label == dm->depthLabel) dm->depthLabel = NULL;
7437       if (*label == dm->celltypeLabel) dm->celltypeLabel = NULL;
7438       if (((PetscObject)link->label)->refct < 2) *label = NULL; /* nullify if exclusive reference */
7439       PetscCall(DMLabelDestroy(&link->label));
7440       PetscCall(PetscFree(link));
7441       break;
7442     }
7443   }
7444   PetscCheck(hasLabel || !failNotFound, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Given label not found in DM");
7445   PetscFunctionReturn(PETSC_SUCCESS);
7446 }
7447 
7448 /*@
7449   DMGetLabelOutput - Get the output flag for a given label
7450 
7451   Not Collective
7452 
7453   Input Parameters:
7454 + dm   - The `DM` object
7455 - name - The label name
7456 
7457   Output Parameter:
7458 . output - The flag for output
7459 
7460   Level: developer
7461 
7462 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMSetLabelOutput()`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7463 @*/
7464 PetscErrorCode DMGetLabelOutput(DM dm, const char name[], PetscBool *output)
7465 {
7466   DMLabelLink next = dm->labels;
7467   const char *lname;
7468 
7469   PetscFunctionBegin;
7470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7471   PetscAssertPointer(name, 2);
7472   PetscAssertPointer(output, 3);
7473   while (next) {
7474     PetscBool flg;
7475 
7476     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7477     PetscCall(PetscStrcmp(name, lname, &flg));
7478     if (flg) {
7479       *output = next->output;
7480       PetscFunctionReturn(PETSC_SUCCESS);
7481     }
7482     next = next->next;
7483   }
7484   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No label named %s was present in this dm", name);
7485 }
7486 
7487 /*@
7488   DMSetLabelOutput - Set if a given label should be saved to a `PetscViewer` in calls to `DMView()`
7489 
7490   Not Collective
7491 
7492   Input Parameters:
7493 + dm     - The `DM` object
7494 . name   - The label name
7495 - output - `PETSC_TRUE` to save the label to the viewer
7496 
7497   Level: developer
7498 
7499 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetOutputFlag()`, `DMGetLabelOutput()`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7500 @*/
7501 PetscErrorCode DMSetLabelOutput(DM dm, const char name[], PetscBool output)
7502 {
7503   DMLabelLink next = dm->labels;
7504   const char *lname;
7505 
7506   PetscFunctionBegin;
7507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7508   PetscAssertPointer(name, 2);
7509   while (next) {
7510     PetscBool flg;
7511 
7512     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7513     PetscCall(PetscStrcmp(name, lname, &flg));
7514     if (flg) {
7515       next->output = output;
7516       PetscFunctionReturn(PETSC_SUCCESS);
7517     }
7518     next = next->next;
7519   }
7520   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No label named %s was present in this dm", name);
7521 }
7522 
7523 /*@
7524   DMCopyLabels - Copy labels from one `DM` mesh to another `DM` with a superset of the points
7525 
7526   Collective
7527 
7528   Input Parameters:
7529 + dmA   - The `DM` object with initial labels
7530 . dmB   - The `DM` object to which labels are copied
7531 . mode  - Copy labels by pointers (`PETSC_OWN_POINTER`) or duplicate them (`PETSC_COPY_VALUES`)
7532 . all   - Copy all labels including "depth", "dim", and "celltype" (`PETSC_TRUE`) which are otherwise ignored (`PETSC_FALSE`)
7533 - emode - How to behave when a `DMLabel` in the source and destination `DM`s with the same name is encountered (see `DMCopyLabelsMode`)
7534 
7535   Level: intermediate
7536 
7537   Note:
7538   This is typically used when interpolating or otherwise adding to a mesh, or testing.
7539 
7540 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMAddLabel()`, `DMCopyLabelsMode`
7541 @*/
7542 PetscErrorCode DMCopyLabels(DM dmA, DM dmB, PetscCopyMode mode, PetscBool all, DMCopyLabelsMode emode)
7543 {
7544   DMLabel     label, labelNew, labelOld;
7545   const char *name;
7546   PetscBool   flg;
7547   DMLabelLink link;
7548 
7549   PetscFunctionBegin;
7550   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
7551   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
7552   PetscValidLogicalCollectiveEnum(dmA, mode, 3);
7553   PetscValidLogicalCollectiveBool(dmA, all, 4);
7554   PetscCheck(mode != PETSC_USE_POINTER, PetscObjectComm((PetscObject)dmA), PETSC_ERR_SUP, "PETSC_USE_POINTER not supported for objects");
7555   if (dmA == dmB) PetscFunctionReturn(PETSC_SUCCESS);
7556   for (link = dmA->labels; link; link = link->next) {
7557     label = link->label;
7558     PetscCall(PetscObjectGetName((PetscObject)label, &name));
7559     if (!all) {
7560       PetscCall(PetscStrcmp(name, "depth", &flg));
7561       if (flg) continue;
7562       PetscCall(PetscStrcmp(name, "dim", &flg));
7563       if (flg) continue;
7564       PetscCall(PetscStrcmp(name, "celltype", &flg));
7565       if (flg) continue;
7566     }
7567     PetscCall(DMGetLabel(dmB, name, &labelOld));
7568     if (labelOld) {
7569       switch (emode) {
7570       case DM_COPY_LABELS_KEEP:
7571         continue;
7572       case DM_COPY_LABELS_REPLACE:
7573         PetscCall(DMRemoveLabelBySelf(dmB, &labelOld, PETSC_TRUE));
7574         break;
7575       case DM_COPY_LABELS_FAIL:
7576         SETERRQ(PetscObjectComm((PetscObject)dmA), PETSC_ERR_ARG_OUTOFRANGE, "Label %s already exists in destination DM", name);
7577       default:
7578         SETERRQ(PetscObjectComm((PetscObject)dmA), PETSC_ERR_ARG_OUTOFRANGE, "Unhandled DMCopyLabelsMode %d", (int)emode);
7579       }
7580     }
7581     if (mode == PETSC_COPY_VALUES) {
7582       PetscCall(DMLabelDuplicate(label, &labelNew));
7583     } else {
7584       labelNew = label;
7585     }
7586     PetscCall(DMAddLabel(dmB, labelNew));
7587     if (mode == PETSC_COPY_VALUES) PetscCall(DMLabelDestroy(&labelNew));
7588   }
7589   PetscFunctionReturn(PETSC_SUCCESS);
7590 }
7591 
7592 /*@C
7593   DMCompareLabels - Compare labels between two `DM` objects
7594 
7595   Collective; No Fortran Support
7596 
7597   Input Parameters:
7598 + dm0 - First `DM` object
7599 - dm1 - Second `DM` object
7600 
7601   Output Parameters:
7602 + equal   - (Optional) Flag whether labels of dm0 and dm1 are the same
7603 - message - (Optional) Message describing the difference, or `NULL` if there is no difference
7604 
7605   Level: intermediate
7606 
7607   Notes:
7608   The output flag equal will be the same on all processes.
7609 
7610   If equal is passed as `NULL` and difference is found, an error is thrown on all processes.
7611 
7612   Make sure to pass equal is `NULL` on all processes or none of them.
7613 
7614   The output message is set independently on each rank.
7615 
7616   message must be freed with `PetscFree()`
7617 
7618   If message is passed as `NULL` and a difference is found, the difference description is printed to stderr in synchronized manner.
7619 
7620   Make sure to pass message as `NULL` on all processes or no processes.
7621 
7622   Labels are matched by name. If the number of labels and their names are equal,
7623   `DMLabelCompare()` is used to compare each pair of labels with the same name.
7624 
7625   Developer Note:
7626   Can automatically generate the Fortran stub because `message` must be freed with `PetscFree()`
7627 
7628 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMAddLabel()`, `DMCopyLabelsMode`, `DMLabelCompare()`
7629 @*/
7630 PetscErrorCode DMCompareLabels(DM dm0, DM dm1, PetscBool *equal, char **message)
7631 {
7632   PetscInt    n, i;
7633   char        msg[PETSC_MAX_PATH_LEN] = "";
7634   PetscBool   eq;
7635   MPI_Comm    comm;
7636   PetscMPIInt rank;
7637 
7638   PetscFunctionBegin;
7639   PetscValidHeaderSpecific(dm0, DM_CLASSID, 1);
7640   PetscValidHeaderSpecific(dm1, DM_CLASSID, 2);
7641   PetscCheckSameComm(dm0, 1, dm1, 2);
7642   if (equal) PetscAssertPointer(equal, 3);
7643   if (message) PetscAssertPointer(message, 4);
7644   PetscCall(PetscObjectGetComm((PetscObject)dm0, &comm));
7645   PetscCallMPI(MPI_Comm_rank(comm, &rank));
7646   {
7647     PetscInt n1;
7648 
7649     PetscCall(DMGetNumLabels(dm0, &n));
7650     PetscCall(DMGetNumLabels(dm1, &n1));
7651     eq = (PetscBool)(n == n1);
7652     if (!eq) PetscCall(PetscSNPrintf(msg, sizeof(msg), "Number of labels in dm0 = %" PetscInt_FMT " != %" PetscInt_FMT " = Number of labels in dm1", n, n1));
7653     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &eq, 1, MPIU_BOOL, MPI_LAND, comm));
7654     if (!eq) goto finish;
7655   }
7656   for (i = 0; i < n; i++) {
7657     DMLabel     l0, l1;
7658     const char *name;
7659     char       *msgInner;
7660 
7661     /* Ignore label order */
7662     PetscCall(DMGetLabelByNum(dm0, i, &l0));
7663     PetscCall(PetscObjectGetName((PetscObject)l0, &name));
7664     PetscCall(DMGetLabel(dm1, name, &l1));
7665     if (!l1) {
7666       PetscCall(PetscSNPrintf(msg, sizeof(msg), "Label \"%s\" (#%" PetscInt_FMT " in dm0) not found in dm1", name, i));
7667       eq = PETSC_FALSE;
7668       break;
7669     }
7670     PetscCall(DMLabelCompare(comm, l0, l1, &eq, &msgInner));
7671     PetscCall(PetscStrncpy(msg, msgInner, sizeof(msg)));
7672     PetscCall(PetscFree(msgInner));
7673     if (!eq) break;
7674   }
7675   PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &eq, 1, MPIU_BOOL, MPI_LAND, comm));
7676 finish:
7677   /* If message output arg not set, print to stderr */
7678   if (message) {
7679     *message = NULL;
7680     if (msg[0]) PetscCall(PetscStrallocpy(msg, message));
7681   } else {
7682     if (msg[0]) PetscCall(PetscSynchronizedFPrintf(comm, PETSC_STDERR, "[%d] %s\n", rank, msg));
7683     PetscCall(PetscSynchronizedFlush(comm, PETSC_STDERR));
7684   }
7685   /* If same output arg not ser and labels are not equal, throw error */
7686   if (equal) *equal = eq;
7687   else PetscCheck(eq, comm, PETSC_ERR_ARG_INCOMP, "DMLabels are not the same in dm0 and dm1");
7688   PetscFunctionReturn(PETSC_SUCCESS);
7689 }
7690 
7691 PetscErrorCode DMSetLabelValue_Fast(DM dm, DMLabel *label, const char name[], PetscInt point, PetscInt value)
7692 {
7693   PetscFunctionBegin;
7694   PetscAssertPointer(label, 2);
7695   if (!*label) {
7696     PetscCall(DMCreateLabel(dm, name));
7697     PetscCall(DMGetLabel(dm, name, label));
7698   }
7699   PetscCall(DMLabelSetValue(*label, point, value));
7700   PetscFunctionReturn(PETSC_SUCCESS);
7701 }
7702 
7703 /*
7704   Many mesh programs, such as Triangle and TetGen, allow only a single label for each mesh point. Therefore, we would
7705   like to encode all label IDs using a single, universal label. We can do this by assigning an integer to every
7706   (label, id) pair in the DM.
7707 
7708   However, a mesh point can have multiple labels, so we must separate all these values. We will assign a bit range to
7709   each label.
7710 */
7711 PetscErrorCode DMUniversalLabelCreate(DM dm, DMUniversalLabel *universal)
7712 {
7713   DMUniversalLabel ul;
7714   PetscBool       *active;
7715   PetscInt         pStart, pEnd, p, Nl, l, m;
7716 
7717   PetscFunctionBegin;
7718   PetscCall(PetscMalloc1(1, &ul));
7719   PetscCall(DMLabelCreate(PETSC_COMM_SELF, "universal", &ul->label));
7720   PetscCall(DMGetNumLabels(dm, &Nl));
7721   PetscCall(PetscCalloc1(Nl, &active));
7722   ul->Nl = 0;
7723   for (l = 0; l < Nl; ++l) {
7724     PetscBool   isdepth, iscelltype;
7725     const char *name;
7726 
7727     PetscCall(DMGetLabelName(dm, l, &name));
7728     PetscCall(PetscStrncmp(name, "depth", 6, &isdepth));
7729     PetscCall(PetscStrncmp(name, "celltype", 9, &iscelltype));
7730     active[l] = !(isdepth || iscelltype) ? PETSC_TRUE : PETSC_FALSE;
7731     if (active[l]) ++ul->Nl;
7732   }
7733   PetscCall(PetscCalloc5(ul->Nl, &ul->names, ul->Nl, &ul->indices, ul->Nl + 1, &ul->offsets, ul->Nl + 1, &ul->bits, ul->Nl, &ul->masks));
7734   ul->Nv = 0;
7735   for (l = 0, m = 0; l < Nl; ++l) {
7736     DMLabel     label;
7737     PetscInt    nv;
7738     const char *name;
7739 
7740     if (!active[l]) continue;
7741     PetscCall(DMGetLabelName(dm, l, &name));
7742     PetscCall(DMGetLabelByNum(dm, l, &label));
7743     PetscCall(DMLabelGetNumValues(label, &nv));
7744     PetscCall(PetscStrallocpy(name, &ul->names[m]));
7745     ul->indices[m] = l;
7746     ul->Nv += nv;
7747     ul->offsets[m + 1] = nv;
7748     ul->bits[m + 1]    = PetscCeilReal(PetscLog2Real(nv + 1));
7749     ++m;
7750   }
7751   for (l = 1; l <= ul->Nl; ++l) {
7752     ul->offsets[l] = ul->offsets[l - 1] + ul->offsets[l];
7753     ul->bits[l]    = ul->bits[l - 1] + ul->bits[l];
7754   }
7755   for (l = 0; l < ul->Nl; ++l) {
7756     PetscInt b;
7757 
7758     ul->masks[l] = 0;
7759     for (b = ul->bits[l]; b < ul->bits[l + 1]; ++b) ul->masks[l] |= 1 << b;
7760   }
7761   PetscCall(PetscMalloc1(ul->Nv, &ul->values));
7762   for (l = 0, m = 0; l < Nl; ++l) {
7763     DMLabel         label;
7764     IS              valueIS;
7765     const PetscInt *varr;
7766     PetscInt        nv, v;
7767 
7768     if (!active[l]) continue;
7769     PetscCall(DMGetLabelByNum(dm, l, &label));
7770     PetscCall(DMLabelGetNumValues(label, &nv));
7771     PetscCall(DMLabelGetValueIS(label, &valueIS));
7772     PetscCall(ISGetIndices(valueIS, &varr));
7773     for (v = 0; v < nv; ++v) ul->values[ul->offsets[m] + v] = varr[v];
7774     PetscCall(ISRestoreIndices(valueIS, &varr));
7775     PetscCall(ISDestroy(&valueIS));
7776     PetscCall(PetscSortInt(nv, &ul->values[ul->offsets[m]]));
7777     ++m;
7778   }
7779   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
7780   for (p = pStart; p < pEnd; ++p) {
7781     PetscInt  uval   = 0;
7782     PetscBool marked = PETSC_FALSE;
7783 
7784     for (l = 0, m = 0; l < Nl; ++l) {
7785       DMLabel  label;
7786       PetscInt val, defval, loc, nv;
7787 
7788       if (!active[l]) continue;
7789       PetscCall(DMGetLabelByNum(dm, l, &label));
7790       PetscCall(DMLabelGetValue(label, p, &val));
7791       PetscCall(DMLabelGetDefaultValue(label, &defval));
7792       if (val == defval) {
7793         ++m;
7794         continue;
7795       }
7796       nv     = ul->offsets[m + 1] - ul->offsets[m];
7797       marked = PETSC_TRUE;
7798       PetscCall(PetscFindInt(val, nv, &ul->values[ul->offsets[m]], &loc));
7799       PetscCheck(loc >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Label value %" PetscInt_FMT " not found in compression array", val);
7800       uval += (loc + 1) << ul->bits[m];
7801       ++m;
7802     }
7803     if (marked) PetscCall(DMLabelSetValue(ul->label, p, uval));
7804   }
7805   PetscCall(PetscFree(active));
7806   *universal = ul;
7807   PetscFunctionReturn(PETSC_SUCCESS);
7808 }
7809 
7810 PetscErrorCode DMUniversalLabelDestroy(DMUniversalLabel *universal)
7811 {
7812   PetscInt l;
7813 
7814   PetscFunctionBegin;
7815   for (l = 0; l < (*universal)->Nl; ++l) PetscCall(PetscFree((*universal)->names[l]));
7816   PetscCall(DMLabelDestroy(&(*universal)->label));
7817   PetscCall(PetscFree5((*universal)->names, (*universal)->indices, (*universal)->offsets, (*universal)->bits, (*universal)->masks));
7818   PetscCall(PetscFree((*universal)->values));
7819   PetscCall(PetscFree(*universal));
7820   *universal = NULL;
7821   PetscFunctionReturn(PETSC_SUCCESS);
7822 }
7823 
7824 PetscErrorCode DMUniversalLabelGetLabel(DMUniversalLabel ul, DMLabel *ulabel)
7825 {
7826   PetscFunctionBegin;
7827   PetscAssertPointer(ulabel, 2);
7828   *ulabel = ul->label;
7829   PetscFunctionReturn(PETSC_SUCCESS);
7830 }
7831 
7832 PetscErrorCode DMUniversalLabelCreateLabels(DMUniversalLabel ul, PetscBool preserveOrder, DM dm)
7833 {
7834   PetscInt Nl = ul->Nl, l;
7835 
7836   PetscFunctionBegin;
7837   PetscValidHeaderSpecific(dm, DM_CLASSID, 3);
7838   for (l = 0; l < Nl; ++l) {
7839     if (preserveOrder) PetscCall(DMCreateLabelAtIndex(dm, ul->indices[l], ul->names[l]));
7840     else PetscCall(DMCreateLabel(dm, ul->names[l]));
7841   }
7842   if (preserveOrder) {
7843     for (l = 0; l < ul->Nl; ++l) {
7844       const char *name;
7845       PetscBool   match;
7846 
7847       PetscCall(DMGetLabelName(dm, ul->indices[l], &name));
7848       PetscCall(PetscStrcmp(name, ul->names[l], &match));
7849       PetscCheck(match, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %" PetscInt_FMT " name %s does not match new name %s", l, name, ul->names[l]);
7850     }
7851   }
7852   PetscFunctionReturn(PETSC_SUCCESS);
7853 }
7854 
7855 PetscErrorCode DMUniversalLabelSetLabelValue(DMUniversalLabel ul, DM dm, PetscBool useIndex, PetscInt p, PetscInt value)
7856 {
7857   PetscInt l;
7858 
7859   PetscFunctionBegin;
7860   for (l = 0; l < ul->Nl; ++l) {
7861     DMLabel  label;
7862     PetscInt lval = (value & ul->masks[l]) >> ul->bits[l];
7863 
7864     if (lval) {
7865       if (useIndex) PetscCall(DMGetLabelByNum(dm, ul->indices[l], &label));
7866       else PetscCall(DMGetLabel(dm, ul->names[l], &label));
7867       PetscCall(DMLabelSetValue(label, p, ul->values[ul->offsets[l] + lval - 1]));
7868     }
7869   }
7870   PetscFunctionReturn(PETSC_SUCCESS);
7871 }
7872 
7873 /*@
7874   DMGetCoarseDM - Get the coarse `DM`from which this `DM` was obtained by refinement
7875 
7876   Not Collective
7877 
7878   Input Parameter:
7879 . dm - The `DM` object
7880 
7881   Output Parameter:
7882 . cdm - The coarse `DM`
7883 
7884   Level: intermediate
7885 
7886 .seealso: [](ch_dmbase), `DM`, `DMSetCoarseDM()`, `DMCoarsen()`
7887 @*/
7888 PetscErrorCode DMGetCoarseDM(DM dm, DM *cdm)
7889 {
7890   PetscFunctionBegin;
7891   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7892   PetscAssertPointer(cdm, 2);
7893   *cdm = dm->coarseMesh;
7894   PetscFunctionReturn(PETSC_SUCCESS);
7895 }
7896 
7897 /*@
7898   DMSetCoarseDM - Set the coarse `DM` from which this `DM` was obtained by refinement
7899 
7900   Input Parameters:
7901 + dm  - The `DM` object
7902 - cdm - The coarse `DM`
7903 
7904   Level: intermediate
7905 
7906   Note:
7907   Normally this is set automatically by `DMRefine()`
7908 
7909 .seealso: [](ch_dmbase), `DM`, `DMGetCoarseDM()`, `DMCoarsen()`, `DMSetRefine()`, `DMSetFineDM()`
7910 @*/
7911 PetscErrorCode DMSetCoarseDM(DM dm, DM cdm)
7912 {
7913   PetscFunctionBegin;
7914   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7915   if (cdm) PetscValidHeaderSpecific(cdm, DM_CLASSID, 2);
7916   if (dm == cdm) cdm = NULL;
7917   PetscCall(PetscObjectReference((PetscObject)cdm));
7918   PetscCall(DMDestroy(&dm->coarseMesh));
7919   dm->coarseMesh = cdm;
7920   PetscFunctionReturn(PETSC_SUCCESS);
7921 }
7922 
7923 /*@
7924   DMGetFineDM - Get the fine mesh from which this `DM` was obtained by coarsening
7925 
7926   Input Parameter:
7927 . dm - The `DM` object
7928 
7929   Output Parameter:
7930 . fdm - The fine `DM`
7931 
7932   Level: intermediate
7933 
7934 .seealso: [](ch_dmbase), `DM`, `DMSetFineDM()`, `DMCoarsen()`, `DMRefine()`
7935 @*/
7936 PetscErrorCode DMGetFineDM(DM dm, DM *fdm)
7937 {
7938   PetscFunctionBegin;
7939   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7940   PetscAssertPointer(fdm, 2);
7941   *fdm = dm->fineMesh;
7942   PetscFunctionReturn(PETSC_SUCCESS);
7943 }
7944 
7945 /*@
7946   DMSetFineDM - Set the fine mesh from which this was obtained by coarsening
7947 
7948   Input Parameters:
7949 + dm  - The `DM` object
7950 - fdm - The fine `DM`
7951 
7952   Level: developer
7953 
7954   Note:
7955   Normally this is set automatically by `DMCoarsen()`
7956 
7957 .seealso: [](ch_dmbase), `DM`, `DMGetFineDM()`, `DMCoarsen()`, `DMRefine()`
7958 @*/
7959 PetscErrorCode DMSetFineDM(DM dm, DM fdm)
7960 {
7961   PetscFunctionBegin;
7962   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7963   if (fdm) PetscValidHeaderSpecific(fdm, DM_CLASSID, 2);
7964   if (dm == fdm) fdm = NULL;
7965   PetscCall(PetscObjectReference((PetscObject)fdm));
7966   PetscCall(DMDestroy(&dm->fineMesh));
7967   dm->fineMesh = fdm;
7968   PetscFunctionReturn(PETSC_SUCCESS);
7969 }
7970 
7971 /*@C
7972   DMAddBoundary - Add a boundary condition to a model represented by a `DM`
7973 
7974   Collective
7975 
7976   Input Parameters:
7977 + dm       - The `DM`, with a `PetscDS` that matches the problem being constrained
7978 . type     - The type of condition, e.g. `DM_BC_ESSENTIAL_ANALYTIC`, `DM_BC_ESSENTIAL_FIELD` (Dirichlet), or `DM_BC_NATURAL` (Neumann)
7979 . name     - The BC name
7980 . label    - The label defining constrained points
7981 . Nv       - The number of `DMLabel` values for constrained points
7982 . values   - An array of values for constrained points
7983 . field    - The field to constrain
7984 . Nc       - The number of constrained field components (0 will constrain all fields)
7985 . comps    - An array of constrained component numbers
7986 . bcFunc   - A pointwise function giving boundary values
7987 . bcFunc_t - A pointwise function giving the time deriative of the boundary values, or NULL
7988 - ctx      - An optional user context for bcFunc
7989 
7990   Output Parameter:
7991 . bd - (Optional) Boundary number
7992 
7993   Options Database Keys:
7994 + -bc_<boundary name> <num>      - Overrides the boundary ids
7995 - -bc_<boundary name>_comp <num> - Overrides the boundary components
7996 
7997   Level: intermediate
7998 
7999   Notes:
8000   Both bcFunc and bcFunc_t will depend on the boundary condition type. If the type if `DM_BC_ESSENTIAL`, then the calling sequence is\:
8001 .vb
8002  void bcFunc(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar bcval[])
8003 .ve
8004 
8005   If the type is `DM_BC_ESSENTIAL_FIELD` or other _FIELD value, then the calling sequence is\:
8006 
8007 .vb
8008   void bcFunc(PetscInt dim, PetscInt Nf, PetscInt NfAux,
8009               const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
8010               const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
8011               PetscReal time, const PetscReal x[], PetscScalar bcval[])
8012 .ve
8013 + dim - the spatial dimension
8014 . Nf - the number of fields
8015 . uOff - the offset into u[] and u_t[] for each field
8016 . uOff_x - the offset into u_x[] for each field
8017 . u - each field evaluated at the current point
8018 . u_t - the time derivative of each field evaluated at the current point
8019 . u_x - the gradient of each field evaluated at the current point
8020 . aOff - the offset into a[] and a_t[] for each auxiliary field
8021 . aOff_x - the offset into a_x[] for each auxiliary field
8022 . a - each auxiliary field evaluated at the current point
8023 . a_t - the time derivative of each auxiliary field evaluated at the current point
8024 . a_x - the gradient of auxiliary each field evaluated at the current point
8025 . t - current time
8026 . x - coordinates of the current point
8027 . numConstants - number of constant parameters
8028 . constants - constant parameters
8029 - bcval - output values at the current point
8030 
8031 .seealso: [](ch_dmbase), `DM`, `DSGetBoundary()`, `PetscDSAddBoundary()`
8032 @*/
8033 PetscErrorCode DMAddBoundary(DM dm, DMBoundaryConditionType type, const char name[], DMLabel label, PetscInt Nv, const PetscInt values[], PetscInt field, PetscInt Nc, const PetscInt comps[], void (*bcFunc)(void), void (*bcFunc_t)(void), void *ctx, PetscInt *bd)
8034 {
8035   PetscDS ds;
8036 
8037   PetscFunctionBegin;
8038   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8039   PetscValidLogicalCollectiveEnum(dm, type, 2);
8040   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 4);
8041   PetscValidLogicalCollectiveInt(dm, Nv, 5);
8042   PetscValidLogicalCollectiveInt(dm, field, 7);
8043   PetscValidLogicalCollectiveInt(dm, Nc, 8);
8044   PetscCheck(!dm->localSection, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Cannot add boundary to DM after creating local section");
8045   PetscCall(DMGetDS(dm, &ds));
8046   /* Complete label */
8047   if (label) {
8048     PetscObject  obj;
8049     PetscClassId id;
8050 
8051     PetscCall(DMGetField(dm, field, NULL, &obj));
8052     PetscCall(PetscObjectGetClassId(obj, &id));
8053     if (id == PETSCFE_CLASSID) {
8054       DM plex;
8055 
8056       PetscCall(DMConvert(dm, DMPLEX, &plex));
8057       if (plex) PetscCall(DMPlexLabelComplete(plex, label));
8058       PetscCall(DMDestroy(&plex));
8059     }
8060   }
8061   PetscCall(PetscDSAddBoundary(ds, type, name, label, Nv, values, field, Nc, comps, bcFunc, bcFunc_t, ctx, bd));
8062   PetscFunctionReturn(PETSC_SUCCESS);
8063 }
8064 
8065 /* TODO Remove this since now the structures are the same */
8066 static PetscErrorCode DMPopulateBoundary(DM dm)
8067 {
8068   PetscDS     ds;
8069   DMBoundary *lastnext;
8070   DSBoundary  dsbound;
8071 
8072   PetscFunctionBegin;
8073   PetscCall(DMGetDS(dm, &ds));
8074   dsbound = ds->boundary;
8075   if (dm->boundary) {
8076     DMBoundary next = dm->boundary;
8077 
8078     /* quick check to see if the PetscDS has changed */
8079     if (next->dsboundary == dsbound) PetscFunctionReturn(PETSC_SUCCESS);
8080     /* the PetscDS has changed: tear down and rebuild */
8081     while (next) {
8082       DMBoundary b = next;
8083 
8084       next = b->next;
8085       PetscCall(PetscFree(b));
8086     }
8087     dm->boundary = NULL;
8088   }
8089 
8090   lastnext = &dm->boundary;
8091   while (dsbound) {
8092     DMBoundary dmbound;
8093 
8094     PetscCall(PetscNew(&dmbound));
8095     dmbound->dsboundary = dsbound;
8096     dmbound->label      = dsbound->label;
8097     /* push on the back instead of the front so that it is in the same order as in the PetscDS */
8098     *lastnext = dmbound;
8099     lastnext  = &dmbound->next;
8100     dsbound   = dsbound->next;
8101   }
8102   PetscFunctionReturn(PETSC_SUCCESS);
8103 }
8104 
8105 /* TODO: missing manual page */
8106 PetscErrorCode DMIsBoundaryPoint(DM dm, PetscInt point, PetscBool *isBd)
8107 {
8108   DMBoundary b;
8109 
8110   PetscFunctionBegin;
8111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8112   PetscAssertPointer(isBd, 3);
8113   *isBd = PETSC_FALSE;
8114   PetscCall(DMPopulateBoundary(dm));
8115   b = dm->boundary;
8116   while (b && !(*isBd)) {
8117     DMLabel    label = b->label;
8118     DSBoundary dsb   = b->dsboundary;
8119     PetscInt   i;
8120 
8121     if (label) {
8122       for (i = 0; i < dsb->Nv && !(*isBd); ++i) PetscCall(DMLabelStratumHasPoint(label, dsb->values[i], point, isBd));
8123     }
8124     b = b->next;
8125   }
8126   PetscFunctionReturn(PETSC_SUCCESS);
8127 }
8128 
8129 /*@C
8130   DMProjectFunction - This projects the given function into the function space provided by a `DM`, putting the coefficients in a global vector.
8131 
8132   Collective
8133 
8134   Input Parameters:
8135 + dm    - The `DM`
8136 . time  - The time
8137 . funcs - The coordinate functions to evaluate, one per field
8138 . ctxs  - Optional array of contexts to pass to each coordinate function.  ctxs itself may be null.
8139 - mode  - The insertion mode for values
8140 
8141   Output Parameter:
8142 . X - vector
8143 
8144   Calling sequence of `funcs`:
8145 + dim  - The spatial dimension
8146 . time - The time at which to sample
8147 . x    - The coordinates
8148 . Nc   - The number of components
8149 . u    - The output field values
8150 - ctx  - optional user-defined function context
8151 
8152   Level: developer
8153 
8154   Developer Notes:
8155   This API is specific to only particular usage of `DM`
8156 
8157   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8158 
8159 .seealso: [](ch_dmbase), `DM`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabel()`, `DMComputeL2Diff()`
8160 @*/
8161 PetscErrorCode DMProjectFunction(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx), void **ctxs, InsertMode mode, Vec X)
8162 {
8163   Vec localX;
8164 
8165   PetscFunctionBegin;
8166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8167   PetscCall(PetscLogEventBegin(DM_ProjectFunction, dm, X, 0, 0));
8168   PetscCall(DMGetLocalVector(dm, &localX));
8169   PetscCall(VecSet(localX, 0.));
8170   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, mode, localX));
8171   PetscCall(DMLocalToGlobalBegin(dm, localX, mode, X));
8172   PetscCall(DMLocalToGlobalEnd(dm, localX, mode, X));
8173   PetscCall(DMRestoreLocalVector(dm, &localX));
8174   PetscCall(PetscLogEventEnd(DM_ProjectFunction, dm, X, 0, 0));
8175   PetscFunctionReturn(PETSC_SUCCESS);
8176 }
8177 
8178 /*@C
8179   DMProjectFunctionLocal - This projects the given function into the function space provided by a `DM`, putting the coefficients in a local vector.
8180 
8181   Not Collective
8182 
8183   Input Parameters:
8184 + dm    - The `DM`
8185 . time  - The time
8186 . funcs - The coordinate functions to evaluate, one per field
8187 . ctxs  - Optional array of contexts to pass to each coordinate function.  ctxs itself may be null.
8188 - mode  - The insertion mode for values
8189 
8190   Output Parameter:
8191 . localX - vector
8192 
8193   Calling sequence of `funcs`:
8194 + dim  - The spatial dimension
8195 . time - The current timestep
8196 . x    - The coordinates
8197 . Nc   - The number of components
8198 . u    - The output field values
8199 - ctx  - optional user-defined function context
8200 
8201   Level: developer
8202 
8203   Developer Notes:
8204   This API is specific to only particular usage of `DM`
8205 
8206   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8207 
8208 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMProjectFunctionLabel()`, `DMComputeL2Diff()`
8209 @*/
8210 PetscErrorCode DMProjectFunctionLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx), void **ctxs, InsertMode mode, Vec localX)
8211 {
8212   PetscFunctionBegin;
8213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8214   PetscValidHeaderSpecific(localX, VEC_CLASSID, 6);
8215   PetscUseTypeMethod(dm, projectfunctionlocal, time, funcs, ctxs, mode, localX);
8216   PetscFunctionReturn(PETSC_SUCCESS);
8217 }
8218 
8219 /*@C
8220   DMProjectFunctionLabel - This projects the given function into the function space provided by the `DM`, putting the coefficients in a global vector, setting values only for points in the given label.
8221 
8222   Collective
8223 
8224   Input Parameters:
8225 + dm     - The `DM`
8226 . time   - The time
8227 . numIds - The number of ids
8228 . ids    - The ids
8229 . Nc     - The number of components
8230 . comps  - The components
8231 . label  - The `DMLabel` selecting the portion of the mesh for projection
8232 . funcs  - The coordinate functions to evaluate, one per field
8233 . ctxs   - Optional array of contexts to pass to each coordinate function.  ctxs may be null.
8234 - mode   - The insertion mode for values
8235 
8236   Output Parameter:
8237 . X - vector
8238 
8239   Calling sequence of `funcs`:
8240 + dim  - The spatial dimension
8241 . time - The current timestep
8242 . x    - The coordinates
8243 . Nc   - The number of components
8244 . u    - The output field values
8245 - ctx  - optional user-defined function context
8246 
8247   Level: developer
8248 
8249   Developer Notes:
8250   This API is specific to only particular usage of `DM`
8251 
8252   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8253 
8254 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`, `DMComputeL2Diff()`
8255 @*/
8256 PetscErrorCode DMProjectFunctionLabel(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Nc, const PetscInt comps[], PetscErrorCode (**funcs)(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx), void **ctxs, InsertMode mode, Vec X)
8257 {
8258   Vec localX;
8259 
8260   PetscFunctionBegin;
8261   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8262   PetscCall(DMGetLocalVector(dm, &localX));
8263   PetscCall(VecSet(localX, 0.));
8264   PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numIds, ids, Nc, comps, funcs, ctxs, mode, localX));
8265   PetscCall(DMLocalToGlobalBegin(dm, localX, mode, X));
8266   PetscCall(DMLocalToGlobalEnd(dm, localX, mode, X));
8267   PetscCall(DMRestoreLocalVector(dm, &localX));
8268   PetscFunctionReturn(PETSC_SUCCESS);
8269 }
8270 
8271 /*@C
8272   DMProjectFunctionLabelLocal - This projects the given function into the function space provided by the `DM`, putting the coefficients in a local vector, setting values only for points in the given label.
8273 
8274   Not Collective
8275 
8276   Input Parameters:
8277 + dm     - The `DM`
8278 . time   - The time
8279 . label  - The `DMLabel` selecting the portion of the mesh for projection
8280 . numIds - The number of ids
8281 . ids    - The ids
8282 . Nc     - The number of components
8283 . comps  - The components
8284 . funcs  - The coordinate functions to evaluate, one per field
8285 . ctxs   - Optional array of contexts to pass to each coordinate function.  ctxs itself may be null.
8286 - mode   - The insertion mode for values
8287 
8288   Output Parameter:
8289 . localX - vector
8290 
8291   Calling sequence of `funcs`:
8292 + dim  - The spatial dimension
8293 . time - The current time
8294 . x    - The coordinates
8295 . Nc   - The number of components
8296 . u    - The output field values
8297 - ctx  - optional user-defined function context
8298 
8299   Level: developer
8300 
8301   Developer Notes:
8302   This API is specific to only particular usage of `DM`
8303 
8304   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8305 
8306 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabel()`, `DMComputeL2Diff()`
8307 @*/
8308 PetscErrorCode DMProjectFunctionLabelLocal(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Nc, const PetscInt comps[], PetscErrorCode (**funcs)(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx), void **ctxs, InsertMode mode, Vec localX)
8309 {
8310   PetscFunctionBegin;
8311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8312   PetscValidHeaderSpecific(localX, VEC_CLASSID, 11);
8313   PetscUseTypeMethod(dm, projectfunctionlabellocal, time, label, numIds, ids, Nc, comps, funcs, ctxs, mode, localX);
8314   PetscFunctionReturn(PETSC_SUCCESS);
8315 }
8316 
8317 /*@C
8318   DMProjectFieldLocal - This projects the given function of the input fields into the function space provided by the `DM`, putting the coefficients in a local vector.
8319 
8320   Not Collective
8321 
8322   Input Parameters:
8323 + dm     - The `DM`
8324 . time   - The time
8325 . localU - The input field vector; may be `NULL` if projection is defined purely by coordinates
8326 . funcs  - The functions to evaluate, one per field
8327 - mode   - The insertion mode for values
8328 
8329   Output Parameter:
8330 . localX - The output vector
8331 
8332   Calling sequence of `funcs`:
8333 + dim          - The spatial dimension
8334 . Nf           - The number of input fields
8335 . NfAux        - The number of input auxiliary fields
8336 . uOff         - The offset of each field in u[]
8337 . uOff_x       - The offset of each field in u_x[]
8338 . u            - The field values at this point in space
8339 . u_t          - The field time derivative at this point in space (or NULL)
8340 . u_x          - The field derivatives at this point in space
8341 . aOff         - The offset of each auxiliary field in u[]
8342 . aOff_x       - The offset of each auxiliary field in u_x[]
8343 . a            - The auxiliary field values at this point in space
8344 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8345 . a_x          - The auxiliary field derivatives at this point in space
8346 . t            - The current time
8347 . x            - The coordinates of this point
8348 . numConstants - The number of constants
8349 . constants    - The value of each constant
8350 - f            - The value of the function at this point in space
8351 
8352   Level: intermediate
8353 
8354   Note:
8355   There are three different `DM`s that potentially interact in this function. The output `DM`, dm, specifies the layout of the values calculates by funcs.
8356   The input `DM`, attached to U, may be different. For example, you can input the solution over the full domain, but output over a piece of the boundary, or
8357   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8358   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8359 
8360   Developer Notes:
8361   This API is specific to only particular usage of `DM`
8362 
8363   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8364 
8365 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabelLocal()`,
8366 `DMProjectFunction()`, `DMComputeL2Diff()`
8367 @*/
8368 PetscErrorCode DMProjectFieldLocal(DM dm, PetscReal time, Vec localU, void (**funcs)(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]), InsertMode mode, Vec localX)
8369 {
8370   PetscFunctionBegin;
8371   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8372   if (localU) PetscValidHeaderSpecific(localU, VEC_CLASSID, 3);
8373   PetscValidHeaderSpecific(localX, VEC_CLASSID, 6);
8374   PetscUseTypeMethod(dm, projectfieldlocal, time, localU, funcs, mode, localX);
8375   PetscFunctionReturn(PETSC_SUCCESS);
8376 }
8377 
8378 /*@C
8379   DMProjectFieldLabelLocal - This projects the given function of the input fields into the function space provided, putting the coefficients in a local vector, calculating only over the portion of the domain specified by the label.
8380 
8381   Not Collective
8382 
8383   Input Parameters:
8384 + dm     - The `DM`
8385 . time   - The time
8386 . label  - The `DMLabel` marking the portion of the domain to output
8387 . numIds - The number of label ids to use
8388 . ids    - The label ids to use for marking
8389 . Nc     - The number of components to set in the output, or `PETSC_DETERMINE` for all components
8390 . comps  - The components to set in the output, or `NULL` for all components
8391 . localU - The input field vector
8392 . funcs  - The functions to evaluate, one per field
8393 - mode   - The insertion mode for values
8394 
8395   Output Parameter:
8396 . localX - The output vector
8397 
8398   Calling sequence of `funcs`:
8399 + dim          - The spatial dimension
8400 . Nf           - The number of input fields
8401 . NfAux        - The number of input auxiliary fields
8402 . uOff         - The offset of each field in u[]
8403 . uOff_x       - The offset of each field in u_x[]
8404 . u            - The field values at this point in space
8405 . u_t          - The field time derivative at this point in space (or NULL)
8406 . u_x          - The field derivatives at this point in space
8407 . aOff         - The offset of each auxiliary field in u[]
8408 . aOff_x       - The offset of each auxiliary field in u_x[]
8409 . a            - The auxiliary field values at this point in space
8410 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8411 . a_x          - The auxiliary field derivatives at this point in space
8412 . t            - The current time
8413 . x            - The coordinates of this point
8414 . numConstants - The number of constants
8415 . constants    - The value of each constant
8416 - f            - The value of the function at this point in space
8417 
8418   Level: intermediate
8419 
8420   Note:
8421   There are three different `DM`s that potentially interact in this function. The output `DM`, dm, specifies the layout of the values calculates by funcs.
8422   The input `DM`, attached to localU, may be different. For example, you can input the solution over the full domain, but output over a piece of the boundary, or
8423   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8424   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8425 
8426   Developer Notes:
8427   This API is specific to only particular usage of `DM`
8428 
8429   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8430 
8431 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabel()`, `DMProjectFunction()`, `DMComputeL2Diff()`
8432 @*/
8433 PetscErrorCode DMProjectFieldLabelLocal(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Nc, const PetscInt comps[], Vec localU, void (**funcs)(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]), InsertMode mode, Vec localX)
8434 {
8435   PetscFunctionBegin;
8436   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8437   PetscValidHeaderSpecific(localU, VEC_CLASSID, 8);
8438   PetscValidHeaderSpecific(localX, VEC_CLASSID, 11);
8439   PetscUseTypeMethod(dm, projectfieldlabellocal, time, label, numIds, ids, Nc, comps, localU, funcs, mode, localX);
8440   PetscFunctionReturn(PETSC_SUCCESS);
8441 }
8442 
8443 /*@C
8444   DMProjectFieldLabel - This projects the given function of the input fields into the function space provided, putting the coefficients in a global vector, calculating only over the portion of the domain specified by the label.
8445 
8446   Not Collective
8447 
8448   Input Parameters:
8449 + dm     - The `DM`
8450 . time   - The time
8451 . label  - The `DMLabel` marking the portion of the domain to output
8452 . numIds - The number of label ids to use
8453 . ids    - The label ids to use for marking
8454 . Nc     - The number of components to set in the output, or `PETSC_DETERMINE` for all components
8455 . comps  - The components to set in the output, or `NULL` for all components
8456 . U      - The input field vector
8457 . funcs  - The functions to evaluate, one per field
8458 - mode   - The insertion mode for values
8459 
8460   Output Parameter:
8461 . X - The output vector
8462 
8463   Calling sequence of `funcs`:
8464 + dim          - The spatial dimension
8465 . Nf           - The number of input fields
8466 . NfAux        - The number of input auxiliary fields
8467 . uOff         - The offset of each field in u[]
8468 . uOff_x       - The offset of each field in u_x[]
8469 . u            - The field values at this point in space
8470 . u_t          - The field time derivative at this point in space (or NULL)
8471 . u_x          - The field derivatives at this point in space
8472 . aOff         - The offset of each auxiliary field in u[]
8473 . aOff_x       - The offset of each auxiliary field in u_x[]
8474 . a            - The auxiliary field values at this point in space
8475 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8476 . a_x          - The auxiliary field derivatives at this point in space
8477 . t            - The current time
8478 . x            - The coordinates of this point
8479 . numConstants - The number of constants
8480 . constants    - The value of each constant
8481 - f            - The value of the function at this point in space
8482 
8483   Level: intermediate
8484 
8485   Note:
8486   There are three different `DM`s that potentially interact in this function. The output `DM`, dm, specifies the layout of the values calculates by funcs.
8487   The input `DM`, attached to U, may be different. For example, you can input the solution over the full domain, but output over a piece of the boundary, or
8488   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8489   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8490 
8491   Developer Notes:
8492   This API is specific to only particular usage of `DM`
8493 
8494   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8495 
8496 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabelLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`
8497 @*/
8498 PetscErrorCode DMProjectFieldLabel(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Nc, const PetscInt comps[], Vec U, void (**funcs)(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]), InsertMode mode, Vec X)
8499 {
8500   DM  dmIn;
8501   Vec localU, localX;
8502 
8503   PetscFunctionBegin;
8504   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8505   PetscCall(VecGetDM(U, &dmIn));
8506   PetscCall(DMGetLocalVector(dmIn, &localU));
8507   PetscCall(DMGetLocalVector(dm, &localX));
8508   PetscCall(VecSet(localX, 0.));
8509   PetscCall(DMGlobalToLocalBegin(dmIn, U, mode, localU));
8510   PetscCall(DMGlobalToLocalEnd(dmIn, U, mode, localU));
8511   PetscCall(DMProjectFieldLabelLocal(dm, time, label, numIds, ids, Nc, comps, localU, funcs, mode, localX));
8512   PetscCall(DMLocalToGlobalBegin(dm, localX, mode, X));
8513   PetscCall(DMLocalToGlobalEnd(dm, localX, mode, X));
8514   PetscCall(DMRestoreLocalVector(dm, &localX));
8515   PetscCall(DMRestoreLocalVector(dmIn, &localU));
8516   PetscFunctionReturn(PETSC_SUCCESS);
8517 }
8518 
8519 /*@C
8520   DMProjectBdFieldLabelLocal - This projects the given function of the input fields into the function space provided, putting the coefficients in a local vector, calculating only over the portion of the domain boundary specified by the label.
8521 
8522   Not Collective
8523 
8524   Input Parameters:
8525 + dm     - The `DM`
8526 . time   - The time
8527 . label  - The `DMLabel` marking the portion of the domain boundary to output
8528 . numIds - The number of label ids to use
8529 . ids    - The label ids to use for marking
8530 . Nc     - The number of components to set in the output, or `PETSC_DETERMINE` for all components
8531 . comps  - The components to set in the output, or `NULL` for all components
8532 . localU - The input field vector
8533 . funcs  - The functions to evaluate, one per field
8534 - mode   - The insertion mode for values
8535 
8536   Output Parameter:
8537 . localX - The output vector
8538 
8539   Calling sequence of `funcs`:
8540 + dim          - The spatial dimension
8541 . Nf           - The number of input fields
8542 . NfAux        - The number of input auxiliary fields
8543 . uOff         - The offset of each field in u[]
8544 . uOff_x       - The offset of each field in u_x[]
8545 . u            - The field values at this point in space
8546 . u_t          - The field time derivative at this point in space (or NULL)
8547 . u_x          - The field derivatives at this point in space
8548 . aOff         - The offset of each auxiliary field in u[]
8549 . aOff_x       - The offset of each auxiliary field in u_x[]
8550 . a            - The auxiliary field values at this point in space
8551 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8552 . a_x          - The auxiliary field derivatives at this point in space
8553 . t            - The current time
8554 . x            - The coordinates of this point
8555 . n            - The face normal
8556 . numConstants - The number of constants
8557 . constants    - The value of each constant
8558 - f            - The value of the function at this point in space
8559 
8560   Level: intermediate
8561 
8562   Note:
8563   There are three different `DM`s that potentially interact in this function. The output `DM`, dm, specifies the layout of the values calculates by funcs.
8564   The input `DM`, attached to U, may be different. For example, you can input the solution over the full domain, but output over a piece of the boundary, or
8565   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8566   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8567 
8568   Developer Notes:
8569   This API is specific to only particular usage of `DM`
8570 
8571   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8572 
8573 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabelLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`
8574 @*/
8575 PetscErrorCode DMProjectBdFieldLabelLocal(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Nc, const PetscInt comps[], Vec localU, void (**funcs)(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], const PetscReal n[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f[]), InsertMode mode, Vec localX)
8576 {
8577   PetscFunctionBegin;
8578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8579   PetscValidHeaderSpecific(localU, VEC_CLASSID, 8);
8580   PetscValidHeaderSpecific(localX, VEC_CLASSID, 11);
8581   PetscUseTypeMethod(dm, projectbdfieldlabellocal, time, label, numIds, ids, Nc, comps, localU, funcs, mode, localX);
8582   PetscFunctionReturn(PETSC_SUCCESS);
8583 }
8584 
8585 /*@C
8586   DMComputeL2Diff - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
8587 
8588   Collective
8589 
8590   Input Parameters:
8591 + dm    - The `DM`
8592 . time  - The time
8593 . funcs - The functions to evaluate for each field component
8594 . ctxs  - Optional array of contexts to pass to each function, or NULL.
8595 - X     - The coefficient vector u_h, a global vector
8596 
8597   Output Parameter:
8598 . diff - The diff ||u - u_h||_2
8599 
8600   Level: developer
8601 
8602   Developer Notes:
8603   This API is specific to only particular usage of `DM`
8604 
8605   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8606 
8607 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
8608 @*/
8609 PetscErrorCode DMComputeL2Diff(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
8610 {
8611   PetscFunctionBegin;
8612   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8613   PetscValidHeaderSpecific(X, VEC_CLASSID, 5);
8614   PetscUseTypeMethod(dm, computel2diff, time, funcs, ctxs, X, diff);
8615   PetscFunctionReturn(PETSC_SUCCESS);
8616 }
8617 
8618 /*@C
8619   DMComputeL2GradientDiff - This function computes the L_2 difference between the gradient of a function u and an FEM interpolant solution grad u_h.
8620 
8621   Collective
8622 
8623   Input Parameters:
8624 + dm    - The `DM`
8625 . time  - The time
8626 . funcs - The gradient functions to evaluate for each field component
8627 . ctxs  - Optional array of contexts to pass to each function, or NULL.
8628 . X     - The coefficient vector u_h, a global vector
8629 - n     - The vector to project along
8630 
8631   Output Parameter:
8632 . diff - The diff ||(grad u - grad u_h) . n||_2
8633 
8634   Level: developer
8635 
8636   Developer Notes:
8637   This API is specific to only particular usage of `DM`
8638 
8639   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8640 
8641 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMComputeL2FieldDiff()`
8642 @*/
8643 PetscErrorCode DMComputeL2GradientDiff(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, const PetscReal n[], PetscReal *diff)
8644 {
8645   PetscFunctionBegin;
8646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8647   PetscValidHeaderSpecific(X, VEC_CLASSID, 5);
8648   PetscUseTypeMethod(dm, computel2gradientdiff, time, funcs, ctxs, X, n, diff);
8649   PetscFunctionReturn(PETSC_SUCCESS);
8650 }
8651 
8652 /*@C
8653   DMComputeL2FieldDiff - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h, separated into field components.
8654 
8655   Collective
8656 
8657   Input Parameters:
8658 + dm    - The `DM`
8659 . time  - The time
8660 . funcs - The functions to evaluate for each field component
8661 . ctxs  - Optional array of contexts to pass to each function, or NULL.
8662 - X     - The coefficient vector u_h, a global vector
8663 
8664   Output Parameter:
8665 . diff - The array of differences, ||u^f - u^f_h||_2
8666 
8667   Level: developer
8668 
8669   Developer Notes:
8670   This API is specific to only particular usage of `DM`
8671 
8672   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8673 
8674 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMComputeL2GradientDiff()`
8675 @*/
8676 PetscErrorCode DMComputeL2FieldDiff(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal diff[])
8677 {
8678   PetscFunctionBegin;
8679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8680   PetscValidHeaderSpecific(X, VEC_CLASSID, 5);
8681   PetscUseTypeMethod(dm, computel2fielddiff, time, funcs, ctxs, X, diff);
8682   PetscFunctionReturn(PETSC_SUCCESS);
8683 }
8684 
8685 /*@C
8686   DMGetNeighbors - Gets an array containing the MPI ranks of all the processes neighbors
8687 
8688   Not Collective
8689 
8690   Input Parameter:
8691 . dm - The `DM`
8692 
8693   Output Parameters:
8694 + nranks - the number of neighbours
8695 - ranks  - the neighbors ranks
8696 
8697   Level: beginner
8698 
8699   Note:
8700   Do not free the array, it is freed when the `DM` is destroyed.
8701 
8702 .seealso: [](ch_dmbase), `DM`, `DMDAGetNeighbors()`, `PetscSFGetRootRanks()`
8703 @*/
8704 PetscErrorCode DMGetNeighbors(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
8705 {
8706   PetscFunctionBegin;
8707   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8708   PetscUseTypeMethod(dm, getneighbors, nranks, ranks);
8709   PetscFunctionReturn(PETSC_SUCCESS);
8710 }
8711 
8712 #include <petsc/private/matimpl.h> /* Needed because of coloring->ctype below */
8713 
8714 /*
8715     Converts the input vector to a ghosted vector and then calls the standard coloring code.
8716     This must be a different function because it requires DM which is not defined in the Mat library
8717 */
8718 static PetscErrorCode MatFDColoringApply_AIJDM(Mat J, MatFDColoring coloring, Vec x1, void *sctx)
8719 {
8720   PetscFunctionBegin;
8721   if (coloring->ctype == IS_COLORING_LOCAL) {
8722     Vec x1local;
8723     DM  dm;
8724     PetscCall(MatGetDM(J, &dm));
8725     PetscCheck(dm, PetscObjectComm((PetscObject)J), PETSC_ERR_ARG_INCOMP, "IS_COLORING_LOCAL requires a DM");
8726     PetscCall(DMGetLocalVector(dm, &x1local));
8727     PetscCall(DMGlobalToLocalBegin(dm, x1, INSERT_VALUES, x1local));
8728     PetscCall(DMGlobalToLocalEnd(dm, x1, INSERT_VALUES, x1local));
8729     x1 = x1local;
8730   }
8731   PetscCall(MatFDColoringApply_AIJ(J, coloring, x1, sctx));
8732   if (coloring->ctype == IS_COLORING_LOCAL) {
8733     DM dm;
8734     PetscCall(MatGetDM(J, &dm));
8735     PetscCall(DMRestoreLocalVector(dm, &x1));
8736   }
8737   PetscFunctionReturn(PETSC_SUCCESS);
8738 }
8739 
8740 /*@
8741   MatFDColoringUseDM - allows a `MatFDColoring` object to use the `DM` associated with the matrix to compute a `IS_COLORING_LOCAL` coloring
8742 
8743   Input Parameters:
8744 + coloring   - The matrix to get the `DM` from
8745 - fdcoloring - the `MatFDColoring` object
8746 
8747   Level: advanced
8748 
8749   Developer Note:
8750   This routine exists because the PETSc `Mat` library does not know about the `DM` objects
8751 
8752 .seealso: [](ch_dmbase), `DM`, `MatFDColoring`, `MatFDColoringCreate()`, `ISColoringType`
8753 @*/
8754 PetscErrorCode MatFDColoringUseDM(Mat coloring, MatFDColoring fdcoloring)
8755 {
8756   PetscFunctionBegin;
8757   coloring->ops->fdcoloringapply = MatFDColoringApply_AIJDM;
8758   PetscFunctionReturn(PETSC_SUCCESS);
8759 }
8760 
8761 /*@
8762   DMGetCompatibility - determine if two `DM`s are compatible
8763 
8764   Collective
8765 
8766   Input Parameters:
8767 + dm1 - the first `DM`
8768 - dm2 - the second `DM`
8769 
8770   Output Parameters:
8771 + compatible - whether or not the two `DM`s are compatible
8772 - set        - whether or not the compatible value was actually determined and set
8773 
8774   Level: advanced
8775 
8776   Notes:
8777   Two `DM`s are deemed compatible if they represent the same parallel decomposition
8778   of the same topology. This implies that the section (field data) on one
8779   "makes sense" with respect to the topology and parallel decomposition of the other.
8780   Loosely speaking, compatible `DM`s represent the same domain and parallel
8781   decomposition, but hold different data.
8782 
8783   Typically, one would confirm compatibility if intending to simultaneously iterate
8784   over a pair of vectors obtained from different `DM`s.
8785 
8786   For example, two `DMDA` objects are compatible if they have the same local
8787   and global sizes and the same stencil width. They can have different numbers
8788   of degrees of freedom per node. Thus, one could use the node numbering from
8789   either `DM` in bounds for a loop over vectors derived from either `DM`.
8790 
8791   Consider the operation of summing data living on a 2-dof `DMDA` to data living
8792   on a 1-dof `DMDA`, which should be compatible, as in the following snippet.
8793 .vb
8794   ...
8795   PetscCall(DMGetCompatibility(da1,da2,&compatible,&set));
8796   if (set && compatible)  {
8797     PetscCall(DMDAVecGetArrayDOF(da1,vec1,&arr1));
8798     PetscCall(DMDAVecGetArrayDOF(da2,vec2,&arr2));
8799     PetscCall(DMDAGetCorners(da1,&x,&y,NULL,&m,&n,NULL));
8800     for (j=y; j<y+n; ++j) {
8801       for (i=x; i<x+m, ++i) {
8802         arr1[j][i][0] = arr2[j][i][0] + arr2[j][i][1];
8803       }
8804     }
8805     PetscCall(DMDAVecRestoreArrayDOF(da1,vec1,&arr1));
8806     PetscCall(DMDAVecRestoreArrayDOF(da2,vec2,&arr2));
8807   } else {
8808     SETERRQ(PetscObjectComm((PetscObject)da1,PETSC_ERR_ARG_INCOMP,"DMDA objects incompatible");
8809   }
8810   ...
8811 .ve
8812 
8813   Checking compatibility might be expensive for a given implementation of `DM`,
8814   or might be impossible to unambiguously confirm or deny. For this reason,
8815   this function may decline to determine compatibility, and hence users should
8816   always check the "set" output parameter.
8817 
8818   A `DM` is always compatible with itself.
8819 
8820   In the current implementation, `DM`s which live on "unequal" communicators
8821   (MPI_UNEQUAL in the terminology of MPI_Comm_compare()) are always deemed
8822   incompatible.
8823 
8824   This function is labeled "Collective," as information about all subdomains
8825   is required on each rank. However, in `DM` implementations which store all this
8826   information locally, this function may be merely "Logically Collective".
8827 
8828   Developer Note:
8829   Compatibility is assumed to be a symmetric concept; `DM` A is compatible with `DM` B
8830   iff B is compatible with A. Thus, this function checks the implementations
8831   of both dm and dmc (if they are of different types), attempting to determine
8832   compatibility. It is left to `DM` implementers to ensure that symmetry is
8833   preserved. The simplest way to do this is, when implementing type-specific
8834   logic for this function, is to check for existing logic in the implementation
8835   of other `DM` types and let *set = PETSC_FALSE if found.
8836 
8837 .seealso: [](ch_dmbase), `DM`, `DMDACreateCompatibleDMDA()`, `DMStagCreateCompatibleDMStag()`
8838 @*/
8839 PetscErrorCode DMGetCompatibility(DM dm1, DM dm2, PetscBool *compatible, PetscBool *set)
8840 {
8841   PetscMPIInt compareResult;
8842   DMType      type, type2;
8843   PetscBool   sameType;
8844 
8845   PetscFunctionBegin;
8846   PetscValidHeaderSpecific(dm1, DM_CLASSID, 1);
8847   PetscValidHeaderSpecific(dm2, DM_CLASSID, 2);
8848 
8849   /* Declare a DM compatible with itself */
8850   if (dm1 == dm2) {
8851     *set        = PETSC_TRUE;
8852     *compatible = PETSC_TRUE;
8853     PetscFunctionReturn(PETSC_SUCCESS);
8854   }
8855 
8856   /* Declare a DM incompatible with a DM that lives on an "unequal"
8857      communicator. Note that this does not preclude compatibility with
8858      DMs living on "congruent" or "similar" communicators, but this must be
8859      determined by the implementation-specific logic */
8860   PetscCallMPI(MPI_Comm_compare(PetscObjectComm((PetscObject)dm1), PetscObjectComm((PetscObject)dm2), &compareResult));
8861   if (compareResult == MPI_UNEQUAL) {
8862     *set        = PETSC_TRUE;
8863     *compatible = PETSC_FALSE;
8864     PetscFunctionReturn(PETSC_SUCCESS);
8865   }
8866 
8867   /* Pass to the implementation-specific routine, if one exists. */
8868   if (dm1->ops->getcompatibility) {
8869     PetscUseTypeMethod(dm1, getcompatibility, dm2, compatible, set);
8870     if (*set) PetscFunctionReturn(PETSC_SUCCESS);
8871   }
8872 
8873   /* If dm1 and dm2 are of different types, then attempt to check compatibility
8874      with an implementation of this function from dm2 */
8875   PetscCall(DMGetType(dm1, &type));
8876   PetscCall(DMGetType(dm2, &type2));
8877   PetscCall(PetscStrcmp(type, type2, &sameType));
8878   if (!sameType && dm2->ops->getcompatibility) {
8879     PetscUseTypeMethod(dm2, getcompatibility, dm1, compatible, set); /* Note argument order */
8880   } else {
8881     *set = PETSC_FALSE;
8882   }
8883   PetscFunctionReturn(PETSC_SUCCESS);
8884 }
8885 
8886 /*@C
8887   DMMonitorSet - Sets an additional monitor function that is to be used after a solve to monitor discretization performance.
8888 
8889   Logically Collective
8890 
8891   Input Parameters:
8892 + dm             - the `DM`
8893 . f              - the monitor function
8894 . mctx           - [optional] user-defined context for private data for the monitor routine (use `NULL` if no context is desired)
8895 - monitordestroy - [optional] routine that frees monitor context (may be `NULL`)
8896 
8897   Options Database Key:
8898 . -dm_monitor_cancel - cancels all monitors that have been hardwired into a code by calls to `DMMonitorSet()`, but
8899                             does not cancel those set via the options database.
8900 
8901   Level: intermediate
8902 
8903   Note:
8904   Several different monitoring routines may be set by calling
8905   `DMMonitorSet()` multiple times or with `DMMonitorSetFromOptions()`; all will be called in the
8906   order in which they were set.
8907 
8908   Fortran Note:
8909   Only a single monitor function can be set for each `DM` object
8910 
8911   Developer Note:
8912   This API has a generic name but seems specific to a very particular aspect of the use of `DM`
8913 
8914 .seealso: [](ch_dmbase), `DM`, `DMMonitorCancel()`, `DMMonitorSetFromOptions()`, `DMMonitor()`
8915 @*/
8916 PetscErrorCode DMMonitorSet(DM dm, PetscErrorCode (*f)(DM, void *), void *mctx, PetscErrorCode (*monitordestroy)(void **))
8917 {
8918   PetscInt m;
8919 
8920   PetscFunctionBegin;
8921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8922   for (m = 0; m < dm->numbermonitors; ++m) {
8923     PetscBool identical;
8924 
8925     PetscCall(PetscMonitorCompare((PetscErrorCode(*)(void))f, mctx, monitordestroy, (PetscErrorCode(*)(void))dm->monitor[m], dm->monitorcontext[m], dm->monitordestroy[m], &identical));
8926     if (identical) PetscFunctionReturn(PETSC_SUCCESS);
8927   }
8928   PetscCheck(dm->numbermonitors < MAXDMMONITORS, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many monitors set");
8929   dm->monitor[dm->numbermonitors]          = f;
8930   dm->monitordestroy[dm->numbermonitors]   = monitordestroy;
8931   dm->monitorcontext[dm->numbermonitors++] = (void *)mctx;
8932   PetscFunctionReturn(PETSC_SUCCESS);
8933 }
8934 
8935 /*@
8936   DMMonitorCancel - Clears all the monitor functions for a `DM` object.
8937 
8938   Logically Collective
8939 
8940   Input Parameter:
8941 . dm - the DM
8942 
8943   Options Database Key:
8944 . -dm_monitor_cancel - cancels all monitors that have been hardwired
8945   into a code by calls to `DMonitorSet()`, but does not cancel those
8946   set via the options database
8947 
8948   Level: intermediate
8949 
8950   Note:
8951   There is no way to clear one specific monitor from a `DM` object.
8952 
8953 .seealso: [](ch_dmbase), `DM`, `DMMonitorSet()`, `DMMonitorSetFromOptions()`, `DMMonitor()`
8954 @*/
8955 PetscErrorCode DMMonitorCancel(DM dm)
8956 {
8957   PetscInt m;
8958 
8959   PetscFunctionBegin;
8960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8961   for (m = 0; m < dm->numbermonitors; ++m) {
8962     if (dm->monitordestroy[m]) PetscCall((*dm->monitordestroy[m])(&dm->monitorcontext[m]));
8963   }
8964   dm->numbermonitors = 0;
8965   PetscFunctionReturn(PETSC_SUCCESS);
8966 }
8967 
8968 /*@C
8969   DMMonitorSetFromOptions - Sets a monitor function and viewer appropriate for the type indicated by the user
8970 
8971   Collective
8972 
8973   Input Parameters:
8974 + dm           - `DM` object you wish to monitor
8975 . name         - the monitor type one is seeking
8976 . help         - message indicating what monitoring is done
8977 . manual       - manual page for the monitor
8978 . monitor      - the monitor function
8979 - monitorsetup - a function that is called once ONLY if the user selected this monitor that may set additional features of the `DM` or `PetscViewer` objects
8980 
8981   Output Parameter:
8982 . flg - Flag set if the monitor was created
8983 
8984   Level: developer
8985 
8986 .seealso: [](ch_dmbase), `DM`, `PetscOptionsCreateViewer()`, `PetscOptionsGetReal()`, `PetscOptionsHasName()`, `PetscOptionsGetString()`,
8987           `PetscOptionsGetIntArray()`, `PetscOptionsGetRealArray()`, `PetscOptionsBool()`
8988           `PetscOptionsInt()`, `PetscOptionsString()`, `PetscOptionsReal()`,
8989           `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`,
8990           `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`,
8991           `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`,
8992           `PetscOptionsFList()`, `PetscOptionsEList()`, `DMMonitor()`, `DMMonitorSet()`
8993 @*/
8994 PetscErrorCode DMMonitorSetFromOptions(DM dm, const char name[], const char help[], const char manual[], PetscErrorCode (*monitor)(DM, void *), PetscErrorCode (*monitorsetup)(DM, PetscViewerAndFormat *), PetscBool *flg)
8995 {
8996   PetscViewer       viewer;
8997   PetscViewerFormat format;
8998 
8999   PetscFunctionBegin;
9000   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9001   PetscCall(PetscOptionsCreateViewer(PetscObjectComm((PetscObject)dm), ((PetscObject)dm)->options, ((PetscObject)dm)->prefix, name, &viewer, &format, flg));
9002   if (*flg) {
9003     PetscViewerAndFormat *vf;
9004 
9005     PetscCall(PetscViewerAndFormatCreate(viewer, format, &vf));
9006     PetscCall(PetscViewerDestroy(&viewer));
9007     if (monitorsetup) PetscCall((*monitorsetup)(dm, vf));
9008     PetscCall(DMMonitorSet(dm, (PetscErrorCode(*)(DM, void *))monitor, vf, (PetscErrorCode(*)(void **))PetscViewerAndFormatDestroy));
9009   }
9010   PetscFunctionReturn(PETSC_SUCCESS);
9011 }
9012 
9013 /*@
9014   DMMonitor - runs the user provided monitor routines, if they exist
9015 
9016   Collective
9017 
9018   Input Parameter:
9019 . dm - The `DM`
9020 
9021   Level: developer
9022 
9023   Developer Note:
9024   Note should indicate when during the life of the `DM` the monitor is run. It appears to be
9025   related to the discretization process seems rather specialized since some `DM` have no
9026   concept of discretization.
9027 
9028 .seealso: [](ch_dmbase), `DM`, `DMMonitorSet()`, `DMMonitorSetFromOptions()`
9029 @*/
9030 PetscErrorCode DMMonitor(DM dm)
9031 {
9032   PetscInt m;
9033 
9034   PetscFunctionBegin;
9035   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
9036   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9037   for (m = 0; m < dm->numbermonitors; ++m) PetscCall((*dm->monitor[m])(dm, dm->monitorcontext[m]));
9038   PetscFunctionReturn(PETSC_SUCCESS);
9039 }
9040 
9041 /*@
9042   DMComputeError - Computes the error assuming the user has provided the exact solution functions
9043 
9044   Collective
9045 
9046   Input Parameters:
9047 + dm  - The `DM`
9048 - sol - The solution vector
9049 
9050   Input/Output Parameter:
9051 . errors - An array of length Nf, the number of fields, or `NULL` for no output; on output
9052            contains the error in each field
9053 
9054   Output Parameter:
9055 . errorVec - A vector to hold the cellwise error (may be `NULL`)
9056 
9057   Level: developer
9058 
9059   Note:
9060   The exact solutions come from the `PetscDS` object, and the time comes from `DMGetOutputSequenceNumber()`.
9061 
9062 .seealso: [](ch_dmbase), `DM`, `DMMonitorSet()`, `DMGetRegionNumDS()`, `PetscDSGetExactSolution()`, `DMGetOutputSequenceNumber()`
9063 @*/
9064 PetscErrorCode DMComputeError(DM dm, Vec sol, PetscReal errors[], Vec *errorVec)
9065 {
9066   PetscErrorCode (**exactSol)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *);
9067   void    **ctxs;
9068   PetscReal time;
9069   PetscInt  Nf, f, Nds, s;
9070 
9071   PetscFunctionBegin;
9072   PetscCall(DMGetNumFields(dm, &Nf));
9073   PetscCall(PetscCalloc2(Nf, &exactSol, Nf, &ctxs));
9074   PetscCall(DMGetNumDS(dm, &Nds));
9075   for (s = 0; s < Nds; ++s) {
9076     PetscDS         ds;
9077     DMLabel         label;
9078     IS              fieldIS;
9079     const PetscInt *fields;
9080     PetscInt        dsNf;
9081 
9082     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
9083     PetscCall(PetscDSGetNumFields(ds, &dsNf));
9084     if (fieldIS) PetscCall(ISGetIndices(fieldIS, &fields));
9085     for (f = 0; f < dsNf; ++f) {
9086       const PetscInt field = fields[f];
9087       PetscCall(PetscDSGetExactSolution(ds, field, &exactSol[field], &ctxs[field]));
9088     }
9089     if (fieldIS) PetscCall(ISRestoreIndices(fieldIS, &fields));
9090   }
9091   for (f = 0; f < Nf; ++f) PetscCheck(exactSol[f], PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "DS must contain exact solution functions in order to calculate error, missing for field %" PetscInt_FMT, f);
9092   PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
9093   if (errors) PetscCall(DMComputeL2FieldDiff(dm, time, exactSol, ctxs, sol, errors));
9094   if (errorVec) {
9095     DM             edm;
9096     DMPolytopeType ct;
9097     PetscBool      simplex;
9098     PetscInt       dim, cStart, Nf;
9099 
9100     PetscCall(DMClone(dm, &edm));
9101     PetscCall(DMGetDimension(edm, &dim));
9102     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
9103     PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9104     simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9105     PetscCall(DMGetNumFields(dm, &Nf));
9106     for (f = 0; f < Nf; ++f) {
9107       PetscFE         fe, efe;
9108       PetscQuadrature q;
9109       const char     *name;
9110 
9111       PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
9112       PetscCall(PetscFECreateLagrange(PETSC_COMM_SELF, dim, Nf, simplex, 0, PETSC_DETERMINE, &efe));
9113       PetscCall(PetscObjectGetName((PetscObject)fe, &name));
9114       PetscCall(PetscObjectSetName((PetscObject)efe, name));
9115       PetscCall(PetscFEGetQuadrature(fe, &q));
9116       PetscCall(PetscFESetQuadrature(efe, q));
9117       PetscCall(DMSetField(edm, f, NULL, (PetscObject)efe));
9118       PetscCall(PetscFEDestroy(&efe));
9119     }
9120     PetscCall(DMCreateDS(edm));
9121 
9122     PetscCall(DMCreateGlobalVector(edm, errorVec));
9123     PetscCall(PetscObjectSetName((PetscObject)*errorVec, "Error"));
9124     PetscCall(DMPlexComputeL2DiffVec(dm, time, exactSol, ctxs, sol, *errorVec));
9125     PetscCall(DMDestroy(&edm));
9126   }
9127   PetscCall(PetscFree2(exactSol, ctxs));
9128   PetscFunctionReturn(PETSC_SUCCESS);
9129 }
9130 
9131 /*@
9132   DMGetNumAuxiliaryVec - Get the number of auxiliary vectors associated with this `DM`
9133 
9134   Not Collective
9135 
9136   Input Parameter:
9137 . dm - The `DM`
9138 
9139   Output Parameter:
9140 . numAux - The number of auxiliary data vectors
9141 
9142   Level: advanced
9143 
9144 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMSetAuxiliaryVec()`, `DMGetAuxiliaryLabels()`, `DMGetAuxiliaryVec()`
9145 @*/
9146 PetscErrorCode DMGetNumAuxiliaryVec(DM dm, PetscInt *numAux)
9147 {
9148   PetscFunctionBegin;
9149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9150   PetscCall(PetscHMapAuxGetSize(dm->auxData, numAux));
9151   PetscFunctionReturn(PETSC_SUCCESS);
9152 }
9153 
9154 /*@
9155   DMGetAuxiliaryVec - Get the auxiliary vector for region specified by the given label and value, and equation part
9156 
9157   Not Collective
9158 
9159   Input Parameters:
9160 + dm    - The `DM`
9161 . label - The `DMLabel`
9162 . value - The label value indicating the region
9163 - part  - The equation part, or 0 if unused
9164 
9165   Output Parameter:
9166 . aux - The `Vec` holding auxiliary field data
9167 
9168   Level: advanced
9169 
9170   Note:
9171   If no auxiliary vector is found for this (label, value), (NULL, 0, 0) is checked as well.
9172 
9173 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMSetAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryLabels()`
9174 @*/
9175 PetscErrorCode DMGetAuxiliaryVec(DM dm, DMLabel label, PetscInt value, PetscInt part, Vec *aux)
9176 {
9177   PetscHashAuxKey key, wild = {NULL, 0, 0};
9178   PetscBool       has;
9179 
9180   PetscFunctionBegin;
9181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9182   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
9183   key.label = label;
9184   key.value = value;
9185   key.part  = part;
9186   PetscCall(PetscHMapAuxHas(dm->auxData, key, &has));
9187   if (has) PetscCall(PetscHMapAuxGet(dm->auxData, key, aux));
9188   else PetscCall(PetscHMapAuxGet(dm->auxData, wild, aux));
9189   PetscFunctionReturn(PETSC_SUCCESS);
9190 }
9191 
9192 /*@
9193   DMSetAuxiliaryVec - Set an auxiliary vector for region specified by the given label and value, and equation part
9194 
9195   Not Collective because auxiliary vectors are not parallel
9196 
9197   Input Parameters:
9198 + dm    - The `DM`
9199 . label - The `DMLabel`
9200 . value - The label value indicating the region
9201 . part  - The equation part, or 0 if unused
9202 - aux   - The `Vec` holding auxiliary field data
9203 
9204   Level: advanced
9205 
9206 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMGetAuxiliaryLabels()`, `DMCopyAuxiliaryVec()`
9207 @*/
9208 PetscErrorCode DMSetAuxiliaryVec(DM dm, DMLabel label, PetscInt value, PetscInt part, Vec aux)
9209 {
9210   Vec             old;
9211   PetscHashAuxKey key;
9212 
9213   PetscFunctionBegin;
9214   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9215   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
9216   key.label = label;
9217   key.value = value;
9218   key.part  = part;
9219   PetscCall(PetscHMapAuxGet(dm->auxData, key, &old));
9220   PetscCall(PetscObjectReference((PetscObject)aux));
9221   if (!aux) PetscCall(PetscHMapAuxDel(dm->auxData, key));
9222   else PetscCall(PetscHMapAuxSet(dm->auxData, key, aux));
9223   PetscCall(VecDestroy(&old));
9224   PetscFunctionReturn(PETSC_SUCCESS);
9225 }
9226 
9227 /*@
9228   DMGetAuxiliaryLabels - Get the labels, values, and parts for all auxiliary vectors in this `DM`
9229 
9230   Not Collective
9231 
9232   Input Parameter:
9233 . dm - The `DM`
9234 
9235   Output Parameters:
9236 + labels - The `DMLabel`s for each `Vec`
9237 . values - The label values for each `Vec`
9238 - parts  - The equation parts for each `Vec`
9239 
9240   Level: advanced
9241 
9242   Note:
9243   The arrays passed in must be at least as large as `DMGetNumAuxiliaryVec()`.
9244 
9245 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMSetAuxiliaryVec()`, `DMCopyAuxiliaryVec()`
9246 @*/
9247 PetscErrorCode DMGetAuxiliaryLabels(DM dm, DMLabel labels[], PetscInt values[], PetscInt parts[])
9248 {
9249   PetscHashAuxKey *keys;
9250   PetscInt         n, i, off = 0;
9251 
9252   PetscFunctionBegin;
9253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9254   PetscAssertPointer(labels, 2);
9255   PetscAssertPointer(values, 3);
9256   PetscAssertPointer(parts, 4);
9257   PetscCall(DMGetNumAuxiliaryVec(dm, &n));
9258   PetscCall(PetscMalloc1(n, &keys));
9259   PetscCall(PetscHMapAuxGetKeys(dm->auxData, &off, keys));
9260   for (i = 0; i < n; ++i) {
9261     labels[i] = keys[i].label;
9262     values[i] = keys[i].value;
9263     parts[i]  = keys[i].part;
9264   }
9265   PetscCall(PetscFree(keys));
9266   PetscFunctionReturn(PETSC_SUCCESS);
9267 }
9268 
9269 /*@
9270   DMCopyAuxiliaryVec - Copy the auxiliary vector data on a `DM` to a new `DM`
9271 
9272   Not Collective
9273 
9274   Input Parameter:
9275 . dm - The `DM`
9276 
9277   Output Parameter:
9278 . dmNew - The new `DM`, now with the same auxiliary data
9279 
9280   Level: advanced
9281 
9282   Note:
9283   This is a shallow copy of the auxiliary vectors
9284 
9285 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMSetAuxiliaryVec()`
9286 @*/
9287 PetscErrorCode DMCopyAuxiliaryVec(DM dm, DM dmNew)
9288 {
9289   PetscFunctionBegin;
9290   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9291   PetscValidHeaderSpecific(dmNew, DM_CLASSID, 2);
9292   if (dm == dmNew) PetscFunctionReturn(PETSC_SUCCESS);
9293   PetscCall(DMClearAuxiliaryVec(dmNew));
9294 
9295   PetscCall(PetscHMapAuxDestroy(&dmNew->auxData));
9296   PetscCall(PetscHMapAuxDuplicate(dm->auxData, &dmNew->auxData));
9297   {
9298     Vec     *auxData;
9299     PetscInt n, i, off = 0;
9300 
9301     PetscCall(PetscHMapAuxGetSize(dmNew->auxData, &n));
9302     PetscCall(PetscMalloc1(n, &auxData));
9303     PetscCall(PetscHMapAuxGetVals(dmNew->auxData, &off, auxData));
9304     for (i = 0; i < n; ++i) PetscCall(PetscObjectReference((PetscObject)auxData[i]));
9305     PetscCall(PetscFree(auxData));
9306   }
9307   PetscFunctionReturn(PETSC_SUCCESS);
9308 }
9309 
9310 /*@
9311   DMClearAuxiliaryVec - Destroys the auxiliary vector information and creates a new empty one
9312 
9313   Not Collective
9314 
9315   Input Parameter:
9316 . dm - The `DM`
9317 
9318   Level: advanced
9319 
9320 .seealso: [](ch_dmbase), `DM`, `DMCopyAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMSetAuxiliaryVec()`
9321 @*/
9322 PetscErrorCode DMClearAuxiliaryVec(DM dm)
9323 {
9324   Vec     *auxData;
9325   PetscInt n, i, off = 0;
9326 
9327   PetscFunctionBegin;
9328   PetscCall(PetscHMapAuxGetSize(dm->auxData, &n));
9329   PetscCall(PetscMalloc1(n, &auxData));
9330   PetscCall(PetscHMapAuxGetVals(dm->auxData, &off, auxData));
9331   for (i = 0; i < n; ++i) PetscCall(VecDestroy(&auxData[i]));
9332   PetscCall(PetscFree(auxData));
9333   PetscCall(PetscHMapAuxDestroy(&dm->auxData));
9334   PetscCall(PetscHMapAuxCreate(&dm->auxData));
9335   PetscFunctionReturn(PETSC_SUCCESS);
9336 }
9337 
9338 /*@
9339   DMPolytopeMatchOrientation - Determine an orientation (transformation) that takes the source face arrangement to the target face arrangement
9340 
9341   Not Collective
9342 
9343   Input Parameters:
9344 + ct         - The `DMPolytopeType`
9345 . sourceCone - The source arrangement of faces
9346 - targetCone - The target arrangement of faces
9347 
9348   Output Parameters:
9349 + ornt  - The orientation (transformation) which will take the source arrangement to the target arrangement
9350 - found - Flag indicating that a suitable orientation was found
9351 
9352   Level: advanced
9353 
9354   Note:
9355   An arrangement is a face order combined with an orientation for each face
9356 
9357   Each orientation (transformation) is labeled with an integer from negative `DMPolytopeTypeGetNumArrangements(ct)`/2 to `DMPolytopeTypeGetNumArrangements(ct)`/2
9358   that labels each arrangement (face ordering plus orientation for each face).
9359 
9360   See `DMPolytopeMatchVertexOrientation()` to find a new vertex orientation that takes the source vertex arrangement to the target vertex arrangement
9361 
9362 .seealso: [](ch_dmbase), `DM`, `DMPolytopeGetOrientation()`, `DMPolytopeMatchVertexOrientation()`, `DMPolytopeGetVertexOrientation()`
9363 @*/
9364 PetscErrorCode DMPolytopeMatchOrientation(DMPolytopeType ct, const PetscInt sourceCone[], const PetscInt targetCone[], PetscInt *ornt, PetscBool *found)
9365 {
9366   const PetscInt cS = DMPolytopeTypeGetConeSize(ct);
9367   const PetscInt nO = DMPolytopeTypeGetNumArrangements(ct) / 2;
9368   PetscInt       o, c;
9369 
9370   PetscFunctionBegin;
9371   if (!nO) {
9372     *ornt  = 0;
9373     *found = PETSC_TRUE;
9374     PetscFunctionReturn(PETSC_SUCCESS);
9375   }
9376   for (o = -nO; o < nO; ++o) {
9377     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
9378 
9379     for (c = 0; c < cS; ++c)
9380       if (sourceCone[arr[c * 2]] != targetCone[c]) break;
9381     if (c == cS) {
9382       *ornt = o;
9383       break;
9384     }
9385   }
9386   *found = o == nO ? PETSC_FALSE : PETSC_TRUE;
9387   PetscFunctionReturn(PETSC_SUCCESS);
9388 }
9389 
9390 /*@
9391   DMPolytopeGetOrientation - Determine an orientation (transformation) that takes the source face arrangement to the target face arrangement
9392 
9393   Not Collective
9394 
9395   Input Parameters:
9396 + ct         - The `DMPolytopeType`
9397 . sourceCone - The source arrangement of faces
9398 - targetCone - The target arrangement of faces
9399 
9400   Output Parameter:
9401 . ornt - The orientation (transformation) which will take the source arrangement to the target arrangement
9402 
9403   Level: advanced
9404 
9405   Note:
9406   This function is the same as `DMPolytopeMatchOrientation()` except it will generate an error if no suitable orientation can be found.
9407 
9408   Developer Note:
9409   It is unclear why this function needs to exist since one can simply call `DMPolytopeMatchOrientation()` and error if none is found
9410 
9411 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMPolytopeMatchOrientation()`, `DMPolytopeGetVertexOrientation()`, `DMPolytopeMatchVertexOrientation()`
9412 @*/
9413 PetscErrorCode DMPolytopeGetOrientation(DMPolytopeType ct, const PetscInt sourceCone[], const PetscInt targetCone[], PetscInt *ornt)
9414 {
9415   PetscBool found;
9416 
9417   PetscFunctionBegin;
9418   PetscCall(DMPolytopeMatchOrientation(ct, sourceCone, targetCone, ornt, &found));
9419   PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not find orientation for %s", DMPolytopeTypes[ct]);
9420   PetscFunctionReturn(PETSC_SUCCESS);
9421 }
9422 
9423 /*@
9424   DMPolytopeMatchVertexOrientation - Determine an orientation (transformation) that takes the source vertex arrangement to the target vertex arrangement
9425 
9426   Not Collective
9427 
9428   Input Parameters:
9429 + ct         - The `DMPolytopeType`
9430 . sourceVert - The source arrangement of vertices
9431 - targetVert - The target arrangement of vertices
9432 
9433   Output Parameters:
9434 + ornt  - The orientation (transformation) which will take the source arrangement to the target arrangement
9435 - found - Flag indicating that a suitable orientation was found
9436 
9437   Level: advanced
9438 
9439   Notes:
9440   An arrangement is a vertex order
9441 
9442   Each orientation (transformation) is labeled with an integer from negative `DMPolytopeTypeGetNumArrangements(ct)`/2 to `DMPolytopeTypeGetNumArrangements(ct)`/2
9443   that labels each arrangement (vertex ordering).
9444 
9445   See `DMPolytopeMatchOrientation()` to find a new face orientation that takes the source face arrangement to the target face arrangement
9446 
9447 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMPolytopeGetOrientation()`, `DMPolytopeMatchOrientation()`, `DMPolytopeTypeGetNumVertices()`, `DMPolytopeTypeGetVertexArrangement()`
9448 @*/
9449 PetscErrorCode DMPolytopeMatchVertexOrientation(DMPolytopeType ct, const PetscInt sourceVert[], const PetscInt targetVert[], PetscInt *ornt, PetscBool *found)
9450 {
9451   const PetscInt cS = DMPolytopeTypeGetNumVertices(ct);
9452   const PetscInt nO = DMPolytopeTypeGetNumArrangements(ct) / 2;
9453   PetscInt       o, c;
9454 
9455   PetscFunctionBegin;
9456   if (!nO) {
9457     *ornt  = 0;
9458     *found = PETSC_TRUE;
9459     PetscFunctionReturn(PETSC_SUCCESS);
9460   }
9461   for (o = -nO; o < nO; ++o) {
9462     const PetscInt *arr = DMPolytopeTypeGetVertexArrangement(ct, o);
9463 
9464     for (c = 0; c < cS; ++c)
9465       if (sourceVert[arr[c]] != targetVert[c]) break;
9466     if (c == cS) {
9467       *ornt = o;
9468       break;
9469     }
9470   }
9471   *found = o == nO ? PETSC_FALSE : PETSC_TRUE;
9472   PetscFunctionReturn(PETSC_SUCCESS);
9473 }
9474 
9475 /*@
9476   DMPolytopeGetVertexOrientation - Determine an orientation (transformation) that takes the source vertex arrangement to the target vertex arrangement
9477 
9478   Not Collective
9479 
9480   Input Parameters:
9481 + ct         - The `DMPolytopeType`
9482 . sourceCone - The source arrangement of vertices
9483 - targetCone - The target arrangement of vertices
9484 
9485   Output Parameter:
9486 . ornt - The orientation (transformation) which will take the source arrangement to the target arrangement
9487 
9488   Level: advanced
9489 
9490   Note:
9491   This function is the same as `DMPolytopeMatchVertexOrientation()` except it errors if not orientation is possible.
9492 
9493   Developer Note:
9494   It is unclear why this function needs to exist since one can simply call `DMPolytopeMatchVertexOrientation()` and error if none is found
9495 
9496 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMPolytopeMatchVertexOrientation()`, `DMPolytopeGetOrientation()`
9497 @*/
9498 PetscErrorCode DMPolytopeGetVertexOrientation(DMPolytopeType ct, const PetscInt sourceCone[], const PetscInt targetCone[], PetscInt *ornt)
9499 {
9500   PetscBool found;
9501 
9502   PetscFunctionBegin;
9503   PetscCall(DMPolytopeMatchVertexOrientation(ct, sourceCone, targetCone, ornt, &found));
9504   PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not find orientation for %s", DMPolytopeTypes[ct]);
9505   PetscFunctionReturn(PETSC_SUCCESS);
9506 }
9507 
9508 /*@
9509   DMPolytopeInCellTest - Check whether a point lies inside the reference cell of given type
9510 
9511   Not Collective
9512 
9513   Input Parameters:
9514 + ct    - The `DMPolytopeType`
9515 - point - Coordinates of the point
9516 
9517   Output Parameter:
9518 . inside - Flag indicating whether the point is inside the reference cell of given type
9519 
9520   Level: advanced
9521 
9522 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMLocatePoints()`
9523 @*/
9524 PetscErrorCode DMPolytopeInCellTest(DMPolytopeType ct, const PetscReal point[], PetscBool *inside)
9525 {
9526   PetscReal sum = 0.0;
9527   PetscInt  d;
9528 
9529   PetscFunctionBegin;
9530   *inside = PETSC_TRUE;
9531   switch (ct) {
9532   case DM_POLYTOPE_TRIANGLE:
9533   case DM_POLYTOPE_TETRAHEDRON:
9534     for (d = 0; d < DMPolytopeTypeGetDim(ct); ++d) {
9535       if (point[d] < -1.0) {
9536         *inside = PETSC_FALSE;
9537         break;
9538       }
9539       sum += point[d];
9540     }
9541     if (sum > PETSC_SMALL) {
9542       *inside = PETSC_FALSE;
9543       break;
9544     }
9545     break;
9546   case DM_POLYTOPE_QUADRILATERAL:
9547   case DM_POLYTOPE_HEXAHEDRON:
9548     for (d = 0; d < DMPolytopeTypeGetDim(ct); ++d)
9549       if (PetscAbsReal(point[d]) > 1. + PETSC_SMALL) {
9550         *inside = PETSC_FALSE;
9551         break;
9552       }
9553     break;
9554   default:
9555     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unsupported polytope type %s", DMPolytopeTypes[ct]);
9556   }
9557   PetscFunctionReturn(PETSC_SUCCESS);
9558 }
9559 
9560 /*@
9561   DMReorderSectionSetDefault - Set flag indicating whether the local section should be reordered by default
9562 
9563   Logically collective
9564 
9565   Input Parameters:
9566 + dm      - The DM
9567 - reorder - Flag for reordering
9568 
9569   Level: intermediate
9570 
9571 .seealso: `DMReorderSectionGetDefault()`
9572 @*/
9573 PetscErrorCode DMReorderSectionSetDefault(DM dm, DMReorderDefaultFlag reorder)
9574 {
9575   PetscFunctionBegin;
9576   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9577   PetscTryMethod(dm, "DMReorderSectionSetDefault_C", (DM, DMReorderDefaultFlag), (dm, reorder));
9578   PetscFunctionReturn(PETSC_SUCCESS);
9579 }
9580 
9581 /*@
9582   DMReorderSectionGetDefault - Get flag indicating whether the local section should be reordered by default
9583 
9584   Not collective
9585 
9586   Input Parameter:
9587 . dm - The DM
9588 
9589   Output Parameter:
9590 . reorder - Flag for reordering
9591 
9592   Level: intermediate
9593 
9594 .seealso: `DMReorderSetDefault()`
9595 @*/
9596 PetscErrorCode DMReorderSectionGetDefault(DM dm, DMReorderDefaultFlag *reorder)
9597 {
9598   PetscFunctionBegin;
9599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9600   PetscAssertPointer(reorder, 2);
9601   *reorder = DM_REORDER_DEFAULT_NOTSET;
9602   PetscTryMethod(dm, "DMReorderSectionGetDefault_C", (DM, DMReorderDefaultFlag *), (dm, reorder));
9603   PetscFunctionReturn(PETSC_SUCCESS);
9604 }
9605 
9606 /*@
9607   DMReorderSectionSetType - Set the type of local section reordering
9608 
9609   Logically collective
9610 
9611   Input Parameters:
9612 + dm      - The DM
9613 - reorder - The reordering method
9614 
9615   Level: intermediate
9616 
9617 .seealso: `DMReorderSectionGetType()`, `DMReorderSectionSetDefault()`
9618 @*/
9619 PetscErrorCode DMReorderSectionSetType(DM dm, MatOrderingType reorder)
9620 {
9621   PetscFunctionBegin;
9622   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9623   PetscTryMethod(dm, "DMReorderSectionSetType_C", (DM, MatOrderingType), (dm, reorder));
9624   PetscFunctionReturn(PETSC_SUCCESS);
9625 }
9626 
9627 /*@
9628   DMReorderSectionGetType - Get the reordering type for the local section
9629 
9630   Not collective
9631 
9632   Input Parameter:
9633 . dm - The DM
9634 
9635   Output Parameter:
9636 . reorder - The reordering method
9637 
9638   Level: intermediate
9639 
9640 .seealso: `DMReorderSetDefault()`, `DMReorderSectionGetDefault()`
9641 @*/
9642 PetscErrorCode DMReorderSectionGetType(DM dm, MatOrderingType *reorder)
9643 {
9644   PetscFunctionBegin;
9645   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9646   PetscAssertPointer(reorder, 2);
9647   *reorder = NULL;
9648   PetscTryMethod(dm, "DMReorderSectionGetType_C", (DM, MatOrderingType *), (dm, reorder));
9649   PetscFunctionReturn(PETSC_SUCCESS);
9650 }
9651