13e72e933SJed Brown #include <petsc/private/dmpleximpl.h> /*I "petscdmplex.h" I*/ 23e72e933SJed Brown #include <petscsf.h> 33e72e933SJed Brown #include <petsc/private/hashset.h> 43e72e933SJed Brown 53e72e933SJed Brown typedef uint64_t ZCode; 63e72e933SJed Brown 73e72e933SJed Brown PETSC_HASH_SET(ZSet, ZCode, PetscHash_UInt64, PetscHashEqual) 83e72e933SJed Brown 93e72e933SJed Brown typedef struct { 103e72e933SJed Brown PetscInt i, j, k; 113e72e933SJed Brown } Ijk; 123e72e933SJed Brown 133e72e933SJed Brown typedef struct { 143e72e933SJed Brown Ijk eextent; 153e72e933SJed Brown Ijk vextent; 163e72e933SJed Brown PetscMPIInt comm_size; 173e72e933SJed Brown ZCode *zstarts; 183e72e933SJed Brown } ZLayout; 193e72e933SJed Brown 203e72e933SJed Brown static unsigned ZCodeSplit1(ZCode z) 213e72e933SJed Brown { 223e72e933SJed Brown z = ((z & 01001001001001001) | ((z >> 2) & 02002002002002002) | ((z >> 4) & 04004004004004004)); 233e72e933SJed Brown z = (z | (z >> 6) | (z >> 12)) & 0000000777000000777; 243e72e933SJed Brown z = (z | (z >> 18)) & 0777777; 253e72e933SJed Brown return (unsigned)z; 263e72e933SJed Brown } 273e72e933SJed Brown 283e72e933SJed Brown static ZCode ZEncode1(unsigned t) 293e72e933SJed Brown { 303e72e933SJed Brown ZCode z = t; 313e72e933SJed Brown z = (z | (z << 18)) & 0777000000777; 323e72e933SJed Brown z = (z | (z << 6) | (z << 12)) & 07007007007007007; 333e72e933SJed Brown z = (z | (z << 2) | (z << 4)) & 0111111111111111111; 343e72e933SJed Brown return z; 353e72e933SJed Brown } 363e72e933SJed Brown 373e72e933SJed Brown static Ijk ZCodeSplit(ZCode z) 383e72e933SJed Brown { 393e72e933SJed Brown Ijk c; 403e72e933SJed Brown c.i = ZCodeSplit1(z >> 2); 413e72e933SJed Brown c.j = ZCodeSplit1(z >> 1); 423e72e933SJed Brown c.k = ZCodeSplit1(z >> 0); 433e72e933SJed Brown return c; 443e72e933SJed Brown } 453e72e933SJed Brown 463e72e933SJed Brown static ZCode ZEncode(Ijk c) 473e72e933SJed Brown { 483e72e933SJed Brown ZCode z = (ZEncode1(c.i) << 2) | (ZEncode1(c.j) << 1) | ZEncode1(c.k); 493e72e933SJed Brown return z; 503e72e933SJed Brown } 513e72e933SJed Brown 523e72e933SJed Brown static PetscBool IjkActive(Ijk extent, Ijk l) 533e72e933SJed Brown { 543e72e933SJed Brown if (l.i < extent.i && l.j < extent.j && l.k < extent.k) return PETSC_TRUE; 553e72e933SJed Brown return PETSC_FALSE; 563e72e933SJed Brown } 573e72e933SJed Brown 586547ddbdSJed Brown // If z is not the base of an octet (last 3 bits 0), return 0. 596547ddbdSJed Brown // 606547ddbdSJed Brown // If z is the base of an octet, we recursively grow to the biggest structured octet. This is typically useful when a z 616547ddbdSJed Brown // is outside the domain and we wish to skip a (possibly recursively large) octet to find our next interesting point. 626547ddbdSJed Brown static ZCode ZStepOct(ZCode z) 636547ddbdSJed Brown { 646547ddbdSJed Brown if (PetscUnlikely(z == 0)) return 0; // Infinite loop below if z == 0 656547ddbdSJed Brown ZCode step = 07; 666547ddbdSJed Brown for (; (z & step) == 0; step = (step << 3) | 07) { } 676547ddbdSJed Brown return step >> 3; 686547ddbdSJed Brown } 696547ddbdSJed Brown 703e72e933SJed Brown // Since element/vertex box extents are typically not equal powers of 2, Z codes that lie within the domain are not contiguous. 715f06a3ddSJed Brown static PetscErrorCode ZLayoutCreate(PetscMPIInt size, const PetscInt eextent[3], const PetscInt vextent[3], ZLayout *layout) 723e72e933SJed Brown { 73c77877e3SJed Brown PetscFunctionBegin; 745f06a3ddSJed Brown layout->eextent.i = eextent[0]; 755f06a3ddSJed Brown layout->eextent.j = eextent[1]; 765f06a3ddSJed Brown layout->eextent.k = eextent[2]; 775f06a3ddSJed Brown layout->vextent.i = vextent[0]; 785f06a3ddSJed Brown layout->vextent.j = vextent[1]; 795f06a3ddSJed Brown layout->vextent.k = vextent[2]; 805f06a3ddSJed Brown layout->comm_size = size; 815f06a3ddSJed Brown layout->zstarts = NULL; 825f06a3ddSJed Brown PetscCall(PetscMalloc1(size + 1, &layout->zstarts)); 833e72e933SJed Brown 843e72e933SJed Brown PetscInt total_elems = eextent[0] * eextent[1] * eextent[2]; 853e72e933SJed Brown ZCode z = 0; 865f06a3ddSJed Brown layout->zstarts[0] = 0; 876547ddbdSJed Brown // This loop traverses all vertices in the global domain, so is worth making fast. We use ZStepBound 883e72e933SJed Brown for (PetscMPIInt r = 0; r < size; r++) { 893e72e933SJed Brown PetscInt elems_needed = (total_elems / size) + (total_elems % size > r), count; 903e72e933SJed Brown for (count = 0; count < elems_needed; z++) { 916547ddbdSJed Brown ZCode skip = ZStepOct(z); // optimistically attempt a longer step 926547ddbdSJed Brown for (ZCode s = skip;; s >>= 3) { 936547ddbdSJed Brown Ijk trial = ZCodeSplit(z + s); 946547ddbdSJed Brown if (IjkActive(layout->eextent, trial)) { 956547ddbdSJed Brown while (count + s + 1 > (ZCode)elems_needed) s >>= 3; // Shrink the octet 966547ddbdSJed Brown count += s + 1; 976547ddbdSJed Brown z += s; 986547ddbdSJed Brown break; 996547ddbdSJed Brown } 1006547ddbdSJed Brown if (s == 0) { // the whole skip octet is inactive 1016547ddbdSJed Brown z += skip; 1026547ddbdSJed Brown break; 1036547ddbdSJed Brown } 1046547ddbdSJed Brown } 1053e72e933SJed Brown } 1063e72e933SJed Brown // Pick up any extra vertices in the Z ordering before the next rank's first owned element. 107c77877e3SJed Brown // 1085f06a3ddSJed Brown // This leads to poorly balanced vertices when eextent is a power of 2, since all the fringe vertices end up 109c77877e3SJed Brown // on the last rank. A possible solution is to balance the Z-order vertices independently from the cells, which will 110c77877e3SJed Brown // result in a lot of element closures being remote. We could finish marking boundary conditions, then do a round of 111c77877e3SJed Brown // vertex ownership smoothing (which would reorder and redistribute vertices without touching element distribution). 112c77877e3SJed Brown // Another would be to have an analytic ownership criteria for vertices in the fringe veextent - eextent. This would 113c77877e3SJed Brown // complicate the job of identifying an owner and its offset. 1145f06a3ddSJed Brown // 1155f06a3ddSJed Brown // The current recommended approach is to let `-dm_distribute 1` (default) resolve vertex ownership. This is 1165f06a3ddSJed Brown // *mandatory* with isoperiodicity (except in special cases) to remove standed vertices from local spaces. Here's 1175f06a3ddSJed Brown // the issue: 1185f06a3ddSJed Brown // 1195f06a3ddSJed Brown // Consider this partition on rank 0 (left) and rank 1. 1205f06a3ddSJed Brown // 1215f06a3ddSJed Brown // 4 -------- 5 -- 14 --10 -- 21 --11 1225f06a3ddSJed Brown // | | | 1235f06a3ddSJed Brown // 7 -- 16 -- 8 | | | 1245f06a3ddSJed Brown // | | 3 ------- 7 ------- 9 1255f06a3ddSJed Brown // | | | | 1265f06a3ddSJed Brown // 4 -------- 6 ------ 10 | | 1275f06a3ddSJed Brown // | | | 6 -- 16 -- 8 1285f06a3ddSJed Brown // | | | 1295f06a3ddSJed Brown // 3 ---11--- 5 --18-- 9 1305f06a3ddSJed Brown // 1315f06a3ddSJed Brown // The periodic face SF looks like 1325f06a3ddSJed Brown // [0] Number of roots=21, leaves=1, remote ranks=1 1335f06a3ddSJed Brown // [0] 16 <- (0,11) 1345f06a3ddSJed Brown // [1] Number of roots=22, leaves=2, remote ranks=2 1355f06a3ddSJed Brown // [1] 14 <- (0,18) 1365f06a3ddSJed Brown // [1] 21 <- (1,16) 1375f06a3ddSJed Brown // 1385f06a3ddSJed Brown // In handling face (0,16), rank 0 learns that (0,7) and (0,8) map to (0,3) and (0,5) respectively, thus we won't use 1395f06a3ddSJed Brown // the point SF links to (1,4) and (1,5). Rank 1 learns about the periodic mapping of (1,5) while handling face 1405f06a3ddSJed Brown // (1,14), but never learns that vertex (1,4) has been mapped to (0,3) by face (0,16). 1415f06a3ddSJed Brown // 1425f06a3ddSJed Brown // We can relatively easily inform vertex (1,4) of this mapping, but it stays in rank 1's local space despite not 1435f06a3ddSJed Brown // being in the closure and thus not being contributed to. This would be mostly harmless except that some viewer 1445f06a3ddSJed Brown // routines expect all local points to be somehow significant. It is not easy to analytically remove the (1,4) 1455f06a3ddSJed Brown // vertex because the point SF and isoperiodic face SF would need to be updated to account for removal of the 1465f06a3ddSJed Brown // stranded vertices. 1475f06a3ddSJed Brown for (; z <= ZEncode(layout->vextent); z++) { 1483e72e933SJed Brown Ijk loc = ZCodeSplit(z); 1495f06a3ddSJed Brown if (IjkActive(layout->eextent, loc)) break; 1506547ddbdSJed Brown z += ZStepOct(z); 1513e72e933SJed Brown } 1525f06a3ddSJed Brown layout->zstarts[r + 1] = z; 1533e72e933SJed Brown } 1546547ddbdSJed Brown layout->zstarts[size] = ZEncode(layout->vextent); 1553ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1563e72e933SJed Brown } 1573e72e933SJed Brown 1584e2e9504SJed Brown static PetscInt ZLayoutElementsOnRank(const ZLayout *layout, PetscMPIInt rank) 1594e2e9504SJed Brown { 1604e2e9504SJed Brown PetscInt remote_elem = 0; 1614e2e9504SJed Brown for (ZCode rz = layout->zstarts[rank]; rz < layout->zstarts[rank + 1]; rz++) { 1624e2e9504SJed Brown Ijk loc = ZCodeSplit(rz); 1634e2e9504SJed Brown if (IjkActive(layout->eextent, loc)) remote_elem++; 1646547ddbdSJed Brown else rz += ZStepOct(rz); 1654e2e9504SJed Brown } 1664e2e9504SJed Brown return remote_elem; 1674e2e9504SJed Brown } 1684e2e9504SJed Brown 16966976f2fSJacob Faibussowitsch static PetscInt ZCodeFind(ZCode key, PetscInt n, const ZCode X[]) 1703e72e933SJed Brown { 1713e72e933SJed Brown PetscInt lo = 0, hi = n; 1723e72e933SJed Brown 1733e72e933SJed Brown if (n == 0) return -1; 1743e72e933SJed Brown while (hi - lo > 1) { 1753e72e933SJed Brown PetscInt mid = lo + (hi - lo) / 2; 1763e72e933SJed Brown if (key < X[mid]) hi = mid; 1773e72e933SJed Brown else lo = mid; 1783e72e933SJed Brown } 1793e72e933SJed Brown return key == X[lo] ? lo : -(lo + (key > X[lo]) + 1); 1803e72e933SJed Brown } 1813e72e933SJed Brown 1829ae3492bSJames 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]) 1834e2e9504SJed Brown { 1844e2e9504SJed Brown MPI_Comm comm; 1859ae3492bSJames Wright PetscInt dim, vStart, vEnd; 1864e2e9504SJed Brown PetscMPIInt size; 1879ae3492bSJames Wright PetscSF face_sfs[3]; 1889ae3492bSJames Wright PetscScalar transforms[3][4][4] = {{{0}}}; 1894e2e9504SJed Brown 1904e2e9504SJed Brown PetscFunctionBegin; 1914e2e9504SJed Brown PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 1924e2e9504SJed Brown PetscCallMPI(MPI_Comm_size(comm, &size)); 1934e2e9504SJed Brown PetscCall(DMGetDimension(dm, &dim)); 1944e2e9504SJed Brown const PetscInt csize = PetscPowInt(2, dim - 1); 1954e2e9504SJed Brown PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 1969ae3492bSJames Wright 1979ae3492bSJames Wright PetscInt num_directions = 0; 1989ae3492bSJames Wright for (PetscInt direction = 0; direction < dim; direction++) { 1999ae3492bSJames Wright size_t num_faces; 2009ae3492bSJames Wright PetscInt *faces; 2019ae3492bSJames Wright ZCode *donor_verts, *donor_minz; 2029ae3492bSJames Wright PetscSFNode *leaf; 2039ae3492bSJames Wright 2049ae3492bSJames Wright if (periodicity[direction] != DM_BOUNDARY_PERIODIC) continue; 2059ae3492bSJames Wright PetscCall(PetscSegBufferGetSize(per_faces[direction], &num_faces)); 2069ae3492bSJames Wright PetscCall(PetscSegBufferExtractInPlace(per_faces[direction], &faces)); 2079ae3492bSJames Wright PetscCall(PetscSegBufferExtractInPlace(donor_face_closure[direction], &donor_verts)); 2084e2e9504SJed Brown PetscCall(PetscMalloc1(num_faces, &donor_minz)); 2094e2e9504SJed Brown PetscCall(PetscMalloc1(num_faces, &leaf)); 2104e2e9504SJed Brown for (PetscInt i = 0; i < (PetscInt)num_faces; i++) { 2114e2e9504SJed Brown ZCode minz = donor_verts[i * csize]; 2124e2e9504SJed Brown for (PetscInt j = 1; j < csize; j++) minz = PetscMin(minz, donor_verts[i * csize + j]); 2134e2e9504SJed Brown donor_minz[i] = minz; 2144e2e9504SJed Brown } 2154e2e9504SJed Brown { 2164e2e9504SJed Brown PetscBool sorted; 2174e2e9504SJed Brown PetscCall(PetscSortedInt64(num_faces, (const PetscInt64 *)donor_minz, &sorted)); 2189ae3492bSJames Wright // If a donor vertex were chosen to broker multiple faces, we would have a logic error. 2199ae3492bSJames Wright // Checking for sorting is a cheap check that there are no duplicates. 2209ae3492bSJames Wright PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_PLIB, "minz not sorted; possible duplicates not checked"); 2214e2e9504SJed Brown } 2224e2e9504SJed Brown for (PetscInt i = 0; i < (PetscInt)num_faces;) { 2234e2e9504SJed Brown ZCode z = donor_minz[i]; 2244e2e9504SJed Brown PetscInt remote_rank = ZCodeFind(z, size + 1, layout->zstarts), remote_count = 0; 2254e2e9504SJed Brown if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1; 2264e2e9504SJed Brown // Process all the vertices on this rank 2274e2e9504SJed Brown for (ZCode rz = layout->zstarts[remote_rank]; rz < layout->zstarts[remote_rank + 1]; rz++) { 2284e2e9504SJed Brown Ijk loc = ZCodeSplit(rz); 2294e2e9504SJed Brown if (rz == z) { 2304e2e9504SJed Brown leaf[i].rank = remote_rank; 2314e2e9504SJed Brown leaf[i].index = remote_count; 2324e2e9504SJed Brown i++; 2334e2e9504SJed Brown if (i == (PetscInt)num_faces) break; 2344e2e9504SJed Brown z = donor_minz[i]; 2354e2e9504SJed Brown } 2364e2e9504SJed Brown if (IjkActive(layout->vextent, loc)) remote_count++; 2374e2e9504SJed Brown } 2384e2e9504SJed Brown } 2394e2e9504SJed Brown PetscCall(PetscFree(donor_minz)); 2409ae3492bSJames Wright PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &face_sfs[num_directions])); 2419ae3492bSJames Wright PetscCall(PetscSFSetGraph(face_sfs[num_directions], vEnd - vStart, num_faces, NULL, PETSC_USE_POINTER, leaf, PETSC_USE_POINTER)); 2424e2e9504SJed Brown const PetscInt *my_donor_degree; 2439ae3492bSJames Wright PetscCall(PetscSFComputeDegreeBegin(face_sfs[num_directions], &my_donor_degree)); 2449ae3492bSJames Wright PetscCall(PetscSFComputeDegreeEnd(face_sfs[num_directions], &my_donor_degree)); 2454e2e9504SJed Brown PetscInt num_multiroots = 0; 2464e2e9504SJed Brown for (PetscInt i = 0; i < vEnd - vStart; i++) { 2474e2e9504SJed Brown num_multiroots += my_donor_degree[i]; 2484e2e9504SJed Brown if (my_donor_degree[i] == 0) continue; 2495f06a3ddSJed Brown PetscAssert(my_donor_degree[i] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex has multiple faces"); 2504e2e9504SJed Brown } 2514e2e9504SJed Brown PetscInt *my_donors, *donor_indices, *my_donor_indices; 2524e2e9504SJed Brown size_t num_my_donors; 2539ae3492bSJames Wright PetscCall(PetscSegBufferGetSize(my_donor_faces[direction], &num_my_donors)); 2544e2e9504SJed Brown PetscCheck((PetscInt)num_my_donors == num_multiroots, PETSC_COMM_SELF, PETSC_ERR_SUP, "Donor request does not match expected donors"); 2559ae3492bSJames Wright PetscCall(PetscSegBufferExtractInPlace(my_donor_faces[direction], &my_donors)); 2564e2e9504SJed Brown PetscCall(PetscMalloc1(vEnd - vStart, &my_donor_indices)); 2574e2e9504SJed Brown for (PetscInt i = 0; i < (PetscInt)num_my_donors; i++) { 2584e2e9504SJed Brown PetscInt f = my_donors[i]; 2594e2e9504SJed Brown PetscInt num_points, *points = NULL, minv = PETSC_MAX_INT; 2604e2e9504SJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points)); 2614e2e9504SJed Brown for (PetscInt j = 0; j < num_points; j++) { 2624e2e9504SJed Brown PetscInt p = points[2 * j]; 2634e2e9504SJed Brown if (p < vStart || vEnd <= p) continue; 2644e2e9504SJed Brown minv = PetscMin(minv, p); 2654e2e9504SJed Brown } 2664e2e9504SJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points)); 2675f06a3ddSJed Brown PetscAssert(my_donor_degree[minv - vStart] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex not requested"); 2684e2e9504SJed Brown my_donor_indices[minv - vStart] = f; 2694e2e9504SJed Brown } 2704e2e9504SJed Brown PetscCall(PetscMalloc1(num_faces, &donor_indices)); 2719ae3492bSJames Wright PetscCall(PetscSFBcastBegin(face_sfs[num_directions], MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE)); 2729ae3492bSJames Wright PetscCall(PetscSFBcastEnd(face_sfs[num_directions], MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE)); 2734e2e9504SJed Brown PetscCall(PetscFree(my_donor_indices)); 2744e2e9504SJed Brown // Modify our leafs so they point to donor faces instead of donor minz. Additionally, give them indices as faces. 2754e2e9504SJed Brown for (PetscInt i = 0; i < (PetscInt)num_faces; i++) leaf[i].index = donor_indices[i]; 2764e2e9504SJed Brown PetscCall(PetscFree(donor_indices)); 2774e2e9504SJed Brown PetscInt pStart, pEnd; 2784e2e9504SJed Brown PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 2799ae3492bSJames Wright PetscCall(PetscSFSetGraph(face_sfs[num_directions], pEnd - pStart, num_faces, faces, PETSC_COPY_VALUES, leaf, PETSC_OWN_POINTER)); 2809ae3492bSJames Wright { 2819ae3492bSJames Wright char face_sf_name[PETSC_MAX_PATH_LEN]; 2829ae3492bSJames Wright PetscCall(PetscSNPrintf(face_sf_name, sizeof face_sf_name, "Z-order Isoperiodic Faces #%" PetscInt_FMT, num_directions)); 2839ae3492bSJames Wright PetscCall(PetscObjectSetName((PetscObject)face_sfs[num_directions], face_sf_name)); 2849ae3492bSJames Wright } 2854e2e9504SJed Brown 2869ae3492bSJames Wright transforms[num_directions][0][0] = 1; 2879ae3492bSJames Wright transforms[num_directions][1][1] = 1; 2889ae3492bSJames Wright transforms[num_directions][2][2] = 1; 2899ae3492bSJames Wright transforms[num_directions][3][3] = 1; 2909ae3492bSJames Wright transforms[num_directions][direction][3] = upper[direction] - lower[direction]; 2919ae3492bSJames Wright num_directions++; 2929ae3492bSJames Wright } 2936725e60dSJed Brown 2941fca310dSJames Wright PetscCall(DMPlexSetIsoperiodicFaceSF(dm, num_directions, face_sfs)); 2951fca310dSJames Wright PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, num_directions, (PetscScalar *)transforms)); 2969ae3492bSJames Wright 2979ae3492bSJames Wright for (PetscInt i = 0; i < num_directions; i++) PetscCall(PetscSFDestroy(&face_sfs[i])); 2983ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2994e2e9504SJed Brown } 3004e2e9504SJed Brown 3015f06a3ddSJed Brown // This is a DMGlobalToLocalHook that applies the affine offsets. When extended for rotated periodicity, it'll need to 3025f06a3ddSJed Brown // apply a rotatonal transform and similar operations will be needed for fields (e.g., to rotate a velocity vector). 3035f06a3ddSJed Brown // We use this crude approach here so we don't have to write new GPU kernels yet. 3046725e60dSJed Brown static PetscErrorCode DMCoordAddPeriodicOffsets_Private(DM dm, Vec g, InsertMode mode, Vec l, void *ctx) 3056725e60dSJed Brown { 3066725e60dSJed Brown PetscFunctionBegin; 307*ddedc8f6SJames Wright // These `VecScatter`s should be merged to improve efficiency; the scatters cannot be overlapped. 308*ddedc8f6SJames Wright for (PetscInt i = 0; i < dm->periodic.num_affines; i++) { 309*ddedc8f6SJames Wright PetscCall(VecScatterBegin(dm->periodic.affine_to_local[i], dm->periodic.affine[i], l, ADD_VALUES, SCATTER_FORWARD)); 310*ddedc8f6SJames Wright PetscCall(VecScatterEnd(dm->periodic.affine_to_local[i], dm->periodic.affine[i], l, ADD_VALUES, SCATTER_FORWARD)); 311*ddedc8f6SJames Wright } 3123ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 3136725e60dSJed Brown } 3146725e60dSJed Brown 3155f06a3ddSJed Brown // Start with an SF for a positive depth (e.g., faces) and create a new SF for matched closure. The caller must ensure 3165f06a3ddSJed Brown // that both the donor (root) face and the periodic (leaf) face have consistent orientation, meaning that their closures 3175f06a3ddSJed Brown // are isomorphic. It may be useful/necessary for this restriction to be loosened. 3186725e60dSJed Brown // 3195f06a3ddSJed Brown // Output Arguments: 3205f06a3ddSJed Brown // 3215f06a3ddSJed Brown // + closure_sf - augmented point SF (see `DMGetPointSF()`) that includes the faces and all points in its closure. This 3225f06a3ddSJed Brown // can be used to create a global section and section SF. 323b83f62b0SJames 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 3245f06a3ddSJed Brown // transformation to periodic dofs; see DMPeriodicCoordinateSetUp_Internal(). 3255f06a3ddSJed Brown // 326b83f62b0SJames Wright static PetscErrorCode DMPlexCreateIsoperiodicPointSF_Private(DM dm, PetscInt num_face_sfs, PetscSF *face_sfs, PetscSF *closure_sf, IS **is_points) 3276725e60dSJed Brown { 3286725e60dSJed Brown MPI_Comm comm; 3296725e60dSJed Brown PetscMPIInt rank; 3306725e60dSJed Brown PetscSF point_sf; 331b83f62b0SJames Wright PetscInt nroots, nleaves; 332b83f62b0SJames Wright const PetscInt *filocal; 333b83f62b0SJames Wright const PetscSFNode *firemote; 3346725e60dSJed Brown 3356725e60dSJed Brown PetscFunctionBegin; 3366725e60dSJed Brown PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 3376725e60dSJed Brown PetscCallMPI(MPI_Comm_rank(comm, &rank)); 3386725e60dSJed Brown PetscCall(DMGetPointSF(dm, &point_sf)); // Point SF has remote points 339b83f62b0SJames Wright PetscCall(PetscMalloc1(num_face_sfs, is_points)); 340b83f62b0SJames Wright 341b83f62b0SJames Wright for (PetscInt f = 0; f < num_face_sfs; f++) { 342b83f62b0SJames Wright PetscSF face_sf = face_sfs[f]; 3436725e60dSJed Brown PetscInt *rootdata, *leafdata; 344b83f62b0SJames Wright 345b83f62b0SJames Wright PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote)); 3466725e60dSJed Brown PetscCall(PetscCalloc2(2 * nroots, &rootdata, 2 * nroots, &leafdata)); 3476725e60dSJed Brown for (PetscInt i = 0; i < nleaves; i++) { 3486725e60dSJed Brown PetscInt point = filocal[i], cl_size, *closure = NULL; 3496725e60dSJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 3506725e60dSJed Brown leafdata[point] = cl_size - 1; 3516725e60dSJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 3526725e60dSJed Brown } 3536725e60dSJed Brown PetscCall(PetscSFReduceBegin(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM)); 3546725e60dSJed Brown PetscCall(PetscSFReduceEnd(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM)); 3556725e60dSJed Brown 3566725e60dSJed Brown PetscInt root_offset = 0; 3576725e60dSJed Brown for (PetscInt p = 0; p < nroots; p++) { 3586725e60dSJed Brown const PetscInt *donor_dof = rootdata + nroots; 3596725e60dSJed Brown if (donor_dof[p] == 0) { 3606725e60dSJed Brown rootdata[2 * p] = -1; 3616725e60dSJed Brown rootdata[2 * p + 1] = -1; 3626725e60dSJed Brown continue; 3636725e60dSJed Brown } 3646725e60dSJed Brown PetscInt cl_size; 3656725e60dSJed Brown PetscInt *closure = NULL; 3666725e60dSJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 3676725e60dSJed Brown // cl_size - 1 = points not including self 3685f06a3ddSJed Brown PetscAssert(donor_dof[p] == cl_size - 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Reduced leaf cone sizes do not match root cone sizes"); 3696725e60dSJed Brown rootdata[2 * p] = root_offset; 3706725e60dSJed Brown rootdata[2 * p + 1] = cl_size - 1; 3716725e60dSJed Brown root_offset += cl_size - 1; 3726725e60dSJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 3736725e60dSJed Brown } 3746725e60dSJed Brown PetscCall(PetscSFBcastBegin(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 3756725e60dSJed Brown PetscCall(PetscSFBcastEnd(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 3766725e60dSJed Brown // Count how many leaves we need to communicate the closures 3776725e60dSJed Brown PetscInt leaf_offset = 0; 3786725e60dSJed Brown for (PetscInt i = 0; i < nleaves; i++) { 3796725e60dSJed Brown PetscInt point = filocal[i]; 3806725e60dSJed Brown if (leafdata[2 * point + 1] < 0) continue; 3816725e60dSJed Brown leaf_offset += leafdata[2 * point + 1]; 3826725e60dSJed Brown } 3836725e60dSJed Brown 3846725e60dSJed Brown PetscSFNode *closure_leaf; 3856725e60dSJed Brown PetscCall(PetscMalloc1(leaf_offset, &closure_leaf)); 3866725e60dSJed Brown leaf_offset = 0; 3876725e60dSJed Brown for (PetscInt i = 0; i < nleaves; i++) { 3886725e60dSJed Brown PetscInt point = filocal[i]; 3896725e60dSJed Brown PetscInt cl_size = leafdata[2 * point + 1]; 3906725e60dSJed Brown if (cl_size < 0) continue; 3916725e60dSJed Brown for (PetscInt j = 0; j < cl_size; j++) { 3926725e60dSJed Brown closure_leaf[leaf_offset].rank = firemote[i].rank; 3936725e60dSJed Brown closure_leaf[leaf_offset].index = leafdata[2 * point] + j; 3946725e60dSJed Brown leaf_offset++; 3956725e60dSJed Brown } 3966725e60dSJed Brown } 3976725e60dSJed Brown 3986725e60dSJed Brown PetscSF sf_closure; 3996725e60dSJed Brown PetscCall(PetscSFCreate(comm, &sf_closure)); 4006725e60dSJed Brown PetscCall(PetscSFSetGraph(sf_closure, root_offset, leaf_offset, NULL, PETSC_USE_POINTER, closure_leaf, PETSC_OWN_POINTER)); 4016725e60dSJed Brown 402b83f62b0SJames Wright PetscSFNode *leaf_donor_closure; 403b83f62b0SJames Wright { // Pack root buffer with owner for every point in the root cones 4046725e60dSJed Brown PetscSFNode *donor_closure; 405b83f62b0SJames Wright const PetscInt *pilocal; 406b83f62b0SJames Wright const PetscSFNode *piremote; 407b83f62b0SJames Wright PetscInt npoints; 408b83f62b0SJames Wright 409b83f62b0SJames Wright PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, &pilocal, &piremote)); 4106725e60dSJed Brown PetscCall(PetscCalloc1(root_offset, &donor_closure)); 4116725e60dSJed Brown root_offset = 0; 4126725e60dSJed Brown for (PetscInt p = 0; p < nroots; p++) { 4136725e60dSJed Brown if (rootdata[2 * p] < 0) continue; 4146725e60dSJed Brown PetscInt cl_size; 4156725e60dSJed Brown PetscInt *closure = NULL; 4166725e60dSJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 4176725e60dSJed Brown for (PetscInt j = 1; j < cl_size; j++) { 4186725e60dSJed Brown PetscInt c = closure[2 * j]; 4196725e60dSJed Brown if (pilocal) { 4206725e60dSJed Brown PetscInt found = -1; 4216725e60dSJed Brown if (npoints > 0) PetscCall(PetscFindInt(c, npoints, pilocal, &found)); 4226725e60dSJed Brown if (found >= 0) { 4236725e60dSJed Brown donor_closure[root_offset++] = piremote[found]; 4246725e60dSJed Brown continue; 4256725e60dSJed Brown } 4266725e60dSJed Brown } 4276725e60dSJed Brown // we own c 4286725e60dSJed Brown donor_closure[root_offset].rank = rank; 4296725e60dSJed Brown donor_closure[root_offset].index = c; 4306725e60dSJed Brown root_offset++; 4316725e60dSJed Brown } 4326725e60dSJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 4336725e60dSJed Brown } 4346725e60dSJed Brown 4356725e60dSJed Brown PetscCall(PetscMalloc1(leaf_offset, &leaf_donor_closure)); 4366725e60dSJed Brown PetscCall(PetscSFBcastBegin(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE)); 4376725e60dSJed Brown PetscCall(PetscSFBcastEnd(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE)); 4386725e60dSJed Brown PetscCall(PetscSFDestroy(&sf_closure)); 4396725e60dSJed Brown PetscCall(PetscFree(donor_closure)); 440b83f62b0SJames Wright } 4416725e60dSJed Brown 4426725e60dSJed Brown PetscSFNode *new_iremote; 4436725e60dSJed Brown PetscCall(PetscCalloc1(nroots, &new_iremote)); 4446725e60dSJed Brown for (PetscInt i = 0; i < nroots; i++) new_iremote[i].rank = -1; 4456725e60dSJed Brown // Walk leaves and match vertices 4466725e60dSJed Brown leaf_offset = 0; 4476725e60dSJed Brown for (PetscInt i = 0; i < nleaves; i++) { 4486725e60dSJed Brown PetscInt point = filocal[i], cl_size; 4496725e60dSJed Brown PetscInt *closure = NULL; 4506725e60dSJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 4516725e60dSJed Brown for (PetscInt j = 1; j < cl_size; j++) { // TODO: should we send donor edge orientations so we can flip for consistency? 4526725e60dSJed Brown PetscInt c = closure[2 * j]; 4536725e60dSJed Brown PetscSFNode lc = leaf_donor_closure[leaf_offset]; 4546725e60dSJed Brown // printf("[%d] face %d.%d: %d ?-- (%d,%d)\n", rank, point, j, c, lc.rank, lc.index); 4556725e60dSJed Brown if (new_iremote[c].rank == -1) { 4566725e60dSJed Brown new_iremote[c] = lc; 4575f06a3ddSJed 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"); 4586725e60dSJed Brown leaf_offset++; 4596725e60dSJed Brown } 4606725e60dSJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 4616725e60dSJed Brown } 4626725e60dSJed Brown PetscCall(PetscFree(leaf_donor_closure)); 4636725e60dSJed Brown 4646725e60dSJed Brown // Include face points in closure SF 4656725e60dSJed Brown for (PetscInt i = 0; i < nleaves; i++) new_iremote[filocal[i]] = firemote[i]; 4666725e60dSJed Brown // consolidate leaves 4676725e60dSJed Brown PetscInt num_new_leaves = 0; 4686725e60dSJed Brown for (PetscInt i = 0; i < nroots; i++) { 4696725e60dSJed Brown if (new_iremote[i].rank == -1) continue; 4706725e60dSJed Brown new_iremote[num_new_leaves] = new_iremote[i]; 4716725e60dSJed Brown leafdata[num_new_leaves] = i; 4726725e60dSJed Brown num_new_leaves++; 4736725e60dSJed Brown } 474b83f62b0SJames Wright PetscCall(ISCreateGeneral(PETSC_COMM_SELF, num_new_leaves, leafdata, PETSC_COPY_VALUES, &(*is_points)[f])); 4756725e60dSJed Brown 4766725e60dSJed Brown PetscSF csf; 4776725e60dSJed Brown PetscCall(PetscSFCreate(comm, &csf)); 4786725e60dSJed Brown PetscCall(PetscSFSetGraph(csf, nroots, num_new_leaves, leafdata, PETSC_COPY_VALUES, new_iremote, PETSC_COPY_VALUES)); 4796725e60dSJed Brown PetscCall(PetscFree(new_iremote)); // copy and delete because new_iremote is longer than it needs to be 4806725e60dSJed Brown PetscCall(PetscFree2(rootdata, leafdata)); 4816725e60dSJed Brown 482b83f62b0SJames Wright PetscInt npoints; 483b83f62b0SJames Wright PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, NULL, NULL)); 4845f06a3ddSJed Brown if (npoints < 0) { // empty point_sf 4856725e60dSJed Brown *closure_sf = csf; 4865f06a3ddSJed Brown } else { 4875f06a3ddSJed Brown PetscCall(PetscSFMerge(point_sf, csf, closure_sf)); 4885f06a3ddSJed Brown PetscCall(PetscSFDestroy(&csf)); 4895f06a3ddSJed Brown } 490b83f62b0SJames Wright if (f > 0) PetscCall(PetscSFDestroy(&point_sf)); // Only destory if point_sf is from previous calls to PetscSFMerge 491b83f62b0SJames Wright point_sf = *closure_sf; // Use combined point + isoperiodic SF to define point ownership for further face_sf 492b83f62b0SJames Wright } 4935f06a3ddSJed Brown PetscCall(PetscObjectSetName((PetscObject)*closure_sf, "Composed Periodic Points")); 4943ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 4956725e60dSJed Brown } 4966725e60dSJed Brown 4975f06a3ddSJed Brown static PetscErrorCode DMGetIsoperiodicPointSF_Plex(DM dm, PetscSF *sf) 4986725e60dSJed Brown { 4996725e60dSJed Brown DM_Plex *plex = (DM_Plex *)dm->data; 5006725e60dSJed Brown 5016725e60dSJed Brown PetscFunctionBegin; 502b83f62b0SJames 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)); 5036725e60dSJed Brown if (sf) *sf = plex->periodic.composed_sf; 5043ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 5056725e60dSJed Brown } 5066725e60dSJed Brown 5075f06a3ddSJed Brown PetscErrorCode DMPlexMigrateIsoperiodicFaceSF_Internal(DM old_dm, DM dm, PetscSF sf_migration) 5085f06a3ddSJed Brown { 5095f06a3ddSJed Brown DM_Plex *plex = (DM_Plex *)old_dm->data; 510a45b0079SJames Wright PetscSF sf_point, *new_face_sfs; 5115f06a3ddSJed Brown PetscMPIInt rank; 5125f06a3ddSJed Brown 5135f06a3ddSJed Brown PetscFunctionBegin; 5141fca310dSJames Wright if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS); 5155f06a3ddSJed Brown PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 5165f06a3ddSJed Brown PetscCall(DMGetPointSF(dm, &sf_point)); 517a45b0079SJames Wright PetscCall(PetscMalloc1(plex->periodic.num_face_sfs, &new_face_sfs)); 518a45b0079SJames Wright 519a45b0079SJames Wright for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) { 5205f06a3ddSJed Brown PetscInt old_npoints, new_npoints, old_nleaf, new_nleaf, point_nleaf; 5215f06a3ddSJed Brown PetscSFNode *new_leafdata, *rootdata, *leafdata; 5225f06a3ddSJed Brown const PetscInt *old_local, *point_local; 5235f06a3ddSJed Brown const PetscSFNode *old_remote, *point_remote; 524a45b0079SJames Wright PetscCall(PetscSFGetGraph(plex->periodic.face_sfs[f], &old_npoints, &old_nleaf, &old_local, &old_remote)); 5255f06a3ddSJed Brown PetscCall(PetscSFGetGraph(sf_migration, NULL, &new_nleaf, NULL, NULL)); 5265f06a3ddSJed Brown PetscCall(PetscSFGetGraph(sf_point, &new_npoints, &point_nleaf, &point_local, &point_remote)); 5275f06a3ddSJed Brown PetscAssert(new_nleaf == new_npoints, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected migration leaf space to match new point root space"); 5285f06a3ddSJed Brown PetscCall(PetscMalloc3(old_npoints, &rootdata, old_npoints, &leafdata, new_npoints, &new_leafdata)); 5295f06a3ddSJed Brown 5305f06a3ddSJed Brown // Fill new_leafdata with new owners of all points 5315f06a3ddSJed Brown for (PetscInt i = 0; i < new_npoints; i++) { 5325f06a3ddSJed Brown new_leafdata[i].rank = rank; 5335f06a3ddSJed Brown new_leafdata[i].index = i; 5345f06a3ddSJed Brown } 5355f06a3ddSJed Brown for (PetscInt i = 0; i < point_nleaf; i++) { 5365f06a3ddSJed Brown PetscInt j = point_local[i]; 5375f06a3ddSJed Brown new_leafdata[j] = point_remote[i]; 5385f06a3ddSJed Brown } 5395f06a3ddSJed Brown // REPLACE is okay because every leaf agrees about the new owners 5405f06a3ddSJed Brown PetscCall(PetscSFReduceBegin(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE)); 5415f06a3ddSJed Brown PetscCall(PetscSFReduceEnd(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE)); 5425f06a3ddSJed Brown // rootdata now contains the new owners 5435f06a3ddSJed Brown 5445f06a3ddSJed Brown // Send to leaves of old space 5455f06a3ddSJed Brown for (PetscInt i = 0; i < old_npoints; i++) { 5465f06a3ddSJed Brown leafdata[i].rank = -1; 5475f06a3ddSJed Brown leafdata[i].index = -1; 5485f06a3ddSJed Brown } 549a45b0079SJames Wright PetscCall(PetscSFBcastBegin(plex->periodic.face_sfs[f], MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 550a45b0079SJames Wright PetscCall(PetscSFBcastEnd(plex->periodic.face_sfs[f], MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 5515f06a3ddSJed Brown 5525f06a3ddSJed Brown // Send to new leaf space 5535f06a3ddSJed Brown PetscCall(PetscSFBcastBegin(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE)); 5545f06a3ddSJed Brown PetscCall(PetscSFBcastEnd(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE)); 5555f06a3ddSJed Brown 5565f06a3ddSJed Brown PetscInt nface = 0, *new_local; 5575f06a3ddSJed Brown PetscSFNode *new_remote; 5585f06a3ddSJed Brown for (PetscInt i = 0; i < new_npoints; i++) nface += (new_leafdata[i].rank >= 0); 5595f06a3ddSJed Brown PetscCall(PetscMalloc1(nface, &new_local)); 5605f06a3ddSJed Brown PetscCall(PetscMalloc1(nface, &new_remote)); 5615f06a3ddSJed Brown nface = 0; 5625f06a3ddSJed Brown for (PetscInt i = 0; i < new_npoints; i++) { 5635f06a3ddSJed Brown if (new_leafdata[i].rank == -1) continue; 5645f06a3ddSJed Brown new_local[nface] = i; 5655f06a3ddSJed Brown new_remote[nface] = new_leafdata[i]; 5665f06a3ddSJed Brown nface++; 5675f06a3ddSJed Brown } 5685f06a3ddSJed Brown PetscCall(PetscFree3(rootdata, leafdata, new_leafdata)); 569a45b0079SJames Wright PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &new_face_sfs[f])); 570a45b0079SJames Wright PetscCall(PetscSFSetGraph(new_face_sfs[f], new_npoints, nface, new_local, PETSC_OWN_POINTER, new_remote, PETSC_OWN_POINTER)); 571a45b0079SJames Wright { 572a45b0079SJames Wright char new_face_sf_name[PETSC_MAX_PATH_LEN]; 573a45b0079SJames Wright PetscCall(PetscSNPrintf(new_face_sf_name, sizeof new_face_sf_name, "Migrated Isoperiodic Faces #%" PetscInt_FMT, f)); 574a45b0079SJames Wright PetscCall(PetscObjectSetName((PetscObject)new_face_sfs[f], new_face_sf_name)); 575a45b0079SJames Wright } 576a45b0079SJames Wright } 577a45b0079SJames Wright 578a45b0079SJames Wright PetscCall(DMPlexSetIsoperiodicFaceSF(dm, plex->periodic.num_face_sfs, new_face_sfs)); 579a45b0079SJames Wright PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, plex->periodic.num_face_sfs, (PetscScalar *)plex->periodic.transform)); 580a45b0079SJames Wright for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) PetscCall(PetscSFDestroy(&new_face_sfs[f])); 581a45b0079SJames Wright PetscCall(PetscFree(new_face_sfs)); 5823ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 5835f06a3ddSJed Brown } 5845f06a3ddSJed Brown 5856725e60dSJed Brown PetscErrorCode DMPeriodicCoordinateSetUp_Internal(DM dm) 5866725e60dSJed Brown { 5876725e60dSJed Brown DM_Plex *plex = (DM_Plex *)dm->data; 588*ddedc8f6SJames Wright size_t count; 589*ddedc8f6SJames Wright IS isdof; 590*ddedc8f6SJames Wright PetscInt dim; 5914d86920dSPierre Jolivet 5926725e60dSJed Brown PetscFunctionBegin; 5931fca310dSJames Wright if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS); 5945f06a3ddSJed Brown PetscCall(DMGetIsoperiodicPointSF_Plex(dm, NULL)); 5955f06a3ddSJed Brown PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex)); 5966725e60dSJed Brown 5976725e60dSJed Brown PetscCall(DMGetDimension(dm, &dim)); 598*ddedc8f6SJames Wright dm->periodic.num_affines = plex->periodic.num_face_sfs; 599*ddedc8f6SJames Wright PetscCall(PetscMalloc2(dm->periodic.num_affines, &dm->periodic.affine_to_local, dm->periodic.num_affines, &dm->periodic.affine)); 600*ddedc8f6SJames Wright 601*ddedc8f6SJames Wright for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) { 6026725e60dSJed Brown { 6036725e60dSJed Brown PetscInt npoints; 6046725e60dSJed Brown const PetscInt *points; 605*ddedc8f6SJames Wright IS is = plex->periodic.periodic_points[f]; 6066725e60dSJed Brown PetscSegBuffer seg; 6076725e60dSJed Brown PetscSection section; 6086725e60dSJed Brown PetscCall(DMGetLocalSection(dm, §ion)); 6096725e60dSJed Brown PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 32, &seg)); 6106725e60dSJed Brown PetscCall(ISGetSize(is, &npoints)); 6116725e60dSJed Brown PetscCall(ISGetIndices(is, &points)); 6126725e60dSJed Brown for (PetscInt i = 0; i < npoints; i++) { 6136725e60dSJed Brown PetscInt point = points[i], off, dof; 6146725e60dSJed Brown PetscCall(PetscSectionGetOffset(section, point, &off)); 6156725e60dSJed Brown PetscCall(PetscSectionGetDof(section, point, &dof)); 6166725e60dSJed Brown PetscAssert(dof % dim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected dof %" PetscInt_FMT " not divisible by dimension %" PetscInt_FMT, dof, dim); 6176725e60dSJed Brown for (PetscInt j = 0; j < dof / dim; j++) { 6186725e60dSJed Brown PetscInt *slot; 6196725e60dSJed Brown PetscCall(PetscSegBufferGetInts(seg, 1, &slot)); 620729bdd54SJed Brown *slot = off / dim + j; 6216725e60dSJed Brown } 6226725e60dSJed Brown } 6236725e60dSJed Brown PetscInt *ind; 6246725e60dSJed Brown PetscCall(PetscSegBufferGetSize(seg, &count)); 6256725e60dSJed Brown PetscCall(PetscSegBufferExtractAlloc(seg, &ind)); 6266725e60dSJed Brown PetscCall(PetscSegBufferDestroy(&seg)); 6276725e60dSJed Brown PetscCall(ISCreateBlock(PETSC_COMM_SELF, dim, count, ind, PETSC_OWN_POINTER, &isdof)); 6286725e60dSJed Brown } 6296725e60dSJed Brown Vec L, P; 6306725e60dSJed Brown VecType vec_type; 6316725e60dSJed Brown VecScatter scatter; 6326725e60dSJed Brown PetscCall(DMGetLocalVector(dm, &L)); 6336725e60dSJed Brown PetscCall(VecCreate(PETSC_COMM_SELF, &P)); 6346725e60dSJed Brown PetscCall(VecSetSizes(P, count * dim, count * dim)); 6356725e60dSJed Brown PetscCall(VecGetType(L, &vec_type)); 6366725e60dSJed Brown PetscCall(VecSetType(P, vec_type)); 6376725e60dSJed Brown PetscCall(VecScatterCreate(P, NULL, L, isdof, &scatter)); 6386725e60dSJed Brown PetscCall(DMRestoreLocalVector(dm, &L)); 6396725e60dSJed Brown PetscCall(ISDestroy(&isdof)); 6406725e60dSJed Brown 6416725e60dSJed Brown { 6426725e60dSJed Brown PetscScalar *x; 6436725e60dSJed Brown PetscCall(VecGetArrayWrite(P, &x)); 6446725e60dSJed Brown for (PetscInt i = 0; i < (PetscInt)count; i++) { 645*ddedc8f6SJames Wright for (PetscInt j = 0; j < dim; j++) x[i * dim + j] = plex->periodic.transform[f][j][3]; 6466725e60dSJed Brown } 6476725e60dSJed Brown PetscCall(VecRestoreArrayWrite(P, &x)); 6486725e60dSJed Brown } 6496725e60dSJed Brown 650*ddedc8f6SJames Wright dm->periodic.affine_to_local[f] = scatter; 651*ddedc8f6SJames Wright dm->periodic.affine[f] = P; 652*ddedc8f6SJames Wright } 6536725e60dSJed Brown PetscCall(DMGlobalToLocalHookAdd(dm, NULL, DMCoordAddPeriodicOffsets_Private, NULL)); 6543ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6556725e60dSJed Brown } 6566725e60dSJed Brown 6575f06a3ddSJed Brown // We'll just orient all the edges, though only periodic boundary edges need orientation 6585f06a3ddSJed Brown static PetscErrorCode DMPlexOrientPositiveEdges_Private(DM dm) 6595f06a3ddSJed Brown { 6605f06a3ddSJed Brown PetscInt dim, eStart, eEnd; 6614d86920dSPierre Jolivet 6625f06a3ddSJed Brown PetscFunctionBegin; 6635f06a3ddSJed Brown PetscCall(DMGetDimension(dm, &dim)); 6643ba16761SJacob Faibussowitsch if (dim < 3) PetscFunctionReturn(PETSC_SUCCESS); // not necessary 6655f06a3ddSJed Brown PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd)); 6665f06a3ddSJed Brown for (PetscInt e = eStart; e < eEnd; e++) { 6675f06a3ddSJed Brown const PetscInt *cone; 6685f06a3ddSJed Brown PetscCall(DMPlexGetCone(dm, e, &cone)); 6695f06a3ddSJed Brown if (cone[0] > cone[1]) PetscCall(DMPlexOrientPoint(dm, e, -1)); 6705f06a3ddSJed Brown } 6713ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6725f06a3ddSJed Brown } 6735f06a3ddSJed Brown 6743e72e933SJed Brown PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate) 6753e72e933SJed Brown { 6763e72e933SJed Brown PetscInt eextent[3] = {1, 1, 1}, vextent[3] = {1, 1, 1}; 6773e72e933SJed Brown const Ijk closure_1[] = { 6783e72e933SJed Brown {0, 0, 0}, 6793e72e933SJed Brown {1, 0, 0}, 6803e72e933SJed Brown }; 6813e72e933SJed Brown const Ijk closure_2[] = { 6823e72e933SJed Brown {0, 0, 0}, 6833e72e933SJed Brown {1, 0, 0}, 6843e72e933SJed Brown {1, 1, 0}, 6853e72e933SJed Brown {0, 1, 0}, 6863e72e933SJed Brown }; 6873e72e933SJed Brown const Ijk closure_3[] = { 6883e72e933SJed Brown {0, 0, 0}, 6893e72e933SJed Brown {0, 1, 0}, 6903e72e933SJed Brown {1, 1, 0}, 6913e72e933SJed Brown {1, 0, 0}, 6923e72e933SJed Brown {0, 0, 1}, 6933e72e933SJed Brown {1, 0, 1}, 6943e72e933SJed Brown {1, 1, 1}, 6953e72e933SJed Brown {0, 1, 1}, 6963e72e933SJed Brown }; 6973431e603SJed Brown const Ijk *const closure_dim[] = {NULL, closure_1, closure_2, closure_3}; 6983431e603SJed Brown // This must be kept consistent with DMPlexCreateCubeMesh_Internal 6993431e603SJed Brown const PetscInt face_marker_1[] = {1, 2}; 7003431e603SJed Brown const PetscInt face_marker_2[] = {4, 2, 1, 3}; 7013431e603SJed Brown const PetscInt face_marker_3[] = {6, 5, 3, 4, 1, 2}; 7023431e603SJed Brown const PetscInt *const face_marker_dim[] = {NULL, face_marker_1, face_marker_2, face_marker_3}; 7036725e60dSJed Brown // Orient faces so the normal is in the positive axis and the first vertex is the one closest to zero. 7046725e60dSJed Brown // These orientations can be determined by examining cones of a reference quad and hex element. 7056725e60dSJed Brown const PetscInt face_orient_1[] = {0, 0}; 7066725e60dSJed Brown const PetscInt face_orient_2[] = {-1, 0, 0, -1}; 7076725e60dSJed Brown const PetscInt face_orient_3[] = {-2, 0, -2, 1, -2, 0}; 7086725e60dSJed Brown const PetscInt *const face_orient_dim[] = {NULL, face_orient_1, face_orient_2, face_orient_3}; 7093e72e933SJed Brown 7103e72e933SJed Brown PetscFunctionBegin; 7114f572ea9SToby Isaac PetscAssertPointer(dm, 1); 7123e72e933SJed Brown PetscValidLogicalCollectiveInt(dm, dim, 2); 7133e72e933SJed Brown PetscCall(DMSetDimension(dm, dim)); 7143e72e933SJed Brown PetscMPIInt rank, size; 7153e72e933SJed Brown PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size)); 7163e72e933SJed Brown PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 7173e72e933SJed Brown for (PetscInt i = 0; i < dim; i++) { 7183e72e933SJed Brown eextent[i] = faces[i]; 7193e72e933SJed Brown vextent[i] = faces[i] + 1; 7203e72e933SJed Brown } 721c77877e3SJed Brown ZLayout layout; 722c77877e3SJed Brown PetscCall(ZLayoutCreate(size, eextent, vextent, &layout)); 7233e72e933SJed Brown PetscZSet vset; // set of all vertices in the closure of the owned elements 7243e72e933SJed Brown PetscCall(PetscZSetCreate(&vset)); 7253e72e933SJed Brown PetscInt local_elems = 0; 7263e72e933SJed Brown for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 7273e72e933SJed Brown Ijk loc = ZCodeSplit(z); 7283ba16761SJacob Faibussowitsch if (IjkActive(layout.vextent, loc)) PetscCall(PetscZSetAdd(vset, z)); 7296547ddbdSJed Brown else { 7306547ddbdSJed Brown z += ZStepOct(z); 7316547ddbdSJed Brown continue; 7326547ddbdSJed Brown } 7333e72e933SJed Brown if (IjkActive(layout.eextent, loc)) { 7343e72e933SJed Brown local_elems++; 7353e72e933SJed Brown // Add all neighboring vertices to set 7363e72e933SJed Brown for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 7373e72e933SJed Brown Ijk inc = closure_dim[dim][n]; 7383e72e933SJed Brown Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 7393e72e933SJed Brown ZCode v = ZEncode(nloc); 7403ba16761SJacob Faibussowitsch PetscCall(PetscZSetAdd(vset, v)); 7413e72e933SJed Brown } 7423e72e933SJed Brown } 7433e72e933SJed Brown } 7443e72e933SJed Brown PetscInt local_verts, off = 0; 7453e72e933SJed Brown ZCode *vert_z; 7463e72e933SJed Brown PetscCall(PetscZSetGetSize(vset, &local_verts)); 7473e72e933SJed Brown PetscCall(PetscMalloc1(local_verts, &vert_z)); 7483e72e933SJed Brown PetscCall(PetscZSetGetElems(vset, &off, vert_z)); 7493e72e933SJed Brown PetscCall(PetscZSetDestroy(&vset)); 7503e72e933SJed Brown // ZCode is unsigned for bitwise convenience, but highest bit should never be set, so can interpret as signed 7513e72e933SJed Brown PetscCall(PetscSortInt64(local_verts, (PetscInt64 *)vert_z)); 7523e72e933SJed Brown 7533e72e933SJed Brown PetscCall(DMPlexSetChart(dm, 0, local_elems + local_verts)); 7543e72e933SJed Brown for (PetscInt e = 0; e < local_elems; e++) PetscCall(DMPlexSetConeSize(dm, e, PetscPowInt(2, dim))); 7553e72e933SJed Brown PetscCall(DMSetUp(dm)); 7563e72e933SJed Brown { 7573e72e933SJed Brown PetscInt e = 0; 7583e72e933SJed Brown for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 7593e72e933SJed Brown Ijk loc = ZCodeSplit(z); 7606547ddbdSJed Brown if (!IjkActive(layout.eextent, loc)) { 7616547ddbdSJed Brown z += ZStepOct(z); 7626547ddbdSJed Brown continue; 7636547ddbdSJed Brown } 7643e72e933SJed Brown PetscInt cone[8], orient[8] = {0}; 7653e72e933SJed Brown for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 7663e72e933SJed Brown Ijk inc = closure_dim[dim][n]; 7673e72e933SJed Brown Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 7683e72e933SJed Brown ZCode v = ZEncode(nloc); 7693e72e933SJed Brown PetscInt ci = ZCodeFind(v, local_verts, vert_z); 7703e72e933SJed Brown PetscAssert(ci >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find neighbor vertex in set"); 7713e72e933SJed Brown cone[n] = local_elems + ci; 7723e72e933SJed Brown } 7733e72e933SJed Brown PetscCall(DMPlexSetCone(dm, e, cone)); 7743e72e933SJed Brown PetscCall(DMPlexSetConeOrientation(dm, e, orient)); 7753e72e933SJed Brown e++; 7763e72e933SJed Brown } 7773e72e933SJed Brown } 7783e72e933SJed Brown 7793e72e933SJed Brown PetscCall(DMPlexSymmetrize(dm)); 7803e72e933SJed Brown PetscCall(DMPlexStratify(dm)); 7816725e60dSJed Brown 7823e72e933SJed Brown { // Create point SF 7833e72e933SJed Brown PetscSF sf; 7843ba16761SJacob Faibussowitsch PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf)); 7853e72e933SJed Brown PetscInt owned_verts = ZCodeFind(layout.zstarts[rank + 1], local_verts, vert_z); 7863e72e933SJed Brown if (owned_verts < 0) owned_verts = -(owned_verts + 1); // We don't care whether the key was found 7873e72e933SJed Brown PetscInt num_ghosts = local_verts - owned_verts; // Due to sorting, owned vertices always come first 7883e72e933SJed Brown PetscInt *local_ghosts; 7893e72e933SJed Brown PetscSFNode *ghosts; 7903e72e933SJed Brown PetscCall(PetscMalloc1(num_ghosts, &local_ghosts)); 7913e72e933SJed Brown PetscCall(PetscMalloc1(num_ghosts, &ghosts)); 7923e72e933SJed Brown for (PetscInt i = 0; i < num_ghosts;) { 7933e72e933SJed Brown ZCode z = vert_z[owned_verts + i]; 7943e72e933SJed Brown PetscInt remote_rank = ZCodeFind(z, size + 1, layout.zstarts), remote_count = 0; 7953e72e933SJed Brown if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1; 7963e72e933SJed Brown // We have a new remote rank; find all the ghost indices (which are contiguous in vert_z) 7973e72e933SJed Brown 7983e72e933SJed Brown // Count the elements on remote_rank 7994e2e9504SJed Brown PetscInt remote_elem = ZLayoutElementsOnRank(&layout, remote_rank); 8003e72e933SJed Brown 8013e72e933SJed Brown // Traverse vertices and make ghost links 8023e72e933SJed Brown for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) { 8033e72e933SJed Brown Ijk loc = ZCodeSplit(rz); 8043e72e933SJed Brown if (rz == z) { 8053e72e933SJed Brown local_ghosts[i] = local_elems + owned_verts + i; 8063e72e933SJed Brown ghosts[i].rank = remote_rank; 8073e72e933SJed Brown ghosts[i].index = remote_elem + remote_count; 8083e72e933SJed Brown i++; 8093e72e933SJed Brown if (i == num_ghosts) break; 8103e72e933SJed Brown z = vert_z[owned_verts + i]; 8113e72e933SJed Brown } 8123e72e933SJed Brown if (IjkActive(layout.vextent, loc)) remote_count++; 8136547ddbdSJed Brown else rz += ZStepOct(rz); 8143e72e933SJed Brown } 8153e72e933SJed Brown } 8163e72e933SJed Brown PetscCall(PetscSFSetGraph(sf, local_elems + local_verts, num_ghosts, local_ghosts, PETSC_OWN_POINTER, ghosts, PETSC_OWN_POINTER)); 8173e72e933SJed Brown PetscCall(PetscObjectSetName((PetscObject)sf, "SFC Point SF")); 8183e72e933SJed Brown PetscCall(DMSetPointSF(dm, sf)); 8193e72e933SJed Brown PetscCall(PetscSFDestroy(&sf)); 8203e72e933SJed Brown } 8213e72e933SJed Brown { 8223e72e933SJed Brown Vec coordinates; 8233e72e933SJed Brown PetscScalar *coords; 8243e72e933SJed Brown PetscSection coord_section; 8253e72e933SJed Brown PetscInt coord_size; 8263e72e933SJed Brown PetscCall(DMGetCoordinateSection(dm, &coord_section)); 8273e72e933SJed Brown PetscCall(PetscSectionSetNumFields(coord_section, 1)); 8283e72e933SJed Brown PetscCall(PetscSectionSetFieldComponents(coord_section, 0, dim)); 8293e72e933SJed Brown PetscCall(PetscSectionSetChart(coord_section, local_elems, local_elems + local_verts)); 8303e72e933SJed Brown for (PetscInt v = 0; v < local_verts; v++) { 8313e72e933SJed Brown PetscInt point = local_elems + v; 8323e72e933SJed Brown PetscCall(PetscSectionSetDof(coord_section, point, dim)); 8333e72e933SJed Brown PetscCall(PetscSectionSetFieldDof(coord_section, point, 0, dim)); 8343e72e933SJed Brown } 8353e72e933SJed Brown PetscCall(PetscSectionSetUp(coord_section)); 8363e72e933SJed Brown PetscCall(PetscSectionGetStorageSize(coord_section, &coord_size)); 8373e72e933SJed Brown PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates)); 8383e72e933SJed Brown PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates")); 8393e72e933SJed Brown PetscCall(VecSetSizes(coordinates, coord_size, PETSC_DETERMINE)); 8403e72e933SJed Brown PetscCall(VecSetBlockSize(coordinates, dim)); 8413e72e933SJed Brown PetscCall(VecSetType(coordinates, VECSTANDARD)); 8423e72e933SJed Brown PetscCall(VecGetArray(coordinates, &coords)); 8433e72e933SJed Brown for (PetscInt v = 0; v < local_verts; v++) { 8443e72e933SJed Brown Ijk loc = ZCodeSplit(vert_z[v]); 8453e72e933SJed Brown coords[v * dim + 0] = lower[0] + loc.i * (upper[0] - lower[0]) / layout.eextent.i; 8463e72e933SJed Brown if (dim > 1) coords[v * dim + 1] = lower[1] + loc.j * (upper[1] - lower[1]) / layout.eextent.j; 8473e72e933SJed Brown if (dim > 2) coords[v * dim + 2] = lower[2] + loc.k * (upper[2] - lower[2]) / layout.eextent.k; 8483e72e933SJed Brown } 8493e72e933SJed Brown PetscCall(VecRestoreArray(coordinates, &coords)); 8503e72e933SJed Brown PetscCall(DMSetCoordinatesLocal(dm, coordinates)); 8513e72e933SJed Brown PetscCall(VecDestroy(&coordinates)); 8523e72e933SJed Brown } 8533e72e933SJed Brown if (interpolate) { 8543431e603SJed Brown PetscCall(DMPlexInterpolateInPlace_Internal(dm)); 8555f06a3ddSJed Brown // It's currently necessary to orient the donor and periodic edges consistently. An easy way to ensure that is ot 8565f06a3ddSJed Brown // give all edges positive orientation. Since vertices are created in Z-order, all ranks will agree about the 8575f06a3ddSJed Brown // ordering cone[0] < cone[1]. This is overkill and it would be nice to remove this preparation and make 8585f06a3ddSJed Brown // DMPlexCreateIsoperiodicClosureSF_Private() more resilient, so it fixes any inconsistent orientations. That might 8595f06a3ddSJed Brown // be needed in a general CGNS reader, for example. 8605f06a3ddSJed Brown PetscCall(DMPlexOrientPositiveEdges_Private(dm)); 8613431e603SJed Brown 8623431e603SJed Brown DMLabel label; 8633431e603SJed Brown PetscCall(DMCreateLabel(dm, "Face Sets")); 8643431e603SJed Brown PetscCall(DMGetLabel(dm, "Face Sets", &label)); 8659ae3492bSJames Wright PetscSegBuffer per_faces[3], donor_face_closure[3], my_donor_faces[3]; 8669ae3492bSJames Wright for (PetscInt i = 0; i < 3; i++) { 8679ae3492bSJames Wright PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &per_faces[i])); 8689ae3492bSJames Wright PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &my_donor_faces[i])); 8699ae3492bSJames Wright PetscCall(PetscSegBufferCreate(sizeof(ZCode), 64 * PetscPowInt(2, dim), &donor_face_closure[i])); 8709ae3492bSJames Wright } 8713431e603SJed Brown PetscInt fStart, fEnd, vStart, vEnd; 8723431e603SJed Brown PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 8733431e603SJed Brown PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 8743431e603SJed Brown for (PetscInt f = fStart; f < fEnd; f++) { 8754e2e9504SJed Brown PetscInt npoints, *points = NULL, num_fverts = 0, fverts[8]; 8763431e603SJed Brown PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 8773431e603SJed Brown PetscInt bc_count[6] = {0}; 8783431e603SJed Brown for (PetscInt i = 0; i < npoints; i++) { 8793431e603SJed Brown PetscInt p = points[2 * i]; 8803431e603SJed Brown if (p < vStart || vEnd <= p) continue; 8814e2e9504SJed Brown fverts[num_fverts++] = p; 8823431e603SJed Brown Ijk loc = ZCodeSplit(vert_z[p - vStart]); 8833431e603SJed Brown // Convention here matches DMPlexCreateCubeMesh_Internal 8843431e603SJed Brown bc_count[0] += loc.i == 0; 8853431e603SJed Brown bc_count[1] += loc.i == layout.vextent.i - 1; 8863431e603SJed Brown bc_count[2] += loc.j == 0; 8873431e603SJed Brown bc_count[3] += loc.j == layout.vextent.j - 1; 8883431e603SJed Brown bc_count[4] += loc.k == 0; 8893431e603SJed Brown bc_count[5] += loc.k == layout.vextent.k - 1; 8903e72e933SJed Brown } 8913431e603SJed Brown PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 8923431e603SJed Brown for (PetscInt bc = 0, bc_match = 0; bc < 2 * dim; bc++) { 8933431e603SJed Brown if (bc_count[bc] == PetscPowInt(2, dim - 1)) { 8946725e60dSJed Brown PetscCall(DMPlexOrientPoint(dm, f, face_orient_dim[dim][bc])); 8954e2e9504SJed Brown if (periodicity[bc / 2] == DM_BOUNDARY_PERIODIC) { 8964e2e9504SJed Brown PetscInt *put; 8974e2e9504SJed Brown if (bc % 2 == 0) { // donor face; no label 8989ae3492bSJames Wright PetscCall(PetscSegBufferGet(my_donor_faces[bc / 2], 1, &put)); 8994e2e9504SJed Brown *put = f; 9004e2e9504SJed Brown } else { // periodic face 9019ae3492bSJames Wright PetscCall(PetscSegBufferGet(per_faces[bc / 2], 1, &put)); 9024e2e9504SJed Brown *put = f; 9034e2e9504SJed Brown ZCode *zput; 9049ae3492bSJames Wright PetscCall(PetscSegBufferGet(donor_face_closure[bc / 2], num_fverts, &zput)); 9054e2e9504SJed Brown for (PetscInt i = 0; i < num_fverts; i++) { 9064e2e9504SJed Brown Ijk loc = ZCodeSplit(vert_z[fverts[i] - vStart]); 9074e2e9504SJed Brown switch (bc / 2) { 9084e2e9504SJed Brown case 0: 9094e2e9504SJed Brown loc.i = 0; 9104e2e9504SJed Brown break; 9114e2e9504SJed Brown case 1: 9124e2e9504SJed Brown loc.j = 0; 9134e2e9504SJed Brown break; 9144e2e9504SJed Brown case 2: 9154e2e9504SJed Brown loc.k = 0; 9164e2e9504SJed Brown break; 9174e2e9504SJed Brown } 9184e2e9504SJed Brown *zput++ = ZEncode(loc); 9194e2e9504SJed Brown } 9204e2e9504SJed Brown } 9214e2e9504SJed Brown continue; 9224e2e9504SJed Brown } 9233431e603SJed Brown PetscAssert(bc_match == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face matches multiple face sets"); 9243431e603SJed Brown PetscCall(DMLabelSetValue(label, f, face_marker_dim[dim][bc])); 9253431e603SJed Brown bc_match++; 9263431e603SJed Brown } 9273431e603SJed Brown } 9283431e603SJed Brown } 9293431e603SJed Brown // Ensure that the Coordinate DM has our new boundary labels 9303431e603SJed Brown DM cdm; 9313431e603SJed Brown PetscCall(DMGetCoordinateDM(dm, &cdm)); 9323431e603SJed Brown PetscCall(DMCopyLabels(dm, cdm, PETSC_COPY_VALUES, PETSC_FALSE, DM_COPY_LABELS_FAIL)); 9336725e60dSJed Brown if (periodicity[0] == DM_BOUNDARY_PERIODIC || (dim > 1 && periodicity[1] == DM_BOUNDARY_PERIODIC) || (dim > 2 && periodicity[2] == DM_BOUNDARY_PERIODIC)) { 9345f06a3ddSJed Brown PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(dm, &layout, vert_z, per_faces, lower, upper, periodicity, donor_face_closure, my_donor_faces)); 9356725e60dSJed Brown } 9369ae3492bSJames Wright for (PetscInt i = 0; i < 3; i++) { 9379ae3492bSJames Wright PetscCall(PetscSegBufferDestroy(&per_faces[i])); 9389ae3492bSJames Wright PetscCall(PetscSegBufferDestroy(&donor_face_closure[i])); 9399ae3492bSJames Wright PetscCall(PetscSegBufferDestroy(&my_donor_faces[i])); 9409ae3492bSJames Wright } 9413431e603SJed Brown } 9424e2e9504SJed Brown PetscCall(PetscFree(layout.zstarts)); 9433431e603SJed Brown PetscCall(PetscFree(vert_z)); 9443ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 9453e72e933SJed Brown } 9464e2e9504SJed Brown 9474e2e9504SJed Brown /*@ 9485f06a3ddSJed Brown DMPlexSetIsoperiodicFaceSF - Express periodicity from an existing mesh 9494e2e9504SJed Brown 95020f4b53cSBarry Smith Logically Collective 9514e2e9504SJed Brown 9524e2e9504SJed Brown Input Parameters: 9534e2e9504SJed Brown + dm - The `DMPLEX` on which to set periodicity 9541fca310dSJames Wright . num_face_sfs - Number of `PetscSF`s in `face_sfs` 9551fca310dSJames 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. 9564e2e9504SJed Brown 9574e2e9504SJed Brown Level: advanced 9584e2e9504SJed Brown 95953c0d4aeSBarry Smith Note: 9605dca41c3SJed Brown One can use `-dm_plex_shape zbox` to use this mode of periodicity, wherein the periodic points are distinct both globally 9614e2e9504SJed Brown and locally, but are paired when creating a global dof space. 9624e2e9504SJed Brown 9631cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexGetIsoperiodicFaceSF()` 9644e2e9504SJed Brown @*/ 9651fca310dSJames Wright PetscErrorCode DMPlexSetIsoperiodicFaceSF(DM dm, PetscInt num_face_sfs, PetscSF *face_sfs) 9664e2e9504SJed Brown { 9674e2e9504SJed Brown DM_Plex *plex = (DM_Plex *)dm->data; 9684d86920dSPierre Jolivet 9694e2e9504SJed Brown PetscFunctionBegin; 9704e2e9504SJed Brown PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 9711fca310dSJames Wright if (face_sfs) PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex)); 9721fca310dSJames Wright if (face_sfs == plex->periodic.face_sfs && num_face_sfs == plex->periodic.num_face_sfs) PetscFunctionReturn(PETSC_SUCCESS); 9731fca310dSJames Wright 9741fca310dSJames Wright for (PetscInt i = 0; i < num_face_sfs; i++) PetscCall(PetscObjectReference((PetscObject)face_sfs[i])); 9751fca310dSJames Wright 9761fca310dSJames Wright if (plex->periodic.num_face_sfs > 0) { 9771fca310dSJames Wright for (PetscInt i = 0; i < plex->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&plex->periodic.face_sfs[i])); 9781fca310dSJames Wright PetscCall(PetscFree(plex->periodic.face_sfs)); 9791fca310dSJames Wright } 9801fca310dSJames Wright 9811fca310dSJames Wright plex->periodic.num_face_sfs = num_face_sfs; 9821fca310dSJames Wright PetscCall(PetscCalloc1(num_face_sfs, &plex->periodic.face_sfs)); 9831fca310dSJames Wright for (PetscInt i = 0; i < num_face_sfs; i++) plex->periodic.face_sfs[i] = face_sfs[i]; 9845f06a3ddSJed Brown 9855f06a3ddSJed Brown DM cdm = dm->coordinates[0].dm; // Can't DMGetCoordinateDM because it automatically creates one 9865f06a3ddSJed Brown if (cdm) { 9871fca310dSJames Wright PetscCall(DMPlexSetIsoperiodicFaceSF(cdm, num_face_sfs, face_sfs)); 9881fca310dSJames Wright if (face_sfs) cdm->periodic.setup = DMPeriodicCoordinateSetUp_Internal; 9895f06a3ddSJed Brown } 9903ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 9914e2e9504SJed Brown } 9924e2e9504SJed Brown 9931fca310dSJames Wright /*@C 9945f06a3ddSJed Brown DMPlexGetIsoperiodicFaceSF - Obtain periodicity for a mesh 9954e2e9504SJed Brown 99620f4b53cSBarry Smith Logically Collective 9974e2e9504SJed Brown 99820f4b53cSBarry Smith Input Parameter: 9994e2e9504SJed Brown . dm - The `DMPLEX` for which to obtain periodic relation 10004e2e9504SJed Brown 100120f4b53cSBarry Smith Output Parameter: 10021fca310dSJames Wright + num_face_sfs - Number of `PetscSF`s in the array 10031fca310dSJames 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. 10044e2e9504SJed Brown 10054e2e9504SJed Brown Level: advanced 10064e2e9504SJed Brown 10071cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()` 10084e2e9504SJed Brown @*/ 10091fca310dSJames Wright PetscErrorCode DMPlexGetIsoperiodicFaceSF(DM dm, PetscInt *num_face_sfs, const PetscSF **face_sfs) 10104e2e9504SJed Brown { 10114e2e9504SJed Brown DM_Plex *plex = (DM_Plex *)dm->data; 10124d86920dSPierre Jolivet 10134e2e9504SJed Brown PetscFunctionBegin; 10144e2e9504SJed Brown PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 10151fca310dSJames Wright *face_sfs = plex->periodic.face_sfs; 10161fca310dSJames Wright *num_face_sfs = plex->periodic.num_face_sfs; 10173ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 10184e2e9504SJed Brown } 10196725e60dSJed Brown 10205f06a3ddSJed Brown /*@C 10215f06a3ddSJed Brown DMPlexSetIsoperiodicFaceTransform - set geometric transform from donor to periodic points 10226725e60dSJed Brown 10236725e60dSJed Brown Logically Collective 10246725e60dSJed Brown 102560225df5SJacob Faibussowitsch Input Parameters: 102653c0d4aeSBarry Smith + dm - `DMPLEX` that has been configured with `DMPlexSetIsoperiodicFaceSF()` 10271fca310dSJames Wright . n - Number of transforms in array 10281fca310dSJames Wright - t - Array of 4x4 affine transformation basis. 10296725e60dSJed Brown 103053c0d4aeSBarry Smith Level: advanced 103153c0d4aeSBarry Smith 10325f06a3ddSJed Brown Notes: 10335f06a3ddSJed Brown Affine transforms are 4x4 matrices in which the leading 3x3 block expresses a rotation (or identity for no rotation), 10345f06a3ddSJed Brown the last column contains a translation vector, and the bottom row is all zero except the last entry, which must always 10355f06a3ddSJed Brown be 1. This representation is common in geometric modeling and allows affine transformations to be composed using 1036aaa8cc7dSPierre Jolivet simple matrix multiplication. 10375f06a3ddSJed Brown 10385f06a3ddSJed Brown Although the interface accepts a general affine transform, only affine translation is supported at present. 10395f06a3ddSJed Brown 104060225df5SJacob Faibussowitsch Developer Notes: 10415f06a3ddSJed Brown This interface should be replaced by making BasisTransform public, expanding it to support affine representations, and 10425f06a3ddSJed Brown adding GPU implementations to apply the G2L/L2G transforms. 104353c0d4aeSBarry Smith 10441cc06b55SBarry Smith .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()` 10456725e60dSJed Brown @*/ 10461fca310dSJames Wright PetscErrorCode DMPlexSetIsoperiodicFaceTransform(DM dm, PetscInt n, const PetscScalar *t) 10476725e60dSJed Brown { 10486725e60dSJed Brown DM_Plex *plex = (DM_Plex *)dm->data; 10494d86920dSPierre Jolivet 10506725e60dSJed Brown PetscFunctionBegin; 10516725e60dSJed Brown PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 10521fca310dSJames 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); 10531fca310dSJames Wright 10541fca310dSJames Wright PetscCall(PetscMalloc1(n, &plex->periodic.transform)); 10551fca310dSJames Wright for (PetscInt i = 0; i < n; i++) { 10566725e60dSJed Brown for (PetscInt j = 0; j < 4; j++) { 10571fca310dSJames Wright for (PetscInt k = 0; k < 4; k++) { 10581fca310dSJames Wright PetscCheck(j != k || t[i * 16 + j * 4 + k] == 1., PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Rotated transforms not supported"); 10591fca310dSJames Wright plex->periodic.transform[i][j][k] = t[i * 16 + j * 4 + k]; 10601fca310dSJames Wright } 10616725e60dSJed Brown } 10626725e60dSJed Brown } 10633ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 10646725e60dSJed Brown } 1065