Actual source code: sfcoord.c

  1: #include <petsc/private/sfimpl.h>

  3: static PetscErrorCode GetBoundingBox_Internal(PetscInt npoints, PetscInt dim, const PetscReal *coords, PetscReal *bbox)
  4: {
  5:   PetscFunctionBegin;
  6:   for (PetscInt d = 0; d < dim; d++) {
  7:     bbox[0 * dim + d] = PETSC_MAX_REAL;
  8:     bbox[1 * dim + d] = PETSC_MIN_REAL;
  9:   }
 10:   for (PetscInt i = 0; i < npoints; i++) {
 11:     for (PetscInt d = 0; d < dim; d++) {
 12:       bbox[0 * dim + d] = PetscMin(bbox[0 * dim + d], coords[i * dim + d]);
 13:       bbox[1 * dim + d] = PetscMax(bbox[1 * dim + d], coords[i * dim + d]);
 14:     }
 15:   }
 16:   PetscFunctionReturn(PETSC_SUCCESS);
 17: }

 19: static PetscBool IntersectBoundingBox_Internal(PetscInt dim, const PetscReal *a, const PetscReal *b, PetscReal tol)
 20: {
 21:   for (PetscInt d = 0; d < dim; d++) {
 22:     if (a[1 * dim + d] + tol < b[0 * dim + d] || b[1 * dim + d] + tol < a[0 * dim + d]) return PETSC_FALSE;
 23:   }
 24:   return PETSC_TRUE;
 25: }

 27: static PetscBool InBoundingBox_Internal(PetscInt dim, const PetscReal *x, const PetscReal *bbox, PetscReal tol)
 28: {
 29:   for (PetscInt d = 0; d < dim; d++) {
 30:     if (x[d] + tol < bbox[0 * dim + d] || bbox[1 * dim + d] + tol < x[d]) return PETSC_FALSE;
 31:   }
 32:   return PETSC_TRUE;
 33: }

 35: /*@
 36:   PetscSFSetGraphFromCoordinates - Create SF by fuzzy matching leaf coordinates to root coordinates

 38:   Collective

 40:   Input Parameters:
 41: + sf         - PetscSF to set graph on
 42: . nroots     - number of root coordinates
 43: . nleaves    - number of leaf coordinates
 44: . dim        - spatial dimension of coordinates
 45: . tol        - positive tolerance for matching
 46: . rootcoords - array of root coordinates in which root i component d is [i*dim+d]
 47: - leafcoords - array of root coordinates in which leaf i component d is [i*dim+d]

 49:   Notes:
 50:   The tolerance typically represents the rounding error incurred by numerically computing coordinates via
 51:   possibly-different procedures. Passing anything from `PETSC_SMALL` to `100 * PETSC_MACHINE_EPSILON` is appropriate for
 52:   most use cases.

 54:   Example:
 55:   As a motivating example, consider fluid flow in the x direction with y (distance from a wall). The spanwise direction,
 56:   z, has periodic boundary conditions and needs some spanwise length to allow turbulent structures to develop. The
 57:   distribution is stationary with respect to z, so you want to average turbulence variables (like Reynolds stress) over
 58:   the z direction. It is complicated in a 3D simulation with arbitrary partitioner to uniquely number the nodes or
 59:   quadrature point coordinates to average these quantities into a 2D plane where they will be visualized, but it's easy
 60:   to compute the projection of each 3D point into the 2D plane.

 62:   Suppose a 2D target mesh and 3D source mesh (logically an extrusion of the 2D, though perhaps not created in that way)
 63:   are distributed independently on a communicator. Each rank passes its 2D target points as root coordinates and the 2D
 64:   projection of its 3D source points as leaf coordinates. Calling `PetscSFReduceBegin()`/`PetscSFReduceEnd()` on the
 65:   result will sum data from the 3D sources to the 2D targets.

 67:   As a concrete example, consider three MPI ranks with targets (roots)
 68: .vb
 69: Rank 0: (0, 0), (0, 1)
 70: Rank 1: (0.1, 0), (0.1, 1)
 71: Rank 2: (0.2, 0), (0.2, 1)
 72: .ve
 73:   Note that targets must be uniquely owned. Suppose also that we identify the following leaf coordinates (perhaps via projection from a 3D space).
 74: .vb
 75: Rank 0: (0, 0), (0.1, 0), (0, 1), (0.1, 1)
 76: Rank 1: (0, 0), (0.1, 0), (0.2, 0), (0, 1), (0.1, 1)
 77: Rank 2: (0.1, 0), (0.2, 0), (0.1, 1), (0.2, 1)
 78: .ve
 79:   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.
 80: .vb
 81: Roots by rank
 82: [0]  0:   0.0000e+00   0.0000e+00   0.0000e+00   1.0000e+00
 83: [1]  0:   1.0000e-01   0.0000e+00   1.0000e-01   1.0000e+00
 84: [2]  0:   2.0000e-01   0.0000e+00   2.0000e-01   1.0000e+00
 85: Leaves by rank
 86: [0]  0:   0.0000e+00   0.0000e+00   1.0000e-01   0.0000e+00   0.0000e+00
 87: [0]  5:   1.0000e+00   1.0000e-01   1.0000e+00
 88: [1]  0:   0.0000e+00   0.0000e+00   1.0000e-01   0.0000e+00   2.0000e-01
 89: [1]  5:   0.0000e+00   0.0000e+00   1.0000e+00   1.0000e-01   1.0000e+00
 90: [1] 10:   2.0000e-01   1.0000e+00
 91: [2]  0:   1.0000e-01   0.0000e+00   2.0000e-01   0.0000e+00   1.0000e-01
 92: [2]  5:   1.0000e+00   2.0000e-01   1.0000e+00
 93: PetscSF Object: 3 MPI processes
 94:   type: basic
 95:   [0] Number of roots=2, leaves=4, remote ranks=2
 96:   [0] 0 <- (0,0)
 97:   [0] 1 <- (1,0)
 98:   [0] 2 <- (0,1)
 99:   [0] 3 <- (1,1)
100:   [1] Number of roots=2, leaves=6, remote ranks=3
101:   [1] 0 <- (0,0)
102:   [1] 1 <- (1,0)
103:   [1] 2 <- (2,0)
104:   [1] 3 <- (0,1)
105:   [1] 4 <- (1,1)
106:   [1] 5 <- (2,1)
107:   [2] Number of roots=2, leaves=4, remote ranks=2
108:   [2] 0 <- (1,0)
109:   [2] 1 <- (2,0)
110:   [2] 2 <- (1,1)
111:   [2] 3 <- (2,1)
112: .ve

114:   Level: advanced

116: .seealso: `PetscSFCreate()`, `PetscSFSetGraph()`, `PetscSFCreateByMatchingIndices()`
117: @*/
118: PetscErrorCode PetscSFSetGraphFromCoordinates(PetscSF sf, PetscInt nroots, PetscInt nleaves, PetscInt dim, PetscReal tol, const PetscReal *rootcoords, const PetscReal *leafcoords)
119: {
120:   PetscReal    bbox[6], *bboxes, *target_coords;
121:   PetscMPIInt  size, *ranks_needed, num_ranks, msize;
122:   PetscInt    *root_sizes, *root_starts;
123:   PetscSFNode *premote, *lremote;
124:   PetscSF      psf;
125:   MPI_Datatype unit;
126:   MPI_Comm     comm;

128:   PetscFunctionBegin;
129:   PetscCall(PetscObjectGetComm((PetscObject)sf, &comm));
130:   PetscCall(GetBoundingBox_Internal(nroots, dim, rootcoords, bbox));
131:   PetscCallMPI(MPI_Comm_size(comm, &size));
132:   PetscCall(PetscMalloc1(size * 2 * dim, &bboxes));
133:   PetscCall(PetscMPIIntCast(2 * dim, &msize));
134:   PetscCallMPI(MPI_Allgather(bbox, msize, MPIU_REAL, bboxes, msize, MPIU_REAL, comm));
135:   PetscCall(GetBoundingBox_Internal(nleaves, dim, leafcoords, bbox));
136:   PetscCall(PetscMalloc1(size, &root_sizes));
137:   PetscCallMPI(MPI_Allgather(&nroots, 1, MPIU_INT, root_sizes, 1, MPIU_INT, comm));

139:   PetscCall(PetscMalloc2(size, &ranks_needed, size + 1, &root_starts));
140:   root_starts[0] = 0;
141:   num_ranks      = 0;
142:   for (PetscMPIInt r = 0; r < size; r++) {
143:     if (IntersectBoundingBox_Internal(dim, bbox, &bboxes[2 * dim * r], tol)) {
144:       ranks_needed[num_ranks++] = r;
145:       root_starts[num_ranks]    = root_starts[num_ranks - 1] + root_sizes[r];
146:     }
147:   }
148:   PetscCall(PetscFree(root_sizes));
149:   PetscCall(PetscMalloc1(root_starts[num_ranks], &premote));
150:   for (PetscInt i = 0; i < num_ranks; i++) {
151:     for (PetscInt j = root_starts[i]; j < root_starts[i + 1]; j++) {
152:       premote[j].rank  = ranks_needed[i];
153:       premote[j].index = j - root_starts[i];
154:     }
155:   }
156:   PetscCall(PetscSFCreate(comm, &psf));
157:   PetscCall(PetscSFSetGraph(psf, nroots, root_starts[num_ranks], NULL, PETSC_USE_POINTER, premote, PETSC_USE_POINTER));
158:   PetscCall(PetscMalloc1(root_starts[num_ranks] * dim, &target_coords));
159:   PetscCall(PetscMPIIntCast(dim, &msize));
160:   PetscCallMPI(MPI_Type_contiguous(msize, MPIU_REAL, &unit));
161:   PetscCallMPI(MPI_Type_commit(&unit));
162:   PetscCall(PetscSFBcastBegin(psf, unit, rootcoords, target_coords, MPI_REPLACE));
163:   PetscCall(PetscSFBcastEnd(psf, unit, rootcoords, target_coords, MPI_REPLACE));
164:   PetscCallMPI(MPI_Type_free(&unit));
165:   PetscCall(PetscSFDestroy(&psf));

167:   // Condense targets to only those that lie within our bounding box
168:   PetscInt num_targets = 0;
169:   for (PetscInt i = 0; i < root_starts[num_ranks]; i++) {
170:     if (InBoundingBox_Internal(dim, &target_coords[i * dim], bbox, tol)) {
171:       premote[num_targets] = premote[i];
172:       for (PetscInt d = 0; d < dim; d++) target_coords[num_targets * dim + d] = target_coords[i * dim + d];
173:       num_targets++;
174:     }
175:   }
176:   PetscCall(PetscFree(bboxes));
177:   PetscCall(PetscFree2(ranks_needed, root_starts));

179:   PetscCall(PetscMalloc1(nleaves, &lremote));
180:   for (PetscInt i = 0; i < nleaves; i++) {
181:     for (PetscInt j = 0; j < num_targets; j++) {
182:       PetscReal sum = 0;
183:       for (PetscInt d = 0; d < dim; d++) sum += PetscSqr(leafcoords[i * dim + d] - target_coords[j * dim + d]);
184:       if (sum < tol * tol) {
185:         lremote[i] = premote[j];
186:         goto matched;
187:       }
188:     }
189:     switch (dim) {
190:     case 1:
191:       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "No target found for leaf coordinate %g", (double)leafcoords[i * dim + 0]);
192:     case 2:
193:       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]);
194:     case 3:
195:       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]);
196:     }
197:   matched:;
198:   }
199:   PetscCall(PetscFree(premote));
200:   PetscCall(PetscFree(target_coords));
201:   PetscCall(PetscSFSetGraph(sf, nroots, nleaves, NULL, PETSC_USE_POINTER, lremote, PETSC_OWN_POINTER));
202:   PetscFunctionReturn(PETSC_SUCCESS);
203: }