xref: /petsc/src/dm/impls/plex/plexsfc.c (revision 4d86920da9ee67c835173a5dfffa1b3a52fd24ca)
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 
203e72e933SJed Brown static unsigned ZCodeSplit1(ZCode z)
213e72e933SJed Brown {
223e72e933SJed Brown   z = ((z & 01001001001001001) | ((z >> 2) & 02002002002002002) | ((z >> 4) & 04004004004004004));
233e72e933SJed Brown   z = (z | (z >> 6) | (z >> 12)) & 0000000777000000777;
243e72e933SJed Brown   z = (z | (z >> 18)) & 0777777;
253e72e933SJed Brown   return (unsigned)z;
263e72e933SJed Brown }
273e72e933SJed Brown 
283e72e933SJed Brown static ZCode ZEncode1(unsigned t)
293e72e933SJed Brown {
303e72e933SJed Brown   ZCode z = t;
313e72e933SJed Brown   z       = (z | (z << 18)) & 0777000000777;
323e72e933SJed Brown   z       = (z | (z << 6) | (z << 12)) & 07007007007007007;
333e72e933SJed Brown   z       = (z | (z << 2) | (z << 4)) & 0111111111111111111;
343e72e933SJed Brown   return z;
353e72e933SJed Brown }
363e72e933SJed Brown 
373e72e933SJed Brown static Ijk ZCodeSplit(ZCode z)
383e72e933SJed Brown {
393e72e933SJed Brown   Ijk c;
403e72e933SJed Brown   c.i = ZCodeSplit1(z >> 2);
413e72e933SJed Brown   c.j = ZCodeSplit1(z >> 1);
423e72e933SJed Brown   c.k = ZCodeSplit1(z >> 0);
433e72e933SJed Brown   return c;
443e72e933SJed Brown }
453e72e933SJed Brown 
463e72e933SJed Brown static ZCode ZEncode(Ijk c)
473e72e933SJed Brown {
483e72e933SJed Brown   ZCode z = (ZEncode1(c.i) << 2) | (ZEncode1(c.j) << 1) | ZEncode1(c.k);
493e72e933SJed Brown   return z;
503e72e933SJed Brown }
513e72e933SJed Brown 
523e72e933SJed Brown static PetscBool IjkActive(Ijk extent, Ijk l)
533e72e933SJed Brown {
543e72e933SJed Brown   if (l.i < extent.i && l.j < extent.j && l.k < extent.k) return PETSC_TRUE;
553e72e933SJed Brown   return PETSC_FALSE;
563e72e933SJed Brown }
573e72e933SJed Brown 
586547ddbdSJed Brown // If z is not the base of an octet (last 3 bits 0), return 0.
596547ddbdSJed Brown //
606547ddbdSJed Brown // If z is the base of an octet, we recursively grow to the biggest structured octet. This is typically useful when a z
616547ddbdSJed Brown // is outside the domain and we wish to skip a (possibly recursively large) octet to find our next interesting point.
626547ddbdSJed Brown static ZCode ZStepOct(ZCode z)
636547ddbdSJed Brown {
646547ddbdSJed Brown   if (PetscUnlikely(z == 0)) return 0; // Infinite loop below if z == 0
656547ddbdSJed Brown   ZCode step = 07;
666547ddbdSJed Brown   for (; (z & step) == 0; step = (step << 3) | 07) { }
676547ddbdSJed Brown   return step >> 3;
686547ddbdSJed Brown }
696547ddbdSJed Brown 
703e72e933SJed Brown // Since element/vertex box extents are typically not equal powers of 2, Z codes that lie within the domain are not contiguous.
715f06a3ddSJed Brown static PetscErrorCode ZLayoutCreate(PetscMPIInt size, const PetscInt eextent[3], const PetscInt vextent[3], ZLayout *layout)
723e72e933SJed Brown {
73c77877e3SJed Brown   PetscFunctionBegin;
745f06a3ddSJed Brown   layout->eextent.i = eextent[0];
755f06a3ddSJed Brown   layout->eextent.j = eextent[1];
765f06a3ddSJed Brown   layout->eextent.k = eextent[2];
775f06a3ddSJed Brown   layout->vextent.i = vextent[0];
785f06a3ddSJed Brown   layout->vextent.j = vextent[1];
795f06a3ddSJed Brown   layout->vextent.k = vextent[2];
805f06a3ddSJed Brown   layout->comm_size = size;
815f06a3ddSJed Brown   layout->zstarts   = NULL;
825f06a3ddSJed Brown   PetscCall(PetscMalloc1(size + 1, &layout->zstarts));
833e72e933SJed Brown 
843e72e933SJed Brown   PetscInt total_elems = eextent[0] * eextent[1] * eextent[2];
853e72e933SJed Brown   ZCode    z           = 0;
865f06a3ddSJed Brown   layout->zstarts[0]   = 0;
876547ddbdSJed Brown   // This loop traverses all vertices in the global domain, so is worth making fast. We use ZStepBound
883e72e933SJed Brown   for (PetscMPIInt r = 0; r < size; r++) {
893e72e933SJed Brown     PetscInt elems_needed = (total_elems / size) + (total_elems % size > r), count;
903e72e933SJed Brown     for (count = 0; count < elems_needed; z++) {
916547ddbdSJed Brown       ZCode skip = ZStepOct(z); // optimistically attempt a longer step
926547ddbdSJed Brown       for (ZCode s = skip;; s >>= 3) {
936547ddbdSJed Brown         Ijk trial = ZCodeSplit(z + s);
946547ddbdSJed Brown         if (IjkActive(layout->eextent, trial)) {
956547ddbdSJed Brown           while (count + s + 1 > (ZCode)elems_needed) s >>= 3; // Shrink the octet
966547ddbdSJed Brown           count += s + 1;
976547ddbdSJed Brown           z += s;
986547ddbdSJed Brown           break;
996547ddbdSJed Brown         }
1006547ddbdSJed Brown         if (s == 0) { // the whole skip octet is inactive
1016547ddbdSJed Brown           z += skip;
1026547ddbdSJed Brown           break;
1036547ddbdSJed Brown         }
1046547ddbdSJed Brown       }
1053e72e933SJed Brown     }
1063e72e933SJed Brown     // Pick up any extra vertices in the Z ordering before the next rank's first owned element.
107c77877e3SJed Brown     //
1085f06a3ddSJed Brown     // This leads to poorly balanced vertices when eextent is a power of 2, since all the fringe vertices end up
109c77877e3SJed Brown     // on the last rank. A possible solution is to balance the Z-order vertices independently from the cells, which will
110c77877e3SJed Brown     // result in a lot of element closures being remote. We could finish marking boundary conditions, then do a round of
111c77877e3SJed Brown     // vertex ownership smoothing (which would reorder and redistribute vertices without touching element distribution).
112c77877e3SJed Brown     // Another would be to have an analytic ownership criteria for vertices in the fringe veextent - eextent. This would
113c77877e3SJed Brown     // complicate the job of identifying an owner and its offset.
1145f06a3ddSJed Brown     //
1155f06a3ddSJed Brown     // The current recommended approach is to let `-dm_distribute 1` (default) resolve vertex ownership. This is
1165f06a3ddSJed Brown     // *mandatory* with isoperiodicity (except in special cases) to remove standed vertices from local spaces. Here's
1175f06a3ddSJed Brown     // the issue:
1185f06a3ddSJed Brown     //
1195f06a3ddSJed Brown     // Consider this partition on rank 0 (left) and rank 1.
1205f06a3ddSJed Brown     //
1215f06a3ddSJed Brown     //    4 --------  5 -- 14 --10 -- 21 --11
1225f06a3ddSJed Brown     //                |          |          |
1235f06a3ddSJed Brown     // 7 -- 16 --  8  |          |          |
1245f06a3ddSJed Brown     // |           |  3 -------  7 -------  9
1255f06a3ddSJed Brown     // |           |             |          |
1265f06a3ddSJed Brown     // 4 --------  6 ------ 10   |          |
1275f06a3ddSJed Brown     // |           |         |   6 -- 16 -- 8
1285f06a3ddSJed Brown     // |           |         |
1295f06a3ddSJed Brown     // 3 ---11---  5 --18--  9
1305f06a3ddSJed Brown     //
1315f06a3ddSJed Brown     // The periodic face SF looks like
1325f06a3ddSJed Brown     // [0] Number of roots=21, leaves=1, remote ranks=1
1335f06a3ddSJed Brown     // [0] 16 <- (0,11)
1345f06a3ddSJed Brown     // [1] Number of roots=22, leaves=2, remote ranks=2
1355f06a3ddSJed Brown     // [1] 14 <- (0,18)
1365f06a3ddSJed Brown     // [1] 21 <- (1,16)
1375f06a3ddSJed Brown     //
1385f06a3ddSJed 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
1395f06a3ddSJed Brown     // the point SF links to (1,4) and (1,5). Rank 1 learns about the periodic mapping of (1,5) while handling face
1405f06a3ddSJed Brown     // (1,14), but never learns that vertex (1,4) has been mapped to (0,3) by face (0,16).
1415f06a3ddSJed Brown     //
1425f06a3ddSJed Brown     // We can relatively easily inform vertex (1,4) of this mapping, but it stays in rank 1's local space despite not
1435f06a3ddSJed Brown     // being in the closure and thus not being contributed to. This would be mostly harmless except that some viewer
1445f06a3ddSJed Brown     // routines expect all local points to be somehow significant. It is not easy to analytically remove the (1,4)
1455f06a3ddSJed Brown     // vertex because the point SF and isoperiodic face SF would need to be updated to account for removal of the
1465f06a3ddSJed Brown     // stranded vertices.
1475f06a3ddSJed Brown     for (; z <= ZEncode(layout->vextent); z++) {
1483e72e933SJed Brown       Ijk loc = ZCodeSplit(z);
1495f06a3ddSJed Brown       if (IjkActive(layout->eextent, loc)) break;
1506547ddbdSJed Brown       z += ZStepOct(z);
1513e72e933SJed Brown     }
1525f06a3ddSJed Brown     layout->zstarts[r + 1] = z;
1533e72e933SJed Brown   }
1546547ddbdSJed Brown   layout->zstarts[size] = ZEncode(layout->vextent);
1553ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1563e72e933SJed Brown }
1573e72e933SJed Brown 
1584e2e9504SJed Brown static PetscInt ZLayoutElementsOnRank(const ZLayout *layout, PetscMPIInt rank)
1594e2e9504SJed Brown {
1604e2e9504SJed Brown   PetscInt remote_elem = 0;
1614e2e9504SJed Brown   for (ZCode rz = layout->zstarts[rank]; rz < layout->zstarts[rank + 1]; rz++) {
1624e2e9504SJed Brown     Ijk loc = ZCodeSplit(rz);
1634e2e9504SJed Brown     if (IjkActive(layout->eextent, loc)) remote_elem++;
1646547ddbdSJed Brown     else rz += ZStepOct(rz);
1654e2e9504SJed Brown   }
1664e2e9504SJed Brown   return remote_elem;
1674e2e9504SJed Brown }
1684e2e9504SJed Brown 
16966976f2fSJacob Faibussowitsch static PetscInt ZCodeFind(ZCode key, PetscInt n, const ZCode X[])
1703e72e933SJed Brown {
1713e72e933SJed Brown   PetscInt lo = 0, hi = n;
1723e72e933SJed Brown 
1733e72e933SJed Brown   if (n == 0) return -1;
1743e72e933SJed Brown   while (hi - lo > 1) {
1753e72e933SJed Brown     PetscInt mid = lo + (hi - lo) / 2;
1763e72e933SJed Brown     if (key < X[mid]) hi = mid;
1773e72e933SJed Brown     else lo = mid;
1783e72e933SJed Brown   }
1793e72e933SJed Brown   return key == X[lo] ? lo : -(lo + (key > X[lo]) + 1);
1803e72e933SJed Brown }
1813e72e933SJed Brown 
1825f06a3ddSJed Brown static PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(DM dm, const ZLayout *layout, const ZCode *vert_z, PetscSegBuffer per_faces, const PetscReal *lower, const PetscReal *upper, const DMBoundaryType *periodicity, PetscSegBuffer donor_face_closure, PetscSegBuffer my_donor_faces)
1834e2e9504SJed Brown {
1844e2e9504SJed Brown   MPI_Comm     comm;
1854e2e9504SJed Brown   size_t       num_faces;
1864e2e9504SJed Brown   PetscInt     dim, *faces, vStart, vEnd;
1874e2e9504SJed Brown   PetscMPIInt  size;
1884e2e9504SJed Brown   ZCode       *donor_verts, *donor_minz;
1894e2e9504SJed Brown   PetscSFNode *leaf;
1904e2e9504SJed Brown 
1914e2e9504SJed Brown   PetscFunctionBegin;
1924e2e9504SJed Brown   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1934e2e9504SJed Brown   PetscCallMPI(MPI_Comm_size(comm, &size));
1944e2e9504SJed Brown   PetscCall(DMGetDimension(dm, &dim));
1954e2e9504SJed Brown   const PetscInt csize = PetscPowInt(2, dim - 1);
1964e2e9504SJed Brown   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1974e2e9504SJed Brown   PetscCall(PetscSegBufferGetSize(per_faces, &num_faces));
1984e2e9504SJed Brown   PetscCall(PetscSegBufferExtractInPlace(per_faces, &faces));
1994e2e9504SJed Brown   PetscCall(PetscSegBufferExtractInPlace(donor_face_closure, &donor_verts));
2004e2e9504SJed Brown   PetscCall(PetscMalloc1(num_faces, &donor_minz));
2014e2e9504SJed Brown   PetscCall(PetscMalloc1(num_faces, &leaf));
2024e2e9504SJed Brown   for (PetscInt i = 0; i < (PetscInt)num_faces; i++) {
2034e2e9504SJed Brown     ZCode minz = donor_verts[i * csize];
2044e2e9504SJed Brown     for (PetscInt j = 1; j < csize; j++) minz = PetscMin(minz, donor_verts[i * csize + j]);
2054e2e9504SJed Brown     donor_minz[i] = minz;
2064e2e9504SJed Brown   }
2074e2e9504SJed Brown   {
2084e2e9504SJed Brown     PetscBool sorted;
2094e2e9504SJed Brown     PetscCall(PetscSortedInt64(num_faces, (const PetscInt64 *)donor_minz, &sorted));
2105f06a3ddSJed Brown     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_PLIB, "minz not sorted; periodicity in multiple dimensions not yet supported");
2114e2e9504SJed Brown   }
2124e2e9504SJed Brown   for (PetscInt i = 0; i < (PetscInt)num_faces;) {
2134e2e9504SJed Brown     ZCode    z           = donor_minz[i];
2144e2e9504SJed Brown     PetscInt remote_rank = ZCodeFind(z, size + 1, layout->zstarts), remote_count = 0;
2154e2e9504SJed Brown     if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1;
2164e2e9504SJed Brown     // Process all the vertices on this rank
2174e2e9504SJed Brown     for (ZCode rz = layout->zstarts[remote_rank]; rz < layout->zstarts[remote_rank + 1]; rz++) {
2184e2e9504SJed Brown       Ijk loc = ZCodeSplit(rz);
2194e2e9504SJed Brown       if (rz == z) {
2204e2e9504SJed Brown         leaf[i].rank  = remote_rank;
2214e2e9504SJed Brown         leaf[i].index = remote_count;
2224e2e9504SJed Brown         i++;
2234e2e9504SJed Brown         if (i == (PetscInt)num_faces) break;
2244e2e9504SJed Brown         z = donor_minz[i];
2254e2e9504SJed Brown       }
2264e2e9504SJed Brown       if (IjkActive(layout->vextent, loc)) remote_count++;
2274e2e9504SJed Brown     }
2284e2e9504SJed Brown   }
2294e2e9504SJed Brown   PetscCall(PetscFree(donor_minz));
2304e2e9504SJed Brown   PetscSF sfper;
2314e2e9504SJed Brown   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sfper));
232f3fa974cSJacob Faibussowitsch   PetscCall(PetscSFSetGraph(sfper, vEnd - vStart, num_faces, NULL, PETSC_USE_POINTER, leaf, PETSC_USE_POINTER));
2334e2e9504SJed Brown   const PetscInt *my_donor_degree;
2344e2e9504SJed Brown   PetscCall(PetscSFComputeDegreeBegin(sfper, &my_donor_degree));
2354e2e9504SJed Brown   PetscCall(PetscSFComputeDegreeEnd(sfper, &my_donor_degree));
2364e2e9504SJed Brown   PetscInt num_multiroots = 0;
2374e2e9504SJed Brown   for (PetscInt i = 0; i < vEnd - vStart; i++) {
2384e2e9504SJed Brown     num_multiroots += my_donor_degree[i];
2394e2e9504SJed Brown     if (my_donor_degree[i] == 0) continue;
2405f06a3ddSJed Brown     PetscAssert(my_donor_degree[i] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex has multiple faces");
2414e2e9504SJed Brown   }
2424e2e9504SJed Brown   PetscInt *my_donors, *donor_indices, *my_donor_indices;
2434e2e9504SJed Brown   size_t    num_my_donors;
2444e2e9504SJed Brown   PetscCall(PetscSegBufferGetSize(my_donor_faces, &num_my_donors));
2454e2e9504SJed Brown   PetscCheck((PetscInt)num_my_donors == num_multiroots, PETSC_COMM_SELF, PETSC_ERR_SUP, "Donor request does not match expected donors");
2464e2e9504SJed Brown   PetscCall(PetscSegBufferExtractInPlace(my_donor_faces, &my_donors));
2474e2e9504SJed Brown   PetscCall(PetscMalloc1(vEnd - vStart, &my_donor_indices));
2484e2e9504SJed Brown   for (PetscInt i = 0; i < (PetscInt)num_my_donors; i++) {
2494e2e9504SJed Brown     PetscInt f = my_donors[i];
2504e2e9504SJed Brown     PetscInt num_points, *points = NULL, minv = PETSC_MAX_INT;
2514e2e9504SJed Brown     PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points));
2524e2e9504SJed Brown     for (PetscInt j = 0; j < num_points; j++) {
2534e2e9504SJed Brown       PetscInt p = points[2 * j];
2544e2e9504SJed Brown       if (p < vStart || vEnd <= p) continue;
2554e2e9504SJed Brown       minv = PetscMin(minv, p);
2564e2e9504SJed Brown     }
2574e2e9504SJed Brown     PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points));
2585f06a3ddSJed Brown     PetscAssert(my_donor_degree[minv - vStart] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex not requested");
2594e2e9504SJed Brown     my_donor_indices[minv - vStart] = f;
2604e2e9504SJed Brown   }
2614e2e9504SJed Brown   PetscCall(PetscMalloc1(num_faces, &donor_indices));
2624e2e9504SJed Brown   PetscCall(PetscSFBcastBegin(sfper, MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE));
2634e2e9504SJed Brown   PetscCall(PetscSFBcastEnd(sfper, MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE));
2644e2e9504SJed Brown   PetscCall(PetscFree(my_donor_indices));
2654e2e9504SJed Brown   // Modify our leafs so they point to donor faces instead of donor minz. Additionally, give them indices as faces.
2664e2e9504SJed Brown   for (PetscInt i = 0; i < (PetscInt)num_faces; i++) leaf[i].index = donor_indices[i];
2674e2e9504SJed Brown   PetscCall(PetscFree(donor_indices));
2684e2e9504SJed Brown   PetscInt pStart, pEnd;
2694e2e9504SJed Brown   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
2704e2e9504SJed Brown   PetscCall(PetscSFSetGraph(sfper, pEnd - pStart, num_faces, faces, PETSC_COPY_VALUES, leaf, PETSC_OWN_POINTER));
2715f06a3ddSJed Brown   PetscCall(PetscObjectSetName((PetscObject)sfper, "Z-order Isoperiodic Faces"));
2724e2e9504SJed Brown 
2735f06a3ddSJed Brown   PetscCall(DMPlexSetIsoperiodicFaceSF(dm, sfper));
2746725e60dSJed Brown 
2756725e60dSJed Brown   PetscScalar t[4][4] = {{0}};
2766725e60dSJed Brown   t[0][0]             = 1;
2776725e60dSJed Brown   t[1][1]             = 1;
2786725e60dSJed Brown   t[2][2]             = 1;
2796725e60dSJed Brown   t[3][3]             = 1;
2806725e60dSJed Brown   for (PetscInt i = 0; i < dim; i++)
2815f06a3ddSJed Brown     if (periodicity[i] == DM_BOUNDARY_PERIODIC) t[i][3] = upper[i] - lower[i];
2825f06a3ddSJed Brown   PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, &t[0][0]));
2834e2e9504SJed Brown   PetscCall(PetscSFDestroy(&sfper));
2843ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2854e2e9504SJed Brown }
2864e2e9504SJed Brown 
2875f06a3ddSJed Brown // This is a DMGlobalToLocalHook that applies the affine offsets. When extended for rotated periodicity, it'll need to
2885f06a3ddSJed Brown // apply a rotatonal transform and similar operations will be needed for fields (e.g., to rotate a velocity vector).
2895f06a3ddSJed Brown // We use this crude approach here so we don't have to write new GPU kernels yet.
2906725e60dSJed Brown static PetscErrorCode DMCoordAddPeriodicOffsets_Private(DM dm, Vec g, InsertMode mode, Vec l, void *ctx)
2916725e60dSJed Brown {
2926725e60dSJed Brown   PetscFunctionBegin;
2936725e60dSJed Brown   PetscCall(VecScatterBegin(dm->periodic.affine_to_local, dm->periodic.affine, l, ADD_VALUES, SCATTER_FORWARD));
2946725e60dSJed Brown   PetscCall(VecScatterEnd(dm->periodic.affine_to_local, dm->periodic.affine, l, ADD_VALUES, SCATTER_FORWARD));
2953ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2966725e60dSJed Brown }
2976725e60dSJed Brown 
2985f06a3ddSJed Brown // Start with an SF for a positive depth (e.g., faces) and create a new SF for matched closure. The caller must ensure
2995f06a3ddSJed Brown // that both the donor (root) face and the periodic (leaf) face have consistent orientation, meaning that their closures
3005f06a3ddSJed Brown // are isomorphic. It may be useful/necessary for this restriction to be loosened.
3016725e60dSJed Brown //
3025f06a3ddSJed Brown // Output Arguments:
3035f06a3ddSJed Brown //
3045f06a3ddSJed Brown // + closure_sf - augmented point SF (see `DMGetPointSF()`) that includes the faces and all points in its closure. This
3055f06a3ddSJed Brown //   can be used to create a global section and section SF.
3065f06a3ddSJed Brown // - is_points - index set for just the points in the closure of `face_sf`. These may be used to apply an affine
3075f06a3ddSJed Brown //   transformation to periodic dofs; see DMPeriodicCoordinateSetUp_Internal().
3085f06a3ddSJed Brown //
3095f06a3ddSJed Brown static PetscErrorCode DMPlexCreateIsoperiodicPointSF_Private(DM dm, PetscSF face_sf, PetscSF *closure_sf, IS *is_points)
3106725e60dSJed Brown {
3116725e60dSJed Brown   MPI_Comm           comm;
3126725e60dSJed Brown   PetscInt           nroots, nleaves, npoints;
3136725e60dSJed Brown   const PetscInt    *filocal, *pilocal;
3146725e60dSJed Brown   const PetscSFNode *firemote, *piremote;
3156725e60dSJed Brown   PetscMPIInt        rank;
3166725e60dSJed Brown   PetscSF            point_sf;
3176725e60dSJed Brown 
3186725e60dSJed Brown   PetscFunctionBegin;
3196725e60dSJed Brown   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
3206725e60dSJed Brown   PetscCallMPI(MPI_Comm_rank(comm, &rank));
3216725e60dSJed Brown   PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote));
3226725e60dSJed Brown   PetscCall(DMGetPointSF(dm, &point_sf)); // Point SF has remote points
3236725e60dSJed Brown   PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, &pilocal, &piremote));
3246725e60dSJed Brown   PetscInt *rootdata, *leafdata;
3256725e60dSJed Brown   PetscCall(PetscCalloc2(2 * nroots, &rootdata, 2 * nroots, &leafdata));
3266725e60dSJed Brown   for (PetscInt i = 0; i < nleaves; i++) {
3276725e60dSJed Brown     PetscInt point = filocal[i], cl_size, *closure = NULL;
3286725e60dSJed Brown     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
3296725e60dSJed Brown     leafdata[point] = cl_size - 1;
3306725e60dSJed Brown     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
3316725e60dSJed Brown   }
3326725e60dSJed Brown   PetscCall(PetscSFReduceBegin(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM));
3336725e60dSJed Brown   PetscCall(PetscSFReduceEnd(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM));
3346725e60dSJed Brown 
3356725e60dSJed Brown   PetscInt root_offset = 0;
3366725e60dSJed Brown   for (PetscInt p = 0; p < nroots; p++) {
3376725e60dSJed Brown     const PetscInt *donor_dof = rootdata + nroots;
3386725e60dSJed Brown     if (donor_dof[p] == 0) {
3396725e60dSJed Brown       rootdata[2 * p]     = -1;
3406725e60dSJed Brown       rootdata[2 * p + 1] = -1;
3416725e60dSJed Brown       continue;
3426725e60dSJed Brown     }
3436725e60dSJed Brown     PetscInt  cl_size;
3446725e60dSJed Brown     PetscInt *closure = NULL;
3456725e60dSJed Brown     PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
3466725e60dSJed Brown     // cl_size - 1 = points not including self
3475f06a3ddSJed Brown     PetscAssert(donor_dof[p] == cl_size - 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Reduced leaf cone sizes do not match root cone sizes");
3486725e60dSJed Brown     rootdata[2 * p]     = root_offset;
3496725e60dSJed Brown     rootdata[2 * p + 1] = cl_size - 1;
3506725e60dSJed Brown     root_offset += cl_size - 1;
3516725e60dSJed Brown     PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
3526725e60dSJed Brown   }
3536725e60dSJed Brown   PetscCall(PetscSFBcastBegin(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
3546725e60dSJed Brown   PetscCall(PetscSFBcastEnd(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
3556725e60dSJed Brown   // Count how many leaves we need to communicate the closures
3566725e60dSJed Brown   PetscInt leaf_offset = 0;
3576725e60dSJed Brown   for (PetscInt i = 0; i < nleaves; i++) {
3586725e60dSJed Brown     PetscInt point = filocal[i];
3596725e60dSJed Brown     if (leafdata[2 * point + 1] < 0) continue;
3606725e60dSJed Brown     leaf_offset += leafdata[2 * point + 1];
3616725e60dSJed Brown   }
3626725e60dSJed Brown 
3636725e60dSJed Brown   PetscSFNode *closure_leaf;
3646725e60dSJed Brown   PetscCall(PetscMalloc1(leaf_offset, &closure_leaf));
3656725e60dSJed Brown   leaf_offset = 0;
3666725e60dSJed Brown   for (PetscInt i = 0; i < nleaves; i++) {
3676725e60dSJed Brown     PetscInt point   = filocal[i];
3686725e60dSJed Brown     PetscInt cl_size = leafdata[2 * point + 1];
3696725e60dSJed Brown     if (cl_size < 0) continue;
3706725e60dSJed Brown     for (PetscInt j = 0; j < cl_size; j++) {
3716725e60dSJed Brown       closure_leaf[leaf_offset].rank  = firemote[i].rank;
3726725e60dSJed Brown       closure_leaf[leaf_offset].index = leafdata[2 * point] + j;
3736725e60dSJed Brown       leaf_offset++;
3746725e60dSJed Brown     }
3756725e60dSJed Brown   }
3766725e60dSJed Brown 
3776725e60dSJed Brown   PetscSF sf_closure;
3786725e60dSJed Brown   PetscCall(PetscSFCreate(comm, &sf_closure));
3796725e60dSJed Brown   PetscCall(PetscSFSetGraph(sf_closure, root_offset, leaf_offset, NULL, PETSC_USE_POINTER, closure_leaf, PETSC_OWN_POINTER));
3806725e60dSJed Brown 
3816725e60dSJed Brown   // Pack root buffer with owner for every point in the root cones
3826725e60dSJed Brown   PetscSFNode *donor_closure;
3836725e60dSJed Brown   PetscCall(PetscCalloc1(root_offset, &donor_closure));
3846725e60dSJed Brown   root_offset = 0;
3856725e60dSJed Brown   for (PetscInt p = 0; p < nroots; p++) {
3866725e60dSJed Brown     if (rootdata[2 * p] < 0) continue;
3876725e60dSJed Brown     PetscInt  cl_size;
3886725e60dSJed Brown     PetscInt *closure = NULL;
3896725e60dSJed Brown     PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
3906725e60dSJed Brown     for (PetscInt j = 1; j < cl_size; j++) {
3916725e60dSJed Brown       PetscInt c = closure[2 * j];
3926725e60dSJed Brown       if (pilocal) {
3936725e60dSJed Brown         PetscInt found = -1;
3946725e60dSJed Brown         if (npoints > 0) PetscCall(PetscFindInt(c, npoints, pilocal, &found));
3956725e60dSJed Brown         if (found >= 0) {
3966725e60dSJed Brown           donor_closure[root_offset++] = piremote[found];
3976725e60dSJed Brown           continue;
3986725e60dSJed Brown         }
3996725e60dSJed Brown       }
4006725e60dSJed Brown       // we own c
4016725e60dSJed Brown       donor_closure[root_offset].rank  = rank;
4026725e60dSJed Brown       donor_closure[root_offset].index = c;
4036725e60dSJed Brown       root_offset++;
4046725e60dSJed Brown     }
4056725e60dSJed Brown     PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
4066725e60dSJed Brown   }
4076725e60dSJed Brown 
4086725e60dSJed Brown   PetscSFNode *leaf_donor_closure;
4096725e60dSJed Brown   PetscCall(PetscMalloc1(leaf_offset, &leaf_donor_closure));
4106725e60dSJed Brown   PetscCall(PetscSFBcastBegin(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE));
4116725e60dSJed Brown   PetscCall(PetscSFBcastEnd(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE));
4126725e60dSJed Brown   PetscCall(PetscSFDestroy(&sf_closure));
4136725e60dSJed Brown   PetscCall(PetscFree(donor_closure));
4146725e60dSJed Brown 
4156725e60dSJed Brown   PetscSFNode *new_iremote;
4166725e60dSJed Brown   PetscCall(PetscCalloc1(nroots, &new_iremote));
4176725e60dSJed Brown   for (PetscInt i = 0; i < nroots; i++) new_iremote[i].rank = -1;
4186725e60dSJed Brown   // Walk leaves and match vertices
4196725e60dSJed Brown   leaf_offset = 0;
4206725e60dSJed Brown   for (PetscInt i = 0; i < nleaves; i++) {
4216725e60dSJed Brown     PetscInt  point   = filocal[i], cl_size;
4226725e60dSJed Brown     PetscInt *closure = NULL;
4236725e60dSJed Brown     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
4246725e60dSJed Brown     for (PetscInt j = 1; j < cl_size; j++) { // TODO: should we send donor edge orientations so we can flip for consistency?
4256725e60dSJed Brown       PetscInt    c  = closure[2 * j];
4266725e60dSJed Brown       PetscSFNode lc = leaf_donor_closure[leaf_offset];
4276725e60dSJed Brown       // printf("[%d] face %d.%d: %d ?-- (%d,%d)\n", rank, point, j, c, lc.rank, lc.index);
4286725e60dSJed Brown       if (new_iremote[c].rank == -1) {
4296725e60dSJed Brown         new_iremote[c] = lc;
4305f06a3ddSJed 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");
4316725e60dSJed Brown       leaf_offset++;
4326725e60dSJed Brown     }
4336725e60dSJed Brown     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
4346725e60dSJed Brown   }
4356725e60dSJed Brown   PetscCall(PetscFree(leaf_donor_closure));
4366725e60dSJed Brown 
4376725e60dSJed Brown   // Include face points in closure SF
4386725e60dSJed Brown   for (PetscInt i = 0; i < nleaves; i++) new_iremote[filocal[i]] = firemote[i];
4396725e60dSJed Brown   // consolidate leaves
4406725e60dSJed Brown   PetscInt num_new_leaves = 0;
4416725e60dSJed Brown   for (PetscInt i = 0; i < nroots; i++) {
4426725e60dSJed Brown     if (new_iremote[i].rank == -1) continue;
4436725e60dSJed Brown     new_iremote[num_new_leaves] = new_iremote[i];
4446725e60dSJed Brown     leafdata[num_new_leaves]    = i;
4456725e60dSJed Brown     num_new_leaves++;
4466725e60dSJed Brown   }
4475f06a3ddSJed Brown   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, num_new_leaves, leafdata, PETSC_COPY_VALUES, is_points));
4486725e60dSJed Brown 
4496725e60dSJed Brown   PetscSF csf;
4506725e60dSJed Brown   PetscCall(PetscSFCreate(comm, &csf));
4516725e60dSJed Brown   PetscCall(PetscSFSetGraph(csf, nroots, num_new_leaves, leafdata, PETSC_COPY_VALUES, new_iremote, PETSC_COPY_VALUES));
4526725e60dSJed Brown   PetscCall(PetscFree(new_iremote)); // copy and delete because new_iremote is longer than it needs to be
4536725e60dSJed Brown   PetscCall(PetscFree2(rootdata, leafdata));
4546725e60dSJed Brown 
4555f06a3ddSJed Brown   if (npoints < 0) { // empty point_sf
4566725e60dSJed Brown     *closure_sf = csf;
4575f06a3ddSJed Brown   } else {
4585f06a3ddSJed Brown     PetscCall(PetscSFMerge(point_sf, csf, closure_sf));
4595f06a3ddSJed Brown     PetscCall(PetscSFDestroy(&csf));
4605f06a3ddSJed Brown   }
4615f06a3ddSJed Brown   PetscCall(PetscObjectSetName((PetscObject)*closure_sf, "Composed Periodic Points"));
4623ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4636725e60dSJed Brown }
4646725e60dSJed Brown 
4655f06a3ddSJed Brown static PetscErrorCode DMGetIsoperiodicPointSF_Plex(DM dm, PetscSF *sf)
4666725e60dSJed Brown {
4676725e60dSJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
4686725e60dSJed Brown 
4696725e60dSJed Brown   PetscFunctionBegin;
4706725e60dSJed Brown   if (!plex->periodic.composed_sf) {
4716725e60dSJed Brown     PetscSF face_sf = plex->periodic.face_sf;
4726725e60dSJed Brown 
4735f06a3ddSJed Brown     PetscCall(DMPlexCreateIsoperiodicPointSF_Private(dm, face_sf, &plex->periodic.composed_sf, &plex->periodic.periodic_points));
4746725e60dSJed Brown   }
4756725e60dSJed Brown   if (sf) *sf = plex->periodic.composed_sf;
4763ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4776725e60dSJed Brown }
4786725e60dSJed Brown 
4795f06a3ddSJed Brown PetscErrorCode DMPlexMigrateIsoperiodicFaceSF_Internal(DM old_dm, DM dm, PetscSF sf_migration)
4805f06a3ddSJed Brown {
4815f06a3ddSJed Brown   DM_Plex    *plex = (DM_Plex *)old_dm->data;
4825f06a3ddSJed Brown   PetscSF     sf_point;
4835f06a3ddSJed Brown   PetscMPIInt rank;
4845f06a3ddSJed Brown 
4855f06a3ddSJed Brown   PetscFunctionBegin;
4863ba16761SJacob Faibussowitsch   if (!plex->periodic.face_sf) PetscFunctionReturn(PETSC_SUCCESS);
4875f06a3ddSJed Brown   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
4885f06a3ddSJed Brown   PetscCall(DMGetPointSF(dm, &sf_point));
4895f06a3ddSJed Brown   PetscInt           old_npoints, new_npoints, old_nleaf, new_nleaf, point_nleaf;
4905f06a3ddSJed Brown   PetscSFNode       *new_leafdata, *rootdata, *leafdata;
4915f06a3ddSJed Brown   const PetscInt    *old_local, *point_local;
4925f06a3ddSJed Brown   const PetscSFNode *old_remote, *point_remote;
4935f06a3ddSJed Brown   PetscCall(PetscSFGetGraph(plex->periodic.face_sf, &old_npoints, &old_nleaf, &old_local, &old_remote));
4945f06a3ddSJed Brown   PetscCall(PetscSFGetGraph(sf_migration, NULL, &new_nleaf, NULL, NULL));
4955f06a3ddSJed Brown   PetscCall(PetscSFGetGraph(sf_point, &new_npoints, &point_nleaf, &point_local, &point_remote));
4965f06a3ddSJed Brown   PetscAssert(new_nleaf == new_npoints, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected migration leaf space to match new point root space");
4975f06a3ddSJed Brown   PetscCall(PetscMalloc3(old_npoints, &rootdata, old_npoints, &leafdata, new_npoints, &new_leafdata));
4985f06a3ddSJed Brown 
4995f06a3ddSJed Brown   // Fill new_leafdata with new owners of all points
5005f06a3ddSJed Brown   for (PetscInt i = 0; i < new_npoints; i++) {
5015f06a3ddSJed Brown     new_leafdata[i].rank  = rank;
5025f06a3ddSJed Brown     new_leafdata[i].index = i;
5035f06a3ddSJed Brown   }
5045f06a3ddSJed Brown   for (PetscInt i = 0; i < point_nleaf; i++) {
5055f06a3ddSJed Brown     PetscInt j      = point_local[i];
5065f06a3ddSJed Brown     new_leafdata[j] = point_remote[i];
5075f06a3ddSJed Brown   }
5085f06a3ddSJed Brown   // REPLACE is okay because every leaf agrees about the new owners
5095f06a3ddSJed Brown   PetscCall(PetscSFReduceBegin(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE));
5105f06a3ddSJed Brown   PetscCall(PetscSFReduceEnd(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE));
5115f06a3ddSJed Brown   // rootdata now contains the new owners
5125f06a3ddSJed Brown 
5135f06a3ddSJed Brown   // Send to leaves of old space
5145f06a3ddSJed Brown   for (PetscInt i = 0; i < old_npoints; i++) {
5155f06a3ddSJed Brown     leafdata[i].rank  = -1;
5165f06a3ddSJed Brown     leafdata[i].index = -1;
5175f06a3ddSJed Brown   }
5185f06a3ddSJed Brown   PetscCall(PetscSFBcastBegin(plex->periodic.face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
5195f06a3ddSJed Brown   PetscCall(PetscSFBcastEnd(plex->periodic.face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
5205f06a3ddSJed Brown 
5215f06a3ddSJed Brown   // Send to new leaf space
5225f06a3ddSJed Brown   PetscCall(PetscSFBcastBegin(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE));
5235f06a3ddSJed Brown   PetscCall(PetscSFBcastEnd(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE));
5245f06a3ddSJed Brown 
5255f06a3ddSJed Brown   PetscInt     nface = 0, *new_local;
5265f06a3ddSJed Brown   PetscSFNode *new_remote;
5275f06a3ddSJed Brown   for (PetscInt i = 0; i < new_npoints; i++) nface += (new_leafdata[i].rank >= 0);
5285f06a3ddSJed Brown   PetscCall(PetscMalloc1(nface, &new_local));
5295f06a3ddSJed Brown   PetscCall(PetscMalloc1(nface, &new_remote));
5305f06a3ddSJed Brown   nface = 0;
5315f06a3ddSJed Brown   for (PetscInt i = 0; i < new_npoints; i++) {
5325f06a3ddSJed Brown     if (new_leafdata[i].rank == -1) continue;
5335f06a3ddSJed Brown     new_local[nface]  = i;
5345f06a3ddSJed Brown     new_remote[nface] = new_leafdata[i];
5355f06a3ddSJed Brown     nface++;
5365f06a3ddSJed Brown   }
5375f06a3ddSJed Brown   PetscCall(PetscFree3(rootdata, leafdata, new_leafdata));
5385f06a3ddSJed Brown   PetscSF sf_face;
5395f06a3ddSJed Brown   PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf_face));
5405f06a3ddSJed Brown   PetscCall(PetscSFSetGraph(sf_face, new_npoints, nface, new_local, PETSC_OWN_POINTER, new_remote, PETSC_OWN_POINTER));
5415f06a3ddSJed Brown   PetscCall(PetscObjectSetName((PetscObject)sf_face, "Migrated Isoperiodic Faces"));
5425f06a3ddSJed Brown   PetscCall(DMPlexSetIsoperiodicFaceSF(dm, sf_face));
5435f06a3ddSJed Brown   PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, &plex->periodic.transform[0][0]));
5445f06a3ddSJed Brown   PetscCall(PetscSFDestroy(&sf_face));
5453ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5465f06a3ddSJed Brown }
5475f06a3ddSJed Brown 
5486725e60dSJed Brown PetscErrorCode DMPeriodicCoordinateSetUp_Internal(DM dm)
5496725e60dSJed Brown {
5506725e60dSJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
551*4d86920dSPierre Jolivet 
5526725e60dSJed Brown   PetscFunctionBegin;
5533ba16761SJacob Faibussowitsch   if (!plex->periodic.face_sf) PetscFunctionReturn(PETSC_SUCCESS);
5545f06a3ddSJed Brown   PetscCall(DMGetIsoperiodicPointSF_Plex(dm, NULL));
5555f06a3ddSJed Brown   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex));
5566725e60dSJed Brown 
5576725e60dSJed Brown   PetscInt dim;
5586725e60dSJed Brown   PetscCall(DMGetDimension(dm, &dim));
5596725e60dSJed Brown   size_t count;
5606725e60dSJed Brown   IS     isdof;
5616725e60dSJed Brown   {
5626725e60dSJed Brown     PetscInt        npoints;
5636725e60dSJed Brown     const PetscInt *points;
5646725e60dSJed Brown     IS              is = plex->periodic.periodic_points;
5656725e60dSJed Brown     PetscSegBuffer  seg;
5666725e60dSJed Brown     PetscSection    section;
5676725e60dSJed Brown     PetscCall(DMGetLocalSection(dm, &section));
5686725e60dSJed Brown     PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 32, &seg));
5696725e60dSJed Brown     PetscCall(ISGetSize(is, &npoints));
5706725e60dSJed Brown     PetscCall(ISGetIndices(is, &points));
5716725e60dSJed Brown     for (PetscInt i = 0; i < npoints; i++) {
5726725e60dSJed Brown       PetscInt point = points[i], off, dof;
5736725e60dSJed Brown       PetscCall(PetscSectionGetOffset(section, point, &off));
5746725e60dSJed Brown       PetscCall(PetscSectionGetDof(section, point, &dof));
5756725e60dSJed Brown       PetscAssert(dof % dim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected dof %" PetscInt_FMT " not divisible by dimension %" PetscInt_FMT, dof, dim);
5766725e60dSJed Brown       for (PetscInt j = 0; j < dof / dim; j++) {
5776725e60dSJed Brown         PetscInt *slot;
5786725e60dSJed Brown         PetscCall(PetscSegBufferGetInts(seg, 1, &slot));
579729bdd54SJed Brown         *slot = off / dim + j;
5806725e60dSJed Brown       }
5816725e60dSJed Brown     }
5826725e60dSJed Brown     PetscInt *ind;
5836725e60dSJed Brown     PetscCall(PetscSegBufferGetSize(seg, &count));
5846725e60dSJed Brown     PetscCall(PetscSegBufferExtractAlloc(seg, &ind));
5856725e60dSJed Brown     PetscCall(PetscSegBufferDestroy(&seg));
5866725e60dSJed Brown     PetscCall(ISCreateBlock(PETSC_COMM_SELF, dim, count, ind, PETSC_OWN_POINTER, &isdof));
5876725e60dSJed Brown   }
5886725e60dSJed Brown   Vec        L, P;
5896725e60dSJed Brown   VecType    vec_type;
5906725e60dSJed Brown   VecScatter scatter;
5916725e60dSJed Brown   PetscCall(DMGetLocalVector(dm, &L));
5926725e60dSJed Brown   PetscCall(VecCreate(PETSC_COMM_SELF, &P));
5936725e60dSJed Brown   PetscCall(VecSetSizes(P, count * dim, count * dim));
5946725e60dSJed Brown   PetscCall(VecGetType(L, &vec_type));
5956725e60dSJed Brown   PetscCall(VecSetType(P, vec_type));
5966725e60dSJed Brown   PetscCall(VecScatterCreate(P, NULL, L, isdof, &scatter));
5976725e60dSJed Brown   PetscCall(DMRestoreLocalVector(dm, &L));
5986725e60dSJed Brown   PetscCall(ISDestroy(&isdof));
5996725e60dSJed Brown 
6006725e60dSJed Brown   {
6016725e60dSJed Brown     PetscScalar *x;
6026725e60dSJed Brown     PetscCall(VecGetArrayWrite(P, &x));
6036725e60dSJed Brown     for (PetscInt i = 0; i < (PetscInt)count; i++) {
6046725e60dSJed Brown       for (PetscInt j = 0; j < dim; j++) x[i * dim + j] = plex->periodic.transform[j][3];
6056725e60dSJed Brown     }
6066725e60dSJed Brown     PetscCall(VecRestoreArrayWrite(P, &x));
6076725e60dSJed Brown   }
6086725e60dSJed Brown 
6096725e60dSJed Brown   dm->periodic.affine_to_local = scatter;
6106725e60dSJed Brown   dm->periodic.affine          = P;
6116725e60dSJed Brown   PetscCall(DMGlobalToLocalHookAdd(dm, NULL, DMCoordAddPeriodicOffsets_Private, NULL));
6123ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6136725e60dSJed Brown }
6146725e60dSJed Brown 
6155f06a3ddSJed Brown // We'll just orient all the edges, though only periodic boundary edges need orientation
6165f06a3ddSJed Brown static PetscErrorCode DMPlexOrientPositiveEdges_Private(DM dm)
6175f06a3ddSJed Brown {
6185f06a3ddSJed Brown   PetscInt dim, eStart, eEnd;
619*4d86920dSPierre Jolivet 
6205f06a3ddSJed Brown   PetscFunctionBegin;
6215f06a3ddSJed Brown   PetscCall(DMGetDimension(dm, &dim));
6223ba16761SJacob Faibussowitsch   if (dim < 3) PetscFunctionReturn(PETSC_SUCCESS); // not necessary
6235f06a3ddSJed Brown   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
6245f06a3ddSJed Brown   for (PetscInt e = eStart; e < eEnd; e++) {
6255f06a3ddSJed Brown     const PetscInt *cone;
6265f06a3ddSJed Brown     PetscCall(DMPlexGetCone(dm, e, &cone));
6275f06a3ddSJed Brown     if (cone[0] > cone[1]) PetscCall(DMPlexOrientPoint(dm, e, -1));
6285f06a3ddSJed Brown   }
6293ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6305f06a3ddSJed Brown }
6315f06a3ddSJed Brown 
6323e72e933SJed Brown PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate)
6333e72e933SJed Brown {
6343e72e933SJed Brown   PetscInt  eextent[3] = {1, 1, 1}, vextent[3] = {1, 1, 1};
6353e72e933SJed Brown   const Ijk closure_1[] = {
6363e72e933SJed Brown     {0, 0, 0},
6373e72e933SJed Brown     {1, 0, 0},
6383e72e933SJed Brown   };
6393e72e933SJed Brown   const Ijk closure_2[] = {
6403e72e933SJed Brown     {0, 0, 0},
6413e72e933SJed Brown     {1, 0, 0},
6423e72e933SJed Brown     {1, 1, 0},
6433e72e933SJed Brown     {0, 1, 0},
6443e72e933SJed Brown   };
6453e72e933SJed Brown   const Ijk closure_3[] = {
6463e72e933SJed Brown     {0, 0, 0},
6473e72e933SJed Brown     {0, 1, 0},
6483e72e933SJed Brown     {1, 1, 0},
6493e72e933SJed Brown     {1, 0, 0},
6503e72e933SJed Brown     {0, 0, 1},
6513e72e933SJed Brown     {1, 0, 1},
6523e72e933SJed Brown     {1, 1, 1},
6533e72e933SJed Brown     {0, 1, 1},
6543e72e933SJed Brown   };
6553431e603SJed Brown   const Ijk *const closure_dim[] = {NULL, closure_1, closure_2, closure_3};
6563431e603SJed Brown   // This must be kept consistent with DMPlexCreateCubeMesh_Internal
6573431e603SJed Brown   const PetscInt        face_marker_1[]   = {1, 2};
6583431e603SJed Brown   const PetscInt        face_marker_2[]   = {4, 2, 1, 3};
6593431e603SJed Brown   const PetscInt        face_marker_3[]   = {6, 5, 3, 4, 1, 2};
6603431e603SJed Brown   const PetscInt *const face_marker_dim[] = {NULL, face_marker_1, face_marker_2, face_marker_3};
6616725e60dSJed Brown   // Orient faces so the normal is in the positive axis and the first vertex is the one closest to zero.
6626725e60dSJed Brown   // These orientations can be determined by examining cones of a reference quad and hex element.
6636725e60dSJed Brown   const PetscInt        face_orient_1[]   = {0, 0};
6646725e60dSJed Brown   const PetscInt        face_orient_2[]   = {-1, 0, 0, -1};
6656725e60dSJed Brown   const PetscInt        face_orient_3[]   = {-2, 0, -2, 1, -2, 0};
6666725e60dSJed Brown   const PetscInt *const face_orient_dim[] = {NULL, face_orient_1, face_orient_2, face_orient_3};
6673e72e933SJed Brown 
6683e72e933SJed Brown   PetscFunctionBegin;
6694f572ea9SToby Isaac   PetscAssertPointer(dm, 1);
6703e72e933SJed Brown   PetscValidLogicalCollectiveInt(dm, dim, 2);
6713e72e933SJed Brown   PetscCall(DMSetDimension(dm, dim));
6723e72e933SJed Brown   PetscMPIInt rank, size;
6733e72e933SJed Brown   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
6743e72e933SJed Brown   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
6753e72e933SJed Brown   for (PetscInt i = 0; i < dim; i++) {
6763e72e933SJed Brown     eextent[i] = faces[i];
6773e72e933SJed Brown     vextent[i] = faces[i] + 1;
6783e72e933SJed Brown   }
679c77877e3SJed Brown   ZLayout layout;
680c77877e3SJed Brown   PetscCall(ZLayoutCreate(size, eextent, vextent, &layout));
6813e72e933SJed Brown   PetscZSet vset; // set of all vertices in the closure of the owned elements
6823e72e933SJed Brown   PetscCall(PetscZSetCreate(&vset));
6833e72e933SJed Brown   PetscInt local_elems = 0;
6843e72e933SJed Brown   for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) {
6853e72e933SJed Brown     Ijk loc = ZCodeSplit(z);
6863ba16761SJacob Faibussowitsch     if (IjkActive(layout.vextent, loc)) PetscCall(PetscZSetAdd(vset, z));
6876547ddbdSJed Brown     else {
6886547ddbdSJed Brown       z += ZStepOct(z);
6896547ddbdSJed Brown       continue;
6906547ddbdSJed Brown     }
6913e72e933SJed Brown     if (IjkActive(layout.eextent, loc)) {
6923e72e933SJed Brown       local_elems++;
6933e72e933SJed Brown       // Add all neighboring vertices to set
6943e72e933SJed Brown       for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) {
6953e72e933SJed Brown         Ijk   inc  = closure_dim[dim][n];
6963e72e933SJed Brown         Ijk   nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k};
6973e72e933SJed Brown         ZCode v    = ZEncode(nloc);
6983ba16761SJacob Faibussowitsch         PetscCall(PetscZSetAdd(vset, v));
6993e72e933SJed Brown       }
7003e72e933SJed Brown     }
7013e72e933SJed Brown   }
7023e72e933SJed Brown   PetscInt local_verts, off = 0;
7033e72e933SJed Brown   ZCode   *vert_z;
7043e72e933SJed Brown   PetscCall(PetscZSetGetSize(vset, &local_verts));
7053e72e933SJed Brown   PetscCall(PetscMalloc1(local_verts, &vert_z));
7063e72e933SJed Brown   PetscCall(PetscZSetGetElems(vset, &off, vert_z));
7073e72e933SJed Brown   PetscCall(PetscZSetDestroy(&vset));
7083e72e933SJed Brown   // ZCode is unsigned for bitwise convenience, but highest bit should never be set, so can interpret as signed
7093e72e933SJed Brown   PetscCall(PetscSortInt64(local_verts, (PetscInt64 *)vert_z));
7103e72e933SJed Brown 
7113e72e933SJed Brown   PetscCall(DMPlexSetChart(dm, 0, local_elems + local_verts));
7123e72e933SJed Brown   for (PetscInt e = 0; e < local_elems; e++) PetscCall(DMPlexSetConeSize(dm, e, PetscPowInt(2, dim)));
7133e72e933SJed Brown   PetscCall(DMSetUp(dm));
7143e72e933SJed Brown   {
7153e72e933SJed Brown     PetscInt e = 0;
7163e72e933SJed Brown     for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) {
7173e72e933SJed Brown       Ijk loc = ZCodeSplit(z);
7186547ddbdSJed Brown       if (!IjkActive(layout.eextent, loc)) {
7196547ddbdSJed Brown         z += ZStepOct(z);
7206547ddbdSJed Brown         continue;
7216547ddbdSJed Brown       }
7223e72e933SJed Brown       PetscInt cone[8], orient[8] = {0};
7233e72e933SJed Brown       for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) {
7243e72e933SJed Brown         Ijk      inc  = closure_dim[dim][n];
7253e72e933SJed Brown         Ijk      nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k};
7263e72e933SJed Brown         ZCode    v    = ZEncode(nloc);
7273e72e933SJed Brown         PetscInt ci   = ZCodeFind(v, local_verts, vert_z);
7283e72e933SJed Brown         PetscAssert(ci >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find neighbor vertex in set");
7293e72e933SJed Brown         cone[n] = local_elems + ci;
7303e72e933SJed Brown       }
7313e72e933SJed Brown       PetscCall(DMPlexSetCone(dm, e, cone));
7323e72e933SJed Brown       PetscCall(DMPlexSetConeOrientation(dm, e, orient));
7333e72e933SJed Brown       e++;
7343e72e933SJed Brown     }
7353e72e933SJed Brown   }
7363e72e933SJed Brown 
7373e72e933SJed Brown   PetscCall(DMPlexSymmetrize(dm));
7383e72e933SJed Brown   PetscCall(DMPlexStratify(dm));
7396725e60dSJed Brown 
7403e72e933SJed Brown   { // Create point SF
7413e72e933SJed Brown     PetscSF sf;
7423ba16761SJacob Faibussowitsch     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf));
7433e72e933SJed Brown     PetscInt owned_verts = ZCodeFind(layout.zstarts[rank + 1], local_verts, vert_z);
7443e72e933SJed Brown     if (owned_verts < 0) owned_verts = -(owned_verts + 1); // We don't care whether the key was found
7453e72e933SJed Brown     PetscInt     num_ghosts = local_verts - owned_verts;   // Due to sorting, owned vertices always come first
7463e72e933SJed Brown     PetscInt    *local_ghosts;
7473e72e933SJed Brown     PetscSFNode *ghosts;
7483e72e933SJed Brown     PetscCall(PetscMalloc1(num_ghosts, &local_ghosts));
7493e72e933SJed Brown     PetscCall(PetscMalloc1(num_ghosts, &ghosts));
7503e72e933SJed Brown     for (PetscInt i = 0; i < num_ghosts;) {
7513e72e933SJed Brown       ZCode    z           = vert_z[owned_verts + i];
7523e72e933SJed Brown       PetscInt remote_rank = ZCodeFind(z, size + 1, layout.zstarts), remote_count = 0;
7533e72e933SJed Brown       if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1;
7543e72e933SJed Brown       // We have a new remote rank; find all the ghost indices (which are contiguous in vert_z)
7553e72e933SJed Brown 
7563e72e933SJed Brown       // Count the elements on remote_rank
7574e2e9504SJed Brown       PetscInt remote_elem = ZLayoutElementsOnRank(&layout, remote_rank);
7583e72e933SJed Brown 
7593e72e933SJed Brown       // Traverse vertices and make ghost links
7603e72e933SJed Brown       for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) {
7613e72e933SJed Brown         Ijk loc = ZCodeSplit(rz);
7623e72e933SJed Brown         if (rz == z) {
7633e72e933SJed Brown           local_ghosts[i] = local_elems + owned_verts + i;
7643e72e933SJed Brown           ghosts[i].rank  = remote_rank;
7653e72e933SJed Brown           ghosts[i].index = remote_elem + remote_count;
7663e72e933SJed Brown           i++;
7673e72e933SJed Brown           if (i == num_ghosts) break;
7683e72e933SJed Brown           z = vert_z[owned_verts + i];
7693e72e933SJed Brown         }
7703e72e933SJed Brown         if (IjkActive(layout.vextent, loc)) remote_count++;
7716547ddbdSJed Brown         else rz += ZStepOct(rz);
7723e72e933SJed Brown       }
7733e72e933SJed Brown     }
7743e72e933SJed Brown     PetscCall(PetscSFSetGraph(sf, local_elems + local_verts, num_ghosts, local_ghosts, PETSC_OWN_POINTER, ghosts, PETSC_OWN_POINTER));
7753e72e933SJed Brown     PetscCall(PetscObjectSetName((PetscObject)sf, "SFC Point SF"));
7763e72e933SJed Brown     PetscCall(DMSetPointSF(dm, sf));
7773e72e933SJed Brown     PetscCall(PetscSFDestroy(&sf));
7783e72e933SJed Brown   }
7793e72e933SJed Brown   {
7803e72e933SJed Brown     Vec          coordinates;
7813e72e933SJed Brown     PetscScalar *coords;
7823e72e933SJed Brown     PetscSection coord_section;
7833e72e933SJed Brown     PetscInt     coord_size;
7843e72e933SJed Brown     PetscCall(DMGetCoordinateSection(dm, &coord_section));
7853e72e933SJed Brown     PetscCall(PetscSectionSetNumFields(coord_section, 1));
7863e72e933SJed Brown     PetscCall(PetscSectionSetFieldComponents(coord_section, 0, dim));
7873e72e933SJed Brown     PetscCall(PetscSectionSetChart(coord_section, local_elems, local_elems + local_verts));
7883e72e933SJed Brown     for (PetscInt v = 0; v < local_verts; v++) {
7893e72e933SJed Brown       PetscInt point = local_elems + v;
7903e72e933SJed Brown       PetscCall(PetscSectionSetDof(coord_section, point, dim));
7913e72e933SJed Brown       PetscCall(PetscSectionSetFieldDof(coord_section, point, 0, dim));
7923e72e933SJed Brown     }
7933e72e933SJed Brown     PetscCall(PetscSectionSetUp(coord_section));
7943e72e933SJed Brown     PetscCall(PetscSectionGetStorageSize(coord_section, &coord_size));
7953e72e933SJed Brown     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
7963e72e933SJed Brown     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
7973e72e933SJed Brown     PetscCall(VecSetSizes(coordinates, coord_size, PETSC_DETERMINE));
7983e72e933SJed Brown     PetscCall(VecSetBlockSize(coordinates, dim));
7993e72e933SJed Brown     PetscCall(VecSetType(coordinates, VECSTANDARD));
8003e72e933SJed Brown     PetscCall(VecGetArray(coordinates, &coords));
8013e72e933SJed Brown     for (PetscInt v = 0; v < local_verts; v++) {
8023e72e933SJed Brown       Ijk loc             = ZCodeSplit(vert_z[v]);
8033e72e933SJed Brown       coords[v * dim + 0] = lower[0] + loc.i * (upper[0] - lower[0]) / layout.eextent.i;
8043e72e933SJed Brown       if (dim > 1) coords[v * dim + 1] = lower[1] + loc.j * (upper[1] - lower[1]) / layout.eextent.j;
8053e72e933SJed Brown       if (dim > 2) coords[v * dim + 2] = lower[2] + loc.k * (upper[2] - lower[2]) / layout.eextent.k;
8063e72e933SJed Brown     }
8073e72e933SJed Brown     PetscCall(VecRestoreArray(coordinates, &coords));
8083e72e933SJed Brown     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
8093e72e933SJed Brown     PetscCall(VecDestroy(&coordinates));
8103e72e933SJed Brown   }
8113e72e933SJed Brown   if (interpolate) {
8123431e603SJed Brown     PetscCall(DMPlexInterpolateInPlace_Internal(dm));
8135f06a3ddSJed Brown     // It's currently necessary to orient the donor and periodic edges consistently. An easy way to ensure that is ot
8145f06a3ddSJed Brown     // give all edges positive orientation. Since vertices are created in Z-order, all ranks will agree about the
8155f06a3ddSJed Brown     // ordering cone[0] < cone[1]. This is overkill and it would be nice to remove this preparation and make
8165f06a3ddSJed Brown     // DMPlexCreateIsoperiodicClosureSF_Private() more resilient, so it fixes any inconsistent orientations. That might
8175f06a3ddSJed Brown     // be needed in a general CGNS reader, for example.
8185f06a3ddSJed Brown     PetscCall(DMPlexOrientPositiveEdges_Private(dm));
8193431e603SJed Brown 
8203431e603SJed Brown     DMLabel label;
8213431e603SJed Brown     PetscCall(DMCreateLabel(dm, "Face Sets"));
8223431e603SJed Brown     PetscCall(DMGetLabel(dm, "Face Sets", &label));
8234e2e9504SJed Brown     PetscSegBuffer per_faces, donor_face_closure, my_donor_faces;
8244e2e9504SJed Brown     PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &per_faces));
8254e2e9504SJed Brown     PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &my_donor_faces));
8264e2e9504SJed Brown     PetscCall(PetscSegBufferCreate(sizeof(ZCode), 64 * PetscPowInt(2, dim), &donor_face_closure));
8273431e603SJed Brown     PetscInt fStart, fEnd, vStart, vEnd;
8283431e603SJed Brown     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
8293431e603SJed Brown     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8303431e603SJed Brown     for (PetscInt f = fStart; f < fEnd; f++) {
8314e2e9504SJed Brown       PetscInt npoints, *points = NULL, num_fverts = 0, fverts[8];
8323431e603SJed Brown       PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points));
8333431e603SJed Brown       PetscInt bc_count[6] = {0};
8343431e603SJed Brown       for (PetscInt i = 0; i < npoints; i++) {
8353431e603SJed Brown         PetscInt p = points[2 * i];
8363431e603SJed Brown         if (p < vStart || vEnd <= p) continue;
8374e2e9504SJed Brown         fverts[num_fverts++] = p;
8383431e603SJed Brown         Ijk loc              = ZCodeSplit(vert_z[p - vStart]);
8393431e603SJed Brown         // Convention here matches DMPlexCreateCubeMesh_Internal
8403431e603SJed Brown         bc_count[0] += loc.i == 0;
8413431e603SJed Brown         bc_count[1] += loc.i == layout.vextent.i - 1;
8423431e603SJed Brown         bc_count[2] += loc.j == 0;
8433431e603SJed Brown         bc_count[3] += loc.j == layout.vextent.j - 1;
8443431e603SJed Brown         bc_count[4] += loc.k == 0;
8453431e603SJed Brown         bc_count[5] += loc.k == layout.vextent.k - 1;
8463e72e933SJed Brown       }
8473431e603SJed Brown       PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points));
8483431e603SJed Brown       for (PetscInt bc = 0, bc_match = 0; bc < 2 * dim; bc++) {
8493431e603SJed Brown         if (bc_count[bc] == PetscPowInt(2, dim - 1)) {
8506725e60dSJed Brown           PetscCall(DMPlexOrientPoint(dm, f, face_orient_dim[dim][bc]));
8514e2e9504SJed Brown           if (periodicity[bc / 2] == DM_BOUNDARY_PERIODIC) {
8524e2e9504SJed Brown             PetscInt *put;
8534e2e9504SJed Brown             if (bc % 2 == 0) { // donor face; no label
8544e2e9504SJed Brown               PetscCall(PetscSegBufferGet(my_donor_faces, 1, &put));
8554e2e9504SJed Brown               *put = f;
8564e2e9504SJed Brown             } else { // periodic face
8574e2e9504SJed Brown               PetscCall(PetscSegBufferGet(per_faces, 1, &put));
8584e2e9504SJed Brown               *put = f;
8594e2e9504SJed Brown               ZCode *zput;
8604e2e9504SJed Brown               PetscCall(PetscSegBufferGet(donor_face_closure, num_fverts, &zput));
8614e2e9504SJed Brown               for (PetscInt i = 0; i < num_fverts; i++) {
8624e2e9504SJed Brown                 Ijk loc = ZCodeSplit(vert_z[fverts[i] - vStart]);
8634e2e9504SJed Brown                 switch (bc / 2) {
8644e2e9504SJed Brown                 case 0:
8654e2e9504SJed Brown                   loc.i = 0;
8664e2e9504SJed Brown                   break;
8674e2e9504SJed Brown                 case 1:
8684e2e9504SJed Brown                   loc.j = 0;
8694e2e9504SJed Brown                   break;
8704e2e9504SJed Brown                 case 2:
8714e2e9504SJed Brown                   loc.k = 0;
8724e2e9504SJed Brown                   break;
8734e2e9504SJed Brown                 }
8744e2e9504SJed Brown                 *zput++ = ZEncode(loc);
8754e2e9504SJed Brown               }
8764e2e9504SJed Brown             }
8774e2e9504SJed Brown             continue;
8784e2e9504SJed Brown           }
8793431e603SJed Brown           PetscAssert(bc_match == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face matches multiple face sets");
8803431e603SJed Brown           PetscCall(DMLabelSetValue(label, f, face_marker_dim[dim][bc]));
8813431e603SJed Brown           bc_match++;
8823431e603SJed Brown         }
8833431e603SJed Brown       }
8843431e603SJed Brown     }
8853431e603SJed Brown     // Ensure that the Coordinate DM has our new boundary labels
8863431e603SJed Brown     DM cdm;
8873431e603SJed Brown     PetscCall(DMGetCoordinateDM(dm, &cdm));
8883431e603SJed Brown     PetscCall(DMCopyLabels(dm, cdm, PETSC_COPY_VALUES, PETSC_FALSE, DM_COPY_LABELS_FAIL));
8896725e60dSJed Brown     if (periodicity[0] == DM_BOUNDARY_PERIODIC || (dim > 1 && periodicity[1] == DM_BOUNDARY_PERIODIC) || (dim > 2 && periodicity[2] == DM_BOUNDARY_PERIODIC)) {
8905f06a3ddSJed Brown       PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(dm, &layout, vert_z, per_faces, lower, upper, periodicity, donor_face_closure, my_donor_faces));
8916725e60dSJed Brown     }
8926725e60dSJed Brown     PetscCall(PetscSegBufferDestroy(&per_faces));
8936725e60dSJed Brown     PetscCall(PetscSegBufferDestroy(&donor_face_closure));
8946725e60dSJed Brown     PetscCall(PetscSegBufferDestroy(&my_donor_faces));
8953431e603SJed Brown   }
8964e2e9504SJed Brown   PetscCall(PetscFree(layout.zstarts));
8973431e603SJed Brown   PetscCall(PetscFree(vert_z));
8983ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
8993e72e933SJed Brown }
9004e2e9504SJed Brown 
9014e2e9504SJed Brown /*@
9025f06a3ddSJed Brown   DMPlexSetIsoperiodicFaceSF - Express periodicity from an existing mesh
9034e2e9504SJed Brown 
90420f4b53cSBarry Smith   Logically Collective
9054e2e9504SJed Brown 
9064e2e9504SJed Brown   Input Parameters:
9074e2e9504SJed Brown + dm      - The `DMPLEX` on which to set periodicity
90853c0d4aeSBarry Smith - face_sf - `PetscSF` in which roots are (owned) donor faces and leaves are faces that must be matched to a (possibly remote) donor face.
9094e2e9504SJed Brown 
9104e2e9504SJed Brown   Level: advanced
9114e2e9504SJed Brown 
91253c0d4aeSBarry Smith   Note:
9135dca41c3SJed Brown   One can use `-dm_plex_shape zbox` to use this mode of periodicity, wherein the periodic points are distinct both globally
9144e2e9504SJed Brown   and locally, but are paired when creating a global dof space.
9154e2e9504SJed Brown 
9161cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexGetIsoperiodicFaceSF()`
9174e2e9504SJed Brown @*/
9185f06a3ddSJed Brown PetscErrorCode DMPlexSetIsoperiodicFaceSF(DM dm, PetscSF face_sf)
9194e2e9504SJed Brown {
9204e2e9504SJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
921*4d86920dSPierre Jolivet 
9224e2e9504SJed Brown   PetscFunctionBegin;
9234e2e9504SJed Brown   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9244e2e9504SJed Brown   PetscCall(PetscObjectReference((PetscObject)face_sf));
9254e2e9504SJed Brown   PetscCall(PetscSFDestroy(&plex->periodic.face_sf));
9264e2e9504SJed Brown   plex->periodic.face_sf = face_sf;
9275f06a3ddSJed Brown   if (face_sf) PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex));
9285f06a3ddSJed Brown 
9295f06a3ddSJed Brown   DM cdm = dm->coordinates[0].dm; // Can't DMGetCoordinateDM because it automatically creates one
9305f06a3ddSJed Brown   if (cdm) {
9315f06a3ddSJed Brown     PetscCall(DMPlexSetIsoperiodicFaceSF(cdm, face_sf));
9325f06a3ddSJed Brown     if (face_sf) cdm->periodic.setup = DMPeriodicCoordinateSetUp_Internal;
9335f06a3ddSJed Brown   }
9343ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
9354e2e9504SJed Brown }
9364e2e9504SJed Brown 
9374e2e9504SJed Brown /*@
9385f06a3ddSJed Brown   DMPlexGetIsoperiodicFaceSF - Obtain periodicity for a mesh
9394e2e9504SJed Brown 
94020f4b53cSBarry Smith   Logically Collective
9414e2e9504SJed Brown 
94220f4b53cSBarry Smith   Input Parameter:
9434e2e9504SJed Brown . dm - The `DMPLEX` for which to obtain periodic relation
9444e2e9504SJed Brown 
94520f4b53cSBarry Smith   Output Parameter:
94620f4b53cSBarry Smith . face_sf - `PetscSF` in which roots are (owned) donor faces and leaves are faces that must be matched to a (possibly remote) donor face.
9474e2e9504SJed Brown 
9484e2e9504SJed Brown   Level: advanced
9494e2e9504SJed Brown 
9501cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()`
9514e2e9504SJed Brown @*/
9525f06a3ddSJed Brown PetscErrorCode DMPlexGetIsoperiodicFaceSF(DM dm, PetscSF *face_sf)
9534e2e9504SJed Brown {
9544e2e9504SJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
955*4d86920dSPierre Jolivet 
9564e2e9504SJed Brown   PetscFunctionBegin;
9574e2e9504SJed Brown   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9584e2e9504SJed Brown   *face_sf = plex->periodic.face_sf;
9593ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
9604e2e9504SJed Brown }
9616725e60dSJed Brown 
9625f06a3ddSJed Brown /*@C
9635f06a3ddSJed Brown   DMPlexSetIsoperiodicFaceTransform - set geometric transform from donor to periodic points
9646725e60dSJed Brown 
9656725e60dSJed Brown   Logically Collective
9666725e60dSJed Brown 
96760225df5SJacob Faibussowitsch   Input Parameters:
96853c0d4aeSBarry Smith + dm - `DMPLEX` that has been configured with `DMPlexSetIsoperiodicFaceSF()`
9696725e60dSJed Brown - t  - 4x4 affine transformation basis.
9706725e60dSJed Brown 
97153c0d4aeSBarry Smith   Level: advanced
97253c0d4aeSBarry Smith 
9735f06a3ddSJed Brown   Notes:
9745f06a3ddSJed Brown   Affine transforms are 4x4 matrices in which the leading 3x3 block expresses a rotation (or identity for no rotation),
9755f06a3ddSJed Brown   the last column contains a translation vector, and the bottom row is all zero except the last entry, which must always
9765f06a3ddSJed Brown   be 1. This representation is common in geometric modeling and allows affine transformations to be composed using
977aaa8cc7dSPierre Jolivet   simple matrix multiplication.
9785f06a3ddSJed Brown 
9795f06a3ddSJed Brown   Although the interface accepts a general affine transform, only affine translation is supported at present.
9805f06a3ddSJed Brown 
98160225df5SJacob Faibussowitsch   Developer Notes:
9825f06a3ddSJed Brown   This interface should be replaced by making BasisTransform public, expanding it to support affine representations, and
9835f06a3ddSJed Brown   adding GPU implementations to apply the G2L/L2G transforms.
98453c0d4aeSBarry Smith 
9851cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()`
9866725e60dSJed Brown @*/
9875f06a3ddSJed Brown PetscErrorCode DMPlexSetIsoperiodicFaceTransform(DM dm, const PetscScalar t[])
9886725e60dSJed Brown {
9896725e60dSJed Brown   DM_Plex *plex = (DM_Plex *)dm->data;
990*4d86920dSPierre Jolivet 
9916725e60dSJed Brown   PetscFunctionBegin;
9926725e60dSJed Brown   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9936725e60dSJed Brown   for (PetscInt i = 0; i < 4; i++) {
9946725e60dSJed Brown     for (PetscInt j = 0; j < 4; j++) {
9955f06a3ddSJed Brown       PetscCheck(i != j || t[i * 4 + j] == 1., PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Rotated transforms not supported");
9965f06a3ddSJed Brown       plex->periodic.transform[i][j] = t[i * 4 + j];
9976725e60dSJed Brown     }
9986725e60dSJed Brown   }
9993ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
10006725e60dSJed Brown }
1001