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