18b5d2d36SJed Brown #include <petsc/private/sfimpl.h> /*I "petscsf.h" I*/
28b5d2d36SJed Brown
GetBoundingBox_Internal(PetscInt npoints,PetscInt dim,const PetscReal * coords,PetscReal * bbox)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
IntersectBoundingBox_Internal(PetscInt dim,const PetscReal * a,const PetscReal * b,PetscReal tol)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
InBoundingBox_Internal(PetscInt dim,const PetscReal * x,const PetscReal * bbox,PetscReal tol)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 @*/
PetscSFSetGraphFromCoordinates(PetscSF sf,PetscInt nroots,PetscInt nleaves,PetscInt dim,PetscReal tol,const PetscReal rootcoords[],const PetscReal leafcoords[])118cf84bf9eSBarry 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;
129*9b6401adSJames Wright PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 1);
130*9b6401adSJames Wright PetscValidLogicalCollectiveInt(sf, dim, 4);
131*9b6401adSJames Wright PetscValidLogicalCollectiveReal(sf, tol, 5);
132*9b6401adSJames Wright if (nroots != 0) PetscAssertPointer(rootcoords, 6);
133*9b6401adSJames Wright if (nleaves != 0) PetscAssertPointer(leafcoords, 7);
1348b5d2d36SJed Brown PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
1358b5d2d36SJed Brown PetscCall(GetBoundingBox_Internal(nroots, dim, rootcoords, bbox));
1368b5d2d36SJed Brown PetscCallMPI(MPI_Comm_size(comm, &size));
1378b5d2d36SJed Brown PetscCall(PetscMalloc1(size * 2 * dim, &bboxes));
138835f2295SStefano Zampini PetscCall(PetscMPIIntCast(2 * dim, &msize));
139835f2295SStefano Zampini PetscCallMPI(MPI_Allgather(bbox, msize, MPIU_REAL, bboxes, msize, MPIU_REAL, comm));
1408b5d2d36SJed Brown PetscCall(GetBoundingBox_Internal(nleaves, dim, leafcoords, bbox));
1418b5d2d36SJed Brown PetscCall(PetscMalloc1(size, &root_sizes));
1428b5d2d36SJed Brown PetscCallMPI(MPI_Allgather(&nroots, 1, MPIU_INT, root_sizes, 1, MPIU_INT, comm));
1438b5d2d36SJed Brown
1448b5d2d36SJed Brown PetscCall(PetscMalloc2(size, &ranks_needed, size + 1, &root_starts));
1458b5d2d36SJed Brown root_starts[0] = 0;
1468b5d2d36SJed Brown num_ranks = 0;
1478b5d2d36SJed Brown for (PetscMPIInt r = 0; r < size; r++) {
1488b5d2d36SJed Brown if (IntersectBoundingBox_Internal(dim, bbox, &bboxes[2 * dim * r], tol)) {
1498b5d2d36SJed Brown ranks_needed[num_ranks++] = r;
1508b5d2d36SJed Brown root_starts[num_ranks] = root_starts[num_ranks - 1] + root_sizes[r];
1518b5d2d36SJed Brown }
1528b5d2d36SJed Brown }
1538b5d2d36SJed Brown PetscCall(PetscFree(root_sizes));
1548b5d2d36SJed Brown PetscCall(PetscMalloc1(root_starts[num_ranks], &premote));
1558b5d2d36SJed Brown for (PetscInt i = 0; i < num_ranks; i++) {
1568b5d2d36SJed Brown for (PetscInt j = root_starts[i]; j < root_starts[i + 1]; j++) {
1578b5d2d36SJed Brown premote[j].rank = ranks_needed[i];
1588b5d2d36SJed Brown premote[j].index = j - root_starts[i];
1598b5d2d36SJed Brown }
1608b5d2d36SJed Brown }
1618b5d2d36SJed Brown PetscCall(PetscSFCreate(comm, &psf));
1628b5d2d36SJed Brown PetscCall(PetscSFSetGraph(psf, nroots, root_starts[num_ranks], NULL, PETSC_USE_POINTER, premote, PETSC_USE_POINTER));
1638b5d2d36SJed Brown PetscCall(PetscMalloc1(root_starts[num_ranks] * dim, &target_coords));
164835f2295SStefano Zampini PetscCall(PetscMPIIntCast(dim, &msize));
165835f2295SStefano Zampini PetscCallMPI(MPI_Type_contiguous(msize, MPIU_REAL, &unit));
1668b5d2d36SJed Brown PetscCallMPI(MPI_Type_commit(&unit));
1678b5d2d36SJed Brown PetscCall(PetscSFBcastBegin(psf, unit, rootcoords, target_coords, MPI_REPLACE));
1688b5d2d36SJed Brown PetscCall(PetscSFBcastEnd(psf, unit, rootcoords, target_coords, MPI_REPLACE));
1698b5d2d36SJed Brown PetscCallMPI(MPI_Type_free(&unit));
1708b5d2d36SJed Brown PetscCall(PetscSFDestroy(&psf));
1718b5d2d36SJed Brown
1728b5d2d36SJed Brown // Condense targets to only those that lie within our bounding box
1738b5d2d36SJed Brown PetscInt num_targets = 0;
1748b5d2d36SJed Brown for (PetscInt i = 0; i < root_starts[num_ranks]; i++) {
1758b5d2d36SJed Brown if (InBoundingBox_Internal(dim, &target_coords[i * dim], bbox, tol)) {
1768b5d2d36SJed Brown premote[num_targets] = premote[i];
1778b5d2d36SJed Brown for (PetscInt d = 0; d < dim; d++) target_coords[num_targets * dim + d] = target_coords[i * dim + d];
1788b5d2d36SJed Brown num_targets++;
1798b5d2d36SJed Brown }
1808b5d2d36SJed Brown }
1818b5d2d36SJed Brown PetscCall(PetscFree(bboxes));
1828b5d2d36SJed Brown PetscCall(PetscFree2(ranks_needed, root_starts));
1838b5d2d36SJed Brown
1848b5d2d36SJed Brown PetscCall(PetscMalloc1(nleaves, &lremote));
185c0713208SJames Wright PetscKDTree tree;
186c0713208SJames Wright PetscCount *indices;
187c0713208SJames Wright PetscReal *distances;
188c0713208SJames Wright
189c0713208SJames Wright PetscCall(PetscKDTreeCreate(num_targets, dim, target_coords, PETSC_USE_POINTER, PETSC_DETERMINE, &tree));
190c0713208SJames Wright PetscCall(PetscMalloc2(nleaves, &indices, nleaves, &distances));
191c0713208SJames Wright PetscCall(PetscKDTreeQueryPointsNearestNeighbor(tree, nleaves, leafcoords, tol, indices, distances));
1928b5d2d36SJed Brown for (PetscInt i = 0; i < nleaves; i++) {
193c0713208SJames Wright if (distances[i] < tol) {
194c0713208SJames Wright lremote[i] = premote[indices[i]];
195c0713208SJames Wright } else {
1968b5d2d36SJed Brown switch (dim) {
1978b5d2d36SJed Brown case 1:
1988b5d2d36SJed Brown SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "No target found for leaf coordinate %g", (double)leafcoords[i * dim + 0]);
1998b5d2d36SJed Brown case 2:
2008b5d2d36SJed 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]);
2018b5d2d36SJed Brown case 3:
2028b5d2d36SJed 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]);
2038b5d2d36SJed Brown }
2048b5d2d36SJed Brown }
205c0713208SJames Wright }
206c0713208SJames Wright PetscCall(PetscFree2(indices, distances));
207c0713208SJames Wright PetscCall(PetscKDTreeDestroy(&tree));
2088b5d2d36SJed Brown PetscCall(PetscFree(premote));
2098b5d2d36SJed Brown PetscCall(PetscFree(target_coords));
2108b5d2d36SJed Brown PetscCall(PetscSFSetGraph(sf, nroots, nleaves, NULL, PETSC_USE_POINTER, lremote, PETSC_OWN_POINTER));
2113ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
2128b5d2d36SJed Brown }
213