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