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