1 #include <petsc/private/dmpleximpl.h> /*I "petscdmplex.h" I*/ 2 #include <petscsf.h> 3 #include <petsc/private/hashset.h> 4 5 typedef uint64_t ZCode; 6 7 PETSC_HASH_SET(ZSet, ZCode, PetscHash_UInt64, PetscHashEqual) 8 9 typedef struct { 10 PetscInt i, j, k; 11 } Ijk; 12 13 typedef struct { 14 Ijk eextent; 15 Ijk vextent; 16 PetscMPIInt comm_size; 17 ZCode *zstarts; 18 } ZLayout; 19 20 static unsigned ZCodeSplit1(ZCode z) 21 { 22 z = ((z & 01001001001001001) | ((z >> 2) & 02002002002002002) | ((z >> 4) & 04004004004004004)); 23 z = (z | (z >> 6) | (z >> 12)) & 0000000777000000777; 24 z = (z | (z >> 18)) & 0777777; 25 return (unsigned)z; 26 } 27 28 static ZCode ZEncode1(unsigned t) 29 { 30 ZCode z = t; 31 z = (z | (z << 18)) & 0777000000777; 32 z = (z | (z << 6) | (z << 12)) & 07007007007007007; 33 z = (z | (z << 2) | (z << 4)) & 0111111111111111111; 34 return z; 35 } 36 37 static Ijk ZCodeSplit(ZCode z) 38 { 39 Ijk c; 40 c.i = ZCodeSplit1(z >> 2); 41 c.j = ZCodeSplit1(z >> 1); 42 c.k = ZCodeSplit1(z >> 0); 43 return c; 44 } 45 46 static ZCode ZEncode(Ijk c) 47 { 48 ZCode z = (ZEncode1(c.i) << 2) | (ZEncode1(c.j) << 1) | ZEncode1(c.k); 49 return z; 50 } 51 52 static PetscBool IjkActive(Ijk extent, Ijk l) 53 { 54 if (l.i < extent.i && l.j < extent.j && l.k < extent.k) return PETSC_TRUE; 55 return PETSC_FALSE; 56 } 57 58 // If z is not the base of an octet (last 3 bits 0), return 0. 59 // 60 // If z is the base of an octet, we recursively grow to the biggest structured octet. This is typically useful when a z 61 // is outside the domain and we wish to skip a (possibly recursively large) octet to find our next interesting point. 62 static ZCode ZStepOct(ZCode z) 63 { 64 if (PetscUnlikely(z == 0)) return 0; // Infinite loop below if z == 0 65 ZCode step = 07; 66 for (; (z & step) == 0; step = (step << 3) | 07) { } 67 return step >> 3; 68 } 69 70 // Since element/vertex box extents are typically not equal powers of 2, Z codes that lie within the domain are not contiguous. 71 static PetscErrorCode ZLayoutCreate(PetscMPIInt size, const PetscInt eextent[3], const PetscInt vextent[3], ZLayout *layout) 72 { 73 PetscFunctionBegin; 74 layout->eextent.i = eextent[0]; 75 layout->eextent.j = eextent[1]; 76 layout->eextent.k = eextent[2]; 77 layout->vextent.i = vextent[0]; 78 layout->vextent.j = vextent[1]; 79 layout->vextent.k = vextent[2]; 80 layout->comm_size = size; 81 layout->zstarts = NULL; 82 PetscCall(PetscMalloc1(size + 1, &layout->zstarts)); 83 84 PetscInt total_elems = eextent[0] * eextent[1] * eextent[2]; 85 ZCode z = 0; 86 layout->zstarts[0] = 0; 87 // This loop traverses all vertices in the global domain, so is worth making fast. We use ZStepBound 88 for (PetscMPIInt r = 0; r < size; r++) { 89 PetscInt elems_needed = (total_elems / size) + (total_elems % size > r), count; 90 for (count = 0; count < elems_needed; z++) { 91 ZCode skip = ZStepOct(z); // optimistically attempt a longer step 92 for (ZCode s = skip;; s >>= 3) { 93 Ijk trial = ZCodeSplit(z + s); 94 if (IjkActive(layout->eextent, trial)) { 95 while (count + s + 1 > (ZCode)elems_needed) s >>= 3; // Shrink the octet 96 count += s + 1; 97 z += s; 98 break; 99 } 100 if (s == 0) { // the whole skip octet is inactive 101 z += skip; 102 break; 103 } 104 } 105 } 106 // Pick up any extra vertices in the Z ordering before the next rank's first owned element. 107 // 108 // This leads to poorly balanced vertices when eextent is a power of 2, since all the fringe vertices end up 109 // on the last rank. A possible solution is to balance the Z-order vertices independently from the cells, which will 110 // result in a lot of element closures being remote. We could finish marking boundary conditions, then do a round of 111 // vertex ownership smoothing (which would reorder and redistribute vertices without touching element distribution). 112 // Another would be to have an analytic ownership criteria for vertices in the fringe veextent - eextent. This would 113 // complicate the job of identifying an owner and its offset. 114 // 115 // The current recommended approach is to let `-dm_distribute 1` (default) resolve vertex ownership. This is 116 // *mandatory* with isoperiodicity (except in special cases) to remove standed vertices from local spaces. Here's 117 // the issue: 118 // 119 // Consider this partition on rank 0 (left) and rank 1. 120 // 121 // 4 -------- 5 -- 14 --10 -- 21 --11 122 // | | | 123 // 7 -- 16 -- 8 | | | 124 // | | 3 ------- 7 ------- 9 125 // | | | | 126 // 4 -------- 6 ------ 10 | | 127 // | | | 6 -- 16 -- 8 128 // | | | 129 // 3 ---11--- 5 --18-- 9 130 // 131 // The periodic face SF looks like 132 // [0] Number of roots=21, leaves=1, remote ranks=1 133 // [0] 16 <- (0,11) 134 // [1] Number of roots=22, leaves=2, remote ranks=2 135 // [1] 14 <- (0,18) 136 // [1] 21 <- (1,16) 137 // 138 // 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 139 // the point SF links to (1,4) and (1,5). Rank 1 learns about the periodic mapping of (1,5) while handling face 140 // (1,14), but never learns that vertex (1,4) has been mapped to (0,3) by face (0,16). 141 // 142 // We can relatively easily inform vertex (1,4) of this mapping, but it stays in rank 1's local space despite not 143 // being in the closure and thus not being contributed to. This would be mostly harmless except that some viewer 144 // routines expect all local points to be somehow significant. It is not easy to analytically remove the (1,4) 145 // vertex because the point SF and isoperiodic face SF would need to be updated to account for removal of the 146 // stranded vertices. 147 for (; z <= ZEncode(layout->vextent); z++) { 148 Ijk loc = ZCodeSplit(z); 149 if (IjkActive(layout->eextent, loc)) break; 150 z += ZStepOct(z); 151 } 152 layout->zstarts[r + 1] = z; 153 } 154 layout->zstarts[size] = ZEncode(layout->vextent); 155 PetscFunctionReturn(PETSC_SUCCESS); 156 } 157 158 static PetscInt ZLayoutElementsOnRank(const ZLayout *layout, PetscMPIInt rank) 159 { 160 PetscInt remote_elem = 0; 161 for (ZCode rz = layout->zstarts[rank]; rz < layout->zstarts[rank + 1]; rz++) { 162 Ijk loc = ZCodeSplit(rz); 163 if (IjkActive(layout->eextent, loc)) remote_elem++; 164 else rz += ZStepOct(rz); 165 } 166 return remote_elem; 167 } 168 169 static PetscInt ZCodeFind(ZCode key, PetscInt n, const ZCode X[]) 170 { 171 PetscInt lo = 0, hi = n; 172 173 if (n == 0) return -1; 174 while (hi - lo > 1) { 175 PetscInt mid = lo + (hi - lo) / 2; 176 if (key < X[mid]) hi = mid; 177 else lo = mid; 178 } 179 return key == X[lo] ? lo : -(lo + (key > X[lo]) + 1); 180 } 181 182 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]) 183 { 184 MPI_Comm comm; 185 PetscInt dim, vStart, vEnd; 186 PetscMPIInt size; 187 PetscSF face_sfs[3]; 188 PetscScalar transforms[3][4][4] = {{{0}}}; 189 190 PetscFunctionBegin; 191 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 192 PetscCallMPI(MPI_Comm_size(comm, &size)); 193 PetscCall(DMGetDimension(dm, &dim)); 194 const PetscInt csize = PetscPowInt(2, dim - 1); 195 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 196 197 PetscInt num_directions = 0; 198 for (PetscInt direction = 0; direction < dim; direction++) { 199 size_t num_faces; 200 PetscInt *faces; 201 ZCode *donor_verts, *donor_minz; 202 PetscSFNode *leaf; 203 204 if (periodicity[direction] != DM_BOUNDARY_PERIODIC) continue; 205 PetscCall(PetscSegBufferGetSize(per_faces[direction], &num_faces)); 206 PetscCall(PetscSegBufferExtractInPlace(per_faces[direction], &faces)); 207 PetscCall(PetscSegBufferExtractInPlace(donor_face_closure[direction], &donor_verts)); 208 PetscCall(PetscMalloc1(num_faces, &donor_minz)); 209 PetscCall(PetscMalloc1(num_faces, &leaf)); 210 for (PetscInt i = 0; i < (PetscInt)num_faces; i++) { 211 ZCode minz = donor_verts[i * csize]; 212 for (PetscInt j = 1; j < csize; j++) minz = PetscMin(minz, donor_verts[i * csize + j]); 213 donor_minz[i] = minz; 214 } 215 { 216 PetscBool sorted; 217 PetscCall(PetscSortedInt64(num_faces, (const PetscInt64 *)donor_minz, &sorted)); 218 // If a donor vertex were chosen to broker multiple faces, we would have a logic error. 219 // Checking for sorting is a cheap check that there are no duplicates. 220 PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_PLIB, "minz not sorted; possible duplicates not checked"); 221 } 222 for (PetscInt i = 0; i < (PetscInt)num_faces;) { 223 ZCode z = donor_minz[i]; 224 PetscInt remote_rank = ZCodeFind(z, size + 1, layout->zstarts), remote_count = 0; 225 if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1; 226 // Process all the vertices on this rank 227 for (ZCode rz = layout->zstarts[remote_rank]; rz < layout->zstarts[remote_rank + 1]; rz++) { 228 Ijk loc = ZCodeSplit(rz); 229 if (rz == z) { 230 leaf[i].rank = remote_rank; 231 leaf[i].index = remote_count; 232 i++; 233 if (i == (PetscInt)num_faces) break; 234 z = donor_minz[i]; 235 } 236 if (IjkActive(layout->vextent, loc)) remote_count++; 237 } 238 } 239 PetscCall(PetscFree(donor_minz)); 240 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &face_sfs[num_directions])); 241 PetscCall(PetscSFSetGraph(face_sfs[num_directions], vEnd - vStart, num_faces, NULL, PETSC_USE_POINTER, leaf, PETSC_USE_POINTER)); 242 const PetscInt *my_donor_degree; 243 PetscCall(PetscSFComputeDegreeBegin(face_sfs[num_directions], &my_donor_degree)); 244 PetscCall(PetscSFComputeDegreeEnd(face_sfs[num_directions], &my_donor_degree)); 245 PetscInt num_multiroots = 0; 246 for (PetscInt i = 0; i < vEnd - vStart; i++) { 247 num_multiroots += my_donor_degree[i]; 248 if (my_donor_degree[i] == 0) continue; 249 PetscAssert(my_donor_degree[i] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex has multiple faces"); 250 } 251 PetscInt *my_donors, *donor_indices, *my_donor_indices; 252 size_t num_my_donors; 253 PetscCall(PetscSegBufferGetSize(my_donor_faces[direction], &num_my_donors)); 254 PetscCheck((PetscInt)num_my_donors == num_multiroots, PETSC_COMM_SELF, PETSC_ERR_SUP, "Donor request does not match expected donors"); 255 PetscCall(PetscSegBufferExtractInPlace(my_donor_faces[direction], &my_donors)); 256 PetscCall(PetscMalloc1(vEnd - vStart, &my_donor_indices)); 257 for (PetscInt i = 0; i < (PetscInt)num_my_donors; i++) { 258 PetscInt f = my_donors[i]; 259 PetscInt num_points, *points = NULL, minv = PETSC_MAX_INT; 260 PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points)); 261 for (PetscInt j = 0; j < num_points; j++) { 262 PetscInt p = points[2 * j]; 263 if (p < vStart || vEnd <= p) continue; 264 minv = PetscMin(minv, p); 265 } 266 PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points)); 267 PetscAssert(my_donor_degree[minv - vStart] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex not requested"); 268 my_donor_indices[minv - vStart] = f; 269 } 270 PetscCall(PetscMalloc1(num_faces, &donor_indices)); 271 PetscCall(PetscSFBcastBegin(face_sfs[num_directions], MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE)); 272 PetscCall(PetscSFBcastEnd(face_sfs[num_directions], MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE)); 273 PetscCall(PetscFree(my_donor_indices)); 274 // Modify our leafs so they point to donor faces instead of donor minz. Additionally, give them indices as faces. 275 for (PetscInt i = 0; i < (PetscInt)num_faces; i++) leaf[i].index = donor_indices[i]; 276 PetscCall(PetscFree(donor_indices)); 277 PetscInt pStart, pEnd; 278 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 279 PetscCall(PetscSFSetGraph(face_sfs[num_directions], pEnd - pStart, num_faces, faces, PETSC_COPY_VALUES, leaf, PETSC_OWN_POINTER)); 280 { 281 char face_sf_name[PETSC_MAX_PATH_LEN]; 282 PetscCall(PetscSNPrintf(face_sf_name, sizeof face_sf_name, "Z-order Isoperiodic Faces #%" PetscInt_FMT, num_directions)); 283 PetscCall(PetscObjectSetName((PetscObject)face_sfs[num_directions], face_sf_name)); 284 } 285 286 transforms[num_directions][0][0] = 1; 287 transforms[num_directions][1][1] = 1; 288 transforms[num_directions][2][2] = 1; 289 transforms[num_directions][3][3] = 1; 290 transforms[num_directions][direction][3] = upper[direction] - lower[direction]; 291 num_directions++; 292 } 293 294 PetscCall(DMPlexSetIsoperiodicFaceSF(dm, num_directions, face_sfs)); 295 PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, num_directions, (PetscScalar *)transforms)); 296 297 for (PetscInt i = 0; i < num_directions; i++) PetscCall(PetscSFDestroy(&face_sfs[i])); 298 PetscFunctionReturn(PETSC_SUCCESS); 299 } 300 301 // This is a DMGlobalToLocalHook that applies the affine offsets. When extended for rotated periodicity, it'll need to 302 // apply a rotatonal transform and similar operations will be needed for fields (e.g., to rotate a velocity vector). 303 // We use this crude approach here so we don't have to write new GPU kernels yet. 304 static PetscErrorCode DMCoordAddPeriodicOffsets_Private(DM dm, Vec g, InsertMode mode, Vec l, void *ctx) 305 { 306 PetscFunctionBegin; 307 PetscCall(VecScatterBegin(dm->periodic.affine_to_local, dm->periodic.affine, l, ADD_VALUES, SCATTER_FORWARD)); 308 PetscCall(VecScatterEnd(dm->periodic.affine_to_local, dm->periodic.affine, l, ADD_VALUES, SCATTER_FORWARD)); 309 PetscFunctionReturn(PETSC_SUCCESS); 310 } 311 312 // Start with an SF for a positive depth (e.g., faces) and create a new SF for matched closure. The caller must ensure 313 // that both the donor (root) face and the periodic (leaf) face have consistent orientation, meaning that their closures 314 // are isomorphic. It may be useful/necessary for this restriction to be loosened. 315 // 316 // Output Arguments: 317 // 318 // + closure_sf - augmented point SF (see `DMGetPointSF()`) that includes the faces and all points in its closure. This 319 // can be used to create a global section and section SF. 320 // - is_points - index set for just the points in the closure of `face_sf`. These may be used to apply an affine 321 // transformation to periodic dofs; see DMPeriodicCoordinateSetUp_Internal(). 322 // 323 static PetscErrorCode DMPlexCreateIsoperiodicPointSF_Private(DM dm, PetscSF face_sf, PetscSF *closure_sf, IS *is_points) 324 { 325 MPI_Comm comm; 326 PetscInt nroots, nleaves, npoints; 327 const PetscInt *filocal, *pilocal; 328 const PetscSFNode *firemote, *piremote; 329 PetscMPIInt rank; 330 PetscSF point_sf; 331 332 PetscFunctionBegin; 333 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 334 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 335 PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote)); 336 PetscCall(DMGetPointSF(dm, &point_sf)); // Point SF has remote points 337 PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, &pilocal, &piremote)); 338 PetscInt *rootdata, *leafdata; 339 PetscCall(PetscCalloc2(2 * nroots, &rootdata, 2 * nroots, &leafdata)); 340 for (PetscInt i = 0; i < nleaves; i++) { 341 PetscInt point = filocal[i], cl_size, *closure = NULL; 342 PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 343 leafdata[point] = cl_size - 1; 344 PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 345 } 346 PetscCall(PetscSFReduceBegin(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM)); 347 PetscCall(PetscSFReduceEnd(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM)); 348 349 PetscInt root_offset = 0; 350 for (PetscInt p = 0; p < nroots; p++) { 351 const PetscInt *donor_dof = rootdata + nroots; 352 if (donor_dof[p] == 0) { 353 rootdata[2 * p] = -1; 354 rootdata[2 * p + 1] = -1; 355 continue; 356 } 357 PetscInt cl_size; 358 PetscInt *closure = NULL; 359 PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 360 // cl_size - 1 = points not including self 361 PetscAssert(donor_dof[p] == cl_size - 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Reduced leaf cone sizes do not match root cone sizes"); 362 rootdata[2 * p] = root_offset; 363 rootdata[2 * p + 1] = cl_size - 1; 364 root_offset += cl_size - 1; 365 PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 366 } 367 PetscCall(PetscSFBcastBegin(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 368 PetscCall(PetscSFBcastEnd(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 369 // Count how many leaves we need to communicate the closures 370 PetscInt leaf_offset = 0; 371 for (PetscInt i = 0; i < nleaves; i++) { 372 PetscInt point = filocal[i]; 373 if (leafdata[2 * point + 1] < 0) continue; 374 leaf_offset += leafdata[2 * point + 1]; 375 } 376 377 PetscSFNode *closure_leaf; 378 PetscCall(PetscMalloc1(leaf_offset, &closure_leaf)); 379 leaf_offset = 0; 380 for (PetscInt i = 0; i < nleaves; i++) { 381 PetscInt point = filocal[i]; 382 PetscInt cl_size = leafdata[2 * point + 1]; 383 if (cl_size < 0) continue; 384 for (PetscInt j = 0; j < cl_size; j++) { 385 closure_leaf[leaf_offset].rank = firemote[i].rank; 386 closure_leaf[leaf_offset].index = leafdata[2 * point] + j; 387 leaf_offset++; 388 } 389 } 390 391 PetscSF sf_closure; 392 PetscCall(PetscSFCreate(comm, &sf_closure)); 393 PetscCall(PetscSFSetGraph(sf_closure, root_offset, leaf_offset, NULL, PETSC_USE_POINTER, closure_leaf, PETSC_OWN_POINTER)); 394 395 // Pack root buffer with owner for every point in the root cones 396 PetscSFNode *donor_closure; 397 PetscCall(PetscCalloc1(root_offset, &donor_closure)); 398 root_offset = 0; 399 for (PetscInt p = 0; p < nroots; p++) { 400 if (rootdata[2 * p] < 0) continue; 401 PetscInt cl_size; 402 PetscInt *closure = NULL; 403 PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 404 for (PetscInt j = 1; j < cl_size; j++) { 405 PetscInt c = closure[2 * j]; 406 if (pilocal) { 407 PetscInt found = -1; 408 if (npoints > 0) PetscCall(PetscFindInt(c, npoints, pilocal, &found)); 409 if (found >= 0) { 410 donor_closure[root_offset++] = piremote[found]; 411 continue; 412 } 413 } 414 // we own c 415 donor_closure[root_offset].rank = rank; 416 donor_closure[root_offset].index = c; 417 root_offset++; 418 } 419 PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 420 } 421 422 PetscSFNode *leaf_donor_closure; 423 PetscCall(PetscMalloc1(leaf_offset, &leaf_donor_closure)); 424 PetscCall(PetscSFBcastBegin(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE)); 425 PetscCall(PetscSFBcastEnd(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE)); 426 PetscCall(PetscSFDestroy(&sf_closure)); 427 PetscCall(PetscFree(donor_closure)); 428 429 PetscSFNode *new_iremote; 430 PetscCall(PetscCalloc1(nroots, &new_iremote)); 431 for (PetscInt i = 0; i < nroots; i++) new_iremote[i].rank = -1; 432 // Walk leaves and match vertices 433 leaf_offset = 0; 434 for (PetscInt i = 0; i < nleaves; i++) { 435 PetscInt point = filocal[i], cl_size; 436 PetscInt *closure = NULL; 437 PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 438 for (PetscInt j = 1; j < cl_size; j++) { // TODO: should we send donor edge orientations so we can flip for consistency? 439 PetscInt c = closure[2 * j]; 440 PetscSFNode lc = leaf_donor_closure[leaf_offset]; 441 // printf("[%d] face %d.%d: %d ?-- (%d,%d)\n", rank, point, j, c, lc.rank, lc.index); 442 if (new_iremote[c].rank == -1) { 443 new_iremote[c] = lc; 444 } 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"); 445 leaf_offset++; 446 } 447 PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 448 } 449 PetscCall(PetscFree(leaf_donor_closure)); 450 451 // Include face points in closure SF 452 for (PetscInt i = 0; i < nleaves; i++) new_iremote[filocal[i]] = firemote[i]; 453 // consolidate leaves 454 PetscInt num_new_leaves = 0; 455 for (PetscInt i = 0; i < nroots; i++) { 456 if (new_iremote[i].rank == -1) continue; 457 new_iremote[num_new_leaves] = new_iremote[i]; 458 leafdata[num_new_leaves] = i; 459 num_new_leaves++; 460 } 461 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, num_new_leaves, leafdata, PETSC_COPY_VALUES, is_points)); 462 463 PetscSF csf; 464 PetscCall(PetscSFCreate(comm, &csf)); 465 PetscCall(PetscSFSetGraph(csf, nroots, num_new_leaves, leafdata, PETSC_COPY_VALUES, new_iremote, PETSC_COPY_VALUES)); 466 PetscCall(PetscFree(new_iremote)); // copy and delete because new_iremote is longer than it needs to be 467 PetscCall(PetscFree2(rootdata, leafdata)); 468 469 if (npoints < 0) { // empty point_sf 470 *closure_sf = csf; 471 } else { 472 PetscCall(PetscSFMerge(point_sf, csf, closure_sf)); 473 PetscCall(PetscSFDestroy(&csf)); 474 } 475 PetscCall(PetscObjectSetName((PetscObject)*closure_sf, "Composed Periodic Points")); 476 PetscFunctionReturn(PETSC_SUCCESS); 477 } 478 479 static PetscErrorCode DMGetIsoperiodicPointSF_Plex(DM dm, PetscSF *sf) 480 { 481 DM_Plex *plex = (DM_Plex *)dm->data; 482 483 PetscFunctionBegin; 484 if (!plex->periodic.composed_sf) PetscCall(DMPlexCreateIsoperiodicPointSF_Private(dm, plex->periodic.face_sfs[0], &plex->periodic.composed_sf, &plex->periodic.periodic_points)); 485 if (sf) *sf = plex->periodic.composed_sf; 486 PetscFunctionReturn(PETSC_SUCCESS); 487 } 488 489 PetscErrorCode DMPlexMigrateIsoperiodicFaceSF_Internal(DM old_dm, DM dm, PetscSF sf_migration) 490 { 491 DM_Plex *plex = (DM_Plex *)old_dm->data; 492 PetscSF sf_point, *new_face_sfs; 493 PetscMPIInt rank; 494 495 PetscFunctionBegin; 496 if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS); 497 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 498 PetscCall(DMGetPointSF(dm, &sf_point)); 499 PetscCall(PetscMalloc1(plex->periodic.num_face_sfs, &new_face_sfs)); 500 501 for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) { 502 PetscInt old_npoints, new_npoints, old_nleaf, new_nleaf, point_nleaf; 503 PetscSFNode *new_leafdata, *rootdata, *leafdata; 504 const PetscInt *old_local, *point_local; 505 const PetscSFNode *old_remote, *point_remote; 506 PetscCall(PetscSFGetGraph(plex->periodic.face_sfs[f], &old_npoints, &old_nleaf, &old_local, &old_remote)); 507 PetscCall(PetscSFGetGraph(sf_migration, NULL, &new_nleaf, NULL, NULL)); 508 PetscCall(PetscSFGetGraph(sf_point, &new_npoints, &point_nleaf, &point_local, &point_remote)); 509 PetscAssert(new_nleaf == new_npoints, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected migration leaf space to match new point root space"); 510 PetscCall(PetscMalloc3(old_npoints, &rootdata, old_npoints, &leafdata, new_npoints, &new_leafdata)); 511 512 // Fill new_leafdata with new owners of all points 513 for (PetscInt i = 0; i < new_npoints; i++) { 514 new_leafdata[i].rank = rank; 515 new_leafdata[i].index = i; 516 } 517 for (PetscInt i = 0; i < point_nleaf; i++) { 518 PetscInt j = point_local[i]; 519 new_leafdata[j] = point_remote[i]; 520 } 521 // REPLACE is okay because every leaf agrees about the new owners 522 PetscCall(PetscSFReduceBegin(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE)); 523 PetscCall(PetscSFReduceEnd(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE)); 524 // rootdata now contains the new owners 525 526 // Send to leaves of old space 527 for (PetscInt i = 0; i < old_npoints; i++) { 528 leafdata[i].rank = -1; 529 leafdata[i].index = -1; 530 } 531 PetscCall(PetscSFBcastBegin(plex->periodic.face_sfs[f], MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 532 PetscCall(PetscSFBcastEnd(plex->periodic.face_sfs[f], MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 533 534 // Send to new leaf space 535 PetscCall(PetscSFBcastBegin(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE)); 536 PetscCall(PetscSFBcastEnd(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE)); 537 538 PetscInt nface = 0, *new_local; 539 PetscSFNode *new_remote; 540 for (PetscInt i = 0; i < new_npoints; i++) nface += (new_leafdata[i].rank >= 0); 541 PetscCall(PetscMalloc1(nface, &new_local)); 542 PetscCall(PetscMalloc1(nface, &new_remote)); 543 nface = 0; 544 for (PetscInt i = 0; i < new_npoints; i++) { 545 if (new_leafdata[i].rank == -1) continue; 546 new_local[nface] = i; 547 new_remote[nface] = new_leafdata[i]; 548 nface++; 549 } 550 PetscCall(PetscFree3(rootdata, leafdata, new_leafdata)); 551 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &new_face_sfs[f])); 552 PetscCall(PetscSFSetGraph(new_face_sfs[f], new_npoints, nface, new_local, PETSC_OWN_POINTER, new_remote, PETSC_OWN_POINTER)); 553 { 554 char new_face_sf_name[PETSC_MAX_PATH_LEN]; 555 PetscCall(PetscSNPrintf(new_face_sf_name, sizeof new_face_sf_name, "Migrated Isoperiodic Faces #%" PetscInt_FMT, f)); 556 PetscCall(PetscObjectSetName((PetscObject)new_face_sfs[f], new_face_sf_name)); 557 } 558 } 559 560 PetscCall(DMPlexSetIsoperiodicFaceSF(dm, plex->periodic.num_face_sfs, new_face_sfs)); 561 PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, plex->periodic.num_face_sfs, (PetscScalar *)plex->periodic.transform)); 562 for (PetscInt f = 0; f < plex->periodic.num_face_sfs; f++) PetscCall(PetscSFDestroy(&new_face_sfs[f])); 563 PetscCall(PetscFree(new_face_sfs)); 564 PetscFunctionReturn(PETSC_SUCCESS); 565 } 566 567 PetscErrorCode DMPeriodicCoordinateSetUp_Internal(DM dm) 568 { 569 DM_Plex *plex = (DM_Plex *)dm->data; 570 571 PetscFunctionBegin; 572 if (!plex->periodic.face_sfs) PetscFunctionReturn(PETSC_SUCCESS); 573 PetscCall(DMGetIsoperiodicPointSF_Plex(dm, NULL)); 574 PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex)); 575 576 PetscInt dim; 577 PetscCall(DMGetDimension(dm, &dim)); 578 size_t count; 579 IS isdof; 580 { 581 PetscInt npoints; 582 const PetscInt *points; 583 IS is = plex->periodic.periodic_points; 584 PetscSegBuffer seg; 585 PetscSection section; 586 PetscCall(DMGetLocalSection(dm, §ion)); 587 PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 32, &seg)); 588 PetscCall(ISGetSize(is, &npoints)); 589 PetscCall(ISGetIndices(is, &points)); 590 for (PetscInt i = 0; i < npoints; i++) { 591 PetscInt point = points[i], off, dof; 592 PetscCall(PetscSectionGetOffset(section, point, &off)); 593 PetscCall(PetscSectionGetDof(section, point, &dof)); 594 PetscAssert(dof % dim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected dof %" PetscInt_FMT " not divisible by dimension %" PetscInt_FMT, dof, dim); 595 for (PetscInt j = 0; j < dof / dim; j++) { 596 PetscInt *slot; 597 PetscCall(PetscSegBufferGetInts(seg, 1, &slot)); 598 *slot = off / dim + j; 599 } 600 } 601 PetscInt *ind; 602 PetscCall(PetscSegBufferGetSize(seg, &count)); 603 PetscCall(PetscSegBufferExtractAlloc(seg, &ind)); 604 PetscCall(PetscSegBufferDestroy(&seg)); 605 PetscCall(ISCreateBlock(PETSC_COMM_SELF, dim, count, ind, PETSC_OWN_POINTER, &isdof)); 606 } 607 Vec L, P; 608 VecType vec_type; 609 VecScatter scatter; 610 PetscCall(DMGetLocalVector(dm, &L)); 611 PetscCall(VecCreate(PETSC_COMM_SELF, &P)); 612 PetscCall(VecSetSizes(P, count * dim, count * dim)); 613 PetscCall(VecGetType(L, &vec_type)); 614 PetscCall(VecSetType(P, vec_type)); 615 PetscCall(VecScatterCreate(P, NULL, L, isdof, &scatter)); 616 PetscCall(DMRestoreLocalVector(dm, &L)); 617 PetscCall(ISDestroy(&isdof)); 618 619 { 620 PetscScalar *x; 621 PetscCall(VecGetArrayWrite(P, &x)); 622 for (PetscInt i = 0; i < (PetscInt)count; i++) { 623 for (PetscInt j = 0; j < dim; j++) x[i * dim + j] = plex->periodic.transform[0][j][3]; 624 } 625 PetscCall(VecRestoreArrayWrite(P, &x)); 626 } 627 628 dm->periodic.affine_to_local = scatter; 629 dm->periodic.affine = P; 630 PetscCall(DMGlobalToLocalHookAdd(dm, NULL, DMCoordAddPeriodicOffsets_Private, NULL)); 631 PetscFunctionReturn(PETSC_SUCCESS); 632 } 633 634 // We'll just orient all the edges, though only periodic boundary edges need orientation 635 static PetscErrorCode DMPlexOrientPositiveEdges_Private(DM dm) 636 { 637 PetscInt dim, eStart, eEnd; 638 639 PetscFunctionBegin; 640 PetscCall(DMGetDimension(dm, &dim)); 641 if (dim < 3) PetscFunctionReturn(PETSC_SUCCESS); // not necessary 642 PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd)); 643 for (PetscInt e = eStart; e < eEnd; e++) { 644 const PetscInt *cone; 645 PetscCall(DMPlexGetCone(dm, e, &cone)); 646 if (cone[0] > cone[1]) PetscCall(DMPlexOrientPoint(dm, e, -1)); 647 } 648 PetscFunctionReturn(PETSC_SUCCESS); 649 } 650 651 PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate) 652 { 653 PetscInt eextent[3] = {1, 1, 1}, vextent[3] = {1, 1, 1}; 654 const Ijk closure_1[] = { 655 {0, 0, 0}, 656 {1, 0, 0}, 657 }; 658 const Ijk closure_2[] = { 659 {0, 0, 0}, 660 {1, 0, 0}, 661 {1, 1, 0}, 662 {0, 1, 0}, 663 }; 664 const Ijk closure_3[] = { 665 {0, 0, 0}, 666 {0, 1, 0}, 667 {1, 1, 0}, 668 {1, 0, 0}, 669 {0, 0, 1}, 670 {1, 0, 1}, 671 {1, 1, 1}, 672 {0, 1, 1}, 673 }; 674 const Ijk *const closure_dim[] = {NULL, closure_1, closure_2, closure_3}; 675 // This must be kept consistent with DMPlexCreateCubeMesh_Internal 676 const PetscInt face_marker_1[] = {1, 2}; 677 const PetscInt face_marker_2[] = {4, 2, 1, 3}; 678 const PetscInt face_marker_3[] = {6, 5, 3, 4, 1, 2}; 679 const PetscInt *const face_marker_dim[] = {NULL, face_marker_1, face_marker_2, face_marker_3}; 680 // Orient faces so the normal is in the positive axis and the first vertex is the one closest to zero. 681 // These orientations can be determined by examining cones of a reference quad and hex element. 682 const PetscInt face_orient_1[] = {0, 0}; 683 const PetscInt face_orient_2[] = {-1, 0, 0, -1}; 684 const PetscInt face_orient_3[] = {-2, 0, -2, 1, -2, 0}; 685 const PetscInt *const face_orient_dim[] = {NULL, face_orient_1, face_orient_2, face_orient_3}; 686 687 PetscFunctionBegin; 688 PetscAssertPointer(dm, 1); 689 PetscValidLogicalCollectiveInt(dm, dim, 2); 690 PetscCall(DMSetDimension(dm, dim)); 691 PetscMPIInt rank, size; 692 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size)); 693 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 694 for (PetscInt i = 0; i < dim; i++) { 695 eextent[i] = faces[i]; 696 vextent[i] = faces[i] + 1; 697 } 698 ZLayout layout; 699 PetscCall(ZLayoutCreate(size, eextent, vextent, &layout)); 700 PetscZSet vset; // set of all vertices in the closure of the owned elements 701 PetscCall(PetscZSetCreate(&vset)); 702 PetscInt local_elems = 0; 703 for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 704 Ijk loc = ZCodeSplit(z); 705 if (IjkActive(layout.vextent, loc)) PetscCall(PetscZSetAdd(vset, z)); 706 else { 707 z += ZStepOct(z); 708 continue; 709 } 710 if (IjkActive(layout.eextent, loc)) { 711 local_elems++; 712 // Add all neighboring vertices to set 713 for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 714 Ijk inc = closure_dim[dim][n]; 715 Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 716 ZCode v = ZEncode(nloc); 717 PetscCall(PetscZSetAdd(vset, v)); 718 } 719 } 720 } 721 PetscInt local_verts, off = 0; 722 ZCode *vert_z; 723 PetscCall(PetscZSetGetSize(vset, &local_verts)); 724 PetscCall(PetscMalloc1(local_verts, &vert_z)); 725 PetscCall(PetscZSetGetElems(vset, &off, vert_z)); 726 PetscCall(PetscZSetDestroy(&vset)); 727 // ZCode is unsigned for bitwise convenience, but highest bit should never be set, so can interpret as signed 728 PetscCall(PetscSortInt64(local_verts, (PetscInt64 *)vert_z)); 729 730 PetscCall(DMPlexSetChart(dm, 0, local_elems + local_verts)); 731 for (PetscInt e = 0; e < local_elems; e++) PetscCall(DMPlexSetConeSize(dm, e, PetscPowInt(2, dim))); 732 PetscCall(DMSetUp(dm)); 733 { 734 PetscInt e = 0; 735 for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 736 Ijk loc = ZCodeSplit(z); 737 if (!IjkActive(layout.eextent, loc)) { 738 z += ZStepOct(z); 739 continue; 740 } 741 PetscInt cone[8], orient[8] = {0}; 742 for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 743 Ijk inc = closure_dim[dim][n]; 744 Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 745 ZCode v = ZEncode(nloc); 746 PetscInt ci = ZCodeFind(v, local_verts, vert_z); 747 PetscAssert(ci >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find neighbor vertex in set"); 748 cone[n] = local_elems + ci; 749 } 750 PetscCall(DMPlexSetCone(dm, e, cone)); 751 PetscCall(DMPlexSetConeOrientation(dm, e, orient)); 752 e++; 753 } 754 } 755 756 PetscCall(DMPlexSymmetrize(dm)); 757 PetscCall(DMPlexStratify(dm)); 758 759 { // Create point SF 760 PetscSF sf; 761 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf)); 762 PetscInt owned_verts = ZCodeFind(layout.zstarts[rank + 1], local_verts, vert_z); 763 if (owned_verts < 0) owned_verts = -(owned_verts + 1); // We don't care whether the key was found 764 PetscInt num_ghosts = local_verts - owned_verts; // Due to sorting, owned vertices always come first 765 PetscInt *local_ghosts; 766 PetscSFNode *ghosts; 767 PetscCall(PetscMalloc1(num_ghosts, &local_ghosts)); 768 PetscCall(PetscMalloc1(num_ghosts, &ghosts)); 769 for (PetscInt i = 0; i < num_ghosts;) { 770 ZCode z = vert_z[owned_verts + i]; 771 PetscInt remote_rank = ZCodeFind(z, size + 1, layout.zstarts), remote_count = 0; 772 if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1; 773 // We have a new remote rank; find all the ghost indices (which are contiguous in vert_z) 774 775 // Count the elements on remote_rank 776 PetscInt remote_elem = ZLayoutElementsOnRank(&layout, remote_rank); 777 778 // Traverse vertices and make ghost links 779 for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) { 780 Ijk loc = ZCodeSplit(rz); 781 if (rz == z) { 782 local_ghosts[i] = local_elems + owned_verts + i; 783 ghosts[i].rank = remote_rank; 784 ghosts[i].index = remote_elem + remote_count; 785 i++; 786 if (i == num_ghosts) break; 787 z = vert_z[owned_verts + i]; 788 } 789 if (IjkActive(layout.vextent, loc)) remote_count++; 790 else rz += ZStepOct(rz); 791 } 792 } 793 PetscCall(PetscSFSetGraph(sf, local_elems + local_verts, num_ghosts, local_ghosts, PETSC_OWN_POINTER, ghosts, PETSC_OWN_POINTER)); 794 PetscCall(PetscObjectSetName((PetscObject)sf, "SFC Point SF")); 795 PetscCall(DMSetPointSF(dm, sf)); 796 PetscCall(PetscSFDestroy(&sf)); 797 } 798 { 799 Vec coordinates; 800 PetscScalar *coords; 801 PetscSection coord_section; 802 PetscInt coord_size; 803 PetscCall(DMGetCoordinateSection(dm, &coord_section)); 804 PetscCall(PetscSectionSetNumFields(coord_section, 1)); 805 PetscCall(PetscSectionSetFieldComponents(coord_section, 0, dim)); 806 PetscCall(PetscSectionSetChart(coord_section, local_elems, local_elems + local_verts)); 807 for (PetscInt v = 0; v < local_verts; v++) { 808 PetscInt point = local_elems + v; 809 PetscCall(PetscSectionSetDof(coord_section, point, dim)); 810 PetscCall(PetscSectionSetFieldDof(coord_section, point, 0, dim)); 811 } 812 PetscCall(PetscSectionSetUp(coord_section)); 813 PetscCall(PetscSectionGetStorageSize(coord_section, &coord_size)); 814 PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates)); 815 PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates")); 816 PetscCall(VecSetSizes(coordinates, coord_size, PETSC_DETERMINE)); 817 PetscCall(VecSetBlockSize(coordinates, dim)); 818 PetscCall(VecSetType(coordinates, VECSTANDARD)); 819 PetscCall(VecGetArray(coordinates, &coords)); 820 for (PetscInt v = 0; v < local_verts; v++) { 821 Ijk loc = ZCodeSplit(vert_z[v]); 822 coords[v * dim + 0] = lower[0] + loc.i * (upper[0] - lower[0]) / layout.eextent.i; 823 if (dim > 1) coords[v * dim + 1] = lower[1] + loc.j * (upper[1] - lower[1]) / layout.eextent.j; 824 if (dim > 2) coords[v * dim + 2] = lower[2] + loc.k * (upper[2] - lower[2]) / layout.eextent.k; 825 } 826 PetscCall(VecRestoreArray(coordinates, &coords)); 827 PetscCall(DMSetCoordinatesLocal(dm, coordinates)); 828 PetscCall(VecDestroy(&coordinates)); 829 } 830 if (interpolate) { 831 PetscCall(DMPlexInterpolateInPlace_Internal(dm)); 832 // It's currently necessary to orient the donor and periodic edges consistently. An easy way to ensure that is ot 833 // give all edges positive orientation. Since vertices are created in Z-order, all ranks will agree about the 834 // ordering cone[0] < cone[1]. This is overkill and it would be nice to remove this preparation and make 835 // DMPlexCreateIsoperiodicClosureSF_Private() more resilient, so it fixes any inconsistent orientations. That might 836 // be needed in a general CGNS reader, for example. 837 PetscCall(DMPlexOrientPositiveEdges_Private(dm)); 838 839 DMLabel label; 840 PetscCall(DMCreateLabel(dm, "Face Sets")); 841 PetscCall(DMGetLabel(dm, "Face Sets", &label)); 842 PetscSegBuffer per_faces[3], donor_face_closure[3], my_donor_faces[3]; 843 for (PetscInt i = 0; i < 3; i++) { 844 PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &per_faces[i])); 845 PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &my_donor_faces[i])); 846 PetscCall(PetscSegBufferCreate(sizeof(ZCode), 64 * PetscPowInt(2, dim), &donor_face_closure[i])); 847 } 848 PetscInt fStart, fEnd, vStart, vEnd; 849 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 850 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 851 for (PetscInt f = fStart; f < fEnd; f++) { 852 PetscInt npoints, *points = NULL, num_fverts = 0, fverts[8]; 853 PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 854 PetscInt bc_count[6] = {0}; 855 for (PetscInt i = 0; i < npoints; i++) { 856 PetscInt p = points[2 * i]; 857 if (p < vStart || vEnd <= p) continue; 858 fverts[num_fverts++] = p; 859 Ijk loc = ZCodeSplit(vert_z[p - vStart]); 860 // Convention here matches DMPlexCreateCubeMesh_Internal 861 bc_count[0] += loc.i == 0; 862 bc_count[1] += loc.i == layout.vextent.i - 1; 863 bc_count[2] += loc.j == 0; 864 bc_count[3] += loc.j == layout.vextent.j - 1; 865 bc_count[4] += loc.k == 0; 866 bc_count[5] += loc.k == layout.vextent.k - 1; 867 } 868 PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 869 for (PetscInt bc = 0, bc_match = 0; bc < 2 * dim; bc++) { 870 if (bc_count[bc] == PetscPowInt(2, dim - 1)) { 871 PetscCall(DMPlexOrientPoint(dm, f, face_orient_dim[dim][bc])); 872 if (periodicity[bc / 2] == DM_BOUNDARY_PERIODIC) { 873 PetscInt *put; 874 if (bc % 2 == 0) { // donor face; no label 875 PetscCall(PetscSegBufferGet(my_donor_faces[bc / 2], 1, &put)); 876 *put = f; 877 } else { // periodic face 878 PetscCall(PetscSegBufferGet(per_faces[bc / 2], 1, &put)); 879 *put = f; 880 ZCode *zput; 881 PetscCall(PetscSegBufferGet(donor_face_closure[bc / 2], num_fverts, &zput)); 882 for (PetscInt i = 0; i < num_fverts; i++) { 883 Ijk loc = ZCodeSplit(vert_z[fverts[i] - vStart]); 884 switch (bc / 2) { 885 case 0: 886 loc.i = 0; 887 break; 888 case 1: 889 loc.j = 0; 890 break; 891 case 2: 892 loc.k = 0; 893 break; 894 } 895 *zput++ = ZEncode(loc); 896 } 897 } 898 continue; 899 } 900 PetscAssert(bc_match == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face matches multiple face sets"); 901 PetscCall(DMLabelSetValue(label, f, face_marker_dim[dim][bc])); 902 bc_match++; 903 } 904 } 905 } 906 // Ensure that the Coordinate DM has our new boundary labels 907 DM cdm; 908 PetscCall(DMGetCoordinateDM(dm, &cdm)); 909 PetscCall(DMCopyLabels(dm, cdm, PETSC_COPY_VALUES, PETSC_FALSE, DM_COPY_LABELS_FAIL)); 910 if (periodicity[0] == DM_BOUNDARY_PERIODIC || (dim > 1 && periodicity[1] == DM_BOUNDARY_PERIODIC) || (dim > 2 && periodicity[2] == DM_BOUNDARY_PERIODIC)) { 911 PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(dm, &layout, vert_z, per_faces, lower, upper, periodicity, donor_face_closure, my_donor_faces)); 912 } 913 for (PetscInt i = 0; i < 3; i++) { 914 PetscCall(PetscSegBufferDestroy(&per_faces[i])); 915 PetscCall(PetscSegBufferDestroy(&donor_face_closure[i])); 916 PetscCall(PetscSegBufferDestroy(&my_donor_faces[i])); 917 } 918 } 919 PetscCall(PetscFree(layout.zstarts)); 920 PetscCall(PetscFree(vert_z)); 921 PetscFunctionReturn(PETSC_SUCCESS); 922 } 923 924 /*@ 925 DMPlexSetIsoperiodicFaceSF - Express periodicity from an existing mesh 926 927 Logically Collective 928 929 Input Parameters: 930 + dm - The `DMPLEX` on which to set periodicity 931 . num_face_sfs - Number of `PetscSF`s in `face_sfs` 932 - 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. 933 934 Level: advanced 935 936 Note: 937 One can use `-dm_plex_shape zbox` to use this mode of periodicity, wherein the periodic points are distinct both globally 938 and locally, but are paired when creating a global dof space. 939 940 .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexGetIsoperiodicFaceSF()` 941 @*/ 942 PetscErrorCode DMPlexSetIsoperiodicFaceSF(DM dm, PetscInt num_face_sfs, PetscSF *face_sfs) 943 { 944 DM_Plex *plex = (DM_Plex *)dm->data; 945 946 PetscFunctionBegin; 947 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 948 if (face_sfs) PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex)); 949 if (face_sfs == plex->periodic.face_sfs && num_face_sfs == plex->periodic.num_face_sfs) PetscFunctionReturn(PETSC_SUCCESS); 950 951 for (PetscInt i = 0; i < num_face_sfs; i++) PetscCall(PetscObjectReference((PetscObject)face_sfs[i])); 952 953 if (plex->periodic.num_face_sfs > 0) { 954 for (PetscInt i = 0; i < plex->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&plex->periodic.face_sfs[i])); 955 PetscCall(PetscFree(plex->periodic.face_sfs)); 956 } 957 958 plex->periodic.num_face_sfs = num_face_sfs; 959 PetscCall(PetscCalloc1(num_face_sfs, &plex->periodic.face_sfs)); 960 for (PetscInt i = 0; i < num_face_sfs; i++) plex->periodic.face_sfs[i] = face_sfs[i]; 961 962 DM cdm = dm->coordinates[0].dm; // Can't DMGetCoordinateDM because it automatically creates one 963 if (cdm) { 964 PetscCall(DMPlexSetIsoperiodicFaceSF(cdm, num_face_sfs, face_sfs)); 965 if (face_sfs) cdm->periodic.setup = DMPeriodicCoordinateSetUp_Internal; 966 } 967 PetscFunctionReturn(PETSC_SUCCESS); 968 } 969 970 /*@C 971 DMPlexGetIsoperiodicFaceSF - Obtain periodicity for a mesh 972 973 Logically Collective 974 975 Input Parameter: 976 . dm - The `DMPLEX` for which to obtain periodic relation 977 978 Output Parameter: 979 + num_face_sfs - Number of `PetscSF`s in the array 980 - 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. 981 982 Level: advanced 983 984 .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()` 985 @*/ 986 PetscErrorCode DMPlexGetIsoperiodicFaceSF(DM dm, PetscInt *num_face_sfs, const PetscSF **face_sfs) 987 { 988 DM_Plex *plex = (DM_Plex *)dm->data; 989 990 PetscFunctionBegin; 991 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 992 *face_sfs = plex->periodic.face_sfs; 993 *num_face_sfs = plex->periodic.num_face_sfs; 994 PetscFunctionReturn(PETSC_SUCCESS); 995 } 996 997 /*@C 998 DMPlexSetIsoperiodicFaceTransform - set geometric transform from donor to periodic points 999 1000 Logically Collective 1001 1002 Input Parameters: 1003 + dm - `DMPLEX` that has been configured with `DMPlexSetIsoperiodicFaceSF()` 1004 . n - Number of transforms in array 1005 - t - Array of 4x4 affine transformation basis. 1006 1007 Level: advanced 1008 1009 Notes: 1010 Affine transforms are 4x4 matrices in which the leading 3x3 block expresses a rotation (or identity for no rotation), 1011 the last column contains a translation vector, and the bottom row is all zero except the last entry, which must always 1012 be 1. This representation is common in geometric modeling and allows affine transformations to be composed using 1013 simple matrix multiplication. 1014 1015 Although the interface accepts a general affine transform, only affine translation is supported at present. 1016 1017 Developer Notes: 1018 This interface should be replaced by making BasisTransform public, expanding it to support affine representations, and 1019 adding GPU implementations to apply the G2L/L2G transforms. 1020 1021 .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()` 1022 @*/ 1023 PetscErrorCode DMPlexSetIsoperiodicFaceTransform(DM dm, PetscInt n, const PetscScalar *t) 1024 { 1025 DM_Plex *plex = (DM_Plex *)dm->data; 1026 1027 PetscFunctionBegin; 1028 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 1029 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); 1030 1031 PetscCall(PetscMalloc1(n, &plex->periodic.transform)); 1032 for (PetscInt i = 0; i < n; i++) { 1033 for (PetscInt j = 0; j < 4; j++) { 1034 for (PetscInt k = 0; k < 4; k++) { 1035 PetscCheck(j != k || t[i * 16 + j * 4 + k] == 1., PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Rotated transforms not supported"); 1036 plex->periodic.transform[i][j][k] = t[i * 16 + j * 4 + k]; 1037 } 1038 } 1039 } 1040 PetscFunctionReturn(PETSC_SUCCESS); 1041 } 1042