xref: /petsc/src/dm/impls/forest/p4est/pforest.h (revision 97ca8683938deabf894660fe75c833d2b9ebe70d)
1 #pragma once
2 
3 #include <petscds.h>
4 #include <petsc/private/dmimpl.h>
5 #include <petsc/private/dmforestimpl.h>
6 #include <petsc/private/dmpleximpl.h>
7 #include <petsc/private/dmlabelimpl.h>
8 #include <petsc/private/viewerimpl.h>
9 #include <../src/sys/classes/viewer/impls/vtk/vtkvimpl.h>
10 #include "petsc_p4est_package.h"
11 
12 #if defined(PETSC_HAVE_P4EST)
13 
14   #if !defined(P4_TO_P8)
15     #include <p4est.h>
16     #include <p4est_extended.h>
17     #include <p4est_geometry.h>
18     #include <p4est_ghost.h>
19     #include <p4est_lnodes.h>
20     #include <p4est_vtk.h>
21     #include <p4est_plex.h>
22     #include <p4est_bits.h>
23     #include <p4est_algorithms.h>
24   #else
25     #include <p8est.h>
26     #include <p8est_extended.h>
27     #include <p8est_geometry.h>
28     #include <p8est_ghost.h>
29     #include <p8est_lnodes.h>
30     #include <p8est_vtk.h>
31     #include <p8est_plex.h>
32     #include <p8est_bits.h>
33     #include <p8est_algorithms.h>
34   #endif
35 
36 typedef enum {
37   PATTERN_HASH,
38   PATTERN_FRACTAL,
39   PATTERN_CORNER,
40   PATTERN_CENTER,
41   PATTERN_COUNT
42 } DMRefinePattern;
43 static const char *DMRefinePatternName[PATTERN_COUNT] = {"hash", "fractal", "corner", "center"};
44 
45 typedef struct _DMRefinePatternCtx {
46   PetscInt       corner;
47   PetscBool      fractal[P4EST_CHILDREN];
48   PetscReal      hashLikelihood;
49   PetscInt       maxLevel;
50   p4est_refine_t refine_fn;
51 } DMRefinePatternCtx;
52 
53 static int DMRefinePattern_Corner(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrant)
54 {
55   p4est_quadrant_t    root, rootcorner;
56   DMRefinePatternCtx *ctx;
57 
58   ctx = (DMRefinePatternCtx *)p4est->user_pointer;
59   if (quadrant->level >= ctx->maxLevel) return 0;
60 
61   root.x = root.y = 0;
62   #if defined(P4_TO_P8)
63   root.z = 0;
64   #endif
65   root.level = 0;
66   p4est_quadrant_corner_descendant(&root, &rootcorner, ctx->corner, quadrant->level);
67   if (p4est_quadrant_is_equal(quadrant, &rootcorner)) return 1;
68   return 0;
69 }
70 
71 static int DMRefinePattern_Center(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrant)
72 {
73   int                 cid;
74   p4est_quadrant_t    ancestor, ancestorcorner;
75   DMRefinePatternCtx *ctx;
76 
77   ctx = (DMRefinePatternCtx *)p4est->user_pointer;
78   if (quadrant->level >= ctx->maxLevel) return 0;
79   if (quadrant->level <= 1) return 1;
80 
81   p4est_quadrant_ancestor(quadrant, 1, &ancestor);
82   cid = p4est_quadrant_child_id(&ancestor);
83   p4est_quadrant_corner_descendant(&ancestor, &ancestorcorner, P4EST_CHILDREN - 1 - cid, quadrant->level);
84   if (p4est_quadrant_is_equal(quadrant, &ancestorcorner)) return 1;
85   return 0;
86 }
87 
88 static int DMRefinePattern_Fractal(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrant)
89 {
90   int                 cid;
91   DMRefinePatternCtx *ctx;
92 
93   ctx = (DMRefinePatternCtx *)p4est->user_pointer;
94   if (quadrant->level >= ctx->maxLevel) return 0;
95   if (!quadrant->level) return 1;
96   cid = p4est_quadrant_child_id(quadrant);
97   if (ctx->fractal[cid ^ ((int)(quadrant->level % P4EST_CHILDREN))]) return 1;
98   return 0;
99 }
100 
101   /* simplified from MurmurHash3 by Austin Appleby */
102   #define DMPROT32(x, y) ((x << y) | (x >> (32 - y)))
103 static uint32_t DMPforestHash(const uint32_t *blocks, uint32_t nblocks)
104 {
105   uint32_t c1   = 0xcc9e2d51;
106   uint32_t c2   = 0x1b873593;
107   uint32_t r1   = 15;
108   uint32_t r2   = 13;
109   uint32_t m    = 5;
110   uint32_t n    = 0xe6546b64;
111   uint32_t hash = 0;
112   int      len  = nblocks * 4;
113   uint32_t i;
114 
115   for (i = 0; i < nblocks; i++) {
116     uint32_t k;
117 
118     k = blocks[i];
119     k *= c1;
120     k = DMPROT32(k, r1);
121     k *= c2;
122 
123     hash ^= k;
124     hash = DMPROT32(hash, r2) * m + n;
125   }
126 
127   hash ^= len;
128   hash ^= (hash >> 16);
129   hash *= 0x85ebca6b;
130   hash ^= (hash >> 13);
131   hash *= 0xc2b2ae35;
132   hash ^= (hash >> 16);
133 
134   return hash;
135 }
136 
137   #if defined(UINT32_MAX)
138     #define DMP4EST_HASH_MAX UINT32_MAX
139   #else
140     #define DMP4EST_HASH_MAX ((uint32_t)0xffffffff)
141   #endif
142 
143 static int DMRefinePattern_Hash(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrant)
144 {
145   uint32_t            data[5];
146   uint32_t            result;
147   DMRefinePatternCtx *ctx;
148 
149   ctx = (DMRefinePatternCtx *)p4est->user_pointer;
150   if (quadrant->level >= ctx->maxLevel) return 0;
151   data[0] = ((uint32_t)quadrant->level) << 24;
152   data[1] = (uint32_t)which_tree;
153   data[2] = (uint32_t)quadrant->x;
154   data[3] = (uint32_t)quadrant->y;
155   #if defined(P4_TO_P8)
156   data[4] = (uint32_t)quadrant->z;
157   #endif
158 
159   result = DMPforestHash(data, 2 + P4EST_DIM);
160   if (((double)result / (double)DMP4EST_HASH_MAX) < ctx->hashLikelihood) return 1;
161   return 0;
162 }
163 
164   #define DMConvert_pforest_plex _infix_pforest(DMConvert, _plex)
165 static PetscErrorCode DMConvert_pforest_plex(DM, DMType, DM *);
166 
167   #define DMFTopology_pforest _append_pforest(DMFTopology)
168 typedef struct {
169   PetscInt              refct;
170   p4est_connectivity_t *conn;
171   p4est_geometry_t     *geom;
172   PetscInt             *tree_face_to_uniq; /* p4est does not explicitly enumerate facets, but we must to keep track of labels */
173 } DMFTopology_pforest;
174 
175   #define DM_Forest_pforest _append_pforest(DM_Forest)
176 typedef struct {
177   DMFTopology_pforest *topo;
178   p4est_t             *forest;
179   p4est_ghost_t       *ghost;
180   p4est_lnodes_t      *lnodes;
181   PetscBool            partition_for_coarsening;
182   PetscBool            coarsen_hierarchy;
183   PetscBool            labelsFinalized;
184   PetscBool            adaptivitySuccess;
185   PetscInt             cLocalStart;
186   PetscInt             cLocalEnd;
187   DM                   plex;
188   char                *ghostName;
189   PetscSF              pointAdaptToSelfSF;
190   PetscSF              pointSelfToAdaptSF;
191   PetscInt            *pointAdaptToSelfCids;
192   PetscInt            *pointSelfToAdaptCids;
193 } DM_Forest_pforest;
194 
195   #define DM_Forest_geometry_pforest _append_pforest(DM_Forest_geometry)
196 typedef struct {
197   DM base;
198   PetscErrorCode (*map)(DM, PetscInt, PetscInt, const PetscReal[], PetscReal[], void *);
199   void             *mapCtx;
200   PetscInt          coordDim;
201   p4est_geometry_t *inner;
202 } DM_Forest_geometry_pforest;
203 
204   #define GeometryMapping_pforest _append_pforest(GeometryMapping)
205 static void GeometryMapping_pforest(p4est_geometry_t *geom, p4est_topidx_t which_tree, const double abc[3], double xyz[3])
206 {
207   DM_Forest_geometry_pforest *geom_pforest = (DM_Forest_geometry_pforest *)geom->user;
208   PetscReal                   PetscABC[3]  = {0.};
209   PetscReal                   PetscXYZ[3]  = {0.};
210   PetscInt                    i, d = PetscMin(3, geom_pforest->coordDim);
211   double                      ABC[3];
212   PetscErrorCode              ierr;
213 
214   (geom_pforest->inner->X)(geom_pforest->inner, which_tree, abc, ABC);
215 
216   for (i = 0; i < d; i++) PetscABC[i] = ABC[i];
217   ierr = (geom_pforest->map)(geom_pforest->base, (PetscInt)which_tree, geom_pforest->coordDim, PetscABC, PetscXYZ, geom_pforest->mapCtx);
218   PETSC_P4EST_ASSERT(!ierr);
219   for (i = 0; i < d; i++) xyz[i] = PetscXYZ[i];
220 }
221 
222   #define GeometryDestroy_pforest _append_pforest(GeometryDestroy)
223 static void GeometryDestroy_pforest(p4est_geometry_t *geom)
224 {
225   DM_Forest_geometry_pforest *geom_pforest = (DM_Forest_geometry_pforest *)geom->user;
226   PetscErrorCode              ierr;
227 
228   p4est_geometry_destroy(geom_pforest->inner);
229   ierr = PetscFree(geom->user);
230   PETSC_P4EST_ASSERT(!ierr);
231   ierr = PetscFree(geom);
232   PETSC_P4EST_ASSERT(!ierr);
233 }
234 
235   #define DMFTopologyDestroy_pforest _append_pforest(DMFTopologyDestroy)
236 static PetscErrorCode DMFTopologyDestroy_pforest(DMFTopology_pforest **topo)
237 {
238   PetscFunctionBegin;
239   if (!(*topo)) PetscFunctionReturn(PETSC_SUCCESS);
240   if (--((*topo)->refct) > 0) {
241     *topo = NULL;
242     PetscFunctionReturn(PETSC_SUCCESS);
243   }
244   if ((*topo)->geom) PetscCallP4est(p4est_geometry_destroy, ((*topo)->geom));
245   PetscCallP4est(p4est_connectivity_destroy, ((*topo)->conn));
246   PetscCall(PetscFree((*topo)->tree_face_to_uniq));
247   PetscCall(PetscFree(*topo));
248   *topo = NULL;
249   PetscFunctionReturn(PETSC_SUCCESS);
250 }
251 
252 static PetscErrorCode PforestConnectivityEnumerateFacets(p4est_connectivity_t *, PetscInt **);
253 
254   #define DMFTopologyCreateBrick_pforest _append_pforest(DMFTopologyCreateBrick)
255 static PetscErrorCode DMFTopologyCreateBrick_pforest(DM dm, PetscInt N[], PetscInt P[], PetscReal B[], DMFTopology_pforest **topo, PetscBool useMorton)
256 {
257   double  *vertices;
258   PetscInt i, numVerts;
259 
260   PetscFunctionBegin;
261   PetscCheck(useMorton, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Lexicographic ordering not implemented yet");
262   PetscCall(PetscNew(topo));
263 
264   (*topo)->refct = 1;
265   #if !defined(P4_TO_P8)
266   PetscCallP4estReturn((*topo)->conn, p4est_connectivity_new_brick, ((int)N[0], (int)N[1], (P[0] == DM_BOUNDARY_NONE) ? 0 : 1, (P[1] == DM_BOUNDARY_NONE) ? 0 : 1));
267   #else
268   PetscCallP4estReturn((*topo)->conn, p8est_connectivity_new_brick, ((int)N[0], (int)N[1], (int)N[2], (P[0] == DM_BOUNDARY_NONE) ? 0 : 1, (P[1] == DM_BOUNDARY_NONE) ? 0 : 1, (P[2] == DM_BOUNDARY_NONE) ? 0 : 1));
269   #endif
270   numVerts = (*topo)->conn->num_vertices;
271   vertices = (*topo)->conn->vertices;
272   for (i = 0; i < 3 * numVerts; i++) {
273     PetscInt j = i % 3;
274 
275     vertices[i] = B[2 * j] + (vertices[i] / N[j]) * (B[2 * j + 1] - B[2 * j]);
276   }
277   (*topo)->geom = NULL;
278   PetscCall(PforestConnectivityEnumerateFacets((*topo)->conn, &(*topo)->tree_face_to_uniq));
279   PetscFunctionReturn(PETSC_SUCCESS);
280 }
281 
282   #define DMFTopologyCreate_pforest _append_pforest(DMFTopologyCreate)
283 static PetscErrorCode DMFTopologyCreate_pforest(DM dm, DMForestTopology topologyName, DMFTopology_pforest **topo)
284 {
285   const char *name = (const char *)topologyName;
286   const char *prefix;
287   PetscBool   isBrick, isShell, isSphere, isMoebius;
288 
289   PetscFunctionBegin;
290   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
291   PetscAssertPointer(name, 2);
292   PetscAssertPointer(topo, 3);
293   PetscCall(PetscStrcmp(name, "brick", &isBrick));
294   PetscCall(PetscStrcmp(name, "shell", &isShell));
295   PetscCall(PetscStrcmp(name, "sphere", &isSphere));
296   PetscCall(PetscStrcmp(name, "moebius", &isMoebius));
297   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
298   if (isBrick) {
299     PetscBool flgN, flgP, flgM, flgB, useMorton = PETSC_TRUE, periodic = PETSC_FALSE;
300     PetscInt  N[3] = {2, 2, 2}, P[3] = {0, 0, 0}, nretN = P4EST_DIM, nretP = P4EST_DIM, nretB = 2 * P4EST_DIM, i;
301     PetscReal B[6] = {0.0, 1.0, 0.0, 1.0, 0.0, 1.0}, Lstart[3] = {0., 0., 0.}, L[3] = {-1.0, -1.0, -1.0}, maxCell[3] = {-1.0, -1.0, -1.0};
302 
303     if (dm->setfromoptionscalled) {
304       PetscCall(PetscOptionsGetIntArray(((PetscObject)dm)->options, prefix, "-dm_p4est_brick_size", N, &nretN, &flgN));
305       PetscCall(PetscOptionsGetIntArray(((PetscObject)dm)->options, prefix, "-dm_p4est_brick_periodicity", P, &nretP, &flgP));
306       PetscCall(PetscOptionsGetRealArray(((PetscObject)dm)->options, prefix, "-dm_p4est_brick_bounds", B, &nretB, &flgB));
307       PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, prefix, "-dm_p4est_brick_use_morton_curve", &useMorton, &flgM));
308       PetscCheck(!flgN || nretN == P4EST_DIM, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Need to give %d sizes in -dm_p4est_brick_size, gave %" PetscInt_FMT, P4EST_DIM, nretN);
309       PetscCheck(!flgP || nretP == P4EST_DIM, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Need to give %d periodicities in -dm_p4est_brick_periodicity, gave %" PetscInt_FMT, P4EST_DIM, nretP);
310       PetscCheck(!flgB || nretB == 2 * P4EST_DIM, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Need to give %d bounds in -dm_p4est_brick_bounds, gave %" PetscInt_FMT, P4EST_DIM, nretP);
311     }
312     for (i = 0; i < P4EST_DIM; i++) {
313       P[i]     = (P[i] ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE);
314       periodic = (PetscBool)(P[i] || periodic);
315       if (!flgB) B[2 * i + 1] = N[i];
316       if (P[i]) {
317         Lstart[i]  = B[2 * i + 0];
318         L[i]       = B[2 * i + 1] - B[2 * i + 0];
319         maxCell[i] = 1.1 * (L[i] / N[i]);
320       }
321     }
322     PetscCall(DMFTopologyCreateBrick_pforest(dm, N, P, B, topo, useMorton));
323     if (periodic) PetscCall(DMSetPeriodicity(dm, maxCell, Lstart, L));
324   } else {
325     PetscCall(PetscNew(topo));
326 
327     (*topo)->refct = 1;
328     PetscCallP4estReturn((*topo)->conn, p4est_connectivity_new_byname, (name));
329     (*topo)->geom = NULL;
330     if (isMoebius) PetscCall(DMSetCoordinateDim(dm, 3));
331   #if defined(P4_TO_P8)
332     if (isShell) {
333       PetscReal R2 = 1., R1 = .55;
334 
335       if (dm->setfromoptionscalled) {
336         PetscCall(PetscOptionsGetReal(((PetscObject)dm)->options, prefix, "-dm_p4est_shell_outer_radius", &R2, NULL));
337         PetscCall(PetscOptionsGetReal(((PetscObject)dm)->options, prefix, "-dm_p4est_shell_inner_radius", &R1, NULL));
338       }
339       PetscCallP4estReturn((*topo)->geom, p8est_geometry_new_shell, ((*topo)->conn, R2, R1));
340     } else if (isSphere) {
341       PetscReal R2 = 1., R1 = 0.191728, R0 = 0.039856;
342 
343       if (dm->setfromoptionscalled) {
344         PetscCall(PetscOptionsGetReal(((PetscObject)dm)->options, prefix, "-dm_p4est_sphere_outer_radius", &R2, NULL));
345         PetscCall(PetscOptionsGetReal(((PetscObject)dm)->options, prefix, "-dm_p4est_sphere_inner_radius", &R1, NULL));
346         PetscCall(PetscOptionsGetReal(((PetscObject)dm)->options, prefix, "-dm_p4est_sphere_core_radius", &R0, NULL));
347       }
348       PetscCallP4estReturn((*topo)->geom, p8est_geometry_new_sphere, ((*topo)->conn, R2, R1, R0));
349     }
350   #endif
351     PetscCall(PforestConnectivityEnumerateFacets((*topo)->conn, &(*topo)->tree_face_to_uniq));
352   }
353   PetscFunctionReturn(PETSC_SUCCESS);
354 }
355 
356   #define DMConvert_plex_pforest _append_pforest(DMConvert_plex)
357 static PetscErrorCode DMConvert_plex_pforest(DM dm, DMType newtype, DM *pforest)
358 {
359   MPI_Comm  comm;
360   PetscBool isPlex;
361   PetscInt  dim;
362   void     *ctx;
363 
364   PetscFunctionBegin;
365 
366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
367   comm = PetscObjectComm((PetscObject)dm);
368   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
369   PetscCheck(isPlex, comm, PETSC_ERR_ARG_WRONG, "Expected DM type %s, got %s", DMPLEX, ((PetscObject)dm)->type_name);
370   PetscCall(DMGetDimension(dm, &dim));
371   PetscCheck(dim == P4EST_DIM, comm, PETSC_ERR_ARG_WRONG, "Expected DM dimension %d, got %" PetscInt_FMT, P4EST_DIM, dim);
372   PetscCall(DMCreate(comm, pforest));
373   PetscCall(DMSetType(*pforest, DMPFOREST));
374   PetscCall(DMForestSetBaseDM(*pforest, dm));
375   PetscCall(DMGetApplicationContext(dm, &ctx));
376   PetscCall(DMSetApplicationContext(*pforest, ctx));
377   PetscCall(DMCopyDisc(dm, *pforest));
378   PetscFunctionReturn(PETSC_SUCCESS);
379 }
380 
381   #define DMForestDestroy_pforest _append_pforest(DMForestDestroy)
382 static PetscErrorCode DMForestDestroy_pforest(DM dm)
383 {
384   DM_Forest         *forest  = (DM_Forest *)dm->data;
385   DM_Forest_pforest *pforest = (DM_Forest_pforest *)forest->data;
386 
387   PetscFunctionBegin;
388   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
389   if (pforest->lnodes) PetscCallP4est(p4est_lnodes_destroy, (pforest->lnodes));
390   pforest->lnodes = NULL;
391   if (pforest->ghost) PetscCallP4est(p4est_ghost_destroy, (pforest->ghost));
392   pforest->ghost = NULL;
393   if (pforest->forest) PetscCallP4est(p4est_destroy, (pforest->forest));
394   pforest->forest = NULL;
395   PetscCall(DMFTopologyDestroy_pforest(&pforest->topo));
396   PetscCall(PetscFree(pforest->ghostName));
397   PetscCall(DMDestroy(&pforest->plex));
398   PetscCall(PetscSFDestroy(&pforest->pointAdaptToSelfSF));
399   PetscCall(PetscSFDestroy(&pforest->pointSelfToAdaptSF));
400   PetscCall(PetscFree(pforest->pointAdaptToSelfCids));
401   PetscCall(PetscFree(pforest->pointSelfToAdaptCids));
402   PetscCall(PetscFree(forest->data));
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406   #define DMForestTemplate_pforest _append_pforest(DMForestTemplate)
407 static PetscErrorCode DMForestTemplate_pforest(DM dm, DM tdm)
408 {
409   DM_Forest_pforest *pforest  = (DM_Forest_pforest *)((DM_Forest *)dm->data)->data;
410   DM_Forest_pforest *tpforest = (DM_Forest_pforest *)((DM_Forest *)tdm->data)->data;
411 
412   PetscFunctionBegin;
413   if (pforest->topo) pforest->topo->refct++;
414   PetscCall(DMFTopologyDestroy_pforest(&(tpforest->topo)));
415   tpforest->topo = pforest->topo;
416   PetscFunctionReturn(PETSC_SUCCESS);
417 }
418 
419   #define DMPlexCreateConnectivity_pforest _append_pforest(DMPlexCreateConnectivity)
420 static PetscErrorCode DMPlexCreateConnectivity_pforest(DM, p4est_connectivity_t **, PetscInt **);
421 
422 typedef struct _PforestAdaptCtx {
423   PetscInt  maxLevel;
424   PetscInt  minLevel;
425   PetscInt  currLevel;
426   PetscBool anyChange;
427 } PforestAdaptCtx;
428 
429 static int pforest_coarsen_currlevel(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrants[])
430 {
431   PforestAdaptCtx *ctx       = (PforestAdaptCtx *)p4est->user_pointer;
432   PetscInt         minLevel  = ctx->minLevel;
433   PetscInt         currLevel = ctx->currLevel;
434 
435   if (quadrants[0]->level <= minLevel) return 0;
436   return (int)((PetscInt)quadrants[0]->level == currLevel);
437 }
438 
439 static int pforest_coarsen_uniform(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrants[])
440 {
441   PforestAdaptCtx *ctx      = (PforestAdaptCtx *)p4est->user_pointer;
442   PetscInt         minLevel = ctx->minLevel;
443 
444   return (int)((PetscInt)quadrants[0]->level > minLevel);
445 }
446 
447 static int pforest_coarsen_flag_any(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrants[])
448 {
449   PetscInt         i;
450   PetscBool        any      = PETSC_FALSE;
451   PforestAdaptCtx *ctx      = (PforestAdaptCtx *)p4est->user_pointer;
452   PetscInt         minLevel = ctx->minLevel;
453 
454   if (quadrants[0]->level <= minLevel) return 0;
455   for (i = 0; i < P4EST_CHILDREN; i++) {
456     if (quadrants[i]->p.user_int == DM_ADAPT_KEEP) {
457       any = PETSC_FALSE;
458       break;
459     }
460     if (quadrants[i]->p.user_int == DM_ADAPT_COARSEN) {
461       any = PETSC_TRUE;
462       break;
463     }
464   }
465   return any ? 1 : 0;
466 }
467 
468 static int pforest_coarsen_flag_all(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrants[])
469 {
470   PetscInt         i;
471   PetscBool        all      = PETSC_TRUE;
472   PforestAdaptCtx *ctx      = (PforestAdaptCtx *)p4est->user_pointer;
473   PetscInt         minLevel = ctx->minLevel;
474 
475   if (quadrants[0]->level <= minLevel) return 0;
476   for (i = 0; i < P4EST_CHILDREN; i++) {
477     if (quadrants[i]->p.user_int != DM_ADAPT_COARSEN) {
478       all = PETSC_FALSE;
479       break;
480     }
481   }
482   return all ? 1 : 0;
483 }
484 
485 static void pforest_init_determine(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrant)
486 {
487   quadrant->p.user_int = DM_ADAPT_DETERMINE;
488 }
489 
490 static int pforest_refine_uniform(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrant)
491 {
492   PforestAdaptCtx *ctx      = (PforestAdaptCtx *)p4est->user_pointer;
493   PetscInt         maxLevel = ctx->maxLevel;
494 
495   return ((PetscInt)quadrant->level < maxLevel);
496 }
497 
498 static int pforest_refine_flag(p4est_t *p4est, p4est_topidx_t which_tree, p4est_quadrant_t *quadrant)
499 {
500   PforestAdaptCtx *ctx      = (PforestAdaptCtx *)p4est->user_pointer;
501   PetscInt         maxLevel = ctx->maxLevel;
502 
503   if ((PetscInt)quadrant->level >= maxLevel) return 0;
504 
505   return (quadrant->p.user_int == DM_ADAPT_REFINE);
506 }
507 
508 static PetscErrorCode DMPforestComputeLocalCellTransferSF_loop(p4est_t *p4estFrom, PetscInt FromOffset, p4est_t *p4estTo, PetscInt ToOffset, p4est_topidx_t flt, p4est_topidx_t llt, PetscInt *toFineLeavesCount, PetscInt *toLeaves, PetscSFNode *fromRoots, PetscInt *fromFineLeavesCount, PetscInt *fromLeaves, PetscSFNode *toRoots)
509 {
510   PetscMPIInt    rank = p4estFrom->mpirank;
511   p4est_topidx_t t;
512   PetscInt       toFineLeaves = 0, fromFineLeaves = 0;
513 
514   PetscFunctionBegin;
515   for (t = flt; t <= llt; t++) { /* count roots and leaves */
516     p4est_tree_t     *treeFrom  = &(((p4est_tree_t *)p4estFrom->trees->array)[t]);
517     p4est_tree_t     *treeTo    = &(((p4est_tree_t *)p4estTo->trees->array)[t]);
518     p4est_quadrant_t *firstFrom = &treeFrom->first_desc;
519     p4est_quadrant_t *firstTo   = &treeTo->first_desc;
520     PetscInt          numFrom   = (PetscInt)treeFrom->quadrants.elem_count;
521     PetscInt          numTo     = (PetscInt)treeTo->quadrants.elem_count;
522     p4est_quadrant_t *quadsFrom = (p4est_quadrant_t *)treeFrom->quadrants.array;
523     p4est_quadrant_t *quadsTo   = (p4est_quadrant_t *)treeTo->quadrants.array;
524     PetscInt          currentFrom, currentTo;
525     PetscInt          treeOffsetFrom = (PetscInt)treeFrom->quadrants_offset;
526     PetscInt          treeOffsetTo   = (PetscInt)treeTo->quadrants_offset;
527     int               comp;
528 
529     PetscCallP4estReturn(comp, p4est_quadrant_is_equal, (firstFrom, firstTo));
530     PetscCheck(comp, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "non-matching partitions");
531 
532     for (currentFrom = 0, currentTo = 0; currentFrom < numFrom && currentTo < numTo;) {
533       p4est_quadrant_t *quadFrom = &quadsFrom[currentFrom];
534       p4est_quadrant_t *quadTo   = &quadsTo[currentTo];
535 
536       if (quadFrom->level == quadTo->level) {
537         if (toLeaves) {
538           toLeaves[toFineLeaves]        = currentTo + treeOffsetTo + ToOffset;
539           fromRoots[toFineLeaves].rank  = rank;
540           fromRoots[toFineLeaves].index = currentFrom + treeOffsetFrom + FromOffset;
541         }
542         toFineLeaves++;
543         currentFrom++;
544         currentTo++;
545       } else {
546         int fromIsAncestor;
547 
548         PetscCallP4estReturn(fromIsAncestor, p4est_quadrant_is_ancestor, (quadFrom, quadTo));
549         if (fromIsAncestor) {
550           p4est_quadrant_t lastDesc;
551 
552           if (toLeaves) {
553             toLeaves[toFineLeaves]        = currentTo + treeOffsetTo + ToOffset;
554             fromRoots[toFineLeaves].rank  = rank;
555             fromRoots[toFineLeaves].index = currentFrom + treeOffsetFrom + FromOffset;
556           }
557           toFineLeaves++;
558           currentTo++;
559           PetscCallP4est(p4est_quadrant_last_descendant, (quadFrom, &lastDesc, quadTo->level));
560           PetscCallP4estReturn(comp, p4est_quadrant_is_equal, (quadTo, &lastDesc));
561           if (comp) currentFrom++;
562         } else {
563           p4est_quadrant_t lastDesc;
564 
565           if (fromLeaves) {
566             fromLeaves[fromFineLeaves]    = currentFrom + treeOffsetFrom + FromOffset;
567             toRoots[fromFineLeaves].rank  = rank;
568             toRoots[fromFineLeaves].index = currentTo + treeOffsetTo + ToOffset;
569           }
570           fromFineLeaves++;
571           currentFrom++;
572           PetscCallP4est(p4est_quadrant_last_descendant, (quadTo, &lastDesc, quadFrom->level));
573           PetscCallP4estReturn(comp, p4est_quadrant_is_equal, (quadFrom, &lastDesc));
574           if (comp) currentTo++;
575         }
576       }
577     }
578   }
579   *toFineLeavesCount   = toFineLeaves;
580   *fromFineLeavesCount = fromFineLeaves;
581   PetscFunctionReturn(PETSC_SUCCESS);
582 }
583 
584 /* Compute the maximum level across all the trees */
585 static PetscErrorCode DMPforestGetRefinementLevel(DM dm, PetscInt *lev)
586 {
587   p4est_topidx_t     t, flt, llt;
588   DM_Forest         *forest      = (DM_Forest *)dm->data;
589   DM_Forest_pforest *pforest     = (DM_Forest_pforest *)forest->data;
590   PetscInt           maxlevelloc = 0;
591   p4est_t           *p4est;
592 
593   PetscFunctionBegin;
594   PetscCheck(pforest, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing DM_Forest_pforest");
595   PetscCheck(pforest->forest, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing p4est_t");
596   p4est = pforest->forest;
597   flt   = p4est->first_local_tree;
598   llt   = p4est->last_local_tree;
599   for (t = flt; t <= llt; t++) {
600     p4est_tree_t *tree = &(((p4est_tree_t *)p4est->trees->array)[t]);
601     maxlevelloc        = PetscMax((PetscInt)tree->maxlevel, maxlevelloc);
602   }
603   PetscCall(MPIU_Allreduce(&maxlevelloc, lev, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
604   PetscFunctionReturn(PETSC_SUCCESS);
605 }
606 
607 /* Puts identity in coarseToFine */
608 /* assumes a matching partition */
609 static PetscErrorCode DMPforestComputeLocalCellTransferSF(MPI_Comm comm, p4est_t *p4estFrom, PetscInt FromOffset, p4est_t *p4estTo, PetscInt ToOffset, PetscSF *fromCoarseToFine, PetscSF *toCoarseFromFine)
610 {
611   p4est_topidx_t flt, llt;
612   PetscSF        fromCoarse, toCoarse;
613   PetscInt       numRootsFrom, numRootsTo, numLeavesFrom, numLeavesTo;
614   PetscInt      *fromLeaves = NULL, *toLeaves = NULL;
615   PetscSFNode   *fromRoots = NULL, *toRoots = NULL;
616 
617   PetscFunctionBegin;
618   flt = p4estFrom->first_local_tree;
619   llt = p4estFrom->last_local_tree;
620   PetscCall(PetscSFCreate(comm, &fromCoarse));
621   if (toCoarseFromFine) PetscCall(PetscSFCreate(comm, &toCoarse));
622   numRootsFrom = p4estFrom->local_num_quadrants + FromOffset;
623   numRootsTo   = p4estTo->local_num_quadrants + ToOffset;
624   PetscCall(DMPforestComputeLocalCellTransferSF_loop(p4estFrom, FromOffset, p4estTo, ToOffset, flt, llt, &numLeavesTo, NULL, NULL, &numLeavesFrom, NULL, NULL));
625   PetscCall(PetscMalloc1(numLeavesTo, &toLeaves));
626   PetscCall(PetscMalloc1(numLeavesTo, &fromRoots));
627   if (toCoarseFromFine) {
628     PetscCall(PetscMalloc1(numLeavesFrom, &fromLeaves));
629     PetscCall(PetscMalloc1(numLeavesFrom, &fromRoots));
630   }
631   PetscCall(DMPforestComputeLocalCellTransferSF_loop(p4estFrom, FromOffset, p4estTo, ToOffset, flt, llt, &numLeavesTo, toLeaves, fromRoots, &numLeavesFrom, fromLeaves, toRoots));
632   if (!ToOffset && (numLeavesTo == numRootsTo)) { /* compress */
633     PetscCall(PetscFree(toLeaves));
634     PetscCall(PetscSFSetGraph(fromCoarse, numRootsFrom, numLeavesTo, NULL, PETSC_OWN_POINTER, fromRoots, PETSC_OWN_POINTER));
635   } else PetscCall(PetscSFSetGraph(fromCoarse, numRootsFrom, numLeavesTo, toLeaves, PETSC_OWN_POINTER, fromRoots, PETSC_OWN_POINTER));
636   *fromCoarseToFine = fromCoarse;
637   if (toCoarseFromFine) {
638     PetscCall(PetscSFSetGraph(toCoarse, numRootsTo, numLeavesFrom, fromLeaves, PETSC_OWN_POINTER, toRoots, PETSC_OWN_POINTER));
639     *toCoarseFromFine = toCoarse;
640   }
641   PetscFunctionReturn(PETSC_SUCCESS);
642 }
643 
644 /* range of processes whose B sections overlap this ranks A section */
645 static PetscErrorCode DMPforestComputeOverlappingRanks(PetscMPIInt size, PetscMPIInt rank, p4est_t *p4estA, p4est_t *p4estB, PetscInt *startB, PetscInt *endB)
646 {
647   p4est_quadrant_t *myCoarseStart = &(p4estA->global_first_position[rank]);
648   p4est_quadrant_t *myCoarseEnd   = &(p4estA->global_first_position[rank + 1]);
649   p4est_quadrant_t *globalFirstB  = p4estB->global_first_position;
650 
651   PetscFunctionBegin;
652   *startB = -1;
653   *endB   = -1;
654   if (p4estA->local_num_quadrants) {
655     PetscInt lo, hi, guess;
656     /* binary search to find interval containing myCoarseStart */
657     lo    = 0;
658     hi    = size;
659     guess = rank;
660     while (1) {
661       int startCompMy, myCompEnd;
662 
663       PetscCallP4estReturn(startCompMy, p4est_quadrant_compare_piggy, (&globalFirstB[guess], myCoarseStart));
664       PetscCallP4estReturn(myCompEnd, p4est_quadrant_compare_piggy, (myCoarseStart, &globalFirstB[guess + 1]));
665       if (startCompMy <= 0 && myCompEnd < 0) {
666         *startB = guess;
667         break;
668       } else if (startCompMy > 0) { /* guess is to high */
669         hi = guess;
670       } else { /* guess is to low */
671         lo = guess + 1;
672       }
673       guess = lo + (hi - lo) / 2;
674     }
675     /* reset bounds, but not guess */
676     lo = 0;
677     hi = size;
678     while (1) {
679       int startCompMy, myCompEnd;
680 
681       PetscCallP4estReturn(startCompMy, p4est_quadrant_compare_piggy, (&globalFirstB[guess], myCoarseEnd));
682       PetscCallP4estReturn(myCompEnd, p4est_quadrant_compare_piggy, (myCoarseEnd, &globalFirstB[guess + 1]));
683       if (startCompMy < 0 && myCompEnd <= 0) { /* notice that the comparison operators are different from above */
684         *endB = guess + 1;
685         break;
686       } else if (startCompMy >= 0) { /* guess is to high */
687         hi = guess;
688       } else { /* guess is to low */
689         lo = guess + 1;
690       }
691       guess = lo + (hi - lo) / 2;
692     }
693   }
694   PetscFunctionReturn(PETSC_SUCCESS);
695 }
696 
697 static PetscErrorCode DMPforestGetPlex(DM, DM *);
698 
699   #define DMSetUp_pforest _append_pforest(DMSetUp)
700 static PetscErrorCode DMSetUp_pforest(DM dm)
701 {
702   DM_Forest         *forest  = (DM_Forest *)dm->data;
703   DM_Forest_pforest *pforest = (DM_Forest_pforest *)forest->data;
704   DM                 base, adaptFrom;
705   DMForestTopology   topoName;
706   PetscSF            preCoarseToFine = NULL, coarseToPreFine = NULL;
707   PforestAdaptCtx    ctx;
708 
709   PetscFunctionBegin;
710   ctx.minLevel  = PETSC_MAX_INT;
711   ctx.maxLevel  = 0;
712   ctx.currLevel = 0;
713   ctx.anyChange = PETSC_FALSE;
714   /* sanity check */
715   PetscCall(DMForestGetAdaptivityForest(dm, &adaptFrom));
716   PetscCall(DMForestGetBaseDM(dm, &base));
717   PetscCall(DMForestGetTopology(dm, &topoName));
718   PetscCheck(adaptFrom || base || topoName, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "A forest needs a topology, a base DM, or a DM to adapt from");
719 
720   /* === Step 1: DMFTopology === */
721   if (adaptFrom) { /* reference already created topology */
722     PetscBool          ispforest;
723     DM_Forest         *aforest  = (DM_Forest *)adaptFrom->data;
724     DM_Forest_pforest *apforest = (DM_Forest_pforest *)aforest->data;
725 
726     PetscCall(PetscObjectTypeCompare((PetscObject)adaptFrom, DMPFOREST, &ispforest));
727     PetscCheck(ispforest, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_NOTSAMETYPE, "Trying to adapt from %s, which is not %s", ((PetscObject)adaptFrom)->type_name, DMPFOREST);
728     PetscCheck(apforest->topo, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "The pre-adaptation forest must have a topology");
729     PetscCall(DMSetUp(adaptFrom));
730     PetscCall(DMForestGetBaseDM(dm, &base));
731     PetscCall(DMForestGetTopology(dm, &topoName));
732   } else if (base) { /* construct a connectivity from base */
733     PetscBool isPlex, isDA;
734 
735     PetscCall(PetscObjectGetName((PetscObject)base, &topoName));
736     PetscCall(DMForestSetTopology(dm, topoName));
737     PetscCall(PetscObjectTypeCompare((PetscObject)base, DMPLEX, &isPlex));
738     PetscCall(PetscObjectTypeCompare((PetscObject)base, DMDA, &isDA));
739     if (isPlex) {
740       MPI_Comm              comm = PetscObjectComm((PetscObject)dm);
741       PetscInt              depth;
742       PetscMPIInt           size;
743       p4est_connectivity_t *conn = NULL;
744       DMFTopology_pforest  *topo;
745       PetscInt             *tree_face_to_uniq = NULL;
746 
747       PetscCall(DMPlexGetDepth(base, &depth));
748       if (depth == 1) {
749         DM connDM;
750 
751         PetscCall(DMPlexInterpolate(base, &connDM));
752         base = connDM;
753         PetscCall(DMForestSetBaseDM(dm, base));
754         PetscCall(DMDestroy(&connDM));
755       } else PetscCheck(depth == P4EST_DIM, comm, PETSC_ERR_ARG_WRONG, "Base plex is neither interpolated nor uninterpolated? depth %" PetscInt_FMT ", expected 2 or %d", depth, P4EST_DIM + 1);
756       PetscCallMPI(MPI_Comm_size(comm, &size));
757       if (size > 1) {
758         DM      dmRedundant;
759         PetscSF sf;
760 
761         PetscCall(DMPlexGetRedundantDM(base, &sf, &dmRedundant));
762         PetscCheck(dmRedundant, comm, PETSC_ERR_PLIB, "Could not create redundant DM");
763         PetscCall(PetscObjectCompose((PetscObject)dmRedundant, "_base_migration_sf", (PetscObject)sf));
764         PetscCall(PetscSFDestroy(&sf));
765         base = dmRedundant;
766         PetscCall(DMForestSetBaseDM(dm, base));
767         PetscCall(DMDestroy(&dmRedundant));
768       }
769       PetscCall(DMViewFromOptions(base, NULL, "-dm_p4est_base_view"));
770       PetscCall(DMPlexCreateConnectivity_pforest(base, &conn, &tree_face_to_uniq));
771       PetscCall(PetscNew(&topo));
772       topo->refct = 1;
773       topo->conn  = conn;
774       topo->geom  = NULL;
775       {
776         PetscErrorCode (*map)(DM, PetscInt, PetscInt, const PetscReal[], PetscReal[], void *);
777         void *mapCtx;
778 
779         PetscCall(DMForestGetBaseCoordinateMapping(dm, &map, &mapCtx));
780         if (map) {
781           DM_Forest_geometry_pforest *geom_pforest;
782           p4est_geometry_t           *geom;
783 
784           PetscCall(PetscNew(&geom_pforest));
785           PetscCall(DMGetCoordinateDim(dm, &geom_pforest->coordDim));
786           geom_pforest->map    = map;
787           geom_pforest->mapCtx = mapCtx;
788           PetscCallP4estReturn(geom_pforest->inner, p4est_geometry_new_connectivity, (conn));
789           PetscCall(PetscNew(&geom));
790           geom->name    = topoName;
791           geom->user    = geom_pforest;
792           geom->X       = GeometryMapping_pforest;
793           geom->destroy = GeometryDestroy_pforest;
794           topo->geom    = geom;
795         }
796       }
797       topo->tree_face_to_uniq = tree_face_to_uniq;
798       pforest->topo           = topo;
799     } else PetscCheck(!isDA, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Not implemented yet");
800   #if 0
801       PetscInt N[3], P[3];
802 
803       /* get the sizes, periodicities */
804       /* ... */
805                                                                   /* don't use Morton order */
806       PetscCall(DMFTopologyCreateBrick_pforest(dm,N,P,&pforest->topo,PETSC_FALSE));
807   #endif
808     {
809       PetscInt numLabels, l;
810 
811       PetscCall(DMGetNumLabels(base, &numLabels));
812       for (l = 0; l < numLabels; l++) {
813         PetscBool   isDepth, isGhost, isVTK, isDim, isCellType;
814         DMLabel     label, labelNew;
815         PetscInt    defVal;
816         const char *name;
817 
818         PetscCall(DMGetLabelName(base, l, &name));
819         PetscCall(DMGetLabelByNum(base, l, &label));
820         PetscCall(PetscStrcmp(name, "depth", &isDepth));
821         if (isDepth) continue;
822         PetscCall(PetscStrcmp(name, "dim", &isDim));
823         if (isDim) continue;
824         PetscCall(PetscStrcmp(name, "celltype", &isCellType));
825         if (isCellType) continue;
826         PetscCall(PetscStrcmp(name, "ghost", &isGhost));
827         if (isGhost) continue;
828         PetscCall(PetscStrcmp(name, "vtk", &isVTK));
829         if (isVTK) continue;
830         PetscCall(DMCreateLabel(dm, name));
831         PetscCall(DMGetLabel(dm, name, &labelNew));
832         PetscCall(DMLabelGetDefaultValue(label, &defVal));
833         PetscCall(DMLabelSetDefaultValue(labelNew, defVal));
834       }
835       /* map dm points (internal plex) to base
836          we currently create the subpoint_map for the entire hierarchy, starting from the finest forest
837          and propagating back to the coarsest
838          This is not an optimal approach, since we need the map only on the coarsest level
839          during DMForestTransferVecFromBase */
840       PetscCall(DMForestGetMinimumRefinement(dm, &l));
841       if (!l) PetscCall(DMCreateLabel(dm, "_forest_base_subpoint_map"));
842     }
843   } else { /* construct from topology name */
844     DMFTopology_pforest *topo;
845 
846     PetscCall(DMFTopologyCreate_pforest(dm, topoName, &topo));
847     pforest->topo = topo;
848     /* TODO: construct base? */
849   }
850 
851   /* === Step 2: get the leaves of the forest === */
852   if (adaptFrom) { /* start with the old forest */
853     DMLabel            adaptLabel;
854     PetscInt           defaultValue;
855     PetscInt           numValues, numValuesGlobal, cLocalStart, count;
856     DM_Forest         *aforest  = (DM_Forest *)adaptFrom->data;
857     DM_Forest_pforest *apforest = (DM_Forest_pforest *)aforest->data;
858     PetscBool          computeAdaptSF;
859     p4est_topidx_t     flt, llt, t;
860 
861     flt         = apforest->forest->first_local_tree;
862     llt         = apforest->forest->last_local_tree;
863     cLocalStart = apforest->cLocalStart;
864     PetscCall(DMForestGetComputeAdaptivitySF(dm, &computeAdaptSF));
865     PetscCallP4estReturn(pforest->forest, p4est_copy, (apforest->forest, 0)); /* 0 indicates no data copying */
866     PetscCall(DMForestGetAdaptivityLabel(dm, &adaptLabel));
867     if (adaptLabel) {
868       /* apply the refinement/coarsening by flags, plus minimum/maximum refinement */
869       PetscCall(DMLabelGetNumValues(adaptLabel, &numValues));
870       PetscCall(MPIU_Allreduce(&numValues, &numValuesGlobal, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)adaptFrom)));
871       PetscCall(DMLabelGetDefaultValue(adaptLabel, &defaultValue));
872       if (!numValuesGlobal && defaultValue == DM_ADAPT_COARSEN_LAST) { /* uniform coarsen of the last level only (equivalent to DM_ADAPT_COARSEN for conforming grids)  */
873         PetscCall(DMForestGetMinimumRefinement(dm, &ctx.minLevel));
874         PetscCall(DMPforestGetRefinementLevel(dm, &ctx.currLevel));
875         pforest->forest->user_pointer = (void *)&ctx;
876         PetscCallP4est(p4est_coarsen, (pforest->forest, 0, pforest_coarsen_currlevel, NULL));
877         pforest->forest->user_pointer = (void *)dm;
878         PetscCallP4est(p4est_balance, (pforest->forest, P4EST_CONNECT_FULL, NULL));
879         /* we will have to change the offset after we compute the overlap */
880         if (computeAdaptSF) PetscCall(DMPforestComputeLocalCellTransferSF(PetscObjectComm((PetscObject)dm), pforest->forest, 0, apforest->forest, apforest->cLocalStart, &coarseToPreFine, NULL));
881       } else if (!numValuesGlobal && defaultValue == DM_ADAPT_COARSEN) { /* uniform coarsen */
882         PetscCall(DMForestGetMinimumRefinement(dm, &ctx.minLevel));
883         pforest->forest->user_pointer = (void *)&ctx;
884         PetscCallP4est(p4est_coarsen, (pforest->forest, 0, pforest_coarsen_uniform, NULL));
885         pforest->forest->user_pointer = (void *)dm;
886         PetscCallP4est(p4est_balance, (pforest->forest, P4EST_CONNECT_FULL, NULL));
887         /* we will have to change the offset after we compute the overlap */
888         if (computeAdaptSF) PetscCall(DMPforestComputeLocalCellTransferSF(PetscObjectComm((PetscObject)dm), pforest->forest, 0, apforest->forest, apforest->cLocalStart, &coarseToPreFine, NULL));
889       } else if (!numValuesGlobal && defaultValue == DM_ADAPT_REFINE) { /* uniform refine */
890         PetscCall(DMForestGetMaximumRefinement(dm, &ctx.maxLevel));
891         pforest->forest->user_pointer = (void *)&ctx;
892         PetscCallP4est(p4est_refine, (pforest->forest, 0, pforest_refine_uniform, NULL));
893         pforest->forest->user_pointer = (void *)dm;
894         PetscCallP4est(p4est_balance, (pforest->forest, P4EST_CONNECT_FULL, NULL));
895         /* we will have to change the offset after we compute the overlap */
896         if (computeAdaptSF) PetscCall(DMPforestComputeLocalCellTransferSF(PetscObjectComm((PetscObject)dm), apforest->forest, apforest->cLocalStart, pforest->forest, 0, &preCoarseToFine, NULL));
897       } else if (numValuesGlobal) {
898         p4est_t                   *p4est = pforest->forest;
899         PetscInt                  *cellFlags;
900         DMForestAdaptivityStrategy strategy;
901         PetscSF                    cellSF;
902         PetscInt                   c, cStart, cEnd;
903         PetscBool                  adaptAny;
904 
905         PetscCall(DMForestGetMaximumRefinement(dm, &ctx.maxLevel));
906         PetscCall(DMForestGetMinimumRefinement(dm, &ctx.minLevel));
907         PetscCall(DMForestGetAdaptivityStrategy(dm, &strategy));
908         PetscCall(PetscStrncmp(strategy, "any", 3, &adaptAny));
909         PetscCall(DMForestGetCellChart(adaptFrom, &cStart, &cEnd));
910         PetscCall(DMForestGetCellSF(adaptFrom, &cellSF));
911         PetscCall(PetscMalloc1(cEnd - cStart, &cellFlags));
912         for (c = cStart; c < cEnd; c++) PetscCall(DMLabelGetValue(adaptLabel, c, &cellFlags[c - cStart]));
913         if (cellSF) {
914           if (adaptAny) {
915             PetscCall(PetscSFReduceBegin(cellSF, MPIU_INT, cellFlags, cellFlags, MPI_MAX));
916             PetscCall(PetscSFReduceEnd(cellSF, MPIU_INT, cellFlags, cellFlags, MPI_MAX));
917           } else {
918             PetscCall(PetscSFReduceBegin(cellSF, MPIU_INT, cellFlags, cellFlags, MPI_MIN));
919             PetscCall(PetscSFReduceEnd(cellSF, MPIU_INT, cellFlags, cellFlags, MPI_MIN));
920           }
921         }
922         for (t = flt, count = cLocalStart; t <= llt; t++) {
923           p4est_tree_t     *tree     = &(((p4est_tree_t *)p4est->trees->array)[t]);
924           PetscInt          numQuads = (PetscInt)tree->quadrants.elem_count, i;
925           p4est_quadrant_t *quads    = (p4est_quadrant_t *)tree->quadrants.array;
926 
927           for (i = 0; i < numQuads; i++) {
928             p4est_quadrant_t *q = &quads[i];
929             q->p.user_int       = cellFlags[count++];
930           }
931         }
932         PetscCall(PetscFree(cellFlags));
933 
934         pforest->forest->user_pointer = (void *)&ctx;
935         if (adaptAny) PetscCallP4est(p4est_coarsen, (pforest->forest, 0, pforest_coarsen_flag_any, pforest_init_determine));
936         else PetscCallP4est(p4est_coarsen, (pforest->forest, 0, pforest_coarsen_flag_all, pforest_init_determine));
937         PetscCallP4est(p4est_refine, (pforest->forest, 0, pforest_refine_flag, NULL));
938         pforest->forest->user_pointer = (void *)dm;
939         PetscCallP4est(p4est_balance, (pforest->forest, P4EST_CONNECT_FULL, NULL));
940         if (computeAdaptSF) PetscCall(DMPforestComputeLocalCellTransferSF(PetscObjectComm((PetscObject)dm), apforest->forest, apforest->cLocalStart, pforest->forest, 0, &preCoarseToFine, &coarseToPreFine));
941       }
942       for (t = flt, count = cLocalStart; t <= llt; t++) {
943         p4est_tree_t     *atree     = &(((p4est_tree_t *)apforest->forest->trees->array)[t]);
944         p4est_tree_t     *tree      = &(((p4est_tree_t *)pforest->forest->trees->array)[t]);
945         PetscInt          anumQuads = (PetscInt)atree->quadrants.elem_count, i;
946         PetscInt          numQuads  = (PetscInt)tree->quadrants.elem_count;
947         p4est_quadrant_t *aquads    = (p4est_quadrant_t *)atree->quadrants.array;
948         p4est_quadrant_t *quads     = (p4est_quadrant_t *)tree->quadrants.array;
949 
950         if (anumQuads != numQuads) {
951           ctx.anyChange = PETSC_TRUE;
952         } else {
953           for (i = 0; i < numQuads; i++) {
954             p4est_quadrant_t *aq = &aquads[i];
955             p4est_quadrant_t *q  = &quads[i];
956 
957             if (aq->level != q->level) {
958               ctx.anyChange = PETSC_TRUE;
959               break;
960             }
961           }
962         }
963         if (ctx.anyChange) break;
964       }
965     }
966     {
967       PetscInt numLabels, l;
968 
969       PetscCall(DMGetNumLabels(adaptFrom, &numLabels));
970       for (l = 0; l < numLabels; l++) {
971         PetscBool   isDepth, isCellType, isGhost, isVTK;
972         DMLabel     label, labelNew;
973         PetscInt    defVal;
974         const char *name;
975 
976         PetscCall(DMGetLabelName(adaptFrom, l, &name));
977         PetscCall(DMGetLabelByNum(adaptFrom, l, &label));
978         PetscCall(PetscStrcmp(name, "depth", &isDepth));
979         if (isDepth) continue;
980         PetscCall(PetscStrcmp(name, "celltype", &isCellType));
981         if (isCellType) continue;
982         PetscCall(PetscStrcmp(name, "ghost", &isGhost));
983         if (isGhost) continue;
984         PetscCall(PetscStrcmp(name, "vtk", &isVTK));
985         if (isVTK) continue;
986         PetscCall(DMCreateLabel(dm, name));
987         PetscCall(DMGetLabel(dm, name, &labelNew));
988         PetscCall(DMLabelGetDefaultValue(label, &defVal));
989         PetscCall(DMLabelSetDefaultValue(labelNew, defVal));
990       }
991     }
992   } else { /* initial */
993     PetscInt initLevel, minLevel;
994   #if defined(PETSC_HAVE_MPIUNI)
995     sc_MPI_Comm comm = sc_MPI_COMM_WORLD;
996   #else
997     MPI_Comm comm = PetscObjectComm((PetscObject)dm);
998   #endif
999 
1000     PetscCall(DMForestGetInitialRefinement(dm, &initLevel));
1001     PetscCall(DMForestGetMinimumRefinement(dm, &minLevel));
1002     PetscCallP4estReturn(pforest->forest, p4est_new_ext,
1003                          (comm, pforest->topo->conn, 0, /* minimum number of quadrants per processor */
1004                           initLevel,                    /* level of refinement */
1005                           1,                            /* uniform refinement */
1006                           0,                            /* we don't allocate any per quadrant data */
1007                           NULL,                         /* there is no special quadrant initialization */
1008                           (void *)dm));                 /* this dm is the user context */
1009 
1010     if (initLevel > minLevel) pforest->coarsen_hierarchy = PETSC_TRUE;
1011     if (dm->setfromoptionscalled) {
1012       PetscBool   flgPattern, flgFractal;
1013       PetscInt    corner = 0;
1014       PetscInt    corners[P4EST_CHILDREN], ncorner = P4EST_CHILDREN;
1015       PetscReal   likelihood = 1. / P4EST_DIM;
1016       PetscInt    pattern;
1017       const char *prefix;
1018 
1019       PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
1020       PetscCall(PetscOptionsGetEList(((PetscObject)dm)->options, prefix, "-dm_p4est_refine_pattern", DMRefinePatternName, PATTERN_COUNT, &pattern, &flgPattern));
1021       PetscCall(PetscOptionsGetInt(((PetscObject)dm)->options, prefix, "-dm_p4est_refine_corner", &corner, NULL));
1022       PetscCall(PetscOptionsGetIntArray(((PetscObject)dm)->options, prefix, "-dm_p4est_refine_fractal_corners", corners, &ncorner, &flgFractal));
1023       PetscCall(PetscOptionsGetReal(((PetscObject)dm)->options, prefix, "-dm_p4est_refine_hash_likelihood", &likelihood, NULL));
1024 
1025       if (flgPattern) {
1026         DMRefinePatternCtx *ctx;
1027         PetscInt            maxLevel;
1028 
1029         PetscCall(DMForestGetMaximumRefinement(dm, &maxLevel));
1030         PetscCall(PetscNew(&ctx));
1031         ctx->maxLevel = PetscMin(maxLevel, P4EST_QMAXLEVEL);
1032         if (initLevel + ctx->maxLevel > minLevel) pforest->coarsen_hierarchy = PETSC_TRUE;
1033         switch (pattern) {
1034         case PATTERN_HASH:
1035           ctx->refine_fn      = DMRefinePattern_Hash;
1036           ctx->hashLikelihood = likelihood;
1037           break;
1038         case PATTERN_CORNER:
1039           ctx->corner    = corner;
1040           ctx->refine_fn = DMRefinePattern_Corner;
1041           break;
1042         case PATTERN_CENTER:
1043           ctx->refine_fn = DMRefinePattern_Center;
1044           break;
1045         case PATTERN_FRACTAL:
1046           if (flgFractal) {
1047             PetscInt i;
1048 
1049             for (i = 0; i < ncorner; i++) ctx->fractal[corners[i]] = PETSC_TRUE;
1050           } else {
1051   #if !defined(P4_TO_P8)
1052             ctx->fractal[0] = ctx->fractal[1] = ctx->fractal[2] = PETSC_TRUE;
1053   #else
1054             ctx->fractal[0] = ctx->fractal[3] = ctx->fractal[5] = ctx->fractal[6] = PETSC_TRUE;
1055   #endif
1056           }
1057           ctx->refine_fn = DMRefinePattern_Fractal;
1058           break;
1059         default:
1060           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Not a valid refinement pattern");
1061         }
1062 
1063         pforest->forest->user_pointer = (void *)ctx;
1064         PetscCallP4est(p4est_refine, (pforest->forest, 1, ctx->refine_fn, NULL));
1065         PetscCallP4est(p4est_balance, (pforest->forest, P4EST_CONNECT_FULL, NULL));
1066         PetscCall(PetscFree(ctx));
1067         pforest->forest->user_pointer = (void *)dm;
1068       }
1069     }
1070   }
1071   if (pforest->coarsen_hierarchy) {
1072     PetscInt initLevel, currLevel, minLevel;
1073 
1074     PetscCall(DMPforestGetRefinementLevel(dm, &currLevel));
1075     PetscCall(DMForestGetInitialRefinement(dm, &initLevel));
1076     PetscCall(DMForestGetMinimumRefinement(dm, &minLevel));
1077     if (currLevel > minLevel) {
1078       DM_Forest_pforest *coarse_pforest;
1079       DMLabel            coarsen;
1080       DM                 coarseDM;
1081 
1082       PetscCall(DMForestTemplate(dm, MPI_COMM_NULL, &coarseDM));
1083       PetscCall(DMForestSetAdaptivityPurpose(coarseDM, DM_ADAPT_COARSEN));
1084       PetscCall(DMLabelCreate(PETSC_COMM_SELF, "coarsen", &coarsen));
1085       PetscCall(DMLabelSetDefaultValue(coarsen, DM_ADAPT_COARSEN));
1086       PetscCall(DMForestSetAdaptivityLabel(coarseDM, coarsen));
1087       PetscCall(DMLabelDestroy(&coarsen));
1088       PetscCall(DMSetCoarseDM(dm, coarseDM));
1089       PetscCall(PetscObjectDereference((PetscObject)coarseDM));
1090       initLevel = currLevel == initLevel ? initLevel - 1 : initLevel;
1091       PetscCall(DMForestSetInitialRefinement(coarseDM, initLevel));
1092       PetscCall(DMForestSetMinimumRefinement(coarseDM, minLevel));
1093       coarse_pforest                    = (DM_Forest_pforest *)((DM_Forest *)coarseDM->data)->data;
1094       coarse_pforest->coarsen_hierarchy = PETSC_TRUE;
1095     }
1096   }
1097 
1098   { /* repartitioning and overlap */
1099     PetscMPIInt size, rank;
1100 
1101     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1102     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1103     if ((size > 1) && (pforest->partition_for_coarsening || forest->cellWeights || forest->weightCapacity != 1. || forest->weightsFactor != 1.)) {
1104       PetscBool      copyForest  = PETSC_FALSE;
1105       p4est_t       *forest_copy = NULL;
1106       p4est_gloidx_t shipped     = 0;
1107 
1108       if (preCoarseToFine || coarseToPreFine) copyForest = PETSC_TRUE;
1109       if (copyForest) PetscCallP4estReturn(forest_copy, p4est_copy, (pforest->forest, 0));
1110 
1111       if (!forest->cellWeights && forest->weightCapacity == 1. && forest->weightsFactor == 1.) {
1112         PetscCallP4estReturn(shipped, p4est_partition_ext, (pforest->forest, (int)pforest->partition_for_coarsening, NULL));
1113       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Non-uniform partition cases not implemented yet");
1114       if (shipped) ctx.anyChange = PETSC_TRUE;
1115       if (forest_copy) {
1116         if (preCoarseToFine || coarseToPreFine) {
1117           PetscSF        repartSF; /* repartSF has roots in the old partition */
1118           PetscInt       pStart = -1, pEnd = -1, p;
1119           PetscInt       numRoots, numLeaves;
1120           PetscSFNode   *repartRoots;
1121           p4est_gloidx_t postStart  = pforest->forest->global_first_quadrant[rank];
1122           p4est_gloidx_t postEnd    = pforest->forest->global_first_quadrant[rank + 1];
1123           p4est_gloidx_t partOffset = postStart;
1124 
1125           numRoots  = (PetscInt)(forest_copy->global_first_quadrant[rank + 1] - forest_copy->global_first_quadrant[rank]);
1126           numLeaves = (PetscInt)(postEnd - postStart);
1127           PetscCall(DMPforestComputeOverlappingRanks(size, rank, pforest->forest, forest_copy, &pStart, &pEnd));
1128           PetscCall(PetscMalloc1((PetscInt)pforest->forest->local_num_quadrants, &repartRoots));
1129           for (p = pStart; p < pEnd; p++) {
1130             p4est_gloidx_t preStart = forest_copy->global_first_quadrant[p];
1131             p4est_gloidx_t preEnd   = forest_copy->global_first_quadrant[p + 1];
1132             PetscInt       q;
1133 
1134             if (preEnd == preStart) continue;
1135             PetscCheck(preStart <= postStart, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Bad partition overlap computation");
1136             preEnd = preEnd > postEnd ? postEnd : preEnd;
1137             for (q = partOffset; q < preEnd; q++) {
1138               repartRoots[q - postStart].rank  = p;
1139               repartRoots[q - postStart].index = partOffset - preStart;
1140             }
1141             partOffset = preEnd;
1142           }
1143           PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &repartSF));
1144           PetscCall(PetscSFSetGraph(repartSF, numRoots, numLeaves, NULL, PETSC_OWN_POINTER, repartRoots, PETSC_OWN_POINTER));
1145           PetscCall(PetscSFSetUp(repartSF));
1146           if (preCoarseToFine) {
1147             PetscSF         repartSFembed, preCoarseToFineNew;
1148             PetscInt        nleaves;
1149             const PetscInt *leaves;
1150 
1151             PetscCall(PetscSFSetUp(preCoarseToFine));
1152             PetscCall(PetscSFGetGraph(preCoarseToFine, NULL, &nleaves, &leaves, NULL));
1153             if (leaves) {
1154               PetscCall(PetscSFCreateEmbeddedRootSF(repartSF, nleaves, leaves, &repartSFembed));
1155             } else {
1156               repartSFembed = repartSF;
1157               PetscCall(PetscObjectReference((PetscObject)repartSFembed));
1158             }
1159             PetscCall(PetscSFCompose(preCoarseToFine, repartSFembed, &preCoarseToFineNew));
1160             PetscCall(PetscSFDestroy(&preCoarseToFine));
1161             PetscCall(PetscSFDestroy(&repartSFembed));
1162             preCoarseToFine = preCoarseToFineNew;
1163           }
1164           if (coarseToPreFine) {
1165             PetscSF repartSFinv, coarseToPreFineNew;
1166 
1167             PetscCall(PetscSFCreateInverseSF(repartSF, &repartSFinv));
1168             PetscCall(PetscSFCompose(repartSFinv, coarseToPreFine, &coarseToPreFineNew));
1169             PetscCall(PetscSFDestroy(&coarseToPreFine));
1170             PetscCall(PetscSFDestroy(&repartSFinv));
1171             coarseToPreFine = coarseToPreFineNew;
1172           }
1173           PetscCall(PetscSFDestroy(&repartSF));
1174         }
1175         PetscCallP4est(p4est_destroy, (forest_copy));
1176       }
1177     }
1178     if (size > 1) {
1179       PetscInt overlap;
1180 
1181       PetscCall(DMForestGetPartitionOverlap(dm, &overlap));
1182 
1183       if (adaptFrom) {
1184         PetscInt aoverlap;
1185 
1186         PetscCall(DMForestGetPartitionOverlap(adaptFrom, &aoverlap));
1187         if (aoverlap != overlap) ctx.anyChange = PETSC_TRUE;
1188       }
1189 
1190       if (overlap > 0) {
1191         PetscInt i, cLocalStart;
1192         PetscInt cEnd;
1193         PetscSF  preCellSF = NULL, cellSF = NULL;
1194 
1195         PetscCallP4estReturn(pforest->ghost, p4est_ghost_new, (pforest->forest, P4EST_CONNECT_FULL));
1196         PetscCallP4estReturn(pforest->lnodes, p4est_lnodes_new, (pforest->forest, pforest->ghost, -P4EST_DIM));
1197         PetscCallP4est(p4est_ghost_support_lnodes, (pforest->forest, pforest->lnodes, pforest->ghost));
1198         for (i = 1; i < overlap; i++) PetscCallP4est(p4est_ghost_expand_by_lnodes, (pforest->forest, pforest->lnodes, pforest->ghost));
1199 
1200         cLocalStart = pforest->cLocalStart = pforest->ghost->proc_offsets[rank];
1201         cEnd                               = pforest->forest->local_num_quadrants + pforest->ghost->proc_offsets[size];
1202 
1203         /* shift sfs by cLocalStart, expand by cell SFs */
1204         if (preCoarseToFine || coarseToPreFine) {
1205           if (adaptFrom) PetscCall(DMForestGetCellSF(adaptFrom, &preCellSF));
1206           dm->setupcalled = PETSC_TRUE;
1207           PetscCall(DMForestGetCellSF(dm, &cellSF));
1208         }
1209         if (preCoarseToFine) {
1210           PetscSF            preCoarseToFineNew;
1211           PetscInt           nleaves, nroots, *leavesNew, i, nleavesNew;
1212           const PetscInt    *leaves;
1213           const PetscSFNode *remotes;
1214           PetscSFNode       *remotesAll;
1215 
1216           PetscCall(PetscSFSetUp(preCoarseToFine));
1217           PetscCall(PetscSFGetGraph(preCoarseToFine, &nroots, &nleaves, &leaves, &remotes));
1218           PetscCall(PetscMalloc1(cEnd, &remotesAll));
1219           for (i = 0; i < cEnd; i++) {
1220             remotesAll[i].rank  = -1;
1221             remotesAll[i].index = -1;
1222           }
1223           for (i = 0; i < nleaves; i++) remotesAll[(leaves ? leaves[i] : i) + cLocalStart] = remotes[i];
1224           PetscCall(PetscSFSetUp(cellSF));
1225           PetscCall(PetscSFBcastBegin(cellSF, MPIU_2INT, remotesAll, remotesAll, MPI_REPLACE));
1226           PetscCall(PetscSFBcastEnd(cellSF, MPIU_2INT, remotesAll, remotesAll, MPI_REPLACE));
1227           nleavesNew = 0;
1228           for (i = 0; i < nleaves; i++) {
1229             if (remotesAll[i].rank >= 0) nleavesNew++;
1230           }
1231           PetscCall(PetscMalloc1(nleavesNew, &leavesNew));
1232           nleavesNew = 0;
1233           for (i = 0; i < nleaves; i++) {
1234             if (remotesAll[i].rank >= 0) {
1235               leavesNew[nleavesNew] = i;
1236               if (i > nleavesNew) remotesAll[nleavesNew] = remotesAll[i];
1237               nleavesNew++;
1238             }
1239           }
1240           PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &preCoarseToFineNew));
1241           if (nleavesNew < cEnd) {
1242             PetscCall(PetscSFSetGraph(preCoarseToFineNew, nroots, nleavesNew, leavesNew, PETSC_OWN_POINTER, remotesAll, PETSC_COPY_VALUES));
1243           } else { /* all cells are leaves */
1244             PetscCall(PetscFree(leavesNew));
1245             PetscCall(PetscSFSetGraph(preCoarseToFineNew, nroots, nleavesNew, NULL, PETSC_OWN_POINTER, remotesAll, PETSC_COPY_VALUES));
1246           }
1247           PetscCall(PetscFree(remotesAll));
1248           PetscCall(PetscSFDestroy(&preCoarseToFine));
1249           preCoarseToFine = preCoarseToFineNew;
1250           preCoarseToFine = preCoarseToFineNew;
1251         }
1252         if (coarseToPreFine) {
1253           PetscSF            coarseToPreFineNew;
1254           PetscInt           nleaves, nroots, i, nleavesCellSF, nleavesExpanded, *leavesNew;
1255           const PetscInt    *leaves;
1256           const PetscSFNode *remotes;
1257           PetscSFNode       *remotesNew, *remotesNewRoot, *remotesExpanded;
1258 
1259           PetscCall(PetscSFSetUp(coarseToPreFine));
1260           PetscCall(PetscSFGetGraph(coarseToPreFine, &nroots, &nleaves, &leaves, &remotes));
1261           PetscCall(PetscSFGetGraph(preCellSF, NULL, &nleavesCellSF, NULL, NULL));
1262           PetscCall(PetscMalloc1(nroots, &remotesNewRoot));
1263           PetscCall(PetscMalloc1(nleaves, &remotesNew));
1264           for (i = 0; i < nroots; i++) {
1265             remotesNewRoot[i].rank  = rank;
1266             remotesNewRoot[i].index = i + cLocalStart;
1267           }
1268           PetscCall(PetscSFBcastBegin(coarseToPreFine, MPIU_2INT, remotesNewRoot, remotesNew, MPI_REPLACE));
1269           PetscCall(PetscSFBcastEnd(coarseToPreFine, MPIU_2INT, remotesNewRoot, remotesNew, MPI_REPLACE));
1270           PetscCall(PetscFree(remotesNewRoot));
1271           PetscCall(PetscMalloc1(nleavesCellSF, &remotesExpanded));
1272           for (i = 0; i < nleavesCellSF; i++) {
1273             remotesExpanded[i].rank  = -1;
1274             remotesExpanded[i].index = -1;
1275           }
1276           for (i = 0; i < nleaves; i++) remotesExpanded[leaves ? leaves[i] : i] = remotesNew[i];
1277           PetscCall(PetscFree(remotesNew));
1278           PetscCall(PetscSFBcastBegin(preCellSF, MPIU_2INT, remotesExpanded, remotesExpanded, MPI_REPLACE));
1279           PetscCall(PetscSFBcastEnd(preCellSF, MPIU_2INT, remotesExpanded, remotesExpanded, MPI_REPLACE));
1280 
1281           nleavesExpanded = 0;
1282           for (i = 0; i < nleavesCellSF; i++) {
1283             if (remotesExpanded[i].rank >= 0) nleavesExpanded++;
1284           }
1285           PetscCall(PetscMalloc1(nleavesExpanded, &leavesNew));
1286           nleavesExpanded = 0;
1287           for (i = 0; i < nleavesCellSF; i++) {
1288             if (remotesExpanded[i].rank >= 0) {
1289               leavesNew[nleavesExpanded] = i;
1290               if (i > nleavesExpanded) remotesExpanded[nleavesExpanded] = remotes[i];
1291               nleavesExpanded++;
1292             }
1293           }
1294           PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &coarseToPreFineNew));
1295           if (nleavesExpanded < nleavesCellSF) {
1296             PetscCall(PetscSFSetGraph(coarseToPreFineNew, cEnd, nleavesExpanded, leavesNew, PETSC_OWN_POINTER, remotesExpanded, PETSC_COPY_VALUES));
1297           } else {
1298             PetscCall(PetscFree(leavesNew));
1299             PetscCall(PetscSFSetGraph(coarseToPreFineNew, cEnd, nleavesExpanded, NULL, PETSC_OWN_POINTER, remotesExpanded, PETSC_COPY_VALUES));
1300           }
1301           PetscCall(PetscFree(remotesExpanded));
1302           PetscCall(PetscSFDestroy(&coarseToPreFine));
1303           coarseToPreFine = coarseToPreFineNew;
1304         }
1305       }
1306     }
1307   }
1308   forest->preCoarseToFine = preCoarseToFine;
1309   forest->coarseToPreFine = coarseToPreFine;
1310   dm->setupcalled         = PETSC_TRUE;
1311   PetscCall(MPIU_Allreduce(&ctx.anyChange, &(pforest->adaptivitySuccess), 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
1312   PetscCall(DMPforestGetPlex(dm, NULL));
1313   PetscFunctionReturn(PETSC_SUCCESS);
1314 }
1315 
1316   #define DMForestGetAdaptivitySuccess_pforest _append_pforest(DMForestGetAdaptivitySuccess)
1317 static PetscErrorCode DMForestGetAdaptivitySuccess_pforest(DM dm, PetscBool *success)
1318 {
1319   DM_Forest         *forest;
1320   DM_Forest_pforest *pforest;
1321 
1322   PetscFunctionBegin;
1323   forest   = (DM_Forest *)dm->data;
1324   pforest  = (DM_Forest_pforest *)forest->data;
1325   *success = pforest->adaptivitySuccess;
1326   PetscFunctionReturn(PETSC_SUCCESS);
1327 }
1328 
1329   #define DMView_ASCII_pforest _append_pforest(DMView_ASCII)
1330 static PetscErrorCode DMView_ASCII_pforest(PetscObject odm, PetscViewer viewer)
1331 {
1332   DM dm = (DM)odm;
1333 
1334   PetscFunctionBegin;
1335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1336   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1337   PetscCall(DMSetUp(dm));
1338   switch (viewer->format) {
1339   case PETSC_VIEWER_DEFAULT:
1340   case PETSC_VIEWER_ASCII_INFO: {
1341     PetscInt    dim;
1342     const char *name;
1343 
1344     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1345     PetscCall(DMGetDimension(dm, &dim));
1346     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "Forest %s in %" PetscInt_FMT " dimensions:\n", name, dim));
1347     else PetscCall(PetscViewerASCIIPrintf(viewer, "Forest in %" PetscInt_FMT " dimensions:\n", dim));
1348   } /* fall through */
1349   case PETSC_VIEWER_ASCII_INFO_DETAIL:
1350   case PETSC_VIEWER_LOAD_BALANCE: {
1351     DM plex;
1352 
1353     PetscCall(DMPforestGetPlex(dm, &plex));
1354     PetscCall(DMView(plex, viewer));
1355   } break;
1356   default:
1357     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "No support for format '%s'", PetscViewerFormats[viewer->format]);
1358   }
1359   PetscFunctionReturn(PETSC_SUCCESS);
1360 }
1361 
1362   #define DMView_VTK_pforest _append_pforest(DMView_VTK)
1363 static PetscErrorCode DMView_VTK_pforest(PetscObject odm, PetscViewer viewer)
1364 {
1365   DM                 dm      = (DM)odm;
1366   DM_Forest         *forest  = (DM_Forest *)dm->data;
1367   DM_Forest_pforest *pforest = (DM_Forest_pforest *)forest->data;
1368   PetscBool          isvtk;
1369   PetscReal          vtkScale = 1. - PETSC_MACHINE_EPSILON;
1370   PetscViewer_VTK   *vtk      = (PetscViewer_VTK *)viewer->data;
1371   const char        *name;
1372   char              *filenameStrip = NULL;
1373   PetscBool          hasExt;
1374   size_t             len;
1375   p4est_geometry_t  *geom;
1376 
1377   PetscFunctionBegin;
1378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1379   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1380   PetscCall(DMSetUp(dm));
1381   geom = pforest->topo->geom;
1382   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1383   PetscCheck(isvtk, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_INCOMP, "Cannot use viewer type %s", ((PetscObject)viewer)->type_name);
1384   switch (viewer->format) {
1385   case PETSC_VIEWER_VTK_VTU:
1386     PetscCheck(pforest->forest, PetscObjectComm(odm), PETSC_ERR_ARG_WRONG, "DM has not been setup with a valid forest");
1387     name = vtk->filename;
1388     PetscCall(PetscStrlen(name, &len));
1389     PetscCall(PetscStrcasecmp(name + len - 4, ".vtu", &hasExt));
1390     if (hasExt) {
1391       PetscCall(PetscStrallocpy(name, &filenameStrip));
1392       filenameStrip[len - 4] = '\0';
1393       name                   = filenameStrip;
1394     }
1395     if (!pforest->topo->geom) PetscCallP4estReturn(geom, p4est_geometry_new_connectivity, (pforest->topo->conn));
1396     {
1397       p4est_vtk_context_t *pvtk;
1398       int                  footerr;
1399 
1400       PetscCallP4estReturn(pvtk, p4est_vtk_context_new, (pforest->forest, name));
1401       PetscCallP4est(p4est_vtk_context_set_geom, (pvtk, geom));
1402       PetscCallP4est(p4est_vtk_context_set_scale, (pvtk, (double)vtkScale));
1403       PetscCallP4estReturn(pvtk, p4est_vtk_write_header, (pvtk));
1404       PetscCheck(pvtk, PetscObjectComm((PetscObject)odm), PETSC_ERR_LIB, P4EST_STRING "_vtk_write_header() failed");
1405       PetscCallP4estReturn(pvtk, p4est_vtk_write_cell_dataf,
1406                            (pvtk, 1, /* write tree */
1407                             1,       /* write level */
1408                             1,       /* write rank */
1409                             0,       /* do not wrap rank */
1410                             0,       /* no scalar fields */
1411                             0,       /* no vector fields */
1412                             pvtk));
1413       PetscCheck(pvtk, PetscObjectComm((PetscObject)odm), PETSC_ERR_LIB, P4EST_STRING "_vtk_write_cell_dataf() failed");
1414       PetscCallP4estReturn(footerr, p4est_vtk_write_footer, (pvtk));
1415       PetscCheck(!footerr, PetscObjectComm((PetscObject)odm), PETSC_ERR_LIB, P4EST_STRING "_vtk_write_footer() failed");
1416     }
1417     if (!pforest->topo->geom) PetscCallP4est(p4est_geometry_destroy, (geom));
1418     PetscCall(PetscFree(filenameStrip));
1419     break;
1420   default:
1421     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "No support for format '%s'", PetscViewerFormats[viewer->format]);
1422   }
1423   PetscFunctionReturn(PETSC_SUCCESS);
1424 }
1425 
1426   #define DMView_HDF5_pforest _append_pforest(DMView_HDF5)
1427 static PetscErrorCode DMView_HDF5_pforest(DM dm, PetscViewer viewer)
1428 {
1429   DM plex;
1430 
1431   PetscFunctionBegin;
1432   PetscCall(DMSetUp(dm));
1433   PetscCall(DMPforestGetPlex(dm, &plex));
1434   PetscCall(DMView(plex, viewer));
1435   PetscFunctionReturn(PETSC_SUCCESS);
1436 }
1437 
1438   #define DMView_GLVis_pforest _append_pforest(DMView_GLVis)
1439 static PetscErrorCode DMView_GLVis_pforest(DM dm, PetscViewer viewer)
1440 {
1441   DM plex;
1442 
1443   PetscFunctionBegin;
1444   PetscCall(DMSetUp(dm));
1445   PetscCall(DMPforestGetPlex(dm, &plex));
1446   PetscCall(DMView(plex, viewer));
1447   PetscFunctionReturn(PETSC_SUCCESS);
1448 }
1449 
1450   #define DMView_pforest _append_pforest(DMView)
1451 static PetscErrorCode DMView_pforest(DM dm, PetscViewer viewer)
1452 {
1453   PetscBool isascii, isvtk, ishdf5, isglvis;
1454 
1455   PetscFunctionBegin;
1456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1457   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
1459   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1460   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1461   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1462   if (isascii) {
1463     PetscCall(DMView_ASCII_pforest((PetscObject)dm, viewer));
1464   } else if (isvtk) {
1465     PetscCall(DMView_VTK_pforest((PetscObject)dm, viewer));
1466   } else if (ishdf5) {
1467     PetscCall(DMView_HDF5_pforest(dm, viewer));
1468   } else if (isglvis) {
1469     PetscCall(DMView_GLVis_pforest(dm, viewer));
1470   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer not supported (not VTK, HDF5, or GLVis)");
1471   PetscFunctionReturn(PETSC_SUCCESS);
1472 }
1473 
1474 static PetscErrorCode PforestConnectivityEnumerateFacets(p4est_connectivity_t *conn, PetscInt **tree_face_to_uniq)
1475 {
1476   PetscInt *ttf, f, t, g, count;
1477   PetscInt  numFacets;
1478 
1479   PetscFunctionBegin;
1480   numFacets = conn->num_trees * P4EST_FACES;
1481   PetscCall(PetscMalloc1(numFacets, &ttf));
1482   for (f = 0; f < numFacets; f++) ttf[f] = -1;
1483   for (g = 0, count = 0, t = 0; t < conn->num_trees; t++) {
1484     for (f = 0; f < P4EST_FACES; f++, g++) {
1485       if (ttf[g] == -1) {
1486         PetscInt ng;
1487 
1488         ttf[g]  = count++;
1489         ng      = conn->tree_to_tree[g] * P4EST_FACES + (conn->tree_to_face[g] % P4EST_FACES);
1490         ttf[ng] = ttf[g];
1491       }
1492     }
1493   }
1494   *tree_face_to_uniq = ttf;
1495   PetscFunctionReturn(PETSC_SUCCESS);
1496 }
1497 
1498 static PetscErrorCode DMPlexCreateConnectivity_pforest(DM dm, p4est_connectivity_t **connOut, PetscInt **tree_face_to_uniq)
1499 {
1500   p4est_topidx_t numTrees, numVerts, numCorns, numCtt;
1501   PetscSection   ctt;
1502   #if defined(P4_TO_P8)
1503   p4est_topidx_t numEdges, numEtt;
1504   PetscSection   ett;
1505   PetscInt       eStart, eEnd, e, ettSize;
1506   PetscInt       vertOff = 1 + P4EST_FACES + P8EST_EDGES;
1507   PetscInt       edgeOff = 1 + P4EST_FACES;
1508   #else
1509   PetscInt vertOff = 1 + P4EST_FACES;
1510   #endif
1511   p4est_connectivity_t *conn;
1512   PetscInt              cStart, cEnd, c, vStart, vEnd, v, fStart, fEnd, f;
1513   PetscInt             *star = NULL, *closure = NULL, closureSize, starSize, cttSize;
1514   PetscInt             *ttf;
1515 
1516   PetscFunctionBegin;
1517   /* 1: count objects, allocate */
1518   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1519   PetscCall(P4estTopidxCast(cEnd - cStart, &numTrees));
1520   numVerts = P4EST_CHILDREN * numTrees;
1521   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1522   PetscCall(P4estTopidxCast(vEnd - vStart, &numCorns));
1523   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &ctt));
1524   PetscCall(PetscSectionSetChart(ctt, vStart, vEnd));
1525   for (v = vStart; v < vEnd; v++) {
1526     PetscInt s;
1527 
1528     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1529     for (s = 0; s < starSize; s++) {
1530       PetscInt p = star[2 * s];
1531 
1532       if (p >= cStart && p < cEnd) {
1533         /* we want to count every time cell p references v, so we see how many times it comes up in the closure.  This
1534          * only protects against periodicity problems */
1535         PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1536         PetscCheck(closureSize == P4EST_INSUL, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Cell %" PetscInt_FMT " with wrong closure size %" PetscInt_FMT " != %d", p, closureSize, P4EST_INSUL);
1537         for (c = 0; c < P4EST_CHILDREN; c++) {
1538           PetscInt cellVert = closure[2 * (c + vertOff)];
1539 
1540           PetscCheck(cellVert >= vStart && cellVert < vEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Non-standard closure: vertices");
1541           if (cellVert == v) PetscCall(PetscSectionAddDof(ctt, v, 1));
1542         }
1543         PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1544       }
1545     }
1546     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1547   }
1548   PetscCall(PetscSectionSetUp(ctt));
1549   PetscCall(PetscSectionGetStorageSize(ctt, &cttSize));
1550   PetscCall(P4estTopidxCast(cttSize, &numCtt));
1551   #if defined(P4_TO_P8)
1552   PetscCall(DMPlexGetSimplexOrBoxCells(dm, P4EST_DIM - 1, &eStart, &eEnd));
1553   PetscCall(P4estTopidxCast(eEnd - eStart, &numEdges));
1554   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &ett));
1555   PetscCall(PetscSectionSetChart(ett, eStart, eEnd));
1556   for (e = eStart; e < eEnd; e++) {
1557     PetscInt s;
1558 
1559     PetscCall(DMPlexGetTransitiveClosure(dm, e, PETSC_FALSE, &starSize, &star));
1560     for (s = 0; s < starSize; s++) {
1561       PetscInt p = star[2 * s];
1562 
1563       if (p >= cStart && p < cEnd) {
1564         /* we want to count every time cell p references e, so we see how many times it comes up in the closure.  This
1565          * only protects against periodicity problems */
1566         PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1567         PetscCheck(closureSize == P4EST_INSUL, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Cell with wrong closure size");
1568         for (c = 0; c < P8EST_EDGES; c++) {
1569           PetscInt cellEdge = closure[2 * (c + edgeOff)];
1570 
1571           PetscCheck(cellEdge >= eStart && cellEdge < eEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Non-standard closure: edges");
1572           if (cellEdge == e) PetscCall(PetscSectionAddDof(ett, e, 1));
1573         }
1574         PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1575       }
1576     }
1577     PetscCall(DMPlexRestoreTransitiveClosure(dm, e, PETSC_FALSE, &starSize, &star));
1578   }
1579   PetscCall(PetscSectionSetUp(ett));
1580   PetscCall(PetscSectionGetStorageSize(ett, &ettSize));
1581   PetscCall(P4estTopidxCast(ettSize, &numEtt));
1582 
1583   /* This routine allocates space for the arrays, which we fill below */
1584   PetscCallP4estReturn(conn, p8est_connectivity_new, (numVerts, numTrees, numEdges, numEtt, numCorns, numCtt));
1585   #else
1586   PetscCallP4estReturn(conn, p4est_connectivity_new, (numVerts, numTrees, numCorns, numCtt));
1587   #endif
1588 
1589   /* 2: visit every face, determine neighboring cells(trees) */
1590   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 1, &fStart, &fEnd));
1591   PetscCall(PetscMalloc1((cEnd - cStart) * P4EST_FACES, &ttf));
1592   for (f = fStart; f < fEnd; f++) {
1593     PetscInt        numSupp, s;
1594     PetscInt        myFace[2] = {-1, -1};
1595     PetscInt        myOrnt[2] = {PETSC_MIN_INT, PETSC_MIN_INT};
1596     const PetscInt *supp;
1597 
1598     PetscCall(DMPlexGetSupportSize(dm, f, &numSupp));
1599     PetscCheck(numSupp == 1 || numSupp == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "point %" PetscInt_FMT " has facet with %" PetscInt_FMT " sides: must be 1 or 2 (boundary or conformal)", f, numSupp);
1600     PetscCall(DMPlexGetSupport(dm, f, &supp));
1601 
1602     for (s = 0; s < numSupp; s++) {
1603       PetscInt p = supp[s];
1604 
1605       if (p >= cEnd) {
1606         numSupp--;
1607         if (s) supp = &supp[1 - s];
1608         break;
1609       }
1610     }
1611     for (s = 0; s < numSupp; s++) {
1612       PetscInt        p = supp[s], i;
1613       PetscInt        numCone;
1614       DMPolytopeType  ct;
1615       const PetscInt *cone;
1616       const PetscInt *ornt;
1617       PetscInt        orient = PETSC_MIN_INT;
1618 
1619       PetscCall(DMPlexGetConeSize(dm, p, &numCone));
1620       PetscCheck(numCone == P4EST_FACES, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "cell %" PetscInt_FMT " has %" PetscInt_FMT " facets, expect %d", p, numCone, P4EST_FACES);
1621       PetscCall(DMPlexGetCone(dm, p, &cone));
1622       PetscCall(DMPlexGetCellType(dm, cone[0], &ct));
1623       PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
1624       for (i = 0; i < P4EST_FACES; i++) {
1625         if (cone[i] == f) {
1626           orient = DMPolytopeConvertNewOrientation_Internal(ct, ornt[i]);
1627           break;
1628         }
1629       }
1630       PetscCheck(i < P4EST_FACES, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "cell %" PetscInt_FMT " faced %" PetscInt_FMT " mismatch", p, f);
1631       if (p < cStart || p >= cEnd) {
1632         DMPolytopeType ct;
1633         PetscCall(DMPlexGetCellType(dm, p, &ct));
1634         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "cell %" PetscInt_FMT " (%s) should be in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, DMPolytopeTypes[ct], cStart, cEnd);
1635       }
1636       ttf[P4EST_FACES * (p - cStart) + PetscFaceToP4estFace[i]] = f - fStart;
1637       if (numSupp == 1) {
1638         /* boundary faces indicated by self reference */
1639         conn->tree_to_tree[P4EST_FACES * (p - cStart) + PetscFaceToP4estFace[i]] = p - cStart;
1640         conn->tree_to_face[P4EST_FACES * (p - cStart) + PetscFaceToP4estFace[i]] = (int8_t)PetscFaceToP4estFace[i];
1641       } else {
1642         const PetscInt N = P4EST_CHILDREN / 2;
1643 
1644         conn->tree_to_tree[P4EST_FACES * (p - cStart) + PetscFaceToP4estFace[i]] = supp[1 - s] - cStart;
1645         myFace[s]                                                                = PetscFaceToP4estFace[i];
1646         /* get the orientation of cell p in p4est-type closure to facet f, by composing the p4est-closure to
1647          * petsc-closure permutation and the petsc-closure to facet orientation */
1648         myOrnt[s] = DihedralCompose(N, orient, DMPolytopeConvertNewOrientation_Internal(ct, P4estFaceToPetscOrnt[myFace[s]]));
1649       }
1650     }
1651     if (numSupp == 2) {
1652       for (s = 0; s < numSupp; s++) {
1653         PetscInt       p = supp[s];
1654         PetscInt       orntAtoB;
1655         PetscInt       p4estOrient;
1656         const PetscInt N = P4EST_CHILDREN / 2;
1657 
1658         /* composing the forward permutation with the other cell's inverse permutation gives the self-to-neighbor
1659          * permutation of this cell-facet's cone */
1660         orntAtoB = DihedralCompose(N, DihedralInvert(N, myOrnt[1 - s]), myOrnt[s]);
1661 
1662         /* convert cone-description permutation (i.e., edges around facet) to cap-description permutation (i.e.,
1663          * vertices around facet) */
1664   #if !defined(P4_TO_P8)
1665         p4estOrient = orntAtoB < 0 ? -(orntAtoB + 1) : orntAtoB;
1666   #else
1667         {
1668           PetscInt firstVert      = orntAtoB < 0 ? ((-orntAtoB) % N) : orntAtoB;
1669           PetscInt p4estFirstVert = firstVert < 2 ? firstVert : (firstVert ^ 1);
1670 
1671           /* swap bits */
1672           p4estOrient = ((myFace[s] <= myFace[1 - s]) || (orntAtoB < 0)) ? p4estFirstVert : ((p4estFirstVert >> 1) | ((p4estFirstVert & 1) << 1));
1673         }
1674   #endif
1675         /* encode neighbor face and orientation in tree_to_face per p4est_connectivity standard (see
1676          * p4est_connectivity.h, p8est_connectivity.h) */
1677         conn->tree_to_face[P4EST_FACES * (p - cStart) + myFace[s]] = (int8_t)myFace[1 - s] + p4estOrient * P4EST_FACES;
1678       }
1679     }
1680   }
1681 
1682   #if defined(P4_TO_P8)
1683   /* 3: visit every edge */
1684   conn->ett_offset[0] = 0;
1685   for (e = eStart; e < eEnd; e++) {
1686     PetscInt off, s;
1687 
1688     PetscCall(PetscSectionGetOffset(ett, e, &off));
1689     conn->ett_offset[e - eStart] = (p4est_topidx_t)off;
1690     PetscCall(DMPlexGetTransitiveClosure(dm, e, PETSC_FALSE, &starSize, &star));
1691     for (s = 0; s < starSize; s++) {
1692       PetscInt p = star[2 * s];
1693 
1694       if (p >= cStart && p < cEnd) {
1695         PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1696         PetscCheck(closureSize == P4EST_INSUL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Non-standard closure");
1697         for (c = 0; c < P8EST_EDGES; c++) {
1698           PetscInt       cellEdge = closure[2 * (c + edgeOff)];
1699           PetscInt       cellOrnt = closure[2 * (c + edgeOff) + 1];
1700           DMPolytopeType ct;
1701 
1702           PetscCall(DMPlexGetCellType(dm, cellEdge, &ct));
1703           cellOrnt = DMPolytopeConvertNewOrientation_Internal(ct, cellOrnt);
1704           if (cellEdge == e) {
1705             PetscInt p4estEdge = PetscEdgeToP4estEdge[c];
1706             PetscInt totalOrient;
1707 
1708             /* compose p4est-closure to petsc-closure permutation and petsc-closure to edge orientation */
1709             totalOrient = DihedralCompose(2, cellOrnt, DMPolytopeConvertNewOrientation_Internal(DM_POLYTOPE_SEGMENT, P4estEdgeToPetscOrnt[p4estEdge]));
1710             /* p4est orientations are positive: -2 => 1, -1 => 0 */
1711             totalOrient             = (totalOrient < 0) ? -(totalOrient + 1) : totalOrient;
1712             conn->edge_to_tree[off] = (p4est_locidx_t)(p - cStart);
1713             /* encode cell-edge and orientation in edge_to_edge per p8est_connectivity standard (see
1714              * p8est_connectivity.h) */
1715             conn->edge_to_edge[off++]                                  = (int8_t)p4estEdge + P8EST_EDGES * totalOrient;
1716             conn->tree_to_edge[P8EST_EDGES * (p - cStart) + p4estEdge] = e - eStart;
1717           }
1718         }
1719         PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1720       }
1721     }
1722     PetscCall(DMPlexRestoreTransitiveClosure(dm, e, PETSC_FALSE, &starSize, &star));
1723   }
1724   PetscCall(PetscSectionDestroy(&ett));
1725   #endif
1726 
1727   /* 4: visit every vertex */
1728   conn->ctt_offset[0] = 0;
1729   for (v = vStart; v < vEnd; v++) {
1730     PetscInt off, s;
1731 
1732     PetscCall(PetscSectionGetOffset(ctt, v, &off));
1733     conn->ctt_offset[v - vStart] = (p4est_topidx_t)off;
1734     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1735     for (s = 0; s < starSize; s++) {
1736       PetscInt p = star[2 * s];
1737 
1738       if (p >= cStart && p < cEnd) {
1739         PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1740         PetscCheck(closureSize == P4EST_INSUL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Non-standard closure");
1741         for (c = 0; c < P4EST_CHILDREN; c++) {
1742           PetscInt cellVert = closure[2 * (c + vertOff)];
1743 
1744           if (cellVert == v) {
1745             PetscInt p4estVert = PetscVertToP4estVert[c];
1746 
1747             conn->corner_to_tree[off]                                       = (p4est_locidx_t)(p - cStart);
1748             conn->corner_to_corner[off++]                                   = (int8_t)p4estVert;
1749             conn->tree_to_corner[P4EST_CHILDREN * (p - cStart) + p4estVert] = v - vStart;
1750           }
1751         }
1752         PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &closureSize, &closure));
1753       }
1754     }
1755     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1756   }
1757   PetscCall(PetscSectionDestroy(&ctt));
1758 
1759   /* 5: Compute the coordinates */
1760   {
1761     PetscInt coordDim;
1762 
1763     PetscCall(DMGetCoordinateDim(dm, &coordDim));
1764     PetscCall(DMGetCoordinatesLocalSetUp(dm));
1765     for (c = cStart; c < cEnd; c++) {
1766       PetscInt           dof;
1767       PetscBool          isDG;
1768       PetscScalar       *cellCoords = NULL;
1769       const PetscScalar *array;
1770 
1771       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &dof, &array, &cellCoords));
1772       PetscCheck(dof == P4EST_CHILDREN * coordDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Need coordinates at the corners: (dof) %" PetscInt_FMT " != %d * %" PetscInt_FMT " (sdim)", dof, P4EST_CHILDREN, coordDim);
1773       for (v = 0; v < P4EST_CHILDREN; v++) {
1774         PetscInt i, lim = PetscMin(3, coordDim);
1775         PetscInt p4estVert = PetscVertToP4estVert[v];
1776 
1777         conn->tree_to_vertex[P4EST_CHILDREN * (c - cStart) + v] = P4EST_CHILDREN * (c - cStart) + v;
1778         /* p4est vertices are always embedded in R^3 */
1779         for (i = 0; i < 3; i++) conn->vertices[3 * (P4EST_CHILDREN * (c - cStart) + p4estVert) + i] = 0.;
1780         for (i = 0; i < lim; i++) conn->vertices[3 * (P4EST_CHILDREN * (c - cStart) + p4estVert) + i] = PetscRealPart(cellCoords[v * coordDim + i]);
1781       }
1782       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &dof, &array, &cellCoords));
1783     }
1784   }
1785 
1786   #if defined(P4EST_ENABLE_DEBUG)
1787   PetscCheck(p4est_connectivity_is_valid(conn), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Plex to p4est conversion failed");
1788   #endif
1789 
1790   *connOut = conn;
1791 
1792   *tree_face_to_uniq = ttf;
1793 
1794   PetscFunctionReturn(PETSC_SUCCESS);
1795 }
1796 
1797 static PetscErrorCode locidx_to_PetscInt(sc_array_t *array)
1798 {
1799   sc_array_t *newarray;
1800   size_t      zz, count = array->elem_count;
1801 
1802   PetscFunctionBegin;
1803   PetscCheck(array->elem_size == sizeof(p4est_locidx_t), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Wrong locidx size");
1804 
1805   if (sizeof(p4est_locidx_t) == sizeof(PetscInt)) PetscFunctionReturn(PETSC_SUCCESS);
1806 
1807   newarray = sc_array_new_size(sizeof(PetscInt), array->elem_count);
1808   for (zz = 0; zz < count; zz++) {
1809     p4est_locidx_t il = *((p4est_locidx_t *)sc_array_index(array, zz));
1810     PetscInt      *ip = (PetscInt *)sc_array_index(newarray, zz);
1811 
1812     *ip = (PetscInt)il;
1813   }
1814 
1815   sc_array_reset(array);
1816   sc_array_init_size(array, sizeof(PetscInt), count);
1817   sc_array_copy(array, newarray);
1818   sc_array_destroy(newarray);
1819   PetscFunctionReturn(PETSC_SUCCESS);
1820 }
1821 
1822 static PetscErrorCode coords_double_to_PetscScalar(sc_array_t *array, PetscInt dim)
1823 {
1824   sc_array_t *newarray;
1825   size_t      zz, count = array->elem_count;
1826 
1827   PetscFunctionBegin;
1828   PetscCheck(array->elem_size == 3 * sizeof(double), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Wrong coordinate size");
1829   #if !defined(PETSC_USE_COMPLEX)
1830   if (sizeof(double) == sizeof(PetscScalar) && dim == 3) PetscFunctionReturn(PETSC_SUCCESS);
1831   #endif
1832 
1833   newarray = sc_array_new_size(dim * sizeof(PetscScalar), array->elem_count);
1834   for (zz = 0; zz < count; zz++) {
1835     int          i;
1836     double      *id = (double *)sc_array_index(array, zz);
1837     PetscScalar *ip = (PetscScalar *)sc_array_index(newarray, zz);
1838 
1839     for (i = 0; i < dim; i++) ip[i] = 0.;
1840     for (i = 0; i < PetscMin(dim, 3); i++) ip[i] = (PetscScalar)id[i];
1841   }
1842 
1843   sc_array_reset(array);
1844   sc_array_init_size(array, dim * sizeof(PetscScalar), count);
1845   sc_array_copy(array, newarray);
1846   sc_array_destroy(newarray);
1847   PetscFunctionReturn(PETSC_SUCCESS);
1848 }
1849 
1850 static PetscErrorCode locidx_pair_to_PetscSFNode(sc_array_t *array)
1851 {
1852   sc_array_t *newarray;
1853   size_t      zz, count = array->elem_count;
1854 
1855   PetscFunctionBegin;
1856   PetscCheck(array->elem_size == 2 * sizeof(p4est_locidx_t), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Wrong locidx size");
1857 
1858   newarray = sc_array_new_size(sizeof(PetscSFNode), array->elem_count);
1859   for (zz = 0; zz < count; zz++) {
1860     p4est_locidx_t *il = (p4est_locidx_t *)sc_array_index(array, zz);
1861     PetscSFNode    *ip = (PetscSFNode *)sc_array_index(newarray, zz);
1862 
1863     ip->rank  = (PetscInt)il[0];
1864     ip->index = (PetscInt)il[1];
1865   }
1866 
1867   sc_array_reset(array);
1868   sc_array_init_size(array, sizeof(PetscSFNode), count);
1869   sc_array_copy(array, newarray);
1870   sc_array_destroy(newarray);
1871   PetscFunctionReturn(PETSC_SUCCESS);
1872 }
1873 
1874 static PetscErrorCode P4estToPlex_Local(p4est_t *p4est, DM *plex)
1875 {
1876   PetscFunctionBegin;
1877   {
1878     sc_array_t    *points_per_dim    = sc_array_new(sizeof(p4est_locidx_t));
1879     sc_array_t    *cone_sizes        = sc_array_new(sizeof(p4est_locidx_t));
1880     sc_array_t    *cones             = sc_array_new(sizeof(p4est_locidx_t));
1881     sc_array_t    *cone_orientations = sc_array_new(sizeof(p4est_locidx_t));
1882     sc_array_t    *coords            = sc_array_new(3 * sizeof(double));
1883     sc_array_t    *children          = sc_array_new(sizeof(p4est_locidx_t));
1884     sc_array_t    *parents           = sc_array_new(sizeof(p4est_locidx_t));
1885     sc_array_t    *childids          = sc_array_new(sizeof(p4est_locidx_t));
1886     sc_array_t    *leaves            = sc_array_new(sizeof(p4est_locidx_t));
1887     sc_array_t    *remotes           = sc_array_new(2 * sizeof(p4est_locidx_t));
1888     p4est_locidx_t first_local_quad;
1889 
1890     PetscCallP4est(p4est_get_plex_data, (p4est, P4EST_CONNECT_FULL, 0, &first_local_quad, points_per_dim, cone_sizes, cones, cone_orientations, coords, children, parents, childids, leaves, remotes));
1891 
1892     PetscCall(locidx_to_PetscInt(points_per_dim));
1893     PetscCall(locidx_to_PetscInt(cone_sizes));
1894     PetscCall(locidx_to_PetscInt(cones));
1895     PetscCall(locidx_to_PetscInt(cone_orientations));
1896     PetscCall(coords_double_to_PetscScalar(coords, P4EST_DIM));
1897 
1898     PetscCall(DMPlexCreate(PETSC_COMM_SELF, plex));
1899     PetscCall(DMSetDimension(*plex, P4EST_DIM));
1900     PetscCall(DMPlexCreateFromDAG(*plex, P4EST_DIM, (PetscInt *)points_per_dim->array, (PetscInt *)cone_sizes->array, (PetscInt *)cones->array, (PetscInt *)cone_orientations->array, (PetscScalar *)coords->array));
1901     PetscCall(DMPlexConvertOldOrientations_Internal(*plex));
1902     sc_array_destroy(points_per_dim);
1903     sc_array_destroy(cone_sizes);
1904     sc_array_destroy(cones);
1905     sc_array_destroy(cone_orientations);
1906     sc_array_destroy(coords);
1907     sc_array_destroy(children);
1908     sc_array_destroy(parents);
1909     sc_array_destroy(childids);
1910     sc_array_destroy(leaves);
1911     sc_array_destroy(remotes);
1912   }
1913   PetscFunctionReturn(PETSC_SUCCESS);
1914 }
1915 
1916   #define DMReferenceTreeGetChildSymmetry_pforest _append_pforest(DMReferenceTreeGetChildSymmetry)
1917 static PetscErrorCode DMReferenceTreeGetChildSymmetry_pforest(DM dm, PetscInt parent, PetscInt parentOrientA, PetscInt childOrientA, PetscInt childA, PetscInt parentOrientB, PetscInt *childOrientB, PetscInt *childB)
1918 {
1919   PetscInt coneSize, dStart, dEnd, vStart, vEnd, dim, ABswap, oAvert, oBvert, ABswapVert;
1920 
1921   PetscFunctionBegin;
1922   if (parentOrientA == parentOrientB) {
1923     if (childOrientB) *childOrientB = childOrientA;
1924     if (childB) *childB = childA;
1925     PetscFunctionReturn(PETSC_SUCCESS);
1926   }
1927   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1928   if (childA >= vStart && childA < vEnd) { /* vertices (always in the middle) are invariant under rotation */
1929     if (childOrientB) *childOrientB = 0;
1930     if (childB) *childB = childA;
1931     PetscFunctionReturn(PETSC_SUCCESS);
1932   }
1933   for (dim = 0; dim < 3; dim++) {
1934     PetscCall(DMPlexGetDepthStratum(dm, dim, &dStart, &dEnd));
1935     if (parent >= dStart && parent <= dEnd) break;
1936   }
1937   PetscCheck(dim <= 2, PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot perform child symmetry for %" PetscInt_FMT "-cells", dim);
1938   PetscCheck(dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "A vertex has no children");
1939   if (childA < dStart || childA >= dEnd) { /* a 1-cell in a 2-cell */
1940     /* this is a lower-dimensional child: bootstrap */
1941     PetscInt        size, i, sA = -1, sB, sOrientB, sConeSize;
1942     const PetscInt *supp, *coneA, *coneB, *oA, *oB;
1943 
1944     PetscCall(DMPlexGetSupportSize(dm, childA, &size));
1945     PetscCall(DMPlexGetSupport(dm, childA, &supp));
1946 
1947     /* find a point sA in supp(childA) that has the same parent */
1948     for (i = 0; i < size; i++) {
1949       PetscInt sParent;
1950 
1951       sA = supp[i];
1952       if (sA == parent) continue;
1953       PetscCall(DMPlexGetTreeParent(dm, sA, &sParent, NULL));
1954       if (sParent == parent) break;
1955     }
1956     PetscCheck(i != size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "could not find support in children");
1957     /* find out which point sB is in an equivalent position to sA under
1958      * parentOrientB */
1959     PetscCall(DMReferenceTreeGetChildSymmetry_pforest(dm, parent, parentOrientA, 0, sA, parentOrientB, &sOrientB, &sB));
1960     PetscCall(DMPlexGetConeSize(dm, sA, &sConeSize));
1961     PetscCall(DMPlexGetCone(dm, sA, &coneA));
1962     PetscCall(DMPlexGetCone(dm, sB, &coneB));
1963     PetscCall(DMPlexGetConeOrientation(dm, sA, &oA));
1964     PetscCall(DMPlexGetConeOrientation(dm, sB, &oB));
1965     /* step through the cone of sA in natural order */
1966     for (i = 0; i < sConeSize; i++) {
1967       if (coneA[i] == childA) {
1968         /* if childA is at position i in coneA,
1969          * then we want the point that is at sOrientB*i in coneB */
1970         PetscInt j = (sOrientB >= 0) ? ((sOrientB + i) % sConeSize) : ((sConeSize - (sOrientB + 1) - i) % sConeSize);
1971         if (childB) *childB = coneB[j];
1972         if (childOrientB) {
1973           DMPolytopeType ct;
1974           PetscInt       oBtrue;
1975 
1976           PetscCall(DMPlexGetConeSize(dm, childA, &coneSize));
1977           /* compose sOrientB and oB[j] */
1978           PetscCheck(coneSize == 0 || coneSize == 2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected a vertex or an edge");
1979           ct = coneSize ? DM_POLYTOPE_SEGMENT : DM_POLYTOPE_POINT;
1980           /* we may have to flip an edge */
1981           oBtrue        = (sOrientB >= 0) ? oB[j] : DMPolytopeTypeComposeOrientationInv(ct, -1, oB[j]);
1982           oBtrue        = DMPolytopeConvertNewOrientation_Internal(ct, oBtrue);
1983           ABswap        = DihedralSwap(coneSize, DMPolytopeConvertNewOrientation_Internal(ct, oA[i]), oBtrue);
1984           *childOrientB = DihedralCompose(coneSize, childOrientA, ABswap);
1985         }
1986         break;
1987       }
1988     }
1989     PetscCheck(i != sConeSize, PETSC_COMM_SELF, PETSC_ERR_PLIB, "support cone mismatch");
1990     PetscFunctionReturn(PETSC_SUCCESS);
1991   }
1992   /* get the cone size and symmetry swap */
1993   PetscCall(DMPlexGetConeSize(dm, parent, &coneSize));
1994   ABswap = DihedralSwap(coneSize, parentOrientA, parentOrientB);
1995   if (dim == 2) {
1996     /* orientations refer to cones: we want them to refer to vertices:
1997      * if it's a rotation, they are the same, but if the order is reversed, a
1998      * permutation that puts side i first does *not* put vertex i first */
1999     oAvert     = (parentOrientA >= 0) ? parentOrientA : -((-parentOrientA % coneSize) + 1);
2000     oBvert     = (parentOrientB >= 0) ? parentOrientB : -((-parentOrientB % coneSize) + 1);
2001     ABswapVert = DihedralSwap(coneSize, oAvert, oBvert);
2002   } else {
2003     oAvert     = parentOrientA;
2004     oBvert     = parentOrientB;
2005     ABswapVert = ABswap;
2006   }
2007   if (childB) {
2008     /* assume that each child corresponds to a vertex, in the same order */
2009     PetscInt        p, posA = -1, numChildren, i;
2010     const PetscInt *children;
2011 
2012     /* count which position the child is in */
2013     PetscCall(DMPlexGetTreeChildren(dm, parent, &numChildren, &children));
2014     for (i = 0; i < numChildren; i++) {
2015       p = children[i];
2016       if (p == childA) {
2017         if (dim == 1) {
2018           posA = i;
2019         } else { /* 2D Morton to rotation */
2020           posA = (i & 2) ? (i ^ 1) : i;
2021         }
2022         break;
2023       }
2024     }
2025     if (posA >= coneSize) {
2026       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find childA in children of parent");
2027     } else {
2028       /* figure out position B by applying ABswapVert */
2029       PetscInt posB, childIdB;
2030 
2031       posB = (ABswapVert >= 0) ? ((ABswapVert + posA) % coneSize) : ((coneSize - (ABswapVert + 1) - posA) % coneSize);
2032       if (dim == 1) {
2033         childIdB = posB;
2034       } else { /* 2D rotation to Morton */
2035         childIdB = (posB & 2) ? (posB ^ 1) : posB;
2036       }
2037       if (childB) *childB = children[childIdB];
2038     }
2039   }
2040   if (childOrientB) *childOrientB = DihedralCompose(coneSize, childOrientA, ABswap);
2041   PetscFunctionReturn(PETSC_SUCCESS);
2042 }
2043 
2044   #define DMCreateReferenceTree_pforest _append_pforest(DMCreateReferenceTree)
2045 static PetscErrorCode DMCreateReferenceTree_pforest(MPI_Comm comm, DM *dm)
2046 {
2047   p4est_connectivity_t *refcube;
2048   p4est_t              *root, *refined;
2049   DM                    dmRoot, dmRefined;
2050   DM_Plex              *mesh;
2051   PetscMPIInt           rank;
2052   #if defined(PETSC_HAVE_MPIUNI)
2053   sc_MPI_Comm comm_self = sc_MPI_COMM_SELF;
2054   #else
2055   MPI_Comm comm_self = PETSC_COMM_SELF;
2056   #endif
2057 
2058   PetscFunctionBegin;
2059   PetscCallP4estReturn(refcube, p4est_connectivity_new_byname, ("unit"));
2060   { /* [-1,1]^d geometry */
2061     PetscInt i, j;
2062 
2063     for (i = 0; i < P4EST_CHILDREN; i++) {
2064       for (j = 0; j < 3; j++) {
2065         refcube->vertices[3 * i + j] *= 2.;
2066         refcube->vertices[3 * i + j] -= 1.;
2067       }
2068     }
2069   }
2070   PetscCallP4estReturn(root, p4est_new, (comm_self, refcube, 0, NULL, NULL));
2071   PetscCallP4estReturn(refined, p4est_new_ext, (comm_self, refcube, 0, 1, 1, 0, NULL, NULL));
2072   PetscCall(P4estToPlex_Local(root, &dmRoot));
2073   PetscCall(P4estToPlex_Local(refined, &dmRefined));
2074   {
2075   #if !defined(P4_TO_P8)
2076     PetscInt nPoints   = 25;
2077     PetscInt perm[25]  = {0, 1, 2, 3, 4, 12, 8, 14, 6, 9, 15, 5, 13, 10, 7, 11, 16, 22, 20, 24, 17, 21, 18, 23, 19};
2078     PetscInt ident[25] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 0, 0, 5, 6, 7, 8, 1, 2, 3, 4, 0};
2079   #else
2080     PetscInt nPoints    = 125;
2081     PetscInt perm[125]  = {0,  1,  2,  3,  4,  5,  6,  7,  8,  32, 16, 36, 24, 40, 12, 17,  37,  25,  41,  9,   33,  20,  26, 42,  13,  21,  27,  43,  10,  34,  18,  38,  28,  14,  19,  39,  29,  11,  35,  22,  30, 15,
2082                            23, 31, 44, 84, 76, 92, 52, 86, 68, 94, 60, 78, 70, 96, 45, 85,  77,  93,  54,  72,  62,  74,  46, 80,  53,  87,  69,  95,  64,  82,  47,  81,  55,  73,  66,  48,  88,  56,  90,  61,  79, 71,
2083                            97, 49, 89, 58, 63, 75, 50, 57, 91, 65, 83, 51, 59, 67, 98, 106, 110, 122, 114, 120, 118, 124, 99, 111, 115, 119, 100, 107, 116, 121, 101, 117, 102, 108, 112, 123, 103, 113, 104, 109, 105};
2084     PetscInt ident[125] = {0,  0,  0,  0,  0,  0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0, 7, 7, 8,  8,  9,  9,  10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
2085                            16, 17, 17, 18, 18, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 19, 20, 21, 22, 23, 24, 25, 26, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 1,  2,  3,  4,  5,  6,  0};
2086 
2087   #endif
2088     IS permIS;
2089     DM dmPerm;
2090 
2091     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nPoints, perm, PETSC_USE_POINTER, &permIS));
2092     PetscCall(DMPlexPermute(dmRefined, permIS, &dmPerm));
2093     if (dmPerm) {
2094       PetscCall(DMDestroy(&dmRefined));
2095       dmRefined = dmPerm;
2096     }
2097     PetscCall(ISDestroy(&permIS));
2098     {
2099       PetscInt p;
2100       PetscCall(DMCreateLabel(dmRoot, "identity"));
2101       PetscCall(DMCreateLabel(dmRefined, "identity"));
2102       for (p = 0; p < P4EST_INSUL; p++) PetscCall(DMSetLabelValue(dmRoot, "identity", p, p));
2103       for (p = 0; p < nPoints; p++) PetscCall(DMSetLabelValue(dmRefined, "identity", p, ident[p]));
2104     }
2105   }
2106   PetscCall(DMPlexCreateReferenceTree_Union(dmRoot, dmRefined, "identity", dm));
2107   mesh                   = (DM_Plex *)(*dm)->data;
2108   mesh->getchildsymmetry = DMReferenceTreeGetChildSymmetry_pforest;
2109   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2110   if (rank == 0) {
2111     PetscCall(DMViewFromOptions(dmRoot, NULL, "-dm_p4est_ref_root_view"));
2112     PetscCall(DMViewFromOptions(dmRefined, NULL, "-dm_p4est_ref_refined_view"));
2113     PetscCall(DMViewFromOptions(dmRefined, NULL, "-dm_p4est_ref_tree_view"));
2114   }
2115   PetscCall(DMDestroy(&dmRefined));
2116   PetscCall(DMDestroy(&dmRoot));
2117   PetscCallP4est(p4est_destroy, (refined));
2118   PetscCallP4est(p4est_destroy, (root));
2119   PetscCallP4est(p4est_connectivity_destroy, (refcube));
2120   PetscFunctionReturn(PETSC_SUCCESS);
2121 }
2122 
2123 static PetscErrorCode DMShareDiscretization(DM dmA, DM dmB)
2124 {
2125   void     *ctx;
2126   PetscInt  num;
2127   PetscReal val;
2128 
2129   PetscFunctionBegin;
2130   PetscCall(DMGetApplicationContext(dmA, &ctx));
2131   PetscCall(DMSetApplicationContext(dmB, ctx));
2132   PetscCall(DMCopyDisc(dmA, dmB));
2133   PetscCall(DMGetOutputSequenceNumber(dmA, &num, &val));
2134   PetscCall(DMSetOutputSequenceNumber(dmB, num, val));
2135   if (dmB->localSection != dmA->localSection || dmB->globalSection != dmA->globalSection) {
2136     PetscCall(DMClearLocalVectors(dmB));
2137     PetscCall(PetscObjectReference((PetscObject)dmA->localSection));
2138     PetscCall(PetscSectionDestroy(&(dmB->localSection)));
2139     dmB->localSection = dmA->localSection;
2140     PetscCall(DMClearGlobalVectors(dmB));
2141     PetscCall(PetscObjectReference((PetscObject)dmA->globalSection));
2142     PetscCall(PetscSectionDestroy(&(dmB->globalSection)));
2143     dmB->globalSection = dmA->globalSection;
2144     PetscCall(PetscObjectReference((PetscObject)dmA->defaultConstraint.section));
2145     PetscCall(PetscSectionDestroy(&(dmB->defaultConstraint.section)));
2146     dmB->defaultConstraint.section = dmA->defaultConstraint.section;
2147     PetscCall(PetscObjectReference((PetscObject)dmA->defaultConstraint.mat));
2148     PetscCall(MatDestroy(&(dmB->defaultConstraint.mat)));
2149     dmB->defaultConstraint.mat = dmA->defaultConstraint.mat;
2150     if (dmA->map) PetscCall(PetscLayoutReference(dmA->map, &dmB->map));
2151   }
2152   if (dmB->sectionSF != dmA->sectionSF) {
2153     PetscCall(PetscObjectReference((PetscObject)dmA->sectionSF));
2154     PetscCall(PetscSFDestroy(&dmB->sectionSF));
2155     dmB->sectionSF = dmA->sectionSF;
2156   }
2157   PetscFunctionReturn(PETSC_SUCCESS);
2158 }
2159 
2160 /* Get an SF that broadcasts a coarse-cell covering of the local fine cells */
2161 static PetscErrorCode DMPforestGetCellCoveringSF(MPI_Comm comm, p4est_t *p4estC, p4est_t *p4estF, PetscInt cStart, PetscInt cEnd, PetscSF *coveringSF)
2162 {
2163   PetscInt     startF, endF, startC, endC, p, nLeaves;
2164   PetscSFNode *leaves;
2165   PetscSF      sf;
2166   PetscInt    *recv, *send;
2167   PetscMPIInt  tag;
2168   MPI_Request *recvReqs, *sendReqs;
2169   PetscSection section;
2170 
2171   PetscFunctionBegin;
2172   PetscCall(DMPforestComputeOverlappingRanks(p4estC->mpisize, p4estC->mpirank, p4estF, p4estC, &startC, &endC));
2173   PetscCall(PetscMalloc2(2 * (endC - startC), &recv, endC - startC, &recvReqs));
2174   PetscCall(PetscCommGetNewTag(comm, &tag));
2175   for (p = startC; p < endC; p++) {
2176     recvReqs[p - startC] = MPI_REQUEST_NULL;                                        /* just in case we don't initiate a receive */
2177     if (p4estC->global_first_quadrant[p] == p4estC->global_first_quadrant[p + 1]) { /* empty coarse partition */
2178       recv[2 * (p - startC)]     = 0;
2179       recv[2 * (p - startC) + 1] = 0;
2180       continue;
2181     }
2182 
2183     PetscCallMPI(MPI_Irecv(&recv[2 * (p - startC)], 2, MPIU_INT, p, tag, comm, &recvReqs[p - startC]));
2184   }
2185   PetscCall(DMPforestComputeOverlappingRanks(p4estC->mpisize, p4estC->mpirank, p4estC, p4estF, &startF, &endF));
2186   PetscCall(PetscMalloc2(2 * (endF - startF), &send, endF - startF, &sendReqs));
2187   /* count the quadrants rank will send to each of [startF,endF) */
2188   for (p = startF; p < endF; p++) {
2189     p4est_quadrant_t *myFineStart = &p4estF->global_first_position[p];
2190     p4est_quadrant_t *myFineEnd   = &p4estF->global_first_position[p + 1];
2191     PetscInt          tStart      = (PetscInt)myFineStart->p.which_tree;
2192     PetscInt          tEnd        = (PetscInt)myFineEnd->p.which_tree;
2193     PetscInt          firstCell = -1, lastCell = -1;
2194     p4est_tree_t     *treeStart = &(((p4est_tree_t *)p4estC->trees->array)[tStart]);
2195     p4est_tree_t     *treeEnd   = (size_t)tEnd < p4estC->trees->elem_count ? &(((p4est_tree_t *)p4estC->trees->array)[tEnd]) : NULL;
2196     ssize_t           overlapIndex;
2197 
2198     sendReqs[p - startF] = MPI_REQUEST_NULL; /* just in case we don't initiate a send */
2199     if (p4estF->global_first_quadrant[p] == p4estF->global_first_quadrant[p + 1]) continue;
2200 
2201     /* locate myFineStart in (or before) a cell */
2202     if (treeStart->quadrants.elem_count) {
2203       PetscCallP4estReturn(overlapIndex, sc_array_bsearch, (&(treeStart->quadrants), myFineStart, p4est_quadrant_disjoint));
2204       if (overlapIndex < 0) {
2205         firstCell = 0;
2206       } else {
2207         firstCell = treeStart->quadrants_offset + overlapIndex;
2208       }
2209     } else {
2210       firstCell = 0;
2211     }
2212     if (treeEnd && treeEnd->quadrants.elem_count) {
2213       PetscCallP4estReturn(overlapIndex, sc_array_bsearch, (&(treeEnd->quadrants), myFineEnd, p4est_quadrant_disjoint));
2214       if (overlapIndex < 0) { /* all of this local section is overlapped */
2215         lastCell = p4estC->local_num_quadrants;
2216       } else {
2217         p4est_quadrant_t *container = &(((p4est_quadrant_t *)treeEnd->quadrants.array)[overlapIndex]);
2218         p4est_quadrant_t  first_desc;
2219         int               equal;
2220 
2221         PetscCallP4est(p4est_quadrant_first_descendant, (container, &first_desc, P4EST_QMAXLEVEL));
2222         PetscCallP4estReturn(equal, p4est_quadrant_is_equal, (myFineEnd, &first_desc));
2223         if (equal) {
2224           lastCell = treeEnd->quadrants_offset + overlapIndex;
2225         } else {
2226           lastCell = treeEnd->quadrants_offset + overlapIndex + 1;
2227         }
2228       }
2229     } else {
2230       lastCell = p4estC->local_num_quadrants;
2231     }
2232     send[2 * (p - startF)]     = firstCell;
2233     send[2 * (p - startF) + 1] = lastCell - firstCell;
2234     PetscCallMPI(MPI_Isend(&send[2 * (p - startF)], 2, MPIU_INT, p, tag, comm, &sendReqs[p - startF]));
2235   }
2236   PetscCallMPI(MPI_Waitall((PetscMPIInt)(endC - startC), recvReqs, MPI_STATUSES_IGNORE));
2237   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &section));
2238   PetscCall(PetscSectionSetChart(section, startC, endC));
2239   for (p = startC; p < endC; p++) {
2240     PetscInt numCells = recv[2 * (p - startC) + 1];
2241     PetscCall(PetscSectionSetDof(section, p, numCells));
2242   }
2243   PetscCall(PetscSectionSetUp(section));
2244   PetscCall(PetscSectionGetStorageSize(section, &nLeaves));
2245   PetscCall(PetscMalloc1(nLeaves, &leaves));
2246   for (p = startC; p < endC; p++) {
2247     PetscInt firstCell = recv[2 * (p - startC)];
2248     PetscInt numCells  = recv[2 * (p - startC) + 1];
2249     PetscInt off, i;
2250 
2251     PetscCall(PetscSectionGetOffset(section, p, &off));
2252     for (i = 0; i < numCells; i++) {
2253       leaves[off + i].rank  = p;
2254       leaves[off + i].index = firstCell + i;
2255     }
2256   }
2257   PetscCall(PetscSFCreate(comm, &sf));
2258   PetscCall(PetscSFSetGraph(sf, cEnd - cStart, nLeaves, NULL, PETSC_OWN_POINTER, leaves, PETSC_OWN_POINTER));
2259   PetscCall(PetscSectionDestroy(&section));
2260   PetscCallMPI(MPI_Waitall((PetscMPIInt)(endF - startF), sendReqs, MPI_STATUSES_IGNORE));
2261   PetscCall(PetscFree2(send, sendReqs));
2262   PetscCall(PetscFree2(recv, recvReqs));
2263   *coveringSF = sf;
2264   PetscFunctionReturn(PETSC_SUCCESS);
2265 }
2266 
2267 /* closure points for locally-owned cells */
2268 static PetscErrorCode DMPforestGetCellSFNodes(DM dm, PetscInt numClosureIndices, PetscInt *numClosurePoints, PetscSFNode **closurePoints, PetscBool redirect)
2269 {
2270   PetscInt           cStart, cEnd;
2271   PetscInt           count, c;
2272   PetscMPIInt        rank;
2273   PetscInt           closureSize = -1;
2274   PetscInt          *closure     = NULL;
2275   PetscSF            pointSF;
2276   PetscInt           nleaves, nroots;
2277   const PetscInt    *ilocal;
2278   const PetscSFNode *iremote;
2279   DM                 plex;
2280   DM_Forest         *forest;
2281   DM_Forest_pforest *pforest;
2282 
2283   PetscFunctionBegin;
2284   forest  = (DM_Forest *)dm->data;
2285   pforest = (DM_Forest_pforest *)forest->data;
2286   cStart  = pforest->cLocalStart;
2287   cEnd    = pforest->cLocalEnd;
2288   PetscCall(DMPforestGetPlex(dm, &plex));
2289   PetscCall(DMGetPointSF(dm, &pointSF));
2290   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &ilocal, &iremote));
2291   nleaves           = PetscMax(0, nleaves);
2292   nroots            = PetscMax(0, nroots);
2293   *numClosurePoints = numClosureIndices * (cEnd - cStart);
2294   PetscCall(PetscMalloc1(*numClosurePoints, closurePoints));
2295   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2296   for (c = cStart, count = 0; c < cEnd; c++) {
2297     PetscInt i;
2298     PetscCall(DMPlexGetTransitiveClosure(plex, c, PETSC_TRUE, &closureSize, &closure));
2299 
2300     for (i = 0; i < numClosureIndices; i++, count++) {
2301       PetscInt p   = closure[2 * i];
2302       PetscInt loc = -1;
2303 
2304       PetscCall(PetscFindInt(p, nleaves, ilocal, &loc));
2305       if (redirect && loc >= 0) {
2306         (*closurePoints)[count].rank  = iremote[loc].rank;
2307         (*closurePoints)[count].index = iremote[loc].index;
2308       } else {
2309         (*closurePoints)[count].rank  = rank;
2310         (*closurePoints)[count].index = p;
2311       }
2312     }
2313     PetscCall(DMPlexRestoreTransitiveClosure(plex, c, PETSC_TRUE, &closureSize, &closure));
2314   }
2315   PetscFunctionReturn(PETSC_SUCCESS);
2316 }
2317 
2318 static void MPIAPI DMPforestMaxSFNode(void *a, void *b, PetscMPIInt *len, MPI_Datatype *type)
2319 {
2320   PetscMPIInt i;
2321 
2322   for (i = 0; i < *len; i++) {
2323     PetscSFNode *A = (PetscSFNode *)a;
2324     PetscSFNode *B = (PetscSFNode *)b;
2325 
2326     if (B->rank < 0) *B = *A;
2327   }
2328 }
2329 
2330 static PetscErrorCode DMPforestGetTransferSF_Point(DM coarse, DM fine, PetscSF *sf, PetscBool transferIdent, PetscInt *childIds[])
2331 {
2332   MPI_Comm           comm;
2333   PetscMPIInt        rank, size;
2334   DM_Forest_pforest *pforestC, *pforestF;
2335   p4est_t           *p4estC, *p4estF;
2336   PetscInt           numClosureIndices;
2337   PetscInt           numClosurePointsC, numClosurePointsF;
2338   PetscSFNode       *closurePointsC, *closurePointsF;
2339   p4est_quadrant_t  *coverQuads = NULL;
2340   p4est_quadrant_t **treeQuads;
2341   PetscInt          *treeQuadCounts;
2342   MPI_Datatype       nodeType;
2343   MPI_Datatype       nodeClosureType;
2344   MPI_Op             sfNodeReduce;
2345   p4est_topidx_t     fltF, lltF, t;
2346   DM                 plexC, plexF;
2347   PetscInt           pStartF, pEndF, pStartC, pEndC;
2348   PetscBool          saveInCoarse = PETSC_FALSE;
2349   PetscBool          saveInFine   = PETSC_FALSE;
2350   PetscBool          formCids     = (childIds != NULL) ? PETSC_TRUE : PETSC_FALSE;
2351   PetscInt          *cids         = NULL;
2352 
2353   PetscFunctionBegin;
2354   pforestC = (DM_Forest_pforest *)((DM_Forest *)coarse->data)->data;
2355   pforestF = (DM_Forest_pforest *)((DM_Forest *)fine->data)->data;
2356   p4estC   = pforestC->forest;
2357   p4estF   = pforestF->forest;
2358   PetscCheck(pforestC->topo == pforestF->topo, PetscObjectComm((PetscObject)coarse), PETSC_ERR_ARG_INCOMP, "DM's must have the same base DM");
2359   comm = PetscObjectComm((PetscObject)coarse);
2360   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2361   PetscCallMPI(MPI_Comm_size(comm, &size));
2362   PetscCall(DMPforestGetPlex(fine, &plexF));
2363   PetscCall(DMPlexGetChart(plexF, &pStartF, &pEndF));
2364   PetscCall(DMPforestGetPlex(coarse, &plexC));
2365   PetscCall(DMPlexGetChart(plexC, &pStartC, &pEndC));
2366   { /* check if the results have been cached */
2367     DM adaptCoarse, adaptFine;
2368 
2369     PetscCall(DMForestGetAdaptivityForest(coarse, &adaptCoarse));
2370     PetscCall(DMForestGetAdaptivityForest(fine, &adaptFine));
2371     if (adaptCoarse && adaptCoarse->data == fine->data) { /* coarse is adapted from fine */
2372       if (pforestC->pointSelfToAdaptSF) {
2373         PetscCall(PetscObjectReference((PetscObject)(pforestC->pointSelfToAdaptSF)));
2374         *sf = pforestC->pointSelfToAdaptSF;
2375         if (childIds) {
2376           PetscCall(PetscMalloc1(pEndF - pStartF, &cids));
2377           PetscCall(PetscArraycpy(cids, pforestC->pointSelfToAdaptCids, pEndF - pStartF));
2378           *childIds = cids;
2379         }
2380         PetscFunctionReturn(PETSC_SUCCESS);
2381       } else {
2382         saveInCoarse = PETSC_TRUE;
2383         formCids     = PETSC_TRUE;
2384       }
2385     } else if (adaptFine && adaptFine->data == coarse->data) { /* fine is adapted from coarse */
2386       if (pforestF->pointAdaptToSelfSF) {
2387         PetscCall(PetscObjectReference((PetscObject)(pforestF->pointAdaptToSelfSF)));
2388         *sf = pforestF->pointAdaptToSelfSF;
2389         if (childIds) {
2390           PetscCall(PetscMalloc1(pEndF - pStartF, &cids));
2391           PetscCall(PetscArraycpy(cids, pforestF->pointAdaptToSelfCids, pEndF - pStartF));
2392           *childIds = cids;
2393         }
2394         PetscFunctionReturn(PETSC_SUCCESS);
2395       } else {
2396         saveInFine = PETSC_TRUE;
2397         formCids   = PETSC_TRUE;
2398       }
2399     }
2400   }
2401 
2402   /* count the number of closure points that have dofs and create a list */
2403   numClosureIndices = P4EST_INSUL;
2404   /* create the datatype */
2405   PetscCallMPI(MPI_Type_contiguous(2, MPIU_INT, &nodeType));
2406   PetscCallMPI(MPI_Type_commit(&nodeType));
2407   PetscCallMPI(MPI_Op_create(DMPforestMaxSFNode, PETSC_FALSE, &sfNodeReduce));
2408   PetscCallMPI(MPI_Type_contiguous(numClosureIndices * 2, MPIU_INT, &nodeClosureType));
2409   PetscCallMPI(MPI_Type_commit(&nodeClosureType));
2410   /* everything has to go through cells: for each cell, create a list of the sfnodes in its closure */
2411   /* get lists of closure point SF nodes for every cell */
2412   PetscCall(DMPforestGetCellSFNodes(coarse, numClosureIndices, &numClosurePointsC, &closurePointsC, PETSC_TRUE));
2413   PetscCall(DMPforestGetCellSFNodes(fine, numClosureIndices, &numClosurePointsF, &closurePointsF, PETSC_FALSE));
2414   /* create pointers for tree lists */
2415   fltF = p4estF->first_local_tree;
2416   lltF = p4estF->last_local_tree;
2417   PetscCall(PetscCalloc2(lltF + 1 - fltF, &treeQuads, lltF + 1 - fltF, &treeQuadCounts));
2418   /* if the partitions don't match, ship the coarse to cover the fine */
2419   if (size > 1) {
2420     PetscInt p;
2421 
2422     for (p = 0; p < size; p++) {
2423       int equal;
2424 
2425       PetscCallP4estReturn(equal, p4est_quadrant_is_equal_piggy, (&p4estC->global_first_position[p], &p4estF->global_first_position[p]));
2426       if (!equal) break;
2427     }
2428     if (p < size) { /* non-matching distribution: send the coarse to cover the fine */
2429       PetscInt          cStartC, cEndC;
2430       PetscSF           coveringSF;
2431       PetscInt          nleaves;
2432       PetscInt          count;
2433       PetscSFNode      *newClosurePointsC;
2434       p4est_quadrant_t *coverQuadsSend;
2435       p4est_topidx_t    fltC = p4estC->first_local_tree;
2436       p4est_topidx_t    lltC = p4estC->last_local_tree;
2437       p4est_topidx_t    t;
2438       PetscMPIInt       blockSizes[4]   = {P4EST_DIM, 2, 1, 1};
2439       MPI_Aint          blockOffsets[4] = {offsetof(p4est_quadrant_t, x), offsetof(p4est_quadrant_t, level), offsetof(p4est_quadrant_t, pad16), offsetof(p4est_quadrant_t, p)};
2440       MPI_Datatype      blockTypes[4]   = {MPI_INT32_T, MPI_INT8_T, MPI_INT16_T, MPI_INT32_T /* p.which_tree */};
2441       MPI_Datatype      quadStruct, quadType;
2442 
2443       PetscCall(DMPlexGetSimplexOrBoxCells(plexC, 0, &cStartC, &cEndC));
2444       PetscCall(DMPforestGetCellCoveringSF(comm, p4estC, p4estF, pforestC->cLocalStart, pforestC->cLocalEnd, &coveringSF));
2445       PetscCall(PetscSFGetGraph(coveringSF, NULL, &nleaves, NULL, NULL));
2446       PetscCall(PetscMalloc1(numClosureIndices * nleaves, &newClosurePointsC));
2447       PetscCall(PetscMalloc1(nleaves, &coverQuads));
2448       PetscCall(PetscMalloc1(cEndC - cStartC, &coverQuadsSend));
2449       count = 0;
2450       for (t = fltC; t <= lltC; t++) { /* unfortunately, we need to pack a send array, since quads are not stored packed in p4est */
2451         p4est_tree_t *tree = &(((p4est_tree_t *)p4estC->trees->array)[t]);
2452         PetscInt      q;
2453 
2454         PetscCall(PetscMemcpy(&coverQuadsSend[count], tree->quadrants.array, tree->quadrants.elem_count * sizeof(p4est_quadrant_t)));
2455         for (q = 0; (size_t)q < tree->quadrants.elem_count; q++) coverQuadsSend[count + q].p.which_tree = t;
2456         count += tree->quadrants.elem_count;
2457       }
2458       /* p is of a union type p4est_quadrant_data, but only the p.which_tree field is active at this time. So, we
2459          have a simple blockTypes[] to use. Note that quadStruct does not count potential padding in array of
2460          p4est_quadrant_t. We have to call MPI_Type_create_resized() to change upper-bound of quadStruct.
2461        */
2462       PetscCallMPI(MPI_Type_create_struct(4, blockSizes, blockOffsets, blockTypes, &quadStruct));
2463       PetscCallMPI(MPI_Type_create_resized(quadStruct, 0, sizeof(p4est_quadrant_t), &quadType));
2464       PetscCallMPI(MPI_Type_commit(&quadType));
2465       PetscCall(PetscSFBcastBegin(coveringSF, nodeClosureType, closurePointsC, newClosurePointsC, MPI_REPLACE));
2466       PetscCall(PetscSFBcastBegin(coveringSF, quadType, coverQuadsSend, coverQuads, MPI_REPLACE));
2467       PetscCall(PetscSFBcastEnd(coveringSF, nodeClosureType, closurePointsC, newClosurePointsC, MPI_REPLACE));
2468       PetscCall(PetscSFBcastEnd(coveringSF, quadType, coverQuadsSend, coverQuads, MPI_REPLACE));
2469       PetscCallMPI(MPI_Type_free(&quadStruct));
2470       PetscCallMPI(MPI_Type_free(&quadType));
2471       PetscCall(PetscFree(coverQuadsSend));
2472       PetscCall(PetscFree(closurePointsC));
2473       PetscCall(PetscSFDestroy(&coveringSF));
2474       closurePointsC = newClosurePointsC;
2475 
2476       /* assign tree quads based on locations in coverQuads */
2477       {
2478         PetscInt q;
2479         for (q = 0; q < nleaves; q++) {
2480           p4est_locidx_t t = coverQuads[q].p.which_tree;
2481           if (!treeQuadCounts[t - fltF]++) treeQuads[t - fltF] = &coverQuads[q];
2482         }
2483       }
2484     }
2485   }
2486   if (!coverQuads) { /* matching partitions: assign tree quads based on locations in p4est native arrays */
2487     for (t = fltF; t <= lltF; t++) {
2488       p4est_tree_t *tree = &(((p4est_tree_t *)p4estC->trees->array)[t]);
2489 
2490       treeQuadCounts[t - fltF] = tree->quadrants.elem_count;
2491       treeQuads[t - fltF]      = (p4est_quadrant_t *)tree->quadrants.array;
2492     }
2493   }
2494 
2495   {
2496     PetscInt     p;
2497     PetscInt     cLocalStartF;
2498     PetscSF      pointSF;
2499     PetscSFNode *roots;
2500     PetscInt    *rootType;
2501     DM           refTree = NULL;
2502     DMLabel      canonical;
2503     PetscInt    *childClosures[P4EST_CHILDREN] = {NULL};
2504     PetscInt    *rootClosure                   = NULL;
2505     PetscInt     coarseOffset;
2506     PetscInt     numCoarseQuads;
2507 
2508     PetscCall(PetscMalloc1(pEndF - pStartF, &roots));
2509     PetscCall(PetscMalloc1(pEndF - pStartF, &rootType));
2510     PetscCall(DMGetPointSF(fine, &pointSF));
2511     for (p = pStartF; p < pEndF; p++) {
2512       roots[p - pStartF].rank  = -1;
2513       roots[p - pStartF].index = -1;
2514       rootType[p - pStartF]    = -1;
2515     }
2516     if (formCids) {
2517       PetscInt child;
2518 
2519       PetscCall(PetscMalloc1(pEndF - pStartF, &cids));
2520       for (p = pStartF; p < pEndF; p++) cids[p - pStartF] = -2;
2521       PetscCall(DMPlexGetReferenceTree(plexF, &refTree));
2522       PetscCall(DMPlexGetTransitiveClosure(refTree, 0, PETSC_TRUE, NULL, &rootClosure));
2523       for (child = 0; child < P4EST_CHILDREN; child++) { /* get the closures of the child cells in the reference tree */
2524         PetscCall(DMPlexGetTransitiveClosure(refTree, child + 1, PETSC_TRUE, NULL, &childClosures[child]));
2525       }
2526       PetscCall(DMGetLabel(refTree, "canonical", &canonical));
2527     }
2528     cLocalStartF = pforestF->cLocalStart;
2529     for (t = fltF, coarseOffset = 0, numCoarseQuads = 0; t <= lltF; t++, coarseOffset += numCoarseQuads) {
2530       p4est_tree_t     *tree         = &(((p4est_tree_t *)p4estF->trees->array)[t]);
2531       PetscInt          numFineQuads = tree->quadrants.elem_count;
2532       p4est_quadrant_t *coarseQuads  = treeQuads[t - fltF];
2533       p4est_quadrant_t *fineQuads    = (p4est_quadrant_t *)tree->quadrants.array;
2534       PetscInt          i, coarseCount = 0;
2535       PetscInt          offset = tree->quadrants_offset;
2536       sc_array_t        coarseQuadsArray;
2537 
2538       numCoarseQuads = treeQuadCounts[t - fltF];
2539       PetscCallP4est(sc_array_init_data, (&coarseQuadsArray, coarseQuads, sizeof(p4est_quadrant_t), (size_t)numCoarseQuads));
2540       for (i = 0; i < numFineQuads; i++) {
2541         PetscInt          c          = i + offset;
2542         p4est_quadrant_t *quad       = &fineQuads[i];
2543         p4est_quadrant_t *quadCoarse = NULL;
2544         ssize_t           disjoint   = -1;
2545 
2546         while (disjoint < 0 && coarseCount < numCoarseQuads) {
2547           quadCoarse = &coarseQuads[coarseCount];
2548           PetscCallP4estReturn(disjoint, p4est_quadrant_disjoint, (quadCoarse, quad));
2549           if (disjoint < 0) coarseCount++;
2550         }
2551         PetscCheck(disjoint == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "did not find overlapping coarse quad");
2552         if (quadCoarse->level > quad->level || (quadCoarse->level == quad->level && !transferIdent)) { /* the "coarse" mesh is finer than the fine mesh at the point: continue */
2553           if (transferIdent) {                                                                         /* find corners */
2554             PetscInt j = 0;
2555 
2556             do {
2557               if (j < P4EST_CHILDREN) {
2558                 p4est_quadrant_t cornerQuad;
2559                 int              equal;
2560 
2561                 PetscCallP4est(p4est_quadrant_corner_descendant, (quad, &cornerQuad, j, quadCoarse->level));
2562                 PetscCallP4estReturn(equal, p4est_quadrant_is_equal, (&cornerQuad, quadCoarse));
2563                 if (equal) {
2564                   PetscInt    petscJ = P4estVertToPetscVert[j];
2565                   PetscInt    p      = closurePointsF[numClosureIndices * c + (P4EST_INSUL - P4EST_CHILDREN) + petscJ].index;
2566                   PetscSFNode q      = closurePointsC[numClosureIndices * (coarseCount + coarseOffset) + (P4EST_INSUL - P4EST_CHILDREN) + petscJ];
2567 
2568                   roots[p - pStartF]    = q;
2569                   rootType[p - pStartF] = PETSC_MAX_INT;
2570                   cids[p - pStartF]     = -1;
2571                   j++;
2572                 }
2573               }
2574               coarseCount++;
2575               disjoint = 1;
2576               if (coarseCount < numCoarseQuads) {
2577                 quadCoarse = &coarseQuads[coarseCount];
2578                 PetscCallP4estReturn(disjoint, p4est_quadrant_disjoint, (quadCoarse, quad));
2579               }
2580             } while (!disjoint);
2581           }
2582           continue;
2583         }
2584         if (quadCoarse->level == quad->level) { /* same quad present in coarse and fine mesh */
2585           PetscInt j;
2586           for (j = 0; j < numClosureIndices; j++) {
2587             PetscInt p = closurePointsF[numClosureIndices * c + j].index;
2588 
2589             roots[p - pStartF]    = closurePointsC[numClosureIndices * (coarseCount + coarseOffset) + j];
2590             rootType[p - pStartF] = PETSC_MAX_INT; /* unconditionally accept */
2591             cids[p - pStartF]     = -1;
2592           }
2593         } else {
2594           PetscInt levelDiff                 = quad->level - quadCoarse->level;
2595           PetscInt proposedCids[P4EST_INSUL] = {0};
2596 
2597           if (formCids) {
2598             PetscInt  cl;
2599             PetscInt *pointClosure = NULL;
2600             int       cid;
2601 
2602             PetscCheck(levelDiff <= 1, PETSC_COMM_SELF, PETSC_ERR_USER, "Recursive child ids not implemented");
2603             PetscCallP4estReturn(cid, p4est_quadrant_child_id, (quad));
2604             PetscCall(DMPlexGetTransitiveClosure(plexF, c + cLocalStartF, PETSC_TRUE, NULL, &pointClosure));
2605             for (cl = 0; cl < P4EST_INSUL; cl++) {
2606               PetscInt       p      = pointClosure[2 * cl];
2607               PetscInt       point  = childClosures[cid][2 * cl];
2608               PetscInt       ornt   = childClosures[cid][2 * cl + 1];
2609               PetscInt       newcid = -1;
2610               DMPolytopeType ct;
2611 
2612               if (rootType[p - pStartF] == PETSC_MAX_INT) continue;
2613               PetscCall(DMPlexGetCellType(refTree, point, &ct));
2614               ornt = DMPolytopeConvertNewOrientation_Internal(ct, ornt);
2615               if (!cl) {
2616                 newcid = cid + 1;
2617               } else {
2618                 PetscInt rcl, parent, parentOrnt = 0;
2619 
2620                 PetscCall(DMPlexGetTreeParent(refTree, point, &parent, NULL));
2621                 if (parent == point) {
2622                   newcid = -1;
2623                 } else if (!parent) { /* in the root */
2624                   newcid = point;
2625                 } else {
2626                   DMPolytopeType rct = DM_POLYTOPE_UNKNOWN;
2627 
2628                   for (rcl = 1; rcl < P4EST_INSUL; rcl++) {
2629                     if (rootClosure[2 * rcl] == parent) {
2630                       PetscCall(DMPlexGetCellType(refTree, parent, &rct));
2631                       parentOrnt = DMPolytopeConvertNewOrientation_Internal(rct, rootClosure[2 * rcl + 1]);
2632                       break;
2633                     }
2634                   }
2635                   PetscCheck(rcl < P4EST_INSUL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Couldn't find parent in root closure");
2636                   PetscCall(DMPlexReferenceTreeGetChildSymmetry(refTree, parent, parentOrnt, ornt, point, DMPolytopeConvertNewOrientation_Internal(rct, pointClosure[2 * rcl + 1]), NULL, &newcid));
2637                 }
2638               }
2639               if (newcid >= 0) {
2640                 if (canonical) PetscCall(DMLabelGetValue(canonical, newcid, &newcid));
2641                 proposedCids[cl] = newcid;
2642               }
2643             }
2644             PetscCall(DMPlexRestoreTransitiveClosure(plexF, c + cLocalStartF, PETSC_TRUE, NULL, &pointClosure));
2645           }
2646           p4est_qcoord_t coarseBound[2][P4EST_DIM] = {
2647             {quadCoarse->x, quadCoarse->y,
2648   #if defined(P4_TO_P8)
2649              quadCoarse->z
2650   #endif
2651             },
2652             {0}
2653           };
2654           p4est_qcoord_t fineBound[2][P4EST_DIM] = {
2655             {quad->x, quad->y,
2656   #if defined(P4_TO_P8)
2657              quad->z
2658   #endif
2659             },
2660             {0}
2661           };
2662           PetscInt j;
2663           for (j = 0; j < P4EST_DIM; j++) { /* get the coordinates of cell boundaries in each direction */
2664             coarseBound[1][j] = coarseBound[0][j] + P4EST_QUADRANT_LEN(quadCoarse->level);
2665             fineBound[1][j]   = fineBound[0][j] + P4EST_QUADRANT_LEN(quad->level);
2666           }
2667           for (j = 0; j < numClosureIndices; j++) {
2668             PetscInt    l, p;
2669             PetscSFNode q;
2670 
2671             p = closurePointsF[numClosureIndices * c + j].index;
2672             if (rootType[p - pStartF] == PETSC_MAX_INT) continue;
2673             if (j == 0) { /* volume: ancestor is volume */
2674               l = 0;
2675             } else if (j < 1 + P4EST_FACES) { /* facet */
2676               PetscInt face       = PetscFaceToP4estFace[j - 1];
2677               PetscInt direction  = face / 2;
2678               PetscInt coarseFace = -1;
2679 
2680               if (coarseBound[face % 2][direction] == fineBound[face % 2][direction]) {
2681                 coarseFace = face;
2682                 l          = 1 + P4estFaceToPetscFace[coarseFace];
2683               } else {
2684                 l = 0;
2685               }
2686   #if defined(P4_TO_P8)
2687             } else if (j < 1 + P4EST_FACES + P8EST_EDGES) {
2688               PetscInt  edge       = PetscEdgeToP4estEdge[j - (1 + P4EST_FACES)];
2689               PetscInt  direction  = edge / 4;
2690               PetscInt  mod        = edge % 4;
2691               PetscInt  coarseEdge = -1, coarseFace = -1;
2692               PetscInt  minDir = PetscMin((direction + 1) % 3, (direction + 2) % 3);
2693               PetscInt  maxDir = PetscMax((direction + 1) % 3, (direction + 2) % 3);
2694               PetscBool dirTest[2];
2695 
2696               dirTest[0] = (PetscBool)(coarseBound[mod % 2][minDir] == fineBound[mod % 2][minDir]);
2697               dirTest[1] = (PetscBool)(coarseBound[mod / 2][maxDir] == fineBound[mod / 2][maxDir]);
2698 
2699               if (dirTest[0] && dirTest[1]) { /* fine edge falls on coarse edge */
2700                 coarseEdge = edge;
2701                 l          = 1 + P4EST_FACES + P4estEdgeToPetscEdge[coarseEdge];
2702               } else if (dirTest[0]) { /* fine edge falls on a coarse face in the minDir direction */
2703                 coarseFace = 2 * minDir + (mod % 2);
2704                 l          = 1 + P4estFaceToPetscFace[coarseFace];
2705               } else if (dirTest[1]) { /* fine edge falls on a coarse face in the maxDir direction */
2706                 coarseFace = 2 * maxDir + (mod / 2);
2707                 l          = 1 + P4estFaceToPetscFace[coarseFace];
2708               } else {
2709                 l = 0;
2710               }
2711   #endif
2712             } else {
2713               PetscInt  vertex = PetscVertToP4estVert[P4EST_CHILDREN - (P4EST_INSUL - j)];
2714               PetscBool dirTest[P4EST_DIM];
2715               PetscInt  m;
2716               PetscInt  numMatch     = 0;
2717               PetscInt  coarseVertex = -1, coarseFace = -1;
2718   #if defined(P4_TO_P8)
2719               PetscInt coarseEdge = -1;
2720   #endif
2721 
2722               for (m = 0; m < P4EST_DIM; m++) {
2723                 dirTest[m] = (PetscBool)(coarseBound[(vertex >> m) & 1][m] == fineBound[(vertex >> m) & 1][m]);
2724                 if (dirTest[m]) numMatch++;
2725               }
2726               if (numMatch == P4EST_DIM) { /* vertex on vertex */
2727                 coarseVertex = vertex;
2728                 l            = P4EST_INSUL - (P4EST_CHILDREN - P4estVertToPetscVert[coarseVertex]);
2729               } else if (numMatch == 1) { /* vertex on face */
2730                 for (m = 0; m < P4EST_DIM; m++) {
2731                   if (dirTest[m]) {
2732                     coarseFace = 2 * m + ((vertex >> m) & 1);
2733                     break;
2734                   }
2735                 }
2736                 l = 1 + P4estFaceToPetscFace[coarseFace];
2737   #if defined(P4_TO_P8)
2738               } else if (numMatch == 2) { /* vertex on edge */
2739                 for (m = 0; m < P4EST_DIM; m++) {
2740                   if (!dirTest[m]) {
2741                     PetscInt otherDir1 = (m + 1) % 3;
2742                     PetscInt otherDir2 = (m + 2) % 3;
2743                     PetscInt minDir    = PetscMin(otherDir1, otherDir2);
2744                     PetscInt maxDir    = PetscMax(otherDir1, otherDir2);
2745 
2746                     coarseEdge = m * 4 + 2 * ((vertex >> maxDir) & 1) + ((vertex >> minDir) & 1);
2747                     break;
2748                   }
2749                 }
2750                 l = 1 + P4EST_FACES + P4estEdgeToPetscEdge[coarseEdge];
2751   #endif
2752               } else { /* volume */
2753                 l = 0;
2754               }
2755             }
2756             q = closurePointsC[numClosureIndices * (coarseCount + coarseOffset) + l];
2757             if (l > rootType[p - pStartF]) {
2758               if (l >= P4EST_INSUL - P4EST_CHILDREN) { /* vertex on vertex: unconditional acceptance */
2759                 if (transferIdent) {
2760                   roots[p - pStartF]    = q;
2761                   rootType[p - pStartF] = PETSC_MAX_INT;
2762                   if (formCids) cids[p - pStartF] = -1;
2763                 }
2764               } else {
2765                 PetscInt k, thisp = p, limit;
2766 
2767                 roots[p - pStartF]    = q;
2768                 rootType[p - pStartF] = l;
2769                 if (formCids) cids[p - pStartF] = proposedCids[j];
2770                 limit = transferIdent ? levelDiff : (levelDiff - 1);
2771                 for (k = 0; k < limit; k++) {
2772                   PetscInt parent;
2773 
2774                   PetscCall(DMPlexGetTreeParent(plexF, thisp, &parent, NULL));
2775                   if (parent == thisp) break;
2776 
2777                   roots[parent - pStartF]    = q;
2778                   rootType[parent - pStartF] = PETSC_MAX_INT;
2779                   if (formCids) cids[parent - pStartF] = -1;
2780                   thisp = parent;
2781                 }
2782               }
2783             }
2784           }
2785         }
2786       }
2787     }
2788 
2789     /* now every cell has labeled the points in its closure, so we first make sure everyone agrees by reducing to roots, and the broadcast the agreements */
2790     if (size > 1) {
2791       PetscInt *rootTypeCopy, p;
2792 
2793       PetscCall(PetscMalloc1(pEndF - pStartF, &rootTypeCopy));
2794       PetscCall(PetscArraycpy(rootTypeCopy, rootType, pEndF - pStartF));
2795       PetscCall(PetscSFReduceBegin(pointSF, MPIU_INT, rootTypeCopy, rootTypeCopy, MPI_MAX));
2796       PetscCall(PetscSFReduceEnd(pointSF, MPIU_INT, rootTypeCopy, rootTypeCopy, MPI_MAX));
2797       PetscCall(PetscSFBcastBegin(pointSF, MPIU_INT, rootTypeCopy, rootTypeCopy, MPI_REPLACE));
2798       PetscCall(PetscSFBcastEnd(pointSF, MPIU_INT, rootTypeCopy, rootTypeCopy, MPI_REPLACE));
2799       for (p = pStartF; p < pEndF; p++) {
2800         if (rootTypeCopy[p - pStartF] > rootType[p - pStartF]) { /* another process found a root of higher type (e.g. vertex instead of edge), which we want to accept, so nullify this */
2801           roots[p - pStartF].rank  = -1;
2802           roots[p - pStartF].index = -1;
2803         }
2804         if (formCids && rootTypeCopy[p - pStartF] == PETSC_MAX_INT) { cids[p - pStartF] = -1; /* we have found an antecedent that is the same: no child id */ }
2805       }
2806       PetscCall(PetscFree(rootTypeCopy));
2807       PetscCall(PetscSFReduceBegin(pointSF, nodeType, roots, roots, sfNodeReduce));
2808       PetscCall(PetscSFReduceEnd(pointSF, nodeType, roots, roots, sfNodeReduce));
2809       PetscCall(PetscSFBcastBegin(pointSF, nodeType, roots, roots, MPI_REPLACE));
2810       PetscCall(PetscSFBcastEnd(pointSF, nodeType, roots, roots, MPI_REPLACE));
2811     }
2812     PetscCall(PetscFree(rootType));
2813 
2814     {
2815       PetscInt     numRoots;
2816       PetscInt     numLeaves;
2817       PetscInt    *leaves;
2818       PetscSFNode *iremote;
2819       /* count leaves */
2820 
2821       numRoots = pEndC - pStartC;
2822 
2823       numLeaves = 0;
2824       for (p = pStartF; p < pEndF; p++) {
2825         if (roots[p - pStartF].index >= 0) numLeaves++;
2826       }
2827       PetscCall(PetscMalloc1(numLeaves, &leaves));
2828       PetscCall(PetscMalloc1(numLeaves, &iremote));
2829       numLeaves = 0;
2830       for (p = pStartF; p < pEndF; p++) {
2831         if (roots[p - pStartF].index >= 0) {
2832           leaves[numLeaves]  = p - pStartF;
2833           iremote[numLeaves] = roots[p - pStartF];
2834           numLeaves++;
2835         }
2836       }
2837       PetscCall(PetscFree(roots));
2838       PetscCall(PetscSFCreate(comm, sf));
2839       if (numLeaves == (pEndF - pStartF)) {
2840         PetscCall(PetscFree(leaves));
2841         PetscCall(PetscSFSetGraph(*sf, numRoots, numLeaves, NULL, PETSC_OWN_POINTER, iremote, PETSC_OWN_POINTER));
2842       } else {
2843         PetscCall(PetscSFSetGraph(*sf, numRoots, numLeaves, leaves, PETSC_OWN_POINTER, iremote, PETSC_OWN_POINTER));
2844       }
2845     }
2846     if (formCids) {
2847       PetscSF  pointSF;
2848       PetscInt child;
2849 
2850       PetscCall(DMPlexGetReferenceTree(plexF, &refTree));
2851       PetscCall(DMGetPointSF(plexF, &pointSF));
2852       PetscCall(PetscSFReduceBegin(pointSF, MPIU_INT, cids, cids, MPI_MAX));
2853       PetscCall(PetscSFReduceEnd(pointSF, MPIU_INT, cids, cids, MPI_MAX));
2854       if (childIds) *childIds = cids;
2855       for (child = 0; child < P4EST_CHILDREN; child++) PetscCall(DMPlexRestoreTransitiveClosure(refTree, child + 1, PETSC_TRUE, NULL, &childClosures[child]));
2856       PetscCall(DMPlexRestoreTransitiveClosure(refTree, 0, PETSC_TRUE, NULL, &rootClosure));
2857     }
2858   }
2859   if (saveInCoarse) { /* cache results */
2860     PetscCall(PetscObjectReference((PetscObject)*sf));
2861     pforestC->pointSelfToAdaptSF = *sf;
2862     if (!childIds) {
2863       pforestC->pointSelfToAdaptCids = cids;
2864     } else {
2865       PetscCall(PetscMalloc1(pEndF - pStartF, &pforestC->pointSelfToAdaptCids));
2866       PetscCall(PetscArraycpy(pforestC->pointSelfToAdaptCids, cids, pEndF - pStartF));
2867     }
2868   } else if (saveInFine) {
2869     PetscCall(PetscObjectReference((PetscObject)*sf));
2870     pforestF->pointAdaptToSelfSF = *sf;
2871     if (!childIds) {
2872       pforestF->pointAdaptToSelfCids = cids;
2873     } else {
2874       PetscCall(PetscMalloc1(pEndF - pStartF, &pforestF->pointAdaptToSelfCids));
2875       PetscCall(PetscArraycpy(pforestF->pointAdaptToSelfCids, cids, pEndF - pStartF));
2876     }
2877   }
2878   PetscCall(PetscFree2(treeQuads, treeQuadCounts));
2879   PetscCall(PetscFree(coverQuads));
2880   PetscCall(PetscFree(closurePointsC));
2881   PetscCall(PetscFree(closurePointsF));
2882   PetscCallMPI(MPI_Type_free(&nodeClosureType));
2883   PetscCallMPI(MPI_Op_free(&sfNodeReduce));
2884   PetscCallMPI(MPI_Type_free(&nodeType));
2885   PetscFunctionReturn(PETSC_SUCCESS);
2886 }
2887 
2888 /* children are sf leaves of parents */
2889 static PetscErrorCode DMPforestGetTransferSF_Internal(DM coarse, DM fine, const PetscInt dofPerDim[], PetscSF *sf, PetscBool transferIdent, PetscInt *childIds[])
2890 {
2891   MPI_Comm           comm;
2892   PetscMPIInt        rank;
2893   DM_Forest_pforest *pforestC, *pforestF;
2894   DM                 plexC, plexF;
2895   PetscInt           pStartC, pEndC, pStartF, pEndF;
2896   PetscSF            pointTransferSF;
2897   PetscBool          allOnes = PETSC_TRUE;
2898 
2899   PetscFunctionBegin;
2900   pforestC = (DM_Forest_pforest *)((DM_Forest *)coarse->data)->data;
2901   pforestF = (DM_Forest_pforest *)((DM_Forest *)fine->data)->data;
2902   PetscCheck(pforestC->topo == pforestF->topo, PetscObjectComm((PetscObject)coarse), PETSC_ERR_ARG_INCOMP, "DM's must have the same base DM");
2903   comm = PetscObjectComm((PetscObject)coarse);
2904   PetscCallMPI(MPI_Comm_rank(comm, &rank));
2905 
2906   {
2907     PetscInt i;
2908     for (i = 0; i <= P4EST_DIM; i++) {
2909       if (dofPerDim[i] != 1) {
2910         allOnes = PETSC_FALSE;
2911         break;
2912       }
2913     }
2914   }
2915   PetscCall(DMPforestGetTransferSF_Point(coarse, fine, &pointTransferSF, transferIdent, childIds));
2916   if (allOnes) {
2917     *sf = pointTransferSF;
2918     PetscFunctionReturn(PETSC_SUCCESS);
2919   }
2920 
2921   PetscCall(DMPforestGetPlex(fine, &plexF));
2922   PetscCall(DMPlexGetChart(plexF, &pStartF, &pEndF));
2923   PetscCall(DMPforestGetPlex(coarse, &plexC));
2924   PetscCall(DMPlexGetChart(plexC, &pStartC, &pEndC));
2925   {
2926     PetscInt           numRoots;
2927     PetscInt           numLeaves;
2928     const PetscInt    *leaves;
2929     const PetscSFNode *iremote;
2930     PetscInt           d;
2931     PetscSection       leafSection, rootSection;
2932     /* count leaves */
2933 
2934     PetscCall(PetscSFGetGraph(pointTransferSF, &numRoots, &numLeaves, &leaves, &iremote));
2935     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &rootSection));
2936     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &leafSection));
2937     PetscCall(PetscSectionSetChart(rootSection, pStartC, pEndC));
2938     PetscCall(PetscSectionSetChart(leafSection, pStartF, pEndF));
2939 
2940     for (d = 0; d <= P4EST_DIM; d++) {
2941       PetscInt startC, endC, e;
2942 
2943       PetscCall(DMPlexGetSimplexOrBoxCells(plexC, P4EST_DIM - d, &startC, &endC));
2944       for (e = startC; e < endC; e++) PetscCall(PetscSectionSetDof(rootSection, e, dofPerDim[d]));
2945     }
2946 
2947     for (d = 0; d <= P4EST_DIM; d++) {
2948       PetscInt startF, endF, e;
2949 
2950       PetscCall(DMPlexGetSimplexOrBoxCells(plexF, P4EST_DIM - d, &startF, &endF));
2951       for (e = startF; e < endF; e++) PetscCall(PetscSectionSetDof(leafSection, e, dofPerDim[d]));
2952     }
2953 
2954     PetscCall(PetscSectionSetUp(rootSection));
2955     PetscCall(PetscSectionSetUp(leafSection));
2956     {
2957       PetscInt     nroots, nleaves;
2958       PetscInt    *mine, i, p;
2959       PetscInt    *offsets, *offsetsRoot;
2960       PetscSFNode *remote;
2961 
2962       PetscCall(PetscMalloc1(pEndF - pStartF, &offsets));
2963       PetscCall(PetscMalloc1(pEndC - pStartC, &offsetsRoot));
2964       for (p = pStartC; p < pEndC; p++) PetscCall(PetscSectionGetOffset(rootSection, p, &offsetsRoot[p - pStartC]));
2965       PetscCall(PetscSFBcastBegin(pointTransferSF, MPIU_INT, offsetsRoot, offsets, MPI_REPLACE));
2966       PetscCall(PetscSFBcastEnd(pointTransferSF, MPIU_INT, offsetsRoot, offsets, MPI_REPLACE));
2967       PetscCall(PetscSectionGetStorageSize(rootSection, &nroots));
2968       nleaves = 0;
2969       for (i = 0; i < numLeaves; i++) {
2970         PetscInt leaf = leaves ? leaves[i] : i;
2971         PetscInt dof;
2972 
2973         PetscCall(PetscSectionGetDof(leafSection, leaf, &dof));
2974         nleaves += dof;
2975       }
2976       PetscCall(PetscMalloc1(nleaves, &mine));
2977       PetscCall(PetscMalloc1(nleaves, &remote));
2978       nleaves = 0;
2979       for (i = 0; i < numLeaves; i++) {
2980         PetscInt leaf = leaves ? leaves[i] : i;
2981         PetscInt dof;
2982         PetscInt off, j;
2983 
2984         PetscCall(PetscSectionGetDof(leafSection, leaf, &dof));
2985         PetscCall(PetscSectionGetOffset(leafSection, leaf, &off));
2986         for (j = 0; j < dof; j++) {
2987           remote[nleaves].rank  = iremote[i].rank;
2988           remote[nleaves].index = offsets[leaf] + j;
2989           mine[nleaves++]       = off + j;
2990         }
2991       }
2992       PetscCall(PetscFree(offsetsRoot));
2993       PetscCall(PetscFree(offsets));
2994       PetscCall(PetscSFCreate(comm, sf));
2995       PetscCall(PetscSFSetGraph(*sf, nroots, nleaves, mine, PETSC_OWN_POINTER, remote, PETSC_OWN_POINTER));
2996     }
2997     PetscCall(PetscSectionDestroy(&leafSection));
2998     PetscCall(PetscSectionDestroy(&rootSection));
2999     PetscCall(PetscSFDestroy(&pointTransferSF));
3000   }
3001   PetscFunctionReturn(PETSC_SUCCESS);
3002 }
3003 
3004 static PetscErrorCode DMPforestGetTransferSF(DM dmA, DM dmB, const PetscInt dofPerDim[], PetscSF *sfAtoB, PetscSF *sfBtoA)
3005 {
3006   DM          adaptA, adaptB;
3007   DMAdaptFlag purpose;
3008 
3009   PetscFunctionBegin;
3010   PetscCall(DMForestGetAdaptivityForest(dmA, &adaptA));
3011   PetscCall(DMForestGetAdaptivityForest(dmB, &adaptB));
3012   /* it is more efficient when the coarser mesh is the first argument: reorder if we know one is coarser than the other */
3013   if (adaptA && adaptA->data == dmB->data) { /* dmA was adapted from dmB */
3014     PetscCall(DMForestGetAdaptivityPurpose(dmA, &purpose));
3015     if (purpose == DM_ADAPT_REFINE) {
3016       PetscCall(DMPforestGetTransferSF(dmB, dmA, dofPerDim, sfBtoA, sfAtoB));
3017       PetscFunctionReturn(PETSC_SUCCESS);
3018     }
3019   } else if (adaptB && adaptB->data == dmA->data) { /* dmB was adapted from dmA */
3020     PetscCall(DMForestGetAdaptivityPurpose(dmB, &purpose));
3021     if (purpose == DM_ADAPT_COARSEN) {
3022       PetscCall(DMPforestGetTransferSF(dmB, dmA, dofPerDim, sfBtoA, sfAtoB));
3023       PetscFunctionReturn(PETSC_SUCCESS);
3024     }
3025   }
3026   if (sfAtoB) PetscCall(DMPforestGetTransferSF_Internal(dmA, dmB, dofPerDim, sfAtoB, PETSC_TRUE, NULL));
3027   if (sfBtoA) PetscCall(DMPforestGetTransferSF_Internal(dmB, dmA, dofPerDim, sfBtoA, (PetscBool)(sfAtoB == NULL), NULL));
3028   PetscFunctionReturn(PETSC_SUCCESS);
3029 }
3030 
3031 static PetscErrorCode DMPforestLabelsInitialize(DM dm, DM plex)
3032 {
3033   DM_Forest         *forest  = (DM_Forest *)dm->data;
3034   DM_Forest_pforest *pforest = (DM_Forest_pforest *)forest->data;
3035   PetscInt           cLocalStart, cLocalEnd, cStart, cEnd, fStart, fEnd, eStart, eEnd, vStart, vEnd;
3036   PetscInt           cStartBase, cEndBase, fStartBase, fEndBase, vStartBase, vEndBase, eStartBase, eEndBase;
3037   PetscInt           pStart, pEnd, pStartBase, pEndBase, p;
3038   DM                 base;
3039   PetscInt          *star      = NULL, starSize;
3040   DMLabelLink        next      = dm->labels;
3041   PetscInt           guess     = 0;
3042   p4est_topidx_t     num_trees = pforest->topo->conn->num_trees;
3043 
3044   PetscFunctionBegin;
3045   pforest->labelsFinalized = PETSC_TRUE;
3046   cLocalStart              = pforest->cLocalStart;
3047   cLocalEnd                = pforest->cLocalEnd;
3048   PetscCall(DMForestGetBaseDM(dm, &base));
3049   if (!base) {
3050     if (pforest->ghostName) { /* insert a label to make the boundaries, with stratum values denoting which face of the element touches the boundary */
3051       p4est_connectivity_t *conn  = pforest->topo->conn;
3052       p4est_t              *p4est = pforest->forest;
3053       p4est_tree_t         *trees = (p4est_tree_t *)p4est->trees->array;
3054       p4est_topidx_t        t, flt = p4est->first_local_tree;
3055       p4est_topidx_t        llt = pforest->forest->last_local_tree;
3056       DMLabel               ghostLabel;
3057       PetscInt              c;
3058 
3059       PetscCall(DMCreateLabel(plex, pforest->ghostName));
3060       PetscCall(DMGetLabel(plex, pforest->ghostName, &ghostLabel));
3061       for (c = cLocalStart, t = flt; t <= llt; t++) {
3062         p4est_tree_t     *tree     = &trees[t];
3063         p4est_quadrant_t *quads    = (p4est_quadrant_t *)tree->quadrants.array;
3064         PetscInt          numQuads = (PetscInt)tree->quadrants.elem_count;
3065         PetscInt          q;
3066 
3067         for (q = 0; q < numQuads; q++, c++) {
3068           p4est_quadrant_t *quad = &quads[q];
3069           PetscInt          f;
3070 
3071           for (f = 0; f < P4EST_FACES; f++) {
3072             p4est_quadrant_t neigh;
3073             int              isOutside;
3074 
3075             PetscCallP4est(p4est_quadrant_face_neighbor, (quad, f, &neigh));
3076             PetscCallP4estReturn(isOutside, p4est_quadrant_is_outside_face, (&neigh));
3077             if (isOutside) {
3078               p4est_topidx_t nt;
3079               PetscInt       nf;
3080 
3081               nt = conn->tree_to_tree[t * P4EST_FACES + f];
3082               nf = (PetscInt)conn->tree_to_face[t * P4EST_FACES + f];
3083               nf = nf % P4EST_FACES;
3084               if (nt == t && nf == f) {
3085                 PetscInt        plexF = P4estFaceToPetscFace[f];
3086                 const PetscInt *cone;
3087 
3088                 PetscCall(DMPlexGetCone(plex, c, &cone));
3089                 PetscCall(DMLabelSetValue(ghostLabel, cone[plexF], plexF + 1));
3090               }
3091             }
3092           }
3093         }
3094       }
3095     }
3096     PetscFunctionReturn(PETSC_SUCCESS);
3097   }
3098   PetscCall(DMPlexGetSimplexOrBoxCells(base, 0, &cStartBase, &cEndBase));
3099   PetscCall(DMPlexGetSimplexOrBoxCells(base, 1, &fStartBase, &fEndBase));
3100   PetscCall(DMPlexGetSimplexOrBoxCells(base, P4EST_DIM - 1, &eStartBase, &eEndBase));
3101   PetscCall(DMPlexGetDepthStratum(base, 0, &vStartBase, &vEndBase));
3102 
3103   PetscCall(DMPlexGetSimplexOrBoxCells(plex, 0, &cStart, &cEnd));
3104   PetscCall(DMPlexGetSimplexOrBoxCells(plex, 1, &fStart, &fEnd));
3105   PetscCall(DMPlexGetSimplexOrBoxCells(plex, P4EST_DIM - 1, &eStart, &eEnd));
3106   PetscCall(DMPlexGetDepthStratum(plex, 0, &vStart, &vEnd));
3107 
3108   PetscCall(DMPlexGetChart(plex, &pStart, &pEnd));
3109   PetscCall(DMPlexGetChart(base, &pStartBase, &pEndBase));
3110   /* go through the mesh: use star to find a quadrant that borders a point.  Use the closure to determine the
3111    * orientation of the quadrant relative to that point.  Use that to relate the point to the numbering in the base
3112    * mesh, and extract a label value (since the base mesh is redundantly distributed, can be found locally). */
3113   while (next) {
3114     DMLabel     baseLabel;
3115     DMLabel     label = next->label;
3116     PetscBool   isDepth, isCellType, isGhost, isVTK, isSpmap;
3117     const char *name;
3118 
3119     PetscCall(PetscObjectGetName((PetscObject)label, &name));
3120     PetscCall(PetscStrcmp(name, "depth", &isDepth));
3121     if (isDepth) {
3122       next = next->next;
3123       continue;
3124     }
3125     PetscCall(PetscStrcmp(name, "celltype", &isCellType));
3126     if (isCellType) {
3127       next = next->next;
3128       continue;
3129     }
3130     PetscCall(PetscStrcmp(name, "ghost", &isGhost));
3131     if (isGhost) {
3132       next = next->next;
3133       continue;
3134     }
3135     PetscCall(PetscStrcmp(name, "vtk", &isVTK));
3136     if (isVTK) {
3137       next = next->next;
3138       continue;
3139     }
3140     PetscCall(PetscStrcmp(name, "_forest_base_subpoint_map", &isSpmap));
3141     if (!isSpmap) {
3142       PetscCall(DMGetLabel(base, name, &baseLabel));
3143       if (!baseLabel) {
3144         next = next->next;
3145         continue;
3146       }
3147       PetscCall(DMLabelCreateIndex(baseLabel, pStartBase, pEndBase));
3148     } else baseLabel = NULL;
3149 
3150     for (p = pStart; p < pEnd; p++) {
3151       PetscInt          s, c = -1, l;
3152       PetscInt         *closure = NULL, closureSize;
3153       p4est_quadrant_t *ghosts  = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
3154       p4est_tree_t     *trees   = (p4est_tree_t *)pforest->forest->trees->array;
3155       p4est_quadrant_t *q;
3156       PetscInt          t, val;
3157       PetscBool         zerosupportpoint = PETSC_FALSE;
3158 
3159       PetscCall(DMPlexGetTransitiveClosure(plex, p, PETSC_FALSE, &starSize, &star));
3160       for (s = 0; s < starSize; s++) {
3161         PetscInt point = star[2 * s];
3162 
3163         if (cStart <= point && point < cEnd) {
3164           PetscCall(DMPlexGetTransitiveClosure(plex, point, PETSC_TRUE, &closureSize, &closure));
3165           for (l = 0; l < closureSize; l++) {
3166             PetscInt qParent = closure[2 * l], q, pp = p, pParent = p;
3167             do { /* check parents of q */
3168               q = qParent;
3169               if (q == p) {
3170                 c = point;
3171                 break;
3172               }
3173               PetscCall(DMPlexGetTreeParent(plex, q, &qParent, NULL));
3174             } while (qParent != q);
3175             if (c != -1) break;
3176             PetscCall(DMPlexGetTreeParent(plex, pp, &pParent, NULL));
3177             q = closure[2 * l];
3178             while (pParent != pp) { /* check parents of p */
3179               pp = pParent;
3180               if (pp == q) {
3181                 c = point;
3182                 break;
3183               }
3184               PetscCall(DMPlexGetTreeParent(plex, pp, &pParent, NULL));
3185             }
3186             if (c != -1) break;
3187           }
3188           PetscCall(DMPlexRestoreTransitiveClosure(plex, point, PETSC_TRUE, NULL, &closure));
3189           if (l < closureSize) break;
3190         } else {
3191           PetscInt supportSize;
3192 
3193           PetscCall(DMPlexGetSupportSize(plex, point, &supportSize));
3194           zerosupportpoint = (PetscBool)(zerosupportpoint || !supportSize);
3195         }
3196       }
3197       if (c < 0) {
3198         const char *prefix;
3199         PetscBool   print = PETSC_FALSE;
3200 
3201         PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
3202         PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, prefix, "-dm_forest_print_label_error", &print, NULL));
3203         if (print) {
3204           PetscInt i;
3205 
3206           PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d] Failed to find cell with point %" PetscInt_FMT " in its closure for label %s (starSize %" PetscInt_FMT ")\n", PetscGlobalRank, p, baseLabel ? ((PetscObject)baseLabel)->name : "_forest_base_subpoint_map", starSize));
3207           for (i = 0; i < starSize; i++) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  star[%" PetscInt_FMT "] = %" PetscInt_FMT ",%" PetscInt_FMT "\n", i, star[2 * i], star[2 * i + 1]));
3208         }
3209         PetscCall(DMPlexRestoreTransitiveClosure(plex, p, PETSC_FALSE, NULL, &star));
3210         if (zerosupportpoint) continue;
3211         else
3212           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Failed to find cell with point %" PetscInt_FMT " in its closure for label %s. Rerun with -dm_forest_print_label_error for more information", p, baseLabel ? ((PetscObject)baseLabel)->name : "_forest_base_subpoint_map");
3213       }
3214       PetscCall(DMPlexRestoreTransitiveClosure(plex, p, PETSC_FALSE, NULL, &star));
3215 
3216       if (c < cLocalStart) {
3217         /* get from the beginning of the ghost layer */
3218         q = &(ghosts[c]);
3219         t = (PetscInt)q->p.which_tree;
3220       } else if (c < cLocalEnd) {
3221         PetscInt lo = 0, hi = num_trees;
3222         /* get from local quadrants: have to find the right tree */
3223 
3224         c -= cLocalStart;
3225 
3226         do {
3227           p4est_tree_t *tree;
3228 
3229           PetscCheck(guess >= lo && guess < num_trees && lo < hi, PETSC_COMM_SELF, PETSC_ERR_PLIB, "failed binary search");
3230           tree = &trees[guess];
3231           if (c < tree->quadrants_offset) {
3232             hi = guess;
3233           } else if (c < tree->quadrants_offset + (PetscInt)tree->quadrants.elem_count) {
3234             q = &((p4est_quadrant_t *)tree->quadrants.array)[c - (PetscInt)tree->quadrants_offset];
3235             t = guess;
3236             break;
3237           } else {
3238             lo = guess + 1;
3239           }
3240           guess = lo + (hi - lo) / 2;
3241         } while (1);
3242       } else {
3243         /* get from the end of the ghost layer */
3244         c -= (cLocalEnd - cLocalStart);
3245 
3246         q = &(ghosts[c]);
3247         t = (PetscInt)q->p.which_tree;
3248       }
3249 
3250       if (l == 0) { /* cell */
3251         if (baseLabel) {
3252           PetscCall(DMLabelGetValue(baseLabel, t + cStartBase, &val));
3253         } else {
3254           val = t + cStartBase;
3255         }
3256         PetscCall(DMLabelSetValue(label, p, val));
3257       } else if (l >= 1 && l < 1 + P4EST_FACES) { /* facet */
3258         p4est_quadrant_t nq;
3259         int              isInside;
3260 
3261         l = PetscFaceToP4estFace[l - 1];
3262         PetscCallP4est(p4est_quadrant_face_neighbor, (q, l, &nq));
3263         PetscCallP4estReturn(isInside, p4est_quadrant_is_inside_root, (&nq));
3264         if (isInside) {
3265           /* this facet is in the interior of a tree, so it inherits the label of the tree */
3266           if (baseLabel) {
3267             PetscCall(DMLabelGetValue(baseLabel, t + cStartBase, &val));
3268           } else {
3269             val = t + cStartBase;
3270           }
3271           PetscCall(DMLabelSetValue(label, p, val));
3272         } else {
3273           PetscInt f = pforest->topo->tree_face_to_uniq[P4EST_FACES * t + l];
3274 
3275           if (baseLabel) {
3276             PetscCall(DMLabelGetValue(baseLabel, f + fStartBase, &val));
3277           } else {
3278             val = f + fStartBase;
3279           }
3280           PetscCall(DMLabelSetValue(label, p, val));
3281         }
3282   #if defined(P4_TO_P8)
3283       } else if (l >= 1 + P4EST_FACES && l < 1 + P4EST_FACES + P8EST_EDGES) { /* edge */
3284         p4est_quadrant_t nq;
3285         int              isInside;
3286 
3287         l = PetscEdgeToP4estEdge[l - (1 + P4EST_FACES)];
3288         PetscCallP4est(p8est_quadrant_edge_neighbor, (q, l, &nq));
3289         PetscCallP4estReturn(isInside, p4est_quadrant_is_inside_root, (&nq));
3290         if (isInside) {
3291           /* this edge is in the interior of a tree, so it inherits the label of the tree */
3292           if (baseLabel) {
3293             PetscCall(DMLabelGetValue(baseLabel, t + cStartBase, &val));
3294           } else {
3295             val = t + cStartBase;
3296           }
3297           PetscCall(DMLabelSetValue(label, p, val));
3298         } else {
3299           int isOutsideFace;
3300 
3301           PetscCallP4estReturn(isOutsideFace, p4est_quadrant_is_outside_face, (&nq));
3302           if (isOutsideFace) {
3303             PetscInt f;
3304 
3305             if (nq.x < 0) {
3306               f = 0;
3307             } else if (nq.x >= P4EST_ROOT_LEN) {
3308               f = 1;
3309             } else if (nq.y < 0) {
3310               f = 2;
3311             } else if (nq.y >= P4EST_ROOT_LEN) {
3312               f = 3;
3313             } else if (nq.z < 0) {
3314               f = 4;
3315             } else {
3316               f = 5;
3317             }
3318             f = pforest->topo->tree_face_to_uniq[P4EST_FACES * t + f];
3319             if (baseLabel) {
3320               PetscCall(DMLabelGetValue(baseLabel, f + fStartBase, &val));
3321             } else {
3322               val = f + fStartBase;
3323             }
3324             PetscCall(DMLabelSetValue(label, p, val));
3325           } else { /* the quadrant edge corresponds to the tree edge */
3326             PetscInt e = pforest->topo->conn->tree_to_edge[P8EST_EDGES * t + l];
3327 
3328             if (baseLabel) {
3329               PetscCall(DMLabelGetValue(baseLabel, e + eStartBase, &val));
3330             } else {
3331               val = e + eStartBase;
3332             }
3333             PetscCall(DMLabelSetValue(label, p, val));
3334           }
3335         }
3336   #endif
3337       } else { /* vertex */
3338         p4est_quadrant_t nq;
3339         int              isInside;
3340 
3341   #if defined(P4_TO_P8)
3342         l = PetscVertToP4estVert[l - (1 + P4EST_FACES + P8EST_EDGES)];
3343   #else
3344         l = PetscVertToP4estVert[l - (1 + P4EST_FACES)];
3345   #endif
3346         PetscCallP4est(p4est_quadrant_corner_neighbor, (q, l, &nq));
3347         PetscCallP4estReturn(isInside, p4est_quadrant_is_inside_root, (&nq));
3348         if (isInside) {
3349           if (baseLabel) {
3350             PetscCall(DMLabelGetValue(baseLabel, t + cStartBase, &val));
3351           } else {
3352             val = t + cStartBase;
3353           }
3354           PetscCall(DMLabelSetValue(label, p, val));
3355         } else {
3356           int isOutside;
3357 
3358           PetscCallP4estReturn(isOutside, p4est_quadrant_is_outside_face, (&nq));
3359           if (isOutside) {
3360             PetscInt f = -1;
3361 
3362             if (nq.x < 0) {
3363               f = 0;
3364             } else if (nq.x >= P4EST_ROOT_LEN) {
3365               f = 1;
3366             } else if (nq.y < 0) {
3367               f = 2;
3368             } else if (nq.y >= P4EST_ROOT_LEN) {
3369               f = 3;
3370   #if defined(P4_TO_P8)
3371             } else if (nq.z < 0) {
3372               f = 4;
3373             } else {
3374               f = 5;
3375   #endif
3376             }
3377             f = pforest->topo->tree_face_to_uniq[P4EST_FACES * t + f];
3378             if (baseLabel) {
3379               PetscCall(DMLabelGetValue(baseLabel, f + fStartBase, &val));
3380             } else {
3381               val = f + fStartBase;
3382             }
3383             PetscCall(DMLabelSetValue(label, p, val));
3384             continue;
3385           }
3386   #if defined(P4_TO_P8)
3387           PetscCallP4estReturn(isOutside, p8est_quadrant_is_outside_edge, (&nq));
3388           if (isOutside) {
3389             /* outside edge */
3390             PetscInt e = -1;
3391 
3392             if (nq.x >= 0 && nq.x < P4EST_ROOT_LEN) {
3393               if (nq.z < 0) {
3394                 if (nq.y < 0) {
3395                   e = 0;
3396                 } else {
3397                   e = 1;
3398                 }
3399               } else {
3400                 if (nq.y < 0) {
3401                   e = 2;
3402                 } else {
3403                   e = 3;
3404                 }
3405               }
3406             } else if (nq.y >= 0 && nq.y < P4EST_ROOT_LEN) {
3407               if (nq.z < 0) {
3408                 if (nq.x < 0) {
3409                   e = 4;
3410                 } else {
3411                   e = 5;
3412                 }
3413               } else {
3414                 if (nq.x < 0) {
3415                   e = 6;
3416                 } else {
3417                   e = 7;
3418                 }
3419               }
3420             } else {
3421               if (nq.y < 0) {
3422                 if (nq.x < 0) {
3423                   e = 8;
3424                 } else {
3425                   e = 9;
3426                 }
3427               } else {
3428                 if (nq.x < 0) {
3429                   e = 10;
3430                 } else {
3431                   e = 11;
3432                 }
3433               }
3434             }
3435 
3436             e = pforest->topo->conn->tree_to_edge[P8EST_EDGES * t + e];
3437             if (baseLabel) {
3438               PetscCall(DMLabelGetValue(baseLabel, e + eStartBase, &val));
3439             } else {
3440               val = e + eStartBase;
3441             }
3442             PetscCall(DMLabelSetValue(label, p, val));
3443             continue;
3444           }
3445   #endif
3446           {
3447             /* outside vertex: same corner as quadrant corner */
3448             PetscInt v = pforest->topo->conn->tree_to_corner[P4EST_CHILDREN * t + l];
3449 
3450             if (baseLabel) {
3451               PetscCall(DMLabelGetValue(baseLabel, v + vStartBase, &val));
3452             } else {
3453               val = v + vStartBase;
3454             }
3455             PetscCall(DMLabelSetValue(label, p, val));
3456           }
3457         }
3458       }
3459     }
3460     next = next->next;
3461   }
3462   PetscFunctionReturn(PETSC_SUCCESS);
3463 }
3464 
3465 static PetscErrorCode DMPforestLabelsFinalize(DM dm, DM plex)
3466 {
3467   DM_Forest_pforest *pforest = (DM_Forest_pforest *)((DM_Forest *)dm->data)->data;
3468   DM                 adapt;
3469 
3470   PetscFunctionBegin;
3471   if (pforest->labelsFinalized) PetscFunctionReturn(PETSC_SUCCESS);
3472   pforest->labelsFinalized = PETSC_TRUE;
3473   PetscCall(DMForestGetAdaptivityForest(dm, &adapt));
3474   if (!adapt) {
3475     /* Initialize labels from the base dm */
3476     PetscCall(DMPforestLabelsInitialize(dm, plex));
3477   } else {
3478     PetscInt    dofPerDim[4] = {1, 1, 1, 1};
3479     PetscSF     transferForward, transferBackward, pointSF;
3480     PetscInt    pStart, pEnd, pStartA, pEndA;
3481     PetscInt   *values, *adaptValues;
3482     DMLabelLink next = adapt->labels;
3483     DMLabel     adaptLabel;
3484     DM          adaptPlex;
3485 
3486     PetscCall(DMForestGetAdaptivityLabel(dm, &adaptLabel));
3487     PetscCall(DMPforestGetPlex(adapt, &adaptPlex));
3488     PetscCall(DMPforestGetTransferSF(adapt, dm, dofPerDim, &transferForward, &transferBackward));
3489     PetscCall(DMPlexGetChart(plex, &pStart, &pEnd));
3490     PetscCall(DMPlexGetChart(adaptPlex, &pStartA, &pEndA));
3491     PetscCall(PetscMalloc2(pEnd - pStart, &values, pEndA - pStartA, &adaptValues));
3492     PetscCall(DMGetPointSF(plex, &pointSF));
3493     if (PetscDefined(USE_DEBUG)) {
3494       PetscInt p;
3495       for (p = pStartA; p < pEndA; p++) adaptValues[p - pStartA] = -1;
3496       for (p = pStart; p < pEnd; p++) values[p - pStart] = -2;
3497       if (transferForward) {
3498         PetscCall(PetscSFBcastBegin(transferForward, MPIU_INT, adaptValues, values, MPI_REPLACE));
3499         PetscCall(PetscSFBcastEnd(transferForward, MPIU_INT, adaptValues, values, MPI_REPLACE));
3500       }
3501       if (transferBackward) {
3502         PetscCall(PetscSFReduceBegin(transferBackward, MPIU_INT, adaptValues, values, MPI_MAX));
3503         PetscCall(PetscSFReduceEnd(transferBackward, MPIU_INT, adaptValues, values, MPI_MAX));
3504       }
3505       for (p = pStart; p < pEnd; p++) {
3506         PetscInt q = p, parent;
3507 
3508         PetscCall(DMPlexGetTreeParent(plex, q, &parent, NULL));
3509         while (parent != q) {
3510           if (values[parent] == -2) values[parent] = values[q];
3511           q = parent;
3512           PetscCall(DMPlexGetTreeParent(plex, q, &parent, NULL));
3513         }
3514       }
3515       PetscCall(PetscSFReduceBegin(pointSF, MPIU_INT, values, values, MPI_MAX));
3516       PetscCall(PetscSFReduceEnd(pointSF, MPIU_INT, values, values, MPI_MAX));
3517       PetscCall(PetscSFBcastBegin(pointSF, MPIU_INT, values, values, MPI_REPLACE));
3518       PetscCall(PetscSFBcastEnd(pointSF, MPIU_INT, values, values, MPI_REPLACE));
3519       for (p = pStart; p < pEnd; p++) PetscCheck(values[p - pStart] != -2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "uncovered point %" PetscInt_FMT, p);
3520     }
3521     while (next) {
3522       DMLabel     nextLabel = next->label;
3523       const char *name;
3524       PetscBool   isDepth, isCellType, isGhost, isVTK;
3525       DMLabel     label;
3526       PetscInt    p;
3527 
3528       PetscCall(PetscObjectGetName((PetscObject)nextLabel, &name));
3529       PetscCall(PetscStrcmp(name, "depth", &isDepth));
3530       if (isDepth) {
3531         next = next->next;
3532         continue;
3533       }
3534       PetscCall(PetscStrcmp(name, "celltype", &isCellType));
3535       if (isCellType) {
3536         next = next->next;
3537         continue;
3538       }
3539       PetscCall(PetscStrcmp(name, "ghost", &isGhost));
3540       if (isGhost) {
3541         next = next->next;
3542         continue;
3543       }
3544       PetscCall(PetscStrcmp(name, "vtk", &isVTK));
3545       if (isVTK) {
3546         next = next->next;
3547         continue;
3548       }
3549       if (nextLabel == adaptLabel) {
3550         next = next->next;
3551         continue;
3552       }
3553       /* label was created earlier */
3554       PetscCall(DMGetLabel(dm, name, &label));
3555       for (p = pStartA; p < pEndA; p++) PetscCall(DMLabelGetValue(nextLabel, p, &adaptValues[p]));
3556       for (p = pStart; p < pEnd; p++) values[p] = PETSC_MIN_INT;
3557 
3558       if (transferForward) PetscCall(PetscSFBcastBegin(transferForward, MPIU_INT, adaptValues, values, MPI_REPLACE));
3559       if (transferBackward) PetscCall(PetscSFReduceBegin(transferBackward, MPIU_INT, adaptValues, values, MPI_MAX));
3560       if (transferForward) PetscCall(PetscSFBcastEnd(transferForward, MPIU_INT, adaptValues, values, MPI_REPLACE));
3561       if (transferBackward) PetscCall(PetscSFReduceEnd(transferBackward, MPIU_INT, adaptValues, values, MPI_MAX));
3562       for (p = pStart; p < pEnd; p++) {
3563         PetscInt q = p, parent;
3564 
3565         PetscCall(DMPlexGetTreeParent(plex, q, &parent, NULL));
3566         while (parent != q) {
3567           if (values[parent] == PETSC_MIN_INT) values[parent] = values[q];
3568           q = parent;
3569           PetscCall(DMPlexGetTreeParent(plex, q, &parent, NULL));
3570         }
3571       }
3572       PetscCall(PetscSFReduceBegin(pointSF, MPIU_INT, values, values, MPI_MAX));
3573       PetscCall(PetscSFReduceEnd(pointSF, MPIU_INT, values, values, MPI_MAX));
3574       PetscCall(PetscSFBcastBegin(pointSF, MPIU_INT, values, values, MPI_REPLACE));
3575       PetscCall(PetscSFBcastEnd(pointSF, MPIU_INT, values, values, MPI_REPLACE));
3576 
3577       for (p = pStart; p < pEnd; p++) PetscCall(DMLabelSetValue(label, p, values[p]));
3578       next = next->next;
3579     }
3580     PetscCall(PetscFree2(values, adaptValues));
3581     PetscCall(PetscSFDestroy(&transferForward));
3582     PetscCall(PetscSFDestroy(&transferBackward));
3583     pforest->labelsFinalized = PETSC_TRUE;
3584   }
3585   PetscFunctionReturn(PETSC_SUCCESS);
3586 }
3587 
3588 static PetscErrorCode DMPforestMapCoordinates_Cell(DM plex, p4est_geometry_t *geom, PetscInt cell, p4est_quadrant_t *q, p4est_topidx_t t, p4est_connectivity_t *conn, PetscScalar *coords)
3589 {
3590   PetscInt     closureSize, c, coordStart, coordEnd, coordDim;
3591   PetscInt    *closure = NULL;
3592   PetscSection coordSec;
3593 
3594   PetscFunctionBegin;
3595   PetscCall(DMGetCoordinateSection(plex, &coordSec));
3596   PetscCall(PetscSectionGetChart(coordSec, &coordStart, &coordEnd));
3597   PetscCall(DMGetCoordinateDim(plex, &coordDim));
3598   PetscCall(DMPlexGetTransitiveClosure(plex, cell, PETSC_TRUE, &closureSize, &closure));
3599   for (c = 0; c < closureSize; c++) {
3600     PetscInt point = closure[2 * c];
3601 
3602     if (point >= coordStart && point < coordEnd) {
3603       PetscInt dof, off;
3604       PetscInt nCoords, i;
3605       PetscCall(PetscSectionGetDof(coordSec, point, &dof));
3606       PetscCheck(dof % coordDim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not understand coordinate layout");
3607       nCoords = dof / coordDim;
3608       PetscCall(PetscSectionGetOffset(coordSec, point, &off));
3609       for (i = 0; i < nCoords; i++) {
3610         PetscScalar *coord               = &coords[off + i * coordDim];
3611         double       coordP4est[3]       = {0.};
3612         double       coordP4estMapped[3] = {0.};
3613         PetscInt     j;
3614         PetscReal    treeCoords[P4EST_CHILDREN][3] = {{0.}};
3615         PetscReal    eta[3]                        = {0.};
3616         PetscInt     numRounds                     = 10;
3617         PetscReal    coordGuess[3]                 = {0.};
3618 
3619         eta[0] = (PetscReal)q->x / (PetscReal)P4EST_ROOT_LEN;
3620         eta[1] = (PetscReal)q->y / (PetscReal)P4EST_ROOT_LEN;
3621   #if defined(P4_TO_P8)
3622         eta[2] = (PetscReal)q->z / (PetscReal)P4EST_ROOT_LEN;
3623   #endif
3624 
3625         for (j = 0; j < P4EST_CHILDREN; j++) {
3626           PetscInt k;
3627 
3628           for (k = 0; k < 3; k++) treeCoords[j][k] = conn->vertices[3 * conn->tree_to_vertex[P4EST_CHILDREN * t + j] + k];
3629         }
3630 
3631         for (j = 0; j < P4EST_CHILDREN; j++) {
3632           PetscInt  k;
3633           PetscReal prod = 1.;
3634 
3635           for (k = 0; k < P4EST_DIM; k++) prod *= (j & (1 << k)) ? eta[k] : (1. - eta[k]);
3636           for (k = 0; k < 3; k++) coordGuess[k] += prod * treeCoords[j][k];
3637         }
3638 
3639         for (j = 0; j < numRounds; j++) {
3640           PetscInt dir;
3641 
3642           for (dir = 0; dir < P4EST_DIM; dir++) {
3643             PetscInt  k;
3644             PetscReal diff[3];
3645             PetscReal dXdeta[3] = {0.};
3646             PetscReal rhs, scale, update;
3647 
3648             for (k = 0; k < 3; k++) diff[k] = coordP4est[k] - coordGuess[k];
3649             for (k = 0; k < P4EST_CHILDREN; k++) {
3650               PetscInt  l;
3651               PetscReal prod = 1.;
3652 
3653               for (l = 0; l < P4EST_DIM; l++) {
3654                 if (l == dir) {
3655                   prod *= (k & (1 << l)) ? 1. : -1.;
3656                 } else {
3657                   prod *= (k & (1 << l)) ? eta[l] : (1. - eta[l]);
3658                 }
3659               }
3660               for (l = 0; l < 3; l++) dXdeta[l] += prod * treeCoords[k][l];
3661             }
3662             rhs   = 0.;
3663             scale = 0;
3664             for (k = 0; k < 3; k++) {
3665               rhs += diff[k] * dXdeta[k];
3666               scale += dXdeta[k] * dXdeta[k];
3667             }
3668             update = rhs / scale;
3669             eta[dir] += update;
3670             eta[dir] = PetscMin(eta[dir], 1.);
3671             eta[dir] = PetscMax(eta[dir], 0.);
3672 
3673             coordGuess[0] = coordGuess[1] = coordGuess[2] = 0.;
3674             for (k = 0; k < P4EST_CHILDREN; k++) {
3675               PetscInt  l;
3676               PetscReal prod = 1.;
3677 
3678               for (l = 0; l < P4EST_DIM; l++) prod *= (k & (1 << l)) ? eta[l] : (1. - eta[l]);
3679               for (l = 0; l < 3; l++) coordGuess[l] += prod * treeCoords[k][l];
3680             }
3681           }
3682         }
3683         for (j = 0; j < 3; j++) coordP4est[j] = (double)eta[j];
3684 
3685         if (geom) {
3686           (geom->X)(geom, t, coordP4est, coordP4estMapped);
3687           for (j = 0; j < coordDim; j++) coord[j] = (PetscScalar)coordP4estMapped[j];
3688         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Not coded");
3689       }
3690     }
3691   }
3692   PetscCall(DMPlexRestoreTransitiveClosure(plex, cell, PETSC_TRUE, &closureSize, &closure));
3693   PetscFunctionReturn(PETSC_SUCCESS);
3694 }
3695 
3696 static PetscErrorCode DMPforestMapCoordinates(DM dm, DM plex)
3697 {
3698   DM_Forest         *forest;
3699   DM_Forest_pforest *pforest;
3700   p4est_geometry_t  *geom;
3701   PetscInt           cLocalStart, cLocalEnd;
3702   Vec                coordLocalVec;
3703   PetscScalar       *coords;
3704   p4est_topidx_t     flt, llt, t;
3705   p4est_tree_t      *trees;
3706   PetscErrorCode (*map)(DM, PetscInt, PetscInt, const PetscReal[], PetscReal[], void *);
3707   void *mapCtx;
3708 
3709   PetscFunctionBegin;
3710   forest  = (DM_Forest *)dm->data;
3711   pforest = (DM_Forest_pforest *)forest->data;
3712   geom    = pforest->topo->geom;
3713   PetscCall(DMForestGetBaseCoordinateMapping(dm, &map, &mapCtx));
3714   if (!geom && !map) PetscFunctionReturn(PETSC_SUCCESS);
3715   PetscCall(DMGetCoordinatesLocal(plex, &coordLocalVec));
3716   PetscCall(VecGetArray(coordLocalVec, &coords));
3717   cLocalStart = pforest->cLocalStart;
3718   cLocalEnd   = pforest->cLocalEnd;
3719   flt         = pforest->forest->first_local_tree;
3720   llt         = pforest->forest->last_local_tree;
3721   trees       = (p4est_tree_t *)pforest->forest->trees->array;
3722   if (map) { /* apply the map directly to the existing coordinates */
3723     PetscSection coordSec;
3724     PetscInt     coordStart, coordEnd, p, coordDim, p4estCoordDim, cStart, cEnd, cEndInterior;
3725     DM           base;
3726 
3727     PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, &cEnd));
3728     PetscCall(DMPlexGetCellTypeStratum(plex, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3729     cEnd = cEndInterior < 0 ? cEnd : cEndInterior;
3730     PetscCall(DMForestGetBaseDM(dm, &base));
3731     PetscCall(DMGetCoordinateSection(plex, &coordSec));
3732     PetscCall(PetscSectionGetChart(coordSec, &coordStart, &coordEnd));
3733     PetscCall(DMGetCoordinateDim(plex, &coordDim));
3734     p4estCoordDim = PetscMin(coordDim, 3);
3735     for (p = coordStart; p < coordEnd; p++) {
3736       PetscInt *star = NULL, starSize;
3737       PetscInt  dof, off, cell = -1, coarsePoint = -1;
3738       PetscInt  nCoords, i;
3739       PetscCall(PetscSectionGetDof(coordSec, p, &dof));
3740       PetscCheck(dof % coordDim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not understand coordinate layout");
3741       nCoords = dof / coordDim;
3742       PetscCall(PetscSectionGetOffset(coordSec, p, &off));
3743       PetscCall(DMPlexGetTransitiveClosure(plex, p, PETSC_FALSE, &starSize, &star));
3744       for (i = 0; i < starSize; i++) {
3745         PetscInt point = star[2 * i];
3746 
3747         if (cStart <= point && point < cEnd) {
3748           cell = point;
3749           break;
3750         }
3751       }
3752       PetscCall(DMPlexRestoreTransitiveClosure(plex, p, PETSC_FALSE, &starSize, &star));
3753       if (cell >= 0) {
3754         if (cell < cLocalStart) {
3755           p4est_quadrant_t *ghosts = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
3756 
3757           coarsePoint = ghosts[cell].p.which_tree;
3758         } else if (cell < cLocalEnd) {
3759           cell -= cLocalStart;
3760           for (t = flt; t <= llt; t++) {
3761             p4est_tree_t *tree = &(trees[t]);
3762 
3763             if (cell >= tree->quadrants_offset && (size_t)cell < tree->quadrants_offset + tree->quadrants.elem_count) {
3764               coarsePoint = t;
3765               break;
3766             }
3767           }
3768         } else {
3769           p4est_quadrant_t *ghosts = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
3770 
3771           coarsePoint = ghosts[cell - cLocalEnd].p.which_tree;
3772         }
3773       }
3774       for (i = 0; i < nCoords; i++) {
3775         PetscScalar *coord               = &coords[off + i * coordDim];
3776         PetscReal    coordP4est[3]       = {0.};
3777         PetscReal    coordP4estMapped[3] = {0.};
3778         PetscInt     j;
3779 
3780         for (j = 0; j < p4estCoordDim; j++) coordP4est[j] = PetscRealPart(coord[j]);
3781         PetscCall((map)(base, coarsePoint, p4estCoordDim, coordP4est, coordP4estMapped, mapCtx));
3782         for (j = 0; j < p4estCoordDim; j++) coord[j] = (PetscScalar)coordP4estMapped[j];
3783       }
3784     }
3785   } else { /* we have to transform coordinates back to the unit cube (where geom is defined), and then apply geom */
3786     PetscInt cStart, cEnd, cEndInterior;
3787 
3788     PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, &cEnd));
3789     PetscCall(DMPlexGetCellTypeStratum(plex, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3790     cEnd = cEndInterior < 0 ? cEnd : cEndInterior;
3791     if (cLocalStart > 0) {
3792       p4est_quadrant_t *ghosts = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
3793       PetscInt          count;
3794 
3795       for (count = 0; count < cLocalStart; count++) {
3796         p4est_quadrant_t *quad = &ghosts[count];
3797         p4est_topidx_t    t    = quad->p.which_tree;
3798 
3799         PetscCall(DMPforestMapCoordinates_Cell(plex, geom, count, quad, t, pforest->topo->conn, coords));
3800       }
3801     }
3802     for (t = flt; t <= llt; t++) {
3803       p4est_tree_t     *tree     = &(trees[t]);
3804       PetscInt          offset   = cLocalStart + tree->quadrants_offset, i;
3805       PetscInt          numQuads = (PetscInt)tree->quadrants.elem_count;
3806       p4est_quadrant_t *quads    = (p4est_quadrant_t *)tree->quadrants.array;
3807 
3808       for (i = 0; i < numQuads; i++) {
3809         PetscInt count = i + offset;
3810 
3811         PetscCall(DMPforestMapCoordinates_Cell(plex, geom, count, &quads[i], t, pforest->topo->conn, coords));
3812       }
3813     }
3814     if (cLocalEnd - cLocalStart < cEnd - cStart) {
3815       p4est_quadrant_t *ghosts    = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
3816       PetscInt          numGhosts = (PetscInt)pforest->ghost->ghosts.elem_count;
3817       PetscInt          count;
3818 
3819       for (count = 0; count < numGhosts - cLocalStart; count++) {
3820         p4est_quadrant_t *quad = &ghosts[count + cLocalStart];
3821         p4est_topidx_t    t    = quad->p.which_tree;
3822 
3823         PetscCall(DMPforestMapCoordinates_Cell(plex, geom, count + cLocalEnd, quad, t, pforest->topo->conn, coords));
3824       }
3825     }
3826   }
3827   PetscCall(VecRestoreArray(coordLocalVec, &coords));
3828   PetscFunctionReturn(PETSC_SUCCESS);
3829 }
3830 
3831 static PetscErrorCode PforestQuadrantIsInterior(p4est_quadrant_t *quad, PetscBool *is_interior)
3832 {
3833   PetscFunctionBegin;
3834   p4est_qcoord_t h = P4EST_QUADRANT_LEN(quad->level);
3835   if ((quad->x > 0) && (quad->x + h < P4EST_ROOT_LEN)
3836   #ifdef P4_TO_P8
3837       && (quad->z > 0) && (quad->z + h < P4EST_ROOT_LEN)
3838   #endif
3839       && (quad->y > 0) && (quad->y + h < P4EST_ROOT_LEN)) {
3840     *is_interior = PETSC_TRUE;
3841   } else {
3842     *is_interior = PETSC_FALSE;
3843   }
3844   PetscFunctionReturn(PETSC_SUCCESS);
3845 }
3846 
3847 /* We always use DG coordinates with p4est: if they do not match the vertex
3848    coordinates, add space for them in the section */
3849 static PetscErrorCode PforestCheckLocalizeCell(DM plex, PetscInt cDim, Vec cVecOld, DM_Forest_pforest *pforest, PetscSection oldSection, PetscSection newSection, PetscInt cell, PetscInt coarsePoint, p4est_quadrant_t *quad)
3850 {
3851   PetscBool is_interior;
3852 
3853   PetscFunctionBegin;
3854   PetscCall(PforestQuadrantIsInterior(quad, &is_interior));
3855   if (is_interior) { // quads in the interior of a coarse cell can't touch periodic interfaces
3856     PetscCall(PetscSectionSetDof(newSection, cell, 0));
3857     PetscCall(PetscSectionSetFieldDof(newSection, cell, 0, 0));
3858   } else {
3859     PetscInt     cSize;
3860     PetscScalar *values      = NULL;
3861     PetscBool    same_coords = PETSC_TRUE;
3862 
3863     PetscCall(DMPlexVecGetClosure(plex, oldSection, cVecOld, cell, &cSize, &values));
3864     PetscAssert(cSize == cDim * P4EST_CHILDREN, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected closure size");
3865     for (int c = 0; c < P4EST_CHILDREN; c++) {
3866       p4est_qcoord_t quad_coords[3];
3867       p4est_qcoord_t h = P4EST_QUADRANT_LEN(quad->level);
3868       double         corner_coords[3];
3869       double         vert_coords[3];
3870       PetscInt       corner = PetscVertToP4estVert[c];
3871 
3872       for (PetscInt d = 0; d < PetscMin(cDim, 3); d++) vert_coords[d] = PetscRealPart(values[c * cDim + d]);
3873 
3874       quad_coords[0] = quad->x;
3875       quad_coords[1] = quad->y;
3876   #ifdef P4_TO_P8
3877       quad_coords[2] = quad->z;
3878   #endif
3879       for (int d = 0; d < 3; d++) quad_coords[d] += (corner & (1 << d)) ? h : 0;
3880   #ifndef P4_TO_P8
3881       PetscCallP4est(p4est_qcoord_to_vertex, (pforest->forest->connectivity, coarsePoint, quad_coords[0], quad_coords[1], corner_coords));
3882   #else
3883       PetscCallP4est(p4est_qcoord_to_vertex, (pforest->forest->connectivity, coarsePoint, quad_coords[0], quad_coords[1], quad_coords[2], corner_coords));
3884   #endif
3885       for (PetscInt d = 0; d < PetscMin(cDim, 3); d++) {
3886         if (fabs(vert_coords[d] - corner_coords[d]) > PETSC_SMALL) {
3887           same_coords = PETSC_FALSE;
3888           break;
3889         }
3890       }
3891     }
3892     if (same_coords) {
3893       PetscCall(PetscSectionSetDof(newSection, cell, 0));
3894       PetscCall(PetscSectionSetFieldDof(newSection, cell, 0, 0));
3895     } else {
3896       PetscCall(PetscSectionSetDof(newSection, cell, cSize));
3897       PetscCall(PetscSectionSetFieldDof(newSection, cell, 0, cSize));
3898     }
3899     PetscCall(DMPlexVecRestoreClosure(plex, oldSection, cVecOld, cell, &cSize, &values));
3900   }
3901   PetscFunctionReturn(PETSC_SUCCESS);
3902 }
3903 
3904 static PetscErrorCode PforestLocalizeCell(DM plex, PetscInt cDim, DM_Forest_pforest *pforest, PetscSection newSection, PetscInt cell, PetscInt coarsePoint, p4est_quadrant_t *quad, PetscScalar coords[])
3905 {
3906   PetscInt cdof, off;
3907 
3908   PetscFunctionBegin;
3909   PetscCall(PetscSectionGetDof(newSection, cell, &cdof));
3910   if (!cdof) PetscFunctionReturn(PETSC_SUCCESS);
3911 
3912   PetscCall(PetscSectionGetOffset(newSection, cell, &off));
3913   for (PetscInt c = 0, pos = off; c < P4EST_CHILDREN; c++) {
3914     p4est_qcoord_t quad_coords[3];
3915     p4est_qcoord_t h = P4EST_QUADRANT_LEN(quad->level);
3916     double         corner_coords[3];
3917     PetscInt       corner = PetscVertToP4estVert[c];
3918 
3919     quad_coords[0] = quad->x;
3920     quad_coords[1] = quad->y;
3921   #ifdef P4_TO_P8
3922     quad_coords[2] = quad->z;
3923   #endif
3924     for (int d = 0; d < 3; d++) quad_coords[d] += (corner & (1 << d)) ? h : 0;
3925   #ifndef P4_TO_P8
3926     PetscCallP4est(p4est_qcoord_to_vertex, (pforest->forest->connectivity, coarsePoint, quad_coords[0], quad_coords[1], corner_coords));
3927   #else
3928     PetscCallP4est(p4est_qcoord_to_vertex, (pforest->forest->connectivity, coarsePoint, quad_coords[0], quad_coords[1], quad_coords[2], corner_coords));
3929   #endif
3930     for (PetscInt d = 0; d < PetscMin(cDim, 3); d++) coords[pos++] = corner_coords[d];
3931     for (PetscInt d = PetscMin(cDim, 3); d < cDim; d++) coords[pos++] = 0.;
3932   }
3933   PetscFunctionReturn(PETSC_SUCCESS);
3934 }
3935 
3936 static PetscErrorCode DMPforestLocalizeCoordinates(DM dm, DM plex)
3937 {
3938   DM_Forest         *forest;
3939   DM_Forest_pforest *pforest;
3940   DM                 base, cdm, cdmCell;
3941   Vec                cVec, cVecOld;
3942   PetscSection       oldSection, newSection;
3943   PetscScalar       *coords2;
3944   const PetscReal   *L;
3945   PetscInt           cLocalStart, cLocalEnd, coarsePoint;
3946   PetscInt           cDim, newStart, newEnd;
3947   PetscInt           v, vStart, vEnd, cp, cStart, cEnd, cEndInterior;
3948   p4est_topidx_t     flt, llt, t;
3949   p4est_tree_t      *trees;
3950   PetscBool          baseLocalized = PETSC_FALSE;
3951 
3952   PetscFunctionBegin;
3953   PetscCall(DMGetPeriodicity(dm, NULL, NULL, &L));
3954   /* we localize on all cells if we don't have a base DM or the base DM coordinates have not been localized */
3955   PetscCall(DMGetCoordinateDim(dm, &cDim));
3956   PetscCall(DMForestGetBaseDM(dm, &base));
3957   if (base) PetscCall(DMGetCoordinatesLocalized(base, &baseLocalized));
3958   if (!baseLocalized) base = NULL;
3959   if (!baseLocalized && !L) PetscFunctionReturn(PETSC_SUCCESS);
3960   PetscCall(DMPlexGetChart(plex, &newStart, &newEnd));
3961 
3962   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &newSection));
3963   PetscCall(PetscSectionSetNumFields(newSection, 1));
3964   PetscCall(PetscSectionSetFieldComponents(newSection, 0, cDim));
3965   PetscCall(PetscSectionSetChart(newSection, newStart, newEnd));
3966 
3967   PetscCall(DMGetCoordinateSection(plex, &oldSection));
3968   PetscCall(DMPlexGetDepthStratum(plex, 0, &vStart, &vEnd));
3969   PetscCall(DMGetCoordinatesLocal(plex, &cVecOld));
3970 
3971   forest      = (DM_Forest *)dm->data;
3972   pforest     = (DM_Forest_pforest *)forest->data;
3973   cLocalStart = pforest->cLocalStart;
3974   cLocalEnd   = pforest->cLocalEnd;
3975   flt         = pforest->forest->first_local_tree;
3976   llt         = pforest->forest->last_local_tree;
3977   trees       = (p4est_tree_t *)pforest->forest->trees->array;
3978 
3979   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, &cEnd));
3980   PetscCall(DMPlexGetCellTypeStratum(plex, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
3981   cEnd = cEndInterior < 0 ? cEnd : cEndInterior;
3982   cp   = 0;
3983   if (cLocalStart > 0) {
3984     p4est_quadrant_t *ghosts = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
3985     PetscInt          cell;
3986 
3987     for (cell = 0; cell < cLocalStart; ++cell, cp++) {
3988       p4est_quadrant_t *quad = &ghosts[cell];
3989 
3990       coarsePoint = quad->p.which_tree;
3991       PetscCall(PforestCheckLocalizeCell(plex, cDim, cVecOld, pforest, oldSection, newSection, cell, coarsePoint, quad));
3992     }
3993   }
3994   for (t = flt; t <= llt; t++) {
3995     p4est_tree_t     *tree     = &(trees[t]);
3996     PetscInt          offset   = cLocalStart + tree->quadrants_offset;
3997     PetscInt          numQuads = (PetscInt)tree->quadrants.elem_count;
3998     p4est_quadrant_t *quads    = (p4est_quadrant_t *)tree->quadrants.array;
3999     PetscInt          i;
4000 
4001     if (!numQuads) continue;
4002     coarsePoint = t;
4003     for (i = 0; i < numQuads; i++, cp++) {
4004       PetscInt          cell = i + offset;
4005       p4est_quadrant_t *quad = &quads[i];
4006 
4007       PetscCall(PforestCheckLocalizeCell(plex, cDim, cVecOld, pforest, oldSection, newSection, cell, coarsePoint, quad));
4008     }
4009   }
4010   if (cLocalEnd - cLocalStart < cEnd - cStart) {
4011     p4est_quadrant_t *ghosts    = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
4012     PetscInt          numGhosts = (PetscInt)pforest->ghost->ghosts.elem_count;
4013     PetscInt          count;
4014 
4015     for (count = 0; count < numGhosts - cLocalStart; count++, cp++) {
4016       p4est_quadrant_t *quad = &ghosts[count + cLocalStart];
4017       coarsePoint            = quad->p.which_tree;
4018       PetscInt cell          = count + cLocalEnd;
4019 
4020       PetscCall(PforestCheckLocalizeCell(plex, cDim, cVecOld, pforest, oldSection, newSection, cell, coarsePoint, quad));
4021     }
4022   }
4023   PetscAssert(cp == cEnd - cStart, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected number of fine cells %" PetscInt_FMT " != %" PetscInt_FMT, cp, cEnd - cStart);
4024 
4025   PetscCall(PetscSectionSetUp(newSection));
4026   PetscCall(DMGetCoordinateDM(plex, &cdm));
4027   PetscCall(DMClone(cdm, &cdmCell));
4028   PetscCall(DMSetCellCoordinateDM(plex, cdmCell));
4029   PetscCall(DMDestroy(&cdmCell));
4030   PetscCall(DMSetCellCoordinateSection(plex, cDim, newSection));
4031   PetscCall(PetscSectionGetStorageSize(newSection, &v));
4032   PetscCall(VecCreate(PETSC_COMM_SELF, &cVec));
4033   PetscCall(PetscObjectSetName((PetscObject)cVec, "coordinates"));
4034   PetscCall(VecSetBlockSize(cVec, cDim));
4035   PetscCall(VecSetSizes(cVec, v, PETSC_DETERMINE));
4036   PetscCall(VecSetType(cVec, VECSTANDARD));
4037   PetscCall(VecSet(cVec, PETSC_MIN_REAL));
4038 
4039   /* Localize coordinates on cells if needed */
4040   PetscCall(VecGetArray(cVec, &coords2));
4041   cp = 0;
4042   if (cLocalStart > 0) {
4043     p4est_quadrant_t *ghosts = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
4044     PetscInt          cell;
4045 
4046     for (cell = 0; cell < cLocalStart; ++cell, cp++) {
4047       p4est_quadrant_t *quad = &ghosts[cell];
4048 
4049       coarsePoint = quad->p.which_tree;
4050       PetscCall(PforestLocalizeCell(plex, cDim, pforest, newSection, cell, coarsePoint, quad, coords2));
4051     }
4052   }
4053   for (t = flt; t <= llt; t++) {
4054     p4est_tree_t     *tree     = &(trees[t]);
4055     PetscInt          offset   = cLocalStart + tree->quadrants_offset;
4056     PetscInt          numQuads = (PetscInt)tree->quadrants.elem_count;
4057     p4est_quadrant_t *quads    = (p4est_quadrant_t *)tree->quadrants.array;
4058     PetscInt          i;
4059 
4060     if (!numQuads) continue;
4061     coarsePoint = t;
4062     for (i = 0; i < numQuads; i++, cp++) {
4063       PetscInt          cell = i + offset;
4064       p4est_quadrant_t *quad = &quads[i];
4065 
4066       PetscCall(PforestLocalizeCell(plex, cDim, pforest, newSection, cell, coarsePoint, quad, coords2));
4067     }
4068   }
4069   if (cLocalEnd - cLocalStart < cEnd - cStart) {
4070     p4est_quadrant_t *ghosts    = (p4est_quadrant_t *)pforest->ghost->ghosts.array;
4071     PetscInt          numGhosts = (PetscInt)pforest->ghost->ghosts.elem_count;
4072     PetscInt          count;
4073 
4074     for (count = 0; count < numGhosts - cLocalStart; count++, cp++) {
4075       p4est_quadrant_t *quad = &ghosts[count + cLocalStart];
4076       coarsePoint            = quad->p.which_tree;
4077       PetscInt cell          = count + cLocalEnd;
4078 
4079       PetscCall(PforestLocalizeCell(plex, cDim, pforest, newSection, cell, coarsePoint, quad, coords2));
4080     }
4081   }
4082   PetscCall(VecRestoreArray(cVec, &coords2));
4083   PetscCall(DMSetCellCoordinatesLocal(plex, cVec));
4084   PetscCall(VecDestroy(&cVec));
4085   PetscCall(PetscSectionDestroy(&newSection));
4086   PetscFunctionReturn(PETSC_SUCCESS);
4087 }
4088 
4089   #define DMForestClearAdaptivityForest_pforest _append_pforest(DMForestClearAdaptivityForest)
4090 static PetscErrorCode DMForestClearAdaptivityForest_pforest(DM dm)
4091 {
4092   DM_Forest         *forest;
4093   DM_Forest_pforest *pforest;
4094 
4095   PetscFunctionBegin;
4096   forest  = (DM_Forest *)dm->data;
4097   pforest = (DM_Forest_pforest *)forest->data;
4098   PetscCall(PetscSFDestroy(&(pforest->pointAdaptToSelfSF)));
4099   PetscCall(PetscSFDestroy(&(pforest->pointSelfToAdaptSF)));
4100   PetscCall(PetscFree(pforest->pointAdaptToSelfCids));
4101   PetscCall(PetscFree(pforest->pointSelfToAdaptCids));
4102   PetscFunctionReturn(PETSC_SUCCESS);
4103 }
4104 
4105 static PetscErrorCode DMConvert_pforest_plex(DM dm, DMType newtype, DM *plex)
4106 {
4107   DM_Forest           *forest;
4108   DM_Forest_pforest   *pforest;
4109   DM                   refTree, newPlex, base;
4110   PetscInt             adjDim, adjCodim, coordDim;
4111   MPI_Comm             comm;
4112   PetscBool            isPforest;
4113   PetscInt             dim;
4114   PetscInt             overlap;
4115   p4est_connect_type_t ctype;
4116   p4est_locidx_t       first_local_quad = -1;
4117   sc_array_t          *points_per_dim, *cone_sizes, *cones, *cone_orientations, *coords, *children, *parents, *childids, *leaves, *remotes;
4118   PetscSection         parentSection;
4119   PetscSF              pointSF;
4120   size_t               zz, count;
4121   PetscInt             pStart, pEnd;
4122   DMLabel              ghostLabelBase = NULL;
4123 
4124   PetscFunctionBegin;
4125 
4126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4127   comm = PetscObjectComm((PetscObject)dm);
4128   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPFOREST, &isPforest));
4129   PetscCheck(isPforest, comm, PETSC_ERR_ARG_WRONG, "Expected DM type %s, got %s", DMPFOREST, ((PetscObject)dm)->type_name);
4130   PetscCall(DMGetDimension(dm, &dim));
4131   PetscCheck(dim == P4EST_DIM, comm, PETSC_ERR_ARG_WRONG, "Expected DM dimension %d, got %" PetscInt_FMT, P4EST_DIM, dim);
4132   forest  = (DM_Forest *)dm->data;
4133   pforest = (DM_Forest_pforest *)forest->data;
4134   PetscCall(DMForestGetBaseDM(dm, &base));
4135   if (base) PetscCall(DMGetLabel(base, "ghost", &ghostLabelBase));
4136   if (!pforest->plex) {
4137     PetscMPIInt size;
4138 
4139     PetscCallMPI(MPI_Comm_size(comm, &size));
4140     PetscCall(DMCreate(comm, &newPlex));
4141     PetscCall(DMSetType(newPlex, DMPLEX));
4142     PetscCall(DMSetMatType(newPlex, dm->mattype));
4143     /* share labels */
4144     PetscCall(DMCopyLabels(dm, newPlex, PETSC_OWN_POINTER, PETSC_TRUE, DM_COPY_LABELS_FAIL));
4145     PetscCall(DMForestGetAdjacencyDimension(dm, &adjDim));
4146     PetscCall(DMForestGetAdjacencyCodimension(dm, &adjCodim));
4147     PetscCall(DMGetCoordinateDim(dm, &coordDim));
4148     if (adjDim == 0) {
4149       ctype = P4EST_CONNECT_FULL;
4150     } else if (adjCodim == 1) {
4151       ctype = P4EST_CONNECT_FACE;
4152   #if defined(P4_TO_P8)
4153     } else if (adjDim == 1) {
4154       ctype = P8EST_CONNECT_EDGE;
4155   #endif
4156     } else {
4157       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Invalid adjacency dimension %" PetscInt_FMT, adjDim);
4158     }
4159     PetscCheck(ctype == P4EST_CONNECT_FULL, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Adjacency dimension %" PetscInt_FMT " / codimension %" PetscInt_FMT " not supported yet", adjDim, adjCodim);
4160     PetscCall(DMForestGetPartitionOverlap(dm, &overlap));
4161     PetscCall(DMPlexSetOverlap_Plex(newPlex, NULL, overlap));
4162 
4163     points_per_dim    = sc_array_new(sizeof(p4est_locidx_t));
4164     cone_sizes        = sc_array_new(sizeof(p4est_locidx_t));
4165     cones             = sc_array_new(sizeof(p4est_locidx_t));
4166     cone_orientations = sc_array_new(sizeof(p4est_locidx_t));
4167     coords            = sc_array_new(3 * sizeof(double));
4168     children          = sc_array_new(sizeof(p4est_locidx_t));
4169     parents           = sc_array_new(sizeof(p4est_locidx_t));
4170     childids          = sc_array_new(sizeof(p4est_locidx_t));
4171     leaves            = sc_array_new(sizeof(p4est_locidx_t));
4172     remotes           = sc_array_new(2 * sizeof(p4est_locidx_t));
4173 
4174     PetscCallP4est(p4est_get_plex_data_ext, (pforest->forest, &pforest->ghost, &pforest->lnodes, ctype, (int)((size > 1) ? overlap : 0), &first_local_quad, points_per_dim, cone_sizes, cones, cone_orientations, coords, children, parents, childids, leaves, remotes, 1));
4175 
4176     pforest->cLocalStart = (PetscInt)first_local_quad;
4177     pforest->cLocalEnd   = pforest->cLocalStart + (PetscInt)pforest->forest->local_num_quadrants;
4178     PetscCall(locidx_to_PetscInt(points_per_dim));
4179     PetscCall(locidx_to_PetscInt(cone_sizes));
4180     PetscCall(locidx_to_PetscInt(cones));
4181     PetscCall(locidx_to_PetscInt(cone_orientations));
4182     PetscCall(coords_double_to_PetscScalar(coords, coordDim));
4183     PetscCall(locidx_to_PetscInt(children));
4184     PetscCall(locidx_to_PetscInt(parents));
4185     PetscCall(locidx_to_PetscInt(childids));
4186     PetscCall(locidx_to_PetscInt(leaves));
4187     PetscCall(locidx_pair_to_PetscSFNode(remotes));
4188 
4189     PetscCall(DMSetDimension(newPlex, P4EST_DIM));
4190     PetscCall(DMSetCoordinateDim(newPlex, coordDim));
4191     PetscCall(DMPlexSetMaxProjectionHeight(newPlex, P4EST_DIM - 1));
4192     PetscCall(DMPlexCreateFromDAG(newPlex, P4EST_DIM, (PetscInt *)points_per_dim->array, (PetscInt *)cone_sizes->array, (PetscInt *)cones->array, (PetscInt *)cone_orientations->array, (PetscScalar *)coords->array));
4193     PetscCall(DMPlexConvertOldOrientations_Internal(newPlex));
4194     PetscCall(DMCreateReferenceTree_pforest(comm, &refTree));
4195     PetscCall(DMPlexSetReferenceTree(newPlex, refTree));
4196     PetscCall(PetscSectionCreate(comm, &parentSection));
4197     PetscCall(DMPlexGetChart(newPlex, &pStart, &pEnd));
4198     PetscCall(PetscSectionSetChart(parentSection, pStart, pEnd));
4199     count = children->elem_count;
4200     for (zz = 0; zz < count; zz++) {
4201       PetscInt child = *((PetscInt *)sc_array_index(children, zz));
4202 
4203       PetscCall(PetscSectionSetDof(parentSection, child, 1));
4204     }
4205     PetscCall(PetscSectionSetUp(parentSection));
4206     PetscCall(DMPlexSetTree(newPlex, parentSection, (PetscInt *)parents->array, (PetscInt *)childids->array));
4207     PetscCall(PetscSectionDestroy(&parentSection));
4208     PetscCall(PetscSFCreate(comm, &pointSF));
4209     /*
4210        These arrays defining the sf are from the p4est library, but the code there shows the leaves being populated in increasing order.
4211        https://gitlab.com/petsc/petsc/merge_requests/2248#note_240186391
4212     */
4213     PetscCall(PetscSFSetGraph(pointSF, pEnd - pStart, (PetscInt)leaves->elem_count, (PetscInt *)leaves->array, PETSC_COPY_VALUES, (PetscSFNode *)remotes->array, PETSC_COPY_VALUES));
4214     PetscCall(DMSetPointSF(newPlex, pointSF));
4215     PetscCall(DMSetPointSF(dm, pointSF));
4216     {
4217       DM coordDM;
4218 
4219       PetscCall(DMGetCoordinateDM(newPlex, &coordDM));
4220       PetscCall(DMSetPointSF(coordDM, pointSF));
4221     }
4222     PetscCall(PetscSFDestroy(&pointSF));
4223     sc_array_destroy(points_per_dim);
4224     sc_array_destroy(cone_sizes);
4225     sc_array_destroy(cones);
4226     sc_array_destroy(cone_orientations);
4227     sc_array_destroy(coords);
4228     sc_array_destroy(children);
4229     sc_array_destroy(parents);
4230     sc_array_destroy(childids);
4231     sc_array_destroy(leaves);
4232     sc_array_destroy(remotes);
4233 
4234     {
4235       const PetscReal *maxCell, *Lstart, *L;
4236 
4237       PetscCall(DMGetPeriodicity(dm, &maxCell, &Lstart, &L));
4238       PetscCall(DMSetPeriodicity(newPlex, maxCell, Lstart, L));
4239       PetscCall(DMPforestLocalizeCoordinates(dm, newPlex));
4240     }
4241 
4242     if (overlap > 0) { /* the p4est routine can't set all of the coordinates in its routine if there is overlap */
4243       Vec                coordsGlobal, coordsLocal;
4244       const PetscScalar *globalArray;
4245       PetscScalar       *localArray;
4246       PetscSF            coordSF;
4247       DM                 coordDM;
4248 
4249       PetscCall(DMGetCoordinateDM(newPlex, &coordDM));
4250       PetscCall(DMGetSectionSF(coordDM, &coordSF));
4251       PetscCall(DMGetCoordinates(newPlex, &coordsGlobal));
4252       PetscCall(DMGetCoordinatesLocal(newPlex, &coordsLocal));
4253       PetscCall(VecGetArrayRead(coordsGlobal, &globalArray));
4254       PetscCall(VecGetArray(coordsLocal, &localArray));
4255       PetscCall(PetscSFBcastBegin(coordSF, MPIU_SCALAR, globalArray, localArray, MPI_REPLACE));
4256       PetscCall(PetscSFBcastEnd(coordSF, MPIU_SCALAR, globalArray, localArray, MPI_REPLACE));
4257       PetscCall(VecRestoreArray(coordsLocal, &localArray));
4258       PetscCall(VecRestoreArrayRead(coordsGlobal, &globalArray));
4259       PetscCall(DMSetCoordinatesLocal(newPlex, coordsLocal));
4260     }
4261     PetscCall(DMPforestMapCoordinates(dm, newPlex));
4262 
4263     pforest->plex = newPlex;
4264 
4265     /* copy labels */
4266     PetscCall(DMPforestLabelsFinalize(dm, newPlex));
4267 
4268     if (ghostLabelBase || pforest->ghostName) { /* we have to do this after copying labels because the labels drive the construction of ghost cells */
4269       PetscInt numAdded;
4270       DM       newPlexGhosted;
4271       void    *ctx;
4272 
4273       PetscCall(DMPlexConstructGhostCells(newPlex, pforest->ghostName, &numAdded, &newPlexGhosted));
4274       PetscCall(DMGetApplicationContext(newPlex, &ctx));
4275       PetscCall(DMSetApplicationContext(newPlexGhosted, ctx));
4276       /* we want the sf for the ghost dm to be the one for the p4est dm as well */
4277       PetscCall(DMGetPointSF(newPlexGhosted, &pointSF));
4278       PetscCall(DMSetPointSF(dm, pointSF));
4279       PetscCall(DMDestroy(&newPlex));
4280       PetscCall(DMPlexSetReferenceTree(newPlexGhosted, refTree));
4281       PetscCall(DMForestClearAdaptivityForest_pforest(dm));
4282       newPlex = newPlexGhosted;
4283 
4284       /* share the labels back */
4285       PetscCall(DMDestroyLabelLinkList_Internal(dm));
4286       PetscCall(DMCopyLabels(newPlex, dm, PETSC_OWN_POINTER, PETSC_TRUE, DM_COPY_LABELS_FAIL));
4287       pforest->plex = newPlex;
4288     }
4289     PetscCall(DMDestroy(&refTree));
4290     if (dm->setfromoptionscalled) {
4291       PetscObjectOptionsBegin((PetscObject)newPlex);
4292       PetscCall(DMSetFromOptions_NonRefinement_Plex(newPlex, PetscOptionsObject));
4293       PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)newPlex, PetscOptionsObject));
4294       PetscOptionsEnd();
4295     }
4296     PetscCall(DMViewFromOptions(newPlex, NULL, "-dm_p4est_plex_view"));
4297     {
4298       DM           cdm;
4299       PetscSection coordsSec;
4300       Vec          coords;
4301       PetscInt     cDim;
4302 
4303       PetscCall(DMGetCoordinateDim(newPlex, &cDim));
4304       PetscCall(DMGetCoordinateSection(newPlex, &coordsSec));
4305       PetscCall(DMSetCoordinateSection(dm, cDim, coordsSec));
4306       PetscCall(DMGetCoordinatesLocal(newPlex, &coords));
4307       PetscCall(DMSetCoordinatesLocal(dm, coords));
4308       PetscCall(DMGetCellCoordinateDM(newPlex, &cdm));
4309       if (cdm) PetscCall(DMSetCellCoordinateDM(dm, cdm));
4310       PetscCall(DMGetCellCoordinateSection(newPlex, &coordsSec));
4311       if (coordsSec) PetscCall(DMSetCellCoordinateSection(dm, cDim, coordsSec));
4312       PetscCall(DMGetCellCoordinatesLocal(newPlex, &coords));
4313       if (coords) PetscCall(DMSetCellCoordinatesLocal(dm, coords));
4314     }
4315   }
4316   newPlex = pforest->plex;
4317   if (plex) {
4318     PetscCall(DMClone(newPlex, plex));
4319   #if 0
4320     PetscCall(DMGetCoordinateDM(newPlex,&coordDM));
4321     PetscCall(DMSetCoordinateDM(*plex,coordDM));
4322     PetscCall(DMGetCellCoordinateDM(newPlex,&coordDM));
4323     PetscCall(DMSetCellCoordinateDM(*plex,coordDM));
4324   #endif
4325     PetscCall(DMShareDiscretization(dm, *plex));
4326   }
4327   PetscFunctionReturn(PETSC_SUCCESS);
4328 }
4329 
4330 static PetscErrorCode DMSetFromOptions_pforest(DM dm, PetscOptionItems *PetscOptionsObject)
4331 {
4332   DM_Forest_pforest *pforest = (DM_Forest_pforest *)((DM_Forest *)dm->data)->data;
4333   char               stringBuffer[256];
4334   PetscBool          flg;
4335 
4336   PetscFunctionBegin;
4337   PetscCall(DMSetFromOptions_Forest(dm, PetscOptionsObject));
4338   PetscOptionsHeadBegin(PetscOptionsObject, "DM" P4EST_STRING " options");
4339   PetscCall(PetscOptionsBool("-dm_p4est_partition_for_coarsening", "partition forest to allow for coarsening", "DMP4estSetPartitionForCoarsening", pforest->partition_for_coarsening, &(pforest->partition_for_coarsening), NULL));
4340   PetscCall(PetscOptionsString("-dm_p4est_ghost_label_name", "the name of the ghost label when converting from a DMPlex", NULL, NULL, stringBuffer, sizeof(stringBuffer), &flg));
4341   PetscOptionsHeadEnd();
4342   if (flg) {
4343     PetscCall(PetscFree(pforest->ghostName));
4344     PetscCall(PetscStrallocpy(stringBuffer, &pforest->ghostName));
4345   }
4346   PetscFunctionReturn(PETSC_SUCCESS);
4347 }
4348 
4349   #if !defined(P4_TO_P8)
4350     #define DMPforestGetPartitionForCoarsening DMP4estGetPartitionForCoarsening
4351     #define DMPforestSetPartitionForCoarsening DMP4estSetPartitionForCoarsening
4352   #else
4353     #define DMPforestGetPartitionForCoarsening DMP8estGetPartitionForCoarsening
4354     #define DMPforestSetPartitionForCoarsening DMP8estSetPartitionForCoarsening
4355   #endif
4356 
4357 PETSC_EXTERN PetscErrorCode DMPforestGetPartitionForCoarsening(DM dm, PetscBool *flg)
4358 {
4359   DM_Forest_pforest *pforest;
4360 
4361   PetscFunctionBegin;
4362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4363   pforest = (DM_Forest_pforest *)((DM_Forest *)dm->data)->data;
4364   *flg    = pforest->partition_for_coarsening;
4365   PetscFunctionReturn(PETSC_SUCCESS);
4366 }
4367 
4368 PETSC_EXTERN PetscErrorCode DMPforestSetPartitionForCoarsening(DM dm, PetscBool flg)
4369 {
4370   DM_Forest_pforest *pforest;
4371 
4372   PetscFunctionBegin;
4373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4374   pforest                           = (DM_Forest_pforest *)((DM_Forest *)dm->data)->data;
4375   pforest->partition_for_coarsening = flg;
4376   PetscFunctionReturn(PETSC_SUCCESS);
4377 }
4378 
4379 static PetscErrorCode DMPforestGetPlex(DM dm, DM *plex)
4380 {
4381   DM_Forest_pforest *pforest;
4382 
4383   PetscFunctionBegin;
4384   if (plex) *plex = NULL;
4385   PetscCall(DMSetUp(dm));
4386   pforest = (DM_Forest_pforest *)((DM_Forest *)dm->data)->data;
4387   if (!pforest->plex) PetscCall(DMConvert_pforest_plex(dm, DMPLEX, NULL));
4388   PetscCall(DMShareDiscretization(dm, pforest->plex));
4389   if (plex) *plex = pforest->plex;
4390   PetscFunctionReturn(PETSC_SUCCESS);
4391 }
4392 
4393   #define DMCreateInterpolation_pforest _append_pforest(DMCreateInterpolation)
4394 static PetscErrorCode DMCreateInterpolation_pforest(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
4395 {
4396   PetscSection gsc, gsf;
4397   PetscInt     m, n;
4398   DM           cdm;
4399 
4400   PetscFunctionBegin;
4401   PetscCall(DMGetGlobalSection(dmFine, &gsf));
4402   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
4403   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
4404   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
4405 
4406   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmFine), interpolation));
4407   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
4408   PetscCall(MatSetType(*interpolation, MATAIJ));
4409 
4410   PetscCall(DMGetCoarseDM(dmFine, &cdm));
4411   PetscCheck(cdm == dmCoarse, PetscObjectComm((PetscObject)dmFine), PETSC_ERR_SUP, "Only interpolation from coarse DM for now");
4412 
4413   {
4414     DM        plexF, plexC;
4415     PetscSF   sf;
4416     PetscInt *cids;
4417     PetscInt  dofPerDim[4] = {1, 1, 1, 1};
4418 
4419     PetscCall(DMPforestGetPlex(dmCoarse, &plexC));
4420     PetscCall(DMPforestGetPlex(dmFine, &plexF));
4421     PetscCall(DMPforestGetTransferSF_Internal(dmCoarse, dmFine, dofPerDim, &sf, PETSC_TRUE, &cids));
4422     PetscCall(PetscSFSetUp(sf));
4423     PetscCall(DMPlexComputeInterpolatorTree(plexC, plexF, sf, cids, *interpolation));
4424     PetscCall(PetscSFDestroy(&sf));
4425     PetscCall(PetscFree(cids));
4426   }
4427   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
4428   /* Use naive scaling */
4429   PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
4430   PetscFunctionReturn(PETSC_SUCCESS);
4431 }
4432 
4433   #define DMCreateInjection_pforest _append_pforest(DMCreateInjection)
4434 static PetscErrorCode DMCreateInjection_pforest(DM dmCoarse, DM dmFine, Mat *injection)
4435 {
4436   PetscSection gsc, gsf;
4437   PetscInt     m, n;
4438   DM           cdm;
4439 
4440   PetscFunctionBegin;
4441   PetscCall(DMGetGlobalSection(dmFine, &gsf));
4442   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &n));
4443   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
4444   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &m));
4445 
4446   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmFine), injection));
4447   PetscCall(MatSetSizes(*injection, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
4448   PetscCall(MatSetType(*injection, MATAIJ));
4449 
4450   PetscCall(DMGetCoarseDM(dmFine, &cdm));
4451   PetscCheck(cdm == dmCoarse, PetscObjectComm((PetscObject)dmFine), PETSC_ERR_SUP, "Only injection to coarse DM for now");
4452 
4453   {
4454     DM        plexF, plexC;
4455     PetscSF   sf;
4456     PetscInt *cids;
4457     PetscInt  dofPerDim[4] = {1, 1, 1, 1};
4458 
4459     PetscCall(DMPforestGetPlex(dmCoarse, &plexC));
4460     PetscCall(DMPforestGetPlex(dmFine, &plexF));
4461     PetscCall(DMPforestGetTransferSF_Internal(dmCoarse, dmFine, dofPerDim, &sf, PETSC_TRUE, &cids));
4462     PetscCall(PetscSFSetUp(sf));
4463     PetscCall(DMPlexComputeInjectorTree(plexC, plexF, sf, cids, *injection));
4464     PetscCall(PetscSFDestroy(&sf));
4465     PetscCall(PetscFree(cids));
4466   }
4467   PetscCall(MatViewFromOptions(*injection, NULL, "-inject_mat_view"));
4468   /* Use naive scaling */
4469   PetscFunctionReturn(PETSC_SUCCESS);
4470 }
4471 
4472   #define DMForestTransferVecFromBase_pforest _append_pforest(DMForestTransferVecFromBase)
4473 static PetscErrorCode DMForestTransferVecFromBase_pforest(DM dm, Vec vecIn, Vec vecOut)
4474 {
4475   DM        dmIn, dmVecIn, base, basec, plex, coarseDM;
4476   DM       *hierarchy;
4477   PetscSF   sfRed = NULL;
4478   PetscDS   ds;
4479   Vec       vecInLocal, vecOutLocal;
4480   DMLabel   subpointMap;
4481   PetscInt  minLevel, mh, n_hi, i;
4482   PetscBool hiforest, *hierarchy_forest;
4483 
4484   PetscFunctionBegin;
4485   PetscCall(VecGetDM(vecIn, &dmVecIn));
4486   PetscCall(DMGetDS(dmVecIn, &ds));
4487   PetscCheck(ds, PetscObjectComm((PetscObject)dmVecIn), PETSC_ERR_SUP, "Cannot transfer without a PetscDS object");
4488   { /* we cannot stick user contexts into function callbacks for DMProjectFieldLocal! */
4489     PetscSection section;
4490     PetscInt     Nf;
4491 
4492     PetscCall(DMGetLocalSection(dmVecIn, &section));
4493     PetscCall(PetscSectionGetNumFields(section, &Nf));
4494     PetscCheck(Nf <= 3, PetscObjectComm((PetscObject)dmVecIn), PETSC_ERR_SUP, "Number of fields %" PetscInt_FMT " are currently not supported! Send an email at petsc-dev@mcs.anl.gov", Nf);
4495   }
4496   PetscCall(DMForestGetMinimumRefinement(dm, &minLevel));
4497   PetscCheck(!minLevel, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot transfer with minimum refinement set to %" PetscInt_FMT ". Rerun with DMForestSetMinimumRefinement(dm,0)", minLevel);
4498   PetscCall(DMForestGetBaseDM(dm, &base));
4499   PetscCheck(base, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Missing base DM");
4500 
4501   PetscCall(VecSet(vecOut, 0.0));
4502   if (dmVecIn == base) { /* sequential runs */
4503     PetscCall(PetscObjectReference((PetscObject)vecIn));
4504   } else {
4505     PetscSection secIn, secInRed;
4506     Vec          vecInRed, vecInLocal;
4507 
4508     PetscCall(PetscObjectQuery((PetscObject)base, "_base_migration_sf", (PetscObject *)&sfRed));
4509     PetscCheck(sfRed, PETSC_COMM_SELF, PETSC_ERR_SUP, "Not the DM set with DMForestSetBaseDM()");
4510     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dmVecIn), &secInRed));
4511     PetscCall(VecCreate(PETSC_COMM_SELF, &vecInRed));
4512     PetscCall(DMGetLocalSection(dmVecIn, &secIn));
4513     PetscCall(DMGetLocalVector(dmVecIn, &vecInLocal));
4514     PetscCall(DMGlobalToLocalBegin(dmVecIn, vecIn, INSERT_VALUES, vecInLocal));
4515     PetscCall(DMGlobalToLocalEnd(dmVecIn, vecIn, INSERT_VALUES, vecInLocal));
4516     PetscCall(DMPlexDistributeField(dmVecIn, sfRed, secIn, vecInLocal, secInRed, vecInRed));
4517     PetscCall(DMRestoreLocalVector(dmVecIn, &vecInLocal));
4518     PetscCall(PetscSectionDestroy(&secInRed));
4519     vecIn = vecInRed;
4520   }
4521 
4522   /* we first search through the AdaptivityForest hierarchy
4523      once we found the first disconnected forest, we upsweep the DM hierarchy */
4524   hiforest = PETSC_TRUE;
4525 
4526   /* upsweep to the coarsest DM */
4527   n_hi     = 0;
4528   coarseDM = dm;
4529   do {
4530     PetscBool isforest;
4531 
4532     dmIn = coarseDM;
4533     /* need to call DMSetUp to have the hierarchy recursively setup */
4534     PetscCall(DMSetUp(dmIn));
4535     PetscCall(DMIsForest(dmIn, &isforest));
4536     PetscCheck(isforest, PetscObjectComm((PetscObject)dmIn), PETSC_ERR_SUP, "Cannot currently transfer through a mixed hierarchy! Found DM type %s", ((PetscObject)dmIn)->type_name);
4537     coarseDM = NULL;
4538     if (hiforest) PetscCall(DMForestGetAdaptivityForest(dmIn, &coarseDM));
4539     if (!coarseDM) { /* DMForest hierarchy ended, we keep upsweeping through the DM hierarchy */
4540       hiforest = PETSC_FALSE;
4541       PetscCall(DMGetCoarseDM(dmIn, &coarseDM));
4542     }
4543     n_hi++;
4544   } while (coarseDM);
4545 
4546   PetscCall(PetscMalloc2(n_hi, &hierarchy, n_hi, &hierarchy_forest));
4547 
4548   i        = 0;
4549   hiforest = PETSC_TRUE;
4550   coarseDM = dm;
4551   do {
4552     dmIn     = coarseDM;
4553     coarseDM = NULL;
4554     if (hiforest) PetscCall(DMForestGetAdaptivityForest(dmIn, &coarseDM));
4555     if (!coarseDM) { /* DMForest hierarchy ended, we keep upsweeping through the DM hierarchy */
4556       hiforest = PETSC_FALSE;
4557       PetscCall(DMGetCoarseDM(dmIn, &coarseDM));
4558     }
4559     i++;
4560     hierarchy[n_hi - i] = dmIn;
4561   } while (coarseDM);
4562 
4563   /* project base vector on the coarsest forest (minimum refinement = 0) */
4564   PetscCall(DMPforestGetPlex(dmIn, &plex));
4565 
4566   /* Check this plex is compatible with the base */
4567   {
4568     IS       gnum[2];
4569     PetscInt ncells[2], gncells[2];
4570 
4571     PetscCall(DMPlexGetCellNumbering(base, &gnum[0]));
4572     PetscCall(DMPlexGetCellNumbering(plex, &gnum[1]));
4573     PetscCall(ISGetMinMax(gnum[0], NULL, &ncells[0]));
4574     PetscCall(ISGetMinMax(gnum[1], NULL, &ncells[1]));
4575     PetscCall(MPIU_Allreduce(ncells, gncells, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4576     PetscCheck(gncells[0] == gncells[1], PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Invalid number of base cells! Expected %" PetscInt_FMT ", found %" PetscInt_FMT, gncells[0] + 1, gncells[1] + 1);
4577   }
4578 
4579   PetscCall(DMGetLabel(dmIn, "_forest_base_subpoint_map", &subpointMap));
4580   PetscCheck(subpointMap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Missing _forest_base_subpoint_map label");
4581 
4582   PetscCall(DMPlexGetMaxProjectionHeight(base, &mh));
4583   PetscCall(DMPlexSetMaxProjectionHeight(plex, mh));
4584 
4585   PetscCall(DMClone(base, &basec));
4586   PetscCall(DMCopyDisc(dmVecIn, basec));
4587   if (sfRed) {
4588     PetscCall(PetscObjectReference((PetscObject)vecIn));
4589     vecInLocal = vecIn;
4590   } else {
4591     PetscCall(DMCreateLocalVector(basec, &vecInLocal));
4592     PetscCall(DMGlobalToLocalBegin(basec, vecIn, INSERT_VALUES, vecInLocal));
4593     PetscCall(DMGlobalToLocalEnd(basec, vecIn, INSERT_VALUES, vecInLocal));
4594   }
4595 
4596   PetscCall(DMGetLocalVector(dmIn, &vecOutLocal));
4597   { /* get degrees of freedom ordered onto dmIn */
4598     PetscSF            basetocoarse;
4599     PetscInt           bStart, bEnd, nroots;
4600     PetscInt           iStart, iEnd, nleaves, leaf;
4601     PetscMPIInt        rank;
4602     PetscSFNode       *remotes;
4603     PetscSection       secIn, secOut;
4604     PetscInt          *remoteOffsets;
4605     PetscSF            transferSF;
4606     const PetscScalar *inArray;
4607     PetscScalar       *outArray;
4608 
4609     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)basec), &rank));
4610     PetscCall(DMPlexGetChart(basec, &bStart, &bEnd));
4611     nroots = PetscMax(bEnd - bStart, 0);
4612     PetscCall(DMPlexGetChart(plex, &iStart, &iEnd));
4613     nleaves = PetscMax(iEnd - iStart, 0);
4614 
4615     PetscCall(PetscMalloc1(nleaves, &remotes));
4616     for (leaf = iStart; leaf < iEnd; leaf++) {
4617       PetscInt index;
4618 
4619       remotes[leaf - iStart].rank = rank;
4620       PetscCall(DMLabelGetValue(subpointMap, leaf, &index));
4621       remotes[leaf - iStart].index = index;
4622     }
4623 
4624     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)basec), &basetocoarse));
4625     PetscCall(PetscSFSetGraph(basetocoarse, nroots, nleaves, NULL, PETSC_OWN_POINTER, remotes, PETSC_OWN_POINTER));
4626     PetscCall(PetscSFSetUp(basetocoarse));
4627     PetscCall(DMGetLocalSection(basec, &secIn));
4628     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dmIn), &secOut));
4629     PetscCall(PetscSFDistributeSection(basetocoarse, secIn, &remoteOffsets, secOut));
4630     PetscCall(PetscSFCreateSectionSF(basetocoarse, secIn, remoteOffsets, secOut, &transferSF));
4631     PetscCall(PetscFree(remoteOffsets));
4632     PetscCall(VecGetArrayWrite(vecOutLocal, &outArray));
4633     PetscCall(VecGetArrayRead(vecInLocal, &inArray));
4634     PetscCall(PetscSFBcastBegin(transferSF, MPIU_SCALAR, inArray, outArray, MPI_REPLACE));
4635     PetscCall(PetscSFBcastEnd(transferSF, MPIU_SCALAR, inArray, outArray, MPI_REPLACE));
4636     PetscCall(VecRestoreArrayRead(vecInLocal, &inArray));
4637     PetscCall(VecRestoreArrayWrite(vecOutLocal, &outArray));
4638     PetscCall(PetscSFDestroy(&transferSF));
4639     PetscCall(PetscSectionDestroy(&secOut));
4640     PetscCall(PetscSFDestroy(&basetocoarse));
4641   }
4642   PetscCall(VecDestroy(&vecInLocal));
4643   PetscCall(DMDestroy(&basec));
4644   PetscCall(VecDestroy(&vecIn));
4645 
4646   /* output */
4647   if (n_hi > 1) { /* downsweep the stored hierarchy */
4648     Vec vecOut1, vecOut2;
4649     DM  fineDM;
4650 
4651     PetscCall(DMGetGlobalVector(dmIn, &vecOut1));
4652     PetscCall(DMLocalToGlobal(dmIn, vecOutLocal, INSERT_VALUES, vecOut1));
4653     PetscCall(DMRestoreLocalVector(dmIn, &vecOutLocal));
4654     for (i = 1; i < n_hi - 1; i++) {
4655       fineDM = hierarchy[i];
4656       PetscCall(DMGetGlobalVector(fineDM, &vecOut2));
4657       PetscCall(DMForestTransferVec(dmIn, vecOut1, fineDM, vecOut2, PETSC_TRUE, 0.0));
4658       PetscCall(DMRestoreGlobalVector(dmIn, &vecOut1));
4659       vecOut1 = vecOut2;
4660       dmIn    = fineDM;
4661     }
4662     PetscCall(DMForestTransferVec(dmIn, vecOut1, dm, vecOut, PETSC_TRUE, 0.0));
4663     PetscCall(DMRestoreGlobalVector(dmIn, &vecOut1));
4664   } else {
4665     PetscCall(DMLocalToGlobal(dmIn, vecOutLocal, INSERT_VALUES, vecOut));
4666     PetscCall(DMRestoreLocalVector(dmIn, &vecOutLocal));
4667   }
4668   PetscCall(PetscFree2(hierarchy, hierarchy_forest));
4669   PetscFunctionReturn(PETSC_SUCCESS);
4670 }
4671 
4672   #define DMForestTransferVec_pforest _append_pforest(DMForestTransferVec)
4673 static PetscErrorCode DMForestTransferVec_pforest(DM dmIn, Vec vecIn, DM dmOut, Vec vecOut, PetscBool useBCs, PetscReal time)
4674 {
4675   DM          adaptIn, adaptOut, plexIn, plexOut;
4676   DM_Forest  *forestIn, *forestOut, *forestAdaptIn, *forestAdaptOut;
4677   PetscInt    dofPerDim[] = {1, 1, 1, 1};
4678   PetscSF     inSF = NULL, outSF = NULL;
4679   PetscInt   *inCids = NULL, *outCids = NULL;
4680   DMAdaptFlag purposeIn, purposeOut;
4681 
4682   PetscFunctionBegin;
4683   forestOut = (DM_Forest *)dmOut->data;
4684   forestIn  = (DM_Forest *)dmIn->data;
4685 
4686   PetscCall(DMForestGetAdaptivityForest(dmOut, &adaptOut));
4687   PetscCall(DMForestGetAdaptivityPurpose(dmOut, &purposeOut));
4688   forestAdaptOut = adaptOut ? (DM_Forest *)adaptOut->data : NULL;
4689 
4690   PetscCall(DMForestGetAdaptivityForest(dmIn, &adaptIn));
4691   PetscCall(DMForestGetAdaptivityPurpose(dmIn, &purposeIn));
4692   forestAdaptIn = adaptIn ? (DM_Forest *)adaptIn->data : NULL;
4693 
4694   if (forestAdaptOut == forestIn) {
4695     switch (purposeOut) {
4696     case DM_ADAPT_REFINE:
4697       PetscCall(DMPforestGetTransferSF_Internal(dmIn, dmOut, dofPerDim, &inSF, PETSC_TRUE, &inCids));
4698       PetscCall(PetscSFSetUp(inSF));
4699       break;
4700     case DM_ADAPT_COARSEN:
4701     case DM_ADAPT_COARSEN_LAST:
4702       PetscCall(DMPforestGetTransferSF_Internal(dmOut, dmIn, dofPerDim, &outSF, PETSC_TRUE, &outCids));
4703       PetscCall(PetscSFSetUp(outSF));
4704       break;
4705     default:
4706       PetscCall(DMPforestGetTransferSF_Internal(dmIn, dmOut, dofPerDim, &inSF, PETSC_TRUE, &inCids));
4707       PetscCall(DMPforestGetTransferSF_Internal(dmOut, dmIn, dofPerDim, &outSF, PETSC_FALSE, &outCids));
4708       PetscCall(PetscSFSetUp(inSF));
4709       PetscCall(PetscSFSetUp(outSF));
4710     }
4711   } else if (forestAdaptIn == forestOut) {
4712     switch (purposeIn) {
4713     case DM_ADAPT_REFINE:
4714       PetscCall(DMPforestGetTransferSF_Internal(dmOut, dmIn, dofPerDim, &outSF, PETSC_TRUE, &inCids));
4715       PetscCall(PetscSFSetUp(outSF));
4716       break;
4717     case DM_ADAPT_COARSEN:
4718     case DM_ADAPT_COARSEN_LAST:
4719       PetscCall(DMPforestGetTransferSF_Internal(dmIn, dmOut, dofPerDim, &inSF, PETSC_TRUE, &inCids));
4720       PetscCall(PetscSFSetUp(inSF));
4721       break;
4722     default:
4723       PetscCall(DMPforestGetTransferSF_Internal(dmIn, dmOut, dofPerDim, &inSF, PETSC_TRUE, &inCids));
4724       PetscCall(DMPforestGetTransferSF_Internal(dmOut, dmIn, dofPerDim, &outSF, PETSC_FALSE, &outCids));
4725       PetscCall(PetscSFSetUp(inSF));
4726       PetscCall(PetscSFSetUp(outSF));
4727     }
4728   } else SETERRQ(PetscObjectComm((PetscObject)dmIn), PETSC_ERR_SUP, "Only support transfer from pre-adaptivity to post-adaptivity right now");
4729   PetscCall(DMPforestGetPlex(dmIn, &plexIn));
4730   PetscCall(DMPforestGetPlex(dmOut, &plexOut));
4731 
4732   PetscCall(DMPlexTransferVecTree(plexIn, vecIn, plexOut, vecOut, inSF, outSF, inCids, outCids, useBCs, time));
4733   PetscCall(PetscFree(inCids));
4734   PetscCall(PetscFree(outCids));
4735   PetscCall(PetscSFDestroy(&inSF));
4736   PetscCall(PetscSFDestroy(&outSF));
4737   PetscCall(PetscFree(inCids));
4738   PetscCall(PetscFree(outCids));
4739   PetscFunctionReturn(PETSC_SUCCESS);
4740 }
4741 
4742   #define DMCreateCoordinateDM_pforest _append_pforest(DMCreateCoordinateDM)
4743 static PetscErrorCode DMCreateCoordinateDM_pforest(DM dm, DM *cdm)
4744 {
4745   DM plex;
4746 
4747   PetscFunctionBegin;
4748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4749   PetscCall(DMPforestGetPlex(dm, &plex));
4750   PetscCall(DMGetCoordinateDM(plex, cdm));
4751   PetscCall(PetscObjectReference((PetscObject)*cdm));
4752   PetscFunctionReturn(PETSC_SUCCESS);
4753 }
4754 
4755   #define VecViewLocal_pforest _append_pforest(VecViewLocal)
4756 static PetscErrorCode VecViewLocal_pforest(Vec vec, PetscViewer viewer)
4757 {
4758   DM dm, plex;
4759 
4760   PetscFunctionBegin;
4761   PetscCall(VecGetDM(vec, &dm));
4762   PetscCall(DMPforestGetPlex(dm, &plex));
4763   PetscCall(VecSetDM(vec, plex));
4764   PetscCall(VecView_Plex_Local(vec, viewer));
4765   PetscCall(VecSetDM(vec, dm));
4766   PetscFunctionReturn(PETSC_SUCCESS);
4767 }
4768 
4769   #define VecView_pforest _append_pforest(VecView)
4770 static PetscErrorCode VecView_pforest(Vec vec, PetscViewer viewer)
4771 {
4772   DM dm, plex;
4773 
4774   PetscFunctionBegin;
4775   PetscCall(VecGetDM(vec, &dm));
4776   PetscCall(DMPforestGetPlex(dm, &plex));
4777   PetscCall(VecSetDM(vec, plex));
4778   PetscCall(VecView_Plex(vec, viewer));
4779   PetscCall(VecSetDM(vec, dm));
4780   PetscFunctionReturn(PETSC_SUCCESS);
4781 }
4782 
4783   #define VecView_pforest_Native _infix_pforest(VecView, _Native)
4784 static PetscErrorCode VecView_pforest_Native(Vec vec, PetscViewer viewer)
4785 {
4786   DM dm, plex;
4787 
4788   PetscFunctionBegin;
4789   PetscCall(VecGetDM(vec, &dm));
4790   PetscCall(DMPforestGetPlex(dm, &plex));
4791   PetscCall(VecSetDM(vec, plex));
4792   PetscCall(VecView_Plex_Native(vec, viewer));
4793   PetscCall(VecSetDM(vec, dm));
4794   PetscFunctionReturn(PETSC_SUCCESS);
4795 }
4796 
4797   #define VecLoad_pforest _append_pforest(VecLoad)
4798 static PetscErrorCode VecLoad_pforest(Vec vec, PetscViewer viewer)
4799 {
4800   DM dm, plex;
4801 
4802   PetscFunctionBegin;
4803   PetscCall(VecGetDM(vec, &dm));
4804   PetscCall(DMPforestGetPlex(dm, &plex));
4805   PetscCall(VecSetDM(vec, plex));
4806   PetscCall(VecLoad_Plex(vec, viewer));
4807   PetscCall(VecSetDM(vec, dm));
4808   PetscFunctionReturn(PETSC_SUCCESS);
4809 }
4810 
4811   #define VecLoad_pforest_Native _infix_pforest(VecLoad, _Native)
4812 static PetscErrorCode VecLoad_pforest_Native(Vec vec, PetscViewer viewer)
4813 {
4814   DM dm, plex;
4815 
4816   PetscFunctionBegin;
4817   PetscCall(VecGetDM(vec, &dm));
4818   PetscCall(DMPforestGetPlex(dm, &plex));
4819   PetscCall(VecSetDM(vec, plex));
4820   PetscCall(VecLoad_Plex_Native(vec, viewer));
4821   PetscCall(VecSetDM(vec, dm));
4822   PetscFunctionReturn(PETSC_SUCCESS);
4823 }
4824 
4825   #define DMCreateGlobalVector_pforest _append_pforest(DMCreateGlobalVector)
4826 static PetscErrorCode DMCreateGlobalVector_pforest(DM dm, Vec *vec)
4827 {
4828   PetscFunctionBegin;
4829   PetscCall(DMCreateGlobalVector_Section_Private(dm, vec));
4830   /* PetscCall(VecSetOperation(*vec, VECOP_DUPLICATE, (void(*)(void)) VecDuplicate_MPI_DM)); */
4831   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_pforest));
4832   PetscCall(VecSetOperation(*vec, VECOP_VIEWNATIVE, (void (*)(void))VecView_pforest_Native));
4833   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_pforest));
4834   PetscCall(VecSetOperation(*vec, VECOP_LOADNATIVE, (void (*)(void))VecLoad_pforest_Native));
4835   PetscFunctionReturn(PETSC_SUCCESS);
4836 }
4837 
4838   #define DMCreateLocalVector_pforest _append_pforest(DMCreateLocalVector)
4839 static PetscErrorCode DMCreateLocalVector_pforest(DM dm, Vec *vec)
4840 {
4841   PetscFunctionBegin;
4842   PetscCall(DMCreateLocalVector_Section_Private(dm, vec));
4843   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecViewLocal_pforest));
4844   PetscFunctionReturn(PETSC_SUCCESS);
4845 }
4846 
4847   #define DMCreateMatrix_pforest _append_pforest(DMCreateMatrix)
4848 static PetscErrorCode DMCreateMatrix_pforest(DM dm, Mat *mat)
4849 {
4850   DM plex;
4851 
4852   PetscFunctionBegin;
4853   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4854   PetscCall(DMPforestGetPlex(dm, &plex));
4855   if (plex->prealloc_only != dm->prealloc_only) plex->prealloc_only = dm->prealloc_only; /* maybe this should go into forest->plex */
4856   PetscCall(DMCreateMatrix(plex, mat));
4857   PetscCall(MatSetDM(*mat, dm));
4858   PetscFunctionReturn(PETSC_SUCCESS);
4859 }
4860 
4861   #define DMProjectFunctionLocal_pforest _append_pforest(DMProjectFunctionLocal)
4862 static PetscErrorCode DMProjectFunctionLocal_pforest(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, InsertMode mode, Vec localX)
4863 {
4864   DM plex;
4865 
4866   PetscFunctionBegin;
4867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4868   PetscCall(DMPforestGetPlex(dm, &plex));
4869   PetscCall(DMProjectFunctionLocal(plex, time, funcs, ctxs, mode, localX));
4870   PetscFunctionReturn(PETSC_SUCCESS);
4871 }
4872 
4873   #define DMProjectFunctionLabelLocal_pforest _append_pforest(DMProjectFunctionLabelLocal)
4874 static PetscErrorCode DMProjectFunctionLabelLocal_pforest(DM dm, PetscReal time, DMLabel label, PetscInt numIds, const PetscInt ids[], PetscInt Ncc, const PetscInt comps[], PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, InsertMode mode, Vec localX)
4875 {
4876   DM plex;
4877 
4878   PetscFunctionBegin;
4879   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4880   PetscCall(DMPforestGetPlex(dm, &plex));
4881   PetscCall(DMProjectFunctionLabelLocal(plex, time, label, numIds, ids, Ncc, comps, funcs, ctxs, mode, localX));
4882   PetscFunctionReturn(PETSC_SUCCESS);
4883 }
4884 
4885   #define DMProjectFieldLocal_pforest _append_pforest(DMProjectFieldLocal)
4886 PetscErrorCode DMProjectFieldLocal_pforest(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)
4887 {
4888   DM plex;
4889 
4890   PetscFunctionBegin;
4891   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4892   PetscCall(DMPforestGetPlex(dm, &plex));
4893   PetscCall(DMProjectFieldLocal(plex, time, localU, funcs, mode, localX));
4894   PetscFunctionReturn(PETSC_SUCCESS);
4895 }
4896 
4897   #define DMComputeL2Diff_pforest _append_pforest(DMComputeL2Diff)
4898 PetscErrorCode DMComputeL2Diff_pforest(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
4899 {
4900   DM plex;
4901 
4902   PetscFunctionBegin;
4903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4904   PetscCall(DMPforestGetPlex(dm, &plex));
4905   PetscCall(DMComputeL2Diff(plex, time, funcs, ctxs, X, diff));
4906   PetscFunctionReturn(PETSC_SUCCESS);
4907 }
4908 
4909   #define DMComputeL2FieldDiff_pforest _append_pforest(DMComputeL2FieldDiff)
4910 PetscErrorCode DMComputeL2FieldDiff_pforest(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal diff[])
4911 {
4912   DM plex;
4913 
4914   PetscFunctionBegin;
4915   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4916   PetscCall(DMPforestGetPlex(dm, &plex));
4917   PetscCall(DMComputeL2FieldDiff(plex, time, funcs, ctxs, X, diff));
4918   PetscFunctionReturn(PETSC_SUCCESS);
4919 }
4920 
4921   #define DMCreatelocalsection_pforest _append_pforest(DMCreatelocalsection)
4922 static PetscErrorCode DMCreatelocalsection_pforest(DM dm)
4923 {
4924   DM           plex;
4925   PetscSection section;
4926 
4927   PetscFunctionBegin;
4928   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4929   PetscCall(DMPforestGetPlex(dm, &plex));
4930   PetscCall(DMGetLocalSection(plex, &section));
4931   PetscCall(DMSetLocalSection(dm, section));
4932   PetscFunctionReturn(PETSC_SUCCESS);
4933 }
4934 
4935   #define DMCreateDefaultConstraints_pforest _append_pforest(DMCreateDefaultConstraints)
4936 static PetscErrorCode DMCreateDefaultConstraints_pforest(DM dm)
4937 {
4938   DM           plex;
4939   Mat          mat;
4940   Vec          bias;
4941   PetscSection section;
4942 
4943   PetscFunctionBegin;
4944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4945   PetscCall(DMPforestGetPlex(dm, &plex));
4946   PetscCall(DMGetDefaultConstraints(plex, &section, &mat, &bias));
4947   PetscCall(DMSetDefaultConstraints(dm, section, mat, bias));
4948   PetscFunctionReturn(PETSC_SUCCESS);
4949 }
4950 
4951   #define DMGetDimPoints_pforest _append_pforest(DMGetDimPoints)
4952 static PetscErrorCode DMGetDimPoints_pforest(DM dm, PetscInt dim, PetscInt *cStart, PetscInt *cEnd)
4953 {
4954   DM plex;
4955 
4956   PetscFunctionBegin;
4957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4958   PetscCall(DMPforestGetPlex(dm, &plex));
4959   PetscCall(DMGetDimPoints(plex, dim, cStart, cEnd));
4960   PetscFunctionReturn(PETSC_SUCCESS);
4961 }
4962 
4963   /* Need to forward declare */
4964   #define DMInitialize_pforest _append_pforest(DMInitialize)
4965 static PetscErrorCode DMInitialize_pforest(DM dm);
4966 
4967   #define DMClone_pforest _append_pforest(DMClone)
4968 static PetscErrorCode DMClone_pforest(DM dm, DM *newdm)
4969 {
4970   PetscFunctionBegin;
4971   PetscCall(DMClone_Forest(dm, newdm));
4972   PetscCall(DMInitialize_pforest(*newdm));
4973   PetscFunctionReturn(PETSC_SUCCESS);
4974 }
4975 
4976   #define DMForestCreateCellChart_pforest _append_pforest(DMForestCreateCellChart)
4977 static PetscErrorCode DMForestCreateCellChart_pforest(DM dm, PetscInt *cStart, PetscInt *cEnd)
4978 {
4979   DM_Forest         *forest;
4980   DM_Forest_pforest *pforest;
4981   PetscInt           overlap;
4982 
4983   PetscFunctionBegin;
4984   PetscCall(DMSetUp(dm));
4985   forest  = (DM_Forest *)dm->data;
4986   pforest = (DM_Forest_pforest *)forest->data;
4987   *cStart = 0;
4988   PetscCall(DMForestGetPartitionOverlap(dm, &overlap));
4989   if (overlap && pforest->ghost) {
4990     *cEnd = pforest->forest->local_num_quadrants + pforest->ghost->proc_offsets[pforest->forest->mpisize];
4991   } else {
4992     *cEnd = pforest->forest->local_num_quadrants;
4993   }
4994   PetscFunctionReturn(PETSC_SUCCESS);
4995 }
4996 
4997   #define DMForestCreateCellSF_pforest _append_pforest(DMForestCreateCellSF)
4998 static PetscErrorCode DMForestCreateCellSF_pforest(DM dm, PetscSF *cellSF)
4999 {
5000   DM_Forest         *forest;
5001   DM_Forest_pforest *pforest;
5002   PetscMPIInt        rank;
5003   PetscInt           overlap;
5004   PetscInt           cStart, cEnd, cLocalStart, cLocalEnd;
5005   PetscInt           nRoots, nLeaves, *mine = NULL;
5006   PetscSFNode       *remote = NULL;
5007   PetscSF            sf;
5008 
5009   PetscFunctionBegin;
5010   PetscCall(DMForestGetCellChart(dm, &cStart, &cEnd));
5011   forest      = (DM_Forest *)dm->data;
5012   pforest     = (DM_Forest_pforest *)forest->data;
5013   nRoots      = cEnd - cStart;
5014   cLocalStart = pforest->cLocalStart;
5015   cLocalEnd   = pforest->cLocalEnd;
5016   nLeaves     = 0;
5017   PetscCall(DMForestGetPartitionOverlap(dm, &overlap));
5018   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
5019   if (overlap && pforest->ghost) {
5020     PetscSFNode      *mirror;
5021     p4est_quadrant_t *mirror_array;
5022     PetscInt          nMirror, nGhostPre, nSelf, q;
5023     void            **mirrorPtrs;
5024 
5025     nMirror   = (PetscInt)pforest->ghost->mirrors.elem_count;
5026     nSelf     = cLocalEnd - cLocalStart;
5027     nLeaves   = nRoots - nSelf;
5028     nGhostPre = (PetscInt)pforest->ghost->proc_offsets[rank];
5029     PetscCall(PetscMalloc1(nLeaves, &mine));
5030     PetscCall(PetscMalloc1(nLeaves, &remote));
5031     PetscCall(PetscMalloc2(nMirror, &mirror, nMirror, &mirrorPtrs));
5032     mirror_array = (p4est_quadrant_t *)pforest->ghost->mirrors.array;
5033     for (q = 0; q < nMirror; q++) {
5034       p4est_quadrant_t *mir = &(mirror_array[q]);
5035 
5036       mirror[q].rank  = rank;
5037       mirror[q].index = (PetscInt)mir->p.piggy3.local_num + cLocalStart;
5038       mirrorPtrs[q]   = (void *)&(mirror[q]);
5039     }
5040     PetscCallP4est(p4est_ghost_exchange_custom, (pforest->forest, pforest->ghost, sizeof(PetscSFNode), mirrorPtrs, remote));
5041     PetscCall(PetscFree2(mirror, mirrorPtrs));
5042     for (q = 0; q < nGhostPre; q++) mine[q] = q;
5043     for (; q < nLeaves; q++) mine[q] = (q - nGhostPre) + cLocalEnd;
5044   }
5045   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf));
5046   PetscCall(PetscSFSetGraph(sf, nRoots, nLeaves, mine, PETSC_OWN_POINTER, remote, PETSC_OWN_POINTER));
5047   *cellSF = sf;
5048   PetscFunctionReturn(PETSC_SUCCESS);
5049 }
5050 
5051 static PetscErrorCode DMCreateNeumannOverlap_pforest(DM dm, IS *ovl, Mat *J, PetscErrorCode (**setup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void **setup_ctx)
5052 {
5053   DM plex;
5054 
5055   PetscFunctionBegin;
5056   PetscCall(DMPforestGetPlex(dm, &plex));
5057   PetscCall(DMCreateNeumannOverlap_Plex(plex, ovl, J, setup, setup_ctx));
5058   if (!*setup) {
5059     PetscCall(PetscObjectQueryFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", setup));
5060     if (*setup) PetscCall(PetscObjectCompose((PetscObject)*ovl, "_DM_Original_HPDDM", (PetscObject)dm));
5061   }
5062   PetscFunctionReturn(PETSC_SUCCESS);
5063 }
5064 
5065   #define DMCreateDomainDecomposition_pforest _append_pforest(DMCreateDomainDecomposition)
5066 static PetscErrorCode DMCreateDomainDecomposition_pforest(DM dm, PetscInt *nsub, char ***names, IS **innerises, IS **outerises, DM **dms)
5067 {
5068   DM plex;
5069 
5070   PetscFunctionBegin;
5071   PetscCall(DMPforestGetPlex(dm, &plex));
5072   PetscCall(DMCreateDomainDecomposition(plex, nsub, names, innerises, outerises, dms));
5073   PetscFunctionReturn(PETSC_SUCCESS);
5074 }
5075 
5076   #define DMCreateDomainDecompositionScatters_pforest _append_pforest(DMCreateDomainDecompositionScatters)
5077 static PetscErrorCode DMCreateDomainDecompositionScatters_pforest(DM dm, PetscInt n, DM *subdms, VecScatter **iscat, VecScatter **oscat, VecScatter **lscat)
5078 {
5079   DM plex;
5080 
5081   PetscFunctionBegin;
5082   PetscCall(DMPforestGetPlex(dm, &plex));
5083   PetscCall(DMCreateDomainDecompositionScatters(plex, n, subdms, iscat, oscat, lscat));
5084   PetscFunctionReturn(PETSC_SUCCESS);
5085 }
5086 
5087 static PetscErrorCode DMInitialize_pforest(DM dm)
5088 {
5089   PetscFunctionBegin;
5090   dm->ops->setup                     = DMSetUp_pforest;
5091   dm->ops->view                      = DMView_pforest;
5092   dm->ops->clone                     = DMClone_pforest;
5093   dm->ops->createinterpolation       = DMCreateInterpolation_pforest;
5094   dm->ops->createinjection           = DMCreateInjection_pforest;
5095   dm->ops->setfromoptions            = DMSetFromOptions_pforest;
5096   dm->ops->createcoordinatedm        = DMCreateCoordinateDM_pforest;
5097   dm->ops->createglobalvector        = DMCreateGlobalVector_pforest;
5098   dm->ops->createlocalvector         = DMCreateLocalVector_pforest;
5099   dm->ops->creatematrix              = DMCreateMatrix_pforest;
5100   dm->ops->projectfunctionlocal      = DMProjectFunctionLocal_pforest;
5101   dm->ops->projectfunctionlabellocal = DMProjectFunctionLabelLocal_pforest;
5102   dm->ops->projectfieldlocal         = DMProjectFieldLocal_pforest;
5103   dm->ops->createlocalsection        = DMCreatelocalsection_pforest;
5104   dm->ops->createdefaultconstraints  = DMCreateDefaultConstraints_pforest;
5105   dm->ops->computel2diff             = DMComputeL2Diff_pforest;
5106   dm->ops->computel2fielddiff        = DMComputeL2FieldDiff_pforest;
5107   dm->ops->getdimpoints              = DMGetDimPoints_pforest;
5108   dm->ops->createdomaindecomposition = DMCreateDomainDecomposition_pforest;
5109   dm->ops->createddscatters          = DMCreateDomainDecompositionScatters_pforest;
5110 
5111   PetscCall(PetscObjectComposeFunction((PetscObject)dm, PetscStringize(DMConvert_plex_pforest) "_C", DMConvert_plex_pforest));
5112   PetscCall(PetscObjectComposeFunction((PetscObject)dm, PetscStringize(DMConvert_pforest_plex) "_C", DMConvert_pforest_plex));
5113   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", DMCreateNeumannOverlap_pforest));
5114   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMForestGetPartitionOverlap));
5115   PetscFunctionReturn(PETSC_SUCCESS);
5116 }
5117 
5118   #define DMCreate_pforest _append_pforest(DMCreate)
5119 PETSC_EXTERN PetscErrorCode DMCreate_pforest(DM dm)
5120 {
5121   DM_Forest         *forest;
5122   DM_Forest_pforest *pforest;
5123 
5124   PetscFunctionBegin;
5125   PetscCall(PetscP4estInitialize());
5126   PetscCall(DMCreate_Forest(dm));
5127   PetscCall(DMInitialize_pforest(dm));
5128   PetscCall(DMSetDimension(dm, P4EST_DIM));
5129 
5130   /* set forest defaults */
5131   PetscCall(DMForestSetTopology(dm, "unit"));
5132   PetscCall(DMForestSetMinimumRefinement(dm, 0));
5133   PetscCall(DMForestSetInitialRefinement(dm, 0));
5134   PetscCall(DMForestSetMaximumRefinement(dm, P4EST_QMAXLEVEL));
5135   PetscCall(DMForestSetGradeFactor(dm, 2));
5136   PetscCall(DMForestSetAdjacencyDimension(dm, 0));
5137   PetscCall(DMForestSetPartitionOverlap(dm, 0));
5138 
5139   /* create p4est data */
5140   PetscCall(PetscNew(&pforest));
5141 
5142   forest                            = (DM_Forest *)dm->data;
5143   forest->data                      = pforest;
5144   forest->destroy                   = DMForestDestroy_pforest;
5145   forest->ftemplate                 = DMForestTemplate_pforest;
5146   forest->transfervec               = DMForestTransferVec_pforest;
5147   forest->transfervecfrombase       = DMForestTransferVecFromBase_pforest;
5148   forest->createcellchart           = DMForestCreateCellChart_pforest;
5149   forest->createcellsf              = DMForestCreateCellSF_pforest;
5150   forest->clearadaptivityforest     = DMForestClearAdaptivityForest_pforest;
5151   forest->getadaptivitysuccess      = DMForestGetAdaptivitySuccess_pforest;
5152   pforest->topo                     = NULL;
5153   pforest->forest                   = NULL;
5154   pforest->ghost                    = NULL;
5155   pforest->lnodes                   = NULL;
5156   pforest->partition_for_coarsening = PETSC_TRUE;
5157   pforest->coarsen_hierarchy        = PETSC_FALSE;
5158   pforest->cLocalStart              = -1;
5159   pforest->cLocalEnd                = -1;
5160   pforest->labelsFinalized          = PETSC_FALSE;
5161   pforest->ghostName                = NULL;
5162   PetscFunctionReturn(PETSC_SUCCESS);
5163 }
5164 
5165 #endif /* defined(PETSC_HAVE_P4EST) */
5166