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