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