18b5d2d36SJed Brown #include <petsc/private/sfimpl.h> /*I "petscsf.h" I*/ 28b5d2d36SJed Brown 38b5d2d36SJed Brown static PetscErrorCode GetBoundingBox_Internal(PetscInt npoints, PetscInt dim, const PetscReal *coords, PetscReal *bbox) 48b5d2d36SJed Brown { 58b5d2d36SJed Brown PetscFunctionBegin; 68b5d2d36SJed Brown for (PetscInt d = 0; d < dim; d++) { 78b5d2d36SJed Brown bbox[0 * dim + d] = PETSC_MAX_REAL; 88b5d2d36SJed Brown bbox[1 * dim + d] = PETSC_MIN_REAL; 98b5d2d36SJed Brown } 108b5d2d36SJed Brown for (PetscInt i = 0; i < npoints; i++) { 118b5d2d36SJed Brown for (PetscInt d = 0; d < dim; d++) { 128b5d2d36SJed Brown bbox[0 * dim + d] = PetscMin(bbox[0 * dim + d], coords[i * dim + d]); 138b5d2d36SJed Brown bbox[1 * dim + d] = PetscMax(bbox[1 * dim + d], coords[i * dim + d]); 148b5d2d36SJed Brown } 158b5d2d36SJed Brown } 163ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 178b5d2d36SJed Brown } 188b5d2d36SJed Brown 198b5d2d36SJed Brown static PetscBool IntersectBoundingBox_Internal(PetscInt dim, const PetscReal *a, const PetscReal *b, PetscReal tol) 208b5d2d36SJed Brown { 218b5d2d36SJed Brown for (PetscInt d = 0; d < dim; d++) { 228b5d2d36SJed Brown if (a[1 * dim + d] + tol < b[0 * dim + d] || b[1 * dim + d] + tol < a[0 * dim + d]) return PETSC_FALSE; 238b5d2d36SJed Brown } 248b5d2d36SJed Brown return PETSC_TRUE; 258b5d2d36SJed Brown } 268b5d2d36SJed Brown 278b5d2d36SJed Brown static PetscBool InBoundingBox_Internal(PetscInt dim, const PetscReal *x, const PetscReal *bbox, PetscReal tol) 288b5d2d36SJed Brown { 298b5d2d36SJed Brown for (PetscInt d = 0; d < dim; d++) { 308b5d2d36SJed Brown if (x[d] + tol < bbox[0 * dim + d] || bbox[1 * dim + d] + tol < x[d]) return PETSC_FALSE; 318b5d2d36SJed Brown } 328b5d2d36SJed Brown return PETSC_TRUE; 338b5d2d36SJed Brown } 348b5d2d36SJed Brown 358b5d2d36SJed Brown /*@ 368b5d2d36SJed Brown PetscSFSetGraphFromCoordinates - Create SF by fuzzy matching leaf coordinates to root coordinates 378b5d2d36SJed Brown 388b5d2d36SJed Brown Collective 398b5d2d36SJed Brown 408b5d2d36SJed Brown Input Parameters: 418b5d2d36SJed Brown + sf - PetscSF to set graph on 428b5d2d36SJed Brown . nroots - number of root coordinates 438b5d2d36SJed Brown . nleaves - number of leaf coordinates 448b5d2d36SJed Brown . dim - spatial dimension of coordinates 458b5d2d36SJed Brown . tol - positive tolerance for matching 468b5d2d36SJed Brown . rootcoords - array of root coordinates in which root i component d is [i*dim+d] 478b5d2d36SJed Brown - leafcoords - array of root coordinates in which leaf i component d is [i*dim+d] 488b5d2d36SJed Brown 498b5d2d36SJed Brown Notes: 508b5d2d36SJed Brown The tolerance typically represents the rounding error incurred by numerically computing coordinates via 518b5d2d36SJed Brown possibly-different procedures. Passing anything from `PETSC_SMALL` to `100 * PETSC_MACHINE_EPSILON` is appropriate for 528b5d2d36SJed Brown most use cases. 538b5d2d36SJed Brown 548b5d2d36SJed Brown Example: 558b5d2d36SJed Brown As a motivating example, consider fluid flow in the x direction with y (distance from a wall). The spanwise direction, 568b5d2d36SJed Brown z, has periodic boundary conditions and needs some spanwise length to allow turbulent structures to develop. The 578b5d2d36SJed Brown distribution is stationary with respect to z, so you want to average turbulence variables (like Reynolds stress) over 588b5d2d36SJed Brown the z direction. It is complicated in a 3D simulation with arbitrary partitioner to uniquely number the nodes or 598b5d2d36SJed Brown quadrature point coordinates to average these quantities into a 2D plane where they will be visualized, but it's easy 608b5d2d36SJed Brown to compute the projection of each 3D point into the 2D plane. 618b5d2d36SJed Brown 628b5d2d36SJed Brown Suppose a 2D target mesh and 3D source mesh (logically an extrusion of the 2D, though perhaps not created in that way) 638b5d2d36SJed Brown are distributed independently on a communicator. Each rank passes its 2D target points as root coordinates and the 2D 648b5d2d36SJed Brown projection of its 3D source points as leaf coordinates. Calling `PetscSFReduceBegin()`/`PetscSFReduceEnd()` on the 658b5d2d36SJed Brown result will sum data from the 3D sources to the 2D targets. 668b5d2d36SJed Brown 678b5d2d36SJed Brown As a concrete example, consider three MPI ranks with targets (roots) 688b5d2d36SJed Brown .vb 698b5d2d36SJed Brown Rank 0: (0, 0), (0, 1) 708b5d2d36SJed Brown Rank 1: (0.1, 0), (0.1, 1) 718b5d2d36SJed Brown Rank 2: (0.2, 0), (0.2, 1) 728b5d2d36SJed Brown .ve 738b5d2d36SJed Brown Note that targets must be uniquely owned. Suppose also that we identify the following leaf coordinates (perhaps via projection from a 3D space). 748b5d2d36SJed Brown .vb 758b5d2d36SJed Brown Rank 0: (0, 0), (0.1, 0), (0, 1), (0.1, 1) 768b5d2d36SJed Brown Rank 1: (0, 0), (0.1, 0), (0.2, 0), (0, 1), (0.1, 1) 778b5d2d36SJed Brown Rank 2: (0.1, 0), (0.2, 0), (0.1, 1), (0.2, 1) 788b5d2d36SJed Brown .ve 798b5d2d36SJed Brown Leaf coordinates may be repeated, both on a rank and between ranks. This example yields the following `PetscSF` capable of reducing from sources to targets. 808b5d2d36SJed Brown .vb 818b5d2d36SJed Brown Roots by rank 828b5d2d36SJed Brown [0] 0: 0.0000e+00 0.0000e+00 0.0000e+00 1.0000e+00 838b5d2d36SJed Brown [1] 0: 1.0000e-01 0.0000e+00 1.0000e-01 1.0000e+00 848b5d2d36SJed Brown [2] 0: 2.0000e-01 0.0000e+00 2.0000e-01 1.0000e+00 858b5d2d36SJed Brown Leaves by rank 868b5d2d36SJed Brown [0] 0: 0.0000e+00 0.0000e+00 1.0000e-01 0.0000e+00 0.0000e+00 878b5d2d36SJed Brown [0] 5: 1.0000e+00 1.0000e-01 1.0000e+00 888b5d2d36SJed Brown [1] 0: 0.0000e+00 0.0000e+00 1.0000e-01 0.0000e+00 2.0000e-01 898b5d2d36SJed Brown [1] 5: 0.0000e+00 0.0000e+00 1.0000e+00 1.0000e-01 1.0000e+00 908b5d2d36SJed Brown [1] 10: 2.0000e-01 1.0000e+00 918b5d2d36SJed Brown [2] 0: 1.0000e-01 0.0000e+00 2.0000e-01 0.0000e+00 1.0000e-01 928b5d2d36SJed Brown [2] 5: 1.0000e+00 2.0000e-01 1.0000e+00 938b5d2d36SJed Brown PetscSF Object: 3 MPI processes 948b5d2d36SJed Brown type: basic 958b5d2d36SJed Brown [0] Number of roots=2, leaves=4, remote ranks=2 968b5d2d36SJed Brown [0] 0 <- (0,0) 978b5d2d36SJed Brown [0] 1 <- (1,0) 988b5d2d36SJed Brown [0] 2 <- (0,1) 998b5d2d36SJed Brown [0] 3 <- (1,1) 1008b5d2d36SJed Brown [1] Number of roots=2, leaves=6, remote ranks=3 1018b5d2d36SJed Brown [1] 0 <- (0,0) 1028b5d2d36SJed Brown [1] 1 <- (1,0) 1038b5d2d36SJed Brown [1] 2 <- (2,0) 1048b5d2d36SJed Brown [1] 3 <- (0,1) 1058b5d2d36SJed Brown [1] 4 <- (1,1) 1068b5d2d36SJed Brown [1] 5 <- (2,1) 1078b5d2d36SJed Brown [2] Number of roots=2, leaves=4, remote ranks=2 1088b5d2d36SJed Brown [2] 0 <- (1,0) 1098b5d2d36SJed Brown [2] 1 <- (2,0) 1108b5d2d36SJed Brown [2] 2 <- (1,1) 1118b5d2d36SJed Brown [2] 3 <- (2,1) 1128b5d2d36SJed Brown .ve 1138b5d2d36SJed Brown 1148b5d2d36SJed Brown Level: advanced 1158b5d2d36SJed Brown 1168b5d2d36SJed Brown .seealso: `PetscSFCreate()`, `PetscSFSetGraph()`, `PetscSFCreateByMatchingIndices()` 1178b5d2d36SJed Brown @*/ 118*cf84bf9eSBarry Smith PetscErrorCode PetscSFSetGraphFromCoordinates(PetscSF sf, PetscInt nroots, PetscInt nleaves, PetscInt dim, PetscReal tol, const PetscReal rootcoords[], const PetscReal leafcoords[]) 1198b5d2d36SJed Brown { 1208b5d2d36SJed Brown PetscReal bbox[6], *bboxes, *target_coords; 121835f2295SStefano Zampini PetscMPIInt size, *ranks_needed, num_ranks, msize; 1228b5d2d36SJed Brown PetscInt *root_sizes, *root_starts; 1238b5d2d36SJed Brown PetscSFNode *premote, *lremote; 1248b5d2d36SJed Brown PetscSF psf; 1258b5d2d36SJed Brown MPI_Datatype unit; 1268b5d2d36SJed Brown MPI_Comm comm; 1278b5d2d36SJed Brown 1288b5d2d36SJed Brown PetscFunctionBegin; 1298b5d2d36SJed Brown PetscCall(PetscObjectGetComm((PetscObject)sf, &comm)); 1308b5d2d36SJed Brown PetscCall(GetBoundingBox_Internal(nroots, dim, rootcoords, bbox)); 1318b5d2d36SJed Brown PetscCallMPI(MPI_Comm_size(comm, &size)); 1328b5d2d36SJed Brown PetscCall(PetscMalloc1(size * 2 * dim, &bboxes)); 133835f2295SStefano Zampini PetscCall(PetscMPIIntCast(2 * dim, &msize)); 134835f2295SStefano Zampini PetscCallMPI(MPI_Allgather(bbox, msize, MPIU_REAL, bboxes, msize, MPIU_REAL, comm)); 1358b5d2d36SJed Brown PetscCall(GetBoundingBox_Internal(nleaves, dim, leafcoords, bbox)); 1368b5d2d36SJed Brown PetscCall(PetscMalloc1(size, &root_sizes)); 1378b5d2d36SJed Brown PetscCallMPI(MPI_Allgather(&nroots, 1, MPIU_INT, root_sizes, 1, MPIU_INT, comm)); 1388b5d2d36SJed Brown 1398b5d2d36SJed Brown PetscCall(PetscMalloc2(size, &ranks_needed, size + 1, &root_starts)); 1408b5d2d36SJed Brown root_starts[0] = 0; 1418b5d2d36SJed Brown num_ranks = 0; 1428b5d2d36SJed Brown for (PetscMPIInt r = 0; r < size; r++) { 1438b5d2d36SJed Brown if (IntersectBoundingBox_Internal(dim, bbox, &bboxes[2 * dim * r], tol)) { 1448b5d2d36SJed Brown ranks_needed[num_ranks++] = r; 1458b5d2d36SJed Brown root_starts[num_ranks] = root_starts[num_ranks - 1] + root_sizes[r]; 1468b5d2d36SJed Brown } 1478b5d2d36SJed Brown } 1488b5d2d36SJed Brown PetscCall(PetscFree(root_sizes)); 1498b5d2d36SJed Brown PetscCall(PetscMalloc1(root_starts[num_ranks], &premote)); 1508b5d2d36SJed Brown for (PetscInt i = 0; i < num_ranks; i++) { 1518b5d2d36SJed Brown for (PetscInt j = root_starts[i]; j < root_starts[i + 1]; j++) { 1528b5d2d36SJed Brown premote[j].rank = ranks_needed[i]; 1538b5d2d36SJed Brown premote[j].index = j - root_starts[i]; 1548b5d2d36SJed Brown } 1558b5d2d36SJed Brown } 1568b5d2d36SJed Brown PetscCall(PetscSFCreate(comm, &psf)); 1578b5d2d36SJed Brown PetscCall(PetscSFSetGraph(psf, nroots, root_starts[num_ranks], NULL, PETSC_USE_POINTER, premote, PETSC_USE_POINTER)); 1588b5d2d36SJed Brown PetscCall(PetscMalloc1(root_starts[num_ranks] * dim, &target_coords)); 159835f2295SStefano Zampini PetscCall(PetscMPIIntCast(dim, &msize)); 160835f2295SStefano Zampini PetscCallMPI(MPI_Type_contiguous(msize, MPIU_REAL, &unit)); 1618b5d2d36SJed Brown PetscCallMPI(MPI_Type_commit(&unit)); 1628b5d2d36SJed Brown PetscCall(PetscSFBcastBegin(psf, unit, rootcoords, target_coords, MPI_REPLACE)); 1638b5d2d36SJed Brown PetscCall(PetscSFBcastEnd(psf, unit, rootcoords, target_coords, MPI_REPLACE)); 1648b5d2d36SJed Brown PetscCallMPI(MPI_Type_free(&unit)); 1658b5d2d36SJed Brown PetscCall(PetscSFDestroy(&psf)); 1668b5d2d36SJed Brown 1678b5d2d36SJed Brown // Condense targets to only those that lie within our bounding box 1688b5d2d36SJed Brown PetscInt num_targets = 0; 1698b5d2d36SJed Brown for (PetscInt i = 0; i < root_starts[num_ranks]; i++) { 1708b5d2d36SJed Brown if (InBoundingBox_Internal(dim, &target_coords[i * dim], bbox, tol)) { 1718b5d2d36SJed Brown premote[num_targets] = premote[i]; 1728b5d2d36SJed Brown for (PetscInt d = 0; d < dim; d++) target_coords[num_targets * dim + d] = target_coords[i * dim + d]; 1738b5d2d36SJed Brown num_targets++; 1748b5d2d36SJed Brown } 1758b5d2d36SJed Brown } 1768b5d2d36SJed Brown PetscCall(PetscFree(bboxes)); 1778b5d2d36SJed Brown PetscCall(PetscFree2(ranks_needed, root_starts)); 1788b5d2d36SJed Brown 1798b5d2d36SJed Brown PetscCall(PetscMalloc1(nleaves, &lremote)); 180c0713208SJames Wright PetscKDTree tree; 181c0713208SJames Wright PetscCount *indices; 182c0713208SJames Wright PetscReal *distances; 183c0713208SJames Wright 184c0713208SJames Wright PetscCall(PetscKDTreeCreate(num_targets, dim, target_coords, PETSC_USE_POINTER, PETSC_DETERMINE, &tree)); 185c0713208SJames Wright PetscCall(PetscMalloc2(nleaves, &indices, nleaves, &distances)); 186c0713208SJames Wright PetscCall(PetscKDTreeQueryPointsNearestNeighbor(tree, nleaves, leafcoords, tol, indices, distances)); 1878b5d2d36SJed Brown for (PetscInt i = 0; i < nleaves; i++) { 188c0713208SJames Wright if (distances[i] < tol) { 189c0713208SJames Wright lremote[i] = premote[indices[i]]; 190c0713208SJames Wright } else { 1918b5d2d36SJed Brown switch (dim) { 1928b5d2d36SJed Brown case 1: 1938b5d2d36SJed Brown SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "No target found for leaf coordinate %g", (double)leafcoords[i * dim + 0]); 1948b5d2d36SJed Brown case 2: 1958b5d2d36SJed Brown SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "No target found for leaf coordinate (%g, %g)", (double)leafcoords[i * dim + 0], (double)leafcoords[i * dim + 1]); 1968b5d2d36SJed Brown case 3: 1978b5d2d36SJed Brown SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "No target found for leaf coordinate (%g, %g, %g)", (double)leafcoords[i * dim + 0], (double)leafcoords[i * dim + 1], (double)leafcoords[i * dim + 2]); 1988b5d2d36SJed Brown } 1998b5d2d36SJed Brown } 200c0713208SJames Wright } 201c0713208SJames Wright PetscCall(PetscFree2(indices, distances)); 202c0713208SJames Wright PetscCall(PetscKDTreeDestroy(&tree)); 2038b5d2d36SJed Brown PetscCall(PetscFree(premote)); 2048b5d2d36SJed Brown PetscCall(PetscFree(target_coords)); 2058b5d2d36SJed Brown PetscCall(PetscSFSetGraph(sf, nroots, nleaves, NULL, PETSC_USE_POINTER, lremote, PETSC_OWN_POINTER)); 2063ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2078b5d2d36SJed Brown } 208