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