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