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