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