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