xref: /petsc/src/dm/impls/plex/plexsfc.c (revision 2b4f33d9851e665c71c735db5ecd66df92829a75)
13e72e933SJed Brown #include <petsc/private/dmpleximpl.h> /*I   "petscdmplex.h"   I*/
23e72e933SJed Brown #include <petscsf.h>
33e72e933SJed Brown #include <petsc/private/hashset.h>
43e72e933SJed Brown 
53e72e933SJed Brown typedef uint64_t ZCode;
63e72e933SJed Brown 
73e72e933SJed Brown PETSC_HASH_SET(ZSet, ZCode, PetscHash_UInt64, PetscHashEqual)
83e72e933SJed Brown 
93e72e933SJed Brown typedef struct {
103e72e933SJed Brown   PetscInt i, j, k;
113e72e933SJed Brown } Ijk;
123e72e933SJed Brown 
133e72e933SJed Brown typedef struct {
143e72e933SJed Brown   Ijk         eextent;
153e72e933SJed Brown   Ijk         vextent;
163e72e933SJed Brown   PetscMPIInt comm_size;
173e72e933SJed Brown   ZCode      *zstarts;
183e72e933SJed Brown } ZLayout;
193e72e933SJed Brown 
20376e073fSJames Wright // ***** Overview of ZCode *******
21376e073fSJames Wright // The SFC uses integer indexing for each dimension and encodes them into a single integer by interleaving the bits of each index.
22376e073fSJames Wright // This is known as Morton encoding, and is refered to as ZCode in this code.
23376e073fSJames Wright // So for index i having bits [i2,i1,i0], and similar for indexes j and k, the ZCode (Morton number) would be:
24376e073fSJames Wright //    [k2,j2,i2,k1,j1,i1,k0,j0,i0]
25376e073fSJames Wright // This encoding allows for easier traversal of the SFC structure (see https://en.wikipedia.org/wiki/Z-order_curve and `ZStepOct()`).
26376e073fSJames Wright // `ZEncode()` is used to go from indices to ZCode, while `ZCodeSplit()` goes from ZCode back to indices.
27376e073fSJames Wright 
28376e073fSJames Wright // Decodes the leading interleaved index from a ZCode
29376e073fSJames Wright // e.g. [k2,j2,i2,k1,j1,i1,k0,j0,i0] -> [i2,i1,i0]
30bc3e2e66SJames Wright // Magic numbers taken from https://stackoverflow.com/a/18528775/7564988 (translated to octal)
313e72e933SJed Brown static unsigned ZCodeSplit1(ZCode z)
323e72e933SJed Brown {
33bc3e2e66SJames Wright   z &= 0111111111111111111111;
34bc3e2e66SJames Wright   z = (z | z >> 2) & 0103030303030303030303;
35bc3e2e66SJames Wright   z = (z | z >> 4) & 0100170017001700170017;
36bc3e2e66SJames Wright   z = (z | z >> 8) & 0370000037700000377;
37bc3e2e66SJames Wright   z = (z | z >> 16) & 0370000000000177777;
38bc3e2e66SJames Wright   z = (z | z >> 32) & 07777777;
393e72e933SJed Brown   return (unsigned)z;
403e72e933SJed Brown }
413e72e933SJed Brown 
42376e073fSJames Wright // Encodes the leading interleaved index from a ZCode
43376e073fSJames Wright // e.g. [i2,i1,i0] -> [0,0,i2,0,0,i1,0,0,i0]
443e72e933SJed Brown static ZCode ZEncode1(unsigned t)
453e72e933SJed Brown {
463e72e933SJed Brown   ZCode z = t;
47bc3e2e66SJames Wright   z &= 07777777;
48bc3e2e66SJames Wright   z = (z | z << 32) & 0370000000000177777;
49bc3e2e66SJames Wright   z = (z | z << 16) & 0370000037700000377;
50bc3e2e66SJames Wright   z = (z | z << 8) & 0100170017001700170017;
51bc3e2e66SJames Wright   z = (z | z << 4) & 0103030303030303030303;
52bc3e2e66SJames Wright   z = (z | z << 2) & 0111111111111111111111;
533e72e933SJed Brown   return z;
543e72e933SJed Brown }
553e72e933SJed Brown 
56376e073fSJames Wright // Decodes i j k indices from a ZCode.
57376e073fSJames Wright // Uses `ZCodeSplit1()` by shifting ZCode so that the leading index is the desired one to decode
583e72e933SJed Brown static Ijk ZCodeSplit(ZCode z)
593e72e933SJed Brown {
603e72e933SJed Brown   Ijk c;
613e72e933SJed Brown   c.i = ZCodeSplit1(z >> 2);
623e72e933SJed Brown   c.j = ZCodeSplit1(z >> 1);
633e72e933SJed Brown   c.k = ZCodeSplit1(z >> 0);
643e72e933SJed Brown   return c;
653e72e933SJed Brown }
663e72e933SJed Brown 
67376e073fSJames Wright // Encodes i j k indices to a ZCode.
68376e073fSJames Wright // Uses `ZCodeEncode1()` by shifting resulting ZCode to the appropriate bit placement
693e72e933SJed Brown static ZCode ZEncode(Ijk c)
703e72e933SJed Brown {
716497c311SBarry Smith   ZCode z = (ZEncode1((unsigned int)c.i) << 2) | (ZEncode1((unsigned int)c.j) << 1) | ZEncode1((unsigned int)c.k);
723e72e933SJed Brown   return z;
733e72e933SJed Brown }
743e72e933SJed Brown 
753e72e933SJed Brown static PetscBool IjkActive(Ijk extent, Ijk l)
763e72e933SJed Brown {
773e72e933SJed Brown   if (l.i < extent.i && l.j < extent.j && l.k < extent.k) return PETSC_TRUE;
783e72e933SJed Brown   return PETSC_FALSE;
793e72e933SJed Brown }
803e72e933SJed Brown 
816547ddbdSJed Brown // If z is not the base of an octet (last 3 bits 0), return 0.
826547ddbdSJed Brown //
836547ddbdSJed Brown // If z is the base of an octet, we recursively grow to the biggest structured octet. This is typically useful when a z
846547ddbdSJed Brown // is outside the domain and we wish to skip a (possibly recursively large) octet to find our next interesting point.
856547ddbdSJed Brown static ZCode ZStepOct(ZCode z)
866547ddbdSJed Brown {
876547ddbdSJed Brown   if (PetscUnlikely(z == 0)) return 0; // Infinite loop below if z == 0
886547ddbdSJed Brown   ZCode step = 07;
896547ddbdSJed Brown   for (; (z & step) == 0; step = (step << 3) | 07) { }
906547ddbdSJed Brown   return step >> 3;
916547ddbdSJed Brown }
926547ddbdSJed Brown 
933e72e933SJed Brown // Since element/vertex box extents are typically not equal powers of 2, Z codes that lie within the domain are not contiguous.
945f06a3ddSJed Brown static PetscErrorCode ZLayoutCreate(PetscMPIInt size, const PetscInt eextent[3], const PetscInt vextent[3], ZLayout *layout)
953e72e933SJed Brown {
96c77877e3SJed Brown   PetscFunctionBegin;
975f06a3ddSJed Brown   layout->eextent.i = eextent[0];
985f06a3ddSJed Brown   layout->eextent.j = eextent[1];
995f06a3ddSJed Brown   layout->eextent.k = eextent[2];
1005f06a3ddSJed Brown   layout->vextent.i = vextent[0];
1015f06a3ddSJed Brown   layout->vextent.j = vextent[1];
1025f06a3ddSJed Brown   layout->vextent.k = vextent[2];
1035f06a3ddSJed Brown   layout->comm_size = size;
1045f06a3ddSJed Brown   layout->zstarts   = NULL;
1055f06a3ddSJed Brown   PetscCall(PetscMalloc1(size + 1, &layout->zstarts));
1063e72e933SJed Brown 
1073e72e933SJed Brown   PetscInt total_elems = eextent[0] * eextent[1] * eextent[2];
1083e72e933SJed Brown   ZCode    z           = 0;
1095f06a3ddSJed Brown   layout->zstarts[0]   = 0;
1106547ddbdSJed Brown   // This loop traverses all vertices in the global domain, so is worth making fast. We use ZStepBound
1113e72e933SJed Brown   for (PetscMPIInt r = 0; r < size; r++) {
1123e72e933SJed Brown     PetscInt elems_needed = (total_elems / size) + (total_elems % size > r), count;
1133e72e933SJed Brown     for (count = 0; count < elems_needed; z++) {
1146547ddbdSJed Brown       ZCode skip = ZStepOct(z); // optimistically attempt a longer step
1156547ddbdSJed Brown       for (ZCode s = skip;; s >>= 3) {
1166547ddbdSJed Brown         Ijk trial = ZCodeSplit(z + s);
1176547ddbdSJed Brown         if (IjkActive(layout->eextent, trial)) {
1186547ddbdSJed Brown           while (count + s + 1 > (ZCode)elems_needed) s >>= 3; // Shrink the octet
1196547ddbdSJed Brown           count += s + 1;
1206547ddbdSJed Brown           z += s;
1216547ddbdSJed Brown           break;
1226547ddbdSJed Brown         }
1236547ddbdSJed Brown         if (s == 0) { // the whole skip octet is inactive
1246547ddbdSJed Brown           z += skip;
1256547ddbdSJed Brown           break;
1266547ddbdSJed Brown         }
1276547ddbdSJed Brown       }
1283e72e933SJed Brown     }
1293e72e933SJed Brown     // Pick up any extra vertices in the Z ordering before the next rank's first owned element.
130c77877e3SJed Brown     //
1315f06a3ddSJed Brown     // This leads to poorly balanced vertices when eextent is a power of 2, since all the fringe vertices end up
132c77877e3SJed Brown     // on the last rank. A possible solution is to balance the Z-order vertices independently from the cells, which will
133c77877e3SJed Brown     // result in a lot of element closures being remote. We could finish marking boundary conditions, then do a round of
134c77877e3SJed Brown     // vertex ownership smoothing (which would reorder and redistribute vertices without touching element distribution).
135c77877e3SJed Brown     // Another would be to have an analytic ownership criteria for vertices in the fringe veextent - eextent. This would
136c77877e3SJed Brown     // complicate the job of identifying an owner and its offset.
1375f06a3ddSJed Brown     //
1385f06a3ddSJed Brown     // The current recommended approach is to let `-dm_distribute 1` (default) resolve vertex ownership. This is
1395f06a3ddSJed Brown     // *mandatory* with isoperiodicity (except in special cases) to remove standed vertices from local spaces. Here's
1405f06a3ddSJed Brown     // the issue:
1415f06a3ddSJed Brown     //
1425f06a3ddSJed Brown     // Consider this partition on rank 0 (left) and rank 1.
1435f06a3ddSJed Brown     //
1445f06a3ddSJed Brown     //    4 --------  5 -- 14 --10 -- 21 --11
1455f06a3ddSJed Brown     //                |          |          |
1465f06a3ddSJed Brown     // 7 -- 16 --  8  |          |          |
1475f06a3ddSJed Brown     // |           |  3 -------  7 -------  9
1485f06a3ddSJed Brown     // |           |             |          |
1495f06a3ddSJed Brown     // 4 --------  6 ------ 10   |          |
1505f06a3ddSJed Brown     // |           |         |   6 -- 16 -- 8
1515f06a3ddSJed Brown     // |           |         |
1525f06a3ddSJed Brown     // 3 ---11---  5 --18--  9
1535f06a3ddSJed Brown     //
1545f06a3ddSJed Brown     // The periodic face SF looks like
1555f06a3ddSJed Brown     // [0] Number of roots=21, leaves=1, remote ranks=1
1565f06a3ddSJed Brown     // [0] 16 <- (0,11)
1575f06a3ddSJed Brown     // [1] Number of roots=22, leaves=2, remote ranks=2
1585f06a3ddSJed Brown     // [1] 14 <- (0,18)
1595f06a3ddSJed Brown     // [1] 21 <- (1,16)
1605f06a3ddSJed Brown     //
1615f06a3ddSJed Brown     // In handling face (0,16), rank 0 learns that (0,7) and (0,8) map to (0,3) and (0,5) respectively, thus we won't use
1625f06a3ddSJed Brown     // the point SF links to (1,4) and (1,5). Rank 1 learns about the periodic mapping of (1,5) while handling face
1635f06a3ddSJed Brown     // (1,14), but never learns that vertex (1,4) has been mapped to (0,3) by face (0,16).
1645f06a3ddSJed Brown     //
1655f06a3ddSJed Brown     // We can relatively easily inform vertex (1,4) of this mapping, but it stays in rank 1's local space despite not
1665f06a3ddSJed Brown     // being in the closure and thus not being contributed to. This would be mostly harmless except that some viewer
1675f06a3ddSJed Brown     // routines expect all local points to be somehow significant. It is not easy to analytically remove the (1,4)
1685f06a3ddSJed Brown     // vertex because the point SF and isoperiodic face SF would need to be updated to account for removal of the
1695f06a3ddSJed Brown     // stranded vertices.
1705f06a3ddSJed Brown     for (; z <= ZEncode(layout->vextent); z++) {
1713e72e933SJed Brown       Ijk loc = ZCodeSplit(z);
1725f06a3ddSJed Brown       if (IjkActive(layout->eextent, loc)) break;
1736547ddbdSJed Brown       z += ZStepOct(z);
1743e72e933SJed Brown     }
1755f06a3ddSJed Brown     layout->zstarts[r + 1] = z;
1763e72e933SJed Brown   }
1776547ddbdSJed Brown   layout->zstarts[size] = ZEncode(layout->vextent);
1783ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1793e72e933SJed Brown }
1803e72e933SJed Brown 
1814e2e9504SJed Brown static PetscInt ZLayoutElementsOnRank(const ZLayout *layout, PetscMPIInt rank)
1824e2e9504SJed Brown {
1834e2e9504SJed Brown   PetscInt remote_elem = 0;
1844e2e9504SJed Brown   for (ZCode rz = layout->zstarts[rank]; rz < layout->zstarts[rank + 1]; rz++) {
1854e2e9504SJed Brown     Ijk loc = ZCodeSplit(rz);
1864e2e9504SJed Brown     if (IjkActive(layout->eextent, loc)) remote_elem++;
1876547ddbdSJed Brown     else rz += ZStepOct(rz);
1884e2e9504SJed Brown   }
1894e2e9504SJed Brown   return remote_elem;
1904e2e9504SJed Brown }
1914e2e9504SJed Brown 
19266976f2fSJacob Faibussowitsch static PetscInt ZCodeFind(ZCode key, PetscInt n, const ZCode X[])
1933e72e933SJed Brown {
1943e72e933SJed Brown   PetscInt lo = 0, hi = n;
1953e72e933SJed Brown 
1963e72e933SJed Brown   if (n == 0) return -1;
1973e72e933SJed Brown   while (hi - lo > 1) {
1983e72e933SJed Brown     PetscInt mid = lo + (hi - lo) / 2;
1993e72e933SJed Brown     if (key < X[mid]) hi = mid;
2003e72e933SJed Brown     else lo = mid;
2013e72e933SJed Brown   }
2023e72e933SJed Brown   return key == X[lo] ? lo : -(lo + (key > X[lo]) + 1);
2033e72e933SJed Brown }
2043e72e933SJed Brown 
20585de0153SJames Wright static inline PetscBool IsPointInsideStratum(PetscInt point, PetscInt pStart, PetscInt pEnd)
20685de0153SJames Wright {
20785de0153SJames Wright   return (point >= pStart && point < pEnd) ? PETSC_TRUE : PETSC_FALSE;
20885de0153SJames Wright }
20985de0153SJames Wright 
2109ae3492bSJames Wright static PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(DM dm, const ZLayout *layout, const ZCode *vert_z, PetscSegBuffer per_faces[3], const PetscReal *lower, const PetscReal *upper, const DMBoundaryType *periodicity, PetscSegBuffer donor_face_closure[3], PetscSegBuffer my_donor_faces[3])
2114e2e9504SJed Brown {
2124e2e9504SJed Brown   MPI_Comm    comm;
2139ae3492bSJames Wright   PetscInt    dim, vStart, vEnd;
2144e2e9504SJed Brown   PetscMPIInt size;
2159ae3492bSJames Wright   PetscSF     face_sfs[3];
2169ae3492bSJames Wright   PetscScalar transforms[3][4][4] = {{{0}}};
2174e2e9504SJed Brown 
2184e2e9504SJed Brown   PetscFunctionBegin;
2194e2e9504SJed Brown   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2204e2e9504SJed Brown   PetscCallMPI(MPI_Comm_size(comm, &size));
2214e2e9504SJed Brown   PetscCall(DMGetDimension(dm, &dim));
2224e2e9504SJed Brown   const PetscInt csize = PetscPowInt(2, dim - 1);
2234e2e9504SJed Brown   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2249ae3492bSJames Wright 
2259ae3492bSJames Wright   PetscInt num_directions = 0;
2269ae3492bSJames Wright   for (PetscInt direction = 0; direction < dim; direction++) {
2276497c311SBarry Smith     PetscCount   num_faces;
2289ae3492bSJames Wright     PetscInt    *faces;
2299ae3492bSJames Wright     ZCode       *donor_verts, *donor_minz;
2309ae3492bSJames Wright     PetscSFNode *leaf;
2316497c311SBarry Smith     PetscCount   num_multiroots = 0;
2326497c311SBarry Smith     PetscInt     pStart, pEnd;
2336497c311SBarry Smith     PetscBool    sorted;
2346497c311SBarry Smith     PetscInt     inum_faces;
2359ae3492bSJames Wright 
2369ae3492bSJames Wright     if (periodicity[direction] != DM_BOUNDARY_PERIODIC) continue;
2379ae3492bSJames Wright     PetscCall(PetscSegBufferGetSize(per_faces[direction], &num_faces));
2389ae3492bSJames Wright     PetscCall(PetscSegBufferExtractInPlace(per_faces[direction], &faces));
2399ae3492bSJames Wright     PetscCall(PetscSegBufferExtractInPlace(donor_face_closure[direction], &donor_verts));
2404e2e9504SJed Brown     PetscCall(PetscMalloc1(num_faces, &donor_minz));
2414e2e9504SJed Brown     PetscCall(PetscMalloc1(num_faces, &leaf));
2426497c311SBarry Smith     for (PetscCount i = 0; i < num_faces; i++) {
2434e2e9504SJed Brown       ZCode minz = donor_verts[i * csize];
2446497c311SBarry Smith 
2454e2e9504SJed Brown       for (PetscInt j = 1; j < csize; j++) minz = PetscMin(minz, donor_verts[i * csize + j]);
2464e2e9504SJed Brown       donor_minz[i] = minz;
2474e2e9504SJed Brown     }
2486497c311SBarry Smith     PetscCall(PetscIntCast(num_faces, &inum_faces));
2496497c311SBarry Smith     PetscCall(PetscSortedInt64(inum_faces, (const PetscInt64 *)donor_minz, &sorted));
2509ae3492bSJames Wright     // If a donor vertex were chosen to broker multiple faces, we would have a logic error.
2519ae3492bSJames Wright     // Checking for sorting is a cheap check that there are no duplicates.
2529ae3492bSJames Wright     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_PLIB, "minz not sorted; possible duplicates not checked");
2536497c311SBarry Smith     for (PetscCount i = 0; i < num_faces;) {
2544e2e9504SJed Brown       ZCode       z = donor_minz[i];
255835f2295SStefano Zampini       PetscMPIInt remote_rank, remote_count = 0;
2566497c311SBarry Smith 
257835f2295SStefano Zampini       PetscCall(PetscMPIIntCast(ZCodeFind(z, size + 1, layout->zstarts), &remote_rank));
2584e2e9504SJed Brown       if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1;
2594e2e9504SJed Brown       // Process all the vertices on this rank
2604e2e9504SJed Brown       for (ZCode rz = layout->zstarts[remote_rank]; rz < layout->zstarts[remote_rank + 1]; rz++) {
2614e2e9504SJed Brown         Ijk loc = ZCodeSplit(rz);
2626497c311SBarry Smith 
2634e2e9504SJed Brown         if (rz == z) {
2644e2e9504SJed Brown           leaf[i].rank  = remote_rank;
2654e2e9504SJed Brown           leaf[i].index = remote_count;
2664e2e9504SJed Brown           i++;
2676497c311SBarry Smith           if (i == num_faces) break;
2684e2e9504SJed Brown           z = donor_minz[i];
2694e2e9504SJed Brown         }
2704e2e9504SJed Brown         if (IjkActive(layout->vextent, loc)) remote_count++;
2714e2e9504SJed Brown       }
2724e2e9504SJed Brown     }
2734e2e9504SJed Brown     PetscCall(PetscFree(donor_minz));
2749ae3492bSJames Wright     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &face_sfs[num_directions]));
2756497c311SBarry Smith     PetscCall(PetscSFSetGraph(face_sfs[num_directions], vEnd - vStart, inum_faces, NULL, PETSC_USE_POINTER, leaf, PETSC_USE_POINTER));
2764e2e9504SJed Brown     const PetscInt *my_donor_degree;
2779ae3492bSJames Wright     PetscCall(PetscSFComputeDegreeBegin(face_sfs[num_directions], &my_donor_degree));
2789ae3492bSJames Wright     PetscCall(PetscSFComputeDegreeEnd(face_sfs[num_directions], &my_donor_degree));
2796497c311SBarry Smith 
2804e2e9504SJed Brown     for (PetscInt i = 0; i < vEnd - vStart; i++) {
2814e2e9504SJed Brown       num_multiroots += my_donor_degree[i];
2824e2e9504SJed Brown       if (my_donor_degree[i] == 0) continue;
2835f06a3ddSJed Brown       PetscAssert(my_donor_degree[i] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex has multiple faces");
2844e2e9504SJed Brown     }
2854e2e9504SJed Brown     PetscInt  *my_donors, *donor_indices, *my_donor_indices;
2866497c311SBarry Smith     PetscCount num_my_donors;
2876497c311SBarry Smith 
2889ae3492bSJames Wright     PetscCall(PetscSegBufferGetSize(my_donor_faces[direction], &num_my_donors));
289563af49cSJames Wright     PetscCheck(num_my_donors == num_multiroots, PETSC_COMM_SELF, PETSC_ERR_SUP, "Donor request (%" PetscCount_FMT ") does not match expected donors (%" PetscCount_FMT ")", num_my_donors, num_multiroots);
2909ae3492bSJames Wright     PetscCall(PetscSegBufferExtractInPlace(my_donor_faces[direction], &my_donors));
2914e2e9504SJed Brown     PetscCall(PetscMalloc1(vEnd - vStart, &my_donor_indices));
2926497c311SBarry Smith     for (PetscCount i = 0; i < num_my_donors; i++) {
2934e2e9504SJed Brown       PetscInt f = my_donors[i];
2941690c2aeSBarry Smith       PetscInt num_points, *points = NULL, minv = PETSC_INT_MAX;
2956497c311SBarry Smith 
2964e2e9504SJed Brown       PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points));
2974e2e9504SJed Brown       for (PetscInt j = 0; j < num_points; j++) {
2984e2e9504SJed Brown         PetscInt p = points[2 * j];
29985de0153SJames Wright         if (!IsPointInsideStratum(p, vStart, vEnd)) continue;
3004e2e9504SJed Brown         minv = PetscMin(minv, p);
3014e2e9504SJed Brown       }
3024e2e9504SJed Brown       PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points));
3035f06a3ddSJed Brown       PetscAssert(my_donor_degree[minv - vStart] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex not requested");
3044e2e9504SJed Brown       my_donor_indices[minv - vStart] = f;
3054e2e9504SJed Brown     }
3064e2e9504SJed Brown     PetscCall(PetscMalloc1(num_faces, &donor_indices));
3079ae3492bSJames Wright     PetscCall(PetscSFBcastBegin(face_sfs[num_directions], MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE));
3089ae3492bSJames Wright     PetscCall(PetscSFBcastEnd(face_sfs[num_directions], MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE));
3094e2e9504SJed Brown     PetscCall(PetscFree(my_donor_indices));
3104e2e9504SJed Brown     // Modify our leafs so they point to donor faces instead of donor minz. Additionally, give them indices as faces.
3116497c311SBarry Smith     for (PetscCount i = 0; i < num_faces; i++) leaf[i].index = donor_indices[i];
3124e2e9504SJed Brown     PetscCall(PetscFree(donor_indices));
3134e2e9504SJed Brown     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3146497c311SBarry Smith     PetscCall(PetscSFSetGraph(face_sfs[num_directions], pEnd - pStart, inum_faces, faces, PETSC_COPY_VALUES, leaf, PETSC_OWN_POINTER));
3159ae3492bSJames Wright     {
3169ae3492bSJames Wright       char face_sf_name[PETSC_MAX_PATH_LEN];
3179ae3492bSJames Wright       PetscCall(PetscSNPrintf(face_sf_name, sizeof face_sf_name, "Z-order Isoperiodic Faces #%" PetscInt_FMT, num_directions));
3189ae3492bSJames Wright       PetscCall(PetscObjectSetName((PetscObject)face_sfs[num_directions], face_sf_name));
3199ae3492bSJames Wright     }
3204e2e9504SJed Brown 
3219ae3492bSJames Wright     transforms[num_directions][0][0]         = 1;
3229ae3492bSJames Wright     transforms[num_directions][1][1]         = 1;
3239ae3492bSJames Wright     transforms[num_directions][2][2]         = 1;
3249ae3492bSJames Wright     transforms[num_directions][3][3]         = 1;
3259ae3492bSJames Wright     transforms[num_directions][direction][3] = upper[direction] - lower[direction];
3269ae3492bSJames Wright     num_directions++;
3279ae3492bSJames Wright   }
3286725e60dSJed Brown 
3291fca310dSJames Wright   PetscCall(DMPlexSetIsoperiodicFaceSF(dm, num_directions, face_sfs));
3301fca310dSJames Wright   PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, num_directions, (PetscScalar *)transforms));
3319ae3492bSJames Wright 
3329ae3492bSJames Wright   for (PetscInt i = 0; i < num_directions; i++) PetscCall(PetscSFDestroy(&face_sfs[i]));
3333ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
3344e2e9504SJed Brown }
3354e2e9504SJed Brown 
3365f06a3ddSJed Brown // This is a DMGlobalToLocalHook that applies the affine offsets. When extended for rotated periodicity, it'll need to
3375f06a3ddSJed Brown // apply a rotatonal transform and similar operations will be needed for fields (e.g., to rotate a velocity vector).
3385f06a3ddSJed Brown // We use this crude approach here so we don't have to write new GPU kernels yet.
3396725e60dSJed Brown static PetscErrorCode DMCoordAddPeriodicOffsets_Private(DM dm, Vec g, InsertMode mode, Vec l, void *ctx)
3406725e60dSJed Brown {
3416725e60dSJed Brown   PetscFunctionBegin;
342ddedc8f6SJames Wright   // These `VecScatter`s should be merged to improve efficiency; the scatters cannot be overlapped.
343ddedc8f6SJames Wright   for (PetscInt i = 0; i < dm->periodic.num_affines; i++) {
344ddedc8f6SJames Wright     PetscCall(VecScatterBegin(dm->periodic.affine_to_local[i], dm->periodic.affine[i], l, ADD_VALUES, SCATTER_FORWARD));
345ddedc8f6SJames Wright     PetscCall(VecScatterEnd(dm->periodic.affine_to_local[i], dm->periodic.affine[i], l, ADD_VALUES, SCATTER_FORWARD));
346ddedc8f6SJames Wright   }
3473ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
3486725e60dSJed Brown }
3496725e60dSJed Brown 
35093defd67SJames Wright // Creates SF to communicate data from donor to periodic faces. The data can be different sizes per donor-periodic pair and is given in `point_sizes[]`
35193defd67SJames Wright PetscErrorCode CreateDonorToPeriodicSF(DM dm, PetscSF face_sf, PetscInt pStart, PetscInt pEnd, const PetscInt point_sizes[], PetscInt *rootbuffersize, PetscInt *leafbuffersize, PetscBT *rootbt, PetscSF *sf_closure)
35293defd67SJames Wright {
35393defd67SJames Wright   MPI_Comm           comm;
35493defd67SJames Wright   PetscMPIInt        rank;
35593defd67SJames Wright   PetscInt           nroots, nleaves;
35693defd67SJames Wright   PetscInt          *rootdata, *leafdata;
35793defd67SJames Wright   const PetscInt    *filocal;
35893defd67SJames Wright   const PetscSFNode *firemote;
35993defd67SJames Wright 
36093defd67SJames Wright   PetscFunctionBeginUser;
36193defd67SJames Wright   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
36293defd67SJames Wright   PetscCallMPI(MPI_Comm_rank(comm, &rank));
36393defd67SJames Wright 
36493defd67SJames Wright   PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote));
36593defd67SJames Wright   PetscCall(PetscCalloc2(2 * nroots, &rootdata, 2 * nroots, &leafdata));
36693defd67SJames Wright   for (PetscInt i = 0; i < nleaves; i++) {
36793defd67SJames Wright     PetscInt point = filocal[i];
36885de0153SJames Wright     PetscCheck(IsPointInsideStratum(point, pStart, pEnd), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " in leaves exists outside of stratum [%" PetscInt_FMT ", %" PetscInt_FMT ")", point, pStart, pEnd);
36993defd67SJames Wright     leafdata[point] = point_sizes[point - pStart];
37093defd67SJames Wright   }
37193defd67SJames Wright   PetscCall(PetscSFReduceBegin(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM));
37293defd67SJames Wright   PetscCall(PetscSFReduceEnd(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM));
37393defd67SJames Wright 
37493defd67SJames Wright   PetscInt root_offset = 0;
37593defd67SJames Wright   PetscCall(PetscBTCreate(nroots, rootbt));
37693defd67SJames Wright   for (PetscInt p = 0; p < nroots; p++) {
37793defd67SJames Wright     const PetscInt *donor_dof = rootdata + nroots;
37893defd67SJames Wright     if (donor_dof[p] == 0) {
37993defd67SJames Wright       rootdata[2 * p]     = -1;
38093defd67SJames Wright       rootdata[2 * p + 1] = -1;
38193defd67SJames Wright       continue;
38293defd67SJames Wright     }
38393defd67SJames Wright     PetscCall(PetscBTSet(*rootbt, p));
38485de0153SJames Wright     PetscCheck(IsPointInsideStratum(p, pStart, pEnd), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " in roots exists outside of stratum [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
38593defd67SJames Wright     PetscInt p_size = point_sizes[p - pStart];
38693defd67SJames Wright     PetscCheck(donor_dof[p] == p_size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Reduced leaf data size (%" PetscInt_FMT ") does not match root data size (%" PetscInt_FMT ")", donor_dof[p], p_size);
38793defd67SJames Wright     rootdata[2 * p]     = root_offset;
38893defd67SJames Wright     rootdata[2 * p + 1] = p_size;
38993defd67SJames Wright     root_offset += p_size;
39093defd67SJames Wright   }
39193defd67SJames Wright   PetscCall(PetscSFBcastBegin(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
39293defd67SJames Wright   PetscCall(PetscSFBcastEnd(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
39393defd67SJames Wright   // Count how many leaves we need to communicate the closures
39493defd67SJames Wright   PetscInt leaf_offset = 0;
39593defd67SJames Wright   for (PetscInt i = 0; i < nleaves; i++) {
39693defd67SJames Wright     PetscInt point = filocal[i];
39793defd67SJames Wright     if (leafdata[2 * point + 1] < 0) continue;
39893defd67SJames Wright     leaf_offset += leafdata[2 * point + 1];
39993defd67SJames Wright   }
40093defd67SJames Wright 
40193defd67SJames Wright   PetscSFNode *closure_leaf;
40293defd67SJames Wright   PetscCall(PetscMalloc1(leaf_offset, &closure_leaf));
40393defd67SJames Wright   leaf_offset = 0;
40493defd67SJames Wright   for (PetscInt i = 0; i < nleaves; i++) {
40593defd67SJames Wright     PetscInt point   = filocal[i];
40693defd67SJames Wright     PetscInt cl_size = leafdata[2 * point + 1];
40793defd67SJames Wright     if (cl_size < 0) continue;
40893defd67SJames Wright     for (PetscInt j = 0; j < cl_size; j++) {
40993defd67SJames Wright       closure_leaf[leaf_offset].rank  = firemote[i].rank;
41093defd67SJames Wright       closure_leaf[leaf_offset].index = leafdata[2 * point] + j;
41193defd67SJames Wright       leaf_offset++;
41293defd67SJames Wright     }
41393defd67SJames Wright   }
41493defd67SJames Wright 
41593defd67SJames Wright   PetscCall(PetscSFCreate(comm, sf_closure));
41693defd67SJames Wright   PetscCall(PetscSFSetGraph(*sf_closure, root_offset, leaf_offset, NULL, PETSC_USE_POINTER, closure_leaf, PETSC_OWN_POINTER));
41793defd67SJames Wright   *rootbuffersize = root_offset;
41893defd67SJames Wright   *leafbuffersize = leaf_offset;
41993defd67SJames Wright   PetscCall(PetscFree2(rootdata, leafdata));
42093defd67SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
42193defd67SJames Wright }
42293defd67SJames Wright 
4235f06a3ddSJed Brown // Start with an SF for a positive depth (e.g., faces) and create a new SF for matched closure. The caller must ensure
4245f06a3ddSJed Brown // that both the donor (root) face and the periodic (leaf) face have consistent orientation, meaning that their closures
4255f06a3ddSJed Brown // are isomorphic. It may be useful/necessary for this restriction to be loosened.
4266725e60dSJed Brown //
4275f06a3ddSJed Brown // Output Arguments:
4285f06a3ddSJed Brown //
4295f06a3ddSJed Brown // + closure_sf - augmented point SF (see `DMGetPointSF()`) that includes the faces and all points in its closure. This
4305f06a3ddSJed Brown //   can be used to create a global section and section SF.
431b83f62b0SJames Wright // - is_points - array of index sets for just the points in the closure of `face_sf`. These may be used to apply an affine
4325f06a3ddSJed Brown //   transformation to periodic dofs; see DMPeriodicCoordinateSetUp_Internal().
4335f06a3ddSJed Brown //
434b83f62b0SJames Wright static PetscErrorCode DMPlexCreateIsoperiodicPointSF_Private(DM dm, PetscInt num_face_sfs, PetscSF *face_sfs, PetscSF *closure_sf, IS **is_points)
4356725e60dSJed Brown {
4366725e60dSJed Brown   MPI_Comm           comm;
4376725e60dSJed Brown   PetscMPIInt        rank;
4386725e60dSJed Brown   PetscSF            point_sf;
439b83f62b0SJames Wright   PetscInt           nroots, nleaves;
440b83f62b0SJames Wright   const PetscInt    *filocal;
441b83f62b0SJames Wright   const PetscSFNode *firemote;
4426725e60dSJed Brown 
4436725e60dSJed Brown   PetscFunctionBegin;
4446725e60dSJed Brown   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4456725e60dSJed Brown   PetscCallMPI(MPI_Comm_rank(comm, &rank));
4466725e60dSJed Brown   PetscCall(DMGetPointSF(dm, &point_sf)); // Point SF has remote points
447b83f62b0SJames Wright   PetscCall(PetscMalloc1(num_face_sfs, is_points));
448b83f62b0SJames Wright 
449b83f62b0SJames Wright   for (PetscInt f = 0; f < num_face_sfs; f++) {
450b83f62b0SJames Wright     PetscSF   face_sf = face_sfs[f];
45193defd67SJames Wright     PetscInt *cl_sizes;
45293defd67SJames Wright     PetscInt  fStart, fEnd, rootbuffersize, leafbuffersize;
4536725e60dSJed Brown     PetscSF   sf_closure;
45493defd67SJames Wright     PetscBT   rootbt;
45593defd67SJames Wright 
45693defd67SJames Wright     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
45793defd67SJames Wright     PetscCall(PetscMalloc1(fEnd - fStart, &cl_sizes));
45893defd67SJames Wright     for (PetscInt f = fStart, index = 0; f < fEnd; f++, index++) {
45993defd67SJames Wright       PetscInt cl_size, *closure = NULL;
46093defd67SJames Wright       PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &cl_size, &closure));
46193defd67SJames Wright       cl_sizes[index] = cl_size - 1;
46293defd67SJames Wright       PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &cl_size, &closure));
46393defd67SJames Wright     }
46493defd67SJames Wright 
46593defd67SJames Wright     PetscCall(CreateDonorToPeriodicSF(dm, face_sf, fStart, fEnd, cl_sizes, &rootbuffersize, &leafbuffersize, &rootbt, &sf_closure));
46693defd67SJames Wright     PetscCall(PetscFree(cl_sizes));
46793defd67SJames Wright     PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote));
4686725e60dSJed Brown 
469b83f62b0SJames Wright     PetscSFNode *leaf_donor_closure;
470b83f62b0SJames Wright     { // Pack root buffer with owner for every point in the root cones
4716725e60dSJed Brown       PetscSFNode       *donor_closure;
472b83f62b0SJames Wright       const PetscInt    *pilocal;
473b83f62b0SJames Wright       const PetscSFNode *piremote;
474b83f62b0SJames Wright       PetscInt           npoints;
475b83f62b0SJames Wright 
476b83f62b0SJames Wright       PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, &pilocal, &piremote));
47793defd67SJames Wright       PetscCall(PetscCalloc1(rootbuffersize, &donor_closure));
47893defd67SJames Wright       for (PetscInt p = 0, root_offset = 0; p < nroots; p++) {
47993defd67SJames Wright         if (!PetscBTLookup(rootbt, p)) continue;
48093defd67SJames Wright         PetscInt cl_size, *closure = NULL;
4816725e60dSJed Brown         PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
4826725e60dSJed Brown         for (PetscInt j = 1; j < cl_size; j++) {
4836725e60dSJed Brown           PetscInt c = closure[2 * j];
4846725e60dSJed Brown           if (pilocal) {
4856725e60dSJed Brown             PetscInt found = -1;
4866725e60dSJed Brown             if (npoints > 0) PetscCall(PetscFindInt(c, npoints, pilocal, &found));
4876725e60dSJed Brown             if (found >= 0) {
4886725e60dSJed Brown               donor_closure[root_offset++] = piremote[found];
4896725e60dSJed Brown               continue;
4906725e60dSJed Brown             }
4916725e60dSJed Brown           }
4926725e60dSJed Brown           // we own c
4936725e60dSJed Brown           donor_closure[root_offset].rank  = rank;
4946725e60dSJed Brown           donor_closure[root_offset].index = c;
4956725e60dSJed Brown           root_offset++;
4966725e60dSJed Brown         }
4976725e60dSJed Brown         PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
4986725e60dSJed Brown       }
4996725e60dSJed Brown 
50093defd67SJames Wright       PetscCall(PetscMalloc1(leafbuffersize, &leaf_donor_closure));
5016497c311SBarry Smith       PetscCall(PetscSFBcastBegin(sf_closure, MPIU_SF_NODE, donor_closure, leaf_donor_closure, MPI_REPLACE));
5026497c311SBarry Smith       PetscCall(PetscSFBcastEnd(sf_closure, MPIU_SF_NODE, donor_closure, leaf_donor_closure, MPI_REPLACE));
5036725e60dSJed Brown       PetscCall(PetscSFDestroy(&sf_closure));
5046725e60dSJed Brown       PetscCall(PetscFree(donor_closure));
505b83f62b0SJames Wright     }
5066725e60dSJed Brown 
5076725e60dSJed Brown     PetscSFNode *new_iremote;
5086725e60dSJed Brown     PetscCall(PetscCalloc1(nroots, &new_iremote));
5096725e60dSJed Brown     for (PetscInt i = 0; i < nroots; i++) new_iremote[i].rank = -1;
5106725e60dSJed Brown     // Walk leaves and match vertices
51193defd67SJames Wright     for (PetscInt i = 0, leaf_offset = 0; i < nleaves; i++) {
5126725e60dSJed Brown       PetscInt  point   = filocal[i], cl_size;
5136725e60dSJed Brown       PetscInt *closure = NULL;
5146725e60dSJed Brown       PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
5156725e60dSJed Brown       for (PetscInt j = 1; j < cl_size; j++) { // TODO: should we send donor edge orientations so we can flip for consistency?
5166725e60dSJed Brown         PetscInt    c  = closure[2 * j];
5176725e60dSJed Brown         PetscSFNode lc = leaf_donor_closure[leaf_offset];
5186725e60dSJed Brown         // printf("[%d] face %d.%d: %d ?-- (%d,%d)\n", rank, point, j, c, lc.rank, lc.index);
5196725e60dSJed Brown         if (new_iremote[c].rank == -1) {
5206725e60dSJed Brown           new_iremote[c] = lc;
5215f06a3ddSJed Brown         } else PetscCheck(new_iremote[c].rank == lc.rank && new_iremote[c].index == lc.index, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Mismatched cone ordering between faces");
5226725e60dSJed Brown         leaf_offset++;
5236725e60dSJed Brown       }
5246725e60dSJed Brown       PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
5256725e60dSJed Brown     }
5266725e60dSJed Brown     PetscCall(PetscFree(leaf_donor_closure));
5276725e60dSJed Brown 
5286725e60dSJed Brown     // Include face points in closure SF
5296725e60dSJed Brown     for (PetscInt i = 0; i < nleaves; i++) new_iremote[filocal[i]] = firemote[i];
5306725e60dSJed Brown     // consolidate leaves
53193defd67SJames Wright     PetscInt *leafdata;
53293defd67SJames Wright     PetscCall(PetscMalloc1(nroots, &leafdata));
5336725e60dSJed Brown     PetscInt num_new_leaves = 0;
5346725e60dSJed Brown     for (PetscInt i = 0; i < nroots; i++) {
5356725e60dSJed Brown       if (new_iremote[i].rank == -1) continue;
5366725e60dSJed Brown       new_iremote[num_new_leaves] = new_iremote[i];
5376725e60dSJed Brown       leafdata[num_new_leaves]    = i;
5386725e60dSJed Brown       num_new_leaves++;
5396725e60dSJed Brown     }
540b83f62b0SJames Wright     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, num_new_leaves, leafdata, PETSC_COPY_VALUES, &(*is_points)[f]));
5416725e60dSJed Brown 
5426725e60dSJed Brown     PetscSF csf;
5436725e60dSJed Brown     PetscCall(PetscSFCreate(comm, &csf));
5446725e60dSJed Brown     PetscCall(PetscSFSetGraph(csf, nroots, num_new_leaves, leafdata, PETSC_COPY_VALUES, new_iremote, PETSC_COPY_VALUES));
5456725e60dSJed Brown     PetscCall(PetscFree(new_iremote)); // copy and delete because new_iremote is longer than it needs to be
54693defd67SJames Wright     PetscCall(PetscFree(leafdata));
54793defd67SJames Wright     PetscCall(PetscBTDestroy(&rootbt));
5486725e60dSJed Brown 
549b83f62b0SJames Wright     PetscInt npoints;
550b83f62b0SJames Wright     PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, NULL, NULL));
5515f06a3ddSJed Brown     if (npoints < 0) { // empty point_sf
5526725e60dSJed Brown       *closure_sf = csf;
5535f06a3ddSJed Brown     } else {
5545f06a3ddSJed Brown       PetscCall(PetscSFMerge(point_sf, csf, closure_sf));
5555f06a3ddSJed Brown       PetscCall(PetscSFDestroy(&csf));
5565f06a3ddSJed Brown     }
557d8b4a066SPierre Jolivet     if (f > 0) PetscCall(PetscSFDestroy(&point_sf)); // Only destroy if point_sf is from previous calls to PetscSFMerge
558b83f62b0SJames Wright     point_sf = *closure_sf;                          // Use combined point + isoperiodic SF to define point ownership for further face_sf
559b83f62b0SJames Wright   }
5605f06a3ddSJed Brown   PetscCall(PetscObjectSetName((PetscObject)*closure_sf, "Composed Periodic Points"));
5613ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5626725e60dSJed Brown }
5636725e60dSJed Brown 
5645f06a3ddSJed Brown static PetscErrorCode DMGetIsoperiodicPointSF_Plex(DM dm, PetscSF *sf)
5656725e60dSJed Brown {
5666725e60dSJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
5676725e60dSJed Brown 
5686725e60dSJed Brown   PetscFunctionBegin;
569b83f62b0SJames Wright   if (!plex->periodic.composed_sf) PetscCall(DMPlexCreateIsoperiodicPointSF_Private(dm, plex->periodic.num_face_sfs, plex->periodic.face_sfs, &plex->periodic.composed_sf, &plex->periodic.periodic_points));
5706725e60dSJed Brown   if (sf) *sf = plex->periodic.composed_sf;
5713ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5726725e60dSJed Brown }
5736725e60dSJed Brown 
5745f06a3ddSJed Brown PetscErrorCode DMPlexMigrateIsoperiodicFaceSF_Internal(DM old_dm, DM dm, PetscSF sf_migration)
5755f06a3ddSJed Brown {
5765f06a3ddSJed Brown   DM_Plex    *plex = (DM_Plex *)old_dm->data;
577a45b0079SJames Wright   PetscSF     sf_point, *new_face_sfs;
5785f06a3ddSJed Brown   PetscMPIInt rank;
5795f06a3ddSJed Brown 
5805f06a3ddSJed Brown   PetscFunctionBegin;
5811fca310dSJames Wright   if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS);
5825f06a3ddSJed Brown   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
5835f06a3ddSJed Brown   PetscCall(DMGetPointSF(dm, &sf_point));
584a45b0079SJames Wright   PetscCall(PetscMalloc1(plex->periodic.num_face_sfs, &new_face_sfs));
585a45b0079SJames Wright 
586a45b0079SJames Wright   for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) {
5875f06a3ddSJed Brown     PetscInt           old_npoints, new_npoints, old_nleaf, new_nleaf, point_nleaf;
5885f06a3ddSJed Brown     PetscSFNode       *new_leafdata, *rootdata, *leafdata;
5895f06a3ddSJed Brown     const PetscInt    *old_local, *point_local;
5905f06a3ddSJed Brown     const PetscSFNode *old_remote, *point_remote;
5916497c311SBarry Smith 
592a45b0079SJames Wright     PetscCall(PetscSFGetGraph(plex->periodic.face_sfs[f], &old_npoints, &old_nleaf, &old_local, &old_remote));
5935f06a3ddSJed Brown     PetscCall(PetscSFGetGraph(sf_migration, NULL, &new_nleaf, NULL, NULL));
5945f06a3ddSJed Brown     PetscCall(PetscSFGetGraph(sf_point, &new_npoints, &point_nleaf, &point_local, &point_remote));
5955f06a3ddSJed Brown     PetscAssert(new_nleaf == new_npoints, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected migration leaf space to match new point root space");
5965f06a3ddSJed Brown     PetscCall(PetscMalloc3(old_npoints, &rootdata, old_npoints, &leafdata, new_npoints, &new_leafdata));
5975f06a3ddSJed Brown 
5985f06a3ddSJed Brown     // Fill new_leafdata with new owners of all points
5995f06a3ddSJed Brown     for (PetscInt i = 0; i < new_npoints; i++) {
6005f06a3ddSJed Brown       new_leafdata[i].rank  = rank;
6015f06a3ddSJed Brown       new_leafdata[i].index = i;
6025f06a3ddSJed Brown     }
6035f06a3ddSJed Brown     for (PetscInt i = 0; i < point_nleaf; i++) {
6045f06a3ddSJed Brown       PetscInt j      = point_local[i];
6055f06a3ddSJed Brown       new_leafdata[j] = point_remote[i];
6065f06a3ddSJed Brown     }
6075f06a3ddSJed Brown     // REPLACE is okay because every leaf agrees about the new owners
6086497c311SBarry Smith     PetscCall(PetscSFReduceBegin(sf_migration, MPIU_SF_NODE, new_leafdata, rootdata, MPI_REPLACE));
6096497c311SBarry Smith     PetscCall(PetscSFReduceEnd(sf_migration, MPIU_SF_NODE, new_leafdata, rootdata, MPI_REPLACE));
6105f06a3ddSJed Brown     // rootdata now contains the new owners
6115f06a3ddSJed Brown 
6125f06a3ddSJed Brown     // Send to leaves of old space
6135f06a3ddSJed Brown     for (PetscInt i = 0; i < old_npoints; i++) {
6145f06a3ddSJed Brown       leafdata[i].rank  = -1;
6155f06a3ddSJed Brown       leafdata[i].index = -1;
6165f06a3ddSJed Brown     }
6176497c311SBarry Smith     PetscCall(PetscSFBcastBegin(plex->periodic.face_sfs[f], MPIU_SF_NODE, rootdata, leafdata, MPI_REPLACE));
6186497c311SBarry Smith     PetscCall(PetscSFBcastEnd(plex->periodic.face_sfs[f], MPIU_SF_NODE, rootdata, leafdata, MPI_REPLACE));
6195f06a3ddSJed Brown 
6205f06a3ddSJed Brown     // Send to new leaf space
6216497c311SBarry Smith     PetscCall(PetscSFBcastBegin(sf_migration, MPIU_SF_NODE, leafdata, new_leafdata, MPI_REPLACE));
6226497c311SBarry Smith     PetscCall(PetscSFBcastEnd(sf_migration, MPIU_SF_NODE, leafdata, new_leafdata, MPI_REPLACE));
6235f06a3ddSJed Brown 
6245f06a3ddSJed Brown     PetscInt     nface = 0, *new_local;
6255f06a3ddSJed Brown     PetscSFNode *new_remote;
6265f06a3ddSJed Brown     for (PetscInt i = 0; i < new_npoints; i++) nface += (new_leafdata[i].rank >= 0);
6275f06a3ddSJed Brown     PetscCall(PetscMalloc1(nface, &new_local));
6285f06a3ddSJed Brown     PetscCall(PetscMalloc1(nface, &new_remote));
6295f06a3ddSJed Brown     nface = 0;
6305f06a3ddSJed Brown     for (PetscInt i = 0; i < new_npoints; i++) {
6315f06a3ddSJed Brown       if (new_leafdata[i].rank == -1) continue;
6325f06a3ddSJed Brown       new_local[nface]  = i;
6335f06a3ddSJed Brown       new_remote[nface] = new_leafdata[i];
6345f06a3ddSJed Brown       nface++;
6355f06a3ddSJed Brown     }
6365f06a3ddSJed Brown     PetscCall(PetscFree3(rootdata, leafdata, new_leafdata));
637a45b0079SJames Wright     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &new_face_sfs[f]));
638a45b0079SJames Wright     PetscCall(PetscSFSetGraph(new_face_sfs[f], new_npoints, nface, new_local, PETSC_OWN_POINTER, new_remote, PETSC_OWN_POINTER));
639a45b0079SJames Wright     {
640a45b0079SJames Wright       char new_face_sf_name[PETSC_MAX_PATH_LEN];
641a45b0079SJames Wright       PetscCall(PetscSNPrintf(new_face_sf_name, sizeof new_face_sf_name, "Migrated Isoperiodic Faces #%" PetscInt_FMT, f));
642a45b0079SJames Wright       PetscCall(PetscObjectSetName((PetscObject)new_face_sfs[f], new_face_sf_name));
643a45b0079SJames Wright     }
644a45b0079SJames Wright   }
645a45b0079SJames Wright 
646a45b0079SJames Wright   PetscCall(DMPlexSetIsoperiodicFaceSF(dm, plex->periodic.num_face_sfs, new_face_sfs));
647a45b0079SJames Wright   PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, plex->periodic.num_face_sfs, (PetscScalar *)plex->periodic.transform));
648a45b0079SJames Wright   for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) PetscCall(PetscSFDestroy(&new_face_sfs[f]));
649a45b0079SJames Wright   PetscCall(PetscFree(new_face_sfs));
6503ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6515f06a3ddSJed Brown }
6525f06a3ddSJed Brown 
6536725e60dSJed Brown PetscErrorCode DMPeriodicCoordinateSetUp_Internal(DM dm)
6546725e60dSJed Brown {
6556725e60dSJed Brown   DM_Plex   *plex = (DM_Plex *)dm->data;
6566497c311SBarry Smith   PetscCount count;
657ddedc8f6SJames Wright   IS         isdof;
658ddedc8f6SJames Wright   PetscInt   dim;
6594d86920dSPierre Jolivet 
6606725e60dSJed Brown   PetscFunctionBegin;
6611fca310dSJames Wright   if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS);
6625f06a3ddSJed Brown   PetscCall(DMGetIsoperiodicPointSF_Plex(dm, NULL));
6635f06a3ddSJed Brown   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex));
6646725e60dSJed Brown 
6656725e60dSJed Brown   PetscCall(DMGetDimension(dm, &dim));
666ddedc8f6SJames Wright   dm->periodic.num_affines = plex->periodic.num_face_sfs;
667ddedc8f6SJames Wright   PetscCall(PetscMalloc2(dm->periodic.num_affines, &dm->periodic.affine_to_local, dm->periodic.num_affines, &dm->periodic.affine));
668ddedc8f6SJames Wright 
669ddedc8f6SJames Wright   for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) {
6706497c311SBarry Smith     PetscInt        npoints, vsize, isize;
6716725e60dSJed Brown     const PetscInt *points;
672ddedc8f6SJames Wright     IS              is = plex->periodic.periodic_points[f];
6736725e60dSJed Brown     PetscSegBuffer  seg;
6746725e60dSJed Brown     PetscSection    section;
6756497c311SBarry Smith     PetscInt       *ind;
6766497c311SBarry Smith     Vec             L, P;
6776497c311SBarry Smith     VecType         vec_type;
6786497c311SBarry Smith     VecScatter      scatter;
6796497c311SBarry Smith     PetscScalar    *x;
6806497c311SBarry Smith 
6816725e60dSJed Brown     PetscCall(DMGetLocalSection(dm, &section));
6826725e60dSJed Brown     PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 32, &seg));
6836725e60dSJed Brown     PetscCall(ISGetSize(is, &npoints));
6846725e60dSJed Brown     PetscCall(ISGetIndices(is, &points));
6856725e60dSJed Brown     for (PetscInt i = 0; i < npoints; i++) {
6866725e60dSJed Brown       PetscInt point = points[i], off, dof;
6876497c311SBarry Smith 
6886725e60dSJed Brown       PetscCall(PetscSectionGetOffset(section, point, &off));
6896725e60dSJed Brown       PetscCall(PetscSectionGetDof(section, point, &dof));
6906725e60dSJed Brown       PetscAssert(dof % dim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected dof %" PetscInt_FMT " not divisible by dimension %" PetscInt_FMT, dof, dim);
6916725e60dSJed Brown       for (PetscInt j = 0; j < dof / dim; j++) {
6926725e60dSJed Brown         PetscInt *slot;
6936497c311SBarry Smith 
6946725e60dSJed Brown         PetscCall(PetscSegBufferGetInts(seg, 1, &slot));
695729bdd54SJed Brown         *slot = off / dim + j;
6966725e60dSJed Brown       }
6976725e60dSJed Brown     }
6986725e60dSJed Brown     PetscCall(PetscSegBufferGetSize(seg, &count));
6996725e60dSJed Brown     PetscCall(PetscSegBufferExtractAlloc(seg, &ind));
7006725e60dSJed Brown     PetscCall(PetscSegBufferDestroy(&seg));
7016497c311SBarry Smith     PetscCall(PetscIntCast(count, &isize));
7026497c311SBarry Smith     PetscCall(ISCreateBlock(PETSC_COMM_SELF, dim, isize, ind, PETSC_OWN_POINTER, &isdof));
7036497c311SBarry Smith 
7046497c311SBarry Smith     PetscCall(PetscIntCast(count * dim, &vsize));
7056725e60dSJed Brown     PetscCall(DMGetLocalVector(dm, &L));
7066725e60dSJed Brown     PetscCall(VecCreate(PETSC_COMM_SELF, &P));
7076497c311SBarry Smith     PetscCall(VecSetSizes(P, vsize, vsize));
7086725e60dSJed Brown     PetscCall(VecGetType(L, &vec_type));
7096725e60dSJed Brown     PetscCall(VecSetType(P, vec_type));
7106725e60dSJed Brown     PetscCall(VecScatterCreate(P, NULL, L, isdof, &scatter));
7116725e60dSJed Brown     PetscCall(DMRestoreLocalVector(dm, &L));
7126725e60dSJed Brown     PetscCall(ISDestroy(&isdof));
7136725e60dSJed Brown 
7146725e60dSJed Brown     PetscCall(VecGetArrayWrite(P, &x));
7156497c311SBarry Smith     for (PetscCount i = 0; i < count; i++) {
716ddedc8f6SJames Wright       for (PetscInt j = 0; j < dim; j++) x[i * dim + j] = plex->periodic.transform[f][j][3];
7176725e60dSJed Brown     }
7186725e60dSJed Brown     PetscCall(VecRestoreArrayWrite(P, &x));
7196725e60dSJed Brown 
720ddedc8f6SJames Wright     dm->periodic.affine_to_local[f] = scatter;
721ddedc8f6SJames Wright     dm->periodic.affine[f]          = P;
722ddedc8f6SJames Wright   }
7236725e60dSJed Brown   PetscCall(DMGlobalToLocalHookAdd(dm, NULL, DMCoordAddPeriodicOffsets_Private, NULL));
7243ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7256725e60dSJed Brown }
7266725e60dSJed Brown 
7275f06a3ddSJed Brown // We'll just orient all the edges, though only periodic boundary edges need orientation
7285f06a3ddSJed Brown static PetscErrorCode DMPlexOrientPositiveEdges_Private(DM dm)
7295f06a3ddSJed Brown {
7305f06a3ddSJed Brown   PetscInt dim, eStart, eEnd;
7314d86920dSPierre Jolivet 
7325f06a3ddSJed Brown   PetscFunctionBegin;
7335f06a3ddSJed Brown   PetscCall(DMGetDimension(dm, &dim));
7343ba16761SJacob Faibussowitsch   if (dim < 3) PetscFunctionReturn(PETSC_SUCCESS); // not necessary
7355f06a3ddSJed Brown   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
7365f06a3ddSJed Brown   for (PetscInt e = eStart; e < eEnd; e++) {
7375f06a3ddSJed Brown     const PetscInt *cone;
7385f06a3ddSJed Brown     PetscCall(DMPlexGetCone(dm, e, &cone));
7395f06a3ddSJed Brown     if (cone[0] > cone[1]) PetscCall(DMPlexOrientPoint(dm, e, -1));
7405f06a3ddSJed Brown   }
7413ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7425f06a3ddSJed Brown }
7435f06a3ddSJed Brown 
7443e72e933SJed Brown PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate)
7453e72e933SJed Brown {
7463e72e933SJed Brown   PetscInt  eextent[3] = {1, 1, 1}, vextent[3] = {1, 1, 1};
7473e72e933SJed Brown   const Ijk closure_1[] = {
7483e72e933SJed Brown     {0, 0, 0},
7493e72e933SJed Brown     {1, 0, 0},
7503e72e933SJed Brown   };
7513e72e933SJed Brown   const Ijk closure_2[] = {
7523e72e933SJed Brown     {0, 0, 0},
7533e72e933SJed Brown     {1, 0, 0},
7543e72e933SJed Brown     {1, 1, 0},
7553e72e933SJed Brown     {0, 1, 0},
7563e72e933SJed Brown   };
7573e72e933SJed Brown   const Ijk closure_3[] = {
7583e72e933SJed Brown     {0, 0, 0},
7593e72e933SJed Brown     {0, 1, 0},
7603e72e933SJed Brown     {1, 1, 0},
7613e72e933SJed Brown     {1, 0, 0},
7623e72e933SJed Brown     {0, 0, 1},
7633e72e933SJed Brown     {1, 0, 1},
7643e72e933SJed Brown     {1, 1, 1},
7653e72e933SJed Brown     {0, 1, 1},
7663e72e933SJed Brown   };
7673431e603SJed Brown   const Ijk *const closure_dim[] = {NULL, closure_1, closure_2, closure_3};
7683431e603SJed Brown   // This must be kept consistent with DMPlexCreateCubeMesh_Internal
7693431e603SJed Brown   const PetscInt        face_marker_1[]   = {1, 2};
7703431e603SJed Brown   const PetscInt        face_marker_2[]   = {4, 2, 1, 3};
7713431e603SJed Brown   const PetscInt        face_marker_3[]   = {6, 5, 3, 4, 1, 2};
7723431e603SJed Brown   const PetscInt *const face_marker_dim[] = {NULL, face_marker_1, face_marker_2, face_marker_3};
7736725e60dSJed Brown   // Orient faces so the normal is in the positive axis and the first vertex is the one closest to zero.
7746725e60dSJed Brown   // These orientations can be determined by examining cones of a reference quad and hex element.
7756725e60dSJed Brown   const PetscInt        face_orient_1[]   = {0, 0};
7766725e60dSJed Brown   const PetscInt        face_orient_2[]   = {-1, 0, 0, -1};
7776725e60dSJed Brown   const PetscInt        face_orient_3[]   = {-2, 0, -2, 1, -2, 0};
7786725e60dSJed Brown   const PetscInt *const face_orient_dim[] = {NULL, face_orient_1, face_orient_2, face_orient_3};
7793e72e933SJed Brown 
7803e72e933SJed Brown   PetscFunctionBegin;
781ea7b3863SJames Wright   PetscCall(PetscLogEventBegin(DMPLEX_CreateBoxSFC, dm, 0, 0, 0));
7824f572ea9SToby Isaac   PetscAssertPointer(dm, 1);
7833e72e933SJed Brown   PetscValidLogicalCollectiveInt(dm, dim, 2);
7843e72e933SJed Brown   PetscCall(DMSetDimension(dm, dim));
7853e72e933SJed Brown   PetscMPIInt rank, size;
7863e72e933SJed Brown   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
7873e72e933SJed Brown   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
7883e72e933SJed Brown   for (PetscInt i = 0; i < dim; i++) {
7893e72e933SJed Brown     eextent[i] = faces[i];
7903e72e933SJed Brown     vextent[i] = faces[i] + 1;
7913e72e933SJed Brown   }
792c77877e3SJed Brown   ZLayout layout;
793c77877e3SJed Brown   PetscCall(ZLayoutCreate(size, eextent, vextent, &layout));
7943e72e933SJed Brown   PetscZSet vset; // set of all vertices in the closure of the owned elements
7953e72e933SJed Brown   PetscCall(PetscZSetCreate(&vset));
7963e72e933SJed Brown   PetscInt local_elems = 0;
7973e72e933SJed Brown   for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) {
7983e72e933SJed Brown     Ijk loc = ZCodeSplit(z);
7993ba16761SJacob Faibussowitsch     if (IjkActive(layout.vextent, loc)) PetscCall(PetscZSetAdd(vset, z));
8006547ddbdSJed Brown     else {
8016547ddbdSJed Brown       z += ZStepOct(z);
8026547ddbdSJed Brown       continue;
8036547ddbdSJed Brown     }
8043e72e933SJed Brown     if (IjkActive(layout.eextent, loc)) {
8053e72e933SJed Brown       local_elems++;
8063e72e933SJed Brown       // Add all neighboring vertices to set
8073e72e933SJed Brown       for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) {
8083e72e933SJed Brown         Ijk   inc  = closure_dim[dim][n];
8093e72e933SJed Brown         Ijk   nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k};
8103e72e933SJed Brown         ZCode v    = ZEncode(nloc);
8113ba16761SJacob Faibussowitsch         PetscCall(PetscZSetAdd(vset, v));
8123e72e933SJed Brown       }
8133e72e933SJed Brown     }
8143e72e933SJed Brown   }
8153e72e933SJed Brown   PetscInt local_verts, off = 0;
8163e72e933SJed Brown   ZCode   *vert_z;
8173e72e933SJed Brown   PetscCall(PetscZSetGetSize(vset, &local_verts));
8183e72e933SJed Brown   PetscCall(PetscMalloc1(local_verts, &vert_z));
8193e72e933SJed Brown   PetscCall(PetscZSetGetElems(vset, &off, vert_z));
8203e72e933SJed Brown   PetscCall(PetscZSetDestroy(&vset));
8213e72e933SJed Brown   // ZCode is unsigned for bitwise convenience, but highest bit should never be set, so can interpret as signed
8223e72e933SJed Brown   PetscCall(PetscSortInt64(local_verts, (PetscInt64 *)vert_z));
8233e72e933SJed Brown 
8243e72e933SJed Brown   PetscCall(DMPlexSetChart(dm, 0, local_elems + local_verts));
8253e72e933SJed Brown   for (PetscInt e = 0; e < local_elems; e++) PetscCall(DMPlexSetConeSize(dm, e, PetscPowInt(2, dim)));
8263e72e933SJed Brown   PetscCall(DMSetUp(dm));
8273e72e933SJed Brown   {
8283e72e933SJed Brown     PetscInt e = 0;
8293e72e933SJed Brown     for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) {
8303e72e933SJed Brown       Ijk loc = ZCodeSplit(z);
8316547ddbdSJed Brown       if (!IjkActive(layout.eextent, loc)) {
8326547ddbdSJed Brown         z += ZStepOct(z);
8336547ddbdSJed Brown         continue;
8346547ddbdSJed Brown       }
8353e72e933SJed Brown       PetscInt cone[8], orient[8] = {0};
8363e72e933SJed Brown       for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) {
8373e72e933SJed Brown         Ijk      inc  = closure_dim[dim][n];
8383e72e933SJed Brown         Ijk      nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k};
8393e72e933SJed Brown         ZCode    v    = ZEncode(nloc);
8403e72e933SJed Brown         PetscInt ci   = ZCodeFind(v, local_verts, vert_z);
8413e72e933SJed Brown         PetscAssert(ci >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find neighbor vertex in set");
8423e72e933SJed Brown         cone[n] = local_elems + ci;
8433e72e933SJed Brown       }
8443e72e933SJed Brown       PetscCall(DMPlexSetCone(dm, e, cone));
8453e72e933SJed Brown       PetscCall(DMPlexSetConeOrientation(dm, e, orient));
8463e72e933SJed Brown       e++;
8473e72e933SJed Brown     }
8483e72e933SJed Brown   }
8493e72e933SJed Brown 
8503e72e933SJed Brown   PetscCall(DMPlexSymmetrize(dm));
8513e72e933SJed Brown   PetscCall(DMPlexStratify(dm));
8526725e60dSJed Brown 
8533e72e933SJed Brown   { // Create point SF
8543e72e933SJed Brown     PetscSF sf;
8553ba16761SJacob Faibussowitsch     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf));
8563e72e933SJed Brown     PetscInt owned_verts = ZCodeFind(layout.zstarts[rank + 1], local_verts, vert_z);
8573e72e933SJed Brown     if (owned_verts < 0) owned_verts = -(owned_verts + 1); // We don't care whether the key was found
8583e72e933SJed Brown     PetscInt     num_ghosts = local_verts - owned_verts;   // Due to sorting, owned vertices always come first
8593e72e933SJed Brown     PetscInt    *local_ghosts;
8603e72e933SJed Brown     PetscSFNode *ghosts;
8613e72e933SJed Brown     PetscCall(PetscMalloc1(num_ghosts, &local_ghosts));
8623e72e933SJed Brown     PetscCall(PetscMalloc1(num_ghosts, &ghosts));
8633e72e933SJed Brown     for (PetscInt i = 0; i < num_ghosts;) {
8643e72e933SJed Brown       ZCode       z = vert_z[owned_verts + i];
865835f2295SStefano Zampini       PetscMPIInt remote_rank, remote_count = 0;
866835f2295SStefano Zampini 
867835f2295SStefano Zampini       PetscCall(PetscMPIIntCast(ZCodeFind(z, size + 1, layout.zstarts), &remote_rank));
8683e72e933SJed Brown       if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1;
8693e72e933SJed Brown       // We have a new remote rank; find all the ghost indices (which are contiguous in vert_z)
8703e72e933SJed Brown 
8713e72e933SJed Brown       // Count the elements on remote_rank
8724e2e9504SJed Brown       PetscInt remote_elem = ZLayoutElementsOnRank(&layout, remote_rank);
8733e72e933SJed Brown 
8743e72e933SJed Brown       // Traverse vertices and make ghost links
8753e72e933SJed Brown       for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) {
8763e72e933SJed Brown         Ijk loc = ZCodeSplit(rz);
8773e72e933SJed Brown         if (rz == z) {
8783e72e933SJed Brown           local_ghosts[i] = local_elems + owned_verts + i;
8793e72e933SJed Brown           ghosts[i].rank  = remote_rank;
8803e72e933SJed Brown           ghosts[i].index = remote_elem + remote_count;
8813e72e933SJed Brown           i++;
8823e72e933SJed Brown           if (i == num_ghosts) break;
8833e72e933SJed Brown           z = vert_z[owned_verts + i];
8843e72e933SJed Brown         }
8853e72e933SJed Brown         if (IjkActive(layout.vextent, loc)) remote_count++;
8866547ddbdSJed Brown         else rz += ZStepOct(rz);
8873e72e933SJed Brown       }
8883e72e933SJed Brown     }
8893e72e933SJed Brown     PetscCall(PetscSFSetGraph(sf, local_elems + local_verts, num_ghosts, local_ghosts, PETSC_OWN_POINTER, ghosts, PETSC_OWN_POINTER));
8903e72e933SJed Brown     PetscCall(PetscObjectSetName((PetscObject)sf, "SFC Point SF"));
8913e72e933SJed Brown     PetscCall(DMSetPointSF(dm, sf));
8923e72e933SJed Brown     PetscCall(PetscSFDestroy(&sf));
8933e72e933SJed Brown   }
8943e72e933SJed Brown   {
8953e72e933SJed Brown     Vec          coordinates;
8963e72e933SJed Brown     PetscScalar *coords;
8973e72e933SJed Brown     PetscSection coord_section;
8983e72e933SJed Brown     PetscInt     coord_size;
8993e72e933SJed Brown     PetscCall(DMGetCoordinateSection(dm, &coord_section));
9003e72e933SJed Brown     PetscCall(PetscSectionSetNumFields(coord_section, 1));
9013e72e933SJed Brown     PetscCall(PetscSectionSetFieldComponents(coord_section, 0, dim));
9023e72e933SJed Brown     PetscCall(PetscSectionSetChart(coord_section, local_elems, local_elems + local_verts));
9033e72e933SJed Brown     for (PetscInt v = 0; v < local_verts; v++) {
9043e72e933SJed Brown       PetscInt point = local_elems + v;
9053e72e933SJed Brown       PetscCall(PetscSectionSetDof(coord_section, point, dim));
9063e72e933SJed Brown       PetscCall(PetscSectionSetFieldDof(coord_section, point, 0, dim));
9073e72e933SJed Brown     }
9083e72e933SJed Brown     PetscCall(PetscSectionSetUp(coord_section));
9093e72e933SJed Brown     PetscCall(PetscSectionGetStorageSize(coord_section, &coord_size));
9103e72e933SJed Brown     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
9113e72e933SJed Brown     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
9123e72e933SJed Brown     PetscCall(VecSetSizes(coordinates, coord_size, PETSC_DETERMINE));
9133e72e933SJed Brown     PetscCall(VecSetBlockSize(coordinates, dim));
9143e72e933SJed Brown     PetscCall(VecSetType(coordinates, VECSTANDARD));
9153e72e933SJed Brown     PetscCall(VecGetArray(coordinates, &coords));
9163e72e933SJed Brown     for (PetscInt v = 0; v < local_verts; v++) {
9173e72e933SJed Brown       Ijk loc             = ZCodeSplit(vert_z[v]);
9183e72e933SJed Brown       coords[v * dim + 0] = lower[0] + loc.i * (upper[0] - lower[0]) / layout.eextent.i;
9193e72e933SJed Brown       if (dim > 1) coords[v * dim + 1] = lower[1] + loc.j * (upper[1] - lower[1]) / layout.eextent.j;
9203e72e933SJed Brown       if (dim > 2) coords[v * dim + 2] = lower[2] + loc.k * (upper[2] - lower[2]) / layout.eextent.k;
9213e72e933SJed Brown     }
9223e72e933SJed Brown     PetscCall(VecRestoreArray(coordinates, &coords));
9233e72e933SJed Brown     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
9243e72e933SJed Brown     PetscCall(VecDestroy(&coordinates));
9253e72e933SJed Brown   }
9263e72e933SJed Brown   if (interpolate) {
9273431e603SJed Brown     PetscCall(DMPlexInterpolateInPlace_Internal(dm));
9285f06a3ddSJed Brown     // It's currently necessary to orient the donor and periodic edges consistently. An easy way to ensure that is ot
9295f06a3ddSJed Brown     // give all edges positive orientation. Since vertices are created in Z-order, all ranks will agree about the
9305f06a3ddSJed Brown     // ordering cone[0] < cone[1]. This is overkill and it would be nice to remove this preparation and make
9315f06a3ddSJed Brown     // DMPlexCreateIsoperiodicClosureSF_Private() more resilient, so it fixes any inconsistent orientations. That might
9325f06a3ddSJed Brown     // be needed in a general CGNS reader, for example.
9335f06a3ddSJed Brown     PetscCall(DMPlexOrientPositiveEdges_Private(dm));
9343431e603SJed Brown 
9353431e603SJed Brown     DMLabel label;
9363431e603SJed Brown     PetscCall(DMCreateLabel(dm, "Face Sets"));
9373431e603SJed Brown     PetscCall(DMGetLabel(dm, "Face Sets", &label));
9389ae3492bSJames Wright     PetscSegBuffer per_faces[3], donor_face_closure[3], my_donor_faces[3];
9399ae3492bSJames Wright     for (PetscInt i = 0; i < 3; i++) {
9409ae3492bSJames Wright       PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &per_faces[i]));
9419ae3492bSJames Wright       PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &my_donor_faces[i]));
9429ae3492bSJames Wright       PetscCall(PetscSegBufferCreate(sizeof(ZCode), 64 * PetscPowInt(2, dim), &donor_face_closure[i]));
9439ae3492bSJames Wright     }
9443431e603SJed Brown     PetscInt fStart, fEnd, vStart, vEnd;
9453431e603SJed Brown     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
9463431e603SJed Brown     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9473431e603SJed Brown     for (PetscInt f = fStart; f < fEnd; f++) {
9484e2e9504SJed Brown       PetscInt npoints, *points = NULL, num_fverts = 0, fverts[8];
9493431e603SJed Brown       PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points));
9503431e603SJed Brown       PetscInt bc_count[6] = {0};
9513431e603SJed Brown       for (PetscInt i = 0; i < npoints; i++) {
9523431e603SJed Brown         PetscInt p = points[2 * i];
95385de0153SJames Wright         if (!IsPointInsideStratum(p, vStart, vEnd)) continue;
9544e2e9504SJed Brown         fverts[num_fverts++] = p;
9553431e603SJed Brown         Ijk loc              = ZCodeSplit(vert_z[p - vStart]);
9563431e603SJed Brown         // Convention here matches DMPlexCreateCubeMesh_Internal
9573431e603SJed Brown         bc_count[0] += loc.i == 0;
9583431e603SJed Brown         bc_count[1] += loc.i == layout.vextent.i - 1;
9593431e603SJed Brown         bc_count[2] += loc.j == 0;
9603431e603SJed Brown         bc_count[3] += loc.j == layout.vextent.j - 1;
9613431e603SJed Brown         bc_count[4] += loc.k == 0;
9623431e603SJed Brown         bc_count[5] += loc.k == layout.vextent.k - 1;
9633e72e933SJed Brown       }
9643431e603SJed Brown       PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points));
9653431e603SJed Brown       for (PetscInt bc = 0, bc_match = 0; bc < 2 * dim; bc++) {
9663431e603SJed Brown         if (bc_count[bc] == PetscPowInt(2, dim - 1)) {
9676725e60dSJed Brown           PetscCall(DMPlexOrientPoint(dm, f, face_orient_dim[dim][bc]));
9684e2e9504SJed Brown           if (periodicity[bc / 2] == DM_BOUNDARY_PERIODIC) {
9694e2e9504SJed Brown             PetscInt *put;
9704e2e9504SJed Brown             if (bc % 2 == 0) { // donor face; no label
9719ae3492bSJames Wright               PetscCall(PetscSegBufferGet(my_donor_faces[bc / 2], 1, &put));
9724e2e9504SJed Brown               *put = f;
9734e2e9504SJed Brown             } else { // periodic face
9749ae3492bSJames Wright               PetscCall(PetscSegBufferGet(per_faces[bc / 2], 1, &put));
9754e2e9504SJed Brown               *put = f;
9764e2e9504SJed Brown               ZCode *zput;
9779ae3492bSJames Wright               PetscCall(PetscSegBufferGet(donor_face_closure[bc / 2], num_fverts, &zput));
9784e2e9504SJed Brown               for (PetscInt i = 0; i < num_fverts; i++) {
9794e2e9504SJed Brown                 Ijk loc = ZCodeSplit(vert_z[fverts[i] - vStart]);
9804e2e9504SJed Brown                 switch (bc / 2) {
9814e2e9504SJed Brown                 case 0:
9824e2e9504SJed Brown                   loc.i = 0;
9834e2e9504SJed Brown                   break;
9844e2e9504SJed Brown                 case 1:
9854e2e9504SJed Brown                   loc.j = 0;
9864e2e9504SJed Brown                   break;
9874e2e9504SJed Brown                 case 2:
9884e2e9504SJed Brown                   loc.k = 0;
9894e2e9504SJed Brown                   break;
9904e2e9504SJed Brown                 }
9914e2e9504SJed Brown                 *zput++ = ZEncode(loc);
9924e2e9504SJed Brown               }
9934e2e9504SJed Brown             }
9944e2e9504SJed Brown             continue;
9954e2e9504SJed Brown           }
9963431e603SJed Brown           PetscAssert(bc_match == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face matches multiple face sets");
9973431e603SJed Brown           PetscCall(DMLabelSetValue(label, f, face_marker_dim[dim][bc]));
9983431e603SJed Brown           bc_match++;
9993431e603SJed Brown         }
10003431e603SJed Brown       }
10013431e603SJed Brown     }
10023431e603SJed Brown     // Ensure that the Coordinate DM has our new boundary labels
10033431e603SJed Brown     DM cdm;
10043431e603SJed Brown     PetscCall(DMGetCoordinateDM(dm, &cdm));
10053431e603SJed Brown     PetscCall(DMCopyLabels(dm, cdm, PETSC_COPY_VALUES, PETSC_FALSE, DM_COPY_LABELS_FAIL));
10066725e60dSJed Brown     if (periodicity[0] == DM_BOUNDARY_PERIODIC || (dim > 1 && periodicity[1] == DM_BOUNDARY_PERIODIC) || (dim > 2 && periodicity[2] == DM_BOUNDARY_PERIODIC)) {
10075f06a3ddSJed Brown       PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(dm, &layout, vert_z, per_faces, lower, upper, periodicity, donor_face_closure, my_donor_faces));
10086725e60dSJed Brown     }
10099ae3492bSJames Wright     for (PetscInt i = 0; i < 3; i++) {
10109ae3492bSJames Wright       PetscCall(PetscSegBufferDestroy(&per_faces[i]));
10119ae3492bSJames Wright       PetscCall(PetscSegBufferDestroy(&donor_face_closure[i]));
10129ae3492bSJames Wright       PetscCall(PetscSegBufferDestroy(&my_donor_faces[i]));
10139ae3492bSJames Wright     }
10143431e603SJed Brown   }
10154e2e9504SJed Brown   PetscCall(PetscFree(layout.zstarts));
10163431e603SJed Brown   PetscCall(PetscFree(vert_z));
1017ea7b3863SJames Wright   PetscCall(PetscLogEventEnd(DMPLEX_CreateBoxSFC, dm, 0, 0, 0));
10183ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
10193e72e933SJed Brown }
10204e2e9504SJed Brown 
10214e2e9504SJed Brown /*@
10225f06a3ddSJed Brown   DMPlexSetIsoperiodicFaceSF - Express periodicity from an existing mesh
10234e2e9504SJed Brown 
102420f4b53cSBarry Smith   Logically Collective
10254e2e9504SJed Brown 
10264e2e9504SJed Brown   Input Parameters:
10274e2e9504SJed Brown + dm           - The `DMPLEX` on which to set periodicity
10281fca310dSJames Wright . num_face_sfs - Number of `PetscSF`s in `face_sfs`
10291fca310dSJames Wright - face_sfs     - Array of `PetscSF` in which roots are (owned) donor faces and leaves are faces that must be matched to a (possibly remote) donor face.
10304e2e9504SJed Brown 
10314e2e9504SJed Brown   Level: advanced
10324e2e9504SJed Brown 
103353c0d4aeSBarry Smith   Note:
10345dca41c3SJed Brown   One can use `-dm_plex_shape zbox` to use this mode of periodicity, wherein the periodic points are distinct both globally
10354e2e9504SJed Brown   and locally, but are paired when creating a global dof space.
10364e2e9504SJed Brown 
10371cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexGetIsoperiodicFaceSF()`
10384e2e9504SJed Brown @*/
10391fca310dSJames Wright PetscErrorCode DMPlexSetIsoperiodicFaceSF(DM dm, PetscInt num_face_sfs, PetscSF *face_sfs)
10404e2e9504SJed Brown {
10414e2e9504SJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
10424d86920dSPierre Jolivet 
10434e2e9504SJed Brown   PetscFunctionBegin;
10444e2e9504SJed Brown   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1045d7d2d1d2SJames Wright   if (num_face_sfs) PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex));
1046*2b4f33d9SJames Wright   else PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
1047*2b4f33d9SJames Wright   if (num_face_sfs == plex->periodic.num_face_sfs && (num_face_sfs == 0 || face_sfs == plex->periodic.face_sfs)) PetscFunctionReturn(PETSC_SUCCESS);
1048*2b4f33d9SJames Wright   PetscCall(DMSetGlobalSection(dm, NULL));
10491fca310dSJames Wright 
10501fca310dSJames Wright   for (PetscInt i = 0; i < num_face_sfs; i++) PetscCall(PetscObjectReference((PetscObject)face_sfs[i]));
10511fca310dSJames Wright 
10521fca310dSJames Wright   if (plex->periodic.num_face_sfs > 0) {
10531fca310dSJames Wright     for (PetscInt i = 0; i < plex->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&plex->periodic.face_sfs[i]));
10541fca310dSJames Wright     PetscCall(PetscFree(plex->periodic.face_sfs));
10551fca310dSJames Wright   }
10561fca310dSJames Wright 
10571fca310dSJames Wright   plex->periodic.num_face_sfs = num_face_sfs;
10581fca310dSJames Wright   PetscCall(PetscCalloc1(num_face_sfs, &plex->periodic.face_sfs));
10591fca310dSJames Wright   for (PetscInt i = 0; i < num_face_sfs; i++) plex->periodic.face_sfs[i] = face_sfs[i];
10605f06a3ddSJed Brown 
10615f06a3ddSJed Brown   DM cdm = dm->coordinates[0].dm; // Can't DMGetCoordinateDM because it automatically creates one
10625f06a3ddSJed Brown   if (cdm) {
10631fca310dSJames Wright     PetscCall(DMPlexSetIsoperiodicFaceSF(cdm, num_face_sfs, face_sfs));
10641fca310dSJames Wright     if (face_sfs) cdm->periodic.setup = DMPeriodicCoordinateSetUp_Internal;
10655f06a3ddSJed Brown   }
10663ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
10674e2e9504SJed Brown }
10684e2e9504SJed Brown 
10691fca310dSJames Wright /*@C
10705f06a3ddSJed Brown   DMPlexGetIsoperiodicFaceSF - Obtain periodicity for a mesh
10714e2e9504SJed Brown 
107220f4b53cSBarry Smith   Logically Collective
10734e2e9504SJed Brown 
107420f4b53cSBarry Smith   Input Parameter:
10754e2e9504SJed Brown . dm - The `DMPLEX` for which to obtain periodic relation
10764e2e9504SJed Brown 
10779cde84edSJose E. Roman   Output Parameters:
10781fca310dSJames Wright + num_face_sfs - Number of `PetscSF`s in the array
10791fca310dSJames Wright - face_sfs     - Array of `PetscSF` in which roots are (owned) donor faces and leaves are faces that must be matched to a (possibly remote) donor face.
10804e2e9504SJed Brown 
10814e2e9504SJed Brown   Level: advanced
10824e2e9504SJed Brown 
10831cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()`
10844e2e9504SJed Brown @*/
10851fca310dSJames Wright PetscErrorCode DMPlexGetIsoperiodicFaceSF(DM dm, PetscInt *num_face_sfs, const PetscSF **face_sfs)
10864e2e9504SJed Brown {
10874e2e9504SJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
10884d86920dSPierre Jolivet 
10894e2e9504SJed Brown   PetscFunctionBegin;
10904e2e9504SJed Brown   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10911fca310dSJames Wright   *face_sfs     = plex->periodic.face_sfs;
10921fca310dSJames Wright   *num_face_sfs = plex->periodic.num_face_sfs;
10933ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
10944e2e9504SJed Brown }
10956725e60dSJed Brown 
10965f06a3ddSJed Brown /*@C
10975f06a3ddSJed Brown   DMPlexSetIsoperiodicFaceTransform - set geometric transform from donor to periodic points
10986725e60dSJed Brown 
10996725e60dSJed Brown   Logically Collective
11006725e60dSJed Brown 
110160225df5SJacob Faibussowitsch   Input Parameters:
110253c0d4aeSBarry Smith + dm - `DMPLEX` that has been configured with `DMPlexSetIsoperiodicFaceSF()`
11031fca310dSJames Wright . n  - Number of transforms in array
11041fca310dSJames Wright - t  - Array of 4x4 affine transformation basis.
11056725e60dSJed Brown 
110653c0d4aeSBarry Smith   Level: advanced
110753c0d4aeSBarry Smith 
11085f06a3ddSJed Brown   Notes:
11095f06a3ddSJed Brown   Affine transforms are 4x4 matrices in which the leading 3x3 block expresses a rotation (or identity for no rotation),
11105f06a3ddSJed Brown   the last column contains a translation vector, and the bottom row is all zero except the last entry, which must always
11115f06a3ddSJed Brown   be 1. This representation is common in geometric modeling and allows affine transformations to be composed using
1112aaa8cc7dSPierre Jolivet   simple matrix multiplication.
11135f06a3ddSJed Brown 
11145f06a3ddSJed Brown   Although the interface accepts a general affine transform, only affine translation is supported at present.
11155f06a3ddSJed Brown 
111660225df5SJacob Faibussowitsch   Developer Notes:
11175f06a3ddSJed Brown   This interface should be replaced by making BasisTransform public, expanding it to support affine representations, and
11185f06a3ddSJed Brown   adding GPU implementations to apply the G2L/L2G transforms.
111953c0d4aeSBarry Smith 
11201cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()`
11216725e60dSJed Brown @*/
1122cc4c1da9SBarry Smith PetscErrorCode DMPlexSetIsoperiodicFaceTransform(DM dm, PetscInt n, const PetscScalar t[])
11236725e60dSJed Brown {
11246725e60dSJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
11254d86920dSPierre Jolivet 
11266725e60dSJed Brown   PetscFunctionBegin;
11276725e60dSJed Brown   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
11281fca310dSJames Wright   PetscCheck(n == plex->periodic.num_face_sfs, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of transforms (%" PetscInt_FMT ") must equal number of isoperiodc face SFs (%" PetscInt_FMT ")", n, plex->periodic.num_face_sfs);
11291fca310dSJames Wright 
1130d7d2d1d2SJames Wright   PetscCall(PetscFree(plex->periodic.transform));
11311fca310dSJames Wright   PetscCall(PetscMalloc1(n, &plex->periodic.transform));
11321fca310dSJames Wright   for (PetscInt i = 0; i < n; i++) {
11336725e60dSJed Brown     for (PetscInt j = 0; j < 4; j++) {
11341fca310dSJames Wright       for (PetscInt k = 0; k < 4; k++) {
11351fca310dSJames Wright         PetscCheck(j != k || t[i * 16 + j * 4 + k] == 1., PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Rotated transforms not supported");
11361fca310dSJames Wright         plex->periodic.transform[i][j][k] = t[i * 16 + j * 4 + k];
11371fca310dSJames Wright       }
11386725e60dSJed Brown     }
11396725e60dSJed Brown   }
11403ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
11416725e60dSJed Brown }
1142