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.
22bfe80ac4SPierre Jolivet // This is known as Morton encoding, and is referred 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)
ZCodeSplit1(ZCode z)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]
ZEncode1(unsigned t)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
ZCodeSplit(ZCode z)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
ZEncode(Ijk c)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
IjkActive(Ijk extent,Ijk l)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.
ZStepOct(ZCode z)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.
ZLayoutCreate(PetscMPIInt size,const PetscInt eextent[3],const PetscInt vextent[3],ZLayout * layout)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
ZLayoutElementsOnRank(const ZLayout * layout,PetscMPIInt rank)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
ZCodeFind(ZCode key,PetscInt n,const ZCode X[])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
IsPointInsideStratum(PetscInt point,PetscInt pStart,PetscInt pEnd)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
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])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.
DMCoordAddPeriodicOffsets_Private(DM dm,Vec g,InsertMode mode,Vec l,PetscCtx ctx)339*2a8381b2SBarry Smith static PetscErrorCode DMCoordAddPeriodicOffsets_Private(DM dm, Vec g, InsertMode mode, Vec l, PetscCtx 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
35092a26154SJames Wright // Modify index array based on the transformation of `point` for the given section and field
35192a26154SJames Wright // Used for correcting the sfNatural based on point reorientation
DMPlexOrientFieldPointIndex(DM dm,PetscSection section,PetscInt field,PetscInt array_size,PetscInt array[],PetscInt point,PetscInt orientation)35292a26154SJames Wright static PetscErrorCode DMPlexOrientFieldPointIndex(DM dm, PetscSection section, PetscInt field, PetscInt array_size, PetscInt array[], PetscInt point, PetscInt orientation)
35392a26154SJames Wright {
35492a26154SJames Wright PetscInt *copy;
35592a26154SJames Wright PetscInt dof, off, point_ornt[2] = {point, orientation};
35692a26154SJames Wright const PetscInt **perms;
35792a26154SJames Wright
35892a26154SJames Wright PetscFunctionBeginUser;
35991fc4ba7SJames Wright PetscCall(PetscSectionGetFieldPointSyms(section, field, 1, point_ornt, &perms, NULL));
36091fc4ba7SJames Wright if (!perms) PetscFunctionReturn(PETSC_SUCCESS); // section may not have symmetries, such as Q2 finite elements
36192a26154SJames Wright PetscCall(PetscSectionGetDof(section, point, &dof));
36292a26154SJames Wright PetscCall(PetscSectionGetOffset(section, point, &off));
36392a26154SJames Wright PetscCheck(off + dof <= array_size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section indices exceed index array bounds");
36492a26154SJames Wright PetscCall(DMGetWorkArray(dm, dof, MPIU_INT, ©));
36592a26154SJames Wright PetscArraycpy(copy, &array[off], dof);
36692a26154SJames Wright
36792a26154SJames Wright for (PetscInt i = 0; i < dof; i++) {
36892a26154SJames Wright if (perms[0]) array[off + perms[0][i]] = copy[i];
36992a26154SJames Wright }
37092a26154SJames Wright
37192a26154SJames Wright PetscCall(PetscSectionRestoreFieldPointSyms(section, field, 1, point_ornt, &perms, NULL));
37292a26154SJames Wright PetscCall(DMRestoreWorkArray(dm, dof, MPIU_INT, ©));
37392a26154SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
37492a26154SJames Wright }
37592a26154SJames Wright
37653b735f8SJames Wright // Modify Vec based on the transformation of `point` for the given section and field
DMPlexOrientFieldPointVec(DM dm,PetscSection section,PetscInt field,Vec V,PetscInt point,PetscInt orientation)37753b735f8SJames Wright static PetscErrorCode DMPlexOrientFieldPointVec(DM dm, PetscSection section, PetscInt field, Vec V, PetscInt point, PetscInt orientation)
37853b735f8SJames Wright {
37953b735f8SJames Wright PetscScalar *copy, *V_arr;
38053b735f8SJames Wright PetscInt dof, off, point_ornt[2] = {point, orientation};
38153b735f8SJames Wright const PetscInt **perms;
38253b735f8SJames Wright const PetscScalar **rots;
38353b735f8SJames Wright
38453b735f8SJames Wright PetscFunctionBeginUser;
38591fc4ba7SJames Wright PetscCall(PetscSectionGetFieldPointSyms(section, field, 1, point_ornt, &perms, &rots));
38691fc4ba7SJames Wright if (!perms) PetscFunctionReturn(PETSC_SUCCESS); // section may not have symmetries, such as Q2 finite elements
38753b735f8SJames Wright PetscCall(PetscSectionGetDof(section, point, &dof));
38853b735f8SJames Wright PetscCall(PetscSectionGetOffset(section, point, &off));
38953b735f8SJames Wright PetscCall(VecGetArray(V, &V_arr));
39053b735f8SJames Wright PetscCall(DMGetWorkArray(dm, dof, MPIU_SCALAR, ©));
39153b735f8SJames Wright PetscArraycpy(copy, &V_arr[off], dof);
39253b735f8SJames Wright
39353b735f8SJames Wright for (PetscInt i = 0; i < dof; i++) {
39453b735f8SJames Wright if (perms[0]) V_arr[off + perms[0][i]] = copy[i];
39553b735f8SJames Wright if (rots[0]) V_arr[off + perms[0][i]] *= rots[0][i];
39653b735f8SJames Wright }
39753b735f8SJames Wright
39853b735f8SJames Wright PetscCall(PetscSectionRestoreFieldPointSyms(section, field, 1, point_ornt, &perms, &rots));
39953b735f8SJames Wright PetscCall(DMRestoreWorkArray(dm, dof, MPIU_SCALAR, ©));
40053b735f8SJames Wright PetscCall(VecRestoreArray(V, &V_arr));
40153b735f8SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
40253b735f8SJames Wright }
40353b735f8SJames Wright
40453b735f8SJames Wright // Reorient the point in the DMPlex while also applying necessary corrections to other structures (e.g. coordinates)
DMPlexOrientPointWithCorrections(DM dm,PetscInt point,PetscInt ornt,PetscSection perm_section,PetscInt perm_length,PetscInt perm[])40592a26154SJames Wright static PetscErrorCode DMPlexOrientPointWithCorrections(DM dm, PetscInt point, PetscInt ornt, PetscSection perm_section, PetscInt perm_length, PetscInt perm[])
40653b735f8SJames Wright {
40753b735f8SJames Wright // TODO: Potential speed up if we early exit for ornt == 0 (i.e. if ornt is identity, we don't need to do anything)
40853b735f8SJames Wright PetscFunctionBeginUser;
40953b735f8SJames Wright PetscCall(DMPlexOrientPoint(dm, point, ornt));
41053b735f8SJames Wright
41153b735f8SJames Wright { // Correct coordinates based on new cone ordering
41253b735f8SJames Wright DM cdm;
41353b735f8SJames Wright PetscSection csection;
41453b735f8SJames Wright Vec coordinates;
41553b735f8SJames Wright PetscInt pStart, pEnd;
41653b735f8SJames Wright
41753b735f8SJames Wright PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
41853b735f8SJames Wright PetscCall(DMGetCoordinateDM(dm, &cdm));
41953b735f8SJames Wright PetscCall(DMGetLocalSection(cdm, &csection));
42053b735f8SJames Wright PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
42153b735f8SJames Wright if (IsPointInsideStratum(point, pStart, pEnd)) PetscCall(DMPlexOrientFieldPointVec(cdm, csection, 0, coordinates, point, ornt));
42253b735f8SJames Wright }
42392a26154SJames Wright
42492a26154SJames Wright if (perm_section) {
42592a26154SJames Wright PetscInt num_fields;
42692a26154SJames Wright PetscCall(PetscSectionGetNumFields(perm_section, &num_fields));
42792a26154SJames Wright for (PetscInt f = 0; f < num_fields; f++) PetscCall(DMPlexOrientFieldPointIndex(dm, perm_section, f, perm_length, perm, point, ornt));
42892a26154SJames Wright }
42953b735f8SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
43053b735f8SJames Wright }
43153b735f8SJames Wright
43293defd67SJames 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[]`
CreateDonorToPeriodicSF(DM dm,PetscSF face_sf,PetscInt pStart,PetscInt pEnd,const PetscInt point_sizes[],PetscInt * rootbuffersize,PetscInt * leafbuffersize,PetscBT * rootbt,PetscSF * sf_closure)43353b735f8SJames Wright static PetscErrorCode CreateDonorToPeriodicSF(DM dm, PetscSF face_sf, PetscInt pStart, PetscInt pEnd, const PetscInt point_sizes[], PetscInt *rootbuffersize, PetscInt *leafbuffersize, PetscBT *rootbt, PetscSF *sf_closure)
43493defd67SJames Wright {
43593defd67SJames Wright MPI_Comm comm;
43693defd67SJames Wright PetscMPIInt rank;
43793defd67SJames Wright PetscInt nroots, nleaves;
43893defd67SJames Wright PetscInt *rootdata, *leafdata;
43993defd67SJames Wright const PetscInt *filocal;
44093defd67SJames Wright const PetscSFNode *firemote;
44193defd67SJames Wright
44293defd67SJames Wright PetscFunctionBeginUser;
44393defd67SJames Wright PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
44493defd67SJames Wright PetscCallMPI(MPI_Comm_rank(comm, &rank));
44593defd67SJames Wright
44693defd67SJames Wright PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote));
44793defd67SJames Wright PetscCall(PetscCalloc2(2 * nroots, &rootdata, 2 * nroots, &leafdata));
44893defd67SJames Wright for (PetscInt i = 0; i < nleaves; i++) {
44993defd67SJames Wright PetscInt point = filocal[i];
45085de0153SJames 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);
45193defd67SJames Wright leafdata[point] = point_sizes[point - pStart];
45293defd67SJames Wright }
45393defd67SJames Wright PetscCall(PetscSFReduceBegin(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM));
45493defd67SJames Wright PetscCall(PetscSFReduceEnd(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM));
45593defd67SJames Wright
45693defd67SJames Wright PetscInt root_offset = 0;
45793defd67SJames Wright PetscCall(PetscBTCreate(nroots, rootbt));
45893defd67SJames Wright for (PetscInt p = 0; p < nroots; p++) {
45993defd67SJames Wright const PetscInt *donor_dof = rootdata + nroots;
46093defd67SJames Wright if (donor_dof[p] == 0) {
46193defd67SJames Wright rootdata[2 * p] = -1;
46293defd67SJames Wright rootdata[2 * p + 1] = -1;
46393defd67SJames Wright continue;
46493defd67SJames Wright }
46593defd67SJames Wright PetscCall(PetscBTSet(*rootbt, p));
46685de0153SJames 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);
46793defd67SJames Wright PetscInt p_size = point_sizes[p - pStart];
46893defd67SJames 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);
46993defd67SJames Wright rootdata[2 * p] = root_offset;
47093defd67SJames Wright rootdata[2 * p + 1] = p_size;
47193defd67SJames Wright root_offset += p_size;
47293defd67SJames Wright }
47393defd67SJames Wright PetscCall(PetscSFBcastBegin(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
47493defd67SJames Wright PetscCall(PetscSFBcastEnd(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE));
47593defd67SJames Wright // Count how many leaves we need to communicate the closures
47693defd67SJames Wright PetscInt leaf_offset = 0;
47793defd67SJames Wright for (PetscInt i = 0; i < nleaves; i++) {
47893defd67SJames Wright PetscInt point = filocal[i];
47993defd67SJames Wright if (leafdata[2 * point + 1] < 0) continue;
48093defd67SJames Wright leaf_offset += leafdata[2 * point + 1];
48193defd67SJames Wright }
48293defd67SJames Wright
48393defd67SJames Wright PetscSFNode *closure_leaf;
48493defd67SJames Wright PetscCall(PetscMalloc1(leaf_offset, &closure_leaf));
48593defd67SJames Wright leaf_offset = 0;
48693defd67SJames Wright for (PetscInt i = 0; i < nleaves; i++) {
48793defd67SJames Wright PetscInt point = filocal[i];
48893defd67SJames Wright PetscInt cl_size = leafdata[2 * point + 1];
48993defd67SJames Wright if (cl_size < 0) continue;
49093defd67SJames Wright for (PetscInt j = 0; j < cl_size; j++) {
49193defd67SJames Wright closure_leaf[leaf_offset].rank = firemote[i].rank;
49293defd67SJames Wright closure_leaf[leaf_offset].index = leafdata[2 * point] + j;
49393defd67SJames Wright leaf_offset++;
49493defd67SJames Wright }
49593defd67SJames Wright }
49693defd67SJames Wright
49793defd67SJames Wright PetscCall(PetscSFCreate(comm, sf_closure));
49893defd67SJames Wright PetscCall(PetscSFSetGraph(*sf_closure, root_offset, leaf_offset, NULL, PETSC_USE_POINTER, closure_leaf, PETSC_OWN_POINTER));
49993defd67SJames Wright *rootbuffersize = root_offset;
50093defd67SJames Wright *leafbuffersize = leaf_offset;
50193defd67SJames Wright PetscCall(PetscFree2(rootdata, leafdata));
50293defd67SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
50393defd67SJames Wright }
50493defd67SJames Wright
50553b735f8SJames Wright // Determine if `key` is in `array`. `array` does NOT need to be sorted
SearchIntArray(PetscInt key,PetscInt array_size,const PetscInt array[])50653b735f8SJames Wright static inline PetscBool SearchIntArray(PetscInt key, PetscInt array_size, const PetscInt array[])
50753b735f8SJames Wright {
50853b735f8SJames Wright for (PetscInt i = 0; i < array_size; i++)
50953b735f8SJames Wright if (array[i] == key) return PETSC_TRUE;
51053b735f8SJames Wright return PETSC_FALSE;
51153b735f8SJames Wright }
51253b735f8SJames Wright
51353b735f8SJames Wright // Translate a cone in periodic points to the cone in donor points based on the `periodic2donor` array
TranslateConeP2D(const PetscInt periodic_cone[],PetscInt cone_size,const PetscInt periodic2donor[],PetscInt p2d_count,PetscInt p2d_cone[])51453b735f8SJames Wright static inline PetscErrorCode TranslateConeP2D(const PetscInt periodic_cone[], PetscInt cone_size, const PetscInt periodic2donor[], PetscInt p2d_count, PetscInt p2d_cone[])
51553b735f8SJames Wright {
51653b735f8SJames Wright PetscFunctionBeginUser;
51753b735f8SJames Wright for (PetscInt p = 0; p < cone_size; p++) {
51853b735f8SJames Wright PetscInt p2d_index = -1;
51953b735f8SJames Wright for (PetscInt p2d = 0; p2d < p2d_count; p2d++) {
52053b735f8SJames Wright if (periodic2donor[p2d * 2] == periodic_cone[p]) p2d_index = p2d;
52153b735f8SJames Wright }
52253b735f8SJames Wright PetscCheck(p2d_index >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find periodic point in periodic-to-donor array");
52353b735f8SJames Wright p2d_cone[p] = periodic2donor[2 * p2d_index + 1];
52453b735f8SJames Wright }
52553b735f8SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
52653b735f8SJames Wright }
52753b735f8SJames Wright
52853b735f8SJames Wright // Corrects the cone order of periodic faces (and their transitive closure's cones) to match their donor face pair.
52953b735f8SJames Wright //
53053b735f8SJames Wright // This is done by:
53153b735f8SJames Wright // 1. Communicating the donor's vertex coordinates and recursive cones (i.e. its own cone and those of it's constituent edges) to it's periodic pairs
53253b735f8SJames Wright // - The donor vertices have the isoperiodic transform applied to them such that they should match exactly
53353b735f8SJames Wright // 2. Translating the periodic vertices into the donor vertices point IDs
53453b735f8SJames Wright // 3. Translating the cone of each periodic point into the donor point IDs
53553b735f8SJames Wright // 4. Comparing the periodic-to-donor cone to the donor cone for each point
53653b735f8SJames Wright // 5. Apply the necessary transformation to the periodic cone to make it match the donor cone
DMPlexCorrectOrientationForIsoperiodic(DM dm)53753b735f8SJames Wright static PetscErrorCode DMPlexCorrectOrientationForIsoperiodic(DM dm)
53853b735f8SJames Wright {
53953b735f8SJames Wright MPI_Comm comm;
54053b735f8SJames Wright DM_Plex *plex = (DM_Plex *)dm->data;
54153b735f8SJames Wright PetscInt nroots, nleaves;
54292a26154SJames Wright PetscInt *local_vec_perm = NULL, local_vec_length = 0, *global_vec_perm = NULL, global_vec_length = 0;
54392a26154SJames Wright const PetscInt *filocal, coords_field_id = 0;
54453b735f8SJames Wright DM cdm;
54592a26154SJames Wright PetscSection csection, localSection = NULL;
54692a26154SJames Wright PetscSF sfNatural_old = NULL;
54753b735f8SJames Wright Vec coordinates;
54892a26154SJames Wright PetscMPIInt myrank;
54953b735f8SJames Wright PetscBool debug_printing = PETSC_FALSE;
55053b735f8SJames Wright
55153b735f8SJames Wright PetscFunctionBeginUser;
55253b735f8SJames Wright PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
55392a26154SJames Wright PetscCallMPI(MPI_Comm_rank(comm, &myrank));
55453b735f8SJames Wright PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
55553b735f8SJames Wright PetscCheck(coordinates, comm, PETSC_ERR_ARG_WRONGSTATE, "DM must have coordinates to setup isoperiodic");
55653b735f8SJames Wright PetscCall(DMGetCoordinateDM(dm, &cdm));
55753b735f8SJames Wright PetscCall(DMGetLocalSection(cdm, &csection));
55853b735f8SJames Wright
55992a26154SJames Wright PetscCall(DMGetNaturalSF(dm, &sfNatural_old));
56092a26154SJames Wright // Prep data for naturalSF correction
56192a26154SJames Wright if (plex->periodic.num_face_sfs > 0 && sfNatural_old) {
56292a26154SJames Wright PetscSection globalSection;
56392a26154SJames Wright PetscSF pointSF, sectionSF;
56492a26154SJames Wright PetscInt nleaves;
56592a26154SJames Wright const PetscInt *ilocal;
56692a26154SJames Wright const PetscSFNode *iremote;
56792a26154SJames Wright
56892a26154SJames Wright // Create global section with just pointSF and including constraints
56992a26154SJames Wright PetscCall(DMGetLocalSection(dm, &localSection));
57092a26154SJames Wright PetscCall(DMGetPointSF(dm, &pointSF));
57192a26154SJames Wright PetscCall(PetscSectionCreateGlobalSection(localSection, pointSF, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &globalSection));
57292a26154SJames Wright
57392a26154SJames Wright // Set local_vec_perm to be negative values when that dof is not owned by the current rank
57492a26154SJames Wright // Dofs that are owned are set to their corresponding global Vec index
57592a26154SJames Wright PetscCall(PetscSectionGetStorageSize(globalSection, &global_vec_length));
57692a26154SJames Wright PetscCall(PetscSectionGetStorageSize(localSection, &local_vec_length));
57792a26154SJames Wright PetscCall(PetscMalloc2(global_vec_length, &global_vec_perm, local_vec_length, &local_vec_perm));
57892a26154SJames Wright for (PetscInt i = 0; i < global_vec_length; i++) global_vec_perm[i] = i;
57992a26154SJames Wright for (PetscInt i = 0; i < local_vec_length; i++) local_vec_perm[i] = -(i + 1);
58092a26154SJames Wright
58192a26154SJames Wright PetscCall(PetscSFCreate(comm, §ionSF));
58292a26154SJames Wright PetscCall(PetscSFSetGraphSection(sectionSF, localSection, globalSection));
58392a26154SJames Wright PetscCall(PetscSFGetGraph(sectionSF, NULL, &nleaves, &ilocal, &iremote));
58492a26154SJames Wright for (PetscInt l = 0; l < nleaves; l++) {
58592a26154SJames Wright if (iremote[l].rank != myrank) continue;
58692a26154SJames Wright PetscInt local_index = ilocal ? ilocal[l] : l;
58792a26154SJames Wright local_vec_perm[local_index] = global_vec_perm[iremote[l].index];
58892a26154SJames Wright }
58992a26154SJames Wright PetscCall(PetscSectionDestroy(&globalSection));
59092a26154SJames Wright PetscCall(PetscSFDestroy(§ionSF));
59192a26154SJames Wright }
59292a26154SJames Wright
59392a26154SJames Wright // Create sf_vert_coords and sf_face_cones for communicating donor vertices and cones to periodic faces, respectively
59453b735f8SJames Wright for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) {
59553b735f8SJames Wright PetscSF face_sf = plex->periodic.face_sfs[f];
59653b735f8SJames Wright const PetscScalar (*transform)[4] = (const PetscScalar (*)[4])plex->periodic.transform[f];
59753b735f8SJames Wright PetscInt *face_vertices_size, *face_cones_size;
59853b735f8SJames Wright PetscInt fStart, fEnd, vStart, vEnd, rootnumvert, leafnumvert, rootconesize, leafconesize, dim;
59953b735f8SJames Wright PetscSF sf_vert_coords, sf_face_cones;
60053b735f8SJames Wright PetscBT rootbt;
60153b735f8SJames Wright
60253b735f8SJames Wright PetscCall(DMGetCoordinateDim(dm, &dim));
60353b735f8SJames Wright PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
60453b735f8SJames Wright PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
60553b735f8SJames Wright PetscCall(PetscCalloc2(fEnd - fStart, &face_vertices_size, fEnd - fStart, &face_cones_size));
60653b735f8SJames Wright
60753b735f8SJames Wright // Create SFs to communicate donor vertices and donor cones to periodic faces
60853b735f8SJames Wright for (PetscInt f = fStart, index = 0; f < fEnd; f++, index++) {
60953b735f8SJames Wright PetscInt cl_size, *closure = NULL, num_vertices = 0;
61053b735f8SJames Wright PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &cl_size, &closure));
61153b735f8SJames Wright for (PetscInt p = 0; p < cl_size; p++) {
61253b735f8SJames Wright PetscInt cl_point = closure[2 * p];
61353b735f8SJames Wright if (IsPointInsideStratum(cl_point, vStart, vEnd)) num_vertices++;
61453b735f8SJames Wright else {
61553b735f8SJames Wright PetscInt cone_size;
61653b735f8SJames Wright PetscCall(DMPlexGetConeSize(dm, cl_point, &cone_size));
61753b735f8SJames Wright face_cones_size[index] += cone_size + 2;
61853b735f8SJames Wright }
61953b735f8SJames Wright }
62053b735f8SJames Wright face_vertices_size[index] = num_vertices;
62153b735f8SJames Wright face_cones_size[index] += num_vertices;
62253b735f8SJames Wright PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &cl_size, &closure));
62353b735f8SJames Wright }
62453b735f8SJames Wright PetscCall(CreateDonorToPeriodicSF(dm, face_sf, fStart, fEnd, face_vertices_size, &rootnumvert, &leafnumvert, &rootbt, &sf_vert_coords));
62553b735f8SJames Wright PetscCall(PetscBTDestroy(&rootbt));
62653b735f8SJames Wright PetscCall(CreateDonorToPeriodicSF(dm, face_sf, fStart, fEnd, face_cones_size, &rootconesize, &leafconesize, &rootbt, &sf_face_cones));
62753b735f8SJames Wright
62853b735f8SJames Wright PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, NULL));
62953b735f8SJames Wright
63053b735f8SJames Wright PetscReal *leaf_donor_coords;
63153b735f8SJames Wright PetscInt *leaf_donor_cones;
63253b735f8SJames Wright
63353b735f8SJames Wright { // Communicate donor coords and cones to the periodic faces
63453b735f8SJames Wright PetscReal *mydonor_vertices;
63553b735f8SJames Wright PetscInt *mydonor_cones;
63653b735f8SJames Wright const PetscScalar *coords_arr;
63753b735f8SJames Wright
63853b735f8SJames Wright PetscCall(PetscCalloc2(rootnumvert * dim, &mydonor_vertices, rootconesize, &mydonor_cones));
63953b735f8SJames Wright PetscCall(VecGetArrayRead(coordinates, &coords_arr));
64053b735f8SJames Wright for (PetscInt donor_face = 0, donor_vert_offset = 0, donor_cone_offset = 0; donor_face < nroots; donor_face++) {
64153b735f8SJames Wright if (!PetscBTLookup(rootbt, donor_face)) continue;
64253b735f8SJames Wright PetscInt cl_size, *closure = NULL;
64353b735f8SJames Wright
64453b735f8SJames Wright PetscCall(DMPlexGetTransitiveClosure(dm, donor_face, PETSC_TRUE, &cl_size, &closure));
64553b735f8SJames Wright // Pack vertex coordinates
64653b735f8SJames Wright for (PetscInt p = 0; p < cl_size; p++) {
64753b735f8SJames Wright PetscInt cl_point = closure[2 * p], dof, offset;
64853b735f8SJames Wright if (!IsPointInsideStratum(cl_point, vStart, vEnd)) continue;
64953b735f8SJames Wright mydonor_cones[donor_cone_offset++] = cl_point;
65053b735f8SJames Wright PetscCall(PetscSectionGetFieldDof(csection, cl_point, coords_field_id, &dof));
65153b735f8SJames Wright PetscCall(PetscSectionGetFieldOffset(csection, cl_point, coords_field_id, &offset));
65253b735f8SJames Wright PetscAssert(dof == dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " has dof size %" PetscInt_FMT ", but should match dimension size %" PetscInt_FMT, cl_point, dof, dim);
65353b735f8SJames Wright // Apply isoperiodic transform to donor vertices such that corresponding periodic vertices should match exactly
65453b735f8SJames Wright for (PetscInt d = 0; d < dof; d++) mydonor_vertices[donor_vert_offset * dim + d] = PetscRealPart(coords_arr[offset + d]) + PetscRealPart(transform[d][3]);
65553b735f8SJames Wright donor_vert_offset++;
65653b735f8SJames Wright }
65753b735f8SJames Wright // Pack cones of face points (including face itself)
65853b735f8SJames Wright for (PetscInt p = 0; p < cl_size; p++) {
65953b735f8SJames Wright PetscInt cl_point = closure[2 * p], cone_size, depth;
66053b735f8SJames Wright const PetscInt *cone;
66153b735f8SJames Wright
66253b735f8SJames Wright PetscCall(DMPlexGetConeSize(dm, cl_point, &cone_size));
66353b735f8SJames Wright PetscCall(DMPlexGetCone(dm, cl_point, &cone));
66453b735f8SJames Wright PetscCall(DMPlexGetPointDepth(dm, cl_point, &depth));
66553b735f8SJames Wright if (depth == 0) continue; // don't include vertex depth
66653b735f8SJames Wright mydonor_cones[donor_cone_offset++] = cone_size;
66753b735f8SJames Wright mydonor_cones[donor_cone_offset++] = cl_point;
66853b735f8SJames Wright PetscArraycpy(&mydonor_cones[donor_cone_offset], cone, cone_size);
66953b735f8SJames Wright donor_cone_offset += cone_size;
67053b735f8SJames Wright }
67153b735f8SJames Wright PetscCall(DMPlexRestoreTransitiveClosure(dm, donor_face, PETSC_TRUE, &cl_size, &closure));
67253b735f8SJames Wright }
67353b735f8SJames Wright PetscCall(VecRestoreArrayRead(coordinates, &coords_arr));
67453b735f8SJames Wright PetscCall(PetscBTDestroy(&rootbt));
67553b735f8SJames Wright
67653b735f8SJames Wright MPI_Datatype vertex_unit;
677f370e7f7SJose E. Roman PetscMPIInt n;
678f370e7f7SJose E. Roman PetscCall(PetscMPIIntCast(dim, &n));
679f370e7f7SJose E. Roman PetscCallMPI(MPI_Type_contiguous(n, MPIU_REAL, &vertex_unit));
68053b735f8SJames Wright PetscCallMPI(MPI_Type_commit(&vertex_unit));
68153b735f8SJames Wright PetscCall(PetscMalloc2(leafnumvert * 3, &leaf_donor_coords, leafconesize, &leaf_donor_cones));
68253b735f8SJames Wright PetscCall(PetscSFBcastBegin(sf_vert_coords, vertex_unit, mydonor_vertices, leaf_donor_coords, MPI_REPLACE));
68353b735f8SJames Wright PetscCall(PetscSFBcastBegin(sf_face_cones, MPIU_INT, mydonor_cones, leaf_donor_cones, MPI_REPLACE));
68453b735f8SJames Wright PetscCall(PetscSFBcastEnd(sf_vert_coords, vertex_unit, mydonor_vertices, leaf_donor_coords, MPI_REPLACE));
68553b735f8SJames Wright PetscCall(PetscSFBcastEnd(sf_face_cones, MPIU_INT, mydonor_cones, leaf_donor_cones, MPI_REPLACE));
68653b735f8SJames Wright PetscCall(PetscSFDestroy(&sf_vert_coords));
68753b735f8SJames Wright PetscCall(PetscSFDestroy(&sf_face_cones));
68853b735f8SJames Wright PetscCallMPI(MPI_Type_free(&vertex_unit));
68953b735f8SJames Wright PetscCall(PetscFree2(mydonor_vertices, mydonor_cones));
69053b735f8SJames Wright }
69153b735f8SJames Wright
69253b735f8SJames Wright { // Determine periodic orientation w/rt donor vertices and reorient
69353b735f8SJames Wright PetscReal tol = PetscSqr(PETSC_MACHINE_EPSILON * 1e3);
69453b735f8SJames Wright PetscInt *periodic2donor, dm_depth, maxConeSize;
69553b735f8SJames Wright PetscInt coords_offset = 0, cones_offset = 0;
69653b735f8SJames Wright
69753b735f8SJames Wright PetscCall(DMPlexGetDepth(dm, &dm_depth));
69853b735f8SJames Wright PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, NULL));
69953b735f8SJames Wright PetscCall(DMGetWorkArray(dm, 2 * PetscPowInt(maxConeSize, dm_depth - 1), MPIU_INT, &periodic2donor));
70053b735f8SJames Wright
70153b735f8SJames Wright // Translate the periodic face vertices into the donor vertices
70253b735f8SJames Wright // Translation stored in periodic2donor
70353b735f8SJames Wright for (PetscInt i = 0; i < nleaves; i++) {
70453b735f8SJames Wright PetscInt periodic_face = filocal[i], cl_size, num_verts = face_vertices_size[periodic_face - fStart];
70553b735f8SJames Wright PetscInt cones_size = face_cones_size[periodic_face - fStart], p2d_count = 0;
70653b735f8SJames Wright PetscInt *closure = NULL;
70753b735f8SJames Wright
70853b735f8SJames Wright PetscCall(DMPlexGetTransitiveClosure(dm, periodic_face, PETSC_TRUE, &cl_size, &closure));
70953b735f8SJames Wright for (PetscInt p = 0; p < cl_size; p++) {
71053b735f8SJames Wright PetscInt cl_point = closure[2 * p], coords_size, donor_vertex = -1;
71153b735f8SJames Wright PetscScalar *coords = NULL;
71253b735f8SJames Wright
71353b735f8SJames Wright if (!IsPointInsideStratum(cl_point, vStart, vEnd)) continue;
71453b735f8SJames Wright PetscCall(DMPlexVecGetClosure(dm, csection, coordinates, cl_point, &coords_size, &coords));
71553b735f8SJames Wright PetscAssert(coords_size == dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " has dof size %" PetscInt_FMT ", but should match dimension size %" PetscInt_FMT, cl_point, coords_size, dim);
71653b735f8SJames Wright
71753b735f8SJames Wright for (PetscInt v = 0; v < num_verts; v++) {
71853b735f8SJames Wright PetscReal dist_sqr = 0;
71953b735f8SJames Wright for (PetscInt d = 0; d < coords_size; d++) dist_sqr += PetscSqr(PetscRealPart(coords[d]) - leaf_donor_coords[(v + coords_offset) * dim + d]);
72053b735f8SJames Wright if (dist_sqr < tol) {
72153b735f8SJames Wright donor_vertex = leaf_donor_cones[cones_offset + v];
72253b735f8SJames Wright break;
72353b735f8SJames Wright }
72453b735f8SJames Wright }
72553b735f8SJames Wright PetscCheck(donor_vertex >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Periodic face %" PetscInt_FMT " could not find matching donor vertex for vertex %" PetscInt_FMT, periodic_face, cl_point);
72653b735f8SJames Wright if (PetscDefined(USE_DEBUG)) {
72753b735f8SJames Wright for (PetscInt c = 0; c < p2d_count; c++) PetscCheck(periodic2donor[2 * c + 1] != donor_vertex, comm, PETSC_ERR_PLIB, "Found repeated cone_point in periodic_ordering");
72853b735f8SJames Wright }
72953b735f8SJames Wright
73053b735f8SJames Wright periodic2donor[2 * p2d_count + 0] = cl_point;
73153b735f8SJames Wright periodic2donor[2 * p2d_count + 1] = donor_vertex;
73253b735f8SJames Wright p2d_count++;
73353b735f8SJames Wright PetscCall(DMPlexVecRestoreClosure(dm, csection, coordinates, cl_point, &coords_size, &coords));
73453b735f8SJames Wright }
73553b735f8SJames Wright coords_offset += num_verts;
73653b735f8SJames Wright PetscCall(DMPlexRestoreTransitiveClosure(dm, periodic_face, PETSC_TRUE, &cl_size, &closure));
73753b735f8SJames Wright
73853b735f8SJames Wright { // Determine periodic orientation w/rt donor vertices and reorient
73953b735f8SJames Wright PetscInt depth, *p2d_cone, face_is_array[1] = {periodic_face};
74053b735f8SJames Wright IS *is_arrays, face_is;
74153b735f8SJames Wright PetscSection *section_arrays;
74253b735f8SJames Wright PetscInt *donor_cone_array = &leaf_donor_cones[cones_offset + num_verts];
74353b735f8SJames Wright
74453b735f8SJames Wright PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1, face_is_array, PETSC_USE_POINTER, &face_is));
74553b735f8SJames Wright PetscCall(DMPlexGetConeRecursive(dm, face_is, &depth, &is_arrays, §ion_arrays));
74653b735f8SJames Wright PetscCall(ISDestroy(&face_is));
74753b735f8SJames Wright PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &p2d_cone));
74853b735f8SJames Wright for (PetscInt d = 0; d < depth - 1; d++) {
74953b735f8SJames Wright PetscInt pStart, pEnd;
75053b735f8SJames Wright PetscSection section = section_arrays[d];
75153b735f8SJames Wright const PetscInt *periodic_cone_arrays, *periodic_point_arrays;
75253b735f8SJames Wright
75353b735f8SJames Wright PetscCall(ISGetIndices(is_arrays[d], &periodic_cone_arrays));
75453b735f8SJames Wright PetscCall(ISGetIndices(is_arrays[d + 1], &periodic_point_arrays)); // Points at d+1 correspond to the cones at d
75553b735f8SJames Wright PetscCall(PetscSectionGetChart(section_arrays[d], &pStart, &pEnd));
75653b735f8SJames Wright for (PetscInt p = pStart; p < pEnd; p++) {
75753b735f8SJames Wright PetscInt periodic_cone_size, periodic_cone_offset, periodic_point = periodic_point_arrays[p];
75853b735f8SJames Wright
75953b735f8SJames Wright PetscCall(PetscSectionGetDof(section, p, &periodic_cone_size));
76053b735f8SJames Wright PetscCall(PetscSectionGetOffset(section, p, &periodic_cone_offset));
76153b735f8SJames Wright const PetscInt *periodic_cone = &periodic_cone_arrays[periodic_cone_offset];
76253b735f8SJames Wright PetscCall(TranslateConeP2D(periodic_cone, periodic_cone_size, periodic2donor, p2d_count, p2d_cone));
76353b735f8SJames Wright
76453b735f8SJames Wright // Find the donor cone that matches the periodic point's cone
76553b735f8SJames Wright PetscInt donor_cone_offset = 0, donor_point = -1, *donor_cone = NULL;
76653b735f8SJames Wright PetscBool cone_matches = PETSC_FALSE;
76753b735f8SJames Wright while (donor_cone_offset < cones_size - num_verts) {
76853b735f8SJames Wright PetscInt donor_cone_size = donor_cone_array[donor_cone_offset];
76953b735f8SJames Wright donor_point = donor_cone_array[donor_cone_offset + 1];
77053b735f8SJames Wright donor_cone = &donor_cone_array[donor_cone_offset + 2];
77153b735f8SJames Wright
77253b735f8SJames Wright if (donor_cone_size != periodic_cone_size) goto next_cone;
77353b735f8SJames Wright for (PetscInt c = 0; c < periodic_cone_size; c++) {
77453b735f8SJames Wright cone_matches = SearchIntArray(donor_cone[c], periodic_cone_size, p2d_cone);
77553b735f8SJames Wright if (!cone_matches) goto next_cone;
77653b735f8SJames Wright }
77753b735f8SJames Wright // Save the found donor cone's point to the translation array. These will be used for higher depth points.
77853b735f8SJames Wright // i.e. we save the edge translations for when we look for face cones
77953b735f8SJames Wright periodic2donor[2 * p2d_count + 0] = periodic_point;
78053b735f8SJames Wright periodic2donor[2 * p2d_count + 1] = donor_point;
78153b735f8SJames Wright p2d_count++;
78253b735f8SJames Wright break;
78353b735f8SJames Wright
78453b735f8SJames Wright next_cone:
78553b735f8SJames Wright donor_cone_offset += donor_cone_size + 2;
78653b735f8SJames Wright }
78753b735f8SJames Wright PetscCheck(donor_cone_offset < cones_size - num_verts, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find donor cone equivalent to cone of periodic point %" PetscInt_FMT, periodic_point);
78853b735f8SJames Wright
78953b735f8SJames Wright { // Compare the donor cone with the translated periodic cone and reorient
79053b735f8SJames Wright PetscInt ornt;
79153b735f8SJames Wright DMPolytopeType cell_type;
79253b735f8SJames Wright PetscBool found;
79353b735f8SJames Wright PetscCall(DMPlexGetCellType(dm, periodic_point, &cell_type));
79453b735f8SJames Wright PetscCall(DMPolytopeMatchOrientation(cell_type, donor_cone, p2d_cone, &ornt, &found));
79553b735f8SJames Wright PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not find transformation between donor (%" PetscInt_FMT ") and periodic (%" PetscInt_FMT ") cone's", periodic_point, donor_point);
79653b735f8SJames Wright if (debug_printing) PetscCall(PetscPrintf(PETSC_COMM_SELF, "Reorienting point %" PetscInt_FMT " by %" PetscInt_FMT "\n", periodic_point, ornt));
79792a26154SJames Wright PetscCall(DMPlexOrientPointWithCorrections(dm, periodic_point, ornt, localSection, local_vec_length, local_vec_perm));
79853b735f8SJames Wright }
79953b735f8SJames Wright }
80053b735f8SJames Wright PetscCall(ISRestoreIndices(is_arrays[d], &periodic_cone_arrays));
80153b735f8SJames Wright PetscCall(ISRestoreIndices(is_arrays[d + 1], &periodic_point_arrays));
80253b735f8SJames Wright }
80353b735f8SJames Wright PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &p2d_cone));
80453b735f8SJames Wright PetscCall(DMPlexRestoreConeRecursive(dm, face_is, &depth, &is_arrays, §ion_arrays));
80553b735f8SJames Wright }
80653b735f8SJames Wright
80753b735f8SJames Wright PetscCall(DMPlexRestoreTransitiveClosure(dm, periodic_face, PETSC_TRUE, &cl_size, &closure));
80853b735f8SJames Wright cones_offset += cones_size;
80953b735f8SJames Wright }
81053b735f8SJames Wright PetscCall(DMRestoreWorkArray(dm, 2 * PetscPowInt(maxConeSize, dm_depth - 1), MPIU_INT, &periodic2donor));
81153b735f8SJames Wright }
81292a26154SJames Wright // Re-set local coordinates (i.e. destroy global coordinates if they were modified)
8130b6e880eSJames Wright PetscCall(DMSetCoordinatesLocal(dm, coordinates));
81453b735f8SJames Wright
81553b735f8SJames Wright PetscCall(PetscFree2(leaf_donor_coords, leaf_donor_cones));
81653b735f8SJames Wright PetscCall(PetscFree2(face_vertices_size, face_cones_size));
81753b735f8SJames Wright }
81892a26154SJames Wright
81992a26154SJames Wright if (sfNatural_old) { // Correct SFNatural based on the permutation of the local vector
82092a26154SJames Wright PetscSF newglob_to_oldglob_sf, sfNatural_old, sfNatural_new;
82192a26154SJames Wright PetscSFNode *remote;
82292a26154SJames Wright
82392a26154SJames Wright { // Translate permutation of local Vec into permutation of global Vec
82492a26154SJames Wright PetscInt g = 0;
82592a26154SJames Wright PetscBT global_vec_check; // Verify that global indices aren't doubled
82692a26154SJames Wright PetscCall(PetscBTCreate(global_vec_length, &global_vec_check));
82792a26154SJames Wright for (PetscInt l = 0; l < local_vec_length; l++) {
82892a26154SJames Wright PetscInt global_index = local_vec_perm[l];
82992a26154SJames Wright if (global_index < 0) continue;
83092a26154SJames Wright PetscCheck(!PetscBTLookupSet(global_vec_check, global_index), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Found duplicate global index %" PetscInt_FMT " in local_vec_perm", global_index);
83192a26154SJames Wright global_vec_perm[g++] = global_index;
83292a26154SJames Wright }
83392a26154SJames Wright PetscCheck(g == global_vec_length, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Wrong number of non-negative local indices");
83492a26154SJames Wright PetscCall(PetscBTDestroy(&global_vec_check));
83592a26154SJames Wright }
83692a26154SJames Wright
83792a26154SJames Wright PetscCall(PetscMalloc1(global_vec_length, &remote));
83892a26154SJames Wright for (PetscInt i = 0; i < global_vec_length; i++) {
83992a26154SJames Wright remote[i].rank = myrank;
84092a26154SJames Wright remote[i].index = global_vec_perm[i];
84192a26154SJames Wright }
84292a26154SJames Wright PetscCall(PetscFree2(global_vec_perm, local_vec_perm));
84392a26154SJames Wright PetscCall(PetscSFCreate(comm, &newglob_to_oldglob_sf));
84492a26154SJames Wright PetscCall(PetscSFSetGraph(newglob_to_oldglob_sf, global_vec_length, global_vec_length, NULL, PETSC_USE_POINTER, remote, PETSC_OWN_POINTER));
84592a26154SJames Wright PetscCall(DMGetNaturalSF(dm, &sfNatural_old));
84692a26154SJames Wright PetscCall(PetscSFCompose(newglob_to_oldglob_sf, sfNatural_old, &sfNatural_new));
84792a26154SJames Wright PetscCall(DMSetNaturalSF(dm, sfNatural_new));
84892a26154SJames Wright PetscCall(PetscSFDestroy(&sfNatural_new));
84992a26154SJames Wright PetscCall(PetscSFDestroy(&newglob_to_oldglob_sf));
85092a26154SJames Wright }
85153b735f8SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
85253b735f8SJames Wright }
85353b735f8SJames Wright
85453b735f8SJames Wright // Start with an SF for a positive depth (e.g., faces) and create a new SF for matched closure.
8556725e60dSJed Brown //
8565f06a3ddSJed Brown // Output Arguments:
8575f06a3ddSJed Brown //
8585f06a3ddSJed Brown // + closure_sf - augmented point SF (see `DMGetPointSF()`) that includes the faces and all points in its closure. This
8595f06a3ddSJed Brown // can be used to create a global section and section SF.
860b83f62b0SJames 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
8615f06a3ddSJed Brown // transformation to periodic dofs; see DMPeriodicCoordinateSetUp_Internal().
8625f06a3ddSJed Brown //
DMPlexCreateIsoperiodicPointSF_Private(DM dm,PetscInt num_face_sfs,PetscSF * face_sfs,PetscSF * closure_sf,IS ** is_points)863b83f62b0SJames Wright static PetscErrorCode DMPlexCreateIsoperiodicPointSF_Private(DM dm, PetscInt num_face_sfs, PetscSF *face_sfs, PetscSF *closure_sf, IS **is_points)
8646725e60dSJed Brown {
8656725e60dSJed Brown MPI_Comm comm;
8666725e60dSJed Brown PetscMPIInt rank;
8676725e60dSJed Brown PetscSF point_sf;
868b83f62b0SJames Wright PetscInt nroots, nleaves;
869b83f62b0SJames Wright const PetscInt *filocal;
870b83f62b0SJames Wright const PetscSFNode *firemote;
8716725e60dSJed Brown
8726725e60dSJed Brown PetscFunctionBegin;
8736725e60dSJed Brown PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8746725e60dSJed Brown PetscCallMPI(MPI_Comm_rank(comm, &rank));
8756725e60dSJed Brown PetscCall(DMGetPointSF(dm, &point_sf)); // Point SF has remote points
876b83f62b0SJames Wright PetscCall(PetscMalloc1(num_face_sfs, is_points));
877b83f62b0SJames Wright
87853b735f8SJames Wright PetscCall(DMPlexCorrectOrientationForIsoperiodic(dm));
87953b735f8SJames Wright
880b83f62b0SJames Wright for (PetscInt f = 0; f < num_face_sfs; f++) {
881b83f62b0SJames Wright PetscSF face_sf = face_sfs[f];
88293defd67SJames Wright PetscInt *cl_sizes;
88393defd67SJames Wright PetscInt fStart, fEnd, rootbuffersize, leafbuffersize;
8846725e60dSJed Brown PetscSF sf_closure;
88593defd67SJames Wright PetscBT rootbt;
88693defd67SJames Wright
88793defd67SJames Wright PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
88893defd67SJames Wright PetscCall(PetscMalloc1(fEnd - fStart, &cl_sizes));
88993defd67SJames Wright for (PetscInt f = fStart, index = 0; f < fEnd; f++, index++) {
89093defd67SJames Wright PetscInt cl_size, *closure = NULL;
89193defd67SJames Wright PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &cl_size, &closure));
89293defd67SJames Wright cl_sizes[index] = cl_size - 1;
89393defd67SJames Wright PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &cl_size, &closure));
89493defd67SJames Wright }
89593defd67SJames Wright
89693defd67SJames Wright PetscCall(CreateDonorToPeriodicSF(dm, face_sf, fStart, fEnd, cl_sizes, &rootbuffersize, &leafbuffersize, &rootbt, &sf_closure));
89793defd67SJames Wright PetscCall(PetscFree(cl_sizes));
89893defd67SJames Wright PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote));
8996725e60dSJed Brown
900b83f62b0SJames Wright PetscSFNode *leaf_donor_closure;
901b83f62b0SJames Wright { // Pack root buffer with owner for every point in the root cones
9026725e60dSJed Brown PetscSFNode *donor_closure;
903b83f62b0SJames Wright const PetscInt *pilocal;
904b83f62b0SJames Wright const PetscSFNode *piremote;
905b83f62b0SJames Wright PetscInt npoints;
906b83f62b0SJames Wright
907b83f62b0SJames Wright PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, &pilocal, &piremote));
90893defd67SJames Wright PetscCall(PetscCalloc1(rootbuffersize, &donor_closure));
90993defd67SJames Wright for (PetscInt p = 0, root_offset = 0; p < nroots; p++) {
91093defd67SJames Wright if (!PetscBTLookup(rootbt, p)) continue;
91193defd67SJames Wright PetscInt cl_size, *closure = NULL;
9126725e60dSJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
9136725e60dSJed Brown for (PetscInt j = 1; j < cl_size; j++) {
9146725e60dSJed Brown PetscInt c = closure[2 * j];
9156725e60dSJed Brown if (pilocal) {
9166725e60dSJed Brown PetscInt found = -1;
9176725e60dSJed Brown if (npoints > 0) PetscCall(PetscFindInt(c, npoints, pilocal, &found));
9186725e60dSJed Brown if (found >= 0) {
9196725e60dSJed Brown donor_closure[root_offset++] = piremote[found];
9206725e60dSJed Brown continue;
9216725e60dSJed Brown }
9226725e60dSJed Brown }
9236725e60dSJed Brown // we own c
9246725e60dSJed Brown donor_closure[root_offset].rank = rank;
9256725e60dSJed Brown donor_closure[root_offset].index = c;
9266725e60dSJed Brown root_offset++;
9276725e60dSJed Brown }
9286725e60dSJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure));
9296725e60dSJed Brown }
9306725e60dSJed Brown
93193defd67SJames Wright PetscCall(PetscMalloc1(leafbuffersize, &leaf_donor_closure));
9326497c311SBarry Smith PetscCall(PetscSFBcastBegin(sf_closure, MPIU_SF_NODE, donor_closure, leaf_donor_closure, MPI_REPLACE));
9336497c311SBarry Smith PetscCall(PetscSFBcastEnd(sf_closure, MPIU_SF_NODE, donor_closure, leaf_donor_closure, MPI_REPLACE));
9346725e60dSJed Brown PetscCall(PetscSFDestroy(&sf_closure));
9356725e60dSJed Brown PetscCall(PetscFree(donor_closure));
936b83f62b0SJames Wright }
9376725e60dSJed Brown
9386725e60dSJed Brown PetscSFNode *new_iremote;
9396725e60dSJed Brown PetscCall(PetscCalloc1(nroots, &new_iremote));
9406725e60dSJed Brown for (PetscInt i = 0; i < nroots; i++) new_iremote[i].rank = -1;
9416725e60dSJed Brown // Walk leaves and match vertices
94293defd67SJames Wright for (PetscInt i = 0, leaf_offset = 0; i < nleaves; i++) {
9436725e60dSJed Brown PetscInt point = filocal[i], cl_size;
9446725e60dSJed Brown PetscInt *closure = NULL;
9456725e60dSJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
94653b735f8SJames Wright for (PetscInt j = 1; j < cl_size; j++) {
9476725e60dSJed Brown PetscInt c = closure[2 * j];
9486725e60dSJed Brown PetscSFNode lc = leaf_donor_closure[leaf_offset];
9496725e60dSJed Brown // printf("[%d] face %d.%d: %d ?-- (%d,%d)\n", rank, point, j, c, lc.rank, lc.index);
9506725e60dSJed Brown if (new_iremote[c].rank == -1) {
9516725e60dSJed Brown new_iremote[c] = lc;
9525f06a3ddSJed 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");
9536725e60dSJed Brown leaf_offset++;
9546725e60dSJed Brown }
9556725e60dSJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure));
9566725e60dSJed Brown }
9576725e60dSJed Brown PetscCall(PetscFree(leaf_donor_closure));
9586725e60dSJed Brown
9596725e60dSJed Brown // Include face points in closure SF
9606725e60dSJed Brown for (PetscInt i = 0; i < nleaves; i++) new_iremote[filocal[i]] = firemote[i];
9616725e60dSJed Brown // consolidate leaves
96293defd67SJames Wright PetscInt *leafdata;
96393defd67SJames Wright PetscCall(PetscMalloc1(nroots, &leafdata));
9646725e60dSJed Brown PetscInt num_new_leaves = 0;
9656725e60dSJed Brown for (PetscInt i = 0; i < nroots; i++) {
9666725e60dSJed Brown if (new_iremote[i].rank == -1) continue;
9676725e60dSJed Brown new_iremote[num_new_leaves] = new_iremote[i];
9686725e60dSJed Brown leafdata[num_new_leaves] = i;
9696725e60dSJed Brown num_new_leaves++;
9706725e60dSJed Brown }
971b83f62b0SJames Wright PetscCall(ISCreateGeneral(PETSC_COMM_SELF, num_new_leaves, leafdata, PETSC_COPY_VALUES, &(*is_points)[f]));
9726725e60dSJed Brown
9736725e60dSJed Brown PetscSF csf;
9746725e60dSJed Brown PetscCall(PetscSFCreate(comm, &csf));
9756725e60dSJed Brown PetscCall(PetscSFSetGraph(csf, nroots, num_new_leaves, leafdata, PETSC_COPY_VALUES, new_iremote, PETSC_COPY_VALUES));
9766725e60dSJed Brown PetscCall(PetscFree(new_iremote)); // copy and delete because new_iremote is longer than it needs to be
97793defd67SJames Wright PetscCall(PetscFree(leafdata));
97893defd67SJames Wright PetscCall(PetscBTDestroy(&rootbt));
9796725e60dSJed Brown
980b83f62b0SJames Wright PetscInt npoints;
981b83f62b0SJames Wright PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, NULL, NULL));
9825f06a3ddSJed Brown if (npoints < 0) { // empty point_sf
9836725e60dSJed Brown *closure_sf = csf;
9845f06a3ddSJed Brown } else {
9855f06a3ddSJed Brown PetscCall(PetscSFMerge(point_sf, csf, closure_sf));
9865f06a3ddSJed Brown PetscCall(PetscSFDestroy(&csf));
9875f06a3ddSJed Brown }
988d8b4a066SPierre Jolivet if (f > 0) PetscCall(PetscSFDestroy(&point_sf)); // Only destroy if point_sf is from previous calls to PetscSFMerge
989b83f62b0SJames Wright point_sf = *closure_sf; // Use combined point + isoperiodic SF to define point ownership for further face_sf
990b83f62b0SJames Wright }
9915f06a3ddSJed Brown PetscCall(PetscObjectSetName((PetscObject)*closure_sf, "Composed Periodic Points"));
9923ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
9936725e60dSJed Brown }
9946725e60dSJed Brown
DMGetIsoperiodicPointSF_Plex(DM dm,PetscSF * sf)9955f06a3ddSJed Brown static PetscErrorCode DMGetIsoperiodicPointSF_Plex(DM dm, PetscSF *sf)
9966725e60dSJed Brown {
9976725e60dSJed Brown DM_Plex *plex = (DM_Plex *)dm->data;
9986725e60dSJed Brown
9996725e60dSJed Brown PetscFunctionBegin;
1000b83f62b0SJames 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));
10016725e60dSJed Brown if (sf) *sf = plex->periodic.composed_sf;
10023ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
10036725e60dSJed Brown }
10046725e60dSJed Brown
DMPlexMigrateIsoperiodicFaceSF_Internal(DM old_dm,DM dm,PetscSF sf_migration)10055f06a3ddSJed Brown PetscErrorCode DMPlexMigrateIsoperiodicFaceSF_Internal(DM old_dm, DM dm, PetscSF sf_migration)
10065f06a3ddSJed Brown {
10075f06a3ddSJed Brown DM_Plex *plex = (DM_Plex *)old_dm->data;
1008a45b0079SJames Wright PetscSF sf_point, *new_face_sfs;
10095f06a3ddSJed Brown PetscMPIInt rank;
10105f06a3ddSJed Brown
10115f06a3ddSJed Brown PetscFunctionBegin;
10121fca310dSJames Wright if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS);
10135f06a3ddSJed Brown PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
10145f06a3ddSJed Brown PetscCall(DMGetPointSF(dm, &sf_point));
1015a45b0079SJames Wright PetscCall(PetscMalloc1(plex->periodic.num_face_sfs, &new_face_sfs));
1016a45b0079SJames Wright
1017a45b0079SJames Wright for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) {
10185f06a3ddSJed Brown PetscInt old_npoints, new_npoints, old_nleaf, new_nleaf, point_nleaf;
10195f06a3ddSJed Brown PetscSFNode *new_leafdata, *rootdata, *leafdata;
10205f06a3ddSJed Brown const PetscInt *old_local, *point_local;
10215f06a3ddSJed Brown const PetscSFNode *old_remote, *point_remote;
10226497c311SBarry Smith
1023a45b0079SJames Wright PetscCall(PetscSFGetGraph(plex->periodic.face_sfs[f], &old_npoints, &old_nleaf, &old_local, &old_remote));
10245f06a3ddSJed Brown PetscCall(PetscSFGetGraph(sf_migration, NULL, &new_nleaf, NULL, NULL));
10255f06a3ddSJed Brown PetscCall(PetscSFGetGraph(sf_point, &new_npoints, &point_nleaf, &point_local, &point_remote));
10265f06a3ddSJed Brown PetscAssert(new_nleaf == new_npoints, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected migration leaf space to match new point root space");
10275f06a3ddSJed Brown PetscCall(PetscMalloc3(old_npoints, &rootdata, old_npoints, &leafdata, new_npoints, &new_leafdata));
10285f06a3ddSJed Brown
10295f06a3ddSJed Brown // Fill new_leafdata with new owners of all points
10305f06a3ddSJed Brown for (PetscInt i = 0; i < new_npoints; i++) {
10315f06a3ddSJed Brown new_leafdata[i].rank = rank;
10325f06a3ddSJed Brown new_leafdata[i].index = i;
10335f06a3ddSJed Brown }
10345f06a3ddSJed Brown for (PetscInt i = 0; i < point_nleaf; i++) {
10355f06a3ddSJed Brown PetscInt j = point_local[i];
10365f06a3ddSJed Brown new_leafdata[j] = point_remote[i];
10375f06a3ddSJed Brown }
10385f06a3ddSJed Brown // REPLACE is okay because every leaf agrees about the new owners
10396497c311SBarry Smith PetscCall(PetscSFReduceBegin(sf_migration, MPIU_SF_NODE, new_leafdata, rootdata, MPI_REPLACE));
10406497c311SBarry Smith PetscCall(PetscSFReduceEnd(sf_migration, MPIU_SF_NODE, new_leafdata, rootdata, MPI_REPLACE));
10415f06a3ddSJed Brown // rootdata now contains the new owners
10425f06a3ddSJed Brown
10435f06a3ddSJed Brown // Send to leaves of old space
10445f06a3ddSJed Brown for (PetscInt i = 0; i < old_npoints; i++) {
10455f06a3ddSJed Brown leafdata[i].rank = -1;
10465f06a3ddSJed Brown leafdata[i].index = -1;
10475f06a3ddSJed Brown }
10486497c311SBarry Smith PetscCall(PetscSFBcastBegin(plex->periodic.face_sfs[f], MPIU_SF_NODE, rootdata, leafdata, MPI_REPLACE));
10496497c311SBarry Smith PetscCall(PetscSFBcastEnd(plex->periodic.face_sfs[f], MPIU_SF_NODE, rootdata, leafdata, MPI_REPLACE));
10505f06a3ddSJed Brown
10515f06a3ddSJed Brown // Send to new leaf space
10526497c311SBarry Smith PetscCall(PetscSFBcastBegin(sf_migration, MPIU_SF_NODE, leafdata, new_leafdata, MPI_REPLACE));
10536497c311SBarry Smith PetscCall(PetscSFBcastEnd(sf_migration, MPIU_SF_NODE, leafdata, new_leafdata, MPI_REPLACE));
10545f06a3ddSJed Brown
10555f06a3ddSJed Brown PetscInt nface = 0, *new_local;
10565f06a3ddSJed Brown PetscSFNode *new_remote;
10575f06a3ddSJed Brown for (PetscInt i = 0; i < new_npoints; i++) nface += (new_leafdata[i].rank >= 0);
10585f06a3ddSJed Brown PetscCall(PetscMalloc1(nface, &new_local));
10595f06a3ddSJed Brown PetscCall(PetscMalloc1(nface, &new_remote));
10605f06a3ddSJed Brown nface = 0;
10615f06a3ddSJed Brown for (PetscInt i = 0; i < new_npoints; i++) {
10625f06a3ddSJed Brown if (new_leafdata[i].rank == -1) continue;
10635f06a3ddSJed Brown new_local[nface] = i;
10645f06a3ddSJed Brown new_remote[nface] = new_leafdata[i];
10655f06a3ddSJed Brown nface++;
10665f06a3ddSJed Brown }
10675f06a3ddSJed Brown PetscCall(PetscFree3(rootdata, leafdata, new_leafdata));
1068a45b0079SJames Wright PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &new_face_sfs[f]));
1069a45b0079SJames Wright PetscCall(PetscSFSetGraph(new_face_sfs[f], new_npoints, nface, new_local, PETSC_OWN_POINTER, new_remote, PETSC_OWN_POINTER));
1070a45b0079SJames Wright {
1071a45b0079SJames Wright char new_face_sf_name[PETSC_MAX_PATH_LEN];
1072a45b0079SJames Wright PetscCall(PetscSNPrintf(new_face_sf_name, sizeof new_face_sf_name, "Migrated Isoperiodic Faces #%" PetscInt_FMT, f));
1073a45b0079SJames Wright PetscCall(PetscObjectSetName((PetscObject)new_face_sfs[f], new_face_sf_name));
1074a45b0079SJames Wright }
1075a45b0079SJames Wright }
1076a45b0079SJames Wright
1077a45b0079SJames Wright PetscCall(DMPlexSetIsoperiodicFaceSF(dm, plex->periodic.num_face_sfs, new_face_sfs));
1078a45b0079SJames Wright PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, plex->periodic.num_face_sfs, (PetscScalar *)plex->periodic.transform));
1079a45b0079SJames Wright for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) PetscCall(PetscSFDestroy(&new_face_sfs[f]));
1080a45b0079SJames Wright PetscCall(PetscFree(new_face_sfs));
10813ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
10825f06a3ddSJed Brown }
10835f06a3ddSJed Brown
DMPeriodicCoordinateSetUp_Internal(DM dm)10846725e60dSJed Brown PetscErrorCode DMPeriodicCoordinateSetUp_Internal(DM dm)
10856725e60dSJed Brown {
10866725e60dSJed Brown DM_Plex *plex = (DM_Plex *)dm->data;
10876497c311SBarry Smith PetscCount count;
1088ddedc8f6SJames Wright IS isdof;
1089ddedc8f6SJames Wright PetscInt dim;
10904d86920dSPierre Jolivet
10916725e60dSJed Brown PetscFunctionBegin;
10921fca310dSJames Wright if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS);
109353b735f8SJames Wright PetscCheck(plex->periodic.periodic_points, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Isoperiodic PointSF must be created before this function is called");
10946725e60dSJed Brown
109553b735f8SJames Wright PetscCall(DMGetCoordinateDim(dm, &dim));
1096ddedc8f6SJames Wright dm->periodic.num_affines = plex->periodic.num_face_sfs;
109753b735f8SJames Wright PetscCall(PetscFree2(dm->periodic.affine_to_local, dm->periodic.affine));
1098ddedc8f6SJames Wright PetscCall(PetscMalloc2(dm->periodic.num_affines, &dm->periodic.affine_to_local, dm->periodic.num_affines, &dm->periodic.affine));
1099ddedc8f6SJames Wright
1100ddedc8f6SJames Wright for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) {
11016497c311SBarry Smith PetscInt npoints, vsize, isize;
11026725e60dSJed Brown const PetscInt *points;
1103ddedc8f6SJames Wright IS is = plex->periodic.periodic_points[f];
11046725e60dSJed Brown PetscSegBuffer seg;
11056725e60dSJed Brown PetscSection section;
11066497c311SBarry Smith PetscInt *ind;
11076497c311SBarry Smith Vec L, P;
11086497c311SBarry Smith VecType vec_type;
11096497c311SBarry Smith VecScatter scatter;
11106497c311SBarry Smith PetscScalar *x;
11116497c311SBarry Smith
11126725e60dSJed Brown PetscCall(DMGetLocalSection(dm, §ion));
11136725e60dSJed Brown PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 32, &seg));
11146725e60dSJed Brown PetscCall(ISGetSize(is, &npoints));
11156725e60dSJed Brown PetscCall(ISGetIndices(is, &points));
11166725e60dSJed Brown for (PetscInt i = 0; i < npoints; i++) {
11176725e60dSJed Brown PetscInt point = points[i], off, dof;
11186497c311SBarry Smith
11196725e60dSJed Brown PetscCall(PetscSectionGetOffset(section, point, &off));
11206725e60dSJed Brown PetscCall(PetscSectionGetDof(section, point, &dof));
11216725e60dSJed Brown PetscAssert(dof % dim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected dof %" PetscInt_FMT " not divisible by dimension %" PetscInt_FMT, dof, dim);
11226725e60dSJed Brown for (PetscInt j = 0; j < dof / dim; j++) {
11236725e60dSJed Brown PetscInt *slot;
11246497c311SBarry Smith
11256725e60dSJed Brown PetscCall(PetscSegBufferGetInts(seg, 1, &slot));
1126729bdd54SJed Brown *slot = off / dim + j;
11276725e60dSJed Brown }
11286725e60dSJed Brown }
11296725e60dSJed Brown PetscCall(PetscSegBufferGetSize(seg, &count));
11306725e60dSJed Brown PetscCall(PetscSegBufferExtractAlloc(seg, &ind));
11316725e60dSJed Brown PetscCall(PetscSegBufferDestroy(&seg));
11326497c311SBarry Smith PetscCall(PetscIntCast(count, &isize));
11336497c311SBarry Smith PetscCall(ISCreateBlock(PETSC_COMM_SELF, dim, isize, ind, PETSC_OWN_POINTER, &isdof));
11346497c311SBarry Smith
11356497c311SBarry Smith PetscCall(PetscIntCast(count * dim, &vsize));
11366725e60dSJed Brown PetscCall(DMGetLocalVector(dm, &L));
11376725e60dSJed Brown PetscCall(VecCreate(PETSC_COMM_SELF, &P));
11386497c311SBarry Smith PetscCall(VecSetSizes(P, vsize, vsize));
11396725e60dSJed Brown PetscCall(VecGetType(L, &vec_type));
11406725e60dSJed Brown PetscCall(VecSetType(P, vec_type));
11416725e60dSJed Brown PetscCall(VecScatterCreate(P, NULL, L, isdof, &scatter));
11426725e60dSJed Brown PetscCall(DMRestoreLocalVector(dm, &L));
11436725e60dSJed Brown PetscCall(ISDestroy(&isdof));
11446725e60dSJed Brown
11456725e60dSJed Brown PetscCall(VecGetArrayWrite(P, &x));
11466497c311SBarry Smith for (PetscCount i = 0; i < count; i++) {
1147ddedc8f6SJames Wright for (PetscInt j = 0; j < dim; j++) x[i * dim + j] = plex->periodic.transform[f][j][3];
11486725e60dSJed Brown }
11496725e60dSJed Brown PetscCall(VecRestoreArrayWrite(P, &x));
11506725e60dSJed Brown
1151ddedc8f6SJames Wright dm->periodic.affine_to_local[f] = scatter;
1152ddedc8f6SJames Wright dm->periodic.affine[f] = P;
1153ddedc8f6SJames Wright }
11546725e60dSJed Brown PetscCall(DMGlobalToLocalHookAdd(dm, NULL, DMCoordAddPeriodicOffsets_Private, NULL));
11553ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
11566725e60dSJed Brown }
11576725e60dSJed Brown
DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm,PetscInt dim,const PetscInt faces[],const PetscReal lower[],const PetscReal upper[],const DMBoundaryType periodicity[],PetscBool interpolate)11583e72e933SJed Brown PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate)
11593e72e933SJed Brown {
11603e72e933SJed Brown PetscInt eextent[3] = {1, 1, 1}, vextent[3] = {1, 1, 1};
11613e72e933SJed Brown const Ijk closure_1[] = {
11623e72e933SJed Brown {0, 0, 0},
11633e72e933SJed Brown {1, 0, 0},
11643e72e933SJed Brown };
11653e72e933SJed Brown const Ijk closure_2[] = {
11663e72e933SJed Brown {0, 0, 0},
11673e72e933SJed Brown {1, 0, 0},
11683e72e933SJed Brown {1, 1, 0},
11693e72e933SJed Brown {0, 1, 0},
11703e72e933SJed Brown };
11713e72e933SJed Brown const Ijk closure_3[] = {
11723e72e933SJed Brown {0, 0, 0},
11733e72e933SJed Brown {0, 1, 0},
11743e72e933SJed Brown {1, 1, 0},
11753e72e933SJed Brown {1, 0, 0},
11763e72e933SJed Brown {0, 0, 1},
11773e72e933SJed Brown {1, 0, 1},
11783e72e933SJed Brown {1, 1, 1},
11793e72e933SJed Brown {0, 1, 1},
11803e72e933SJed Brown };
11813431e603SJed Brown const Ijk *const closure_dim[] = {NULL, closure_1, closure_2, closure_3};
11823431e603SJed Brown // This must be kept consistent with DMPlexCreateCubeMesh_Internal
11833431e603SJed Brown const PetscInt face_marker_1[] = {1, 2};
11843431e603SJed Brown const PetscInt face_marker_2[] = {4, 2, 1, 3};
11853431e603SJed Brown const PetscInt face_marker_3[] = {6, 5, 3, 4, 1, 2};
11863431e603SJed Brown const PetscInt *const face_marker_dim[] = {NULL, face_marker_1, face_marker_2, face_marker_3};
11876725e60dSJed Brown // Orient faces so the normal is in the positive axis and the first vertex is the one closest to zero.
11886725e60dSJed Brown // These orientations can be determined by examining cones of a reference quad and hex element.
11896725e60dSJed Brown const PetscInt face_orient_1[] = {0, 0};
11906725e60dSJed Brown const PetscInt face_orient_2[] = {-1, 0, 0, -1};
11916725e60dSJed Brown const PetscInt face_orient_3[] = {-2, 0, -2, 1, -2, 0};
11926725e60dSJed Brown const PetscInt *const face_orient_dim[] = {NULL, face_orient_1, face_orient_2, face_orient_3};
11933e72e933SJed Brown
11943e72e933SJed Brown PetscFunctionBegin;
1195ea7b3863SJames Wright PetscCall(PetscLogEventBegin(DMPLEX_CreateBoxSFC, dm, 0, 0, 0));
11964f572ea9SToby Isaac PetscAssertPointer(dm, 1);
11973e72e933SJed Brown PetscValidLogicalCollectiveInt(dm, dim, 2);
11983e72e933SJed Brown PetscCall(DMSetDimension(dm, dim));
11993e72e933SJed Brown PetscMPIInt rank, size;
12003e72e933SJed Brown PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
12013e72e933SJed Brown PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
12023e72e933SJed Brown for (PetscInt i = 0; i < dim; i++) {
12033e72e933SJed Brown eextent[i] = faces[i];
12043e72e933SJed Brown vextent[i] = faces[i] + 1;
12053e72e933SJed Brown }
1206c77877e3SJed Brown ZLayout layout;
1207c77877e3SJed Brown PetscCall(ZLayoutCreate(size, eextent, vextent, &layout));
12083e72e933SJed Brown PetscZSet vset; // set of all vertices in the closure of the owned elements
12093e72e933SJed Brown PetscCall(PetscZSetCreate(&vset));
12103e72e933SJed Brown PetscInt local_elems = 0;
12113e72e933SJed Brown for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) {
12123e72e933SJed Brown Ijk loc = ZCodeSplit(z);
12133ba16761SJacob Faibussowitsch if (IjkActive(layout.vextent, loc)) PetscCall(PetscZSetAdd(vset, z));
12146547ddbdSJed Brown else {
12156547ddbdSJed Brown z += ZStepOct(z);
12166547ddbdSJed Brown continue;
12176547ddbdSJed Brown }
12183e72e933SJed Brown if (IjkActive(layout.eextent, loc)) {
12193e72e933SJed Brown local_elems++;
12203e72e933SJed Brown // Add all neighboring vertices to set
12213e72e933SJed Brown for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) {
12223e72e933SJed Brown Ijk inc = closure_dim[dim][n];
12233e72e933SJed Brown Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k};
12243e72e933SJed Brown ZCode v = ZEncode(nloc);
12253ba16761SJacob Faibussowitsch PetscCall(PetscZSetAdd(vset, v));
12263e72e933SJed Brown }
12273e72e933SJed Brown }
12283e72e933SJed Brown }
12293e72e933SJed Brown PetscInt local_verts, off = 0;
12303e72e933SJed Brown ZCode *vert_z;
12313e72e933SJed Brown PetscCall(PetscZSetGetSize(vset, &local_verts));
12323e72e933SJed Brown PetscCall(PetscMalloc1(local_verts, &vert_z));
12333e72e933SJed Brown PetscCall(PetscZSetGetElems(vset, &off, vert_z));
12343e72e933SJed Brown PetscCall(PetscZSetDestroy(&vset));
12353e72e933SJed Brown // ZCode is unsigned for bitwise convenience, but highest bit should never be set, so can interpret as signed
12363e72e933SJed Brown PetscCall(PetscSortInt64(local_verts, (PetscInt64 *)vert_z));
12373e72e933SJed Brown
12383e72e933SJed Brown PetscCall(DMPlexSetChart(dm, 0, local_elems + local_verts));
12393e72e933SJed Brown for (PetscInt e = 0; e < local_elems; e++) PetscCall(DMPlexSetConeSize(dm, e, PetscPowInt(2, dim)));
12403e72e933SJed Brown PetscCall(DMSetUp(dm));
12413e72e933SJed Brown {
12423e72e933SJed Brown PetscInt e = 0;
12433e72e933SJed Brown for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) {
12443e72e933SJed Brown Ijk loc = ZCodeSplit(z);
12456547ddbdSJed Brown if (!IjkActive(layout.eextent, loc)) {
12466547ddbdSJed Brown z += ZStepOct(z);
12476547ddbdSJed Brown continue;
12486547ddbdSJed Brown }
12493e72e933SJed Brown PetscInt cone[8], orient[8] = {0};
12503e72e933SJed Brown for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) {
12513e72e933SJed Brown Ijk inc = closure_dim[dim][n];
12523e72e933SJed Brown Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k};
12533e72e933SJed Brown ZCode v = ZEncode(nloc);
12543e72e933SJed Brown PetscInt ci = ZCodeFind(v, local_verts, vert_z);
12553e72e933SJed Brown PetscAssert(ci >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find neighbor vertex in set");
12563e72e933SJed Brown cone[n] = local_elems + ci;
12573e72e933SJed Brown }
12583e72e933SJed Brown PetscCall(DMPlexSetCone(dm, e, cone));
12593e72e933SJed Brown PetscCall(DMPlexSetConeOrientation(dm, e, orient));
12603e72e933SJed Brown e++;
12613e72e933SJed Brown }
12623e72e933SJed Brown }
12633e72e933SJed Brown
12643e72e933SJed Brown PetscCall(DMPlexSymmetrize(dm));
12653e72e933SJed Brown PetscCall(DMPlexStratify(dm));
12666725e60dSJed Brown
12673e72e933SJed Brown { // Create point SF
12683e72e933SJed Brown PetscSF sf;
12693ba16761SJacob Faibussowitsch PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf));
12703e72e933SJed Brown PetscInt owned_verts = ZCodeFind(layout.zstarts[rank + 1], local_verts, vert_z);
12713e72e933SJed Brown if (owned_verts < 0) owned_verts = -(owned_verts + 1); // We don't care whether the key was found
12723e72e933SJed Brown PetscInt num_ghosts = local_verts - owned_verts; // Due to sorting, owned vertices always come first
12733e72e933SJed Brown PetscInt *local_ghosts;
12743e72e933SJed Brown PetscSFNode *ghosts;
12753e72e933SJed Brown PetscCall(PetscMalloc1(num_ghosts, &local_ghosts));
12763e72e933SJed Brown PetscCall(PetscMalloc1(num_ghosts, &ghosts));
12773e72e933SJed Brown for (PetscInt i = 0; i < num_ghosts;) {
12783e72e933SJed Brown ZCode z = vert_z[owned_verts + i];
1279835f2295SStefano Zampini PetscMPIInt remote_rank, remote_count = 0;
1280835f2295SStefano Zampini
1281835f2295SStefano Zampini PetscCall(PetscMPIIntCast(ZCodeFind(z, size + 1, layout.zstarts), &remote_rank));
12823e72e933SJed Brown if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1;
12833e72e933SJed Brown // We have a new remote rank; find all the ghost indices (which are contiguous in vert_z)
12843e72e933SJed Brown
12853e72e933SJed Brown // Count the elements on remote_rank
12864e2e9504SJed Brown PetscInt remote_elem = ZLayoutElementsOnRank(&layout, remote_rank);
12873e72e933SJed Brown
12883e72e933SJed Brown // Traverse vertices and make ghost links
12893e72e933SJed Brown for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) {
12903e72e933SJed Brown Ijk loc = ZCodeSplit(rz);
12913e72e933SJed Brown if (rz == z) {
12923e72e933SJed Brown local_ghosts[i] = local_elems + owned_verts + i;
12933e72e933SJed Brown ghosts[i].rank = remote_rank;
12943e72e933SJed Brown ghosts[i].index = remote_elem + remote_count;
12953e72e933SJed Brown i++;
12963e72e933SJed Brown if (i == num_ghosts) break;
12973e72e933SJed Brown z = vert_z[owned_verts + i];
12983e72e933SJed Brown }
12993e72e933SJed Brown if (IjkActive(layout.vextent, loc)) remote_count++;
13006547ddbdSJed Brown else rz += ZStepOct(rz);
13013e72e933SJed Brown }
13023e72e933SJed Brown }
13033e72e933SJed Brown PetscCall(PetscSFSetGraph(sf, local_elems + local_verts, num_ghosts, local_ghosts, PETSC_OWN_POINTER, ghosts, PETSC_OWN_POINTER));
13043e72e933SJed Brown PetscCall(PetscObjectSetName((PetscObject)sf, "SFC Point SF"));
13053e72e933SJed Brown PetscCall(DMSetPointSF(dm, sf));
13063e72e933SJed Brown PetscCall(PetscSFDestroy(&sf));
13073e72e933SJed Brown }
13083e72e933SJed Brown {
13093e72e933SJed Brown Vec coordinates;
13103e72e933SJed Brown PetscScalar *coords;
13113e72e933SJed Brown PetscSection coord_section;
13123e72e933SJed Brown PetscInt coord_size;
13133e72e933SJed Brown PetscCall(DMGetCoordinateSection(dm, &coord_section));
13143e72e933SJed Brown PetscCall(PetscSectionSetNumFields(coord_section, 1));
13153e72e933SJed Brown PetscCall(PetscSectionSetFieldComponents(coord_section, 0, dim));
13163e72e933SJed Brown PetscCall(PetscSectionSetChart(coord_section, local_elems, local_elems + local_verts));
13173e72e933SJed Brown for (PetscInt v = 0; v < local_verts; v++) {
13183e72e933SJed Brown PetscInt point = local_elems + v;
13193e72e933SJed Brown PetscCall(PetscSectionSetDof(coord_section, point, dim));
13203e72e933SJed Brown PetscCall(PetscSectionSetFieldDof(coord_section, point, 0, dim));
13213e72e933SJed Brown }
13223e72e933SJed Brown PetscCall(PetscSectionSetUp(coord_section));
13233e72e933SJed Brown PetscCall(PetscSectionGetStorageSize(coord_section, &coord_size));
13243e72e933SJed Brown PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
13253e72e933SJed Brown PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
13263e72e933SJed Brown PetscCall(VecSetSizes(coordinates, coord_size, PETSC_DETERMINE));
13273e72e933SJed Brown PetscCall(VecSetBlockSize(coordinates, dim));
13283e72e933SJed Brown PetscCall(VecSetType(coordinates, VECSTANDARD));
13293e72e933SJed Brown PetscCall(VecGetArray(coordinates, &coords));
13303e72e933SJed Brown for (PetscInt v = 0; v < local_verts; v++) {
13313e72e933SJed Brown Ijk loc = ZCodeSplit(vert_z[v]);
13323e72e933SJed Brown coords[v * dim + 0] = lower[0] + loc.i * (upper[0] - lower[0]) / layout.eextent.i;
13333e72e933SJed Brown if (dim > 1) coords[v * dim + 1] = lower[1] + loc.j * (upper[1] - lower[1]) / layout.eextent.j;
13343e72e933SJed Brown if (dim > 2) coords[v * dim + 2] = lower[2] + loc.k * (upper[2] - lower[2]) / layout.eextent.k;
13353e72e933SJed Brown }
13363e72e933SJed Brown PetscCall(VecRestoreArray(coordinates, &coords));
13373e72e933SJed Brown PetscCall(DMSetCoordinatesLocal(dm, coordinates));
13383e72e933SJed Brown PetscCall(VecDestroy(&coordinates));
13393e72e933SJed Brown }
13403e72e933SJed Brown if (interpolate) {
13413431e603SJed Brown PetscCall(DMPlexInterpolateInPlace_Internal(dm));
13423431e603SJed Brown
13433431e603SJed Brown DMLabel label;
13443431e603SJed Brown PetscCall(DMCreateLabel(dm, "Face Sets"));
13453431e603SJed Brown PetscCall(DMGetLabel(dm, "Face Sets", &label));
13469ae3492bSJames Wright PetscSegBuffer per_faces[3], donor_face_closure[3], my_donor_faces[3];
13479ae3492bSJames Wright for (PetscInt i = 0; i < 3; i++) {
13489ae3492bSJames Wright PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &per_faces[i]));
13499ae3492bSJames Wright PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &my_donor_faces[i]));
13509ae3492bSJames Wright PetscCall(PetscSegBufferCreate(sizeof(ZCode), 64 * PetscPowInt(2, dim), &donor_face_closure[i]));
13519ae3492bSJames Wright }
13523431e603SJed Brown PetscInt fStart, fEnd, vStart, vEnd;
13533431e603SJed Brown PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
13543431e603SJed Brown PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
13553431e603SJed Brown for (PetscInt f = fStart; f < fEnd; f++) {
13564e2e9504SJed Brown PetscInt npoints, *points = NULL, num_fverts = 0, fverts[8];
13573431e603SJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points));
13583431e603SJed Brown PetscInt bc_count[6] = {0};
13593431e603SJed Brown for (PetscInt i = 0; i < npoints; i++) {
13603431e603SJed Brown PetscInt p = points[2 * i];
136185de0153SJames Wright if (!IsPointInsideStratum(p, vStart, vEnd)) continue;
13624e2e9504SJed Brown fverts[num_fverts++] = p;
13633431e603SJed Brown Ijk loc = ZCodeSplit(vert_z[p - vStart]);
13643431e603SJed Brown // Convention here matches DMPlexCreateCubeMesh_Internal
13653431e603SJed Brown bc_count[0] += loc.i == 0;
13663431e603SJed Brown bc_count[1] += loc.i == layout.vextent.i - 1;
13673431e603SJed Brown bc_count[2] += loc.j == 0;
13683431e603SJed Brown bc_count[3] += loc.j == layout.vextent.j - 1;
13693431e603SJed Brown bc_count[4] += loc.k == 0;
13703431e603SJed Brown bc_count[5] += loc.k == layout.vextent.k - 1;
13713e72e933SJed Brown }
13723431e603SJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points));
13733431e603SJed Brown for (PetscInt bc = 0, bc_match = 0; bc < 2 * dim; bc++) {
13743431e603SJed Brown if (bc_count[bc] == PetscPowInt(2, dim - 1)) {
13756725e60dSJed Brown PetscCall(DMPlexOrientPoint(dm, f, face_orient_dim[dim][bc]));
13764e2e9504SJed Brown if (periodicity[bc / 2] == DM_BOUNDARY_PERIODIC) {
13774e2e9504SJed Brown PetscInt *put;
13784e2e9504SJed Brown if (bc % 2 == 0) { // donor face; no label
13799ae3492bSJames Wright PetscCall(PetscSegBufferGet(my_donor_faces[bc / 2], 1, &put));
13804e2e9504SJed Brown *put = f;
13814e2e9504SJed Brown } else { // periodic face
13829ae3492bSJames Wright PetscCall(PetscSegBufferGet(per_faces[bc / 2], 1, &put));
13834e2e9504SJed Brown *put = f;
13844e2e9504SJed Brown ZCode *zput;
13859ae3492bSJames Wright PetscCall(PetscSegBufferGet(donor_face_closure[bc / 2], num_fverts, &zput));
13864e2e9504SJed Brown for (PetscInt i = 0; i < num_fverts; i++) {
13874e2e9504SJed Brown Ijk loc = ZCodeSplit(vert_z[fverts[i] - vStart]);
13884e2e9504SJed Brown switch (bc / 2) {
13894e2e9504SJed Brown case 0:
13904e2e9504SJed Brown loc.i = 0;
13914e2e9504SJed Brown break;
13924e2e9504SJed Brown case 1:
13934e2e9504SJed Brown loc.j = 0;
13944e2e9504SJed Brown break;
13954e2e9504SJed Brown case 2:
13964e2e9504SJed Brown loc.k = 0;
13974e2e9504SJed Brown break;
13984e2e9504SJed Brown }
13994e2e9504SJed Brown *zput++ = ZEncode(loc);
14004e2e9504SJed Brown }
14014e2e9504SJed Brown }
14024e2e9504SJed Brown continue;
14034e2e9504SJed Brown }
14043431e603SJed Brown PetscAssert(bc_match == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face matches multiple face sets");
14053431e603SJed Brown PetscCall(DMLabelSetValue(label, f, face_marker_dim[dim][bc]));
14063431e603SJed Brown bc_match++;
14073431e603SJed Brown }
14083431e603SJed Brown }
14093431e603SJed Brown }
14103431e603SJed Brown // Ensure that the Coordinate DM has our new boundary labels
14113431e603SJed Brown DM cdm;
14123431e603SJed Brown PetscCall(DMGetCoordinateDM(dm, &cdm));
14133431e603SJed Brown PetscCall(DMCopyLabels(dm, cdm, PETSC_COPY_VALUES, PETSC_FALSE, DM_COPY_LABELS_FAIL));
14146725e60dSJed Brown if (periodicity[0] == DM_BOUNDARY_PERIODIC || (dim > 1 && periodicity[1] == DM_BOUNDARY_PERIODIC) || (dim > 2 && periodicity[2] == DM_BOUNDARY_PERIODIC)) {
14155f06a3ddSJed Brown PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(dm, &layout, vert_z, per_faces, lower, upper, periodicity, donor_face_closure, my_donor_faces));
14166725e60dSJed Brown }
14179ae3492bSJames Wright for (PetscInt i = 0; i < 3; i++) {
14189ae3492bSJames Wright PetscCall(PetscSegBufferDestroy(&per_faces[i]));
14199ae3492bSJames Wright PetscCall(PetscSegBufferDestroy(&donor_face_closure[i]));
14209ae3492bSJames Wright PetscCall(PetscSegBufferDestroy(&my_donor_faces[i]));
14219ae3492bSJames Wright }
14223431e603SJed Brown }
14234e2e9504SJed Brown PetscCall(PetscFree(layout.zstarts));
14243431e603SJed Brown PetscCall(PetscFree(vert_z));
1425ea7b3863SJames Wright PetscCall(PetscLogEventEnd(DMPLEX_CreateBoxSFC, dm, 0, 0, 0));
14263ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
14273e72e933SJed Brown }
14284e2e9504SJed Brown
14294e2e9504SJed Brown /*@
14305f06a3ddSJed Brown DMPlexSetIsoperiodicFaceSF - Express periodicity from an existing mesh
14314e2e9504SJed Brown
143220f4b53cSBarry Smith Logically Collective
14334e2e9504SJed Brown
14344e2e9504SJed Brown Input Parameters:
14354e2e9504SJed Brown + dm - The `DMPLEX` on which to set periodicity
14361fca310dSJames Wright . num_face_sfs - Number of `PetscSF`s in `face_sfs`
14371fca310dSJames 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.
14384e2e9504SJed Brown
14394e2e9504SJed Brown Level: advanced
14404e2e9504SJed Brown
144153c0d4aeSBarry Smith Note:
14425dca41c3SJed Brown One can use `-dm_plex_shape zbox` to use this mode of periodicity, wherein the periodic points are distinct both globally
14434e2e9504SJed Brown and locally, but are paired when creating a global dof space.
14444e2e9504SJed Brown
14451cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexGetIsoperiodicFaceSF()`
14464e2e9504SJed Brown @*/
DMPlexSetIsoperiodicFaceSF(DM dm,PetscInt num_face_sfs,PetscSF * face_sfs)14471fca310dSJames Wright PetscErrorCode DMPlexSetIsoperiodicFaceSF(DM dm, PetscInt num_face_sfs, PetscSF *face_sfs)
14484e2e9504SJed Brown {
14494e2e9504SJed Brown DM_Plex *plex = (DM_Plex *)dm->data;
14504d86920dSPierre Jolivet
14514e2e9504SJed Brown PetscFunctionBegin;
14524e2e9504SJed Brown PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1453545efb26SJames Wright if (num_face_sfs) {
1454545efb26SJames Wright PetscAssertPointer(face_sfs, 3);
1455545efb26SJames Wright PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex));
1456545efb26SJames Wright } else PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
14572b4f33d9SJames Wright if (num_face_sfs == plex->periodic.num_face_sfs && (num_face_sfs == 0 || face_sfs == plex->periodic.face_sfs)) PetscFunctionReturn(PETSC_SUCCESS);
14582b4f33d9SJames Wright PetscCall(DMSetGlobalSection(dm, NULL));
14591fca310dSJames Wright
14601fca310dSJames Wright for (PetscInt i = 0; i < num_face_sfs; i++) PetscCall(PetscObjectReference((PetscObject)face_sfs[i]));
14611fca310dSJames Wright if (plex->periodic.num_face_sfs > 0) {
14621fca310dSJames Wright for (PetscInt i = 0; i < plex->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&plex->periodic.face_sfs[i]));
14631fca310dSJames Wright PetscCall(PetscFree(plex->periodic.face_sfs));
14641fca310dSJames Wright }
14651fca310dSJames Wright
14661fca310dSJames Wright plex->periodic.num_face_sfs = num_face_sfs;
14671fca310dSJames Wright PetscCall(PetscCalloc1(num_face_sfs, &plex->periodic.face_sfs));
14681fca310dSJames Wright for (PetscInt i = 0; i < num_face_sfs; i++) plex->periodic.face_sfs[i] = face_sfs[i];
14695f06a3ddSJed Brown
14705f06a3ddSJed Brown DM cdm = dm->coordinates[0].dm; // Can't DMGetCoordinateDM because it automatically creates one
14715f06a3ddSJed Brown if (cdm) {
14721fca310dSJames Wright PetscCall(DMPlexSetIsoperiodicFaceSF(cdm, num_face_sfs, face_sfs));
14731fca310dSJames Wright if (face_sfs) cdm->periodic.setup = DMPeriodicCoordinateSetUp_Internal;
14745f06a3ddSJed Brown }
14753ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
14764e2e9504SJed Brown }
14774e2e9504SJed Brown
14781fca310dSJames Wright /*@C
14795f06a3ddSJed Brown DMPlexGetIsoperiodicFaceSF - Obtain periodicity for a mesh
14804e2e9504SJed Brown
148120f4b53cSBarry Smith Logically Collective
14824e2e9504SJed Brown
148320f4b53cSBarry Smith Input Parameter:
14844e2e9504SJed Brown . dm - The `DMPLEX` for which to obtain periodic relation
14854e2e9504SJed Brown
14869cde84edSJose E. Roman Output Parameters:
14871fca310dSJames Wright + num_face_sfs - Number of `PetscSF`s in the array
14881fca310dSJames 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.
14894e2e9504SJed Brown
14904e2e9504SJed Brown Level: advanced
14914e2e9504SJed Brown
14921cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()`
14934e2e9504SJed Brown @*/
DMPlexGetIsoperiodicFaceSF(DM dm,PetscInt * num_face_sfs,const PetscSF ** face_sfs)14941fca310dSJames Wright PetscErrorCode DMPlexGetIsoperiodicFaceSF(DM dm, PetscInt *num_face_sfs, const PetscSF **face_sfs)
14954e2e9504SJed Brown {
149681316d9bSJames Wright PetscBool isPlex;
14974d86920dSPierre Jolivet
14984e2e9504SJed Brown PetscFunctionBegin;
14994e2e9504SJed Brown PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
150081316d9bSJames Wright PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
150181316d9bSJames Wright if (isPlex) {
150281316d9bSJames Wright DM_Plex *plex = (DM_Plex *)dm->data;
150381316d9bSJames Wright if (face_sfs) *face_sfs = plex->periodic.face_sfs;
150481316d9bSJames Wright if (num_face_sfs) *num_face_sfs = plex->periodic.num_face_sfs;
150581316d9bSJames Wright } else {
150681316d9bSJames Wright if (face_sfs) *face_sfs = NULL;
150781316d9bSJames Wright if (num_face_sfs) *num_face_sfs = 0;
150881316d9bSJames Wright }
15093ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
15104e2e9504SJed Brown }
15116725e60dSJed Brown
15125f06a3ddSJed Brown /*@C
15135f06a3ddSJed Brown DMPlexSetIsoperiodicFaceTransform - set geometric transform from donor to periodic points
15146725e60dSJed Brown
15156725e60dSJed Brown Logically Collective
15166725e60dSJed Brown
151760225df5SJacob Faibussowitsch Input Parameters:
151853c0d4aeSBarry Smith + dm - `DMPLEX` that has been configured with `DMPlexSetIsoperiodicFaceSF()`
15191fca310dSJames Wright . n - Number of transforms in array
15201fca310dSJames Wright - t - Array of 4x4 affine transformation basis.
15216725e60dSJed Brown
152253c0d4aeSBarry Smith Level: advanced
152353c0d4aeSBarry Smith
15245f06a3ddSJed Brown Notes:
15255f06a3ddSJed Brown Affine transforms are 4x4 matrices in which the leading 3x3 block expresses a rotation (or identity for no rotation),
15265f06a3ddSJed Brown the last column contains a translation vector, and the bottom row is all zero except the last entry, which must always
15275f06a3ddSJed Brown be 1. This representation is common in geometric modeling and allows affine transformations to be composed using
1528aaa8cc7dSPierre Jolivet simple matrix multiplication.
15295f06a3ddSJed Brown
15305f06a3ddSJed Brown Although the interface accepts a general affine transform, only affine translation is supported at present.
15315f06a3ddSJed Brown
153260225df5SJacob Faibussowitsch Developer Notes:
15335f06a3ddSJed Brown This interface should be replaced by making BasisTransform public, expanding it to support affine representations, and
15345f06a3ddSJed Brown adding GPU implementations to apply the G2L/L2G transforms.
153553c0d4aeSBarry Smith
15361cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()`
15376725e60dSJed Brown @*/
DMPlexSetIsoperiodicFaceTransform(DM dm,PetscInt n,const PetscScalar t[])1538cc4c1da9SBarry Smith PetscErrorCode DMPlexSetIsoperiodicFaceTransform(DM dm, PetscInt n, const PetscScalar t[])
15396725e60dSJed Brown {
15406725e60dSJed Brown DM_Plex *plex = (DM_Plex *)dm->data;
15414d86920dSPierre Jolivet
15426725e60dSJed Brown PetscFunctionBegin;
15436725e60dSJed Brown PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
15441fca310dSJames 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);
15451fca310dSJames Wright
1546d7d2d1d2SJames Wright PetscCall(PetscFree(plex->periodic.transform));
15471fca310dSJames Wright PetscCall(PetscMalloc1(n, &plex->periodic.transform));
15481fca310dSJames Wright for (PetscInt i = 0; i < n; i++) {
15496725e60dSJed Brown for (PetscInt j = 0; j < 4; j++) {
15501fca310dSJames Wright for (PetscInt k = 0; k < 4; k++) {
15511fca310dSJames Wright PetscCheck(j != k || t[i * 16 + j * 4 + k] == 1., PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Rotated transforms not supported");
15521fca310dSJames Wright plex->periodic.transform[i][j][k] = t[i * 16 + j * 4 + k];
15531fca310dSJames Wright }
15546725e60dSJed Brown }
15556725e60dSJed Brown }
15563ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
15576725e60dSJed Brown }
1558