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, const PetscReal *lower, const PetscReal *upper, const DMBoundaryType *periodicity, PetscSegBuffer donor_face_closure, PetscSegBuffer my_donor_faces) 183 { 184 MPI_Comm comm; 185 size_t num_faces; 186 PetscInt dim, *faces, vStart, vEnd; 187 PetscMPIInt size; 188 ZCode *donor_verts, *donor_minz; 189 PetscSFNode *leaf; 190 191 PetscFunctionBegin; 192 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 193 PetscCallMPI(MPI_Comm_size(comm, &size)); 194 PetscCall(DMGetDimension(dm, &dim)); 195 const PetscInt csize = PetscPowInt(2, dim - 1); 196 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 197 PetscCall(PetscSegBufferGetSize(per_faces, &num_faces)); 198 PetscCall(PetscSegBufferExtractInPlace(per_faces, &faces)); 199 PetscCall(PetscSegBufferExtractInPlace(donor_face_closure, &donor_verts)); 200 PetscCall(PetscMalloc1(num_faces, &donor_minz)); 201 PetscCall(PetscMalloc1(num_faces, &leaf)); 202 for (PetscInt i = 0; i < (PetscInt)num_faces; i++) { 203 ZCode minz = donor_verts[i * csize]; 204 for (PetscInt j = 1; j < csize; j++) minz = PetscMin(minz, donor_verts[i * csize + j]); 205 donor_minz[i] = minz; 206 } 207 { 208 PetscBool sorted; 209 PetscCall(PetscSortedInt64(num_faces, (const PetscInt64 *)donor_minz, &sorted)); 210 PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_PLIB, "minz not sorted; periodicity in multiple dimensions not yet supported"); 211 } 212 for (PetscInt i = 0; i < (PetscInt)num_faces;) { 213 ZCode z = donor_minz[i]; 214 PetscInt remote_rank = ZCodeFind(z, size + 1, layout->zstarts), remote_count = 0; 215 if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1; 216 // Process all the vertices on this rank 217 for (ZCode rz = layout->zstarts[remote_rank]; rz < layout->zstarts[remote_rank + 1]; rz++) { 218 Ijk loc = ZCodeSplit(rz); 219 if (rz == z) { 220 leaf[i].rank = remote_rank; 221 leaf[i].index = remote_count; 222 i++; 223 if (i == (PetscInt)num_faces) break; 224 z = donor_minz[i]; 225 } 226 if (IjkActive(layout->vextent, loc)) remote_count++; 227 } 228 } 229 PetscCall(PetscFree(donor_minz)); 230 PetscSF sfper; 231 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sfper)); 232 PetscCall(PetscSFSetGraph(sfper, vEnd - vStart, num_faces, NULL, PETSC_USE_POINTER, leaf, PETSC_USE_POINTER)); 233 const PetscInt *my_donor_degree; 234 PetscCall(PetscSFComputeDegreeBegin(sfper, &my_donor_degree)); 235 PetscCall(PetscSFComputeDegreeEnd(sfper, &my_donor_degree)); 236 PetscInt num_multiroots = 0; 237 for (PetscInt i = 0; i < vEnd - vStart; i++) { 238 num_multiroots += my_donor_degree[i]; 239 if (my_donor_degree[i] == 0) continue; 240 PetscAssert(my_donor_degree[i] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex has multiple faces"); 241 } 242 PetscInt *my_donors, *donor_indices, *my_donor_indices; 243 size_t num_my_donors; 244 PetscCall(PetscSegBufferGetSize(my_donor_faces, &num_my_donors)); 245 PetscCheck((PetscInt)num_my_donors == num_multiroots, PETSC_COMM_SELF, PETSC_ERR_SUP, "Donor request does not match expected donors"); 246 PetscCall(PetscSegBufferExtractInPlace(my_donor_faces, &my_donors)); 247 PetscCall(PetscMalloc1(vEnd - vStart, &my_donor_indices)); 248 for (PetscInt i = 0; i < (PetscInt)num_my_donors; i++) { 249 PetscInt f = my_donors[i]; 250 PetscInt num_points, *points = NULL, minv = PETSC_MAX_INT; 251 PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points)); 252 for (PetscInt j = 0; j < num_points; j++) { 253 PetscInt p = points[2 * j]; 254 if (p < vStart || vEnd <= p) continue; 255 minv = PetscMin(minv, p); 256 } 257 PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &num_points, &points)); 258 PetscAssert(my_donor_degree[minv - vStart] == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Local vertex not requested"); 259 my_donor_indices[minv - vStart] = f; 260 } 261 PetscCall(PetscMalloc1(num_faces, &donor_indices)); 262 PetscCall(PetscSFBcastBegin(sfper, MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE)); 263 PetscCall(PetscSFBcastEnd(sfper, MPIU_INT, my_donor_indices, donor_indices, MPI_REPLACE)); 264 PetscCall(PetscFree(my_donor_indices)); 265 // Modify our leafs so they point to donor faces instead of donor minz. Additionally, give them indices as faces. 266 for (PetscInt i = 0; i < (PetscInt)num_faces; i++) leaf[i].index = donor_indices[i]; 267 PetscCall(PetscFree(donor_indices)); 268 PetscInt pStart, pEnd; 269 PetscCall(DMPlexGetChart(dm, &pStart, &pEnd)); 270 PetscCall(PetscSFSetGraph(sfper, pEnd - pStart, num_faces, faces, PETSC_COPY_VALUES, leaf, PETSC_OWN_POINTER)); 271 PetscCall(PetscObjectSetName((PetscObject)sfper, "Z-order Isoperiodic Faces")); 272 273 PetscCall(DMPlexSetIsoperiodicFaceSF(dm, sfper)); 274 275 PetscScalar t[4][4] = {{0}}; 276 t[0][0] = 1; 277 t[1][1] = 1; 278 t[2][2] = 1; 279 t[3][3] = 1; 280 for (PetscInt i = 0; i < dim; i++) 281 if (periodicity[i] == DM_BOUNDARY_PERIODIC) t[i][3] = upper[i] - lower[i]; 282 PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, &t[0][0])); 283 PetscCall(PetscSFDestroy(&sfper)); 284 PetscFunctionReturn(PETSC_SUCCESS); 285 } 286 287 // This is a DMGlobalToLocalHook that applies the affine offsets. When extended for rotated periodicity, it'll need to 288 // apply a rotatonal transform and similar operations will be needed for fields (e.g., to rotate a velocity vector). 289 // We use this crude approach here so we don't have to write new GPU kernels yet. 290 static PetscErrorCode DMCoordAddPeriodicOffsets_Private(DM dm, Vec g, InsertMode mode, Vec l, void *ctx) 291 { 292 PetscFunctionBegin; 293 PetscCall(VecScatterBegin(dm->periodic.affine_to_local, dm->periodic.affine, l, ADD_VALUES, SCATTER_FORWARD)); 294 PetscCall(VecScatterEnd(dm->periodic.affine_to_local, dm->periodic.affine, l, ADD_VALUES, SCATTER_FORWARD)); 295 PetscFunctionReturn(PETSC_SUCCESS); 296 } 297 298 // Start with an SF for a positive depth (e.g., faces) and create a new SF for matched closure. The caller must ensure 299 // that both the donor (root) face and the periodic (leaf) face have consistent orientation, meaning that their closures 300 // are isomorphic. It may be useful/necessary for this restriction to be loosened. 301 // 302 // Output Arguments: 303 // 304 // + closure_sf - augmented point SF (see `DMGetPointSF()`) that includes the faces and all points in its closure. This 305 // can be used to create a global section and section SF. 306 // - is_points - index set for just the points in the closure of `face_sf`. These may be used to apply an affine 307 // transformation to periodic dofs; see DMPeriodicCoordinateSetUp_Internal(). 308 // 309 static PetscErrorCode DMPlexCreateIsoperiodicPointSF_Private(DM dm, PetscSF face_sf, PetscSF *closure_sf, IS *is_points) 310 { 311 MPI_Comm comm; 312 PetscInt nroots, nleaves, npoints; 313 const PetscInt *filocal, *pilocal; 314 const PetscSFNode *firemote, *piremote; 315 PetscMPIInt rank; 316 PetscSF point_sf; 317 318 PetscFunctionBegin; 319 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 320 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 321 PetscCall(PetscSFGetGraph(face_sf, &nroots, &nleaves, &filocal, &firemote)); 322 PetscCall(DMGetPointSF(dm, &point_sf)); // Point SF has remote points 323 PetscCall(PetscSFGetGraph(point_sf, NULL, &npoints, &pilocal, &piremote)); 324 PetscInt *rootdata, *leafdata; 325 PetscCall(PetscCalloc2(2 * nroots, &rootdata, 2 * nroots, &leafdata)); 326 for (PetscInt i = 0; i < nleaves; i++) { 327 PetscInt point = filocal[i], cl_size, *closure = NULL; 328 PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 329 leafdata[point] = cl_size - 1; 330 PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 331 } 332 PetscCall(PetscSFReduceBegin(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM)); 333 PetscCall(PetscSFReduceEnd(face_sf, MPIU_INT, leafdata, rootdata + nroots, MPIU_SUM)); 334 335 PetscInt root_offset = 0; 336 for (PetscInt p = 0; p < nroots; p++) { 337 const PetscInt *donor_dof = rootdata + nroots; 338 if (donor_dof[p] == 0) { 339 rootdata[2 * p] = -1; 340 rootdata[2 * p + 1] = -1; 341 continue; 342 } 343 PetscInt cl_size; 344 PetscInt *closure = NULL; 345 PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 346 // cl_size - 1 = points not including self 347 PetscAssert(donor_dof[p] == cl_size - 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Reduced leaf cone sizes do not match root cone sizes"); 348 rootdata[2 * p] = root_offset; 349 rootdata[2 * p + 1] = cl_size - 1; 350 root_offset += cl_size - 1; 351 PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 352 } 353 PetscCall(PetscSFBcastBegin(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 354 PetscCall(PetscSFBcastEnd(face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 355 // Count how many leaves we need to communicate the closures 356 PetscInt leaf_offset = 0; 357 for (PetscInt i = 0; i < nleaves; i++) { 358 PetscInt point = filocal[i]; 359 if (leafdata[2 * point + 1] < 0) continue; 360 leaf_offset += leafdata[2 * point + 1]; 361 } 362 363 PetscSFNode *closure_leaf; 364 PetscCall(PetscMalloc1(leaf_offset, &closure_leaf)); 365 leaf_offset = 0; 366 for (PetscInt i = 0; i < nleaves; i++) { 367 PetscInt point = filocal[i]; 368 PetscInt cl_size = leafdata[2 * point + 1]; 369 if (cl_size < 0) continue; 370 for (PetscInt j = 0; j < cl_size; j++) { 371 closure_leaf[leaf_offset].rank = firemote[i].rank; 372 closure_leaf[leaf_offset].index = leafdata[2 * point] + j; 373 leaf_offset++; 374 } 375 } 376 377 PetscSF sf_closure; 378 PetscCall(PetscSFCreate(comm, &sf_closure)); 379 PetscCall(PetscSFSetGraph(sf_closure, root_offset, leaf_offset, NULL, PETSC_USE_POINTER, closure_leaf, PETSC_OWN_POINTER)); 380 381 // Pack root buffer with owner for every point in the root cones 382 PetscSFNode *donor_closure; 383 PetscCall(PetscCalloc1(root_offset, &donor_closure)); 384 root_offset = 0; 385 for (PetscInt p = 0; p < nroots; p++) { 386 if (rootdata[2 * p] < 0) continue; 387 PetscInt cl_size; 388 PetscInt *closure = NULL; 389 PetscCall(DMPlexGetTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 390 for (PetscInt j = 1; j < cl_size; j++) { 391 PetscInt c = closure[2 * j]; 392 if (pilocal) { 393 PetscInt found = -1; 394 if (npoints > 0) PetscCall(PetscFindInt(c, npoints, pilocal, &found)); 395 if (found >= 0) { 396 donor_closure[root_offset++] = piremote[found]; 397 continue; 398 } 399 } 400 // we own c 401 donor_closure[root_offset].rank = rank; 402 donor_closure[root_offset].index = c; 403 root_offset++; 404 } 405 PetscCall(DMPlexRestoreTransitiveClosure(dm, p, PETSC_TRUE, &cl_size, &closure)); 406 } 407 408 PetscSFNode *leaf_donor_closure; 409 PetscCall(PetscMalloc1(leaf_offset, &leaf_donor_closure)); 410 PetscCall(PetscSFBcastBegin(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE)); 411 PetscCall(PetscSFBcastEnd(sf_closure, MPIU_2INT, donor_closure, leaf_donor_closure, MPI_REPLACE)); 412 PetscCall(PetscSFDestroy(&sf_closure)); 413 PetscCall(PetscFree(donor_closure)); 414 415 PetscSFNode *new_iremote; 416 PetscCall(PetscCalloc1(nroots, &new_iremote)); 417 for (PetscInt i = 0; i < nroots; i++) new_iremote[i].rank = -1; 418 // Walk leaves and match vertices 419 leaf_offset = 0; 420 for (PetscInt i = 0; i < nleaves; i++) { 421 PetscInt point = filocal[i], cl_size; 422 PetscInt *closure = NULL; 423 PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 424 for (PetscInt j = 1; j < cl_size; j++) { // TODO: should we send donor edge orientations so we can flip for consistency? 425 PetscInt c = closure[2 * j]; 426 PetscSFNode lc = leaf_donor_closure[leaf_offset]; 427 // printf("[%d] face %d.%d: %d ?-- (%d,%d)\n", rank, point, j, c, lc.rank, lc.index); 428 if (new_iremote[c].rank == -1) { 429 new_iremote[c] = lc; 430 } 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"); 431 leaf_offset++; 432 } 433 PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &cl_size, &closure)); 434 } 435 PetscCall(PetscFree(leaf_donor_closure)); 436 437 // Include face points in closure SF 438 for (PetscInt i = 0; i < nleaves; i++) new_iremote[filocal[i]] = firemote[i]; 439 // consolidate leaves 440 PetscInt num_new_leaves = 0; 441 for (PetscInt i = 0; i < nroots; i++) { 442 if (new_iremote[i].rank == -1) continue; 443 new_iremote[num_new_leaves] = new_iremote[i]; 444 leafdata[num_new_leaves] = i; 445 num_new_leaves++; 446 } 447 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, num_new_leaves, leafdata, PETSC_COPY_VALUES, is_points)); 448 449 PetscSF csf; 450 PetscCall(PetscSFCreate(comm, &csf)); 451 PetscCall(PetscSFSetGraph(csf, nroots, num_new_leaves, leafdata, PETSC_COPY_VALUES, new_iremote, PETSC_COPY_VALUES)); 452 PetscCall(PetscFree(new_iremote)); // copy and delete because new_iremote is longer than it needs to be 453 PetscCall(PetscFree2(rootdata, leafdata)); 454 455 if (npoints < 0) { // empty point_sf 456 *closure_sf = csf; 457 } else { 458 PetscCall(PetscSFMerge(point_sf, csf, closure_sf)); 459 PetscCall(PetscSFDestroy(&csf)); 460 } 461 PetscCall(PetscObjectSetName((PetscObject)*closure_sf, "Composed Periodic Points")); 462 PetscFunctionReturn(PETSC_SUCCESS); 463 } 464 465 static PetscErrorCode DMGetIsoperiodicPointSF_Plex(DM dm, PetscSF *sf) 466 { 467 DM_Plex *plex = (DM_Plex *)dm->data; 468 469 PetscFunctionBegin; 470 if (!plex->periodic.composed_sf) { 471 PetscSF face_sf = plex->periodic.face_sf; 472 473 PetscCall(DMPlexCreateIsoperiodicPointSF_Private(dm, face_sf, &plex->periodic.composed_sf, &plex->periodic.periodic_points)); 474 } 475 if (sf) *sf = plex->periodic.composed_sf; 476 PetscFunctionReturn(PETSC_SUCCESS); 477 } 478 479 PetscErrorCode DMPlexMigrateIsoperiodicFaceSF_Internal(DM old_dm, DM dm, PetscSF sf_migration) 480 { 481 DM_Plex *plex = (DM_Plex *)old_dm->data; 482 PetscSF sf_point; 483 PetscMPIInt rank; 484 485 PetscFunctionBegin; 486 if (!plex->periodic.face_sf) PetscFunctionReturn(PETSC_SUCCESS); 487 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 488 PetscCall(DMGetPointSF(dm, &sf_point)); 489 PetscInt old_npoints, new_npoints, old_nleaf, new_nleaf, point_nleaf; 490 PetscSFNode *new_leafdata, *rootdata, *leafdata; 491 const PetscInt *old_local, *point_local; 492 const PetscSFNode *old_remote, *point_remote; 493 PetscCall(PetscSFGetGraph(plex->periodic.face_sf, &old_npoints, &old_nleaf, &old_local, &old_remote)); 494 PetscCall(PetscSFGetGraph(sf_migration, NULL, &new_nleaf, NULL, NULL)); 495 PetscCall(PetscSFGetGraph(sf_point, &new_npoints, &point_nleaf, &point_local, &point_remote)); 496 PetscAssert(new_nleaf == new_npoints, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected migration leaf space to match new point root space"); 497 PetscCall(PetscMalloc3(old_npoints, &rootdata, old_npoints, &leafdata, new_npoints, &new_leafdata)); 498 499 // Fill new_leafdata with new owners of all points 500 for (PetscInt i = 0; i < new_npoints; i++) { 501 new_leafdata[i].rank = rank; 502 new_leafdata[i].index = i; 503 } 504 for (PetscInt i = 0; i < point_nleaf; i++) { 505 PetscInt j = point_local[i]; 506 new_leafdata[j] = point_remote[i]; 507 } 508 // REPLACE is okay because every leaf agrees about the new owners 509 PetscCall(PetscSFReduceBegin(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE)); 510 PetscCall(PetscSFReduceEnd(sf_migration, MPIU_2INT, new_leafdata, rootdata, MPI_REPLACE)); 511 // rootdata now contains the new owners 512 513 // Send to leaves of old space 514 for (PetscInt i = 0; i < old_npoints; i++) { 515 leafdata[i].rank = -1; 516 leafdata[i].index = -1; 517 } 518 PetscCall(PetscSFBcastBegin(plex->periodic.face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 519 PetscCall(PetscSFBcastEnd(plex->periodic.face_sf, MPIU_2INT, rootdata, leafdata, MPI_REPLACE)); 520 521 // Send to new leaf space 522 PetscCall(PetscSFBcastBegin(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE)); 523 PetscCall(PetscSFBcastEnd(sf_migration, MPIU_2INT, leafdata, new_leafdata, MPI_REPLACE)); 524 525 PetscInt nface = 0, *new_local; 526 PetscSFNode *new_remote; 527 for (PetscInt i = 0; i < new_npoints; i++) nface += (new_leafdata[i].rank >= 0); 528 PetscCall(PetscMalloc1(nface, &new_local)); 529 PetscCall(PetscMalloc1(nface, &new_remote)); 530 nface = 0; 531 for (PetscInt i = 0; i < new_npoints; i++) { 532 if (new_leafdata[i].rank == -1) continue; 533 new_local[nface] = i; 534 new_remote[nface] = new_leafdata[i]; 535 nface++; 536 } 537 PetscCall(PetscFree3(rootdata, leafdata, new_leafdata)); 538 PetscSF sf_face; 539 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf_face)); 540 PetscCall(PetscSFSetGraph(sf_face, new_npoints, nface, new_local, PETSC_OWN_POINTER, new_remote, PETSC_OWN_POINTER)); 541 PetscCall(PetscObjectSetName((PetscObject)sf_face, "Migrated Isoperiodic Faces")); 542 PetscCall(DMPlexSetIsoperiodicFaceSF(dm, sf_face)); 543 PetscCall(DMPlexSetIsoperiodicFaceTransform(dm, &plex->periodic.transform[0][0])); 544 PetscCall(PetscSFDestroy(&sf_face)); 545 PetscFunctionReturn(PETSC_SUCCESS); 546 } 547 548 PetscErrorCode DMPeriodicCoordinateSetUp_Internal(DM dm) 549 { 550 DM_Plex *plex = (DM_Plex *)dm->data; 551 552 PetscFunctionBegin; 553 if (!plex->periodic.face_sf) PetscFunctionReturn(PETSC_SUCCESS); 554 PetscCall(DMGetIsoperiodicPointSF_Plex(dm, NULL)); 555 PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex)); 556 557 PetscInt dim; 558 PetscCall(DMGetDimension(dm, &dim)); 559 size_t count; 560 IS isdof; 561 { 562 PetscInt npoints; 563 const PetscInt *points; 564 IS is = plex->periodic.periodic_points; 565 PetscSegBuffer seg; 566 PetscSection section; 567 PetscCall(DMGetLocalSection(dm, §ion)); 568 PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 32, &seg)); 569 PetscCall(ISGetSize(is, &npoints)); 570 PetscCall(ISGetIndices(is, &points)); 571 for (PetscInt i = 0; i < npoints; i++) { 572 PetscInt point = points[i], off, dof; 573 PetscCall(PetscSectionGetOffset(section, point, &off)); 574 PetscCall(PetscSectionGetDof(section, point, &dof)); 575 PetscAssert(dof % dim == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected dof %" PetscInt_FMT " not divisible by dimension %" PetscInt_FMT, dof, dim); 576 for (PetscInt j = 0; j < dof / dim; j++) { 577 PetscInt *slot; 578 PetscCall(PetscSegBufferGetInts(seg, 1, &slot)); 579 *slot = off / dim + j; 580 } 581 } 582 PetscInt *ind; 583 PetscCall(PetscSegBufferGetSize(seg, &count)); 584 PetscCall(PetscSegBufferExtractAlloc(seg, &ind)); 585 PetscCall(PetscSegBufferDestroy(&seg)); 586 PetscCall(ISCreateBlock(PETSC_COMM_SELF, dim, count, ind, PETSC_OWN_POINTER, &isdof)); 587 } 588 Vec L, P; 589 VecType vec_type; 590 VecScatter scatter; 591 PetscCall(DMGetLocalVector(dm, &L)); 592 PetscCall(VecCreate(PETSC_COMM_SELF, &P)); 593 PetscCall(VecSetSizes(P, count * dim, count * dim)); 594 PetscCall(VecGetType(L, &vec_type)); 595 PetscCall(VecSetType(P, vec_type)); 596 PetscCall(VecScatterCreate(P, NULL, L, isdof, &scatter)); 597 PetscCall(DMRestoreLocalVector(dm, &L)); 598 PetscCall(ISDestroy(&isdof)); 599 600 { 601 PetscScalar *x; 602 PetscCall(VecGetArrayWrite(P, &x)); 603 for (PetscInt i = 0; i < (PetscInt)count; i++) { 604 for (PetscInt j = 0; j < dim; j++) x[i * dim + j] = plex->periodic.transform[j][3]; 605 } 606 PetscCall(VecRestoreArrayWrite(P, &x)); 607 } 608 609 dm->periodic.affine_to_local = scatter; 610 dm->periodic.affine = P; 611 PetscCall(DMGlobalToLocalHookAdd(dm, NULL, DMCoordAddPeriodicOffsets_Private, NULL)); 612 PetscFunctionReturn(PETSC_SUCCESS); 613 } 614 615 // We'll just orient all the edges, though only periodic boundary edges need orientation 616 static PetscErrorCode DMPlexOrientPositiveEdges_Private(DM dm) 617 { 618 PetscInt dim, eStart, eEnd; 619 620 PetscFunctionBegin; 621 PetscCall(DMGetDimension(dm, &dim)); 622 if (dim < 3) PetscFunctionReturn(PETSC_SUCCESS); // not necessary 623 PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd)); 624 for (PetscInt e = eStart; e < eEnd; e++) { 625 const PetscInt *cone; 626 PetscCall(DMPlexGetCone(dm, e, &cone)); 627 if (cone[0] > cone[1]) PetscCall(DMPlexOrientPoint(dm, e, -1)); 628 } 629 PetscFunctionReturn(PETSC_SUCCESS); 630 } 631 632 PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate) 633 { 634 PetscInt eextent[3] = {1, 1, 1}, vextent[3] = {1, 1, 1}; 635 const Ijk closure_1[] = { 636 {0, 0, 0}, 637 {1, 0, 0}, 638 }; 639 const Ijk closure_2[] = { 640 {0, 0, 0}, 641 {1, 0, 0}, 642 {1, 1, 0}, 643 {0, 1, 0}, 644 }; 645 const Ijk closure_3[] = { 646 {0, 0, 0}, 647 {0, 1, 0}, 648 {1, 1, 0}, 649 {1, 0, 0}, 650 {0, 0, 1}, 651 {1, 0, 1}, 652 {1, 1, 1}, 653 {0, 1, 1}, 654 }; 655 const Ijk *const closure_dim[] = {NULL, closure_1, closure_2, closure_3}; 656 // This must be kept consistent with DMPlexCreateCubeMesh_Internal 657 const PetscInt face_marker_1[] = {1, 2}; 658 const PetscInt face_marker_2[] = {4, 2, 1, 3}; 659 const PetscInt face_marker_3[] = {6, 5, 3, 4, 1, 2}; 660 const PetscInt *const face_marker_dim[] = {NULL, face_marker_1, face_marker_2, face_marker_3}; 661 // Orient faces so the normal is in the positive axis and the first vertex is the one closest to zero. 662 // These orientations can be determined by examining cones of a reference quad and hex element. 663 const PetscInt face_orient_1[] = {0, 0}; 664 const PetscInt face_orient_2[] = {-1, 0, 0, -1}; 665 const PetscInt face_orient_3[] = {-2, 0, -2, 1, -2, 0}; 666 const PetscInt *const face_orient_dim[] = {NULL, face_orient_1, face_orient_2, face_orient_3}; 667 668 PetscFunctionBegin; 669 PetscAssertPointer(dm, 1); 670 PetscValidLogicalCollectiveInt(dm, dim, 2); 671 PetscCall(DMSetDimension(dm, dim)); 672 PetscMPIInt rank, size; 673 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size)); 674 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank)); 675 for (PetscInt i = 0; i < dim; i++) { 676 eextent[i] = faces[i]; 677 vextent[i] = faces[i] + 1; 678 } 679 ZLayout layout; 680 PetscCall(ZLayoutCreate(size, eextent, vextent, &layout)); 681 PetscZSet vset; // set of all vertices in the closure of the owned elements 682 PetscCall(PetscZSetCreate(&vset)); 683 PetscInt local_elems = 0; 684 for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 685 Ijk loc = ZCodeSplit(z); 686 if (IjkActive(layout.vextent, loc)) PetscCall(PetscZSetAdd(vset, z)); 687 else { 688 z += ZStepOct(z); 689 continue; 690 } 691 if (IjkActive(layout.eextent, loc)) { 692 local_elems++; 693 // Add all neighboring vertices to set 694 for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 695 Ijk inc = closure_dim[dim][n]; 696 Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 697 ZCode v = ZEncode(nloc); 698 PetscCall(PetscZSetAdd(vset, v)); 699 } 700 } 701 } 702 PetscInt local_verts, off = 0; 703 ZCode *vert_z; 704 PetscCall(PetscZSetGetSize(vset, &local_verts)); 705 PetscCall(PetscMalloc1(local_verts, &vert_z)); 706 PetscCall(PetscZSetGetElems(vset, &off, vert_z)); 707 PetscCall(PetscZSetDestroy(&vset)); 708 // ZCode is unsigned for bitwise convenience, but highest bit should never be set, so can interpret as signed 709 PetscCall(PetscSortInt64(local_verts, (PetscInt64 *)vert_z)); 710 711 PetscCall(DMPlexSetChart(dm, 0, local_elems + local_verts)); 712 for (PetscInt e = 0; e < local_elems; e++) PetscCall(DMPlexSetConeSize(dm, e, PetscPowInt(2, dim))); 713 PetscCall(DMSetUp(dm)); 714 { 715 PetscInt e = 0; 716 for (ZCode z = layout.zstarts[rank]; z < layout.zstarts[rank + 1]; z++) { 717 Ijk loc = ZCodeSplit(z); 718 if (!IjkActive(layout.eextent, loc)) { 719 z += ZStepOct(z); 720 continue; 721 } 722 PetscInt cone[8], orient[8] = {0}; 723 for (PetscInt n = 0; n < PetscPowInt(2, dim); n++) { 724 Ijk inc = closure_dim[dim][n]; 725 Ijk nloc = {loc.i + inc.i, loc.j + inc.j, loc.k + inc.k}; 726 ZCode v = ZEncode(nloc); 727 PetscInt ci = ZCodeFind(v, local_verts, vert_z); 728 PetscAssert(ci >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find neighbor vertex in set"); 729 cone[n] = local_elems + ci; 730 } 731 PetscCall(DMPlexSetCone(dm, e, cone)); 732 PetscCall(DMPlexSetConeOrientation(dm, e, orient)); 733 e++; 734 } 735 } 736 737 PetscCall(DMPlexSymmetrize(dm)); 738 PetscCall(DMPlexStratify(dm)); 739 740 { // Create point SF 741 PetscSF sf; 742 PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm), &sf)); 743 PetscInt owned_verts = ZCodeFind(layout.zstarts[rank + 1], local_verts, vert_z); 744 if (owned_verts < 0) owned_verts = -(owned_verts + 1); // We don't care whether the key was found 745 PetscInt num_ghosts = local_verts - owned_verts; // Due to sorting, owned vertices always come first 746 PetscInt *local_ghosts; 747 PetscSFNode *ghosts; 748 PetscCall(PetscMalloc1(num_ghosts, &local_ghosts)); 749 PetscCall(PetscMalloc1(num_ghosts, &ghosts)); 750 for (PetscInt i = 0; i < num_ghosts;) { 751 ZCode z = vert_z[owned_verts + i]; 752 PetscInt remote_rank = ZCodeFind(z, size + 1, layout.zstarts), remote_count = 0; 753 if (remote_rank < 0) remote_rank = -(remote_rank + 1) - 1; 754 // We have a new remote rank; find all the ghost indices (which are contiguous in vert_z) 755 756 // Count the elements on remote_rank 757 PetscInt remote_elem = ZLayoutElementsOnRank(&layout, remote_rank); 758 759 // Traverse vertices and make ghost links 760 for (ZCode rz = layout.zstarts[remote_rank]; rz < layout.zstarts[remote_rank + 1]; rz++) { 761 Ijk loc = ZCodeSplit(rz); 762 if (rz == z) { 763 local_ghosts[i] = local_elems + owned_verts + i; 764 ghosts[i].rank = remote_rank; 765 ghosts[i].index = remote_elem + remote_count; 766 i++; 767 if (i == num_ghosts) break; 768 z = vert_z[owned_verts + i]; 769 } 770 if (IjkActive(layout.vextent, loc)) remote_count++; 771 else rz += ZStepOct(rz); 772 } 773 } 774 PetscCall(PetscSFSetGraph(sf, local_elems + local_verts, num_ghosts, local_ghosts, PETSC_OWN_POINTER, ghosts, PETSC_OWN_POINTER)); 775 PetscCall(PetscObjectSetName((PetscObject)sf, "SFC Point SF")); 776 PetscCall(DMSetPointSF(dm, sf)); 777 PetscCall(PetscSFDestroy(&sf)); 778 } 779 { 780 Vec coordinates; 781 PetscScalar *coords; 782 PetscSection coord_section; 783 PetscInt coord_size; 784 PetscCall(DMGetCoordinateSection(dm, &coord_section)); 785 PetscCall(PetscSectionSetNumFields(coord_section, 1)); 786 PetscCall(PetscSectionSetFieldComponents(coord_section, 0, dim)); 787 PetscCall(PetscSectionSetChart(coord_section, local_elems, local_elems + local_verts)); 788 for (PetscInt v = 0; v < local_verts; v++) { 789 PetscInt point = local_elems + v; 790 PetscCall(PetscSectionSetDof(coord_section, point, dim)); 791 PetscCall(PetscSectionSetFieldDof(coord_section, point, 0, dim)); 792 } 793 PetscCall(PetscSectionSetUp(coord_section)); 794 PetscCall(PetscSectionGetStorageSize(coord_section, &coord_size)); 795 PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates)); 796 PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates")); 797 PetscCall(VecSetSizes(coordinates, coord_size, PETSC_DETERMINE)); 798 PetscCall(VecSetBlockSize(coordinates, dim)); 799 PetscCall(VecSetType(coordinates, VECSTANDARD)); 800 PetscCall(VecGetArray(coordinates, &coords)); 801 for (PetscInt v = 0; v < local_verts; v++) { 802 Ijk loc = ZCodeSplit(vert_z[v]); 803 coords[v * dim + 0] = lower[0] + loc.i * (upper[0] - lower[0]) / layout.eextent.i; 804 if (dim > 1) coords[v * dim + 1] = lower[1] + loc.j * (upper[1] - lower[1]) / layout.eextent.j; 805 if (dim > 2) coords[v * dim + 2] = lower[2] + loc.k * (upper[2] - lower[2]) / layout.eextent.k; 806 } 807 PetscCall(VecRestoreArray(coordinates, &coords)); 808 PetscCall(DMSetCoordinatesLocal(dm, coordinates)); 809 PetscCall(VecDestroy(&coordinates)); 810 } 811 if (interpolate) { 812 PetscCall(DMPlexInterpolateInPlace_Internal(dm)); 813 // It's currently necessary to orient the donor and periodic edges consistently. An easy way to ensure that is ot 814 // give all edges positive orientation. Since vertices are created in Z-order, all ranks will agree about the 815 // ordering cone[0] < cone[1]. This is overkill and it would be nice to remove this preparation and make 816 // DMPlexCreateIsoperiodicClosureSF_Private() more resilient, so it fixes any inconsistent orientations. That might 817 // be needed in a general CGNS reader, for example. 818 PetscCall(DMPlexOrientPositiveEdges_Private(dm)); 819 820 DMLabel label; 821 PetscCall(DMCreateLabel(dm, "Face Sets")); 822 PetscCall(DMGetLabel(dm, "Face Sets", &label)); 823 PetscSegBuffer per_faces, donor_face_closure, my_donor_faces; 824 PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &per_faces)); 825 PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 64, &my_donor_faces)); 826 PetscCall(PetscSegBufferCreate(sizeof(ZCode), 64 * PetscPowInt(2, dim), &donor_face_closure)); 827 PetscInt fStart, fEnd, vStart, vEnd; 828 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 829 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 830 for (PetscInt f = fStart; f < fEnd; f++) { 831 PetscInt npoints, *points = NULL, num_fverts = 0, fverts[8]; 832 PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 833 PetscInt bc_count[6] = {0}; 834 for (PetscInt i = 0; i < npoints; i++) { 835 PetscInt p = points[2 * i]; 836 if (p < vStart || vEnd <= p) continue; 837 fverts[num_fverts++] = p; 838 Ijk loc = ZCodeSplit(vert_z[p - vStart]); 839 // Convention here matches DMPlexCreateCubeMesh_Internal 840 bc_count[0] += loc.i == 0; 841 bc_count[1] += loc.i == layout.vextent.i - 1; 842 bc_count[2] += loc.j == 0; 843 bc_count[3] += loc.j == layout.vextent.j - 1; 844 bc_count[4] += loc.k == 0; 845 bc_count[5] += loc.k == layout.vextent.k - 1; 846 } 847 PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &npoints, &points)); 848 for (PetscInt bc = 0, bc_match = 0; bc < 2 * dim; bc++) { 849 if (bc_count[bc] == PetscPowInt(2, dim - 1)) { 850 PetscCall(DMPlexOrientPoint(dm, f, face_orient_dim[dim][bc])); 851 if (periodicity[bc / 2] == DM_BOUNDARY_PERIODIC) { 852 PetscInt *put; 853 if (bc % 2 == 0) { // donor face; no label 854 PetscCall(PetscSegBufferGet(my_donor_faces, 1, &put)); 855 *put = f; 856 } else { // periodic face 857 PetscCall(PetscSegBufferGet(per_faces, 1, &put)); 858 *put = f; 859 ZCode *zput; 860 PetscCall(PetscSegBufferGet(donor_face_closure, num_fverts, &zput)); 861 for (PetscInt i = 0; i < num_fverts; i++) { 862 Ijk loc = ZCodeSplit(vert_z[fverts[i] - vStart]); 863 switch (bc / 2) { 864 case 0: 865 loc.i = 0; 866 break; 867 case 1: 868 loc.j = 0; 869 break; 870 case 2: 871 loc.k = 0; 872 break; 873 } 874 *zput++ = ZEncode(loc); 875 } 876 } 877 continue; 878 } 879 PetscAssert(bc_match == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Face matches multiple face sets"); 880 PetscCall(DMLabelSetValue(label, f, face_marker_dim[dim][bc])); 881 bc_match++; 882 } 883 } 884 } 885 // Ensure that the Coordinate DM has our new boundary labels 886 DM cdm; 887 PetscCall(DMGetCoordinateDM(dm, &cdm)); 888 PetscCall(DMCopyLabels(dm, cdm, PETSC_COPY_VALUES, PETSC_FALSE, DM_COPY_LABELS_FAIL)); 889 if (periodicity[0] == DM_BOUNDARY_PERIODIC || (dim > 1 && periodicity[1] == DM_BOUNDARY_PERIODIC) || (dim > 2 && periodicity[2] == DM_BOUNDARY_PERIODIC)) { 890 PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Periodicity_Private(dm, &layout, vert_z, per_faces, lower, upper, periodicity, donor_face_closure, my_donor_faces)); 891 } 892 PetscCall(PetscSegBufferDestroy(&per_faces)); 893 PetscCall(PetscSegBufferDestroy(&donor_face_closure)); 894 PetscCall(PetscSegBufferDestroy(&my_donor_faces)); 895 } 896 PetscCall(PetscFree(layout.zstarts)); 897 PetscCall(PetscFree(vert_z)); 898 PetscFunctionReturn(PETSC_SUCCESS); 899 } 900 901 /*@ 902 DMPlexSetIsoperiodicFaceSF - Express periodicity from an existing mesh 903 904 Logically Collective 905 906 Input Parameters: 907 + dm - The `DMPLEX` on which to set periodicity 908 - face_sf - `PetscSF` in which roots are (owned) donor faces and leaves are faces that must be matched to a (possibly remote) donor face. 909 910 Level: advanced 911 912 Note: 913 One can use `-dm_plex_shape zbox` to use this mode of periodicity, wherein the periodic points are distinct both globally 914 and locally, but are paired when creating a global dof space. 915 916 .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexGetIsoperiodicFaceSF()` 917 @*/ 918 PetscErrorCode DMPlexSetIsoperiodicFaceSF(DM dm, PetscSF face_sf) 919 { 920 DM_Plex *plex = (DM_Plex *)dm->data; 921 922 PetscFunctionBegin; 923 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 924 PetscCall(PetscObjectReference((PetscObject)face_sf)); 925 PetscCall(PetscSFDestroy(&plex->periodic.face_sf)); 926 plex->periodic.face_sf = face_sf; 927 if (face_sf) PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", DMGetIsoperiodicPointSF_Plex)); 928 929 DM cdm = dm->coordinates[0].dm; // Can't DMGetCoordinateDM because it automatically creates one 930 if (cdm) { 931 PetscCall(DMPlexSetIsoperiodicFaceSF(cdm, face_sf)); 932 if (face_sf) cdm->periodic.setup = DMPeriodicCoordinateSetUp_Internal; 933 } 934 PetscFunctionReturn(PETSC_SUCCESS); 935 } 936 937 /*@ 938 DMPlexGetIsoperiodicFaceSF - Obtain periodicity for a mesh 939 940 Logically Collective 941 942 Input Parameter: 943 . dm - The `DMPLEX` for which to obtain periodic relation 944 945 Output Parameter: 946 . face_sf - `PetscSF` in which roots are (owned) donor faces and leaves are faces that must be matched to a (possibly remote) donor face. 947 948 Level: advanced 949 950 .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()` 951 @*/ 952 PetscErrorCode DMPlexGetIsoperiodicFaceSF(DM dm, PetscSF *face_sf) 953 { 954 DM_Plex *plex = (DM_Plex *)dm->data; 955 956 PetscFunctionBegin; 957 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 958 *face_sf = plex->periodic.face_sf; 959 PetscFunctionReturn(PETSC_SUCCESS); 960 } 961 962 /*@C 963 DMPlexSetIsoperiodicFaceTransform - set geometric transform from donor to periodic points 964 965 Logically Collective 966 967 Input Parameters: 968 + dm - `DMPLEX` that has been configured with `DMPlexSetIsoperiodicFaceSF()` 969 - t - 4x4 affine transformation basis. 970 971 Level: advanced 972 973 Notes: 974 Affine transforms are 4x4 matrices in which the leading 3x3 block expresses a rotation (or identity for no rotation), 975 the last column contains a translation vector, and the bottom row is all zero except the last entry, which must always 976 be 1. This representation is common in geometric modeling and allows affine transformations to be composed using 977 simple matrix multiplication. 978 979 Although the interface accepts a general affine transform, only affine translation is supported at present. 980 981 Developer Notes: 982 This interface should be replaced by making BasisTransform public, expanding it to support affine representations, and 983 adding GPU implementations to apply the G2L/L2G transforms. 984 985 .seealso: [](ch_unstructured), `DMPLEX`, `DMGetGlobalSection()`, `DMPlexSetIsoperiodicFaceSF()` 986 @*/ 987 PetscErrorCode DMPlexSetIsoperiodicFaceTransform(DM dm, const PetscScalar t[]) 988 { 989 DM_Plex *plex = (DM_Plex *)dm->data; 990 991 PetscFunctionBegin; 992 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 993 for (PetscInt i = 0; i < 4; i++) { 994 for (PetscInt j = 0; j < 4; j++) { 995 PetscCheck(i != j || t[i * 4 + j] == 1., PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Rotated transforms not supported"); 996 plex->periodic.transform[i][j] = t[i * 4 + j]; 997 } 998 } 999 PetscFunctionReturn(PETSC_SUCCESS); 1000 } 1001