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