xref: /petsc/src/dm/interface/dm.c (revision cb359cd464fc40f56c50f7eb5c225ee3d03df42f)
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   Fortran Note:
557   Pass in a string 'prefix' of
558   sufficient length to hold the prefix.
559 
560 .seealso: [](ch_dmbase), `DM`, `DMSetOptionsPrefix()`, `DMAppendOptionsPrefix()`, `DMSetFromOptions()`
561 @*/
562 PetscErrorCode DMGetOptionsPrefix(DM dm, const char *prefix[])
563 {
564   PetscFunctionBegin;
565   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
566   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, prefix));
567   PetscFunctionReturn(PETSC_SUCCESS);
568 }
569 
570 static PetscErrorCode DMCountNonCyclicReferences_Internal(DM dm, PetscBool recurseCoarse, PetscBool recurseFine, PetscInt *ncrefct)
571 {
572   PetscInt refct = ((PetscObject)dm)->refct;
573 
574   PetscFunctionBegin;
575   *ncrefct = 0;
576   if (dm->coarseMesh && dm->coarseMesh->fineMesh == dm) {
577     refct--;
578     if (recurseCoarse) {
579       PetscInt coarseCount;
580 
581       PetscCall(DMCountNonCyclicReferences_Internal(dm->coarseMesh, PETSC_TRUE, PETSC_FALSE, &coarseCount));
582       refct += coarseCount;
583     }
584   }
585   if (dm->fineMesh && dm->fineMesh->coarseMesh == dm) {
586     refct--;
587     if (recurseFine) {
588       PetscInt fineCount;
589 
590       PetscCall(DMCountNonCyclicReferences_Internal(dm->fineMesh, PETSC_FALSE, PETSC_TRUE, &fineCount));
591       refct += fineCount;
592     }
593   }
594   *ncrefct = refct;
595   PetscFunctionReturn(PETSC_SUCCESS);
596 }
597 
598 /* Generic wrapper for DMCountNonCyclicReferences_Internal() */
599 PetscErrorCode DMCountNonCyclicReferences(PetscObject dm, PetscInt *ncrefct)
600 {
601   PetscFunctionBegin;
602   PetscCall(DMCountNonCyclicReferences_Internal((DM)dm, PETSC_TRUE, PETSC_TRUE, ncrefct));
603   PetscFunctionReturn(PETSC_SUCCESS);
604 }
605 
606 PetscErrorCode DMDestroyLabelLinkList_Internal(DM dm)
607 {
608   DMLabelLink next = dm->labels;
609 
610   PetscFunctionBegin;
611   /* destroy the labels */
612   while (next) {
613     DMLabelLink tmp = next->next;
614 
615     if (next->label == dm->depthLabel) dm->depthLabel = NULL;
616     if (next->label == dm->celltypeLabel) dm->celltypeLabel = NULL;
617     PetscCall(DMLabelDestroy(&next->label));
618     PetscCall(PetscFree(next));
619     next = tmp;
620   }
621   dm->labels = NULL;
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 static PetscErrorCode DMDestroyCoordinates_Private(DMCoordinates *c)
626 {
627   PetscFunctionBegin;
628   c->dim = PETSC_DEFAULT;
629   PetscCall(DMDestroy(&c->dm));
630   PetscCall(VecDestroy(&c->x));
631   PetscCall(VecDestroy(&c->xl));
632   PetscCall(DMFieldDestroy(&c->field));
633   PetscFunctionReturn(PETSC_SUCCESS);
634 }
635 
636 /*@
637   DMDestroy - Destroys a `DM`.
638 
639   Collective
640 
641   Input Parameter:
642 . dm - the `DM` object to destroy
643 
644   Level: developer
645 
646 .seealso: [](ch_dmbase), `DM`, `DMCreate()`, `DMType`, `DMSetType()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`
647 @*/
648 PetscErrorCode DMDestroy(DM *dm)
649 {
650   PetscInt cnt;
651 
652   PetscFunctionBegin;
653   if (!*dm) PetscFunctionReturn(PETSC_SUCCESS);
654   PetscValidHeaderSpecific(*dm, DM_CLASSID, 1);
655 
656   /* count all non-cyclic references in the doubly-linked list of coarse<->fine meshes */
657   PetscCall(DMCountNonCyclicReferences_Internal(*dm, PETSC_TRUE, PETSC_TRUE, &cnt));
658   --((PetscObject)*dm)->refct;
659   if (--cnt > 0) {
660     *dm = NULL;
661     PetscFunctionReturn(PETSC_SUCCESS);
662   }
663   if (((PetscObject)*dm)->refct < 0) PetscFunctionReturn(PETSC_SUCCESS);
664   ((PetscObject)*dm)->refct = 0;
665 
666   PetscCall(DMClearGlobalVectors(*dm));
667   PetscCall(DMClearLocalVectors(*dm));
668   PetscCall(DMClearNamedGlobalVectors(*dm));
669   PetscCall(DMClearNamedLocalVectors(*dm));
670 
671   /* Destroy the list of hooks */
672   {
673     DMCoarsenHookLink link, next;
674     for (link = (*dm)->coarsenhook; link; link = next) {
675       next = link->next;
676       PetscCall(PetscFree(link));
677     }
678     (*dm)->coarsenhook = NULL;
679   }
680   {
681     DMRefineHookLink link, next;
682     for (link = (*dm)->refinehook; link; link = next) {
683       next = link->next;
684       PetscCall(PetscFree(link));
685     }
686     (*dm)->refinehook = NULL;
687   }
688   {
689     DMSubDomainHookLink link, next;
690     for (link = (*dm)->subdomainhook; link; link = next) {
691       next = link->next;
692       PetscCall(PetscFree(link));
693     }
694     (*dm)->subdomainhook = NULL;
695   }
696   {
697     DMGlobalToLocalHookLink link, next;
698     for (link = (*dm)->gtolhook; link; link = next) {
699       next = link->next;
700       PetscCall(PetscFree(link));
701     }
702     (*dm)->gtolhook = NULL;
703   }
704   {
705     DMLocalToGlobalHookLink link, next;
706     for (link = (*dm)->ltoghook; link; link = next) {
707       next = link->next;
708       PetscCall(PetscFree(link));
709     }
710     (*dm)->ltoghook = NULL;
711   }
712   /* Destroy the work arrays */
713   {
714     DMWorkLink link, next;
715     PetscCheck(!(*dm)->workout, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Work array still checked out %p %p", (void *)(*dm)->workout, (*dm)->workout->mem);
716     for (link = (*dm)->workin; link; link = next) {
717       next = link->next;
718       PetscCall(PetscFree(link->mem));
719       PetscCall(PetscFree(link));
720     }
721     (*dm)->workin = NULL;
722   }
723   /* destroy the labels */
724   PetscCall(DMDestroyLabelLinkList_Internal(*dm));
725   /* destroy the fields */
726   PetscCall(DMClearFields(*dm));
727   /* destroy the boundaries */
728   {
729     DMBoundary next = (*dm)->boundary;
730     while (next) {
731       DMBoundary b = next;
732 
733       next = b->next;
734       PetscCall(PetscFree(b));
735     }
736   }
737 
738   PetscCall(PetscObjectDestroy(&(*dm)->dmksp));
739   PetscCall(PetscObjectDestroy(&(*dm)->dmsnes));
740   PetscCall(PetscObjectDestroy(&(*dm)->dmts));
741 
742   if ((*dm)->ctx && (*dm)->ctxdestroy) PetscCall((*(*dm)->ctxdestroy)(&(*dm)->ctx));
743   PetscCall(MatFDColoringDestroy(&(*dm)->fd));
744   PetscCall(ISLocalToGlobalMappingDestroy(&(*dm)->ltogmap));
745   PetscCall(PetscFree((*dm)->vectype));
746   PetscCall(PetscFree((*dm)->mattype));
747 
748   PetscCall(PetscSectionDestroy(&(*dm)->localSection));
749   PetscCall(PetscSectionDestroy(&(*dm)->globalSection));
750   PetscCall(PetscFree((*dm)->reorderSectionType));
751   PetscCall(PetscLayoutDestroy(&(*dm)->map));
752   PetscCall(PetscSectionDestroy(&(*dm)->defaultConstraint.section));
753   PetscCall(MatDestroy(&(*dm)->defaultConstraint.mat));
754   PetscCall(PetscSFDestroy(&(*dm)->sf));
755   PetscCall(PetscSFDestroy(&(*dm)->sectionSF));
756   if ((*dm)->sfNatural) PetscCall(PetscSFDestroy(&(*dm)->sfNatural));
757   PetscCall(PetscObjectDereference((PetscObject)(*dm)->sfMigration));
758   PetscCall(DMClearAuxiliaryVec(*dm));
759   PetscCall(PetscHMapAuxDestroy(&(*dm)->auxData));
760   if ((*dm)->coarseMesh && (*dm)->coarseMesh->fineMesh == *dm) PetscCall(DMSetFineDM((*dm)->coarseMesh, NULL));
761 
762   PetscCall(DMDestroy(&(*dm)->coarseMesh));
763   if ((*dm)->fineMesh && (*dm)->fineMesh->coarseMesh == *dm) PetscCall(DMSetCoarseDM((*dm)->fineMesh, NULL));
764   PetscCall(DMDestroy(&(*dm)->fineMesh));
765   PetscCall(PetscFree((*dm)->Lstart));
766   PetscCall(PetscFree((*dm)->L));
767   PetscCall(PetscFree((*dm)->maxCell));
768   PetscCall(DMDestroyCoordinates_Private(&(*dm)->coordinates[0]));
769   PetscCall(DMDestroyCoordinates_Private(&(*dm)->coordinates[1]));
770   if ((*dm)->transformDestroy) PetscCall((*(*dm)->transformDestroy)(*dm, (*dm)->transformCtx));
771   PetscCall(DMDestroy(&(*dm)->transformDM));
772   PetscCall(VecDestroy(&(*dm)->transform));
773   for (PetscInt i = 0; i < (*dm)->periodic.num_affines; i++) {
774     PetscCall(VecScatterDestroy(&(*dm)->periodic.affine_to_local[i]));
775     PetscCall(VecDestroy(&(*dm)->periodic.affine[i]));
776   }
777   if ((*dm)->periodic.num_affines > 0) PetscCall(PetscFree2((*dm)->periodic.affine_to_local, (*dm)->periodic.affine));
778 
779   PetscCall(DMClearDS(*dm));
780   PetscCall(DMDestroy(&(*dm)->dmBC));
781   /* if memory was published with SAWs then destroy it */
782   PetscCall(PetscObjectSAWsViewOff((PetscObject)*dm));
783 
784   PetscTryTypeMethod(*dm, destroy);
785   PetscCall(DMMonitorCancel(*dm));
786   PetscCall(DMCeedDestroy(&(*dm)->dmceed));
787 #ifdef PETSC_HAVE_LIBCEED
788   PetscCallCEED(CeedElemRestrictionDestroy(&(*dm)->ceedERestrict));
789   PetscCallCEED(CeedDestroy(&(*dm)->ceed));
790 #endif
791   /* We do not destroy (*dm)->data here so that we can reference count backend objects */
792   PetscCall(PetscHeaderDestroy(dm));
793   PetscFunctionReturn(PETSC_SUCCESS);
794 }
795 
796 /*@
797   DMSetUp - sets up the data structures inside a `DM` object
798 
799   Collective
800 
801   Input Parameter:
802 . dm - the `DM` object to setup
803 
804   Level: intermediate
805 
806   Note:
807   This is usually called after various parameter setting operations and `DMSetFromOptions()` are called on the `DM`
808 
809 .seealso: [](ch_dmbase), `DM`, `DMCreate()`, `DMSetType()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`
810 @*/
811 PetscErrorCode DMSetUp(DM dm)
812 {
813   PetscFunctionBegin;
814   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
815   if (dm->setupcalled) PetscFunctionReturn(PETSC_SUCCESS);
816   PetscTryTypeMethod(dm, setup);
817   dm->setupcalled = PETSC_TRUE;
818   PetscFunctionReturn(PETSC_SUCCESS);
819 }
820 
821 /*@
822   DMSetFromOptions - sets parameters in a `DM` from the options database
823 
824   Collective
825 
826   Input Parameter:
827 . dm - the `DM` object to set options for
828 
829   Options Database Keys:
830 + -dm_preallocate_only                               - Only preallocate the matrix for `DMCreateMatrix()` and `DMCreateMassMatrix()`, but do not fill it with zeros
831 . -dm_vec_type <type>                                - type of vector to create inside `DM`
832 . -dm_mat_type <type>                                - type of matrix to create inside `DM`
833 . -dm_is_coloring_type                               - <global or local>
834 . -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`
835 . -dm_plex_option_phases <ph0_, ph1_, ...>           - List of prefixes for option processing phases
836 . -dm_plex_filename <str>                            - File containing a mesh
837 . -dm_plex_boundary_filename <str>                   - File containing a mesh boundary
838 . -dm_plex_name <str>                                - Name of the mesh in the file
839 . -dm_plex_shape <shape>                             - The domain shape, such as `BOX`, `SPHERE`, etc.
840 . -dm_plex_cell <ct>                                 - Cell shape
841 . -dm_plex_reference_cell_domain <bool>              - Use a reference cell domain
842 . -dm_plex_dim <dim>                                 - Set the topological dimension
843 . -dm_plex_simplex <bool>                            - `PETSC_TRUE` for simplex elements, `PETSC_FALSE` for tensor elements
844 . -dm_plex_interpolate <bool>                        - `PETSC_TRUE` turns on topological interpolation (creating edges and faces)
845 . -dm_plex_orient <bool>                             - `PETSC_TRUE` turns on topological orientation (flipping edges and faces)
846 . -dm_plex_scale <sc>                                - Scale factor for mesh coordinates
847 . -dm_coord_remap <bool>                             - Map coordinates using a function
848 . -dm_plex_coordinate_dim <dim>                      - Change the coordinate dimension of a mesh (usually given with cdm_ prefix)
849 . -dm_coord_map <mapname>                            - Select a builtin coordinate map
850 . -dm_coord_map_params <p0,p1,p2,...>                - Set coordinate mapping parameters
851 . -dm_plex_box_faces <m,n,p>                         - Number of faces along each dimension
852 . -dm_plex_box_lower <x,y,z>                         - Specify lower-left-bottom coordinates for the box
853 . -dm_plex_box_upper <x,y,z>                         - Specify upper-right-top coordinates for the box
854 . -dm_plex_box_bd <bx,by,bz>                         - Specify the `DMBoundaryType` for each direction
855 . -dm_plex_sphere_radius <r>                         - The sphere radius
856 . -dm_plex_ball_radius <r>                           - Radius of the ball
857 . -dm_plex_cylinder_bd <bz>                          - Boundary type in the z direction
858 . -dm_plex_cylinder_num_wedges <n>                   - Number of wedges around the cylinder
859 . -dm_plex_reorder <order>                           - Reorder the mesh using the specified algorithm
860 . -dm_refine_pre <n>                                 - The number of refinements before distribution
861 . -dm_refine_uniform_pre <bool>                      - Flag for uniform refinement before distribution
862 . -dm_refine_volume_limit_pre <v>                    - The maximum cell volume after refinement before distribution
863 . -dm_refine <n>                                     - The number of refinements after distribution
864 . -dm_extrude <l>                                    - Activate extrusion and specify the number of layers to extrude
865 . -dm_plex_transform_extrude_thickness <t>           - The total thickness of extruded layers
866 . -dm_plex_transform_extrude_use_tensor <bool>       - Use tensor cells when extruding
867 . -dm_plex_transform_extrude_symmetric <bool>        - Extrude layers symmetrically about the surface
868 . -dm_plex_transform_extrude_normal <n0,...,nd>      - Specify the extrusion direction
869 . -dm_plex_transform_extrude_thicknesses <t0,...,tl> - Specify thickness of each layer
870 . -dm_plex_create_fv_ghost_cells                     - Flag to create finite volume ghost cells on the boundary
871 . -dm_plex_fv_ghost_cells_label <name>               - Label name for ghost cells boundary
872 . -dm_distribute <bool>                              - Flag to redistribute a mesh among processes
873 . -dm_distribute_overlap <n>                         - The size of the overlap halo
874 . -dm_plex_adj_cone <bool>                           - Set adjacency direction
875 . -dm_plex_adj_closure <bool>                        - Set adjacency size
876 . -dm_plex_use_ceed <bool>                           - Use LibCEED as the FEM backend
877 . -dm_plex_check_symmetry                            - Check that the adjacency information in the mesh is symmetric - `DMPlexCheckSymmetry()`
878 . -dm_plex_check_skeleton                            - Check that each cell has the correct number of vertices (only for homogeneous simplex or tensor meshes) - `DMPlexCheckSkeleton()`
879 . -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()`
880 . -dm_plex_check_geometry                            - Check that cells have positive volume - `DMPlexCheckGeometry()`
881 . -dm_plex_check_pointsf                             - Check some necessary conditions for `PointSF` - `DMPlexCheckPointSF()`
882 . -dm_plex_check_interface_cones                     - Check points on inter-partition interfaces have conforming order of cone points - `DMPlexCheckInterfaceCones()`
883 - -dm_plex_check_all                                 - Perform all the checks above
884 
885   Level: intermediate
886 
887   Note:
888   For some `DMType` such as `DMDA` this cannot be called after `DMSetUp()` has been called.
889 
890 .seealso: [](ch_dmbase), `DM`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`,
891          `DMPlexCheckSymmetry()`, `DMPlexCheckSkeleton()`, `DMPlexCheckFaces()`, `DMPlexCheckGeometry()`, `DMPlexCheckPointSF()`, `DMPlexCheckInterfaceCones()`,
892          `DMSetOptionsPrefix()`, `DMType`, `DMPLEX`, `DMDA`, `DMSetUp()`
893 @*/
894 PetscErrorCode DMSetFromOptions(DM dm)
895 {
896   char      typeName[256];
897   PetscBool flg;
898 
899   PetscFunctionBegin;
900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
901   dm->setfromoptionscalled = PETSC_TRUE;
902   if (dm->sf) PetscCall(PetscSFSetFromOptions(dm->sf));
903   if (dm->sectionSF) PetscCall(PetscSFSetFromOptions(dm->sectionSF));
904   if (dm->coordinates[0].dm) PetscCall(DMSetFromOptions(dm->coordinates[0].dm));
905   PetscObjectOptionsBegin((PetscObject)dm);
906   PetscCall(PetscOptionsBool("-dm_preallocate_only", "only preallocate matrix, but do not set column indices", "DMSetMatrixPreallocateOnly", dm->prealloc_only, &dm->prealloc_only, NULL));
907   PetscCall(PetscOptionsFList("-dm_vec_type", "Vector type used for created vectors", "DMSetVecType", VecList, dm->vectype, typeName, 256, &flg));
908   if (flg) PetscCall(DMSetVecType(dm, typeName));
909   PetscCall(PetscOptionsFList("-dm_mat_type", "Matrix type used for created matrices", "DMSetMatType", MatList, dm->mattype ? dm->mattype : typeName, typeName, sizeof(typeName), &flg));
910   if (flg) PetscCall(DMSetMatType(dm, typeName));
911   PetscCall(PetscOptionsEnum("-dm_blocking_type", "Topological point or field node blocking", "DMSetBlockingType", DMBlockingTypes, (PetscEnum)dm->blocking_type, (PetscEnum *)&dm->blocking_type, NULL));
912   PetscCall(PetscOptionsEnum("-dm_is_coloring_type", "Global or local coloring of Jacobian", "DMSetISColoringType", ISColoringTypes, (PetscEnum)dm->coloringtype, (PetscEnum *)&dm->coloringtype, NULL));
913   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));
914   PetscCall(PetscOptionsBool("-dm_ignore_perm_output", "Ignore the local section permutation on output", "DMGetOutputDM", dm->ignorePermOutput, &dm->ignorePermOutput, NULL));
915   PetscTryTypeMethod(dm, setfromoptions, PetscOptionsObject);
916   /* process any options handlers added with PetscObjectAddOptionsHandler() */
917   PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)dm, PetscOptionsObject));
918   PetscOptionsEnd();
919   PetscFunctionReturn(PETSC_SUCCESS);
920 }
921 
922 /*@
923   DMViewFromOptions - View a `DM` in a particular way based on a request in the options database
924 
925   Collective
926 
927   Input Parameters:
928 + dm   - the `DM` object
929 . obj  - optional object that provides the prefix for the options database (if `NULL` then the prefix in obj is used)
930 - name - option string that is used to activate viewing
931 
932   Level: intermediate
933 
934   Note:
935   See `PetscObjectViewFromOptions()` for a list of values that can be provided in the options database to determine how the `DM` is viewed
936 
937 .seealso: [](ch_dmbase), `DM`, `DMView()`, `PetscObjectViewFromOptions()`, `DMCreate()`
938 @*/
939 PetscErrorCode DMViewFromOptions(DM dm, PetscObject obj, const char name[])
940 {
941   PetscFunctionBegin;
942   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
943   PetscCall(PetscObjectViewFromOptions((PetscObject)dm, obj, name));
944   PetscFunctionReturn(PETSC_SUCCESS);
945 }
946 
947 /*@
948   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
949   save the `DM` in a binary file to be loaded later or create a visualization of the `DM`
950 
951   Collective
952 
953   Input Parameters:
954 + dm - the `DM` object to view
955 - v  - the viewer
956 
957   Level: beginner
958 
959   Notes:
960 
961   `PetscViewer` = `PETSCVIEWERHDF5` i.e. HDF5 format can be used with `PETSC_VIEWER_HDF5_PETSC` as the `PetscViewerFormat` to save multiple `DMPLEX`
962   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
963   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
964 
965   `PetscViewer` = `PETSCVIEWEREXODUSII` i.e. ExodusII format assumes that element blocks (mapped to "Cell sets" labels)
966   consists of sequentially numbered cells.
967 
968   If `dm` has been distributed, only the part of the `DM` on MPI rank 0 (including "ghost" cells and vertices) will be written.
969 
970   Only TRI, TET, QUAD, and HEX cells are supported.
971 
972   `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.
973   The order of the mesh shall be set using `PetscViewerExodusIISetOrder()`
974 
975   Variable names can be set and queried using `PetscViewerExodusII[Set/Get][Nodal/Zonal]VariableNames[s]`.
976 
977 .seealso: [](ch_dmbase), `DM`, `PetscViewer`, `PetscViewerFormat`, `PetscViewerSetFormat()`, `DMDestroy()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMLoad()`, `PetscObjectSetName()`
978 @*/
979 PetscErrorCode DMView(DM dm, PetscViewer v)
980 {
981   PetscBool         isbinary;
982   PetscMPIInt       size;
983   PetscViewerFormat format;
984 
985   PetscFunctionBegin;
986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
987   if (!v) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)dm), &v));
988   PetscValidHeaderSpecific(v, PETSC_VIEWER_CLASSID, 2);
989   /* Ideally, we would like to have this test on.
990      However, it currently breaks socket viz via GLVis.
991      During DMView(parallel_mesh,glvis_viewer), each
992      process opens a sequential ASCII socket to visualize
993      the local mesh, and PetscObjectView(dm,local_socket)
994      is internally called inside VecView_GLVis, incurring
995      in an error here */
996   /* PetscCheckSameComm(dm,1,v,2); */
997   PetscCall(PetscViewerCheckWritable(v));
998 
999   PetscCall(PetscLogEventBegin(DM_View, v, 0, 0, 0));
1000   PetscCall(PetscViewerGetFormat(v, &format));
1001   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1002   if (size == 1 && format == PETSC_VIEWER_LOAD_BALANCE) PetscFunctionReturn(PETSC_SUCCESS);
1003   PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)dm, v));
1004   PetscCall(PetscObjectTypeCompare((PetscObject)v, PETSCVIEWERBINARY, &isbinary));
1005   if (isbinary) {
1006     PetscInt classid = DM_FILE_CLASSID;
1007     char     type[256];
1008 
1009     PetscCall(PetscViewerBinaryWrite(v, &classid, 1, PETSC_INT));
1010     PetscCall(PetscStrncpy(type, ((PetscObject)dm)->type_name, sizeof(type)));
1011     PetscCall(PetscViewerBinaryWrite(v, type, 256, PETSC_CHAR));
1012   }
1013   PetscTryTypeMethod(dm, view, v);
1014   PetscCall(PetscLogEventEnd(DM_View, v, 0, 0, 0));
1015   PetscFunctionReturn(PETSC_SUCCESS);
1016 }
1017 
1018 /*@
1019   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,
1020   that is it has no ghost locations.
1021 
1022   Collective
1023 
1024   Input Parameter:
1025 . dm - the `DM` object
1026 
1027   Output Parameter:
1028 . vec - the global vector
1029 
1030   Level: beginner
1031 
1032 .seealso: [](ch_dmbase), `DM`, `Vec`, `DMCreateLocalVector()`, `DMGetGlobalVector()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`,
1033          `DMGlobalToLocalBegin()`, `DMGlobalToLocalEnd()`
1034 @*/
1035 PetscErrorCode DMCreateGlobalVector(DM dm, Vec *vec)
1036 {
1037   PetscFunctionBegin;
1038   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1039   PetscAssertPointer(vec, 2);
1040   PetscUseTypeMethod(dm, createglobalvector, vec);
1041   if (PetscDefined(USE_DEBUG)) {
1042     DM vdm;
1043 
1044     PetscCall(VecGetDM(*vec, &vdm));
1045     PetscCheck(vdm, PETSC_COMM_SELF, PETSC_ERR_PLIB, "DM type '%s' did not attach the DM to the vector", ((PetscObject)dm)->type_name);
1046   }
1047   PetscFunctionReturn(PETSC_SUCCESS);
1048 }
1049 
1050 /*@
1051   DMCreateLocalVector - Creates a local vector from a `DM` object.
1052 
1053   Not Collective
1054 
1055   Input Parameter:
1056 . dm - the `DM` object
1057 
1058   Output Parameter:
1059 . vec - the local vector
1060 
1061   Level: beginner
1062 
1063   Note:
1064   A local vector usually has ghost locations that contain values that are owned by different MPI ranks. A global vector has no ghost locations.
1065 
1066 .seealso: [](ch_dmbase), `DM`, `Vec`, `DMCreateGlobalVector()`, `DMGetLocalVector()`, `DMDestroy()`, `DMView()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`
1067          `DMGlobalToLocalBegin()`, `DMGlobalToLocalEnd()`
1068 @*/
1069 PetscErrorCode DMCreateLocalVector(DM dm, Vec *vec)
1070 {
1071   PetscFunctionBegin;
1072   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1073   PetscAssertPointer(vec, 2);
1074   PetscUseTypeMethod(dm, createlocalvector, vec);
1075   if (PetscDefined(USE_DEBUG)) {
1076     DM vdm;
1077 
1078     PetscCall(VecGetDM(*vec, &vdm));
1079     PetscCheck(vdm, PETSC_COMM_SELF, PETSC_ERR_LIB, "DM type '%s' did not attach the DM to the vector", ((PetscObject)dm)->type_name);
1080   }
1081   PetscFunctionReturn(PETSC_SUCCESS);
1082 }
1083 
1084 /*@
1085   DMGetLocalToGlobalMapping - Accesses the local-to-global mapping in a `DM`.
1086 
1087   Collective
1088 
1089   Input Parameter:
1090 . dm - the `DM` that provides the mapping
1091 
1092   Output Parameter:
1093 . ltog - the mapping
1094 
1095   Level: advanced
1096 
1097   Notes:
1098   The global to local mapping allows one to set values into the global vector or matrix using `VecSetValuesLocal()` and `MatSetValuesLocal()`
1099 
1100   Vectors obtained with  `DMCreateGlobalVector()` and matrices obtained with `DMCreateMatrix()` already contain the global mapping so you do
1101   need to use this function with those objects.
1102 
1103   This mapping can then be used by `VecSetLocalToGlobalMapping()` or `MatSetLocalToGlobalMapping()`.
1104 
1105 .seealso: [](ch_dmbase), `DM`, `DMCreateLocalVector()`, `DMCreateGlobalVector()`, `VecSetLocalToGlobalMapping()`, `MatSetLocalToGlobalMapping()`,
1106           `DMCreateMatrix()`
1107 @*/
1108 PetscErrorCode DMGetLocalToGlobalMapping(DM dm, ISLocalToGlobalMapping *ltog)
1109 {
1110   PetscInt bs = -1, bsLocal[2], bsMinMax[2];
1111 
1112   PetscFunctionBegin;
1113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1114   PetscAssertPointer(ltog, 2);
1115   if (!dm->ltogmap) {
1116     PetscSection section, sectionGlobal;
1117 
1118     PetscCall(DMGetLocalSection(dm, &section));
1119     if (section) {
1120       const PetscInt *cdofs;
1121       PetscInt       *ltog;
1122       PetscInt        pStart, pEnd, n, p, k, l;
1123 
1124       PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
1125       PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
1126       PetscCall(PetscSectionGetStorageSize(section, &n));
1127       PetscCall(PetscMalloc1(n, &ltog)); /* We want the local+overlap size */
1128       for (p = pStart, l = 0; p < pEnd; ++p) {
1129         PetscInt bdof, cdof, dof, off, c, cind;
1130 
1131         /* Should probably use constrained dofs */
1132         PetscCall(PetscSectionGetDof(section, p, &dof));
1133         PetscCall(PetscSectionGetConstraintDof(section, p, &cdof));
1134         PetscCall(PetscSectionGetConstraintIndices(section, p, &cdofs));
1135         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &off));
1136         /* If you have dofs, and constraints, and they are unequal, we set the blocksize to 1 */
1137         bdof = cdof && (dof - cdof) ? 1 : dof;
1138         if (dof) bs = bs < 0 ? bdof : PetscGCD(bs, bdof);
1139 
1140         for (c = 0, cind = 0; c < dof; ++c, ++l) {
1141           if (cind < cdof && c == cdofs[cind]) {
1142             ltog[l] = off < 0 ? off - c : -(off + c + 1);
1143             cind++;
1144           } else {
1145             ltog[l] = (off < 0 ? -(off + 1) : off) + c - cind;
1146           }
1147         }
1148       }
1149       /* Must have same blocksize on all procs (some might have no points) */
1150       bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
1151       bsLocal[1] = bs;
1152       PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
1153       if (bsMinMax[0] != bsMinMax[1]) {
1154         bs = 1;
1155       } else {
1156         bs = bsMinMax[0];
1157       }
1158       bs = bs < 0 ? 1 : bs;
1159       /* Must reduce indices by blocksize */
1160       if (bs > 1) {
1161         for (l = 0, k = 0; l < n; l += bs, ++k) {
1162           // Integer division of negative values truncates toward zero(!), not toward negative infinity
1163           ltog[k] = ltog[l] >= 0 ? ltog[l] / bs : -(-(ltog[l] + 1) / bs + 1);
1164         }
1165         n /= bs;
1166       }
1167       PetscCall(ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)dm), bs, n, ltog, PETSC_OWN_POINTER, &dm->ltogmap));
1168     } else PetscUseTypeMethod(dm, getlocaltoglobalmapping);
1169   }
1170   *ltog = dm->ltogmap;
1171   PetscFunctionReturn(PETSC_SUCCESS);
1172 }
1173 
1174 /*@
1175   DMGetBlockSize - Gets the inherent block size associated with a `DM`
1176 
1177   Not Collective
1178 
1179   Input Parameter:
1180 . dm - the `DM` with block structure
1181 
1182   Output Parameter:
1183 . bs - the block size, 1 implies no exploitable block structure
1184 
1185   Level: intermediate
1186 
1187   Notes:
1188   This might be the number of degrees of freedom at each grid point for a structured grid.
1189 
1190   Complex `DM` that represent multiphysics or staggered grids or mixed-methods do not generally have a single inherent block size, but
1191   rather different locations in the vectors may have a different block size.
1192 
1193 .seealso: [](ch_dmbase), `DM`, `ISCreateBlock()`, `VecSetBlockSize()`, `MatSetBlockSize()`, `DMGetLocalToGlobalMapping()`
1194 @*/
1195 PetscErrorCode DMGetBlockSize(DM dm, PetscInt *bs)
1196 {
1197   PetscFunctionBegin;
1198   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1199   PetscAssertPointer(bs, 2);
1200   PetscCheck(dm->bs >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM does not have enough information to provide a block size yet");
1201   *bs = dm->bs;
1202   PetscFunctionReturn(PETSC_SUCCESS);
1203 }
1204 
1205 /*@
1206   DMCreateInterpolation - Gets the interpolation matrix between two `DM` objects. The resulting matrix map degrees of freedom in the vector obtained by
1207   `DMCreateGlobalVector()` on the coarse `DM` to similar vectors on the fine grid `DM`.
1208 
1209   Collective
1210 
1211   Input Parameters:
1212 + dmc - the `DM` object
1213 - dmf - the second, finer `DM` object
1214 
1215   Output Parameters:
1216 + mat - the interpolation
1217 - vec - the scaling (optional, pass `NULL` if not needed), see `DMCreateInterpolationScale()`
1218 
1219   Level: developer
1220 
1221   Notes:
1222   For `DMDA` objects this only works for "uniform refinement", that is the refined mesh was obtained `DMRefine()` or the coarse mesh was obtained by
1223   DMCoarsen(). The coordinates set into the `DMDA` are completely ignored in computing the interpolation.
1224 
1225   For `DMDA` objects you can use this interpolation (more precisely the interpolation from the `DMGetCoordinateDM()`) to interpolate the mesh coordinate
1226   vectors EXCEPT in the periodic case where it does not make sense since the coordinate vectors are not periodic.
1227 
1228 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateRestriction()`, `DMCreateInterpolationScale()`
1229 @*/
1230 PetscErrorCode DMCreateInterpolation(DM dmc, DM dmf, Mat *mat, Vec *vec)
1231 {
1232   PetscFunctionBegin;
1233   PetscValidHeaderSpecific(dmc, DM_CLASSID, 1);
1234   PetscValidHeaderSpecific(dmf, DM_CLASSID, 2);
1235   PetscAssertPointer(mat, 3);
1236   PetscCall(PetscLogEventBegin(DM_CreateInterpolation, dmc, dmf, 0, 0));
1237   PetscUseTypeMethod(dmc, createinterpolation, dmf, mat, vec);
1238   PetscCall(PetscLogEventEnd(DM_CreateInterpolation, dmc, dmf, 0, 0));
1239   PetscFunctionReturn(PETSC_SUCCESS);
1240 }
1241 
1242 /*@
1243   DMCreateInterpolationScale - Forms L = 1/(R*1) where 1 is the vector of all ones, and R is
1244   the transpose of the interpolation between the `DM`.
1245 
1246   Input Parameters:
1247 + dac - `DM` that defines a coarse mesh
1248 . daf - `DM` that defines a fine mesh
1249 - mat - the restriction (or interpolation operator) from fine to coarse
1250 
1251   Output Parameter:
1252 . scale - the scaled vector
1253 
1254   Level: advanced
1255 
1256   Note:
1257   xcoarse = diag(L)*R*xfine preserves scale and is thus suitable for state (versus residual)
1258   restriction. In other words xcoarse is the coarse representation of xfine.
1259 
1260   Developer Note:
1261   If the fine-scale `DMDA` has the -dm_bind_below option set to true, then `DMCreateInterpolationScale()` calls `MatSetBindingPropagates()`
1262   on the restriction/interpolation operator to set the bindingpropagates flag to true.
1263 
1264 .seealso: [](ch_dmbase), `DM`, `MatRestrict()`, `MatInterpolate()`, `DMCreateInterpolation()`, `DMCreateRestriction()`, `DMCreateGlobalVector()`
1265 @*/
1266 PetscErrorCode DMCreateInterpolationScale(DM dac, DM daf, Mat mat, Vec *scale)
1267 {
1268   Vec         fine;
1269   PetscScalar one = 1.0;
1270 #if defined(PETSC_HAVE_CUDA)
1271   PetscBool bindingpropagates, isbound;
1272 #endif
1273 
1274   PetscFunctionBegin;
1275   PetscCall(DMCreateGlobalVector(daf, &fine));
1276   PetscCall(DMCreateGlobalVector(dac, scale));
1277   PetscCall(VecSet(fine, one));
1278 #if defined(PETSC_HAVE_CUDA)
1279   /* If the 'fine' Vec is bound to the CPU, it makes sense to bind 'mat' as well.
1280    * Note that we only do this for the CUDA case, right now, but if we add support for MatMultTranspose() via ViennaCL,
1281    * we'll need to do it for that case, too.*/
1282   PetscCall(VecGetBindingPropagates(fine, &bindingpropagates));
1283   if (bindingpropagates) {
1284     PetscCall(MatSetBindingPropagates(mat, PETSC_TRUE));
1285     PetscCall(VecBoundToCPU(fine, &isbound));
1286     PetscCall(MatBindToCPU(mat, isbound));
1287   }
1288 #endif
1289   PetscCall(MatRestrict(mat, fine, *scale));
1290   PetscCall(VecDestroy(&fine));
1291   PetscCall(VecReciprocal(*scale));
1292   PetscFunctionReturn(PETSC_SUCCESS);
1293 }
1294 
1295 /*@
1296   DMCreateRestriction - Gets restriction matrix between two `DM` objects. The resulting matrix map degrees of freedom in the vector obtained by
1297   `DMCreateGlobalVector()` on the fine `DM` to similar vectors on the coarse grid `DM`.
1298 
1299   Collective
1300 
1301   Input Parameters:
1302 + dmc - the `DM` object
1303 - dmf - the second, finer `DM` object
1304 
1305   Output Parameter:
1306 . mat - the restriction
1307 
1308   Level: developer
1309 
1310   Note:
1311   This only works for `DMSTAG`. For many situations either the transpose of the operator obtained with `DMCreateInterpolation()` or that
1312   matrix multiplied by the vector obtained with `DMCreateInterpolationScale()` provides the desired object.
1313 
1314 .seealso: [](ch_dmbase), `DM`, `DMRestrict()`, `DMInterpolate()`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateInterpolation()`
1315 @*/
1316 PetscErrorCode DMCreateRestriction(DM dmc, DM dmf, Mat *mat)
1317 {
1318   PetscFunctionBegin;
1319   PetscValidHeaderSpecific(dmc, DM_CLASSID, 1);
1320   PetscValidHeaderSpecific(dmf, DM_CLASSID, 2);
1321   PetscAssertPointer(mat, 3);
1322   PetscCall(PetscLogEventBegin(DM_CreateRestriction, dmc, dmf, 0, 0));
1323   PetscUseTypeMethod(dmc, createrestriction, dmf, mat);
1324   PetscCall(PetscLogEventEnd(DM_CreateRestriction, dmc, dmf, 0, 0));
1325   PetscFunctionReturn(PETSC_SUCCESS);
1326 }
1327 
1328 /*@
1329   DMCreateInjection - Gets injection matrix between two `DM` objects.
1330 
1331   Collective
1332 
1333   Input Parameters:
1334 + dac - the `DM` object
1335 - daf - the second, finer `DM` object
1336 
1337   Output Parameter:
1338 . mat - the injection
1339 
1340   Level: developer
1341 
1342   Notes:
1343   This is an operator that applied to a vector obtained with `DMCreateGlobalVector()` on the
1344   fine grid maps the values to a vector on the vector on the coarse `DM` by simply selecting
1345   the values on the coarse grid points. This compares to the operator obtained by
1346   `DMCreateRestriction()` or the transpose of the operator obtained by
1347   `DMCreateInterpolation()` that uses a "local weighted average" of the values around the
1348   coarse grid point as the coarse grid value.
1349 
1350   For `DMDA` objects this only works for "uniform refinement", that is the refined mesh was obtained `DMRefine()` or the coarse mesh was obtained by
1351   `DMCoarsen()`. The coordinates set into the `DMDA` are completely ignored in computing the injection.
1352 
1353 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMCreateInterpolation()`,
1354           `DMCreateRestriction()`, `MatRestrict()`, `MatInterpolate()`
1355 @*/
1356 PetscErrorCode DMCreateInjection(DM dac, DM daf, Mat *mat)
1357 {
1358   PetscFunctionBegin;
1359   PetscValidHeaderSpecific(dac, DM_CLASSID, 1);
1360   PetscValidHeaderSpecific(daf, DM_CLASSID, 2);
1361   PetscAssertPointer(mat, 3);
1362   PetscCall(PetscLogEventBegin(DM_CreateInjection, dac, daf, 0, 0));
1363   PetscUseTypeMethod(dac, createinjection, daf, mat);
1364   PetscCall(PetscLogEventEnd(DM_CreateInjection, dac, daf, 0, 0));
1365   PetscFunctionReturn(PETSC_SUCCESS);
1366 }
1367 
1368 /*@
1369   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
1370   a Galerkin finite element model on the `DM`
1371 
1372   Collective
1373 
1374   Input Parameters:
1375 + dmc - the target `DM` object
1376 - dmf - the source `DM` object, can be `NULL`
1377 
1378   Output Parameter:
1379 . mat - the mass matrix
1380 
1381   Level: developer
1382 
1383   Notes:
1384   For `DMPLEX` the finite element model for the `DM` must have been already provided.
1385 
1386   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()`
1387 
1388 .seealso: [](ch_dmbase), `DM`, `DMCreateMassMatrixLumped()`, `DMCreateMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateRestriction()`, `DMCreateInterpolation()`, `DMCreateInjection()`
1389 @*/
1390 PetscErrorCode DMCreateMassMatrix(DM dmc, DM dmf, Mat *mat)
1391 {
1392   PetscFunctionBegin;
1393   PetscValidHeaderSpecific(dmc, DM_CLASSID, 1);
1394   if (!dmf) dmf = dmc;
1395   PetscValidHeaderSpecific(dmf, DM_CLASSID, 2);
1396   PetscAssertPointer(mat, 3);
1397   PetscCall(PetscLogEventBegin(DM_CreateMassMatrix, dmc, dmf, 0, 0));
1398   PetscUseTypeMethod(dmc, createmassmatrix, dmf, mat);
1399   PetscCall(PetscLogEventEnd(DM_CreateMassMatrix, dmc, dmf, 0, 0));
1400   PetscFunctionReturn(PETSC_SUCCESS);
1401 }
1402 
1403 /*@
1404   DMCreateMassMatrixLumped - Gets the lumped mass matrix for a given `DM`
1405 
1406   Collective
1407 
1408   Input Parameter:
1409 . dm - the `DM` object
1410 
1411   Output Parameters:
1412 + llm - the local lumped mass matrix, which is a diagonal matrix, represented as a vector
1413 - lm  - the global lumped mass matrix, which is a diagonal matrix, represented as a vector
1414 
1415   Level: developer
1416 
1417   Note:
1418   See `DMCreateMassMatrix()` for how to create the non-lumped version of the mass matrix.
1419 
1420 .seealso: [](ch_dmbase), `DM`, `DMCreateMassMatrix()`, `DMCreateMatrix()`, `DMRefine()`, `DMCoarsen()`, `DMCreateRestriction()`, `DMCreateInterpolation()`, `DMCreateInjection()`
1421 @*/
1422 PetscErrorCode DMCreateMassMatrixLumped(DM dm, Vec *llm, Vec *lm)
1423 {
1424   PetscFunctionBegin;
1425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1426   if (llm) PetscAssertPointer(llm, 2);
1427   if (lm) PetscAssertPointer(lm, 3);
1428   if (llm || lm) PetscUseTypeMethod(dm, createmassmatrixlumped, llm, lm);
1429   PetscFunctionReturn(PETSC_SUCCESS);
1430 }
1431 
1432 /*@
1433   DMCreateColoring - Gets coloring of a graph associated with the `DM`. Often the graph represents the operator matrix associated with the discretization
1434   of a PDE on the `DM`.
1435 
1436   Collective
1437 
1438   Input Parameters:
1439 + dm    - the `DM` object
1440 - ctype - `IS_COLORING_LOCAL` or `IS_COLORING_GLOBAL`
1441 
1442   Output Parameter:
1443 . coloring - the coloring
1444 
1445   Level: developer
1446 
1447   Notes:
1448   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
1449   matrix comes from (what this function provides). In general using the mesh produces a more optimal coloring (fewer colors).
1450 
1451   This produces a coloring with the distance of 2, see `MatSetColoringDistance()` which can be used for efficiently computing Jacobians with `MatFDColoringCreate()`
1452   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,
1453   otherwise an error will be generated.
1454 
1455 .seealso: [](ch_dmbase), `DM`, `ISColoring`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatType()`, `MatColoring`, `MatFDColoringCreate()`
1456 @*/
1457 PetscErrorCode DMCreateColoring(DM dm, ISColoringType ctype, ISColoring *coloring)
1458 {
1459   PetscFunctionBegin;
1460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1461   PetscAssertPointer(coloring, 3);
1462   PetscUseTypeMethod(dm, getcoloring, ctype, coloring);
1463   PetscFunctionReturn(PETSC_SUCCESS);
1464 }
1465 
1466 /*@
1467   DMCreateMatrix - Gets an empty matrix for a `DM` that is most commonly used to store the Jacobian of a discrete PDE operator.
1468 
1469   Collective
1470 
1471   Input Parameter:
1472 . dm - the `DM` object
1473 
1474   Output Parameter:
1475 . mat - the empty Jacobian
1476 
1477   Options Database Key:
1478 . -dm_preallocate_only - Only preallocate the matrix for `DMCreateMatrix()` and `DMCreateMassMatrix()`, but do not fill it with zeros
1479 
1480   Level: beginner
1481 
1482   Notes:
1483   This properly preallocates the number of nonzeros in the sparse matrix so you
1484   do not need to do it yourself.
1485 
1486   By default it also sets the nonzero structure and puts in the zero entries. To prevent setting
1487   the nonzero pattern call `DMSetMatrixPreallocateOnly()`
1488 
1489   For `DMDA`, when you call `MatView()` on this matrix it is displayed using the global natural ordering, NOT in the ordering used
1490   internally by PETSc.
1491 
1492   For `DMDA`, in general it is easiest to use `MatSetValuesStencil()` or `MatSetValuesLocal()` to put values into the matrix because
1493   `MatSetValues()` requires the indices for the global numbering for the `DMDA` which is complic`ated to compute
1494 
1495 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMSetMatType()`, `DMCreateMassMatrix()`
1496 @*/
1497 PetscErrorCode DMCreateMatrix(DM dm, Mat *mat)
1498 {
1499   PetscFunctionBegin;
1500   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1501   PetscAssertPointer(mat, 2);
1502   PetscCall(MatInitializePackage());
1503   PetscCall(PetscLogEventBegin(DM_CreateMatrix, 0, 0, 0, 0));
1504   PetscUseTypeMethod(dm, creatematrix, mat);
1505   if (PetscDefined(USE_DEBUG)) {
1506     DM mdm;
1507 
1508     PetscCall(MatGetDM(*mat, &mdm));
1509     PetscCheck(mdm, PETSC_COMM_SELF, PETSC_ERR_PLIB, "DM type '%s' did not attach the DM to the matrix", ((PetscObject)dm)->type_name);
1510   }
1511   /* Handle nullspace and near nullspace */
1512   if (dm->Nf) {
1513     MatNullSpace nullSpace;
1514     PetscInt     Nf, f;
1515 
1516     PetscCall(DMGetNumFields(dm, &Nf));
1517     for (f = 0; f < Nf; ++f) {
1518       if (dm->nullspaceConstructors[f]) {
1519         PetscCall((*dm->nullspaceConstructors[f])(dm, f, f, &nullSpace));
1520         PetscCall(MatSetNullSpace(*mat, nullSpace));
1521         PetscCall(MatNullSpaceDestroy(&nullSpace));
1522         break;
1523       }
1524     }
1525     for (f = 0; f < Nf; ++f) {
1526       if (dm->nearnullspaceConstructors[f]) {
1527         PetscCall((*dm->nearnullspaceConstructors[f])(dm, f, f, &nullSpace));
1528         PetscCall(MatSetNearNullSpace(*mat, nullSpace));
1529         PetscCall(MatNullSpaceDestroy(&nullSpace));
1530       }
1531     }
1532   }
1533   PetscCall(PetscLogEventEnd(DM_CreateMatrix, 0, 0, 0, 0));
1534   PetscFunctionReturn(PETSC_SUCCESS);
1535 }
1536 
1537 /*@
1538   DMSetMatrixPreallocateSkip - When `DMCreateMatrix()` is called the matrix sizes and
1539   `ISLocalToGlobalMapping` will be properly set, but the data structures to store values in the
1540   matrices will not be preallocated.
1541 
1542   Logically Collective
1543 
1544   Input Parameters:
1545 + dm   - the `DM`
1546 - skip - `PETSC_TRUE` to skip preallocation
1547 
1548   Level: developer
1549 
1550   Note:
1551   This is most useful to reduce initialization costs when `MatSetPreallocationCOO()` and
1552   `MatSetValuesCOO()` will be used.
1553 
1554 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `DMSetMatrixStructureOnly()`, `DMSetMatrixPreallocateOnly()`
1555 @*/
1556 PetscErrorCode DMSetMatrixPreallocateSkip(DM dm, PetscBool skip)
1557 {
1558   PetscFunctionBegin;
1559   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1560   dm->prealloc_skip = skip;
1561   PetscFunctionReturn(PETSC_SUCCESS);
1562 }
1563 
1564 /*@
1565   DMSetMatrixPreallocateOnly - When `DMCreateMatrix()` is called the matrix will be properly
1566   preallocated but the nonzero structure and zero values will not be set.
1567 
1568   Logically Collective
1569 
1570   Input Parameters:
1571 + dm   - the `DM`
1572 - only - `PETSC_TRUE` if only want preallocation
1573 
1574   Options Database Key:
1575 . -dm_preallocate_only - Only preallocate the matrix for `DMCreateMatrix()`, `DMCreateMassMatrix()`, but do not fill it with zeros
1576 
1577   Level: developer
1578 
1579 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMSetMatrixStructureOnly()`, `DMSetMatrixPreallocateSkip()`
1580 @*/
1581 PetscErrorCode DMSetMatrixPreallocateOnly(DM dm, PetscBool only)
1582 {
1583   PetscFunctionBegin;
1584   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1585   dm->prealloc_only = only;
1586   PetscFunctionReturn(PETSC_SUCCESS);
1587 }
1588 
1589 /*@
1590   DMSetMatrixStructureOnly - When `DMCreateMatrix()` is called, the matrix nonzero structure will be created
1591   but the array for numerical values will not be allocated.
1592 
1593   Logically Collective
1594 
1595   Input Parameters:
1596 + dm   - the `DM`
1597 - only - `PETSC_TRUE` if you only want matrix nonzero structure
1598 
1599   Level: developer
1600 
1601 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `DMSetMatrixPreallocateOnly()`, `DMSetMatrixPreallocateSkip()`
1602 @*/
1603 PetscErrorCode DMSetMatrixStructureOnly(DM dm, PetscBool only)
1604 {
1605   PetscFunctionBegin;
1606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1607   dm->structure_only = only;
1608   PetscFunctionReturn(PETSC_SUCCESS);
1609 }
1610 
1611 /*@
1612   DMSetBlockingType - set the blocking granularity to be used for variable block size `DMCreateMatrix()` is called
1613 
1614   Logically Collective
1615 
1616   Input Parameters:
1617 + dm    - the `DM`
1618 - btype - block by topological point or field node
1619 
1620   Options Database Key:
1621 . -dm_blocking_type [topological_point, field_node] - use topological point blocking or field node blocking
1622 
1623   Level: advanced
1624 
1625 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `MatSetVariableBlockSizes()`
1626 @*/
1627 PetscErrorCode DMSetBlockingType(DM dm, DMBlockingType btype)
1628 {
1629   PetscFunctionBegin;
1630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1631   dm->blocking_type = btype;
1632   PetscFunctionReturn(PETSC_SUCCESS);
1633 }
1634 
1635 /*@
1636   DMGetBlockingType - get the blocking granularity to be used for variable block size `DMCreateMatrix()` is called
1637 
1638   Not Collective
1639 
1640   Input Parameter:
1641 . dm - the `DM`
1642 
1643   Output Parameter:
1644 . btype - block by topological point or field node
1645 
1646   Level: advanced
1647 
1648 .seealso: [](ch_dmbase), `DM`, `DMCreateMatrix()`, `MatSetVariableBlockSizes()`
1649 @*/
1650 PetscErrorCode DMGetBlockingType(DM dm, DMBlockingType *btype)
1651 {
1652   PetscFunctionBegin;
1653   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1654   PetscAssertPointer(btype, 2);
1655   *btype = dm->blocking_type;
1656   PetscFunctionReturn(PETSC_SUCCESS);
1657 }
1658 
1659 /*@C
1660   DMGetWorkArray - Gets a work array guaranteed to be at least the input size, restore with `DMRestoreWorkArray()`
1661 
1662   Not Collective
1663 
1664   Input Parameters:
1665 + dm    - the `DM` object
1666 . count - The minimum size
1667 - dtype - MPI data type, often `MPIU_REAL`, `MPIU_SCALAR`, or `MPIU_INT`)
1668 
1669   Output Parameter:
1670 . mem - the work array
1671 
1672   Level: developer
1673 
1674   Notes:
1675   A `DM` may stash the array between instantiations so using this routine may be more efficient than calling `PetscMalloc()`
1676 
1677   The array may contain nonzero values
1678 
1679 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMCreate()`, `DMRestoreWorkArray()`, `PetscMalloc()`
1680 @*/
1681 PetscErrorCode DMGetWorkArray(DM dm, PetscInt count, MPI_Datatype dtype, void *mem)
1682 {
1683   DMWorkLink  link;
1684   PetscMPIInt dsize;
1685 
1686   PetscFunctionBegin;
1687   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1688   PetscAssertPointer(mem, 4);
1689   if (!count) {
1690     *(void **)mem = NULL;
1691     PetscFunctionReturn(PETSC_SUCCESS);
1692   }
1693   if (dm->workin) {
1694     link       = dm->workin;
1695     dm->workin = dm->workin->next;
1696   } else {
1697     PetscCall(PetscNew(&link));
1698   }
1699   /* Avoid MPI_Type_size for most used datatypes
1700      Get size directly */
1701   if (dtype == MPIU_INT) dsize = sizeof(PetscInt);
1702   else if (dtype == MPIU_REAL) dsize = sizeof(PetscReal);
1703 #if defined(PETSC_USE_64BIT_INDICES)
1704   else if (dtype == MPI_INT) dsize = sizeof(int);
1705 #endif
1706 #if defined(PETSC_USE_COMPLEX)
1707   else if (dtype == MPIU_SCALAR) dsize = sizeof(PetscScalar);
1708 #endif
1709   else PetscCallMPI(MPI_Type_size(dtype, &dsize));
1710 
1711   if (((size_t)dsize * count) > link->bytes) {
1712     PetscCall(PetscFree(link->mem));
1713     PetscCall(PetscMalloc(dsize * count, &link->mem));
1714     link->bytes = dsize * count;
1715   }
1716   link->next    = dm->workout;
1717   dm->workout   = link;
1718   *(void **)mem = link->mem;
1719   PetscFunctionReturn(PETSC_SUCCESS);
1720 }
1721 
1722 /*@C
1723   DMRestoreWorkArray - Restores a work array obtained with `DMCreateWorkArray()`
1724 
1725   Not Collective
1726 
1727   Input Parameters:
1728 + dm    - the `DM` object
1729 . count - The minimum size
1730 - dtype - MPI data type, often `MPIU_REAL`, `MPIU_SCALAR`, `MPIU_INT`
1731 
1732   Output Parameter:
1733 . mem - the work array
1734 
1735   Level: developer
1736 
1737   Developer Note:
1738   count and dtype are ignored, they are only needed for `DMGetWorkArray()`
1739 
1740 .seealso: [](ch_dmbase), `DM`, `DMDestroy()`, `DMCreate()`, `DMGetWorkArray()`
1741 @*/
1742 PetscErrorCode DMRestoreWorkArray(DM dm, PetscInt count, MPI_Datatype dtype, void *mem)
1743 {
1744   DMWorkLink *p, link;
1745 
1746   PetscFunctionBegin;
1747   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
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 number 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 `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
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)
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   Notes:
3732   A user context is a way to pass problem specific information that is accessible whenever the `DM` is available
3733   In a multilevel solver, the user context is shared by all the `DM` in the hierarchy; it is thus not advisable
3734   to store objects that represent discretized quantities inside the context.
3735 
3736 .seealso: [](ch_dmbase), `DM`, `DMGetApplicationContext()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`
3737 @*/
3738 PetscErrorCode DMSetApplicationContext(DM dm, void *ctx)
3739 {
3740   PetscFunctionBegin;
3741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3742   dm->ctx = ctx;
3743   PetscFunctionReturn(PETSC_SUCCESS);
3744 }
3745 
3746 /*@
3747   DMGetApplicationContext - Gets a user context from a `DM` object
3748 
3749   Not Collective
3750 
3751   Input Parameter:
3752 . dm - the `DM` object
3753 
3754   Output Parameter:
3755 . ctx - the user context
3756 
3757   Level: intermediate
3758 
3759   Note:
3760   A user context is a way to pass problem specific information that is accessible whenever the `DM` is available
3761 
3762 .seealso: [](ch_dmbase), `DM`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`
3763 @*/
3764 PetscErrorCode DMGetApplicationContext(DM dm, void *ctx)
3765 {
3766   PetscFunctionBegin;
3767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3768   *(void **)ctx = dm->ctx;
3769   PetscFunctionReturn(PETSC_SUCCESS);
3770 }
3771 
3772 /*@C
3773   DMSetVariableBounds - sets a function to compute the lower and upper bound vectors for `SNESVI`.
3774 
3775   Logically Collective
3776 
3777   Input Parameters:
3778 + dm - the DM object
3779 - f  - the function that computes variable bounds used by SNESVI (use `NULL` to cancel a previous function that was set)
3780 
3781   Level: intermediate
3782 
3783 .seealso: [](ch_dmbase), `DM`, `DMComputeVariableBounds()`, `DMHasVariableBounds()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMGetApplicationContext()`,
3784          `DMSetJacobian()`
3785 @*/
3786 PetscErrorCode DMSetVariableBounds(DM dm, PetscErrorCode (*f)(DM, Vec, Vec))
3787 {
3788   PetscFunctionBegin;
3789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3790   dm->ops->computevariablebounds = f;
3791   PetscFunctionReturn(PETSC_SUCCESS);
3792 }
3793 
3794 /*@
3795   DMHasVariableBounds - does the `DM` object have a variable bounds function?
3796 
3797   Not Collective
3798 
3799   Input Parameter:
3800 . dm - the `DM` object to destroy
3801 
3802   Output Parameter:
3803 . flg - `PETSC_TRUE` if the variable bounds function exists
3804 
3805   Level: developer
3806 
3807 .seealso: [](ch_dmbase), `DM`, `DMComputeVariableBounds()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMGetApplicationContext()`
3808 @*/
3809 PetscErrorCode DMHasVariableBounds(DM dm, PetscBool *flg)
3810 {
3811   PetscFunctionBegin;
3812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3813   PetscAssertPointer(flg, 2);
3814   *flg = (dm->ops->computevariablebounds) ? PETSC_TRUE : PETSC_FALSE;
3815   PetscFunctionReturn(PETSC_SUCCESS);
3816 }
3817 
3818 /*@
3819   DMComputeVariableBounds - compute variable bounds used by `SNESVI`.
3820 
3821   Logically Collective
3822 
3823   Input Parameter:
3824 . dm - the `DM` object
3825 
3826   Output Parameters:
3827 + xl - lower bound
3828 - xu - upper bound
3829 
3830   Level: advanced
3831 
3832   Note:
3833   This is generally not called by users. It calls the function provided by the user with DMSetVariableBounds()
3834 
3835 .seealso: [](ch_dmbase), `DM`, `DMHasVariableBounds()`, `DMView()`, `DMCreateGlobalVector()`, `DMCreateInterpolation()`, `DMCreateColoring()`, `DMCreateMatrix()`, `DMCreateMassMatrix()`, `DMGetApplicationContext()`
3836 @*/
3837 PetscErrorCode DMComputeVariableBounds(DM dm, Vec xl, Vec xu)
3838 {
3839   PetscFunctionBegin;
3840   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3841   PetscValidHeaderSpecific(xl, VEC_CLASSID, 2);
3842   PetscValidHeaderSpecific(xu, VEC_CLASSID, 3);
3843   PetscUseTypeMethod(dm, computevariablebounds, xl, xu);
3844   PetscFunctionReturn(PETSC_SUCCESS);
3845 }
3846 
3847 /*@
3848   DMHasColoring - does the `DM` object have a method of providing a coloring?
3849 
3850   Not Collective
3851 
3852   Input Parameter:
3853 . dm - the DM object
3854 
3855   Output Parameter:
3856 . flg - `PETSC_TRUE` if the `DM` has facilities for `DMCreateColoring()`.
3857 
3858   Level: developer
3859 
3860 .seealso: [](ch_dmbase), `DM`, `DMCreateColoring()`
3861 @*/
3862 PetscErrorCode DMHasColoring(DM dm, PetscBool *flg)
3863 {
3864   PetscFunctionBegin;
3865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3866   PetscAssertPointer(flg, 2);
3867   *flg = (dm->ops->getcoloring) ? PETSC_TRUE : PETSC_FALSE;
3868   PetscFunctionReturn(PETSC_SUCCESS);
3869 }
3870 
3871 /*@
3872   DMHasCreateRestriction - does the `DM` object have a method of providing a restriction?
3873 
3874   Not Collective
3875 
3876   Input Parameter:
3877 . dm - the `DM` object
3878 
3879   Output Parameter:
3880 . flg - `PETSC_TRUE` if the `DM` has facilities for `DMCreateRestriction()`.
3881 
3882   Level: developer
3883 
3884 .seealso: [](ch_dmbase), `DM`, `DMCreateRestriction()`, `DMHasCreateInterpolation()`, `DMHasCreateInjection()`
3885 @*/
3886 PetscErrorCode DMHasCreateRestriction(DM dm, PetscBool *flg)
3887 {
3888   PetscFunctionBegin;
3889   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3890   PetscAssertPointer(flg, 2);
3891   *flg = (dm->ops->createrestriction) ? PETSC_TRUE : PETSC_FALSE;
3892   PetscFunctionReturn(PETSC_SUCCESS);
3893 }
3894 
3895 /*@
3896   DMHasCreateInjection - does the `DM` object have a method of providing an injection?
3897 
3898   Not Collective
3899 
3900   Input Parameter:
3901 . dm - the `DM` object
3902 
3903   Output Parameter:
3904 . flg - `PETSC_TRUE` if the `DM` has facilities for `DMCreateInjection()`.
3905 
3906   Level: developer
3907 
3908 .seealso: [](ch_dmbase), `DM`, `DMCreateInjection()`, `DMHasCreateRestriction()`, `DMHasCreateInterpolation()`
3909 @*/
3910 PetscErrorCode DMHasCreateInjection(DM dm, PetscBool *flg)
3911 {
3912   PetscFunctionBegin;
3913   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3914   PetscAssertPointer(flg, 2);
3915   if (dm->ops->hascreateinjection) PetscUseTypeMethod(dm, hascreateinjection, flg);
3916   else *flg = (dm->ops->createinjection) ? PETSC_TRUE : PETSC_FALSE;
3917   PetscFunctionReturn(PETSC_SUCCESS);
3918 }
3919 
3920 PetscFunctionList DMList              = NULL;
3921 PetscBool         DMRegisterAllCalled = PETSC_FALSE;
3922 
3923 /*@
3924   DMSetType - Builds a `DM`, for a particular `DM` implementation.
3925 
3926   Collective
3927 
3928   Input Parameters:
3929 + dm     - The `DM` object
3930 - method - The name of the `DMType`, for example `DMDA`, `DMPLEX`
3931 
3932   Options Database Key:
3933 . -dm_type <type> - Sets the `DM` type; use -help for a list of available types
3934 
3935   Level: intermediate
3936 
3937   Note:
3938   Of the `DM` is constructed by directly calling a function to construct a particular `DM`, for example, `DMDACreate2d()` or `DMPlexCreateBoxMesh()`
3939 
3940 .seealso: [](ch_dmbase), `DM`, `DMType`, `DMDA`, `DMPLEX`, `DMGetType()`, `DMCreate()`, `DMDACreate2d()`
3941 @*/
3942 PetscErrorCode DMSetType(DM dm, DMType method)
3943 {
3944   PetscErrorCode (*r)(DM);
3945   PetscBool match;
3946 
3947   PetscFunctionBegin;
3948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3949   PetscCall(PetscObjectTypeCompare((PetscObject)dm, method, &match));
3950   if (match) PetscFunctionReturn(PETSC_SUCCESS);
3951 
3952   PetscCall(DMRegisterAll());
3953   PetscCall(PetscFunctionListFind(DMList, method, &r));
3954   PetscCheck(r, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown DM type: %s", method);
3955 
3956   PetscTryTypeMethod(dm, destroy);
3957   PetscCall(PetscMemzero(dm->ops, sizeof(*dm->ops)));
3958   PetscCall(PetscObjectChangeTypeName((PetscObject)dm, method));
3959   PetscCall((*r)(dm));
3960   PetscFunctionReturn(PETSC_SUCCESS);
3961 }
3962 
3963 /*@
3964   DMGetType - Gets the `DM` type name (as a string) from the `DM`.
3965 
3966   Not Collective
3967 
3968   Input Parameter:
3969 . dm - The `DM`
3970 
3971   Output Parameter:
3972 . type - The `DMType` name
3973 
3974   Level: intermediate
3975 
3976 .seealso: [](ch_dmbase), `DM`, `DMType`, `DMDA`, `DMPLEX`, `DMSetType()`, `DMCreate()`
3977 @*/
3978 PetscErrorCode DMGetType(DM dm, DMType *type)
3979 {
3980   PetscFunctionBegin;
3981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3982   PetscAssertPointer(type, 2);
3983   PetscCall(DMRegisterAll());
3984   *type = ((PetscObject)dm)->type_name;
3985   PetscFunctionReturn(PETSC_SUCCESS);
3986 }
3987 
3988 /*@
3989   DMConvert - Converts a `DM` to another `DM`, either of the same or different type.
3990 
3991   Collective
3992 
3993   Input Parameters:
3994 + dm      - the `DM`
3995 - newtype - new `DM` type (use "same" for the same type)
3996 
3997   Output Parameter:
3998 . M - pointer to new `DM`
3999 
4000   Level: intermediate
4001 
4002   Note:
4003   Cannot be used to convert a sequential `DM` to a parallel or a parallel to sequential,
4004   the MPI communicator of the generated `DM` is always the same as the communicator
4005   of the input `DM`.
4006 
4007 .seealso: [](ch_dmbase), `DM`, `DMSetType()`, `DMCreate()`, `DMClone()`
4008 @*/
4009 PetscErrorCode DMConvert(DM dm, DMType newtype, DM *M)
4010 {
4011   DM        B;
4012   char      convname[256];
4013   PetscBool sametype /*, issame */;
4014 
4015   PetscFunctionBegin;
4016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4017   PetscValidType(dm, 1);
4018   PetscAssertPointer(M, 3);
4019   PetscCall(PetscObjectTypeCompare((PetscObject)dm, newtype, &sametype));
4020   /* PetscCall(PetscStrcmp(newtype, "same", &issame)); */
4021   if (sametype) {
4022     *M = dm;
4023     PetscCall(PetscObjectReference((PetscObject)dm));
4024     PetscFunctionReturn(PETSC_SUCCESS);
4025   } else {
4026     PetscErrorCode (*conv)(DM, DMType, DM *) = NULL;
4027 
4028     /*
4029        Order of precedence:
4030        1) See if a specialized converter is known to the current DM.
4031        2) See if a specialized converter is known to the desired DM class.
4032        3) See if a good general converter is registered for the desired class
4033        4) See if a good general converter is known for the current matrix.
4034        5) Use a really basic converter.
4035     */
4036 
4037     /* 1) See if a specialized converter is known to the current DM and the desired class */
4038     PetscCall(PetscStrncpy(convname, "DMConvert_", sizeof(convname)));
4039     PetscCall(PetscStrlcat(convname, ((PetscObject)dm)->type_name, sizeof(convname)));
4040     PetscCall(PetscStrlcat(convname, "_", sizeof(convname)));
4041     PetscCall(PetscStrlcat(convname, newtype, sizeof(convname)));
4042     PetscCall(PetscStrlcat(convname, "_C", sizeof(convname)));
4043     PetscCall(PetscObjectQueryFunction((PetscObject)dm, convname, &conv));
4044     if (conv) goto foundconv;
4045 
4046     /* 2)  See if a specialized converter is known to the desired DM class. */
4047     PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &B));
4048     PetscCall(DMSetType(B, newtype));
4049     PetscCall(PetscStrncpy(convname, "DMConvert_", sizeof(convname)));
4050     PetscCall(PetscStrlcat(convname, ((PetscObject)dm)->type_name, sizeof(convname)));
4051     PetscCall(PetscStrlcat(convname, "_", sizeof(convname)));
4052     PetscCall(PetscStrlcat(convname, newtype, sizeof(convname)));
4053     PetscCall(PetscStrlcat(convname, "_C", sizeof(convname)));
4054     PetscCall(PetscObjectQueryFunction((PetscObject)B, convname, &conv));
4055     if (conv) {
4056       PetscCall(DMDestroy(&B));
4057       goto foundconv;
4058     }
4059 
4060 #if 0
4061     /* 3) See if a good general converter is registered for the desired class */
4062     conv = B->ops->convertfrom;
4063     PetscCall(DMDestroy(&B));
4064     if (conv) goto foundconv;
4065 
4066     /* 4) See if a good general converter is known for the current matrix */
4067     if (dm->ops->convert) {
4068       conv = dm->ops->convert;
4069     }
4070     if (conv) goto foundconv;
4071 #endif
4072 
4073     /* 5) Use a really basic converter. */
4074     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "No conversion possible between DM types %s and %s", ((PetscObject)dm)->type_name, newtype);
4075 
4076   foundconv:
4077     PetscCall(PetscLogEventBegin(DM_Convert, dm, 0, 0, 0));
4078     PetscCall((*conv)(dm, newtype, M));
4079     /* Things that are independent of DM type: We should consult DMClone() here */
4080     {
4081       const PetscReal *maxCell, *Lstart, *L;
4082 
4083       PetscCall(DMGetPeriodicity(dm, &maxCell, &Lstart, &L));
4084       PetscCall(DMSetPeriodicity(*M, maxCell, Lstart, L));
4085       (*M)->prealloc_only = dm->prealloc_only;
4086       PetscCall(PetscFree((*M)->vectype));
4087       PetscCall(PetscStrallocpy(dm->vectype, (char **)&(*M)->vectype));
4088       PetscCall(PetscFree((*M)->mattype));
4089       PetscCall(PetscStrallocpy(dm->mattype, (char **)&(*M)->mattype));
4090     }
4091     PetscCall(PetscLogEventEnd(DM_Convert, dm, 0, 0, 0));
4092   }
4093   PetscCall(PetscObjectStateIncrease((PetscObject)*M));
4094   PetscFunctionReturn(PETSC_SUCCESS);
4095 }
4096 
4097 /*--------------------------------------------------------------------------------------------------------------------*/
4098 
4099 /*@C
4100   DMRegister -  Adds a new `DM` type implementation
4101 
4102   Not Collective, No Fortran Support
4103 
4104   Input Parameters:
4105 + sname    - The name of a new user-defined creation routine
4106 - function - The creation routine itself
4107 
4108   Level: advanced
4109 
4110   Note:
4111   `DMRegister()` may be called multiple times to add several user-defined `DM`s
4112 
4113   Example Usage:
4114 .vb
4115     DMRegister("my_da", MyDMCreate);
4116 .ve
4117 
4118   Then, your `DM` type can be chosen with the procedural interface via
4119 .vb
4120     DMCreate(MPI_Comm, DM *);
4121     DMSetType(DM,"my_da");
4122 .ve
4123   or at runtime via the option
4124 .vb
4125     -da_type my_da
4126 .ve
4127 
4128 .seealso: [](ch_dmbase), `DM`, `DMType`, `DMSetType()`, `DMRegisterAll()`, `DMRegisterDestroy()`
4129 @*/
4130 PetscErrorCode DMRegister(const char sname[], PetscErrorCode (*function)(DM))
4131 {
4132   PetscFunctionBegin;
4133   PetscCall(DMInitializePackage());
4134   PetscCall(PetscFunctionListAdd(&DMList, sname, function));
4135   PetscFunctionReturn(PETSC_SUCCESS);
4136 }
4137 
4138 /*@
4139   DMLoad - Loads a DM that has been stored in binary  with `DMView()`.
4140 
4141   Collective
4142 
4143   Input Parameters:
4144 + newdm  - the newly loaded `DM`, this needs to have been created with `DMCreate()` or
4145            some related function before a call to `DMLoad()`.
4146 - viewer - binary file viewer, obtained from `PetscViewerBinaryOpen()` or
4147            `PETSCVIEWERHDF5` file viewer, obtained from `PetscViewerHDF5Open()`
4148 
4149   Level: intermediate
4150 
4151   Notes:
4152   The type is determined by the data in the file, any type set into the DM before this call is ignored.
4153 
4154   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
4155   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
4156   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
4157 
4158 .seealso: [](ch_dmbase), `DM`, `PetscViewerBinaryOpen()`, `DMView()`, `MatLoad()`, `VecLoad()`
4159 @*/
4160 PetscErrorCode DMLoad(DM newdm, PetscViewer viewer)
4161 {
4162   PetscBool isbinary, ishdf5;
4163 
4164   PetscFunctionBegin;
4165   PetscValidHeaderSpecific(newdm, DM_CLASSID, 1);
4166   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
4167   PetscCall(PetscViewerCheckReadable(viewer));
4168   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERBINARY, &isbinary));
4169   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
4170   PetscCall(PetscLogEventBegin(DM_Load, viewer, 0, 0, 0));
4171   if (isbinary) {
4172     PetscInt classid;
4173     char     type[256];
4174 
4175     PetscCall(PetscViewerBinaryRead(viewer, &classid, 1, NULL, PETSC_INT));
4176     PetscCheck(classid == DM_FILE_CLASSID, PetscObjectComm((PetscObject)newdm), PETSC_ERR_ARG_WRONG, "Not DM next in file, classid found %" PetscInt_FMT, classid);
4177     PetscCall(PetscViewerBinaryRead(viewer, type, 256, NULL, PETSC_CHAR));
4178     PetscCall(DMSetType(newdm, type));
4179     PetscTryTypeMethod(newdm, load, viewer);
4180   } else if (ishdf5) {
4181     PetscTryTypeMethod(newdm, load, viewer);
4182   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid viewer; open viewer with PetscViewerBinaryOpen() or PetscViewerHDF5Open()");
4183   PetscCall(PetscLogEventEnd(DM_Load, viewer, 0, 0, 0));
4184   PetscFunctionReturn(PETSC_SUCCESS);
4185 }
4186 
4187 /* FEM Support */
4188 
4189 PetscErrorCode DMPrintCellIndices(PetscInt c, const char name[], PetscInt len, const PetscInt x[])
4190 {
4191   PetscInt f;
4192 
4193   PetscFunctionBegin;
4194   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4195   for (f = 0; f < len; ++f) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  | %" PetscInt_FMT " |\n", x[f]));
4196   PetscFunctionReturn(PETSC_SUCCESS);
4197 }
4198 
4199 PetscErrorCode DMPrintCellVector(PetscInt c, const char name[], PetscInt len, const PetscScalar x[])
4200 {
4201   PetscInt f;
4202 
4203   PetscFunctionBegin;
4204   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4205   for (f = 0; f < len; ++f) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  | %g |\n", (double)PetscRealPart(x[f])));
4206   PetscFunctionReturn(PETSC_SUCCESS);
4207 }
4208 
4209 PetscErrorCode DMPrintCellVectorReal(PetscInt c, const char name[], PetscInt len, const PetscReal x[])
4210 {
4211   PetscInt f;
4212 
4213   PetscFunctionBegin;
4214   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4215   for (f = 0; f < len; ++f) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  | %g |\n", (double)x[f]));
4216   PetscFunctionReturn(PETSC_SUCCESS);
4217 }
4218 
4219 PetscErrorCode DMPrintCellMatrix(PetscInt c, const char name[], PetscInt rows, PetscInt cols, const PetscScalar A[])
4220 {
4221   PetscInt f, g;
4222 
4223   PetscFunctionBegin;
4224   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Cell %" PetscInt_FMT " Element %s\n", c, name));
4225   for (f = 0; f < rows; ++f) {
4226     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  |"));
4227     for (g = 0; g < cols; ++g) PetscCall(PetscPrintf(PETSC_COMM_SELF, " % 9.5g", (double)PetscRealPart(A[f * cols + g])));
4228     PetscCall(PetscPrintf(PETSC_COMM_SELF, " |\n"));
4229   }
4230   PetscFunctionReturn(PETSC_SUCCESS);
4231 }
4232 
4233 PetscErrorCode DMPrintLocalVec(DM dm, const char name[], PetscReal tol, Vec X)
4234 {
4235   PetscInt           localSize, bs;
4236   PetscMPIInt        size;
4237   Vec                x, xglob;
4238   const PetscScalar *xarray;
4239 
4240   PetscFunctionBegin;
4241   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
4242   PetscCall(VecDuplicate(X, &x));
4243   PetscCall(VecCopy(X, x));
4244   PetscCall(VecFilter(x, tol));
4245   PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "%s:\n", name));
4246   if (size > 1) {
4247     PetscCall(VecGetLocalSize(x, &localSize));
4248     PetscCall(VecGetArrayRead(x, &xarray));
4249     PetscCall(VecGetBlockSize(x, &bs));
4250     PetscCall(VecCreateMPIWithArray(PetscObjectComm((PetscObject)dm), bs, localSize, PETSC_DETERMINE, xarray, &xglob));
4251   } else {
4252     xglob = x;
4253   }
4254   PetscCall(VecView(xglob, PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)dm))));
4255   if (size > 1) {
4256     PetscCall(VecDestroy(&xglob));
4257     PetscCall(VecRestoreArrayRead(x, &xarray));
4258   }
4259   PetscCall(VecDestroy(&x));
4260   PetscFunctionReturn(PETSC_SUCCESS);
4261 }
4262 
4263 /*@
4264   DMGetLocalSection - Get the `PetscSection` encoding the local data layout for the `DM`.
4265 
4266   Input Parameter:
4267 . dm - The `DM`
4268 
4269   Output Parameter:
4270 . section - The `PetscSection`
4271 
4272   Options Database Key:
4273 . -dm_petscsection_view - View the section created by the `DM`
4274 
4275   Level: intermediate
4276 
4277   Note:
4278   This gets a borrowed reference, so the user should not destroy this `PetscSection`.
4279 
4280 .seealso: [](ch_dmbase), `DM`, `DMSetLocalSection()`, `DMGetGlobalSection()`
4281 @*/
4282 PetscErrorCode DMGetLocalSection(DM dm, PetscSection *section)
4283 {
4284   PetscFunctionBegin;
4285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4286   PetscAssertPointer(section, 2);
4287   if (!dm->localSection && dm->ops->createlocalsection) {
4288     PetscInt d;
4289 
4290     if (dm->setfromoptionscalled) {
4291       PetscObject       obj = (PetscObject)dm;
4292       PetscViewer       viewer;
4293       PetscViewerFormat format;
4294       PetscBool         flg;
4295 
4296       PetscCall(PetscOptionsCreateViewer(PetscObjectComm(obj), obj->options, obj->prefix, "-dm_petscds_view", &viewer, &format, &flg));
4297       if (flg) PetscCall(PetscViewerPushFormat(viewer, format));
4298       for (d = 0; d < dm->Nds; ++d) {
4299         PetscCall(PetscDSSetFromOptions(dm->probs[d].ds));
4300         if (flg) PetscCall(PetscDSView(dm->probs[d].ds, viewer));
4301       }
4302       if (flg) {
4303         PetscCall(PetscViewerFlush(viewer));
4304         PetscCall(PetscViewerPopFormat(viewer));
4305         PetscCall(PetscViewerDestroy(&viewer));
4306       }
4307     }
4308     PetscUseTypeMethod(dm, createlocalsection);
4309     if (dm->localSection) PetscCall(PetscObjectViewFromOptions((PetscObject)dm->localSection, NULL, "-dm_petscsection_view"));
4310   }
4311   *section = dm->localSection;
4312   PetscFunctionReturn(PETSC_SUCCESS);
4313 }
4314 
4315 /*@
4316   DMSetLocalSection - Set the `PetscSection` encoding the local data layout for the `DM`.
4317 
4318   Input Parameters:
4319 + dm      - The `DM`
4320 - section - The `PetscSection`
4321 
4322   Level: intermediate
4323 
4324   Note:
4325   Any existing Section will be destroyed
4326 
4327 .seealso: [](ch_dmbase), `DM`, `PetscSection`, `DMGetLocalSection()`, `DMSetGlobalSection()`
4328 @*/
4329 PetscErrorCode DMSetLocalSection(DM dm, PetscSection section)
4330 {
4331   PetscInt numFields = 0;
4332   PetscInt f;
4333 
4334   PetscFunctionBegin;
4335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4336   if (section) PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
4337   PetscCall(PetscObjectReference((PetscObject)section));
4338   PetscCall(PetscSectionDestroy(&dm->localSection));
4339   dm->localSection = section;
4340   if (section) PetscCall(PetscSectionGetNumFields(dm->localSection, &numFields));
4341   if (numFields) {
4342     PetscCall(DMSetNumFields(dm, numFields));
4343     for (f = 0; f < numFields; ++f) {
4344       PetscObject disc;
4345       const char *name;
4346 
4347       PetscCall(PetscSectionGetFieldName(dm->localSection, f, &name));
4348       PetscCall(DMGetField(dm, f, NULL, &disc));
4349       PetscCall(PetscObjectSetName(disc, name));
4350     }
4351   }
4352   /* The global section and the SectionSF will be rebuilt
4353      in the next call to DMGetGlobalSection() and DMGetSectionSF(). */
4354   PetscCall(PetscSectionDestroy(&dm->globalSection));
4355   PetscCall(PetscSFDestroy(&dm->sectionSF));
4356   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &dm->sectionSF));
4357 
4358   /* Clear scratch vectors */
4359   PetscCall(DMClearGlobalVectors(dm));
4360   PetscCall(DMClearLocalVectors(dm));
4361   PetscCall(DMClearNamedGlobalVectors(dm));
4362   PetscCall(DMClearNamedLocalVectors(dm));
4363   PetscFunctionReturn(PETSC_SUCCESS);
4364 }
4365 
4366 /*@C
4367   DMCreateSectionPermutation - Create a permutation of the `PetscSection` chart and optionally a block structure.
4368 
4369   Input Parameter:
4370 . dm - The `DM`
4371 
4372   Output Parameters:
4373 + perm        - A permutation of the mesh points in the chart
4374 - blockStarts - A high bit is set for the point that begins every block, or `NULL` for default blocking
4375 
4376   Level: developer
4377 
4378 .seealso: [](ch_dmbase), `DM`, `PetscSection`, `DMGetLocalSection()`, `DMGetGlobalSection()`
4379 @*/
4380 PetscErrorCode DMCreateSectionPermutation(DM dm, IS *perm, PetscBT *blockStarts)
4381 {
4382   PetscFunctionBegin;
4383   *perm        = NULL;
4384   *blockStarts = NULL;
4385   PetscTryTypeMethod(dm, createsectionpermutation, perm, blockStarts);
4386   PetscFunctionReturn(PETSC_SUCCESS);
4387 }
4388 
4389 /*@
4390   DMGetDefaultConstraints - Get the `PetscSection` and `Mat` that specify the local constraint interpolation. See `DMSetDefaultConstraints()` for a description of the purpose of constraint interpolation.
4391 
4392   not Collective
4393 
4394   Input Parameter:
4395 . dm - The `DM`
4396 
4397   Output Parameters:
4398 + 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.
4399 . 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.
4400 - bias    - Vector containing bias to be added to constrained dofs
4401 
4402   Level: advanced
4403 
4404   Note:
4405   This gets borrowed references, so the user should not destroy the `PetscSection`, `Mat`, or `Vec`.
4406 
4407 .seealso: [](ch_dmbase), `DM`, `DMSetDefaultConstraints()`
4408 @*/
4409 PetscErrorCode DMGetDefaultConstraints(DM dm, PetscSection *section, Mat *mat, Vec *bias)
4410 {
4411   PetscFunctionBegin;
4412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4413   if (!dm->defaultConstraint.section && !dm->defaultConstraint.mat && dm->ops->createdefaultconstraints) PetscUseTypeMethod(dm, createdefaultconstraints);
4414   if (section) *section = dm->defaultConstraint.section;
4415   if (mat) *mat = dm->defaultConstraint.mat;
4416   if (bias) *bias = dm->defaultConstraint.bias;
4417   PetscFunctionReturn(PETSC_SUCCESS);
4418 }
4419 
4420 /*@
4421   DMSetDefaultConstraints - Set the `PetscSection` and `Mat` that specify the local constraint interpolation.
4422 
4423   Collective
4424 
4425   Input Parameters:
4426 + dm      - The `DM`
4427 . 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).
4428 . 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).
4429 - 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).
4430 
4431   Level: advanced
4432 
4433   Notes:
4434   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()`.
4435 
4436   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.
4437 
4438   This increments the references of the `PetscSection`, `Mat`, and `Vec`, so they user can destroy them.
4439 
4440 .seealso: [](ch_dmbase), `DM`, `DMGetDefaultConstraints()`
4441 @*/
4442 PetscErrorCode DMSetDefaultConstraints(DM dm, PetscSection section, Mat mat, Vec bias)
4443 {
4444   PetscMPIInt result;
4445 
4446   PetscFunctionBegin;
4447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4448   if (section) {
4449     PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
4450     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)section), &result));
4451     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "constraint section must have local communicator");
4452   }
4453   if (mat) {
4454     PetscValidHeaderSpecific(mat, MAT_CLASSID, 3);
4455     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)mat), &result));
4456     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "constraint matrix must have local communicator");
4457   }
4458   if (bias) {
4459     PetscValidHeaderSpecific(bias, VEC_CLASSID, 4);
4460     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)bias), &result));
4461     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "constraint bias must have local communicator");
4462   }
4463   PetscCall(PetscObjectReference((PetscObject)section));
4464   PetscCall(PetscSectionDestroy(&dm->defaultConstraint.section));
4465   dm->defaultConstraint.section = section;
4466   PetscCall(PetscObjectReference((PetscObject)mat));
4467   PetscCall(MatDestroy(&dm->defaultConstraint.mat));
4468   dm->defaultConstraint.mat = mat;
4469   PetscCall(PetscObjectReference((PetscObject)bias));
4470   PetscCall(VecDestroy(&dm->defaultConstraint.bias));
4471   dm->defaultConstraint.bias = bias;
4472   PetscFunctionReturn(PETSC_SUCCESS);
4473 }
4474 
4475 #if defined(PETSC_USE_DEBUG)
4476 /*
4477   DMDefaultSectionCheckConsistency - Check the consistentcy of the global and local sections. Generates and error if they are not consistent.
4478 
4479   Input Parameters:
4480 + dm - The `DM`
4481 . localSection - `PetscSection` describing the local data layout
4482 - globalSection - `PetscSection` describing the global data layout
4483 
4484   Level: intermediate
4485 
4486 .seealso: [](ch_dmbase), `DM`, `DMGetSectionSF()`, `DMSetSectionSF()`
4487 */
4488 static PetscErrorCode DMDefaultSectionCheckConsistency_Internal(DM dm, PetscSection localSection, PetscSection globalSection)
4489 {
4490   MPI_Comm        comm;
4491   PetscLayout     layout;
4492   const PetscInt *ranges;
4493   PetscInt        pStart, pEnd, p, nroots;
4494   PetscMPIInt     size, rank;
4495   PetscBool       valid = PETSC_TRUE, gvalid;
4496 
4497   PetscFunctionBegin;
4498   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4500   PetscCallMPI(MPI_Comm_size(comm, &size));
4501   PetscCallMPI(MPI_Comm_rank(comm, &rank));
4502   PetscCall(PetscSectionGetChart(globalSection, &pStart, &pEnd));
4503   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &nroots));
4504   PetscCall(PetscLayoutCreate(comm, &layout));
4505   PetscCall(PetscLayoutSetBlockSize(layout, 1));
4506   PetscCall(PetscLayoutSetLocalSize(layout, nroots));
4507   PetscCall(PetscLayoutSetUp(layout));
4508   PetscCall(PetscLayoutGetRanges(layout, &ranges));
4509   for (p = pStart; p < pEnd; ++p) {
4510     PetscInt dof, cdof, off, gdof, gcdof, goff, gsize, d;
4511 
4512     PetscCall(PetscSectionGetDof(localSection, p, &dof));
4513     PetscCall(PetscSectionGetOffset(localSection, p, &off));
4514     PetscCall(PetscSectionGetConstraintDof(localSection, p, &cdof));
4515     PetscCall(PetscSectionGetDof(globalSection, p, &gdof));
4516     PetscCall(PetscSectionGetConstraintDof(globalSection, p, &gcdof));
4517     PetscCall(PetscSectionGetOffset(globalSection, p, &goff));
4518     if (!gdof) continue; /* Censored point */
4519     if ((gdof < 0 ? -(gdof + 1) : gdof) != dof) {
4520       PetscCall(PetscSynchronizedPrintf(comm, "[%d]Global dof %" PetscInt_FMT " for point %" PetscInt_FMT " not equal to local dof %" PetscInt_FMT "\n", rank, gdof, p, dof));
4521       valid = PETSC_FALSE;
4522     }
4523     if (gcdof && (gcdof != cdof)) {
4524       PetscCall(PetscSynchronizedPrintf(comm, "[%d]Global constraints %" PetscInt_FMT " for point %" PetscInt_FMT " not equal to local constraints %" PetscInt_FMT "\n", rank, gcdof, p, cdof));
4525       valid = PETSC_FALSE;
4526     }
4527     if (gdof < 0) {
4528       gsize = gdof < 0 ? -(gdof + 1) - gcdof : gdof - gcdof;
4529       for (d = 0; d < gsize; ++d) {
4530         PetscInt offset = -(goff + 1) + d, r;
4531 
4532         PetscCall(PetscFindInt(offset, size + 1, ranges, &r));
4533         if (r < 0) r = -(r + 2);
4534         if ((r < 0) || (r >= size)) {
4535           PetscCall(PetscSynchronizedPrintf(comm, "[%d]Point %" PetscInt_FMT " mapped to invalid process %" PetscInt_FMT " (%" PetscInt_FMT ", %" PetscInt_FMT ")\n", rank, p, r, gdof, goff));
4536           valid = PETSC_FALSE;
4537           break;
4538         }
4539       }
4540     }
4541   }
4542   PetscCall(PetscLayoutDestroy(&layout));
4543   PetscCall(PetscSynchronizedFlush(comm, NULL));
4544   PetscCallMPI(MPIU_Allreduce(&valid, &gvalid, 1, MPIU_BOOL, MPI_LAND, comm));
4545   if (!gvalid) {
4546     PetscCall(DMView(dm, NULL));
4547     SETERRQ(comm, PETSC_ERR_ARG_WRONG, "Inconsistent local and global sections");
4548   }
4549   PetscFunctionReturn(PETSC_SUCCESS);
4550 }
4551 #endif
4552 
4553 PetscErrorCode DMGetIsoperiodicPointSF_Internal(DM dm, PetscSF *sf)
4554 {
4555   PetscErrorCode (*f)(DM, PetscSF *);
4556 
4557   PetscFunctionBegin;
4558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4559   PetscAssertPointer(sf, 2);
4560   PetscCall(PetscObjectQueryFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", &f));
4561   if (f) PetscCall(f(dm, sf));
4562   else *sf = dm->sf;
4563   PetscFunctionReturn(PETSC_SUCCESS);
4564 }
4565 
4566 /*@
4567   DMGetGlobalSection - Get the `PetscSection` encoding the global data layout for the `DM`.
4568 
4569   Collective
4570 
4571   Input Parameter:
4572 . dm - The `DM`
4573 
4574   Output Parameter:
4575 . section - The `PetscSection`
4576 
4577   Level: intermediate
4578 
4579   Note:
4580   This gets a borrowed reference, so the user should not destroy this `PetscSection`.
4581 
4582 .seealso: [](ch_dmbase), `DM`, `DMSetLocalSection()`, `DMGetLocalSection()`
4583 @*/
4584 PetscErrorCode DMGetGlobalSection(DM dm, PetscSection *section)
4585 {
4586   PetscFunctionBegin;
4587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4588   PetscAssertPointer(section, 2);
4589   if (!dm->globalSection) {
4590     PetscSection s;
4591     PetscSF      sf;
4592 
4593     PetscCall(DMGetLocalSection(dm, &s));
4594     PetscCheck(s, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "DM must have a default PetscSection in order to create a global PetscSection");
4595     PetscCheck(dm->sf, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "DM must have a point PetscSF in order to create a global PetscSection");
4596     PetscCall(DMGetIsoperiodicPointSF_Internal(dm, &sf));
4597     PetscCall(PetscSectionCreateGlobalSection(s, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &dm->globalSection));
4598     PetscCall(PetscLayoutDestroy(&dm->map));
4599     PetscCall(PetscSectionGetValueLayout(PetscObjectComm((PetscObject)dm), dm->globalSection, &dm->map));
4600     PetscCall(PetscSectionViewFromOptions(dm->globalSection, NULL, "-global_section_view"));
4601   }
4602   *section = dm->globalSection;
4603   PetscFunctionReturn(PETSC_SUCCESS);
4604 }
4605 
4606 /*@
4607   DMSetGlobalSection - Set the `PetscSection` encoding the global data layout for the `DM`.
4608 
4609   Input Parameters:
4610 + dm      - The `DM`
4611 - section - The PetscSection, or `NULL`
4612 
4613   Level: intermediate
4614 
4615   Note:
4616   Any existing `PetscSection` will be destroyed
4617 
4618 .seealso: [](ch_dmbase), `DM`, `DMGetGlobalSection()`, `DMSetLocalSection()`
4619 @*/
4620 PetscErrorCode DMSetGlobalSection(DM dm, PetscSection section)
4621 {
4622   PetscFunctionBegin;
4623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4624   if (section) PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
4625   PetscCall(PetscObjectReference((PetscObject)section));
4626   PetscCall(PetscSectionDestroy(&dm->globalSection));
4627   dm->globalSection = section;
4628 #if defined(PETSC_USE_DEBUG)
4629   if (section) PetscCall(DMDefaultSectionCheckConsistency_Internal(dm, dm->localSection, section));
4630 #endif
4631   /* Clear global scratch vectors and sectionSF */
4632   PetscCall(PetscSFDestroy(&dm->sectionSF));
4633   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &dm->sectionSF));
4634   PetscCall(DMClearGlobalVectors(dm));
4635   PetscCall(DMClearNamedGlobalVectors(dm));
4636   PetscFunctionReturn(PETSC_SUCCESS);
4637 }
4638 
4639 /*@
4640   DMGetSectionSF - Get the `PetscSF` encoding the parallel dof overlap for the `DM`. If it has not been set,
4641   it is created from the default `PetscSection` layouts in the `DM`.
4642 
4643   Input Parameter:
4644 . dm - The `DM`
4645 
4646   Output Parameter:
4647 . sf - The `PetscSF`
4648 
4649   Level: intermediate
4650 
4651   Note:
4652   This gets a borrowed reference, so the user should not destroy this `PetscSF`.
4653 
4654 .seealso: [](ch_dmbase), `DM`, `DMSetSectionSF()`, `DMCreateSectionSF()`
4655 @*/
4656 PetscErrorCode DMGetSectionSF(DM dm, PetscSF *sf)
4657 {
4658   PetscInt nroots;
4659 
4660   PetscFunctionBegin;
4661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4662   PetscAssertPointer(sf, 2);
4663   if (!dm->sectionSF) PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &dm->sectionSF));
4664   PetscCall(PetscSFGetGraph(dm->sectionSF, &nroots, NULL, NULL, NULL));
4665   if (nroots < 0) {
4666     PetscSection section, gSection;
4667 
4668     PetscCall(DMGetLocalSection(dm, &section));
4669     if (section) {
4670       PetscCall(DMGetGlobalSection(dm, &gSection));
4671       PetscCall(DMCreateSectionSF(dm, section, gSection));
4672     } else {
4673       *sf = NULL;
4674       PetscFunctionReturn(PETSC_SUCCESS);
4675     }
4676   }
4677   *sf = dm->sectionSF;
4678   PetscFunctionReturn(PETSC_SUCCESS);
4679 }
4680 
4681 /*@
4682   DMSetSectionSF - Set the `PetscSF` encoding the parallel dof overlap for the `DM`
4683 
4684   Input Parameters:
4685 + dm - The `DM`
4686 - sf - The `PetscSF`
4687 
4688   Level: intermediate
4689 
4690   Note:
4691   Any previous `PetscSF` is destroyed
4692 
4693 .seealso: [](ch_dmbase), `DM`, `DMGetSectionSF()`, `DMCreateSectionSF()`
4694 @*/
4695 PetscErrorCode DMSetSectionSF(DM dm, PetscSF sf)
4696 {
4697   PetscFunctionBegin;
4698   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4699   if (sf) PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
4700   PetscCall(PetscObjectReference((PetscObject)sf));
4701   PetscCall(PetscSFDestroy(&dm->sectionSF));
4702   dm->sectionSF = sf;
4703   PetscFunctionReturn(PETSC_SUCCESS);
4704 }
4705 
4706 /*@
4707   DMCreateSectionSF - Create the `PetscSF` encoding the parallel dof overlap for the `DM` based upon the `PetscSection`s
4708   describing the data layout.
4709 
4710   Input Parameters:
4711 + dm            - The `DM`
4712 . localSection  - `PetscSection` describing the local data layout
4713 - globalSection - `PetscSection` describing the global data layout
4714 
4715   Level: developer
4716 
4717   Note:
4718   One usually uses `DMGetSectionSF()` to obtain the `PetscSF`
4719 
4720   Developer Note:
4721   Since this routine has for arguments the two sections from the `DM` and puts the resulting `PetscSF`
4722   directly into the `DM`, perhaps this function should not take the local and global sections as
4723   input and should just obtain them from the `DM`? Plus PETSc creation functions return the thing
4724   they create, this returns nothing
4725 
4726 .seealso: [](ch_dmbase), `DM`, `DMGetSectionSF()`, `DMSetSectionSF()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
4727 @*/
4728 PetscErrorCode DMCreateSectionSF(DM dm, PetscSection localSection, PetscSection globalSection)
4729 {
4730   PetscFunctionBegin;
4731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4732   PetscCall(PetscSFSetGraphSection(dm->sectionSF, localSection, globalSection));
4733   PetscFunctionReturn(PETSC_SUCCESS);
4734 }
4735 
4736 /*@
4737   DMGetPointSF - Get the `PetscSF` encoding the parallel section point overlap for the `DM`.
4738 
4739   Not collective but the resulting `PetscSF` is collective
4740 
4741   Input Parameter:
4742 . dm - The `DM`
4743 
4744   Output Parameter:
4745 . sf - The `PetscSF`
4746 
4747   Level: intermediate
4748 
4749   Note:
4750   This gets a borrowed reference, so the user should not destroy this `PetscSF`.
4751 
4752 .seealso: [](ch_dmbase), `DM`, `DMSetPointSF()`, `DMGetSectionSF()`, `DMSetSectionSF()`, `DMCreateSectionSF()`
4753 @*/
4754 PetscErrorCode DMGetPointSF(DM dm, PetscSF *sf)
4755 {
4756   PetscFunctionBegin;
4757   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4758   PetscAssertPointer(sf, 2);
4759   *sf = dm->sf;
4760   PetscFunctionReturn(PETSC_SUCCESS);
4761 }
4762 
4763 /*@
4764   DMSetPointSF - Set the `PetscSF` encoding the parallel section point overlap for the `DM`.
4765 
4766   Collective
4767 
4768   Input Parameters:
4769 + dm - The `DM`
4770 - sf - The `PetscSF`
4771 
4772   Level: intermediate
4773 
4774 .seealso: [](ch_dmbase), `DM`, `DMGetPointSF()`, `DMGetSectionSF()`, `DMSetSectionSF()`, `DMCreateSectionSF()`
4775 @*/
4776 PetscErrorCode DMSetPointSF(DM dm, PetscSF sf)
4777 {
4778   PetscFunctionBegin;
4779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4780   if (sf) PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
4781   PetscCall(PetscObjectReference((PetscObject)sf));
4782   PetscCall(PetscSFDestroy(&dm->sf));
4783   dm->sf = sf;
4784   PetscFunctionReturn(PETSC_SUCCESS);
4785 }
4786 
4787 /*@
4788   DMGetNaturalSF - Get the `PetscSF` encoding the map back to the original mesh ordering
4789 
4790   Input Parameter:
4791 . dm - The `DM`
4792 
4793   Output Parameter:
4794 . sf - The `PetscSF`
4795 
4796   Level: intermediate
4797 
4798   Note:
4799   This gets a borrowed reference, so the user should not destroy this `PetscSF`.
4800 
4801 .seealso: [](ch_dmbase), `DM`, `DMSetNaturalSF()`, `DMSetUseNatural()`, `DMGetUseNatural()`, `DMPlexCreateGlobalToNaturalSF()`, `DMPlexDistribute()`
4802 @*/
4803 PetscErrorCode DMGetNaturalSF(DM dm, PetscSF *sf)
4804 {
4805   PetscFunctionBegin;
4806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4807   PetscAssertPointer(sf, 2);
4808   *sf = dm->sfNatural;
4809   PetscFunctionReturn(PETSC_SUCCESS);
4810 }
4811 
4812 /*@
4813   DMSetNaturalSF - Set the PetscSF encoding the map back to the original mesh ordering
4814 
4815   Input Parameters:
4816 + dm - The DM
4817 - sf - The PetscSF
4818 
4819   Level: intermediate
4820 
4821 .seealso: [](ch_dmbase), `DM`, `DMGetNaturalSF()`, `DMSetUseNatural()`, `DMGetUseNatural()`, `DMPlexCreateGlobalToNaturalSF()`, `DMPlexDistribute()`
4822 @*/
4823 PetscErrorCode DMSetNaturalSF(DM dm, PetscSF sf)
4824 {
4825   PetscFunctionBegin;
4826   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4827   if (sf) PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 2);
4828   PetscCall(PetscObjectReference((PetscObject)sf));
4829   PetscCall(PetscSFDestroy(&dm->sfNatural));
4830   dm->sfNatural = sf;
4831   PetscFunctionReturn(PETSC_SUCCESS);
4832 }
4833 
4834 static PetscErrorCode DMSetDefaultAdjacency_Private(DM dm, PetscInt f, PetscObject disc)
4835 {
4836   PetscClassId id;
4837 
4838   PetscFunctionBegin;
4839   PetscCall(PetscObjectGetClassId(disc, &id));
4840   if (id == PETSCFE_CLASSID) {
4841     PetscCall(DMSetAdjacency(dm, f, PETSC_FALSE, PETSC_TRUE));
4842   } else if (id == PETSCFV_CLASSID) {
4843     PetscCall(DMSetAdjacency(dm, f, PETSC_TRUE, PETSC_FALSE));
4844   } else {
4845     PetscCall(DMSetAdjacency(dm, f, PETSC_FALSE, PETSC_TRUE));
4846   }
4847   PetscFunctionReturn(PETSC_SUCCESS);
4848 }
4849 
4850 static PetscErrorCode DMFieldEnlarge_Static(DM dm, PetscInt NfNew)
4851 {
4852   RegionField *tmpr;
4853   PetscInt     Nf = dm->Nf, f;
4854 
4855   PetscFunctionBegin;
4856   if (Nf >= NfNew) PetscFunctionReturn(PETSC_SUCCESS);
4857   PetscCall(PetscMalloc1(NfNew, &tmpr));
4858   for (f = 0; f < Nf; ++f) tmpr[f] = dm->fields[f];
4859   for (f = Nf; f < NfNew; ++f) {
4860     tmpr[f].disc        = NULL;
4861     tmpr[f].label       = NULL;
4862     tmpr[f].avoidTensor = PETSC_FALSE;
4863   }
4864   PetscCall(PetscFree(dm->fields));
4865   dm->Nf     = NfNew;
4866   dm->fields = tmpr;
4867   PetscFunctionReturn(PETSC_SUCCESS);
4868 }
4869 
4870 /*@
4871   DMClearFields - Remove all fields from the `DM`
4872 
4873   Logically Collective
4874 
4875   Input Parameter:
4876 . dm - The `DM`
4877 
4878   Level: intermediate
4879 
4880 .seealso: [](ch_dmbase), `DM`, `DMGetNumFields()`, `DMSetNumFields()`, `DMSetField()`
4881 @*/
4882 PetscErrorCode DMClearFields(DM dm)
4883 {
4884   PetscInt f;
4885 
4886   PetscFunctionBegin;
4887   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4888   for (f = 0; f < dm->Nf; ++f) {
4889     PetscCall(PetscObjectDestroy(&dm->fields[f].disc));
4890     PetscCall(DMLabelDestroy(&dm->fields[f].label));
4891   }
4892   PetscCall(PetscFree(dm->fields));
4893   dm->fields = NULL;
4894   dm->Nf     = 0;
4895   PetscFunctionReturn(PETSC_SUCCESS);
4896 }
4897 
4898 /*@
4899   DMGetNumFields - Get the number of fields in the `DM`
4900 
4901   Not Collective
4902 
4903   Input Parameter:
4904 . dm - The `DM`
4905 
4906   Output Parameter:
4907 . numFields - The number of fields
4908 
4909   Level: intermediate
4910 
4911 .seealso: [](ch_dmbase), `DM`, `DMSetNumFields()`, `DMSetField()`
4912 @*/
4913 PetscErrorCode DMGetNumFields(DM dm, PetscInt *numFields)
4914 {
4915   PetscFunctionBegin;
4916   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4917   PetscAssertPointer(numFields, 2);
4918   *numFields = dm->Nf;
4919   PetscFunctionReturn(PETSC_SUCCESS);
4920 }
4921 
4922 /*@
4923   DMSetNumFields - Set the number of fields in the `DM`
4924 
4925   Logically Collective
4926 
4927   Input Parameters:
4928 + dm        - The `DM`
4929 - numFields - The number of fields
4930 
4931   Level: intermediate
4932 
4933 .seealso: [](ch_dmbase), `DM`, `DMGetNumFields()`, `DMSetField()`
4934 @*/
4935 PetscErrorCode DMSetNumFields(DM dm, PetscInt numFields)
4936 {
4937   PetscInt Nf, f;
4938 
4939   PetscFunctionBegin;
4940   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4941   PetscCall(DMGetNumFields(dm, &Nf));
4942   for (f = Nf; f < numFields; ++f) {
4943     PetscContainer obj;
4944 
4945     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)dm), &obj));
4946     PetscCall(DMAddField(dm, NULL, (PetscObject)obj));
4947     PetscCall(PetscContainerDestroy(&obj));
4948   }
4949   PetscFunctionReturn(PETSC_SUCCESS);
4950 }
4951 
4952 /*@
4953   DMGetField - Return the `DMLabel` and discretization object for a given `DM` field
4954 
4955   Not Collective
4956 
4957   Input Parameters:
4958 + dm - The `DM`
4959 - f  - The field number
4960 
4961   Output Parameters:
4962 + label - The label indicating the support of the field, or `NULL` for the entire mesh (pass in `NULL` if not needed)
4963 - disc  - The discretization object (pass in `NULL` if not needed)
4964 
4965   Level: intermediate
4966 
4967 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMSetField()`
4968 @*/
4969 PetscErrorCode DMGetField(DM dm, PetscInt f, DMLabel *label, PetscObject *disc)
4970 {
4971   PetscFunctionBegin;
4972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4973   PetscAssertPointer(disc, 4);
4974   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);
4975   if (label) *label = dm->fields[f].label;
4976   if (disc) *disc = dm->fields[f].disc;
4977   PetscFunctionReturn(PETSC_SUCCESS);
4978 }
4979 
4980 /* Does not clear the DS */
4981 PetscErrorCode DMSetField_Internal(DM dm, PetscInt f, DMLabel label, PetscObject disc)
4982 {
4983   PetscFunctionBegin;
4984   PetscCall(DMFieldEnlarge_Static(dm, f + 1));
4985   PetscCall(DMLabelDestroy(&dm->fields[f].label));
4986   PetscCall(PetscObjectDestroy(&dm->fields[f].disc));
4987   dm->fields[f].label = label;
4988   dm->fields[f].disc  = disc;
4989   PetscCall(PetscObjectReference((PetscObject)label));
4990   PetscCall(PetscObjectReference(disc));
4991   PetscFunctionReturn(PETSC_SUCCESS);
4992 }
4993 
4994 /*@
4995   DMSetField - Set the discretization object for a given `DM` field. Usually one would call `DMAddField()` which automatically handles
4996   the field numbering.
4997 
4998   Logically Collective
4999 
5000   Input Parameters:
5001 + dm    - The `DM`
5002 . f     - The field number
5003 . label - The label indicating the support of the field, or `NULL` for the entire mesh
5004 - disc  - The discretization object
5005 
5006   Level: intermediate
5007 
5008 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMGetField()`
5009 @*/
5010 PetscErrorCode DMSetField(DM dm, PetscInt f, DMLabel label, PetscObject disc)
5011 {
5012   PetscFunctionBegin;
5013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5014   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3);
5015   PetscValidHeader(disc, 4);
5016   PetscCheck(f >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field number %" PetscInt_FMT " must be non-negative", f);
5017   PetscCall(DMSetField_Internal(dm, f, label, disc));
5018   PetscCall(DMSetDefaultAdjacency_Private(dm, f, disc));
5019   PetscCall(DMClearDS(dm));
5020   PetscFunctionReturn(PETSC_SUCCESS);
5021 }
5022 
5023 /*@
5024   DMAddField - Add a field to a `DM` object. A field is a function space defined by of a set of discretization points (geometric entities)
5025   and a discretization object that defines the function space associated with those points.
5026 
5027   Logically Collective
5028 
5029   Input Parameters:
5030 + dm    - The `DM`
5031 . label - The label indicating the support of the field, or `NULL` for the entire mesh
5032 - disc  - The discretization object
5033 
5034   Level: intermediate
5035 
5036   Notes:
5037   The label already exists or will be added to the `DM` with `DMSetLabel()`.
5038 
5039   For example, a piecewise continuous pressure field can be defined by coefficients at the cell centers of a mesh and piecewise constant functions
5040   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
5041   geometry entities, a `DMLabel` indicating a subset of those geometric entities, and a discretization object, such as a `PetscFE`.
5042 
5043 .seealso: [](ch_dmbase), `DM`, `DMSetLabel()`, `DMSetField()`, `DMGetField()`, `PetscFE`
5044 @*/
5045 PetscErrorCode DMAddField(DM dm, DMLabel label, PetscObject disc)
5046 {
5047   PetscInt Nf = dm->Nf;
5048 
5049   PetscFunctionBegin;
5050   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5051   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
5052   PetscValidHeader(disc, 3);
5053   PetscCall(DMFieldEnlarge_Static(dm, Nf + 1));
5054   dm->fields[Nf].label = label;
5055   dm->fields[Nf].disc  = disc;
5056   PetscCall(PetscObjectReference((PetscObject)label));
5057   PetscCall(PetscObjectReference(disc));
5058   PetscCall(DMSetDefaultAdjacency_Private(dm, Nf, disc));
5059   PetscCall(DMClearDS(dm));
5060   PetscFunctionReturn(PETSC_SUCCESS);
5061 }
5062 
5063 /*@
5064   DMSetFieldAvoidTensor - Set flag to avoid defining the field on tensor cells
5065 
5066   Logically Collective
5067 
5068   Input Parameters:
5069 + dm          - The `DM`
5070 . f           - The field index
5071 - avoidTensor - `PETSC_TRUE` to skip defining the field on tensor cells
5072 
5073   Level: intermediate
5074 
5075 .seealso: [](ch_dmbase), `DM`, `DMGetFieldAvoidTensor()`, `DMSetField()`, `DMGetField()`
5076 @*/
5077 PetscErrorCode DMSetFieldAvoidTensor(DM dm, PetscInt f, PetscBool avoidTensor)
5078 {
5079   PetscFunctionBegin;
5080   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);
5081   dm->fields[f].avoidTensor = avoidTensor;
5082   PetscFunctionReturn(PETSC_SUCCESS);
5083 }
5084 
5085 /*@
5086   DMGetFieldAvoidTensor - Get flag to avoid defining the field on tensor cells
5087 
5088   Not Collective
5089 
5090   Input Parameters:
5091 + dm - The `DM`
5092 - f  - The field index
5093 
5094   Output Parameter:
5095 . avoidTensor - The flag to avoid defining the field on tensor cells
5096 
5097   Level: intermediate
5098 
5099 .seealso: [](ch_dmbase), `DM`, `DMAddField()`, `DMSetField()`, `DMGetField()`, `DMSetFieldAvoidTensor()`
5100 @*/
5101 PetscErrorCode DMGetFieldAvoidTensor(DM dm, PetscInt f, PetscBool *avoidTensor)
5102 {
5103   PetscFunctionBegin;
5104   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);
5105   *avoidTensor = dm->fields[f].avoidTensor;
5106   PetscFunctionReturn(PETSC_SUCCESS);
5107 }
5108 
5109 /*@
5110   DMCopyFields - Copy the discretizations for the `DM` into another `DM`
5111 
5112   Collective
5113 
5114   Input Parameters:
5115 + dm        - The `DM`
5116 . minDegree - Minimum degree for a discretization, or `PETSC_DETERMINE` for no limit
5117 - maxDegree - Maximum degree for a discretization, or `PETSC_DETERMINE` for no limit
5118 
5119   Output Parameter:
5120 . newdm - The `DM`
5121 
5122   Level: advanced
5123 
5124 .seealso: [](ch_dmbase), `DM`, `DMGetField()`, `DMSetField()`, `DMAddField()`, `DMCopyDS()`, `DMGetDS()`, `DMGetCellDS()`
5125 @*/
5126 PetscErrorCode DMCopyFields(DM dm, PetscInt minDegree, PetscInt maxDegree, DM newdm)
5127 {
5128   PetscInt Nf, f;
5129 
5130   PetscFunctionBegin;
5131   if (dm == newdm) PetscFunctionReturn(PETSC_SUCCESS);
5132   PetscCall(DMGetNumFields(dm, &Nf));
5133   PetscCall(DMClearFields(newdm));
5134   for (f = 0; f < Nf; ++f) {
5135     DMLabel      label;
5136     PetscObject  field;
5137     PetscClassId id;
5138     PetscBool    useCone, useClosure;
5139 
5140     PetscCall(DMGetField(dm, f, &label, &field));
5141     PetscCall(PetscObjectGetClassId(field, &id));
5142     if (id == PETSCFE_CLASSID) {
5143       PetscFE newfe;
5144 
5145       PetscCall(PetscFELimitDegree((PetscFE)field, minDegree, maxDegree, &newfe));
5146       PetscCall(DMSetField(newdm, f, label, (PetscObject)newfe));
5147       PetscCall(PetscFEDestroy(&newfe));
5148     } else {
5149       PetscCall(DMSetField(newdm, f, label, field));
5150     }
5151     PetscCall(DMGetAdjacency(dm, f, &useCone, &useClosure));
5152     PetscCall(DMSetAdjacency(newdm, f, useCone, useClosure));
5153   }
5154   PetscFunctionReturn(PETSC_SUCCESS);
5155 }
5156 
5157 /*@
5158   DMGetAdjacency - Returns the flags for determining variable influence
5159 
5160   Not Collective
5161 
5162   Input Parameters:
5163 + dm - The `DM` object
5164 - f  - The field number, or `PETSC_DEFAULT` for the default adjacency
5165 
5166   Output Parameters:
5167 + useCone    - Flag for variable influence starting with the cone operation
5168 - useClosure - Flag for variable influence using transitive closure
5169 
5170   Level: developer
5171 
5172   Notes:
5173 .vb
5174      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5175      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5176      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5177 .ve
5178   Further explanation can be found in the User's Manual Section on the Influence of Variables on One Another.
5179 
5180 .seealso: [](ch_dmbase), `DM`, `DMSetAdjacency()`, `DMGetField()`, `DMSetField()`
5181 @*/
5182 PetscErrorCode DMGetAdjacency(DM dm, PetscInt f, PetscBool *useCone, PetscBool *useClosure)
5183 {
5184   PetscFunctionBegin;
5185   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5186   if (useCone) PetscAssertPointer(useCone, 3);
5187   if (useClosure) PetscAssertPointer(useClosure, 4);
5188   if (f < 0) {
5189     if (useCone) *useCone = dm->adjacency[0];
5190     if (useClosure) *useClosure = dm->adjacency[1];
5191   } else {
5192     PetscInt Nf;
5193 
5194     PetscCall(DMGetNumFields(dm, &Nf));
5195     PetscCheck(f < Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field number %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", f, Nf);
5196     if (useCone) *useCone = dm->fields[f].adjacency[0];
5197     if (useClosure) *useClosure = dm->fields[f].adjacency[1];
5198   }
5199   PetscFunctionReturn(PETSC_SUCCESS);
5200 }
5201 
5202 /*@
5203   DMSetAdjacency - Set the flags for determining variable influence
5204 
5205   Not Collective
5206 
5207   Input Parameters:
5208 + dm         - The `DM` object
5209 . f          - The field number
5210 . useCone    - Flag for variable influence starting with the cone operation
5211 - useClosure - Flag for variable influence using transitive closure
5212 
5213   Level: developer
5214 
5215   Notes:
5216 .vb
5217      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5218      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5219      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5220 .ve
5221   Further explanation can be found in the User's Manual Section on the Influence of Variables on One Another.
5222 
5223 .seealso: [](ch_dmbase), `DM`, `DMGetAdjacency()`, `DMGetField()`, `DMSetField()`
5224 @*/
5225 PetscErrorCode DMSetAdjacency(DM dm, PetscInt f, PetscBool useCone, PetscBool useClosure)
5226 {
5227   PetscFunctionBegin;
5228   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5229   if (f < 0) {
5230     dm->adjacency[0] = useCone;
5231     dm->adjacency[1] = useClosure;
5232   } else {
5233     PetscInt Nf;
5234 
5235     PetscCall(DMGetNumFields(dm, &Nf));
5236     PetscCheck(f < Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field number %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", f, Nf);
5237     dm->fields[f].adjacency[0] = useCone;
5238     dm->fields[f].adjacency[1] = useClosure;
5239   }
5240   PetscFunctionReturn(PETSC_SUCCESS);
5241 }
5242 
5243 /*@
5244   DMGetBasicAdjacency - Returns the flags for determining variable influence, using either the default or field 0 if it is defined
5245 
5246   Not collective
5247 
5248   Input Parameter:
5249 . dm - The `DM` object
5250 
5251   Output Parameters:
5252 + useCone    - Flag for variable influence starting with the cone operation
5253 - useClosure - Flag for variable influence using transitive closure
5254 
5255   Level: developer
5256 
5257   Notes:
5258 .vb
5259      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5260      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5261      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5262 .ve
5263 
5264 .seealso: [](ch_dmbase), `DM`, `DMSetBasicAdjacency()`, `DMGetField()`, `DMSetField()`
5265 @*/
5266 PetscErrorCode DMGetBasicAdjacency(DM dm, PetscBool *useCone, PetscBool *useClosure)
5267 {
5268   PetscInt Nf;
5269 
5270   PetscFunctionBegin;
5271   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5272   if (useCone) PetscAssertPointer(useCone, 2);
5273   if (useClosure) PetscAssertPointer(useClosure, 3);
5274   PetscCall(DMGetNumFields(dm, &Nf));
5275   if (!Nf) {
5276     PetscCall(DMGetAdjacency(dm, PETSC_DEFAULT, useCone, useClosure));
5277   } else {
5278     PetscCall(DMGetAdjacency(dm, 0, useCone, useClosure));
5279   }
5280   PetscFunctionReturn(PETSC_SUCCESS);
5281 }
5282 
5283 /*@
5284   DMSetBasicAdjacency - Set the flags for determining variable influence, using either the default or field 0 if it is defined
5285 
5286   Not Collective
5287 
5288   Input Parameters:
5289 + dm         - The `DM` object
5290 . useCone    - Flag for variable influence starting with the cone operation
5291 - useClosure - Flag for variable influence using transitive closure
5292 
5293   Level: developer
5294 
5295   Notes:
5296 .vb
5297      FEM:   Two points p and q are adjacent if q \in closure(star(p)),   useCone = PETSC_FALSE, useClosure = PETSC_TRUE
5298      FVM:   Two points p and q are adjacent if q \in support(p+cone(p)), useCone = PETSC_TRUE,  useClosure = PETSC_FALSE
5299      FVM++: Two points p and q are adjacent if q \in star(closure(p)),   useCone = PETSC_TRUE,  useClosure = PETSC_TRUE
5300 .ve
5301 
5302 .seealso: [](ch_dmbase), `DM`, `DMGetBasicAdjacency()`, `DMGetField()`, `DMSetField()`
5303 @*/
5304 PetscErrorCode DMSetBasicAdjacency(DM dm, PetscBool useCone, PetscBool useClosure)
5305 {
5306   PetscInt Nf;
5307 
5308   PetscFunctionBegin;
5309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5310   PetscCall(DMGetNumFields(dm, &Nf));
5311   if (!Nf) {
5312     PetscCall(DMSetAdjacency(dm, PETSC_DEFAULT, useCone, useClosure));
5313   } else {
5314     PetscCall(DMSetAdjacency(dm, 0, useCone, useClosure));
5315   }
5316   PetscFunctionReturn(PETSC_SUCCESS);
5317 }
5318 
5319 PetscErrorCode DMCompleteBCLabels_Internal(DM dm)
5320 {
5321   DM           plex;
5322   DMLabel     *labels, *glabels;
5323   const char **names;
5324   char        *sendNames, *recvNames;
5325   PetscInt     Nds, s, maxLabels = 0, maxLen = 0, gmaxLen, Nl = 0, gNl, l, gl, m;
5326   size_t       len;
5327   MPI_Comm     comm;
5328   PetscMPIInt  rank, size, p, *counts, *displs;
5329 
5330   PetscFunctionBegin;
5331   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5332   PetscCallMPI(MPI_Comm_size(comm, &size));
5333   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5334   PetscCall(DMGetNumDS(dm, &Nds));
5335   for (s = 0; s < Nds; ++s) {
5336     PetscDS  dsBC;
5337     PetscInt numBd;
5338 
5339     PetscCall(DMGetRegionNumDS(dm, s, NULL, NULL, &dsBC, NULL));
5340     PetscCall(PetscDSGetNumBoundary(dsBC, &numBd));
5341     maxLabels += numBd;
5342   }
5343   PetscCall(PetscCalloc1(maxLabels, &labels));
5344   /* Get list of labels to be completed */
5345   for (s = 0; s < Nds; ++s) {
5346     PetscDS  dsBC;
5347     PetscInt numBd, bd;
5348 
5349     PetscCall(DMGetRegionNumDS(dm, s, NULL, NULL, &dsBC, NULL));
5350     PetscCall(PetscDSGetNumBoundary(dsBC, &numBd));
5351     for (bd = 0; bd < numBd; ++bd) {
5352       DMLabel      label;
5353       PetscInt     field;
5354       PetscObject  obj;
5355       PetscClassId id;
5356 
5357       PetscCall(PetscDSGetBoundary(dsBC, bd, NULL, NULL, NULL, &label, NULL, NULL, &field, NULL, NULL, NULL, NULL, NULL));
5358       PetscCall(DMGetField(dm, field, NULL, &obj));
5359       PetscCall(PetscObjectGetClassId(obj, &id));
5360       if (!(id == PETSCFE_CLASSID) || !label) continue;
5361       for (l = 0; l < Nl; ++l)
5362         if (labels[l] == label) break;
5363       if (l == Nl) labels[Nl++] = label;
5364     }
5365   }
5366   /* Get label names */
5367   PetscCall(PetscMalloc1(Nl, &names));
5368   for (l = 0; l < Nl; ++l) PetscCall(PetscObjectGetName((PetscObject)labels[l], &names[l]));
5369   for (l = 0; l < Nl; ++l) {
5370     PetscCall(PetscStrlen(names[l], &len));
5371     maxLen = PetscMax(maxLen, (PetscInt)len + 2);
5372   }
5373   PetscCall(PetscFree(labels));
5374   PetscCallMPI(MPIU_Allreduce(&maxLen, &gmaxLen, 1, MPIU_INT, MPI_MAX, comm));
5375   PetscCall(PetscCalloc1(Nl * gmaxLen, &sendNames));
5376   for (l = 0; l < Nl; ++l) PetscCall(PetscStrncpy(&sendNames[gmaxLen * l], names[l], gmaxLen));
5377   PetscCall(PetscFree(names));
5378   /* Put all names on all processes */
5379   PetscCall(PetscCalloc2(size, &counts, size + 1, &displs));
5380   PetscCallMPI(MPI_Allgather(&Nl, 1, MPI_INT, counts, 1, MPI_INT, comm));
5381   for (p = 0; p < size; ++p) displs[p + 1] = displs[p] + counts[p];
5382   gNl = displs[size];
5383   for (p = 0; p < size; ++p) {
5384     counts[p] *= gmaxLen;
5385     displs[p] *= gmaxLen;
5386   }
5387   PetscCall(PetscCalloc2(gNl * gmaxLen, &recvNames, gNl, &glabels));
5388   PetscCallMPI(MPI_Allgatherv(sendNames, counts[rank], MPI_CHAR, recvNames, counts, displs, MPI_CHAR, comm));
5389   PetscCall(PetscFree2(counts, displs));
5390   PetscCall(PetscFree(sendNames));
5391   for (l = 0, gl = 0; l < gNl; ++l) {
5392     PetscCall(DMGetLabel(dm, &recvNames[l * gmaxLen], &glabels[gl]));
5393     PetscCheck(glabels[gl], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Label %s missing on rank %d", &recvNames[l * gmaxLen], rank);
5394     for (m = 0; m < gl; ++m)
5395       if (glabels[m] == glabels[gl]) goto next_label;
5396     PetscCall(DMConvert(dm, DMPLEX, &plex));
5397     PetscCall(DMPlexLabelComplete(plex, glabels[gl]));
5398     PetscCall(DMDestroy(&plex));
5399     ++gl;
5400   next_label:
5401     continue;
5402   }
5403   PetscCall(PetscFree2(recvNames, glabels));
5404   PetscFunctionReturn(PETSC_SUCCESS);
5405 }
5406 
5407 static PetscErrorCode DMDSEnlarge_Static(DM dm, PetscInt NdsNew)
5408 {
5409   DMSpace *tmpd;
5410   PetscInt Nds = dm->Nds, s;
5411 
5412   PetscFunctionBegin;
5413   if (Nds >= NdsNew) PetscFunctionReturn(PETSC_SUCCESS);
5414   PetscCall(PetscMalloc1(NdsNew, &tmpd));
5415   for (s = 0; s < Nds; ++s) tmpd[s] = dm->probs[s];
5416   for (s = Nds; s < NdsNew; ++s) {
5417     tmpd[s].ds     = NULL;
5418     tmpd[s].label  = NULL;
5419     tmpd[s].fields = NULL;
5420   }
5421   PetscCall(PetscFree(dm->probs));
5422   dm->Nds   = NdsNew;
5423   dm->probs = tmpd;
5424   PetscFunctionReturn(PETSC_SUCCESS);
5425 }
5426 
5427 /*@
5428   DMGetNumDS - Get the number of discrete systems in the `DM`
5429 
5430   Not Collective
5431 
5432   Input Parameter:
5433 . dm - The `DM`
5434 
5435   Output Parameter:
5436 . Nds - The number of `PetscDS` objects
5437 
5438   Level: intermediate
5439 
5440 .seealso: [](ch_dmbase), `DM`, `DMGetDS()`, `DMGetCellDS()`
5441 @*/
5442 PetscErrorCode DMGetNumDS(DM dm, PetscInt *Nds)
5443 {
5444   PetscFunctionBegin;
5445   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5446   PetscAssertPointer(Nds, 2);
5447   *Nds = dm->Nds;
5448   PetscFunctionReturn(PETSC_SUCCESS);
5449 }
5450 
5451 /*@
5452   DMClearDS - Remove all discrete systems from the `DM`
5453 
5454   Logically Collective
5455 
5456   Input Parameter:
5457 . dm - The `DM`
5458 
5459   Level: intermediate
5460 
5461 .seealso: [](ch_dmbase), `DM`, `DMGetNumDS()`, `DMGetDS()`, `DMSetField()`
5462 @*/
5463 PetscErrorCode DMClearDS(DM dm)
5464 {
5465   PetscInt s;
5466 
5467   PetscFunctionBegin;
5468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5469   for (s = 0; s < dm->Nds; ++s) {
5470     PetscCall(PetscDSDestroy(&dm->probs[s].ds));
5471     PetscCall(PetscDSDestroy(&dm->probs[s].dsIn));
5472     PetscCall(DMLabelDestroy(&dm->probs[s].label));
5473     PetscCall(ISDestroy(&dm->probs[s].fields));
5474   }
5475   PetscCall(PetscFree(dm->probs));
5476   dm->probs = NULL;
5477   dm->Nds   = 0;
5478   PetscFunctionReturn(PETSC_SUCCESS);
5479 }
5480 
5481 /*@
5482   DMGetDS - Get the default `PetscDS`
5483 
5484   Not Collective
5485 
5486   Input Parameter:
5487 . dm - The `DM`
5488 
5489   Output Parameter:
5490 . ds - The default `PetscDS`
5491 
5492   Level: intermediate
5493 
5494 .seealso: [](ch_dmbase), `DM`, `DMGetCellDS()`, `DMGetRegionDS()`
5495 @*/
5496 PetscErrorCode DMGetDS(DM dm, PetscDS *ds)
5497 {
5498   PetscFunctionBeginHot;
5499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5500   PetscAssertPointer(ds, 2);
5501   PetscCheck(dm->Nds > 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Need to call DMCreateDS() before calling DMGetDS()");
5502   *ds = dm->probs[0].ds;
5503   PetscFunctionReturn(PETSC_SUCCESS);
5504 }
5505 
5506 /*@
5507   DMGetCellDS - Get the `PetscDS` defined on a given cell
5508 
5509   Not Collective
5510 
5511   Input Parameters:
5512 + dm    - The `DM`
5513 - point - Cell for the `PetscDS`
5514 
5515   Output Parameters:
5516 + ds   - The `PetscDS` defined on the given cell
5517 - dsIn - The `PetscDS` for input on the given cell, or NULL if the same ds
5518 
5519   Level: developer
5520 
5521 .seealso: [](ch_dmbase), `DM`, `DMGetDS()`, `DMSetRegionDS()`
5522 @*/
5523 PetscErrorCode DMGetCellDS(DM dm, PetscInt point, PetscDS *ds, PetscDS *dsIn)
5524 {
5525   PetscDS  dsDef = NULL;
5526   PetscInt s;
5527 
5528   PetscFunctionBeginHot;
5529   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5530   if (ds) PetscAssertPointer(ds, 3);
5531   if (dsIn) PetscAssertPointer(dsIn, 4);
5532   PetscCheck(point >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Mesh point cannot be negative: %" PetscInt_FMT, point);
5533   if (ds) *ds = NULL;
5534   if (dsIn) *dsIn = NULL;
5535   for (s = 0; s < dm->Nds; ++s) {
5536     PetscInt val;
5537 
5538     if (!dm->probs[s].label) {
5539       dsDef = dm->probs[s].ds;
5540     } else {
5541       PetscCall(DMLabelGetValue(dm->probs[s].label, point, &val));
5542       if (val >= 0) {
5543         if (ds) *ds = dm->probs[s].ds;
5544         if (dsIn) *dsIn = dm->probs[s].dsIn;
5545         break;
5546       }
5547     }
5548   }
5549   if (ds && !*ds) *ds = dsDef;
5550   PetscFunctionReturn(PETSC_SUCCESS);
5551 }
5552 
5553 /*@
5554   DMGetRegionDS - Get the `PetscDS` for a given mesh region, defined by a `DMLabel`
5555 
5556   Not Collective
5557 
5558   Input Parameters:
5559 + dm    - The `DM`
5560 - label - The `DMLabel` defining the mesh region, or `NULL` for the entire mesh
5561 
5562   Output Parameters:
5563 + fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL`
5564 . ds     - The `PetscDS` defined on the given region, or `NULL`
5565 - dsIn   - The `PetscDS` for input in the given region, or `NULL`
5566 
5567   Level: advanced
5568 
5569   Note:
5570   If a non-`NULL` label is given, but there is no `PetscDS` on that specific label,
5571   the `PetscDS` for the full domain (if present) is returned. Returns with
5572   fields = `NULL` and ds = `NULL` if there is no `PetscDS` for the full domain.
5573 
5574 .seealso: [](ch_dmbase), `DM`, `DMGetRegionNumDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5575 @*/
5576 PetscErrorCode DMGetRegionDS(DM dm, DMLabel label, IS *fields, PetscDS *ds, PetscDS *dsIn)
5577 {
5578   PetscInt Nds = dm->Nds, s;
5579 
5580   PetscFunctionBegin;
5581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5582   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
5583   if (fields) {
5584     PetscAssertPointer(fields, 3);
5585     *fields = NULL;
5586   }
5587   if (ds) {
5588     PetscAssertPointer(ds, 4);
5589     *ds = NULL;
5590   }
5591   if (dsIn) {
5592     PetscAssertPointer(dsIn, 5);
5593     *dsIn = NULL;
5594   }
5595   for (s = 0; s < Nds; ++s) {
5596     if (dm->probs[s].label == label || !dm->probs[s].label) {
5597       if (fields) *fields = dm->probs[s].fields;
5598       if (ds) *ds = dm->probs[s].ds;
5599       if (dsIn) *dsIn = dm->probs[s].dsIn;
5600       if (dm->probs[s].label) PetscFunctionReturn(PETSC_SUCCESS);
5601     }
5602   }
5603   PetscFunctionReturn(PETSC_SUCCESS);
5604 }
5605 
5606 /*@
5607   DMSetRegionDS - Set the `PetscDS` for a given mesh region, defined by a `DMLabel`
5608 
5609   Collective
5610 
5611   Input Parameters:
5612 + dm     - The `DM`
5613 . label  - The `DMLabel` defining the mesh region, or `NULL` for the entire mesh
5614 . fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL` for all fields
5615 . ds     - The `PetscDS` defined on the given region
5616 - dsIn   - The `PetscDS` for input on the given cell, or `NULL` if it is the same `PetscDS`
5617 
5618   Level: advanced
5619 
5620   Note:
5621   If the label has a `PetscDS` defined, it will be replaced. Otherwise, it will be added to the `DM`. If the `PetscDS` is replaced,
5622   the fields argument is ignored.
5623 
5624 .seealso: [](ch_dmbase), `DM`, `DMGetRegionDS()`, `DMSetRegionNumDS()`, `DMGetDS()`, `DMGetCellDS()`
5625 @*/
5626 PetscErrorCode DMSetRegionDS(DM dm, DMLabel label, IS fields, PetscDS ds, PetscDS dsIn)
5627 {
5628   PetscInt Nds = dm->Nds, s;
5629 
5630   PetscFunctionBegin;
5631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5632   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
5633   if (fields) PetscValidHeaderSpecific(fields, IS_CLASSID, 3);
5634   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 4);
5635   if (dsIn) PetscValidHeaderSpecific(dsIn, PETSCDS_CLASSID, 5);
5636   for (s = 0; s < Nds; ++s) {
5637     if (dm->probs[s].label == label) {
5638       PetscCall(PetscDSDestroy(&dm->probs[s].ds));
5639       PetscCall(PetscDSDestroy(&dm->probs[s].dsIn));
5640       dm->probs[s].ds   = ds;
5641       dm->probs[s].dsIn = dsIn;
5642       PetscFunctionReturn(PETSC_SUCCESS);
5643     }
5644   }
5645   PetscCall(DMDSEnlarge_Static(dm, Nds + 1));
5646   PetscCall(PetscObjectReference((PetscObject)label));
5647   PetscCall(PetscObjectReference((PetscObject)fields));
5648   PetscCall(PetscObjectReference((PetscObject)ds));
5649   PetscCall(PetscObjectReference((PetscObject)dsIn));
5650   if (!label) {
5651     /* Put the NULL label at the front, so it is returned as the default */
5652     for (s = Nds - 1; s >= 0; --s) dm->probs[s + 1] = dm->probs[s];
5653     Nds = 0;
5654   }
5655   dm->probs[Nds].label  = label;
5656   dm->probs[Nds].fields = fields;
5657   dm->probs[Nds].ds     = ds;
5658   dm->probs[Nds].dsIn   = dsIn;
5659   PetscFunctionReturn(PETSC_SUCCESS);
5660 }
5661 
5662 /*@
5663   DMGetRegionNumDS - Get the `PetscDS` for a given mesh region, defined by the region number
5664 
5665   Not Collective
5666 
5667   Input Parameters:
5668 + dm  - The `DM`
5669 - num - The region number, in [0, Nds)
5670 
5671   Output Parameters:
5672 + label  - The region label, or `NULL`
5673 . fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL`
5674 . ds     - The `PetscDS` defined on the given region, or `NULL`
5675 - dsIn   - The `PetscDS` for input in the given region, or `NULL`
5676 
5677   Level: advanced
5678 
5679 .seealso: [](ch_dmbase), `DM`, `DMGetRegionDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5680 @*/
5681 PetscErrorCode DMGetRegionNumDS(DM dm, PetscInt num, DMLabel *label, IS *fields, PetscDS *ds, PetscDS *dsIn)
5682 {
5683   PetscInt Nds;
5684 
5685   PetscFunctionBegin;
5686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5687   PetscCall(DMGetNumDS(dm, &Nds));
5688   PetscCheck((num >= 0) && (num < Nds), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Region number %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", num, Nds);
5689   if (label) {
5690     PetscAssertPointer(label, 3);
5691     *label = dm->probs[num].label;
5692   }
5693   if (fields) {
5694     PetscAssertPointer(fields, 4);
5695     *fields = dm->probs[num].fields;
5696   }
5697   if (ds) {
5698     PetscAssertPointer(ds, 5);
5699     *ds = dm->probs[num].ds;
5700   }
5701   if (dsIn) {
5702     PetscAssertPointer(dsIn, 6);
5703     *dsIn = dm->probs[num].dsIn;
5704   }
5705   PetscFunctionReturn(PETSC_SUCCESS);
5706 }
5707 
5708 /*@
5709   DMSetRegionNumDS - Set the `PetscDS` for a given mesh region, defined by the region number
5710 
5711   Not Collective
5712 
5713   Input Parameters:
5714 + dm     - The `DM`
5715 . num    - The region number, in [0, Nds)
5716 . label  - The region label, or `NULL`
5717 . fields - The `IS` containing the `DM` field numbers for the fields in this `PetscDS`, or `NULL` to prevent setting
5718 . ds     - The `PetscDS` defined on the given region, or `NULL` to prevent setting
5719 - dsIn   - The `PetscDS` for input on the given cell, or `NULL` if it is the same `PetscDS`
5720 
5721   Level: advanced
5722 
5723 .seealso: [](ch_dmbase), `DM`, `DMGetRegionDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5724 @*/
5725 PetscErrorCode DMSetRegionNumDS(DM dm, PetscInt num, DMLabel label, IS fields, PetscDS ds, PetscDS dsIn)
5726 {
5727   PetscInt Nds;
5728 
5729   PetscFunctionBegin;
5730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5731   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3);
5732   PetscCall(DMGetNumDS(dm, &Nds));
5733   PetscCheck((num >= 0) && (num < Nds), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Region number %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", num, Nds);
5734   PetscCall(PetscObjectReference((PetscObject)label));
5735   PetscCall(DMLabelDestroy(&dm->probs[num].label));
5736   dm->probs[num].label = label;
5737   if (fields) {
5738     PetscValidHeaderSpecific(fields, IS_CLASSID, 4);
5739     PetscCall(PetscObjectReference((PetscObject)fields));
5740     PetscCall(ISDestroy(&dm->probs[num].fields));
5741     dm->probs[num].fields = fields;
5742   }
5743   if (ds) {
5744     PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 5);
5745     PetscCall(PetscObjectReference((PetscObject)ds));
5746     PetscCall(PetscDSDestroy(&dm->probs[num].ds));
5747     dm->probs[num].ds = ds;
5748   }
5749   if (dsIn) {
5750     PetscValidHeaderSpecific(dsIn, PETSCDS_CLASSID, 6);
5751     PetscCall(PetscObjectReference((PetscObject)dsIn));
5752     PetscCall(PetscDSDestroy(&dm->probs[num].dsIn));
5753     dm->probs[num].dsIn = dsIn;
5754   }
5755   PetscFunctionReturn(PETSC_SUCCESS);
5756 }
5757 
5758 /*@
5759   DMFindRegionNum - Find the region number for a given `PetscDS`, or -1 if it is not found.
5760 
5761   Not Collective
5762 
5763   Input Parameters:
5764 + dm - The `DM`
5765 - ds - The `PetscDS` defined on the given region
5766 
5767   Output Parameter:
5768 . num - The region number, in [0, Nds), or -1 if not found
5769 
5770   Level: advanced
5771 
5772 .seealso: [](ch_dmbase), `DM`, `DMGetRegionNumDS()`, `DMGetRegionDS()`, `DMSetRegionDS()`, `DMGetDS()`, `DMGetCellDS()`
5773 @*/
5774 PetscErrorCode DMFindRegionNum(DM dm, PetscDS ds, PetscInt *num)
5775 {
5776   PetscInt Nds, n;
5777 
5778   PetscFunctionBegin;
5779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5780   PetscValidHeaderSpecific(ds, PETSCDS_CLASSID, 2);
5781   PetscAssertPointer(num, 3);
5782   PetscCall(DMGetNumDS(dm, &Nds));
5783   for (n = 0; n < Nds; ++n)
5784     if (ds == dm->probs[n].ds) break;
5785   if (n >= Nds) *num = -1;
5786   else *num = n;
5787   PetscFunctionReturn(PETSC_SUCCESS);
5788 }
5789 
5790 /*@
5791   DMCreateFEDefault - Create a `PetscFE` based on the celltype for the mesh
5792 
5793   Not Collective
5794 
5795   Input Parameters:
5796 + dm     - The `DM`
5797 . Nc     - The number of components for the field
5798 . prefix - The options prefix for the output `PetscFE`, or `NULL`
5799 - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree
5800 
5801   Output Parameter:
5802 . fem - The `PetscFE`
5803 
5804   Level: intermediate
5805 
5806   Note:
5807   This is a convenience method that just calls `PetscFECreateByCell()` underneath.
5808 
5809 .seealso: [](ch_dmbase), `DM`, `PetscFECreateByCell()`, `DMAddField()`, `DMCreateDS()`, `DMGetCellDS()`, `DMGetRegionDS()`
5810 @*/
5811 PetscErrorCode DMCreateFEDefault(DM dm, PetscInt Nc, const char prefix[], PetscInt qorder, PetscFE *fem)
5812 {
5813   DMPolytopeType ct;
5814   PetscInt       dim, cStart;
5815 
5816   PetscFunctionBegin;
5817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5818   PetscValidLogicalCollectiveInt(dm, Nc, 2);
5819   if (prefix) PetscAssertPointer(prefix, 3);
5820   PetscValidLogicalCollectiveInt(dm, qorder, 4);
5821   PetscAssertPointer(fem, 5);
5822   PetscCall(DMGetDimension(dm, &dim));
5823   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
5824   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
5825   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, Nc, ct, prefix, qorder, fem));
5826   PetscFunctionReturn(PETSC_SUCCESS);
5827 }
5828 
5829 /*@
5830   DMCreateDS - Create the discrete systems for the `DM` based upon the fields added to the `DM`
5831 
5832   Collective
5833 
5834   Input Parameter:
5835 . dm - The `DM`
5836 
5837   Options Database Key:
5838 . -dm_petscds_view - View all the `PetscDS` objects in this `DM`
5839 
5840   Level: intermediate
5841 
5842 .seealso: [](ch_dmbase), `DM`, `DMSetField`, `DMAddField()`, `DMGetDS()`, `DMGetCellDS()`, `DMGetRegionDS()`, `DMSetRegionDS()`
5843 @*/
5844 PetscErrorCode DMCreateDS(DM dm)
5845 {
5846   MPI_Comm  comm;
5847   PetscDS   dsDef;
5848   DMLabel  *labelSet;
5849   PetscInt  dE, Nf = dm->Nf, f, s, Nl, l, Ndef, k;
5850   PetscBool doSetup = PETSC_TRUE, flg;
5851 
5852   PetscFunctionBegin;
5853   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5854   if (!dm->fields) PetscFunctionReturn(PETSC_SUCCESS);
5855   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5856   PetscCall(DMGetCoordinateDim(dm, &dE));
5857   /* Determine how many regions we have */
5858   PetscCall(PetscMalloc1(Nf, &labelSet));
5859   Nl   = 0;
5860   Ndef = 0;
5861   for (f = 0; f < Nf; ++f) {
5862     DMLabel  label = dm->fields[f].label;
5863     PetscInt l;
5864 
5865 #ifdef PETSC_HAVE_LIBCEED
5866     /* Move CEED context to discretizations */
5867     {
5868       PetscClassId id;
5869 
5870       PetscCall(PetscObjectGetClassId(dm->fields[f].disc, &id));
5871       if (id == PETSCFE_CLASSID) {
5872         Ceed ceed;
5873 
5874         PetscCall(DMGetCeed(dm, &ceed));
5875         PetscCall(PetscFESetCeed((PetscFE)dm->fields[f].disc, ceed));
5876       }
5877     }
5878 #endif
5879     if (!label) {
5880       ++Ndef;
5881       continue;
5882     }
5883     for (l = 0; l < Nl; ++l)
5884       if (label == labelSet[l]) break;
5885     if (l < Nl) continue;
5886     labelSet[Nl++] = label;
5887   }
5888   /* Create default DS if there are no labels to intersect with */
5889   PetscCall(DMGetRegionDS(dm, NULL, NULL, &dsDef, NULL));
5890   if (!dsDef && Ndef && !Nl) {
5891     IS        fields;
5892     PetscInt *fld, nf;
5893 
5894     for (f = 0, nf = 0; f < Nf; ++f)
5895       if (!dm->fields[f].label) ++nf;
5896     PetscCheck(nf, comm, PETSC_ERR_PLIB, "All fields have labels, but we are trying to create a default DS");
5897     PetscCall(PetscMalloc1(nf, &fld));
5898     for (f = 0, nf = 0; f < Nf; ++f)
5899       if (!dm->fields[f].label) fld[nf++] = f;
5900     PetscCall(ISCreate(PETSC_COMM_SELF, &fields));
5901     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fields, "dm_fields_"));
5902     PetscCall(ISSetType(fields, ISGENERAL));
5903     PetscCall(ISGeneralSetIndices(fields, nf, fld, PETSC_OWN_POINTER));
5904 
5905     PetscCall(PetscDSCreate(PETSC_COMM_SELF, &dsDef));
5906     PetscCall(DMSetRegionDS(dm, NULL, fields, dsDef, NULL));
5907     PetscCall(PetscDSDestroy(&dsDef));
5908     PetscCall(ISDestroy(&fields));
5909   }
5910   PetscCall(DMGetRegionDS(dm, NULL, NULL, &dsDef, NULL));
5911   if (dsDef) PetscCall(PetscDSSetCoordinateDimension(dsDef, dE));
5912   /* Intersect labels with default fields */
5913   if (Ndef && Nl) {
5914     DM              plex;
5915     DMLabel         cellLabel;
5916     IS              fieldIS, allcellIS, defcellIS = NULL;
5917     PetscInt       *fields;
5918     const PetscInt *cells;
5919     PetscInt        depth, nf = 0, n, c;
5920 
5921     PetscCall(DMConvert(dm, DMPLEX, &plex));
5922     PetscCall(DMPlexGetDepth(plex, &depth));
5923     PetscCall(DMGetStratumIS(plex, "dim", depth, &allcellIS));
5924     if (!allcellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, &allcellIS));
5925     /* TODO This looks like it only works for one label */
5926     for (l = 0; l < Nl; ++l) {
5927       DMLabel label = labelSet[l];
5928       IS      pointIS;
5929 
5930       PetscCall(ISDestroy(&defcellIS));
5931       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
5932       PetscCall(ISDifference(allcellIS, pointIS, &defcellIS));
5933       PetscCall(ISDestroy(&pointIS));
5934     }
5935     PetscCall(ISDestroy(&allcellIS));
5936 
5937     PetscCall(DMLabelCreate(PETSC_COMM_SELF, "defaultCells", &cellLabel));
5938     PetscCall(ISGetLocalSize(defcellIS, &n));
5939     PetscCall(ISGetIndices(defcellIS, &cells));
5940     for (c = 0; c < n; ++c) PetscCall(DMLabelSetValue(cellLabel, cells[c], 1));
5941     PetscCall(ISRestoreIndices(defcellIS, &cells));
5942     PetscCall(ISDestroy(&defcellIS));
5943     PetscCall(DMPlexLabelComplete(plex, cellLabel));
5944 
5945     PetscCall(PetscMalloc1(Ndef, &fields));
5946     for (f = 0; f < Nf; ++f)
5947       if (!dm->fields[f].label) fields[nf++] = f;
5948     PetscCall(ISCreate(PETSC_COMM_SELF, &fieldIS));
5949     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fieldIS, "dm_fields_"));
5950     PetscCall(ISSetType(fieldIS, ISGENERAL));
5951     PetscCall(ISGeneralSetIndices(fieldIS, nf, fields, PETSC_OWN_POINTER));
5952 
5953     PetscCall(PetscDSCreate(PETSC_COMM_SELF, &dsDef));
5954     PetscCall(DMSetRegionDS(dm, cellLabel, fieldIS, dsDef, NULL));
5955     PetscCall(PetscDSSetCoordinateDimension(dsDef, dE));
5956     PetscCall(DMLabelDestroy(&cellLabel));
5957     PetscCall(PetscDSDestroy(&dsDef));
5958     PetscCall(ISDestroy(&fieldIS));
5959     PetscCall(DMDestroy(&plex));
5960   }
5961   /* Create label DSes
5962      - WE ONLY SUPPORT IDENTICAL OR DISJOINT LABELS
5963   */
5964   /* TODO Should check that labels are disjoint */
5965   for (l = 0; l < Nl; ++l) {
5966     DMLabel   label = labelSet[l];
5967     PetscDS   ds, dsIn = NULL;
5968     IS        fields;
5969     PetscInt *fld, nf;
5970 
5971     PetscCall(PetscDSCreate(PETSC_COMM_SELF, &ds));
5972     for (f = 0, nf = 0; f < Nf; ++f)
5973       if (label == dm->fields[f].label || !dm->fields[f].label) ++nf;
5974     PetscCall(PetscMalloc1(nf, &fld));
5975     for (f = 0, nf = 0; f < Nf; ++f)
5976       if (label == dm->fields[f].label || !dm->fields[f].label) fld[nf++] = f;
5977     PetscCall(ISCreate(PETSC_COMM_SELF, &fields));
5978     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fields, "dm_fields_"));
5979     PetscCall(ISSetType(fields, ISGENERAL));
5980     PetscCall(ISGeneralSetIndices(fields, nf, fld, PETSC_OWN_POINTER));
5981     PetscCall(PetscDSSetCoordinateDimension(ds, dE));
5982     {
5983       DMPolytopeType ct;
5984       PetscInt       lStart, lEnd;
5985       PetscBool      isCohesiveLocal = PETSC_FALSE, isCohesive;
5986 
5987       PetscCall(DMLabelGetBounds(label, &lStart, &lEnd));
5988       if (lStart >= 0) {
5989         PetscCall(DMPlexGetCellType(dm, lStart, &ct));
5990         switch (ct) {
5991         case DM_POLYTOPE_POINT_PRISM_TENSOR:
5992         case DM_POLYTOPE_SEG_PRISM_TENSOR:
5993         case DM_POLYTOPE_TRI_PRISM_TENSOR:
5994         case DM_POLYTOPE_QUAD_PRISM_TENSOR:
5995           isCohesiveLocal = PETSC_TRUE;
5996           break;
5997         default:
5998           break;
5999         }
6000       }
6001       PetscCallMPI(MPIU_Allreduce(&isCohesiveLocal, &isCohesive, 1, MPIU_BOOL, MPI_LOR, comm));
6002       if (isCohesive) {
6003         PetscCall(PetscDSCreate(PETSC_COMM_SELF, &dsIn));
6004         PetscCall(PetscDSSetCoordinateDimension(dsIn, dE));
6005       }
6006       for (f = 0, nf = 0; f < Nf; ++f) {
6007         if (label == dm->fields[f].label || !dm->fields[f].label) {
6008           if (label == dm->fields[f].label) {
6009             PetscCall(PetscDSSetDiscretization(ds, nf, NULL));
6010             PetscCall(PetscDSSetCohesive(ds, nf, isCohesive));
6011             if (dsIn) {
6012               PetscCall(PetscDSSetDiscretization(dsIn, nf, NULL));
6013               PetscCall(PetscDSSetCohesive(dsIn, nf, isCohesive));
6014             }
6015           }
6016           ++nf;
6017         }
6018       }
6019     }
6020     PetscCall(DMSetRegionDS(dm, label, fields, ds, dsIn));
6021     PetscCall(ISDestroy(&fields));
6022     PetscCall(PetscDSDestroy(&ds));
6023     PetscCall(PetscDSDestroy(&dsIn));
6024   }
6025   PetscCall(PetscFree(labelSet));
6026   /* Set fields in DSes */
6027   for (s = 0; s < dm->Nds; ++s) {
6028     PetscDS         ds     = dm->probs[s].ds;
6029     PetscDS         dsIn   = dm->probs[s].dsIn;
6030     IS              fields = dm->probs[s].fields;
6031     const PetscInt *fld;
6032     PetscInt        nf, dsnf;
6033     PetscBool       isCohesive;
6034 
6035     PetscCall(PetscDSGetNumFields(ds, &dsnf));
6036     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
6037     PetscCall(ISGetLocalSize(fields, &nf));
6038     PetscCall(ISGetIndices(fields, &fld));
6039     for (f = 0; f < nf; ++f) {
6040       PetscObject  disc = dm->fields[fld[f]].disc;
6041       PetscBool    isCohesiveField;
6042       PetscClassId id;
6043 
6044       /* Handle DS with no fields */
6045       if (dsnf) PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
6046       /* If this is a cohesive cell, then regular fields need the lower dimensional discretization */
6047       if (isCohesive) {
6048         if (!isCohesiveField) {
6049           PetscObject bdDisc;
6050 
6051           PetscCall(PetscFEGetHeightSubspace((PetscFE)disc, 1, (PetscFE *)&bdDisc));
6052           PetscCall(PetscDSSetDiscretization(ds, f, bdDisc));
6053           PetscCall(PetscDSSetDiscretization(dsIn, f, disc));
6054         } else {
6055           PetscCall(PetscDSSetDiscretization(ds, f, disc));
6056           PetscCall(PetscDSSetDiscretization(dsIn, f, disc));
6057         }
6058       } else {
6059         PetscCall(PetscDSSetDiscretization(ds, f, disc));
6060       }
6061       /* We allow people to have placeholder fields and construct the Section by hand */
6062       PetscCall(PetscObjectGetClassId(disc, &id));
6063       if ((id != PETSCFE_CLASSID) && (id != PETSCFV_CLASSID)) doSetup = PETSC_FALSE;
6064     }
6065     PetscCall(ISRestoreIndices(fields, &fld));
6066   }
6067   /* Allow k-jet tabulation */
6068   PetscCall(PetscOptionsGetInt(NULL, ((PetscObject)dm)->prefix, "-dm_ds_jet_degree", &k, &flg));
6069   if (flg) {
6070     for (s = 0; s < dm->Nds; ++s) {
6071       PetscDS  ds   = dm->probs[s].ds;
6072       PetscDS  dsIn = dm->probs[s].dsIn;
6073       PetscInt Nf, f;
6074 
6075       PetscCall(PetscDSGetNumFields(ds, &Nf));
6076       for (f = 0; f < Nf; ++f) {
6077         PetscCall(PetscDSSetJetDegree(ds, f, k));
6078         if (dsIn) PetscCall(PetscDSSetJetDegree(dsIn, f, k));
6079       }
6080     }
6081   }
6082   /* Setup DSes */
6083   if (doSetup) {
6084     for (s = 0; s < dm->Nds; ++s) {
6085       if (dm->setfromoptionscalled) {
6086         PetscCall(PetscDSSetFromOptions(dm->probs[s].ds));
6087         if (dm->probs[s].dsIn) PetscCall(PetscDSSetFromOptions(dm->probs[s].dsIn));
6088       }
6089       PetscCall(PetscDSSetUp(dm->probs[s].ds));
6090       if (dm->probs[s].dsIn) PetscCall(PetscDSSetUp(dm->probs[s].dsIn));
6091     }
6092   }
6093   PetscFunctionReturn(PETSC_SUCCESS);
6094 }
6095 
6096 /*@
6097   DMUseTensorOrder - Use a tensor product closure ordering for the default section
6098 
6099   Input Parameters:
6100 + dm     - The DM
6101 - tensor - Flag for tensor order
6102 
6103   Level: developer
6104 
6105 .seealso: `DMPlexSetClosurePermutationTensor()`, `PetscSectionResetClosurePermutation()`
6106 @*/
6107 PetscErrorCode DMUseTensorOrder(DM dm, PetscBool tensor)
6108 {
6109   PetscInt  Nf;
6110   PetscBool reorder = PETSC_TRUE, isPlex;
6111 
6112   PetscFunctionBegin;
6113   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
6114   PetscCall(DMGetNumFields(dm, &Nf));
6115   for (PetscInt f = 0; f < Nf; ++f) {
6116     PetscObject  obj;
6117     PetscClassId id;
6118 
6119     PetscCall(DMGetField(dm, f, NULL, &obj));
6120     PetscCall(PetscObjectGetClassId(obj, &id));
6121     if (id == PETSCFE_CLASSID) {
6122       PetscSpace sp;
6123       PetscBool  tensor;
6124 
6125       PetscCall(PetscFEGetBasisSpace((PetscFE)obj, &sp));
6126       PetscCall(PetscSpacePolynomialGetTensor(sp, &tensor));
6127       reorder = reorder && tensor ? PETSC_TRUE : PETSC_FALSE;
6128     } else reorder = PETSC_FALSE;
6129   }
6130   if (tensor) {
6131     if (reorder && isPlex) PetscCall(DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL));
6132   } else {
6133     PetscSection s;
6134 
6135     PetscCall(DMGetLocalSection(dm, &s));
6136     if (s) PetscCall(PetscSectionResetClosurePermutation(s));
6137   }
6138   PetscFunctionReturn(PETSC_SUCCESS);
6139 }
6140 
6141 /*@
6142   DMComputeExactSolution - Compute the exact solution for a given `DM`, using the `PetscDS` information.
6143 
6144   Collective
6145 
6146   Input Parameters:
6147 + dm   - The `DM`
6148 - time - The time
6149 
6150   Output Parameters:
6151 + u   - The vector will be filled with exact solution values, or `NULL`
6152 - u_t - The vector will be filled with the time derivative of exact solution values, or `NULL`
6153 
6154   Level: developer
6155 
6156   Note:
6157   The user must call `PetscDSSetExactSolution()` before using this routine
6158 
6159 .seealso: [](ch_dmbase), `DM`, `PetscDSSetExactSolution()`
6160 @*/
6161 PetscErrorCode DMComputeExactSolution(DM dm, PetscReal time, Vec u, Vec u_t)
6162 {
6163   PetscErrorCode (**exacts)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
6164   void   **ectxs;
6165   Vec      locu, locu_t;
6166   PetscInt Nf, Nds, s;
6167 
6168   PetscFunctionBegin;
6169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6170   if (u) {
6171     PetscValidHeaderSpecific(u, VEC_CLASSID, 3);
6172     PetscCall(DMGetLocalVector(dm, &locu));
6173     PetscCall(VecSet(locu, 0.));
6174   }
6175   if (u_t) {
6176     PetscValidHeaderSpecific(u_t, VEC_CLASSID, 4);
6177     PetscCall(DMGetLocalVector(dm, &locu_t));
6178     PetscCall(VecSet(locu_t, 0.));
6179   }
6180   PetscCall(DMGetNumFields(dm, &Nf));
6181   PetscCall(PetscMalloc2(Nf, &exacts, Nf, &ectxs));
6182   PetscCall(DMGetNumDS(dm, &Nds));
6183   for (s = 0; s < Nds; ++s) {
6184     PetscDS         ds;
6185     DMLabel         label;
6186     IS              fieldIS;
6187     const PetscInt *fields, id = 1;
6188     PetscInt        dsNf, f;
6189 
6190     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
6191     PetscCall(PetscDSGetNumFields(ds, &dsNf));
6192     PetscCall(ISGetIndices(fieldIS, &fields));
6193     PetscCall(PetscArrayzero(exacts, Nf));
6194     PetscCall(PetscArrayzero(ectxs, Nf));
6195     if (u) {
6196       for (f = 0; f < dsNf; ++f) PetscCall(PetscDSGetExactSolution(ds, fields[f], &exacts[fields[f]], &ectxs[fields[f]]));
6197       if (label) PetscCall(DMProjectFunctionLabelLocal(dm, time, label, 1, &id, 0, NULL, exacts, ectxs, INSERT_ALL_VALUES, locu));
6198       else PetscCall(DMProjectFunctionLocal(dm, time, exacts, ectxs, INSERT_ALL_VALUES, locu));
6199     }
6200     if (u_t) {
6201       PetscCall(PetscArrayzero(exacts, Nf));
6202       PetscCall(PetscArrayzero(ectxs, Nf));
6203       for (f = 0; f < dsNf; ++f) PetscCall(PetscDSGetExactSolutionTimeDerivative(ds, fields[f], &exacts[fields[f]], &ectxs[fields[f]]));
6204       if (label) PetscCall(DMProjectFunctionLabelLocal(dm, time, label, 1, &id, 0, NULL, exacts, ectxs, INSERT_ALL_VALUES, locu_t));
6205       else PetscCall(DMProjectFunctionLocal(dm, time, exacts, ectxs, INSERT_ALL_VALUES, locu_t));
6206     }
6207     PetscCall(ISRestoreIndices(fieldIS, &fields));
6208   }
6209   if (u) {
6210     PetscCall(PetscObjectSetName((PetscObject)u, "Exact Solution"));
6211     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)u, "exact_"));
6212   }
6213   if (u_t) {
6214     PetscCall(PetscObjectSetName((PetscObject)u, "Exact Solution Time Derivative"));
6215     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)u_t, "exact_t_"));
6216   }
6217   PetscCall(PetscFree2(exacts, ectxs));
6218   if (u) {
6219     PetscCall(DMLocalToGlobalBegin(dm, locu, INSERT_ALL_VALUES, u));
6220     PetscCall(DMLocalToGlobalEnd(dm, locu, INSERT_ALL_VALUES, u));
6221     PetscCall(DMRestoreLocalVector(dm, &locu));
6222   }
6223   if (u_t) {
6224     PetscCall(DMLocalToGlobalBegin(dm, locu_t, INSERT_ALL_VALUES, u_t));
6225     PetscCall(DMLocalToGlobalEnd(dm, locu_t, INSERT_ALL_VALUES, u_t));
6226     PetscCall(DMRestoreLocalVector(dm, &locu_t));
6227   }
6228   PetscFunctionReturn(PETSC_SUCCESS);
6229 }
6230 
6231 static PetscErrorCode DMTransferDS_Internal(DM dm, DMLabel label, IS fields, PetscInt minDegree, PetscInt maxDegree, PetscDS ds, PetscDS dsIn)
6232 {
6233   PetscDS dsNew, dsInNew = NULL;
6234 
6235   PetscFunctionBegin;
6236   PetscCall(PetscDSCreate(PetscObjectComm((PetscObject)ds), &dsNew));
6237   PetscCall(PetscDSCopy(ds, minDegree, maxDegree, dm, dsNew));
6238   if (dsIn) {
6239     PetscCall(PetscDSCreate(PetscObjectComm((PetscObject)dsIn), &dsInNew));
6240     PetscCall(PetscDSCopy(dsIn, minDegree, maxDegree, dm, dsInNew));
6241   }
6242   PetscCall(DMSetRegionDS(dm, label, fields, dsNew, dsInNew));
6243   PetscCall(PetscDSDestroy(&dsNew));
6244   PetscCall(PetscDSDestroy(&dsInNew));
6245   PetscFunctionReturn(PETSC_SUCCESS);
6246 }
6247 
6248 /*@
6249   DMCopyDS - Copy the discrete systems for the `DM` into another `DM`
6250 
6251   Collective
6252 
6253   Input Parameters:
6254 + dm        - The `DM`
6255 . minDegree - Minimum degree for a discretization, or `PETSC_DETERMINE` for no limit
6256 - maxDegree - Maximum degree for a discretization, or `PETSC_DETERMINE` for no limit
6257 
6258   Output Parameter:
6259 . newdm - The `DM`
6260 
6261   Level: advanced
6262 
6263 .seealso: [](ch_dmbase), `DM`, `DMCopyFields()`, `DMAddField()`, `DMGetDS()`, `DMGetCellDS()`, `DMGetRegionDS()`, `DMSetRegionDS()`
6264 @*/
6265 PetscErrorCode DMCopyDS(DM dm, PetscInt minDegree, PetscInt maxDegree, DM newdm)
6266 {
6267   PetscInt Nds, s;
6268 
6269   PetscFunctionBegin;
6270   if (dm == newdm) PetscFunctionReturn(PETSC_SUCCESS);
6271   PetscCall(DMGetNumDS(dm, &Nds));
6272   PetscCall(DMClearDS(newdm));
6273   for (s = 0; s < Nds; ++s) {
6274     DMLabel  label;
6275     IS       fields;
6276     PetscDS  ds, dsIn, newds;
6277     PetscInt Nbd, bd;
6278 
6279     PetscCall(DMGetRegionNumDS(dm, s, &label, &fields, &ds, &dsIn));
6280     /* TODO: We need to change all keys from labels in the old DM to labels in the new DM */
6281     PetscCall(DMTransferDS_Internal(newdm, label, fields, minDegree, maxDegree, ds, dsIn));
6282     /* Complete new labels in the new DS */
6283     PetscCall(DMGetRegionDS(newdm, label, NULL, &newds, NULL));
6284     PetscCall(PetscDSGetNumBoundary(newds, &Nbd));
6285     for (bd = 0; bd < Nbd; ++bd) {
6286       PetscWeakForm wf;
6287       DMLabel       label;
6288       PetscInt      field;
6289 
6290       PetscCall(PetscDSGetBoundary(newds, bd, &wf, NULL, NULL, &label, NULL, NULL, &field, NULL, NULL, NULL, NULL, NULL));
6291       PetscCall(PetscWeakFormReplaceLabel(wf, label));
6292     }
6293   }
6294   PetscCall(DMCompleteBCLabels_Internal(newdm));
6295   PetscFunctionReturn(PETSC_SUCCESS);
6296 }
6297 
6298 /*@
6299   DMCopyDisc - Copy the fields and discrete systems for the `DM` into another `DM`
6300 
6301   Collective
6302 
6303   Input Parameter:
6304 . dm - The `DM`
6305 
6306   Output Parameter:
6307 . newdm - The `DM`
6308 
6309   Level: advanced
6310 
6311   Developer Note:
6312   Really ugly name, nothing in PETSc is called a `Disc` plus it is an ugly abbreviation
6313 
6314 .seealso: [](ch_dmbase), `DM`, `DMCopyFields()`, `DMCopyDS()`
6315 @*/
6316 PetscErrorCode DMCopyDisc(DM dm, DM newdm)
6317 {
6318   PetscFunctionBegin;
6319   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, newdm));
6320   PetscCall(DMCopyDS(dm, PETSC_DETERMINE, PETSC_DETERMINE, newdm));
6321   PetscFunctionReturn(PETSC_SUCCESS);
6322 }
6323 
6324 /*@
6325   DMGetDimension - Return the topological dimension of the `DM`
6326 
6327   Not Collective
6328 
6329   Input Parameter:
6330 . dm - The `DM`
6331 
6332   Output Parameter:
6333 . dim - The topological dimension
6334 
6335   Level: beginner
6336 
6337 .seealso: [](ch_dmbase), `DM`, `DMSetDimension()`, `DMCreate()`
6338 @*/
6339 PetscErrorCode DMGetDimension(DM dm, PetscInt *dim)
6340 {
6341   PetscFunctionBegin;
6342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6343   PetscAssertPointer(dim, 2);
6344   *dim = dm->dim;
6345   PetscFunctionReturn(PETSC_SUCCESS);
6346 }
6347 
6348 /*@
6349   DMSetDimension - Set the topological dimension of the `DM`
6350 
6351   Collective
6352 
6353   Input Parameters:
6354 + dm  - The `DM`
6355 - dim - The topological dimension
6356 
6357   Level: beginner
6358 
6359 .seealso: [](ch_dmbase), `DM`, `DMGetDimension()`, `DMCreate()`
6360 @*/
6361 PetscErrorCode DMSetDimension(DM dm, PetscInt dim)
6362 {
6363   PetscDS  ds;
6364   PetscInt Nds, n;
6365 
6366   PetscFunctionBegin;
6367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6368   PetscValidLogicalCollectiveInt(dm, dim, 2);
6369   dm->dim = dim;
6370   if (dm->dim >= 0) {
6371     PetscCall(DMGetNumDS(dm, &Nds));
6372     for (n = 0; n < Nds; ++n) {
6373       PetscCall(DMGetRegionNumDS(dm, n, NULL, NULL, &ds, NULL));
6374       if (ds->dimEmbed < 0) PetscCall(PetscDSSetCoordinateDimension(ds, dim));
6375     }
6376   }
6377   PetscFunctionReturn(PETSC_SUCCESS);
6378 }
6379 
6380 /*@
6381   DMGetDimPoints - Get the half-open interval for all points of a given dimension
6382 
6383   Collective
6384 
6385   Input Parameters:
6386 + dm  - the `DM`
6387 - dim - the dimension
6388 
6389   Output Parameters:
6390 + pStart - The first point of the given dimension
6391 - pEnd   - The first point following points of the given dimension
6392 
6393   Level: intermediate
6394 
6395   Note:
6396   The points are vertices in the Hasse diagram encoding the topology. This is explained in
6397   https://arxiv.org/abs/0908.4427. If no points exist of this dimension in the storage scheme,
6398   then the interval is empty.
6399 
6400 .seealso: [](ch_dmbase), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
6401 @*/
6402 PetscErrorCode DMGetDimPoints(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
6403 {
6404   PetscInt d;
6405 
6406   PetscFunctionBegin;
6407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6408   PetscCall(DMGetDimension(dm, &d));
6409   PetscCheck((dim >= 0) && (dim <= d), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid dimension %" PetscInt_FMT, dim);
6410   PetscUseTypeMethod(dm, getdimpoints, dim, pStart, pEnd);
6411   PetscFunctionReturn(PETSC_SUCCESS);
6412 }
6413 
6414 /*@
6415   DMGetOutputDM - Retrieve the `DM` associated with the layout for output
6416 
6417   Collective
6418 
6419   Input Parameter:
6420 . dm - The original `DM`
6421 
6422   Output Parameter:
6423 . odm - The `DM` which provides the layout for output
6424 
6425   Level: intermediate
6426 
6427   Note:
6428   In some situations the vector obtained with `DMCreateGlobalVector()` excludes points for degrees of freedom that are associated with fixed (Dirichelet) boundary
6429   conditions since the algebraic solver does not solve for those variables. The output `DM` includes these excluded points and its global vector contains the
6430   locations for those dof so that they can be output to a file or other viewer along with the unconstrained dof.
6431 
6432 .seealso: [](ch_dmbase), `DM`, `VecView()`, `DMGetGlobalSection()`, `DMCreateGlobalVector()`, `PetscSectionHasConstraints()`, `DMSetGlobalSection()`
6433 @*/
6434 PetscErrorCode DMGetOutputDM(DM dm, DM *odm)
6435 {
6436   PetscSection section;
6437   IS           perm;
6438   PetscBool    hasConstraints, newDM, gnewDM;
6439   PetscInt     num_face_sfs = 0;
6440 
6441   PetscFunctionBegin;
6442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6443   PetscAssertPointer(odm, 2);
6444   PetscCall(DMGetLocalSection(dm, &section));
6445   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
6446   PetscCall(PetscSectionGetPermutation(section, &perm));
6447   PetscCall(DMPlexGetIsoperiodicFaceSF(dm, &num_face_sfs, NULL));
6448   newDM = hasConstraints || perm || (num_face_sfs > 0) ? PETSC_TRUE : PETSC_FALSE;
6449   PetscCallMPI(MPIU_Allreduce(&newDM, &gnewDM, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
6450   if (!gnewDM) {
6451     *odm = dm;
6452     PetscFunctionReturn(PETSC_SUCCESS);
6453   }
6454   if (!dm->dmBC) {
6455     PetscSection newSection, gsection;
6456     PetscSF      sf, sfNatural;
6457     PetscBool    usePerm = dm->ignorePermOutput ? PETSC_FALSE : PETSC_TRUE;
6458 
6459     PetscCall(DMClone(dm, &dm->dmBC));
6460     PetscCall(DMCopyDisc(dm, dm->dmBC));
6461     PetscCall(PetscSectionClone(section, &newSection));
6462     PetscCall(DMSetLocalSection(dm->dmBC, newSection));
6463     PetscCall(PetscSectionDestroy(&newSection));
6464     PetscCall(DMGetNaturalSF(dm, &sfNatural));
6465     PetscCall(DMSetNaturalSF(dm->dmBC, sfNatural));
6466     PetscCall(DMGetPointSF(dm->dmBC, &sf));
6467     PetscCall(PetscSectionCreateGlobalSection(section, sf, usePerm, PETSC_TRUE, PETSC_FALSE, &gsection));
6468     PetscCall(DMSetGlobalSection(dm->dmBC, gsection));
6469     PetscCall(PetscSectionDestroy(&gsection));
6470   }
6471   *odm = dm->dmBC;
6472   PetscFunctionReturn(PETSC_SUCCESS);
6473 }
6474 
6475 /*@
6476   DMGetOutputSequenceNumber - Retrieve the sequence number/value for output
6477 
6478   Input Parameter:
6479 . dm - The original `DM`
6480 
6481   Output Parameters:
6482 + num - The output sequence number
6483 - val - The output sequence value
6484 
6485   Level: intermediate
6486 
6487   Note:
6488   This is intended for output that should appear in sequence, for instance
6489   a set of timesteps in an `PETSCVIEWERHDF5` file, or a set of realizations of a stochastic system.
6490 
6491   Developer Note:
6492   The `DM` serves as a convenient place to store the current iteration value. The iteration is not
6493   not directly related to the `DM`.
6494 
6495 .seealso: [](ch_dmbase), `DM`, `VecView()`
6496 @*/
6497 PetscErrorCode DMGetOutputSequenceNumber(DM dm, PetscInt *num, PetscReal *val)
6498 {
6499   PetscFunctionBegin;
6500   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6501   if (num) {
6502     PetscAssertPointer(num, 2);
6503     *num = dm->outputSequenceNum;
6504   }
6505   if (val) {
6506     PetscAssertPointer(val, 3);
6507     *val = dm->outputSequenceVal;
6508   }
6509   PetscFunctionReturn(PETSC_SUCCESS);
6510 }
6511 
6512 /*@
6513   DMSetOutputSequenceNumber - Set the sequence number/value for output
6514 
6515   Input Parameters:
6516 + dm  - The original `DM`
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 .seealso: [](ch_dmbase), `DM`, `VecView()`
6527 @*/
6528 PetscErrorCode DMSetOutputSequenceNumber(DM dm, PetscInt num, PetscReal val)
6529 {
6530   PetscFunctionBegin;
6531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6532   dm->outputSequenceNum = num;
6533   dm->outputSequenceVal = val;
6534   PetscFunctionReturn(PETSC_SUCCESS);
6535 }
6536 
6537 /*@
6538   DMOutputSequenceLoad - Retrieve the sequence value from a `PetscViewer`
6539 
6540   Input Parameters:
6541 + dm     - The original `DM`
6542 . viewer - The `PetscViewer` to get it from
6543 . name   - The sequence name
6544 - num    - The output sequence number
6545 
6546   Output Parameter:
6547 . val - The output sequence value
6548 
6549   Level: intermediate
6550 
6551   Note:
6552   This is intended for output that should appear in sequence, for instance
6553   a set of timesteps in an `PETSCVIEWERHDF5` file, or a set of realizations of a stochastic system.
6554 
6555   Developer Note:
6556   It is unclear at the user API level why a `DM` is needed as input
6557 
6558 .seealso: [](ch_dmbase), `DM`, `DMGetOutputSequenceNumber()`, `DMSetOutputSequenceNumber()`, `VecView()`
6559 @*/
6560 PetscErrorCode DMOutputSequenceLoad(DM dm, PetscViewer viewer, const char name[], PetscInt num, PetscReal *val)
6561 {
6562   PetscBool ishdf5;
6563 
6564   PetscFunctionBegin;
6565   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6566   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
6567   PetscAssertPointer(name, 3);
6568   PetscAssertPointer(val, 5);
6569   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
6570   if (ishdf5) {
6571 #if defined(PETSC_HAVE_HDF5)
6572     PetscScalar value;
6573 
6574     PetscCall(DMSequenceLoad_HDF5_Internal(dm, name, num, &value, viewer));
6575     *val = PetscRealPart(value);
6576 #endif
6577   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid viewer; open viewer with PetscViewerHDF5Open()");
6578   PetscFunctionReturn(PETSC_SUCCESS);
6579 }
6580 
6581 /*@
6582   DMGetOutputSequenceLength - Retrieve the number of sequence values from a `PetscViewer`
6583 
6584   Input Parameters:
6585 + dm     - The original `DM`
6586 . viewer - The `PetscViewer` to get it from
6587 - name   - The sequence name
6588 
6589   Output Parameter:
6590 . len - The length of the output sequence
6591 
6592   Level: intermediate
6593 
6594   Note:
6595   This is intended for output that should appear in sequence, for instance
6596   a set of timesteps in an `PETSCVIEWERHDF5` file, or a set of realizations of a stochastic system.
6597 
6598   Developer Note:
6599   It is unclear at the user API level why a `DM` is needed as input
6600 
6601 .seealso: [](ch_dmbase), `DM`, `DMGetOutputSequenceNumber()`, `DMSetOutputSequenceNumber()`, `VecView()`
6602 @*/
6603 PetscErrorCode DMGetOutputSequenceLength(DM dm, PetscViewer viewer, const char name[], PetscInt *len)
6604 {
6605   PetscBool ishdf5;
6606 
6607   PetscFunctionBegin;
6608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6609   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
6610   PetscAssertPointer(name, 3);
6611   PetscAssertPointer(len, 4);
6612   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
6613   if (ishdf5) {
6614 #if defined(PETSC_HAVE_HDF5)
6615     PetscCall(DMSequenceGetLength_HDF5_Internal(dm, name, len, viewer));
6616 #endif
6617   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid viewer; open viewer with PetscViewerHDF5Open()");
6618   PetscFunctionReturn(PETSC_SUCCESS);
6619 }
6620 
6621 /*@
6622   DMGetUseNatural - Get the flag for creating a mapping to the natural order when a `DM` is (re)distributed in parallel
6623 
6624   Not Collective
6625 
6626   Input Parameter:
6627 . dm - The `DM`
6628 
6629   Output Parameter:
6630 . useNatural - `PETSC_TRUE` to build the mapping to a natural order during distribution
6631 
6632   Level: beginner
6633 
6634 .seealso: [](ch_dmbase), `DM`, `DMSetUseNatural()`, `DMCreate()`
6635 @*/
6636 PetscErrorCode DMGetUseNatural(DM dm, PetscBool *useNatural)
6637 {
6638   PetscFunctionBegin;
6639   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6640   PetscAssertPointer(useNatural, 2);
6641   *useNatural = dm->useNatural;
6642   PetscFunctionReturn(PETSC_SUCCESS);
6643 }
6644 
6645 /*@
6646   DMSetUseNatural - Set the flag for creating a mapping to the natural order when a `DM` is (re)distributed in parallel
6647 
6648   Collective
6649 
6650   Input Parameters:
6651 + dm         - The `DM`
6652 - useNatural - `PETSC_TRUE` to build the mapping to a natural order during distribution
6653 
6654   Level: beginner
6655 
6656   Note:
6657   This also causes the map to be build after `DMCreateSubDM()` and `DMCreateSuperDM()`
6658 
6659 .seealso: [](ch_dmbase), `DM`, `DMGetUseNatural()`, `DMCreate()`, `DMPlexDistribute()`, `DMCreateSubDM()`, `DMCreateSuperDM()`
6660 @*/
6661 PetscErrorCode DMSetUseNatural(DM dm, PetscBool useNatural)
6662 {
6663   PetscFunctionBegin;
6664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6665   PetscValidLogicalCollectiveBool(dm, useNatural, 2);
6666   dm->useNatural = useNatural;
6667   PetscFunctionReturn(PETSC_SUCCESS);
6668 }
6669 
6670 /*@
6671   DMCreateLabel - Create a label of the given name if it does not already exist in the `DM`
6672 
6673   Not Collective
6674 
6675   Input Parameters:
6676 + dm   - The `DM` object
6677 - name - The label name
6678 
6679   Level: intermediate
6680 
6681 .seealso: [](ch_dmbase), `DM`, `DMLabelCreate()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6682 @*/
6683 PetscErrorCode DMCreateLabel(DM dm, const char name[])
6684 {
6685   PetscBool flg;
6686   DMLabel   label;
6687 
6688   PetscFunctionBegin;
6689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6690   PetscAssertPointer(name, 2);
6691   PetscCall(DMHasLabel(dm, name, &flg));
6692   if (!flg) {
6693     PetscCall(DMLabelCreate(PETSC_COMM_SELF, name, &label));
6694     PetscCall(DMAddLabel(dm, label));
6695     PetscCall(DMLabelDestroy(&label));
6696   }
6697   PetscFunctionReturn(PETSC_SUCCESS);
6698 }
6699 
6700 /*@
6701   DMCreateLabelAtIndex - Create a label of the given name at the given index. If it already exists in the `DM`, move it to this index.
6702 
6703   Not Collective
6704 
6705   Input Parameters:
6706 + dm   - The `DM` object
6707 . l    - The index for the label
6708 - name - The label name
6709 
6710   Level: intermediate
6711 
6712 .seealso: [](ch_dmbase), `DM`, `DMCreateLabel()`, `DMLabelCreate()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6713 @*/
6714 PetscErrorCode DMCreateLabelAtIndex(DM dm, PetscInt l, const char name[])
6715 {
6716   DMLabelLink orig, prev = NULL;
6717   DMLabel     label;
6718   PetscInt    Nl, m;
6719   PetscBool   flg, match;
6720   const char *lname;
6721 
6722   PetscFunctionBegin;
6723   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6724   PetscAssertPointer(name, 3);
6725   PetscCall(DMHasLabel(dm, name, &flg));
6726   if (!flg) {
6727     PetscCall(DMLabelCreate(PETSC_COMM_SELF, name, &label));
6728     PetscCall(DMAddLabel(dm, label));
6729     PetscCall(DMLabelDestroy(&label));
6730   }
6731   PetscCall(DMGetNumLabels(dm, &Nl));
6732   PetscCheck(l < Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label index %" PetscInt_FMT " must be in [0, %" PetscInt_FMT ")", l, Nl);
6733   for (m = 0, orig = dm->labels; m < Nl; ++m, prev = orig, orig = orig->next) {
6734     PetscCall(PetscObjectGetName((PetscObject)orig->label, &lname));
6735     PetscCall(PetscStrcmp(name, lname, &match));
6736     if (match) break;
6737   }
6738   if (m == l) PetscFunctionReturn(PETSC_SUCCESS);
6739   if (!m) dm->labels = orig->next;
6740   else prev->next = orig->next;
6741   if (!l) {
6742     orig->next = dm->labels;
6743     dm->labels = orig;
6744   } else {
6745     for (m = 0, prev = dm->labels; m < l - 1; ++m, prev = prev->next);
6746     orig->next = prev->next;
6747     prev->next = orig;
6748   }
6749   PetscFunctionReturn(PETSC_SUCCESS);
6750 }
6751 
6752 /*@
6753   DMGetLabelValue - Get the value in a `DMLabel` for the given point, with -1 as the default
6754 
6755   Not Collective
6756 
6757   Input Parameters:
6758 + dm    - The `DM` object
6759 . name  - The label name
6760 - point - The mesh point
6761 
6762   Output Parameter:
6763 . value - The label value for this point, or -1 if the point is not in the label
6764 
6765   Level: beginner
6766 
6767 .seealso: [](ch_dmbase), `DM`, `DMLabelGetValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6768 @*/
6769 PetscErrorCode DMGetLabelValue(DM dm, const char name[], PetscInt point, PetscInt *value)
6770 {
6771   DMLabel label;
6772 
6773   PetscFunctionBegin;
6774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6775   PetscAssertPointer(name, 2);
6776   PetscCall(DMGetLabel(dm, name, &label));
6777   PetscCheck(label, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "No label named %s was found", name);
6778   PetscCall(DMLabelGetValue(label, point, value));
6779   PetscFunctionReturn(PETSC_SUCCESS);
6780 }
6781 
6782 /*@
6783   DMSetLabelValue - Add a point to a `DMLabel` with given value
6784 
6785   Not Collective
6786 
6787   Input Parameters:
6788 + dm    - The `DM` object
6789 . name  - The label name
6790 . point - The mesh point
6791 - value - The label value for this point
6792 
6793   Output Parameter:
6794 
6795   Level: beginner
6796 
6797 .seealso: [](ch_dmbase), `DM`, `DMLabelSetValue()`, `DMGetStratumIS()`, `DMClearLabelValue()`
6798 @*/
6799 PetscErrorCode DMSetLabelValue(DM dm, const char name[], PetscInt point, PetscInt value)
6800 {
6801   DMLabel label;
6802 
6803   PetscFunctionBegin;
6804   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6805   PetscAssertPointer(name, 2);
6806   PetscCall(DMGetLabel(dm, name, &label));
6807   if (!label) {
6808     PetscCall(DMCreateLabel(dm, name));
6809     PetscCall(DMGetLabel(dm, name, &label));
6810   }
6811   PetscCall(DMLabelSetValue(label, point, value));
6812   PetscFunctionReturn(PETSC_SUCCESS);
6813 }
6814 
6815 /*@
6816   DMClearLabelValue - Remove a point from a `DMLabel` with given value
6817 
6818   Not Collective
6819 
6820   Input Parameters:
6821 + dm    - The `DM` object
6822 . name  - The label name
6823 . point - The mesh point
6824 - value - The label value for this point
6825 
6826   Level: beginner
6827 
6828 .seealso: [](ch_dmbase), `DM`, `DMLabelClearValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
6829 @*/
6830 PetscErrorCode DMClearLabelValue(DM dm, const char name[], PetscInt point, PetscInt value)
6831 {
6832   DMLabel label;
6833 
6834   PetscFunctionBegin;
6835   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6836   PetscAssertPointer(name, 2);
6837   PetscCall(DMGetLabel(dm, name, &label));
6838   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
6839   PetscCall(DMLabelClearValue(label, point, value));
6840   PetscFunctionReturn(PETSC_SUCCESS);
6841 }
6842 
6843 /*@
6844   DMGetLabelSize - Get the value of `DMLabelGetNumValues()` of a `DMLabel` in the `DM`
6845 
6846   Not Collective
6847 
6848   Input Parameters:
6849 + dm   - The `DM` object
6850 - name - The label name
6851 
6852   Output Parameter:
6853 . size - The number of different integer ids, or 0 if the label does not exist
6854 
6855   Level: beginner
6856 
6857   Developer Note:
6858   This should be renamed to something like `DMGetLabelNumValues()` or removed.
6859 
6860 .seealso: [](ch_dmbase), `DM`, `DMLabelGetNumValues()`, `DMSetLabelValue()`, `DMGetLabel()`
6861 @*/
6862 PetscErrorCode DMGetLabelSize(DM dm, const char name[], PetscInt *size)
6863 {
6864   DMLabel label;
6865 
6866   PetscFunctionBegin;
6867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6868   PetscAssertPointer(name, 2);
6869   PetscAssertPointer(size, 3);
6870   PetscCall(DMGetLabel(dm, name, &label));
6871   *size = 0;
6872   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
6873   PetscCall(DMLabelGetNumValues(label, size));
6874   PetscFunctionReturn(PETSC_SUCCESS);
6875 }
6876 
6877 /*@
6878   DMGetLabelIdIS - Get the `DMLabelGetValueIS()` from a `DMLabel` in the `DM`
6879 
6880   Not Collective
6881 
6882   Input Parameters:
6883 + dm   - The `DM` object
6884 - name - The label name
6885 
6886   Output Parameter:
6887 . ids - The integer ids, or `NULL` if the label does not exist
6888 
6889   Level: beginner
6890 
6891 .seealso: [](ch_dmbase), `DM`, `DMLabelGetValueIS()`, `DMGetLabelSize()`
6892 @*/
6893 PetscErrorCode DMGetLabelIdIS(DM dm, const char name[], IS *ids)
6894 {
6895   DMLabel label;
6896 
6897   PetscFunctionBegin;
6898   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6899   PetscAssertPointer(name, 2);
6900   PetscAssertPointer(ids, 3);
6901   PetscCall(DMGetLabel(dm, name, &label));
6902   *ids = NULL;
6903   if (label) {
6904     PetscCall(DMLabelGetValueIS(label, ids));
6905   } else {
6906     /* returning an empty IS */
6907     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 0, NULL, PETSC_USE_POINTER, ids));
6908   }
6909   PetscFunctionReturn(PETSC_SUCCESS);
6910 }
6911 
6912 /*@
6913   DMGetStratumSize - Get the number of points in a label stratum
6914 
6915   Not Collective
6916 
6917   Input Parameters:
6918 + dm    - The `DM` object
6919 . name  - The label name of the stratum
6920 - value - The stratum value
6921 
6922   Output Parameter:
6923 . size - The number of points, also called the stratum size
6924 
6925   Level: beginner
6926 
6927 .seealso: [](ch_dmbase), `DM`, `DMLabelGetStratumSize()`, `DMGetLabelSize()`, `DMGetLabelIds()`
6928 @*/
6929 PetscErrorCode DMGetStratumSize(DM dm, const char name[], PetscInt value, PetscInt *size)
6930 {
6931   DMLabel label;
6932 
6933   PetscFunctionBegin;
6934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6935   PetscAssertPointer(name, 2);
6936   PetscAssertPointer(size, 4);
6937   PetscCall(DMGetLabel(dm, name, &label));
6938   *size = 0;
6939   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
6940   PetscCall(DMLabelGetStratumSize(label, value, size));
6941   PetscFunctionReturn(PETSC_SUCCESS);
6942 }
6943 
6944 /*@
6945   DMGetStratumIS - Get the points in a label stratum
6946 
6947   Not Collective
6948 
6949   Input Parameters:
6950 + dm    - The `DM` object
6951 . name  - The label name
6952 - value - The stratum value
6953 
6954   Output Parameter:
6955 . points - The stratum points, or `NULL` if the label does not exist or does not have that value
6956 
6957   Level: beginner
6958 
6959 .seealso: [](ch_dmbase), `DM`, `DMLabelGetStratumIS()`, `DMGetStratumSize()`
6960 @*/
6961 PetscErrorCode DMGetStratumIS(DM dm, const char name[], PetscInt value, IS *points)
6962 {
6963   DMLabel label;
6964 
6965   PetscFunctionBegin;
6966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6967   PetscAssertPointer(name, 2);
6968   PetscAssertPointer(points, 4);
6969   PetscCall(DMGetLabel(dm, name, &label));
6970   *points = NULL;
6971   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
6972   PetscCall(DMLabelGetStratumIS(label, value, points));
6973   PetscFunctionReturn(PETSC_SUCCESS);
6974 }
6975 
6976 /*@
6977   DMSetStratumIS - Set the points in a label stratum
6978 
6979   Not Collective
6980 
6981   Input Parameters:
6982 + dm     - The `DM` object
6983 . name   - The label name
6984 . value  - The stratum value
6985 - points - The stratum points
6986 
6987   Level: beginner
6988 
6989 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMClearLabelStratum()`, `DMLabelClearStratum()`, `DMLabelSetStratumIS()`, `DMGetStratumSize()`
6990 @*/
6991 PetscErrorCode DMSetStratumIS(DM dm, const char name[], PetscInt value, IS points)
6992 {
6993   DMLabel label;
6994 
6995   PetscFunctionBegin;
6996   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6997   PetscAssertPointer(name, 2);
6998   PetscValidHeaderSpecific(points, IS_CLASSID, 4);
6999   PetscCall(DMGetLabel(dm, name, &label));
7000   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
7001   PetscCall(DMLabelSetStratumIS(label, value, points));
7002   PetscFunctionReturn(PETSC_SUCCESS);
7003 }
7004 
7005 /*@
7006   DMClearLabelStratum - Remove all points from a stratum from a `DMLabel`
7007 
7008   Not Collective
7009 
7010   Input Parameters:
7011 + dm    - The `DM` object
7012 . name  - The label name
7013 - value - The label value for this point
7014 
7015   Output Parameter:
7016 
7017   Level: beginner
7018 
7019 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMLabelClearStratum()`, `DMSetLabelValue()`, `DMGetStratumIS()`, `DMClearLabelValue()`
7020 @*/
7021 PetscErrorCode DMClearLabelStratum(DM dm, const char name[], PetscInt value)
7022 {
7023   DMLabel label;
7024 
7025   PetscFunctionBegin;
7026   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7027   PetscAssertPointer(name, 2);
7028   PetscCall(DMGetLabel(dm, name, &label));
7029   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
7030   PetscCall(DMLabelClearStratum(label, value));
7031   PetscFunctionReturn(PETSC_SUCCESS);
7032 }
7033 
7034 /*@
7035   DMGetNumLabels - Return the number of labels defined by on the `DM`
7036 
7037   Not Collective
7038 
7039   Input Parameter:
7040 . dm - The `DM` object
7041 
7042   Output Parameter:
7043 . numLabels - the number of Labels
7044 
7045   Level: intermediate
7046 
7047 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetLabelByNum()`, `DMGetLabelName()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7048 @*/
7049 PetscErrorCode DMGetNumLabels(DM dm, PetscInt *numLabels)
7050 {
7051   DMLabelLink next = dm->labels;
7052   PetscInt    n    = 0;
7053 
7054   PetscFunctionBegin;
7055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7056   PetscAssertPointer(numLabels, 2);
7057   while (next) {
7058     ++n;
7059     next = next->next;
7060   }
7061   *numLabels = n;
7062   PetscFunctionReturn(PETSC_SUCCESS);
7063 }
7064 
7065 /*@
7066   DMGetLabelName - Return the name of nth label
7067 
7068   Not Collective
7069 
7070   Input Parameters:
7071 + dm - The `DM` object
7072 - n  - the label number
7073 
7074   Output Parameter:
7075 . name - the label name
7076 
7077   Level: intermediate
7078 
7079   Developer Note:
7080   Some of the functions that appropriate on labels using their number have the suffix ByNum, others do not.
7081 
7082 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetLabelByNum()`, `DMGetLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7083 @*/
7084 PetscErrorCode DMGetLabelName(DM dm, PetscInt n, const char *name[])
7085 {
7086   DMLabelLink next = dm->labels;
7087   PetscInt    l    = 0;
7088 
7089   PetscFunctionBegin;
7090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7091   PetscAssertPointer(name, 3);
7092   while (next) {
7093     if (l == n) {
7094       PetscCall(PetscObjectGetName((PetscObject)next->label, name));
7095       PetscFunctionReturn(PETSC_SUCCESS);
7096     }
7097     ++l;
7098     next = next->next;
7099   }
7100   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label %" PetscInt_FMT " does not exist in this DM", n);
7101 }
7102 
7103 /*@
7104   DMHasLabel - Determine whether the `DM` has a label of a given name
7105 
7106   Not Collective
7107 
7108   Input Parameters:
7109 + dm   - The `DM` object
7110 - name - The label name
7111 
7112   Output Parameter:
7113 . hasLabel - `PETSC_TRUE` if the label is present
7114 
7115   Level: intermediate
7116 
7117 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetLabel()`, `DMGetLabelByNum()`, `DMCreateLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7118 @*/
7119 PetscErrorCode DMHasLabel(DM dm, const char name[], PetscBool *hasLabel)
7120 {
7121   DMLabelLink next = dm->labels;
7122   const char *lname;
7123 
7124   PetscFunctionBegin;
7125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7126   PetscAssertPointer(name, 2);
7127   PetscAssertPointer(hasLabel, 3);
7128   *hasLabel = PETSC_FALSE;
7129   while (next) {
7130     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7131     PetscCall(PetscStrcmp(name, lname, hasLabel));
7132     if (*hasLabel) break;
7133     next = next->next;
7134   }
7135   PetscFunctionReturn(PETSC_SUCCESS);
7136 }
7137 
7138 // PetscClangLinter pragma ignore: -fdoc-section-header-unknown
7139 /*@
7140   DMGetLabel - Return the label of a given name, or `NULL`, from a `DM`
7141 
7142   Not Collective
7143 
7144   Input Parameters:
7145 + dm   - The `DM` object
7146 - name - The label name
7147 
7148   Output Parameter:
7149 . label - The `DMLabel`, or `NULL` if the label is absent
7150 
7151   Default labels in a `DMPLEX`:
7152 + "depth"       - Holds the depth (co-dimension) of each mesh point
7153 . "celltype"    - Holds the topological type of each cell
7154 . "ghost"       - If the DM is distributed with overlap, this marks the cells and faces in the overlap
7155 . "Cell Sets"   - Mirrors the cell sets defined by GMsh and ExodusII
7156 . "Face Sets"   - Mirrors the face sets defined by GMsh and ExodusII
7157 - "Vertex Sets" - Mirrors the vertex sets defined by GMsh
7158 
7159   Level: intermediate
7160 
7161 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMHasLabel()`, `DMGetLabelByNum()`, `DMAddLabel()`, `DMCreateLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetCellType()`
7162 @*/
7163 PetscErrorCode DMGetLabel(DM dm, const char name[], DMLabel *label)
7164 {
7165   DMLabelLink next = dm->labels;
7166   PetscBool   hasLabel;
7167   const char *lname;
7168 
7169   PetscFunctionBegin;
7170   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7171   PetscAssertPointer(name, 2);
7172   PetscAssertPointer(label, 3);
7173   *label = NULL;
7174   while (next) {
7175     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7176     PetscCall(PetscStrcmp(name, lname, &hasLabel));
7177     if (hasLabel) {
7178       *label = next->label;
7179       break;
7180     }
7181     next = next->next;
7182   }
7183   PetscFunctionReturn(PETSC_SUCCESS);
7184 }
7185 
7186 /*@
7187   DMGetLabelByNum - Return the nth label on a `DM`
7188 
7189   Not Collective
7190 
7191   Input Parameters:
7192 + dm - The `DM` object
7193 - n  - the label number
7194 
7195   Output Parameter:
7196 . label - the label
7197 
7198   Level: intermediate
7199 
7200 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMAddLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7201 @*/
7202 PetscErrorCode DMGetLabelByNum(DM dm, PetscInt n, DMLabel *label)
7203 {
7204   DMLabelLink next = dm->labels;
7205   PetscInt    l    = 0;
7206 
7207   PetscFunctionBegin;
7208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7209   PetscAssertPointer(label, 3);
7210   while (next) {
7211     if (l == n) {
7212       *label = next->label;
7213       PetscFunctionReturn(PETSC_SUCCESS);
7214     }
7215     ++l;
7216     next = next->next;
7217   }
7218   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label %" PetscInt_FMT " does not exist in this DM", n);
7219 }
7220 
7221 /*@
7222   DMAddLabel - Add the label to this `DM`
7223 
7224   Not Collective
7225 
7226   Input Parameters:
7227 + dm    - The `DM` object
7228 - label - The `DMLabel`
7229 
7230   Level: developer
7231 
7232 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7233 @*/
7234 PetscErrorCode DMAddLabel(DM dm, DMLabel label)
7235 {
7236   DMLabelLink l, *p, tmpLabel;
7237   PetscBool   hasLabel;
7238   const char *lname;
7239   PetscBool   flg;
7240 
7241   PetscFunctionBegin;
7242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7243   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
7244   PetscCall(DMHasLabel(dm, lname, &hasLabel));
7245   PetscCheck(!hasLabel, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Label %s already exists in this DM", lname);
7246   PetscCall(PetscCalloc1(1, &tmpLabel));
7247   tmpLabel->label  = label;
7248   tmpLabel->output = PETSC_TRUE;
7249   for (p = &dm->labels; (l = *p); p = &l->next) { }
7250   *p = tmpLabel;
7251   PetscCall(PetscObjectReference((PetscObject)label));
7252   PetscCall(PetscStrcmp(lname, "depth", &flg));
7253   if (flg) dm->depthLabel = label;
7254   PetscCall(PetscStrcmp(lname, "celltype", &flg));
7255   if (flg) dm->celltypeLabel = label;
7256   PetscFunctionReturn(PETSC_SUCCESS);
7257 }
7258 
7259 // PetscClangLinter pragma ignore: -fdoc-section-header-unknown
7260 /*@
7261   DMSetLabel - Replaces the label of a given name, or ignores it if the name is not present
7262 
7263   Not Collective
7264 
7265   Input Parameters:
7266 + dm    - The `DM` object
7267 - label - The `DMLabel`, having the same name, to substitute
7268 
7269   Default labels in a `DMPLEX`:
7270 + "depth"       - Holds the depth (co-dimension) of each mesh point
7271 . "celltype"    - Holds the topological type of each cell
7272 . "ghost"       - If the DM is distributed with overlap, this marks the cells and faces in the overlap
7273 . "Cell Sets"   - Mirrors the cell sets defined by GMsh and ExodusII
7274 . "Face Sets"   - Mirrors the face sets defined by GMsh and ExodusII
7275 - "Vertex Sets" - Mirrors the vertex sets defined by GMsh
7276 
7277   Level: intermediate
7278 
7279 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetCellType()`
7280 @*/
7281 PetscErrorCode DMSetLabel(DM dm, DMLabel label)
7282 {
7283   DMLabelLink next = dm->labels;
7284   PetscBool   hasLabel, flg;
7285   const char *name, *lname;
7286 
7287   PetscFunctionBegin;
7288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7289   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
7290   PetscCall(PetscObjectGetName((PetscObject)label, &name));
7291   while (next) {
7292     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7293     PetscCall(PetscStrcmp(name, lname, &hasLabel));
7294     if (hasLabel) {
7295       PetscCall(PetscObjectReference((PetscObject)label));
7296       PetscCall(PetscStrcmp(lname, "depth", &flg));
7297       if (flg) dm->depthLabel = label;
7298       PetscCall(PetscStrcmp(lname, "celltype", &flg));
7299       if (flg) dm->celltypeLabel = label;
7300       PetscCall(DMLabelDestroy(&next->label));
7301       next->label = label;
7302       break;
7303     }
7304     next = next->next;
7305   }
7306   PetscFunctionReturn(PETSC_SUCCESS);
7307 }
7308 
7309 /*@
7310   DMRemoveLabel - Remove the label given by name from this `DM`
7311 
7312   Not Collective
7313 
7314   Input Parameters:
7315 + dm   - The `DM` object
7316 - name - The label name
7317 
7318   Output Parameter:
7319 . label - The `DMLabel`, or `NULL` if the label is absent. Pass in `NULL` to call `DMLabelDestroy()` on the label, otherwise the
7320           caller is responsible for calling `DMLabelDestroy()`.
7321 
7322   Level: developer
7323 
7324 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMLabelDestroy()`, `DMRemoveLabelBySelf()`
7325 @*/
7326 PetscErrorCode DMRemoveLabel(DM dm, const char name[], DMLabel *label)
7327 {
7328   DMLabelLink link, *pnext;
7329   PetscBool   hasLabel;
7330   const char *lname;
7331 
7332   PetscFunctionBegin;
7333   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7334   PetscAssertPointer(name, 2);
7335   if (label) {
7336     PetscAssertPointer(label, 3);
7337     *label = NULL;
7338   }
7339   for (pnext = &dm->labels; (link = *pnext); pnext = &link->next) {
7340     PetscCall(PetscObjectGetName((PetscObject)link->label, &lname));
7341     PetscCall(PetscStrcmp(name, lname, &hasLabel));
7342     if (hasLabel) {
7343       *pnext = link->next; /* Remove from list */
7344       PetscCall(PetscStrcmp(name, "depth", &hasLabel));
7345       if (hasLabel) dm->depthLabel = NULL;
7346       PetscCall(PetscStrcmp(name, "celltype", &hasLabel));
7347       if (hasLabel) dm->celltypeLabel = NULL;
7348       if (label) *label = link->label;
7349       else PetscCall(DMLabelDestroy(&link->label));
7350       PetscCall(PetscFree(link));
7351       break;
7352     }
7353   }
7354   PetscFunctionReturn(PETSC_SUCCESS);
7355 }
7356 
7357 /*@
7358   DMRemoveLabelBySelf - Remove the label from this `DM`
7359 
7360   Not Collective
7361 
7362   Input Parameters:
7363 + dm           - The `DM` object
7364 . label        - The `DMLabel` to be removed from the `DM`
7365 - failNotFound - Should it fail if the label is not found in the `DM`?
7366 
7367   Level: developer
7368 
7369   Note:
7370   Only exactly the same instance is removed if found, name match is ignored.
7371   If the `DM` has an exclusive reference to the label, the label gets destroyed and
7372   *label nullified.
7373 
7374 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabel()` `DMGetLabelValue()`, `DMSetLabelValue()`, `DMLabelDestroy()`, `DMRemoveLabel()`
7375 @*/
7376 PetscErrorCode DMRemoveLabelBySelf(DM dm, DMLabel *label, PetscBool failNotFound)
7377 {
7378   DMLabelLink link, *pnext;
7379   PetscBool   hasLabel = PETSC_FALSE;
7380 
7381   PetscFunctionBegin;
7382   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7383   PetscAssertPointer(label, 2);
7384   if (!*label && !failNotFound) PetscFunctionReturn(PETSC_SUCCESS);
7385   PetscValidHeaderSpecific(*label, DMLABEL_CLASSID, 2);
7386   PetscValidLogicalCollectiveBool(dm, failNotFound, 3);
7387   for (pnext = &dm->labels; (link = *pnext); pnext = &link->next) {
7388     if (*label == link->label) {
7389       hasLabel = PETSC_TRUE;
7390       *pnext   = link->next; /* Remove from list */
7391       if (*label == dm->depthLabel) dm->depthLabel = NULL;
7392       if (*label == dm->celltypeLabel) dm->celltypeLabel = NULL;
7393       if (((PetscObject)link->label)->refct < 2) *label = NULL; /* nullify if exclusive reference */
7394       PetscCall(DMLabelDestroy(&link->label));
7395       PetscCall(PetscFree(link));
7396       break;
7397     }
7398   }
7399   PetscCheck(hasLabel || !failNotFound, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Given label not found in DM");
7400   PetscFunctionReturn(PETSC_SUCCESS);
7401 }
7402 
7403 /*@
7404   DMGetLabelOutput - Get the output flag for a given label
7405 
7406   Not Collective
7407 
7408   Input Parameters:
7409 + dm   - The `DM` object
7410 - name - The label name
7411 
7412   Output Parameter:
7413 . output - The flag for output
7414 
7415   Level: developer
7416 
7417 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMSetLabelOutput()`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7418 @*/
7419 PetscErrorCode DMGetLabelOutput(DM dm, const char name[], PetscBool *output)
7420 {
7421   DMLabelLink next = dm->labels;
7422   const char *lname;
7423 
7424   PetscFunctionBegin;
7425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7426   PetscAssertPointer(name, 2);
7427   PetscAssertPointer(output, 3);
7428   while (next) {
7429     PetscBool flg;
7430 
7431     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7432     PetscCall(PetscStrcmp(name, lname, &flg));
7433     if (flg) {
7434       *output = next->output;
7435       PetscFunctionReturn(PETSC_SUCCESS);
7436     }
7437     next = next->next;
7438   }
7439   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No label named %s was present in this dm", name);
7440 }
7441 
7442 /*@
7443   DMSetLabelOutput - Set if a given label should be saved to a `PetscViewer` in calls to `DMView()`
7444 
7445   Not Collective
7446 
7447   Input Parameters:
7448 + dm     - The `DM` object
7449 . name   - The label name
7450 - output - `PETSC_TRUE` to save the label to the viewer
7451 
7452   Level: developer
7453 
7454 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMGetOutputFlag()`, `DMGetLabelOutput()`, `DMCreateLabel()`, `DMHasLabel()`, `DMGetLabelValue()`, `DMSetLabelValue()`, `DMGetStratumIS()`
7455 @*/
7456 PetscErrorCode DMSetLabelOutput(DM dm, const char name[], PetscBool output)
7457 {
7458   DMLabelLink next = dm->labels;
7459   const char *lname;
7460 
7461   PetscFunctionBegin;
7462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7463   PetscAssertPointer(name, 2);
7464   while (next) {
7465     PetscBool flg;
7466 
7467     PetscCall(PetscObjectGetName((PetscObject)next->label, &lname));
7468     PetscCall(PetscStrcmp(name, lname, &flg));
7469     if (flg) {
7470       next->output = output;
7471       PetscFunctionReturn(PETSC_SUCCESS);
7472     }
7473     next = next->next;
7474   }
7475   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No label named %s was present in this dm", name);
7476 }
7477 
7478 /*@
7479   DMCopyLabels - Copy labels from one `DM` mesh to another `DM` with a superset of the points
7480 
7481   Collective
7482 
7483   Input Parameters:
7484 + dmA   - The `DM` object with initial labels
7485 . dmB   - The `DM` object to which labels are copied
7486 . mode  - Copy labels by pointers (`PETSC_OWN_POINTER`) or duplicate them (`PETSC_COPY_VALUES`)
7487 . all   - Copy all labels including "depth", "dim", and "celltype" (`PETSC_TRUE`) which are otherwise ignored (`PETSC_FALSE`)
7488 - emode - How to behave when a `DMLabel` in the source and destination `DM`s with the same name is encountered (see `DMCopyLabelsMode`)
7489 
7490   Level: intermediate
7491 
7492   Note:
7493   This is typically used when interpolating or otherwise adding to a mesh, or testing.
7494 
7495 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMAddLabel()`, `DMCopyLabelsMode`
7496 @*/
7497 PetscErrorCode DMCopyLabels(DM dmA, DM dmB, PetscCopyMode mode, PetscBool all, DMCopyLabelsMode emode)
7498 {
7499   DMLabel     label, labelNew, labelOld;
7500   const char *name;
7501   PetscBool   flg;
7502   DMLabelLink link;
7503 
7504   PetscFunctionBegin;
7505   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
7506   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
7507   PetscValidLogicalCollectiveEnum(dmA, mode, 3);
7508   PetscValidLogicalCollectiveBool(dmA, all, 4);
7509   PetscCheck(mode != PETSC_USE_POINTER, PetscObjectComm((PetscObject)dmA), PETSC_ERR_SUP, "PETSC_USE_POINTER not supported for objects");
7510   if (dmA == dmB) PetscFunctionReturn(PETSC_SUCCESS);
7511   for (link = dmA->labels; link; link = link->next) {
7512     label = link->label;
7513     PetscCall(PetscObjectGetName((PetscObject)label, &name));
7514     if (!all) {
7515       PetscCall(PetscStrcmp(name, "depth", &flg));
7516       if (flg) continue;
7517       PetscCall(PetscStrcmp(name, "dim", &flg));
7518       if (flg) continue;
7519       PetscCall(PetscStrcmp(name, "celltype", &flg));
7520       if (flg) continue;
7521     }
7522     PetscCall(DMGetLabel(dmB, name, &labelOld));
7523     if (labelOld) {
7524       switch (emode) {
7525       case DM_COPY_LABELS_KEEP:
7526         continue;
7527       case DM_COPY_LABELS_REPLACE:
7528         PetscCall(DMRemoveLabelBySelf(dmB, &labelOld, PETSC_TRUE));
7529         break;
7530       case DM_COPY_LABELS_FAIL:
7531         SETERRQ(PetscObjectComm((PetscObject)dmA), PETSC_ERR_ARG_OUTOFRANGE, "Label %s already exists in destination DM", name);
7532       default:
7533         SETERRQ(PetscObjectComm((PetscObject)dmA), PETSC_ERR_ARG_OUTOFRANGE, "Unhandled DMCopyLabelsMode %d", (int)emode);
7534       }
7535     }
7536     if (mode == PETSC_COPY_VALUES) {
7537       PetscCall(DMLabelDuplicate(label, &labelNew));
7538     } else {
7539       labelNew = label;
7540     }
7541     PetscCall(DMAddLabel(dmB, labelNew));
7542     if (mode == PETSC_COPY_VALUES) PetscCall(DMLabelDestroy(&labelNew));
7543   }
7544   PetscFunctionReturn(PETSC_SUCCESS);
7545 }
7546 
7547 /*@C
7548   DMCompareLabels - Compare labels between two `DM` objects
7549 
7550   Collective; No Fortran Support
7551 
7552   Input Parameters:
7553 + dm0 - First `DM` object
7554 - dm1 - Second `DM` object
7555 
7556   Output Parameters:
7557 + equal   - (Optional) Flag whether labels of dm0 and dm1 are the same
7558 - message - (Optional) Message describing the difference, or `NULL` if there is no difference
7559 
7560   Level: intermediate
7561 
7562   Notes:
7563   The output flag equal will be the same on all processes.
7564 
7565   If equal is passed as `NULL` and difference is found, an error is thrown on all processes.
7566 
7567   Make sure to pass equal is `NULL` on all processes or none of them.
7568 
7569   The output message is set independently on each rank.
7570 
7571   message must be freed with `PetscFree()`
7572 
7573   If message is passed as `NULL` and a difference is found, the difference description is printed to stderr in synchronized manner.
7574 
7575   Make sure to pass message as `NULL` on all processes or no processes.
7576 
7577   Labels are matched by name. If the number of labels and their names are equal,
7578   `DMLabelCompare()` is used to compare each pair of labels with the same name.
7579 
7580   Developer Note:
7581   Can automatically generate the Fortran stub because `message` must be freed with `PetscFree()`
7582 
7583 .seealso: [](ch_dmbase), `DM`, `DMLabel`, `DMAddLabel()`, `DMCopyLabelsMode`, `DMLabelCompare()`
7584 @*/
7585 PetscErrorCode DMCompareLabels(DM dm0, DM dm1, PetscBool *equal, char **message)
7586 {
7587   PetscInt    n, i;
7588   char        msg[PETSC_MAX_PATH_LEN] = "";
7589   PetscBool   eq;
7590   MPI_Comm    comm;
7591   PetscMPIInt rank;
7592 
7593   PetscFunctionBegin;
7594   PetscValidHeaderSpecific(dm0, DM_CLASSID, 1);
7595   PetscValidHeaderSpecific(dm1, DM_CLASSID, 2);
7596   PetscCheckSameComm(dm0, 1, dm1, 2);
7597   if (equal) PetscAssertPointer(equal, 3);
7598   if (message) PetscAssertPointer(message, 4);
7599   PetscCall(PetscObjectGetComm((PetscObject)dm0, &comm));
7600   PetscCallMPI(MPI_Comm_rank(comm, &rank));
7601   {
7602     PetscInt n1;
7603 
7604     PetscCall(DMGetNumLabels(dm0, &n));
7605     PetscCall(DMGetNumLabels(dm1, &n1));
7606     eq = (PetscBool)(n == n1);
7607     if (!eq) PetscCall(PetscSNPrintf(msg, sizeof(msg), "Number of labels in dm0 = %" PetscInt_FMT " != %" PetscInt_FMT " = Number of labels in dm1", n, n1));
7608     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &eq, 1, MPIU_BOOL, MPI_LAND, comm));
7609     if (!eq) goto finish;
7610   }
7611   for (i = 0; i < n; i++) {
7612     DMLabel     l0, l1;
7613     const char *name;
7614     char       *msgInner;
7615 
7616     /* Ignore label order */
7617     PetscCall(DMGetLabelByNum(dm0, i, &l0));
7618     PetscCall(PetscObjectGetName((PetscObject)l0, &name));
7619     PetscCall(DMGetLabel(dm1, name, &l1));
7620     if (!l1) {
7621       PetscCall(PetscSNPrintf(msg, sizeof(msg), "Label \"%s\" (#%" PetscInt_FMT " in dm0) not found in dm1", name, i));
7622       eq = PETSC_FALSE;
7623       break;
7624     }
7625     PetscCall(DMLabelCompare(comm, l0, l1, &eq, &msgInner));
7626     PetscCall(PetscStrncpy(msg, msgInner, sizeof(msg)));
7627     PetscCall(PetscFree(msgInner));
7628     if (!eq) break;
7629   }
7630   PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &eq, 1, MPIU_BOOL, MPI_LAND, comm));
7631 finish:
7632   /* If message output arg not set, print to stderr */
7633   if (message) {
7634     *message = NULL;
7635     if (msg[0]) PetscCall(PetscStrallocpy(msg, message));
7636   } else {
7637     if (msg[0]) PetscCall(PetscSynchronizedFPrintf(comm, PETSC_STDERR, "[%d] %s\n", rank, msg));
7638     PetscCall(PetscSynchronizedFlush(comm, PETSC_STDERR));
7639   }
7640   /* If same output arg not ser and labels are not equal, throw error */
7641   if (equal) *equal = eq;
7642   else PetscCheck(eq, comm, PETSC_ERR_ARG_INCOMP, "DMLabels are not the same in dm0 and dm1");
7643   PetscFunctionReturn(PETSC_SUCCESS);
7644 }
7645 
7646 PetscErrorCode DMSetLabelValue_Fast(DM dm, DMLabel *label, const char name[], PetscInt point, PetscInt value)
7647 {
7648   PetscFunctionBegin;
7649   PetscAssertPointer(label, 2);
7650   if (!*label) {
7651     PetscCall(DMCreateLabel(dm, name));
7652     PetscCall(DMGetLabel(dm, name, label));
7653   }
7654   PetscCall(DMLabelSetValue(*label, point, value));
7655   PetscFunctionReturn(PETSC_SUCCESS);
7656 }
7657 
7658 /*
7659   Many mesh programs, such as Triangle and TetGen, allow only a single label for each mesh point. Therefore, we would
7660   like to encode all label IDs using a single, universal label. We can do this by assigning an integer to every
7661   (label, id) pair in the DM.
7662 
7663   However, a mesh point can have multiple labels, so we must separate all these values. We will assign a bit range to
7664   each label.
7665 */
7666 PetscErrorCode DMUniversalLabelCreate(DM dm, DMUniversalLabel *universal)
7667 {
7668   DMUniversalLabel ul;
7669   PetscBool       *active;
7670   PetscInt         pStart, pEnd, p, Nl, l, m;
7671 
7672   PetscFunctionBegin;
7673   PetscCall(PetscMalloc1(1, &ul));
7674   PetscCall(DMLabelCreate(PETSC_COMM_SELF, "universal", &ul->label));
7675   PetscCall(DMGetNumLabels(dm, &Nl));
7676   PetscCall(PetscCalloc1(Nl, &active));
7677   ul->Nl = 0;
7678   for (l = 0; l < Nl; ++l) {
7679     PetscBool   isdepth, iscelltype;
7680     const char *name;
7681 
7682     PetscCall(DMGetLabelName(dm, l, &name));
7683     PetscCall(PetscStrncmp(name, "depth", 6, &isdepth));
7684     PetscCall(PetscStrncmp(name, "celltype", 9, &iscelltype));
7685     active[l] = !(isdepth || iscelltype) ? PETSC_TRUE : PETSC_FALSE;
7686     if (active[l]) ++ul->Nl;
7687   }
7688   PetscCall(PetscCalloc5(ul->Nl, &ul->names, ul->Nl, &ul->indices, ul->Nl + 1, &ul->offsets, ul->Nl + 1, &ul->bits, ul->Nl, &ul->masks));
7689   ul->Nv = 0;
7690   for (l = 0, m = 0; l < Nl; ++l) {
7691     DMLabel     label;
7692     PetscInt    nv;
7693     const char *name;
7694 
7695     if (!active[l]) continue;
7696     PetscCall(DMGetLabelName(dm, l, &name));
7697     PetscCall(DMGetLabelByNum(dm, l, &label));
7698     PetscCall(DMLabelGetNumValues(label, &nv));
7699     PetscCall(PetscStrallocpy(name, &ul->names[m]));
7700     ul->indices[m] = l;
7701     ul->Nv += nv;
7702     ul->offsets[m + 1] = nv;
7703     ul->bits[m + 1]    = PetscCeilReal(PetscLog2Real(nv + 1));
7704     ++m;
7705   }
7706   for (l = 1; l <= ul->Nl; ++l) {
7707     ul->offsets[l] = ul->offsets[l - 1] + ul->offsets[l];
7708     ul->bits[l]    = ul->bits[l - 1] + ul->bits[l];
7709   }
7710   for (l = 0; l < ul->Nl; ++l) {
7711     PetscInt b;
7712 
7713     ul->masks[l] = 0;
7714     for (b = ul->bits[l]; b < ul->bits[l + 1]; ++b) ul->masks[l] |= 1 << b;
7715   }
7716   PetscCall(PetscMalloc1(ul->Nv, &ul->values));
7717   for (l = 0, m = 0; l < Nl; ++l) {
7718     DMLabel         label;
7719     IS              valueIS;
7720     const PetscInt *varr;
7721     PetscInt        nv, v;
7722 
7723     if (!active[l]) continue;
7724     PetscCall(DMGetLabelByNum(dm, l, &label));
7725     PetscCall(DMLabelGetNumValues(label, &nv));
7726     PetscCall(DMLabelGetValueIS(label, &valueIS));
7727     PetscCall(ISGetIndices(valueIS, &varr));
7728     for (v = 0; v < nv; ++v) ul->values[ul->offsets[m] + v] = varr[v];
7729     PetscCall(ISRestoreIndices(valueIS, &varr));
7730     PetscCall(ISDestroy(&valueIS));
7731     PetscCall(PetscSortInt(nv, &ul->values[ul->offsets[m]]));
7732     ++m;
7733   }
7734   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
7735   for (p = pStart; p < pEnd; ++p) {
7736     PetscInt  uval   = 0;
7737     PetscBool marked = PETSC_FALSE;
7738 
7739     for (l = 0, m = 0; l < Nl; ++l) {
7740       DMLabel  label;
7741       PetscInt val, defval, loc, nv;
7742 
7743       if (!active[l]) continue;
7744       PetscCall(DMGetLabelByNum(dm, l, &label));
7745       PetscCall(DMLabelGetValue(label, p, &val));
7746       PetscCall(DMLabelGetDefaultValue(label, &defval));
7747       if (val == defval) {
7748         ++m;
7749         continue;
7750       }
7751       nv     = ul->offsets[m + 1] - ul->offsets[m];
7752       marked = PETSC_TRUE;
7753       PetscCall(PetscFindInt(val, nv, &ul->values[ul->offsets[m]], &loc));
7754       PetscCheck(loc >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Label value %" PetscInt_FMT " not found in compression array", val);
7755       uval += (loc + 1) << ul->bits[m];
7756       ++m;
7757     }
7758     if (marked) PetscCall(DMLabelSetValue(ul->label, p, uval));
7759   }
7760   PetscCall(PetscFree(active));
7761   *universal = ul;
7762   PetscFunctionReturn(PETSC_SUCCESS);
7763 }
7764 
7765 PetscErrorCode DMUniversalLabelDestroy(DMUniversalLabel *universal)
7766 {
7767   PetscInt l;
7768 
7769   PetscFunctionBegin;
7770   for (l = 0; l < (*universal)->Nl; ++l) PetscCall(PetscFree((*universal)->names[l]));
7771   PetscCall(DMLabelDestroy(&(*universal)->label));
7772   PetscCall(PetscFree5((*universal)->names, (*universal)->indices, (*universal)->offsets, (*universal)->bits, (*universal)->masks));
7773   PetscCall(PetscFree((*universal)->values));
7774   PetscCall(PetscFree(*universal));
7775   *universal = NULL;
7776   PetscFunctionReturn(PETSC_SUCCESS);
7777 }
7778 
7779 PetscErrorCode DMUniversalLabelGetLabel(DMUniversalLabel ul, DMLabel *ulabel)
7780 {
7781   PetscFunctionBegin;
7782   PetscAssertPointer(ulabel, 2);
7783   *ulabel = ul->label;
7784   PetscFunctionReturn(PETSC_SUCCESS);
7785 }
7786 
7787 PetscErrorCode DMUniversalLabelCreateLabels(DMUniversalLabel ul, PetscBool preserveOrder, DM dm)
7788 {
7789   PetscInt Nl = ul->Nl, l;
7790 
7791   PetscFunctionBegin;
7792   PetscValidHeaderSpecific(dm, DM_CLASSID, 3);
7793   for (l = 0; l < Nl; ++l) {
7794     if (preserveOrder) PetscCall(DMCreateLabelAtIndex(dm, ul->indices[l], ul->names[l]));
7795     else PetscCall(DMCreateLabel(dm, ul->names[l]));
7796   }
7797   if (preserveOrder) {
7798     for (l = 0; l < ul->Nl; ++l) {
7799       const char *name;
7800       PetscBool   match;
7801 
7802       PetscCall(DMGetLabelName(dm, ul->indices[l], &name));
7803       PetscCall(PetscStrcmp(name, ul->names[l], &match));
7804       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]);
7805     }
7806   }
7807   PetscFunctionReturn(PETSC_SUCCESS);
7808 }
7809 
7810 PetscErrorCode DMUniversalLabelSetLabelValue(DMUniversalLabel ul, DM dm, PetscBool useIndex, PetscInt p, PetscInt value)
7811 {
7812   PetscInt l;
7813 
7814   PetscFunctionBegin;
7815   for (l = 0; l < ul->Nl; ++l) {
7816     DMLabel  label;
7817     PetscInt lval = (value & ul->masks[l]) >> ul->bits[l];
7818 
7819     if (lval) {
7820       if (useIndex) PetscCall(DMGetLabelByNum(dm, ul->indices[l], &label));
7821       else PetscCall(DMGetLabel(dm, ul->names[l], &label));
7822       PetscCall(DMLabelSetValue(label, p, ul->values[ul->offsets[l] + lval - 1]));
7823     }
7824   }
7825   PetscFunctionReturn(PETSC_SUCCESS);
7826 }
7827 
7828 /*@
7829   DMGetCoarseDM - Get the coarse `DM`from which this `DM` was obtained by refinement
7830 
7831   Not Collective
7832 
7833   Input Parameter:
7834 . dm - The `DM` object
7835 
7836   Output Parameter:
7837 . cdm - The coarse `DM`
7838 
7839   Level: intermediate
7840 
7841 .seealso: [](ch_dmbase), `DM`, `DMSetCoarseDM()`, `DMCoarsen()`
7842 @*/
7843 PetscErrorCode DMGetCoarseDM(DM dm, DM *cdm)
7844 {
7845   PetscFunctionBegin;
7846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7847   PetscAssertPointer(cdm, 2);
7848   *cdm = dm->coarseMesh;
7849   PetscFunctionReturn(PETSC_SUCCESS);
7850 }
7851 
7852 /*@
7853   DMSetCoarseDM - Set the coarse `DM` from which this `DM` was obtained by refinement
7854 
7855   Input Parameters:
7856 + dm  - The `DM` object
7857 - cdm - The coarse `DM`
7858 
7859   Level: intermediate
7860 
7861   Note:
7862   Normally this is set automatically by `DMRefine()`
7863 
7864 .seealso: [](ch_dmbase), `DM`, `DMGetCoarseDM()`, `DMCoarsen()`, `DMSetRefine()`, `DMSetFineDM()`
7865 @*/
7866 PetscErrorCode DMSetCoarseDM(DM dm, DM cdm)
7867 {
7868   PetscFunctionBegin;
7869   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7870   if (cdm) PetscValidHeaderSpecific(cdm, DM_CLASSID, 2);
7871   if (dm == cdm) cdm = NULL;
7872   PetscCall(PetscObjectReference((PetscObject)cdm));
7873   PetscCall(DMDestroy(&dm->coarseMesh));
7874   dm->coarseMesh = cdm;
7875   PetscFunctionReturn(PETSC_SUCCESS);
7876 }
7877 
7878 /*@
7879   DMGetFineDM - Get the fine mesh from which this `DM` was obtained by coarsening
7880 
7881   Input Parameter:
7882 . dm - The `DM` object
7883 
7884   Output Parameter:
7885 . fdm - The fine `DM`
7886 
7887   Level: intermediate
7888 
7889 .seealso: [](ch_dmbase), `DM`, `DMSetFineDM()`, `DMCoarsen()`, `DMRefine()`
7890 @*/
7891 PetscErrorCode DMGetFineDM(DM dm, DM *fdm)
7892 {
7893   PetscFunctionBegin;
7894   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7895   PetscAssertPointer(fdm, 2);
7896   *fdm = dm->fineMesh;
7897   PetscFunctionReturn(PETSC_SUCCESS);
7898 }
7899 
7900 /*@
7901   DMSetFineDM - Set the fine mesh from which this was obtained by coarsening
7902 
7903   Input Parameters:
7904 + dm  - The `DM` object
7905 - fdm - The fine `DM`
7906 
7907   Level: developer
7908 
7909   Note:
7910   Normally this is set automatically by `DMCoarsen()`
7911 
7912 .seealso: [](ch_dmbase), `DM`, `DMGetFineDM()`, `DMCoarsen()`, `DMRefine()`
7913 @*/
7914 PetscErrorCode DMSetFineDM(DM dm, DM fdm)
7915 {
7916   PetscFunctionBegin;
7917   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7918   if (fdm) PetscValidHeaderSpecific(fdm, DM_CLASSID, 2);
7919   if (dm == fdm) fdm = NULL;
7920   PetscCall(PetscObjectReference((PetscObject)fdm));
7921   PetscCall(DMDestroy(&dm->fineMesh));
7922   dm->fineMesh = fdm;
7923   PetscFunctionReturn(PETSC_SUCCESS);
7924 }
7925 
7926 /*@C
7927   DMAddBoundary - Add a boundary condition to a model represented by a `DM`
7928 
7929   Collective
7930 
7931   Input Parameters:
7932 + dm       - The `DM`, with a `PetscDS` that matches the problem being constrained
7933 . type     - The type of condition, e.g. `DM_BC_ESSENTIAL_ANALYTIC`, `DM_BC_ESSENTIAL_FIELD` (Dirichlet), or `DM_BC_NATURAL` (Neumann)
7934 . name     - The BC name
7935 . label    - The label defining constrained points
7936 . Nv       - The number of `DMLabel` values for constrained points
7937 . values   - An array of values for constrained points
7938 . field    - The field to constrain
7939 . Nc       - The number of constrained field components (0 will constrain all fields)
7940 . comps    - An array of constrained component numbers
7941 . bcFunc   - A pointwise function giving boundary values
7942 . bcFunc_t - A pointwise function giving the time deriative of the boundary values, or NULL
7943 - ctx      - An optional user context for bcFunc
7944 
7945   Output Parameter:
7946 . bd - (Optional) Boundary number
7947 
7948   Options Database Keys:
7949 + -bc_<boundary name> <num>      - Overrides the boundary ids
7950 - -bc_<boundary name>_comp <num> - Overrides the boundary components
7951 
7952   Level: intermediate
7953 
7954   Notes:
7955   Both bcFunc and bcFunc_t will depend on the boundary condition type. If the type if `DM_BC_ESSENTIAL`, then the calling sequence is\:
7956 .vb
7957  void bcFunc(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar bcval[])
7958 .ve
7959 
7960   If the type is `DM_BC_ESSENTIAL_FIELD` or other _FIELD value, then the calling sequence is\:
7961 
7962 .vb
7963   void bcFunc(PetscInt dim, PetscInt Nf, PetscInt NfAux,
7964               const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
7965               const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
7966               PetscReal time, const PetscReal x[], PetscScalar bcval[])
7967 .ve
7968 + dim - the spatial dimension
7969 . Nf - the number of fields
7970 . uOff - the offset into u[] and u_t[] for each field
7971 . uOff_x - the offset into u_x[] for each field
7972 . u - each field evaluated at the current point
7973 . u_t - the time derivative of each field evaluated at the current point
7974 . u_x - the gradient of each field evaluated at the current point
7975 . aOff - the offset into a[] and a_t[] for each auxiliary field
7976 . aOff_x - the offset into a_x[] for each auxiliary field
7977 . a - each auxiliary field evaluated at the current point
7978 . a_t - the time derivative of each auxiliary field evaluated at the current point
7979 . a_x - the gradient of auxiliary each field evaluated at the current point
7980 . t - current time
7981 . x - coordinates of the current point
7982 . numConstants - number of constant parameters
7983 . constants - constant parameters
7984 - bcval - output values at the current point
7985 
7986 .seealso: [](ch_dmbase), `DM`, `DSGetBoundary()`, `PetscDSAddBoundary()`
7987 @*/
7988 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)
7989 {
7990   PetscDS ds;
7991 
7992   PetscFunctionBegin;
7993   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7994   PetscValidLogicalCollectiveEnum(dm, type, 2);
7995   PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 4);
7996   PetscValidLogicalCollectiveInt(dm, Nv, 5);
7997   PetscValidLogicalCollectiveInt(dm, field, 7);
7998   PetscValidLogicalCollectiveInt(dm, Nc, 8);
7999   PetscCheck(!dm->localSection, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Cannot add boundary to DM after creating local section");
8000   PetscCall(DMGetDS(dm, &ds));
8001   /* Complete label */
8002   if (label) {
8003     PetscObject  obj;
8004     PetscClassId id;
8005 
8006     PetscCall(DMGetField(dm, field, NULL, &obj));
8007     PetscCall(PetscObjectGetClassId(obj, &id));
8008     if (id == PETSCFE_CLASSID) {
8009       DM plex;
8010 
8011       PetscCall(DMConvert(dm, DMPLEX, &plex));
8012       if (plex) PetscCall(DMPlexLabelComplete(plex, label));
8013       PetscCall(DMDestroy(&plex));
8014     }
8015   }
8016   PetscCall(PetscDSAddBoundary(ds, type, name, label, Nv, values, field, Nc, comps, bcFunc, bcFunc_t, ctx, bd));
8017   PetscFunctionReturn(PETSC_SUCCESS);
8018 }
8019 
8020 /* TODO Remove this since now the structures are the same */
8021 static PetscErrorCode DMPopulateBoundary(DM dm)
8022 {
8023   PetscDS     ds;
8024   DMBoundary *lastnext;
8025   DSBoundary  dsbound;
8026 
8027   PetscFunctionBegin;
8028   PetscCall(DMGetDS(dm, &ds));
8029   dsbound = ds->boundary;
8030   if (dm->boundary) {
8031     DMBoundary next = dm->boundary;
8032 
8033     /* quick check to see if the PetscDS has changed */
8034     if (next->dsboundary == dsbound) PetscFunctionReturn(PETSC_SUCCESS);
8035     /* the PetscDS has changed: tear down and rebuild */
8036     while (next) {
8037       DMBoundary b = next;
8038 
8039       next = b->next;
8040       PetscCall(PetscFree(b));
8041     }
8042     dm->boundary = NULL;
8043   }
8044 
8045   lastnext = &dm->boundary;
8046   while (dsbound) {
8047     DMBoundary dmbound;
8048 
8049     PetscCall(PetscNew(&dmbound));
8050     dmbound->dsboundary = dsbound;
8051     dmbound->label      = dsbound->label;
8052     /* push on the back instead of the front so that it is in the same order as in the PetscDS */
8053     *lastnext = dmbound;
8054     lastnext  = &dmbound->next;
8055     dsbound   = dsbound->next;
8056   }
8057   PetscFunctionReturn(PETSC_SUCCESS);
8058 }
8059 
8060 /* TODO: missing manual page */
8061 PetscErrorCode DMIsBoundaryPoint(DM dm, PetscInt point, PetscBool *isBd)
8062 {
8063   DMBoundary b;
8064 
8065   PetscFunctionBegin;
8066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8067   PetscAssertPointer(isBd, 3);
8068   *isBd = PETSC_FALSE;
8069   PetscCall(DMPopulateBoundary(dm));
8070   b = dm->boundary;
8071   while (b && !*isBd) {
8072     DMLabel    label = b->label;
8073     DSBoundary dsb   = b->dsboundary;
8074     PetscInt   i;
8075 
8076     if (label) {
8077       for (i = 0; i < dsb->Nv && !*isBd; ++i) PetscCall(DMLabelStratumHasPoint(label, dsb->values[i], point, isBd));
8078     }
8079     b = b->next;
8080   }
8081   PetscFunctionReturn(PETSC_SUCCESS);
8082 }
8083 
8084 /*@C
8085   DMProjectFunction - This projects the given function into the function space provided by a `DM`, putting the coefficients in a global vector.
8086 
8087   Collective
8088 
8089   Input Parameters:
8090 + dm    - The `DM`
8091 . time  - The time
8092 . funcs - The coordinate functions to evaluate, one per field
8093 . ctxs  - Optional array of contexts to pass to each coordinate function.  ctxs itself may be null.
8094 - mode  - The insertion mode for values
8095 
8096   Output Parameter:
8097 . X - vector
8098 
8099   Calling sequence of `funcs`:
8100 + dim  - The spatial dimension
8101 . time - The time at which to sample
8102 . x    - The coordinates
8103 . Nc   - The number of components
8104 . u    - The output field values
8105 - ctx  - optional user-defined function context
8106 
8107   Level: developer
8108 
8109   Developer Notes:
8110   This API is specific to only particular usage of `DM`
8111 
8112   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8113 
8114 .seealso: [](ch_dmbase), `DM`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabel()`, `DMComputeL2Diff()`
8115 @*/
8116 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)
8117 {
8118   Vec localX;
8119 
8120   PetscFunctionBegin;
8121   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8122   PetscCall(PetscLogEventBegin(DM_ProjectFunction, dm, X, 0, 0));
8123   PetscCall(DMGetLocalVector(dm, &localX));
8124   PetscCall(VecSet(localX, 0.));
8125   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, mode, localX));
8126   PetscCall(DMLocalToGlobalBegin(dm, localX, mode, X));
8127   PetscCall(DMLocalToGlobalEnd(dm, localX, mode, X));
8128   PetscCall(DMRestoreLocalVector(dm, &localX));
8129   PetscCall(PetscLogEventEnd(DM_ProjectFunction, dm, X, 0, 0));
8130   PetscFunctionReturn(PETSC_SUCCESS);
8131 }
8132 
8133 /*@C
8134   DMProjectFunctionLocal - This projects the given function into the function space provided by a `DM`, putting the coefficients in a local vector.
8135 
8136   Not Collective
8137 
8138   Input Parameters:
8139 + dm    - The `DM`
8140 . time  - The time
8141 . funcs - The coordinate functions to evaluate, one per field
8142 . ctxs  - Optional array of contexts to pass to each coordinate function.  ctxs itself may be null.
8143 - mode  - The insertion mode for values
8144 
8145   Output Parameter:
8146 . localX - vector
8147 
8148   Calling sequence of `funcs`:
8149 + dim  - The spatial dimension
8150 . time - The current timestep
8151 . x    - The coordinates
8152 . Nc   - The number of components
8153 . u    - The output field values
8154 - ctx  - optional user-defined function context
8155 
8156   Level: developer
8157 
8158   Developer Notes:
8159   This API is specific to only particular usage of `DM`
8160 
8161   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8162 
8163 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMProjectFunctionLabel()`, `DMComputeL2Diff()`
8164 @*/
8165 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)
8166 {
8167   PetscFunctionBegin;
8168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8169   PetscValidHeaderSpecific(localX, VEC_CLASSID, 6);
8170   PetscUseTypeMethod(dm, projectfunctionlocal, time, funcs, ctxs, mode, localX);
8171   PetscFunctionReturn(PETSC_SUCCESS);
8172 }
8173 
8174 /*@C
8175   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.
8176 
8177   Collective
8178 
8179   Input Parameters:
8180 + dm     - The `DM`
8181 . time   - The time
8182 . numIds - The number of ids
8183 . ids    - The ids
8184 . Nc     - The number of components
8185 . comps  - The components
8186 . label  - The `DMLabel` selecting the portion of the mesh for projection
8187 . funcs  - The coordinate functions to evaluate, one per field
8188 . ctxs   - Optional array of contexts to pass to each coordinate function.  ctxs may be null.
8189 - mode   - The insertion mode for values
8190 
8191   Output Parameter:
8192 . X - vector
8193 
8194   Calling sequence of `funcs`:
8195 + dim  - The spatial dimension
8196 . time - The current timestep
8197 . x    - The coordinates
8198 . Nc   - The number of components
8199 . u    - The output field values
8200 - ctx  - optional user-defined function context
8201 
8202   Level: developer
8203 
8204   Developer Notes:
8205   This API is specific to only particular usage of `DM`
8206 
8207   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8208 
8209 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`, `DMComputeL2Diff()`
8210 @*/
8211 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)
8212 {
8213   Vec localX;
8214 
8215   PetscFunctionBegin;
8216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8217   PetscCall(DMGetLocalVector(dm, &localX));
8218   PetscCall(VecSet(localX, 0.));
8219   PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numIds, ids, Nc, comps, funcs, ctxs, mode, localX));
8220   PetscCall(DMLocalToGlobalBegin(dm, localX, mode, X));
8221   PetscCall(DMLocalToGlobalEnd(dm, localX, mode, X));
8222   PetscCall(DMRestoreLocalVector(dm, &localX));
8223   PetscFunctionReturn(PETSC_SUCCESS);
8224 }
8225 
8226 /*@C
8227   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.
8228 
8229   Not Collective
8230 
8231   Input Parameters:
8232 + dm     - The `DM`
8233 . time   - The time
8234 . label  - The `DMLabel` selecting the portion of the mesh for projection
8235 . numIds - The number of ids
8236 . ids    - The ids
8237 . Nc     - The number of components
8238 . comps  - The components
8239 . funcs  - The coordinate functions to evaluate, one per field
8240 . ctxs   - Optional array of contexts to pass to each coordinate function.  ctxs itself may be null.
8241 - mode   - The insertion mode for values
8242 
8243   Output Parameter:
8244 . localX - vector
8245 
8246   Calling sequence of `funcs`:
8247 + dim  - The spatial dimension
8248 . time - The current time
8249 . x    - The coordinates
8250 . Nc   - The number of components
8251 . u    - The output field values
8252 - ctx  - optional user-defined function context
8253 
8254   Level: developer
8255 
8256   Developer Notes:
8257   This API is specific to only particular usage of `DM`
8258 
8259   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8260 
8261 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabel()`, `DMComputeL2Diff()`
8262 @*/
8263 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)
8264 {
8265   PetscFunctionBegin;
8266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8267   PetscValidHeaderSpecific(localX, VEC_CLASSID, 11);
8268   PetscUseTypeMethod(dm, projectfunctionlabellocal, time, label, numIds, ids, Nc, comps, funcs, ctxs, mode, localX);
8269   PetscFunctionReturn(PETSC_SUCCESS);
8270 }
8271 
8272 /*@C
8273   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.
8274 
8275   Not Collective
8276 
8277   Input Parameters:
8278 + dm     - The `DM`
8279 . time   - The time
8280 . localU - The input field vector; may be `NULL` if projection is defined purely by coordinates
8281 . funcs  - The functions to evaluate, one per field
8282 - mode   - The insertion mode for values
8283 
8284   Output Parameter:
8285 . localX - The output vector
8286 
8287   Calling sequence of `funcs`:
8288 + dim          - The spatial dimension
8289 . Nf           - The number of input fields
8290 . NfAux        - The number of input auxiliary fields
8291 . uOff         - The offset of each field in u[]
8292 . uOff_x       - The offset of each field in u_x[]
8293 . u            - The field values at this point in space
8294 . u_t          - The field time derivative at this point in space (or NULL)
8295 . u_x          - The field derivatives at this point in space
8296 . aOff         - The offset of each auxiliary field in u[]
8297 . aOff_x       - The offset of each auxiliary field in u_x[]
8298 . a            - The auxiliary field values at this point in space
8299 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8300 . a_x          - The auxiliary field derivatives at this point in space
8301 . t            - The current time
8302 . x            - The coordinates of this point
8303 . numConstants - The number of constants
8304 . constants    - The value of each constant
8305 - f            - The value of the function at this point in space
8306 
8307   Level: intermediate
8308 
8309   Note:
8310   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.
8311   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
8312   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8313   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8314 
8315   Developer Notes:
8316   This API is specific to only particular usage of `DM`
8317 
8318   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8319 
8320 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabelLocal()`,
8321 `DMProjectFunction()`, `DMComputeL2Diff()`
8322 @*/
8323 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)
8324 {
8325   PetscFunctionBegin;
8326   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8327   if (localU) PetscValidHeaderSpecific(localU, VEC_CLASSID, 3);
8328   PetscValidHeaderSpecific(localX, VEC_CLASSID, 6);
8329   PetscUseTypeMethod(dm, projectfieldlocal, time, localU, funcs, mode, localX);
8330   PetscFunctionReturn(PETSC_SUCCESS);
8331 }
8332 
8333 /*@C
8334   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.
8335 
8336   Not Collective
8337 
8338   Input Parameters:
8339 + dm     - The `DM`
8340 . time   - The time
8341 . label  - The `DMLabel` marking the portion of the domain to output
8342 . numIds - The number of label ids to use
8343 . ids    - The label ids to use for marking
8344 . Nc     - The number of components to set in the output, or `PETSC_DETERMINE` for all components
8345 . comps  - The components to set in the output, or `NULL` for all components
8346 . localU - The input field vector
8347 . funcs  - The functions to evaluate, one per field
8348 - mode   - The insertion mode for values
8349 
8350   Output Parameter:
8351 . localX - The output vector
8352 
8353   Calling sequence of `funcs`:
8354 + dim          - The spatial dimension
8355 . Nf           - The number of input fields
8356 . NfAux        - The number of input auxiliary fields
8357 . uOff         - The offset of each field in u[]
8358 . uOff_x       - The offset of each field in u_x[]
8359 . u            - The field values at this point in space
8360 . u_t          - The field time derivative at this point in space (or NULL)
8361 . u_x          - The field derivatives at this point in space
8362 . aOff         - The offset of each auxiliary field in u[]
8363 . aOff_x       - The offset of each auxiliary field in u_x[]
8364 . a            - The auxiliary field values at this point in space
8365 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8366 . a_x          - The auxiliary field derivatives at this point in space
8367 . t            - The current time
8368 . x            - The coordinates of this point
8369 . numConstants - The number of constants
8370 . constants    - The value of each constant
8371 - f            - The value of the function at this point in space
8372 
8373   Level: intermediate
8374 
8375   Note:
8376   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.
8377   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
8378   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8379   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8380 
8381   Developer Notes:
8382   This API is specific to only particular usage of `DM`
8383 
8384   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8385 
8386 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabel()`, `DMProjectFunction()`, `DMComputeL2Diff()`
8387 @*/
8388 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)
8389 {
8390   PetscFunctionBegin;
8391   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8392   PetscValidHeaderSpecific(localU, VEC_CLASSID, 8);
8393   PetscValidHeaderSpecific(localX, VEC_CLASSID, 11);
8394   PetscUseTypeMethod(dm, projectfieldlabellocal, time, label, numIds, ids, Nc, comps, localU, funcs, mode, localX);
8395   PetscFunctionReturn(PETSC_SUCCESS);
8396 }
8397 
8398 /*@C
8399   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.
8400 
8401   Not Collective
8402 
8403   Input Parameters:
8404 + dm     - The `DM`
8405 . time   - The time
8406 . label  - The `DMLabel` marking the portion of the domain to output
8407 . numIds - The number of label ids to use
8408 . ids    - The label ids to use for marking
8409 . Nc     - The number of components to set in the output, or `PETSC_DETERMINE` for all components
8410 . comps  - The components to set in the output, or `NULL` for all components
8411 . U      - The input field vector
8412 . funcs  - The functions to evaluate, one per field
8413 - mode   - The insertion mode for values
8414 
8415   Output Parameter:
8416 . X - The output vector
8417 
8418   Calling sequence of `funcs`:
8419 + dim          - The spatial dimension
8420 . Nf           - The number of input fields
8421 . NfAux        - The number of input auxiliary fields
8422 . uOff         - The offset of each field in u[]
8423 . uOff_x       - The offset of each field in u_x[]
8424 . u            - The field values at this point in space
8425 . u_t          - The field time derivative at this point in space (or NULL)
8426 . u_x          - The field derivatives at this point in space
8427 . aOff         - The offset of each auxiliary field in u[]
8428 . aOff_x       - The offset of each auxiliary field in u_x[]
8429 . a            - The auxiliary field values at this point in space
8430 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8431 . a_x          - The auxiliary field derivatives at this point in space
8432 . t            - The current time
8433 . x            - The coordinates of this point
8434 . numConstants - The number of constants
8435 . constants    - The value of each constant
8436 - f            - The value of the function at this point in space
8437 
8438   Level: intermediate
8439 
8440   Note:
8441   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.
8442   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
8443   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8444   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8445 
8446   Developer Notes:
8447   This API is specific to only particular usage of `DM`
8448 
8449   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8450 
8451 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabelLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`
8452 @*/
8453 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)
8454 {
8455   DM  dmIn;
8456   Vec localU, localX;
8457 
8458   PetscFunctionBegin;
8459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8460   PetscCall(VecGetDM(U, &dmIn));
8461   PetscCall(DMGetLocalVector(dmIn, &localU));
8462   PetscCall(DMGetLocalVector(dm, &localX));
8463   PetscCall(VecSet(localX, 0.));
8464   PetscCall(DMGlobalToLocalBegin(dmIn, U, mode, localU));
8465   PetscCall(DMGlobalToLocalEnd(dmIn, U, mode, localU));
8466   PetscCall(DMProjectFieldLabelLocal(dm, time, label, numIds, ids, Nc, comps, localU, funcs, mode, localX));
8467   PetscCall(DMLocalToGlobalBegin(dm, localX, mode, X));
8468   PetscCall(DMLocalToGlobalEnd(dm, localX, mode, X));
8469   PetscCall(DMRestoreLocalVector(dm, &localX));
8470   PetscCall(DMRestoreLocalVector(dmIn, &localU));
8471   PetscFunctionReturn(PETSC_SUCCESS);
8472 }
8473 
8474 /*@C
8475   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.
8476 
8477   Not Collective
8478 
8479   Input Parameters:
8480 + dm     - The `DM`
8481 . time   - The time
8482 . label  - The `DMLabel` marking the portion of the domain boundary to output
8483 . numIds - The number of label ids to use
8484 . ids    - The label ids to use for marking
8485 . Nc     - The number of components to set in the output, or `PETSC_DETERMINE` for all components
8486 . comps  - The components to set in the output, or `NULL` for all components
8487 . localU - The input field vector
8488 . funcs  - The functions to evaluate, one per field
8489 - mode   - The insertion mode for values
8490 
8491   Output Parameter:
8492 . localX - The output vector
8493 
8494   Calling sequence of `funcs`:
8495 + dim          - The spatial dimension
8496 . Nf           - The number of input fields
8497 . NfAux        - The number of input auxiliary fields
8498 . uOff         - The offset of each field in u[]
8499 . uOff_x       - The offset of each field in u_x[]
8500 . u            - The field values at this point in space
8501 . u_t          - The field time derivative at this point in space (or NULL)
8502 . u_x          - The field derivatives at this point in space
8503 . aOff         - The offset of each auxiliary field in u[]
8504 . aOff_x       - The offset of each auxiliary field in u_x[]
8505 . a            - The auxiliary field values at this point in space
8506 . a_t          - The auxiliary field time derivative at this point in space (or NULL)
8507 . a_x          - The auxiliary field derivatives at this point in space
8508 . t            - The current time
8509 . x            - The coordinates of this point
8510 . n            - The face normal
8511 . numConstants - The number of constants
8512 . constants    - The value of each constant
8513 - f            - The value of the function at this point in space
8514 
8515   Level: intermediate
8516 
8517   Note:
8518   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.
8519   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
8520   a subdomain. You can also output a different number of fields than the input, with different discretizations. Last the auxiliary `DM`, attached to the
8521   auxiliary field vector, which is attached to dm, can also be different. It can have a different topology, number of fields, and discretizations.
8522 
8523   Developer Notes:
8524   This API is specific to only particular usage of `DM`
8525 
8526   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8527 
8528 .seealso: [](ch_dmbase), `DM`, `DMProjectField()`, `DMProjectFieldLabelLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`
8529 @*/
8530 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)
8531 {
8532   PetscFunctionBegin;
8533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8534   PetscValidHeaderSpecific(localU, VEC_CLASSID, 8);
8535   PetscValidHeaderSpecific(localX, VEC_CLASSID, 11);
8536   PetscUseTypeMethod(dm, projectbdfieldlabellocal, time, label, numIds, ids, Nc, comps, localU, funcs, mode, localX);
8537   PetscFunctionReturn(PETSC_SUCCESS);
8538 }
8539 
8540 /*@C
8541   DMComputeL2Diff - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.
8542 
8543   Collective
8544 
8545   Input Parameters:
8546 + dm    - The `DM`
8547 . time  - The time
8548 . funcs - The functions to evaluate for each field component
8549 . ctxs  - Optional array of contexts to pass to each function, or NULL.
8550 - X     - The coefficient vector u_h, a global vector
8551 
8552   Output Parameter:
8553 . diff - The diff ||u - u_h||_2
8554 
8555   Level: developer
8556 
8557   Developer Notes:
8558   This API is specific to only particular usage of `DM`
8559 
8560   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8561 
8562 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
8563 @*/
8564 PetscErrorCode DMComputeL2Diff(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
8565 {
8566   PetscFunctionBegin;
8567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8568   PetscValidHeaderSpecific(X, VEC_CLASSID, 5);
8569   PetscUseTypeMethod(dm, computel2diff, time, funcs, ctxs, X, diff);
8570   PetscFunctionReturn(PETSC_SUCCESS);
8571 }
8572 
8573 /*@C
8574   DMComputeL2GradientDiff - This function computes the L_2 difference between the gradient of a function u and an FEM interpolant solution grad u_h.
8575 
8576   Collective
8577 
8578   Input Parameters:
8579 + dm    - The `DM`
8580 . time  - The time
8581 . funcs - The gradient functions to evaluate for each field component
8582 . ctxs  - Optional array of contexts to pass to each function, or NULL.
8583 . X     - The coefficient vector u_h, a global vector
8584 - n     - The vector to project along
8585 
8586   Output Parameter:
8587 . diff - The diff ||(grad u - grad u_h) . n||_2
8588 
8589   Level: developer
8590 
8591   Developer Notes:
8592   This API is specific to only particular usage of `DM`
8593 
8594   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8595 
8596 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMComputeL2FieldDiff()`
8597 @*/
8598 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)
8599 {
8600   PetscFunctionBegin;
8601   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8602   PetscValidHeaderSpecific(X, VEC_CLASSID, 5);
8603   PetscUseTypeMethod(dm, computel2gradientdiff, time, funcs, ctxs, X, n, diff);
8604   PetscFunctionReturn(PETSC_SUCCESS);
8605 }
8606 
8607 /*@C
8608   DMComputeL2FieldDiff - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h, separated into field components.
8609 
8610   Collective
8611 
8612   Input Parameters:
8613 + dm    - The `DM`
8614 . time  - The time
8615 . funcs - The functions to evaluate for each field component
8616 . ctxs  - Optional array of contexts to pass to each function, or NULL.
8617 - X     - The coefficient vector u_h, a global vector
8618 
8619   Output Parameter:
8620 . diff - The array of differences, ||u^f - u^f_h||_2
8621 
8622   Level: developer
8623 
8624   Developer Notes:
8625   This API is specific to only particular usage of `DM`
8626 
8627   The notes need to provide some information about what has to be provided to the `DM` to be able to perform the computation.
8628 
8629 .seealso: [](ch_dmbase), `DM`, `DMProjectFunction()`, `DMComputeL2GradientDiff()`
8630 @*/
8631 PetscErrorCode DMComputeL2FieldDiff(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal diff[])
8632 {
8633   PetscFunctionBegin;
8634   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8635   PetscValidHeaderSpecific(X, VEC_CLASSID, 5);
8636   PetscUseTypeMethod(dm, computel2fielddiff, time, funcs, ctxs, X, diff);
8637   PetscFunctionReturn(PETSC_SUCCESS);
8638 }
8639 
8640 /*@C
8641   DMGetNeighbors - Gets an array containing the MPI ranks of all the processes neighbors
8642 
8643   Not Collective
8644 
8645   Input Parameter:
8646 . dm - The `DM`
8647 
8648   Output Parameters:
8649 + nranks - the number of neighbours
8650 - ranks  - the neighbors ranks
8651 
8652   Level: beginner
8653 
8654   Note:
8655   Do not free the array, it is freed when the `DM` is destroyed.
8656 
8657 .seealso: [](ch_dmbase), `DM`, `DMDAGetNeighbors()`, `PetscSFGetRootRanks()`
8658 @*/
8659 PetscErrorCode DMGetNeighbors(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
8660 {
8661   PetscFunctionBegin;
8662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8663   PetscUseTypeMethod(dm, getneighbors, nranks, ranks);
8664   PetscFunctionReturn(PETSC_SUCCESS);
8665 }
8666 
8667 #include <petsc/private/matimpl.h> /* Needed because of coloring->ctype below */
8668 
8669 /*
8670     Converts the input vector to a ghosted vector and then calls the standard coloring code.
8671     This must be a different function because it requires DM which is not defined in the Mat library
8672 */
8673 static PetscErrorCode MatFDColoringApply_AIJDM(Mat J, MatFDColoring coloring, Vec x1, void *sctx)
8674 {
8675   PetscFunctionBegin;
8676   if (coloring->ctype == IS_COLORING_LOCAL) {
8677     Vec x1local;
8678     DM  dm;
8679     PetscCall(MatGetDM(J, &dm));
8680     PetscCheck(dm, PetscObjectComm((PetscObject)J), PETSC_ERR_ARG_INCOMP, "IS_COLORING_LOCAL requires a DM");
8681     PetscCall(DMGetLocalVector(dm, &x1local));
8682     PetscCall(DMGlobalToLocalBegin(dm, x1, INSERT_VALUES, x1local));
8683     PetscCall(DMGlobalToLocalEnd(dm, x1, INSERT_VALUES, x1local));
8684     x1 = x1local;
8685   }
8686   PetscCall(MatFDColoringApply_AIJ(J, coloring, x1, sctx));
8687   if (coloring->ctype == IS_COLORING_LOCAL) {
8688     DM dm;
8689     PetscCall(MatGetDM(J, &dm));
8690     PetscCall(DMRestoreLocalVector(dm, &x1));
8691   }
8692   PetscFunctionReturn(PETSC_SUCCESS);
8693 }
8694 
8695 /*@
8696   MatFDColoringUseDM - allows a `MatFDColoring` object to use the `DM` associated with the matrix to compute a `IS_COLORING_LOCAL` coloring
8697 
8698   Input Parameters:
8699 + coloring   - The matrix to get the `DM` from
8700 - fdcoloring - the `MatFDColoring` object
8701 
8702   Level: advanced
8703 
8704   Developer Note:
8705   This routine exists because the PETSc `Mat` library does not know about the `DM` objects
8706 
8707 .seealso: [](ch_dmbase), `DM`, `MatFDColoring`, `MatFDColoringCreate()`, `ISColoringType`
8708 @*/
8709 PetscErrorCode MatFDColoringUseDM(Mat coloring, MatFDColoring fdcoloring)
8710 {
8711   PetscFunctionBegin;
8712   coloring->ops->fdcoloringapply = MatFDColoringApply_AIJDM;
8713   PetscFunctionReturn(PETSC_SUCCESS);
8714 }
8715 
8716 /*@
8717   DMGetCompatibility - determine if two `DM`s are compatible
8718 
8719   Collective
8720 
8721   Input Parameters:
8722 + dm1 - the first `DM`
8723 - dm2 - the second `DM`
8724 
8725   Output Parameters:
8726 + compatible - whether or not the two `DM`s are compatible
8727 - set        - whether or not the compatible value was actually determined and set
8728 
8729   Level: advanced
8730 
8731   Notes:
8732   Two `DM`s are deemed compatible if they represent the same parallel decomposition
8733   of the same topology. This implies that the section (field data) on one
8734   "makes sense" with respect to the topology and parallel decomposition of the other.
8735   Loosely speaking, compatible `DM`s represent the same domain and parallel
8736   decomposition, but hold different data.
8737 
8738   Typically, one would confirm compatibility if intending to simultaneously iterate
8739   over a pair of vectors obtained from different `DM`s.
8740 
8741   For example, two `DMDA` objects are compatible if they have the same local
8742   and global sizes and the same stencil width. They can have different numbers
8743   of degrees of freedom per node. Thus, one could use the node numbering from
8744   either `DM` in bounds for a loop over vectors derived from either `DM`.
8745 
8746   Consider the operation of summing data living on a 2-dof `DMDA` to data living
8747   on a 1-dof `DMDA`, which should be compatible, as in the following snippet.
8748 .vb
8749   ...
8750   PetscCall(DMGetCompatibility(da1,da2,&compatible,&set));
8751   if (set && compatible)  {
8752     PetscCall(DMDAVecGetArrayDOF(da1,vec1,&arr1));
8753     PetscCall(DMDAVecGetArrayDOF(da2,vec2,&arr2));
8754     PetscCall(DMDAGetCorners(da1,&x,&y,NULL,&m,&n,NULL));
8755     for (j=y; j<y+n; ++j) {
8756       for (i=x; i<x+m, ++i) {
8757         arr1[j][i][0] = arr2[j][i][0] + arr2[j][i][1];
8758       }
8759     }
8760     PetscCall(DMDAVecRestoreArrayDOF(da1,vec1,&arr1));
8761     PetscCall(DMDAVecRestoreArrayDOF(da2,vec2,&arr2));
8762   } else {
8763     SETERRQ(PetscObjectComm((PetscObject)da1,PETSC_ERR_ARG_INCOMP,"DMDA objects incompatible");
8764   }
8765   ...
8766 .ve
8767 
8768   Checking compatibility might be expensive for a given implementation of `DM`,
8769   or might be impossible to unambiguously confirm or deny. For this reason,
8770   this function may decline to determine compatibility, and hence users should
8771   always check the "set" output parameter.
8772 
8773   A `DM` is always compatible with itself.
8774 
8775   In the current implementation, `DM`s which live on "unequal" communicators
8776   (MPI_UNEQUAL in the terminology of MPI_Comm_compare()) are always deemed
8777   incompatible.
8778 
8779   This function is labeled "Collective," as information about all subdomains
8780   is required on each rank. However, in `DM` implementations which store all this
8781   information locally, this function may be merely "Logically Collective".
8782 
8783   Developer Note:
8784   Compatibility is assumed to be a symmetric concept; `DM` A is compatible with `DM` B
8785   iff B is compatible with A. Thus, this function checks the implementations
8786   of both dm and dmc (if they are of different types), attempting to determine
8787   compatibility. It is left to `DM` implementers to ensure that symmetry is
8788   preserved. The simplest way to do this is, when implementing type-specific
8789   logic for this function, is to check for existing logic in the implementation
8790   of other `DM` types and let *set = PETSC_FALSE if found.
8791 
8792 .seealso: [](ch_dmbase), `DM`, `DMDACreateCompatibleDMDA()`, `DMStagCreateCompatibleDMStag()`
8793 @*/
8794 PetscErrorCode DMGetCompatibility(DM dm1, DM dm2, PetscBool *compatible, PetscBool *set)
8795 {
8796   PetscMPIInt compareResult;
8797   DMType      type, type2;
8798   PetscBool   sameType;
8799 
8800   PetscFunctionBegin;
8801   PetscValidHeaderSpecific(dm1, DM_CLASSID, 1);
8802   PetscValidHeaderSpecific(dm2, DM_CLASSID, 2);
8803 
8804   /* Declare a DM compatible with itself */
8805   if (dm1 == dm2) {
8806     *set        = PETSC_TRUE;
8807     *compatible = PETSC_TRUE;
8808     PetscFunctionReturn(PETSC_SUCCESS);
8809   }
8810 
8811   /* Declare a DM incompatible with a DM that lives on an "unequal"
8812      communicator. Note that this does not preclude compatibility with
8813      DMs living on "congruent" or "similar" communicators, but this must be
8814      determined by the implementation-specific logic */
8815   PetscCallMPI(MPI_Comm_compare(PetscObjectComm((PetscObject)dm1), PetscObjectComm((PetscObject)dm2), &compareResult));
8816   if (compareResult == MPI_UNEQUAL) {
8817     *set        = PETSC_TRUE;
8818     *compatible = PETSC_FALSE;
8819     PetscFunctionReturn(PETSC_SUCCESS);
8820   }
8821 
8822   /* Pass to the implementation-specific routine, if one exists. */
8823   if (dm1->ops->getcompatibility) {
8824     PetscUseTypeMethod(dm1, getcompatibility, dm2, compatible, set);
8825     if (*set) PetscFunctionReturn(PETSC_SUCCESS);
8826   }
8827 
8828   /* If dm1 and dm2 are of different types, then attempt to check compatibility
8829      with an implementation of this function from dm2 */
8830   PetscCall(DMGetType(dm1, &type));
8831   PetscCall(DMGetType(dm2, &type2));
8832   PetscCall(PetscStrcmp(type, type2, &sameType));
8833   if (!sameType && dm2->ops->getcompatibility) {
8834     PetscUseTypeMethod(dm2, getcompatibility, dm1, compatible, set); /* Note argument order */
8835   } else {
8836     *set = PETSC_FALSE;
8837   }
8838   PetscFunctionReturn(PETSC_SUCCESS);
8839 }
8840 
8841 /*@C
8842   DMMonitorSet - Sets an additional monitor function that is to be used after a solve to monitor discretization performance.
8843 
8844   Logically Collective
8845 
8846   Input Parameters:
8847 + dm             - the `DM`
8848 . f              - the monitor function
8849 . mctx           - [optional] user-defined context for private data for the monitor routine (use `NULL` if no context is desired)
8850 - monitordestroy - [optional] routine that frees monitor context (may be `NULL`), see `PetscCtxDestroyFn` for the calling sequence
8851 
8852   Options Database Key:
8853 . -dm_monitor_cancel - cancels all monitors that have been hardwired into a code by calls to `DMMonitorSet()`, but
8854                        does not cancel those set via the options database.
8855 
8856   Level: intermediate
8857 
8858   Note:
8859   Several different monitoring routines may be set by calling
8860   `DMMonitorSet()` multiple times or with `DMMonitorSetFromOptions()`; all will be called in the
8861   order in which they were set.
8862 
8863   Fortran Note:
8864   Only a single monitor function can be set for each `DM` object
8865 
8866   Developer Note:
8867   This API has a generic name but seems specific to a very particular aspect of the use of `DM`
8868 
8869 .seealso: [](ch_dmbase), `DM`, `DMMonitorCancel()`, `DMMonitorSetFromOptions()`, `DMMonitor()`, `PetscCtxDestroyFn`
8870 @*/
8871 PetscErrorCode DMMonitorSet(DM dm, PetscErrorCode (*f)(DM, void *), void *mctx, PetscCtxDestroyFn *monitordestroy)
8872 {
8873   PetscInt m;
8874 
8875   PetscFunctionBegin;
8876   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8877   for (m = 0; m < dm->numbermonitors; ++m) {
8878     PetscBool identical;
8879 
8880     PetscCall(PetscMonitorCompare((PetscErrorCode (*)(void))f, mctx, monitordestroy, (PetscErrorCode (*)(void))dm->monitor[m], dm->monitorcontext[m], dm->monitordestroy[m], &identical));
8881     if (identical) PetscFunctionReturn(PETSC_SUCCESS);
8882   }
8883   PetscCheck(dm->numbermonitors < MAXDMMONITORS, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many monitors set");
8884   dm->monitor[dm->numbermonitors]          = f;
8885   dm->monitordestroy[dm->numbermonitors]   = monitordestroy;
8886   dm->monitorcontext[dm->numbermonitors++] = mctx;
8887   PetscFunctionReturn(PETSC_SUCCESS);
8888 }
8889 
8890 /*@
8891   DMMonitorCancel - Clears all the monitor functions for a `DM` object.
8892 
8893   Logically Collective
8894 
8895   Input Parameter:
8896 . dm - the DM
8897 
8898   Options Database Key:
8899 . -dm_monitor_cancel - cancels all monitors that have been hardwired
8900   into a code by calls to `DMonitorSet()`, but does not cancel those
8901   set via the options database
8902 
8903   Level: intermediate
8904 
8905   Note:
8906   There is no way to clear one specific monitor from a `DM` object.
8907 
8908 .seealso: [](ch_dmbase), `DM`, `DMMonitorSet()`, `DMMonitorSetFromOptions()`, `DMMonitor()`
8909 @*/
8910 PetscErrorCode DMMonitorCancel(DM dm)
8911 {
8912   PetscInt m;
8913 
8914   PetscFunctionBegin;
8915   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8916   for (m = 0; m < dm->numbermonitors; ++m) {
8917     if (dm->monitordestroy[m]) PetscCall((*dm->monitordestroy[m])(&dm->monitorcontext[m]));
8918   }
8919   dm->numbermonitors = 0;
8920   PetscFunctionReturn(PETSC_SUCCESS);
8921 }
8922 
8923 /*@C
8924   DMMonitorSetFromOptions - Sets a monitor function and viewer appropriate for the type indicated by the user
8925 
8926   Collective
8927 
8928   Input Parameters:
8929 + dm           - `DM` object you wish to monitor
8930 . name         - the monitor type one is seeking
8931 . help         - message indicating what monitoring is done
8932 . manual       - manual page for the monitor
8933 . monitor      - the monitor function, this must use a `PetscViewerFormat` as its context
8934 - 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
8935 
8936   Output Parameter:
8937 . flg - Flag set if the monitor was created
8938 
8939   Level: developer
8940 
8941 .seealso: [](ch_dmbase), `DM`, `PetscOptionsCreateViewer()`, `PetscOptionsGetReal()`, `PetscOptionsHasName()`, `PetscOptionsGetString()`,
8942           `PetscOptionsGetIntArray()`, `PetscOptionsGetRealArray()`, `PetscOptionsBool()`
8943           `PetscOptionsInt()`, `PetscOptionsString()`, `PetscOptionsReal()`,
8944           `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`,
8945           `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`,
8946           `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`,
8947           `PetscOptionsFList()`, `PetscOptionsEList()`, `DMMonitor()`, `DMMonitorSet()`
8948 @*/
8949 PetscErrorCode DMMonitorSetFromOptions(DM dm, const char name[], const char help[], const char manual[], PetscErrorCode (*monitor)(DM, void *), PetscErrorCode (*monitorsetup)(DM, PetscViewerAndFormat *), PetscBool *flg)
8950 {
8951   PetscViewer       viewer;
8952   PetscViewerFormat format;
8953 
8954   PetscFunctionBegin;
8955   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8956   PetscCall(PetscOptionsCreateViewer(PetscObjectComm((PetscObject)dm), ((PetscObject)dm)->options, ((PetscObject)dm)->prefix, name, &viewer, &format, flg));
8957   if (*flg) {
8958     PetscViewerAndFormat *vf;
8959 
8960     PetscCall(PetscViewerAndFormatCreate(viewer, format, &vf));
8961     PetscCall(PetscViewerDestroy(&viewer));
8962     if (monitorsetup) PetscCall((*monitorsetup)(dm, vf));
8963     PetscCall(DMMonitorSet(dm, monitor, vf, (PetscCtxDestroyFn *)PetscViewerAndFormatDestroy));
8964   }
8965   PetscFunctionReturn(PETSC_SUCCESS);
8966 }
8967 
8968 /*@
8969   DMMonitor - runs the user provided monitor routines, if they exist
8970 
8971   Collective
8972 
8973   Input Parameter:
8974 . dm - The `DM`
8975 
8976   Level: developer
8977 
8978   Developer Note:
8979   Note should indicate when during the life of the `DM` the monitor is run. It appears to be
8980   related to the discretization process seems rather specialized since some `DM` have no
8981   concept of discretization.
8982 
8983 .seealso: [](ch_dmbase), `DM`, `DMMonitorSet()`, `DMMonitorSetFromOptions()`
8984 @*/
8985 PetscErrorCode DMMonitor(DM dm)
8986 {
8987   PetscInt m;
8988 
8989   PetscFunctionBegin;
8990   if (!dm) PetscFunctionReturn(PETSC_SUCCESS);
8991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8992   for (m = 0; m < dm->numbermonitors; ++m) PetscCall((*dm->monitor[m])(dm, dm->monitorcontext[m]));
8993   PetscFunctionReturn(PETSC_SUCCESS);
8994 }
8995 
8996 /*@
8997   DMComputeError - Computes the error assuming the user has provided the exact solution functions
8998 
8999   Collective
9000 
9001   Input Parameters:
9002 + dm  - The `DM`
9003 - sol - The solution vector
9004 
9005   Input/Output Parameter:
9006 . errors - An array of length Nf, the number of fields, or `NULL` for no output; on output
9007            contains the error in each field
9008 
9009   Output Parameter:
9010 . errorVec - A vector to hold the cellwise error (may be `NULL`)
9011 
9012   Level: developer
9013 
9014   Note:
9015   The exact solutions come from the `PetscDS` object, and the time comes from `DMGetOutputSequenceNumber()`.
9016 
9017 .seealso: [](ch_dmbase), `DM`, `DMMonitorSet()`, `DMGetRegionNumDS()`, `PetscDSGetExactSolution()`, `DMGetOutputSequenceNumber()`
9018 @*/
9019 PetscErrorCode DMComputeError(DM dm, Vec sol, PetscReal errors[], Vec *errorVec)
9020 {
9021   PetscErrorCode (**exactSol)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *);
9022   void    **ctxs;
9023   PetscReal time;
9024   PetscInt  Nf, f, Nds, s;
9025 
9026   PetscFunctionBegin;
9027   PetscCall(DMGetNumFields(dm, &Nf));
9028   PetscCall(PetscCalloc2(Nf, &exactSol, Nf, &ctxs));
9029   PetscCall(DMGetNumDS(dm, &Nds));
9030   for (s = 0; s < Nds; ++s) {
9031     PetscDS         ds;
9032     DMLabel         label;
9033     IS              fieldIS;
9034     const PetscInt *fields;
9035     PetscInt        dsNf;
9036 
9037     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
9038     PetscCall(PetscDSGetNumFields(ds, &dsNf));
9039     if (fieldIS) PetscCall(ISGetIndices(fieldIS, &fields));
9040     for (f = 0; f < dsNf; ++f) {
9041       const PetscInt field = fields[f];
9042       PetscCall(PetscDSGetExactSolution(ds, field, &exactSol[field], &ctxs[field]));
9043     }
9044     if (fieldIS) PetscCall(ISRestoreIndices(fieldIS, &fields));
9045   }
9046   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);
9047   PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
9048   if (errors) PetscCall(DMComputeL2FieldDiff(dm, time, exactSol, ctxs, sol, errors));
9049   if (errorVec) {
9050     DM             edm;
9051     DMPolytopeType ct;
9052     PetscBool      simplex;
9053     PetscInt       dim, cStart, Nf;
9054 
9055     PetscCall(DMClone(dm, &edm));
9056     PetscCall(DMGetDimension(edm, &dim));
9057     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
9058     PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9059     simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9060     PetscCall(DMGetNumFields(dm, &Nf));
9061     for (f = 0; f < Nf; ++f) {
9062       PetscFE         fe, efe;
9063       PetscQuadrature q;
9064       const char     *name;
9065 
9066       PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
9067       PetscCall(PetscFECreateLagrange(PETSC_COMM_SELF, dim, Nf, simplex, 0, PETSC_DETERMINE, &efe));
9068       PetscCall(PetscObjectGetName((PetscObject)fe, &name));
9069       PetscCall(PetscObjectSetName((PetscObject)efe, name));
9070       PetscCall(PetscFEGetQuadrature(fe, &q));
9071       PetscCall(PetscFESetQuadrature(efe, q));
9072       PetscCall(DMSetField(edm, f, NULL, (PetscObject)efe));
9073       PetscCall(PetscFEDestroy(&efe));
9074     }
9075     PetscCall(DMCreateDS(edm));
9076 
9077     PetscCall(DMCreateGlobalVector(edm, errorVec));
9078     PetscCall(PetscObjectSetName((PetscObject)*errorVec, "Error"));
9079     PetscCall(DMPlexComputeL2DiffVec(dm, time, exactSol, ctxs, sol, *errorVec));
9080     PetscCall(DMDestroy(&edm));
9081   }
9082   PetscCall(PetscFree2(exactSol, ctxs));
9083   PetscFunctionReturn(PETSC_SUCCESS);
9084 }
9085 
9086 /*@
9087   DMGetNumAuxiliaryVec - Get the number of auxiliary vectors associated with this `DM`
9088 
9089   Not Collective
9090 
9091   Input Parameter:
9092 . dm - The `DM`
9093 
9094   Output Parameter:
9095 . numAux - The number of auxiliary data vectors
9096 
9097   Level: advanced
9098 
9099 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMSetAuxiliaryVec()`, `DMGetAuxiliaryLabels()`, `DMGetAuxiliaryVec()`
9100 @*/
9101 PetscErrorCode DMGetNumAuxiliaryVec(DM dm, PetscInt *numAux)
9102 {
9103   PetscFunctionBegin;
9104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9105   PetscCall(PetscHMapAuxGetSize(dm->auxData, numAux));
9106   PetscFunctionReturn(PETSC_SUCCESS);
9107 }
9108 
9109 /*@
9110   DMGetAuxiliaryVec - Get the auxiliary vector for region specified by the given label and value, and equation part
9111 
9112   Not Collective
9113 
9114   Input Parameters:
9115 + dm    - The `DM`
9116 . label - The `DMLabel`
9117 . value - The label value indicating the region
9118 - part  - The equation part, or 0 if unused
9119 
9120   Output Parameter:
9121 . aux - The `Vec` holding auxiliary field data
9122 
9123   Level: advanced
9124 
9125   Note:
9126   If no auxiliary vector is found for this (label, value), (NULL, 0, 0) is checked as well.
9127 
9128 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMSetAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryLabels()`
9129 @*/
9130 PetscErrorCode DMGetAuxiliaryVec(DM dm, DMLabel label, PetscInt value, PetscInt part, Vec *aux)
9131 {
9132   PetscHashAuxKey key, wild = {NULL, 0, 0};
9133   PetscBool       has;
9134 
9135   PetscFunctionBegin;
9136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9137   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
9138   key.label = label;
9139   key.value = value;
9140   key.part  = part;
9141   PetscCall(PetscHMapAuxHas(dm->auxData, key, &has));
9142   if (has) PetscCall(PetscHMapAuxGet(dm->auxData, key, aux));
9143   else PetscCall(PetscHMapAuxGet(dm->auxData, wild, aux));
9144   PetscFunctionReturn(PETSC_SUCCESS);
9145 }
9146 
9147 /*@
9148   DMSetAuxiliaryVec - Set an auxiliary vector for region specified by the given label and value, and equation part
9149 
9150   Not Collective because auxiliary vectors are not parallel
9151 
9152   Input Parameters:
9153 + dm    - The `DM`
9154 . label - The `DMLabel`
9155 . value - The label value indicating the region
9156 . part  - The equation part, or 0 if unused
9157 - aux   - The `Vec` holding auxiliary field data
9158 
9159   Level: advanced
9160 
9161 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMGetAuxiliaryLabels()`, `DMCopyAuxiliaryVec()`
9162 @*/
9163 PetscErrorCode DMSetAuxiliaryVec(DM dm, DMLabel label, PetscInt value, PetscInt part, Vec aux)
9164 {
9165   Vec             old;
9166   PetscHashAuxKey key;
9167 
9168   PetscFunctionBegin;
9169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9170   if (label) PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 2);
9171   key.label = label;
9172   key.value = value;
9173   key.part  = part;
9174   PetscCall(PetscHMapAuxGet(dm->auxData, key, &old));
9175   PetscCall(PetscObjectReference((PetscObject)aux));
9176   if (!aux) PetscCall(PetscHMapAuxDel(dm->auxData, key));
9177   else PetscCall(PetscHMapAuxSet(dm->auxData, key, aux));
9178   PetscCall(VecDestroy(&old));
9179   PetscFunctionReturn(PETSC_SUCCESS);
9180 }
9181 
9182 /*@
9183   DMGetAuxiliaryLabels - Get the labels, values, and parts for all auxiliary vectors in this `DM`
9184 
9185   Not Collective
9186 
9187   Input Parameter:
9188 . dm - The `DM`
9189 
9190   Output Parameters:
9191 + labels - The `DMLabel`s for each `Vec`
9192 . values - The label values for each `Vec`
9193 - parts  - The equation parts for each `Vec`
9194 
9195   Level: advanced
9196 
9197   Note:
9198   The arrays passed in must be at least as large as `DMGetNumAuxiliaryVec()`.
9199 
9200 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMSetAuxiliaryVec()`, `DMCopyAuxiliaryVec()`
9201 @*/
9202 PetscErrorCode DMGetAuxiliaryLabels(DM dm, DMLabel labels[], PetscInt values[], PetscInt parts[])
9203 {
9204   PetscHashAuxKey *keys;
9205   PetscInt         n, i, off = 0;
9206 
9207   PetscFunctionBegin;
9208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9209   PetscAssertPointer(labels, 2);
9210   PetscAssertPointer(values, 3);
9211   PetscAssertPointer(parts, 4);
9212   PetscCall(DMGetNumAuxiliaryVec(dm, &n));
9213   PetscCall(PetscMalloc1(n, &keys));
9214   PetscCall(PetscHMapAuxGetKeys(dm->auxData, &off, keys));
9215   for (i = 0; i < n; ++i) {
9216     labels[i] = keys[i].label;
9217     values[i] = keys[i].value;
9218     parts[i]  = keys[i].part;
9219   }
9220   PetscCall(PetscFree(keys));
9221   PetscFunctionReturn(PETSC_SUCCESS);
9222 }
9223 
9224 /*@
9225   DMCopyAuxiliaryVec - Copy the auxiliary vector data on a `DM` to a new `DM`
9226 
9227   Not Collective
9228 
9229   Input Parameter:
9230 . dm - The `DM`
9231 
9232   Output Parameter:
9233 . dmNew - The new `DM`, now with the same auxiliary data
9234 
9235   Level: advanced
9236 
9237   Note:
9238   This is a shallow copy of the auxiliary vectors
9239 
9240 .seealso: [](ch_dmbase), `DM`, `DMClearAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMSetAuxiliaryVec()`
9241 @*/
9242 PetscErrorCode DMCopyAuxiliaryVec(DM dm, DM dmNew)
9243 {
9244   PetscFunctionBegin;
9245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9246   PetscValidHeaderSpecific(dmNew, DM_CLASSID, 2);
9247   if (dm == dmNew) PetscFunctionReturn(PETSC_SUCCESS);
9248   PetscCall(DMClearAuxiliaryVec(dmNew));
9249 
9250   PetscCall(PetscHMapAuxDestroy(&dmNew->auxData));
9251   PetscCall(PetscHMapAuxDuplicate(dm->auxData, &dmNew->auxData));
9252   {
9253     Vec     *auxData;
9254     PetscInt n, i, off = 0;
9255 
9256     PetscCall(PetscHMapAuxGetSize(dmNew->auxData, &n));
9257     PetscCall(PetscMalloc1(n, &auxData));
9258     PetscCall(PetscHMapAuxGetVals(dmNew->auxData, &off, auxData));
9259     for (i = 0; i < n; ++i) PetscCall(PetscObjectReference((PetscObject)auxData[i]));
9260     PetscCall(PetscFree(auxData));
9261   }
9262   PetscFunctionReturn(PETSC_SUCCESS);
9263 }
9264 
9265 /*@
9266   DMClearAuxiliaryVec - Destroys the auxiliary vector information and creates a new empty one
9267 
9268   Not Collective
9269 
9270   Input Parameter:
9271 . dm - The `DM`
9272 
9273   Level: advanced
9274 
9275 .seealso: [](ch_dmbase), `DM`, `DMCopyAuxiliaryVec()`, `DMGetNumAuxiliaryVec()`, `DMGetAuxiliaryVec()`, `DMSetAuxiliaryVec()`
9276 @*/
9277 PetscErrorCode DMClearAuxiliaryVec(DM dm)
9278 {
9279   Vec     *auxData;
9280   PetscInt n, i, off = 0;
9281 
9282   PetscFunctionBegin;
9283   PetscCall(PetscHMapAuxGetSize(dm->auxData, &n));
9284   PetscCall(PetscMalloc1(n, &auxData));
9285   PetscCall(PetscHMapAuxGetVals(dm->auxData, &off, auxData));
9286   for (i = 0; i < n; ++i) PetscCall(VecDestroy(&auxData[i]));
9287   PetscCall(PetscFree(auxData));
9288   PetscCall(PetscHMapAuxDestroy(&dm->auxData));
9289   PetscCall(PetscHMapAuxCreate(&dm->auxData));
9290   PetscFunctionReturn(PETSC_SUCCESS);
9291 }
9292 
9293 /*@
9294   DMPolytopeMatchOrientation - Determine an orientation (transformation) that takes the source face arrangement to the target face arrangement
9295 
9296   Not Collective
9297 
9298   Input Parameters:
9299 + ct         - The `DMPolytopeType`
9300 . sourceCone - The source arrangement of faces
9301 - targetCone - The target arrangement of faces
9302 
9303   Output Parameters:
9304 + ornt  - The orientation (transformation) which will take the source arrangement to the target arrangement
9305 - found - Flag indicating that a suitable orientation was found
9306 
9307   Level: advanced
9308 
9309   Note:
9310   An arrangement is a face order combined with an orientation for each face
9311 
9312   Each orientation (transformation) is labeled with an integer from negative `DMPolytopeTypeGetNumArrangements(ct)`/2 to `DMPolytopeTypeGetNumArrangements(ct)`/2
9313   that labels each arrangement (face ordering plus orientation for each face).
9314 
9315   See `DMPolytopeMatchVertexOrientation()` to find a new vertex orientation that takes the source vertex arrangement to the target vertex arrangement
9316 
9317 .seealso: [](ch_dmbase), `DM`, `DMPolytopeGetOrientation()`, `DMPolytopeMatchVertexOrientation()`, `DMPolytopeGetVertexOrientation()`
9318 @*/
9319 PetscErrorCode DMPolytopeMatchOrientation(DMPolytopeType ct, const PetscInt sourceCone[], const PetscInt targetCone[], PetscInt *ornt, PetscBool *found)
9320 {
9321   const PetscInt cS = DMPolytopeTypeGetConeSize(ct);
9322   const PetscInt nO = DMPolytopeTypeGetNumArrangements(ct) / 2;
9323   PetscInt       o, c;
9324 
9325   PetscFunctionBegin;
9326   if (!nO) {
9327     *ornt  = 0;
9328     *found = PETSC_TRUE;
9329     PetscFunctionReturn(PETSC_SUCCESS);
9330   }
9331   for (o = -nO; o < nO; ++o) {
9332     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
9333 
9334     for (c = 0; c < cS; ++c)
9335       if (sourceCone[arr[c * 2]] != targetCone[c]) break;
9336     if (c == cS) {
9337       *ornt = o;
9338       break;
9339     }
9340   }
9341   *found = o == nO ? PETSC_FALSE : PETSC_TRUE;
9342   PetscFunctionReturn(PETSC_SUCCESS);
9343 }
9344 
9345 /*@
9346   DMPolytopeGetOrientation - Determine an orientation (transformation) that takes the source face arrangement to the target face arrangement
9347 
9348   Not Collective
9349 
9350   Input Parameters:
9351 + ct         - The `DMPolytopeType`
9352 . sourceCone - The source arrangement of faces
9353 - targetCone - The target arrangement of faces
9354 
9355   Output Parameter:
9356 . ornt - The orientation (transformation) which will take the source arrangement to the target arrangement
9357 
9358   Level: advanced
9359 
9360   Note:
9361   This function is the same as `DMPolytopeMatchOrientation()` except it will generate an error if no suitable orientation can be found.
9362 
9363   Developer Note:
9364   It is unclear why this function needs to exist since one can simply call `DMPolytopeMatchOrientation()` and error if none is found
9365 
9366 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMPolytopeMatchOrientation()`, `DMPolytopeGetVertexOrientation()`, `DMPolytopeMatchVertexOrientation()`
9367 @*/
9368 PetscErrorCode DMPolytopeGetOrientation(DMPolytopeType ct, const PetscInt sourceCone[], const PetscInt targetCone[], PetscInt *ornt)
9369 {
9370   PetscBool found;
9371 
9372   PetscFunctionBegin;
9373   PetscCall(DMPolytopeMatchOrientation(ct, sourceCone, targetCone, ornt, &found));
9374   PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not find orientation for %s", DMPolytopeTypes[ct]);
9375   PetscFunctionReturn(PETSC_SUCCESS);
9376 }
9377 
9378 /*@
9379   DMPolytopeMatchVertexOrientation - Determine an orientation (transformation) that takes the source vertex arrangement to the target vertex arrangement
9380 
9381   Not Collective
9382 
9383   Input Parameters:
9384 + ct         - The `DMPolytopeType`
9385 . sourceVert - The source arrangement of vertices
9386 - targetVert - The target arrangement of vertices
9387 
9388   Output Parameters:
9389 + ornt  - The orientation (transformation) which will take the source arrangement to the target arrangement
9390 - found - Flag indicating that a suitable orientation was found
9391 
9392   Level: advanced
9393 
9394   Notes:
9395   An arrangement is a vertex order
9396 
9397   Each orientation (transformation) is labeled with an integer from negative `DMPolytopeTypeGetNumArrangements(ct)`/2 to `DMPolytopeTypeGetNumArrangements(ct)`/2
9398   that labels each arrangement (vertex ordering).
9399 
9400   See `DMPolytopeMatchOrientation()` to find a new face orientation that takes the source face arrangement to the target face arrangement
9401 
9402 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMPolytopeGetOrientation()`, `DMPolytopeMatchOrientation()`, `DMPolytopeTypeGetNumVertices()`, `DMPolytopeTypeGetVertexArrangement()`
9403 @*/
9404 PetscErrorCode DMPolytopeMatchVertexOrientation(DMPolytopeType ct, const PetscInt sourceVert[], const PetscInt targetVert[], PetscInt *ornt, PetscBool *found)
9405 {
9406   const PetscInt cS = DMPolytopeTypeGetNumVertices(ct);
9407   const PetscInt nO = DMPolytopeTypeGetNumArrangements(ct) / 2;
9408   PetscInt       o, c;
9409 
9410   PetscFunctionBegin;
9411   if (!nO) {
9412     *ornt  = 0;
9413     *found = PETSC_TRUE;
9414     PetscFunctionReturn(PETSC_SUCCESS);
9415   }
9416   for (o = -nO; o < nO; ++o) {
9417     const PetscInt *arr = DMPolytopeTypeGetVertexArrangement(ct, o);
9418 
9419     for (c = 0; c < cS; ++c)
9420       if (sourceVert[arr[c]] != targetVert[c]) break;
9421     if (c == cS) {
9422       *ornt = o;
9423       break;
9424     }
9425   }
9426   *found = o == nO ? PETSC_FALSE : PETSC_TRUE;
9427   PetscFunctionReturn(PETSC_SUCCESS);
9428 }
9429 
9430 /*@
9431   DMPolytopeGetVertexOrientation - Determine an orientation (transformation) that takes the source vertex arrangement to the target vertex arrangement
9432 
9433   Not Collective
9434 
9435   Input Parameters:
9436 + ct         - The `DMPolytopeType`
9437 . sourceCone - The source arrangement of vertices
9438 - targetCone - The target arrangement of vertices
9439 
9440   Output Parameter:
9441 . ornt - The orientation (transformation) which will take the source arrangement to the target arrangement
9442 
9443   Level: advanced
9444 
9445   Note:
9446   This function is the same as `DMPolytopeMatchVertexOrientation()` except it errors if not orientation is possible.
9447 
9448   Developer Note:
9449   It is unclear why this function needs to exist since one can simply call `DMPolytopeMatchVertexOrientation()` and error if none is found
9450 
9451 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMPolytopeMatchVertexOrientation()`, `DMPolytopeGetOrientation()`
9452 @*/
9453 PetscErrorCode DMPolytopeGetVertexOrientation(DMPolytopeType ct, const PetscInt sourceCone[], const PetscInt targetCone[], PetscInt *ornt)
9454 {
9455   PetscBool found;
9456 
9457   PetscFunctionBegin;
9458   PetscCall(DMPolytopeMatchVertexOrientation(ct, sourceCone, targetCone, ornt, &found));
9459   PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not find orientation for %s", DMPolytopeTypes[ct]);
9460   PetscFunctionReturn(PETSC_SUCCESS);
9461 }
9462 
9463 /*@
9464   DMPolytopeInCellTest - Check whether a point lies inside the reference cell of given type
9465 
9466   Not Collective
9467 
9468   Input Parameters:
9469 + ct    - The `DMPolytopeType`
9470 - point - Coordinates of the point
9471 
9472   Output Parameter:
9473 . inside - Flag indicating whether the point is inside the reference cell of given type
9474 
9475   Level: advanced
9476 
9477 .seealso: [](ch_dmbase), `DM`, `DMPolytopeType`, `DMLocatePoints()`
9478 @*/
9479 PetscErrorCode DMPolytopeInCellTest(DMPolytopeType ct, const PetscReal point[], PetscBool *inside)
9480 {
9481   PetscReal sum = 0.0;
9482   PetscInt  d;
9483 
9484   PetscFunctionBegin;
9485   *inside = PETSC_TRUE;
9486   switch (ct) {
9487   case DM_POLYTOPE_TRIANGLE:
9488   case DM_POLYTOPE_TETRAHEDRON:
9489     for (d = 0; d < DMPolytopeTypeGetDim(ct); ++d) {
9490       if (point[d] < -1.0) {
9491         *inside = PETSC_FALSE;
9492         break;
9493       }
9494       sum += point[d];
9495     }
9496     if (sum > PETSC_SMALL) {
9497       *inside = PETSC_FALSE;
9498       break;
9499     }
9500     break;
9501   case DM_POLYTOPE_QUADRILATERAL:
9502   case DM_POLYTOPE_HEXAHEDRON:
9503     for (d = 0; d < DMPolytopeTypeGetDim(ct); ++d)
9504       if (PetscAbsReal(point[d]) > 1. + PETSC_SMALL) {
9505         *inside = PETSC_FALSE;
9506         break;
9507       }
9508     break;
9509   default:
9510     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unsupported polytope type %s", DMPolytopeTypes[ct]);
9511   }
9512   PetscFunctionReturn(PETSC_SUCCESS);
9513 }
9514 
9515 /*@
9516   DMReorderSectionSetDefault - Set flag indicating whether the local section should be reordered by default
9517 
9518   Logically collective
9519 
9520   Input Parameters:
9521 + dm      - The DM
9522 - reorder - Flag for reordering
9523 
9524   Level: intermediate
9525 
9526 .seealso: `DMReorderSectionGetDefault()`
9527 @*/
9528 PetscErrorCode DMReorderSectionSetDefault(DM dm, DMReorderDefaultFlag reorder)
9529 {
9530   PetscFunctionBegin;
9531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9532   PetscTryMethod(dm, "DMReorderSectionSetDefault_C", (DM, DMReorderDefaultFlag), (dm, reorder));
9533   PetscFunctionReturn(PETSC_SUCCESS);
9534 }
9535 
9536 /*@
9537   DMReorderSectionGetDefault - Get flag indicating whether the local section should be reordered by default
9538 
9539   Not collective
9540 
9541   Input Parameter:
9542 . dm - The DM
9543 
9544   Output Parameter:
9545 . reorder - Flag for reordering
9546 
9547   Level: intermediate
9548 
9549 .seealso: `DMReorderSetDefault()`
9550 @*/
9551 PetscErrorCode DMReorderSectionGetDefault(DM dm, DMReorderDefaultFlag *reorder)
9552 {
9553   PetscFunctionBegin;
9554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9555   PetscAssertPointer(reorder, 2);
9556   *reorder = DM_REORDER_DEFAULT_NOTSET;
9557   PetscTryMethod(dm, "DMReorderSectionGetDefault_C", (DM, DMReorderDefaultFlag *), (dm, reorder));
9558   PetscFunctionReturn(PETSC_SUCCESS);
9559 }
9560 
9561 /*@
9562   DMReorderSectionSetType - Set the type of local section reordering
9563 
9564   Logically collective
9565 
9566   Input Parameters:
9567 + dm      - The DM
9568 - reorder - The reordering method
9569 
9570   Level: intermediate
9571 
9572 .seealso: `DMReorderSectionGetType()`, `DMReorderSectionSetDefault()`
9573 @*/
9574 PetscErrorCode DMReorderSectionSetType(DM dm, MatOrderingType reorder)
9575 {
9576   PetscFunctionBegin;
9577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9578   PetscTryMethod(dm, "DMReorderSectionSetType_C", (DM, MatOrderingType), (dm, reorder));
9579   PetscFunctionReturn(PETSC_SUCCESS);
9580 }
9581 
9582 /*@
9583   DMReorderSectionGetType - Get the reordering type for the local section
9584 
9585   Not collective
9586 
9587   Input Parameter:
9588 . dm - The DM
9589 
9590   Output Parameter:
9591 . reorder - The reordering method
9592 
9593   Level: intermediate
9594 
9595 .seealso: `DMReorderSetDefault()`, `DMReorderSectionGetDefault()`
9596 @*/
9597 PetscErrorCode DMReorderSectionGetType(DM dm, MatOrderingType *reorder)
9598 {
9599   PetscFunctionBegin;
9600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9601   PetscAssertPointer(reorder, 2);
9602   *reorder = NULL;
9603   PetscTryMethod(dm, "DMReorderSectionGetType_C", (DM, MatOrderingType *), (dm, reorder));
9604   PetscFunctionReturn(PETSC_SUCCESS);
9605 }
9606