Actual source code: swarmpic_sort.c

  1: #include "petscdm.h"
  2: #include "petscsystypes.h"
  3: #include <petscdmda.h>
  4: #include <petscdmplex.h>
  5: #include <petsc/private/dmswarmimpl.h>

  7: static int sort_CompareSwarmPoint(const void *dataA, const void *dataB)
  8: {
  9:   SwarmPoint *pointA = (SwarmPoint *)dataA;
 10:   SwarmPoint *pointB = (SwarmPoint *)dataB;

 12:   if (pointA->cell_index < pointB->cell_index) {
 13:     return -1;
 14:   } else if (pointA->cell_index > pointB->cell_index) {
 15:     return 1;
 16:   } else {
 17:     return 0;
 18:   }
 19: }

 21: static PetscErrorCode DMSwarmSortApplyCellIndexSort(DMSwarmSort ctx)
 22: {
 23:   PetscFunctionBegin;
 24:   if (ctx->list) qsort(ctx->list, ctx->npoints, sizeof(SwarmPoint), sort_CompareSwarmPoint);
 25:   PetscFunctionReturn(PETSC_SUCCESS);
 26: }

 28: static PetscErrorCode DMSwarmSortCreate(DMSwarmSort *_ctx)
 29: {
 30:   DMSwarmSort ctx;

 32:   PetscFunctionBegin;
 33:   PetscCall(PetscNew(&ctx));
 34:   ctx->isvalid = PETSC_FALSE;
 35:   ctx->ncells  = 0;
 36:   ctx->npoints = 0;
 37:   PetscCall(PetscMalloc1(1, &ctx->pcell_offsets));
 38:   PetscCall(PetscMalloc1(1, &ctx->list));
 39:   *_ctx = ctx;
 40:   PetscFunctionReturn(PETSC_SUCCESS);
 41: }

 43: static PetscErrorCode DMSwarmSortSetup(DMSwarmSort ctx, DM dm, PetscInt ncells)
 44: {
 45:   PetscInt *swarm_cellid;
 46:   PetscInt  p, npoints;
 47:   PetscInt  tmp, c, count;

 49:   PetscFunctionBegin;
 50:   if (!ctx) PetscFunctionReturn(PETSC_SUCCESS);
 51:   if (ctx->isvalid) PetscFunctionReturn(PETSC_SUCCESS);

 53:   PetscCall(PetscLogEventBegin(DMSWARM_Sort, 0, 0, 0, 0));
 54:   /* check the number of cells */
 55:   if (ncells != ctx->ncells) {
 56:     PetscCall(PetscRealloc(sizeof(PetscInt) * (ncells + 1), &ctx->pcell_offsets));
 57:     ctx->ncells = ncells;
 58:   }
 59:   PetscCall(PetscArrayzero(ctx->pcell_offsets, ctx->ncells + 1));

 61:   /* get the number of points */
 62:   PetscCall(DMSwarmGetLocalSize(dm, &npoints));
 63:   if (npoints != ctx->npoints) {
 64:     PetscCall(PetscRealloc(sizeof(SwarmPoint) * npoints, &ctx->list));
 65:     ctx->npoints = npoints;
 66:   }
 67:   PetscCall(PetscArrayzero(ctx->list, npoints));

 69:   PetscCall(DMSwarmGetField(dm, DMSwarmPICField_cellid, NULL, NULL, (void **)&swarm_cellid));
 70:   for (p = 0; p < ctx->npoints; p++) {
 71:     ctx->list[p].point_index = p;
 72:     ctx->list[p].cell_index  = swarm_cellid[p];
 73:   }
 74:   PetscCall(DMSwarmRestoreField(dm, DMSwarmPICField_cellid, NULL, NULL, (void **)&swarm_cellid));
 75:   PetscCall(DMSwarmSortApplyCellIndexSort(ctx));

 77:   /* sum points per cell */
 78:   for (p = 0; p < ctx->npoints; p++) ctx->pcell_offsets[ctx->list[p].cell_index]++;

 80:   /* create offset list */
 81:   count = 0;
 82:   for (c = 0; c < ctx->ncells; c++) {
 83:     tmp                   = ctx->pcell_offsets[c];
 84:     ctx->pcell_offsets[c] = count;
 85:     count                 = count + tmp;
 86:   }
 87:   ctx->pcell_offsets[c] = count;

 89:   ctx->isvalid = PETSC_TRUE;
 90:   PetscCall(PetscLogEventEnd(DMSWARM_Sort, 0, 0, 0, 0));
 91:   PetscFunctionReturn(PETSC_SUCCESS);
 92: }

 94: PetscErrorCode DMSwarmSortDestroy(DMSwarmSort *_ctx)
 95: {
 96:   DMSwarmSort ctx;

 98:   PetscFunctionBegin;
 99:   if (!_ctx) PetscFunctionReturn(PETSC_SUCCESS);
100:   if (!*_ctx) PetscFunctionReturn(PETSC_SUCCESS);
101:   ctx = *_ctx;
102:   if (ctx->list) PetscCall(PetscFree(ctx->list));
103:   if (ctx->pcell_offsets) PetscCall(PetscFree(ctx->pcell_offsets));
104:   PetscCall(PetscFree(ctx));
105:   *_ctx = NULL;
106:   PetscFunctionReturn(PETSC_SUCCESS);
107: }

109: /*@
110:   DMSwarmSortGetNumberOfPointsPerCell - Returns the number of points in a cell

112:   Not Collective

114:   Input Parameters:
115: + dm - a `DMSWARM` objects
116: - e  - the index of the cell

118:   Output Parameter:
119: . npoints - the number of points in the cell

121:   Level: advanced

123:   Notes:
124:   You must call `DMSwarmSortGetAccess()` before you can call `DMSwarmSortGetNumberOfPointsPerCell()`

126: .seealso: `DMSWARM`, `DMSwarmSetType()`, `DMSwarmSortGetAccess()`, `DMSwarmSortGetPointsPerCell()`
127: @*/
128: PetscErrorCode DMSwarmSortGetNumberOfPointsPerCell(DM dm, PetscInt e, PetscInt *npoints)
129: {
130:   DM_Swarm   *swarm = (DM_Swarm *)dm->data;
131:   PetscInt    points_per_cell;
132:   DMSwarmSort ctx;

134:   PetscFunctionBegin;
135:   ctx = swarm->sort_context;
136:   PetscCheck(ctx, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "The DMSwarmSort context has not been created. Must call DMSwarmSortGetAccess() first");
137:   PetscCheck(ctx->isvalid, PETSC_COMM_SELF, PETSC_ERR_USER, "SwarmPointSort container is not valid. Must call DMSwarmSortGetAccess() first");
138:   PetscCheck(e < ctx->ncells, PETSC_COMM_SELF, PETSC_ERR_USER, "Cell index (%" PetscInt_FMT ") is greater than max number of local cells (%" PetscInt_FMT ")", e, ctx->ncells);
139:   PetscCheck(e >= 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Cell index (%" PetscInt_FMT ") cannot be negative", e);
140:   points_per_cell = ctx->pcell_offsets[e + 1] - ctx->pcell_offsets[e];
141:   *npoints        = points_per_cell;
142:   PetscFunctionReturn(PETSC_SUCCESS);
143: }

145: /*@C
146:   DMSwarmSortGetPointsPerCell - Creates an array of point indices for all points in a cell

148:   Not Collective

150:   Input Parameters:
151: + dm      - a `DMSWARM` object
152: . e       - the index of the cell
153: . npoints - the number of points in the cell
154: - pidlist - array of the indices identifying all points in cell e

156:   Level: advanced

158:   Note:
159:   You must call `DMSwarmSortGetAccess()` before you can call `DMSwarmSortGetPointsPerCell()`, and call `DMSwarmRestorePointsPerCell()` afterwards

161: .seealso: `DMSWARM`, `DMSwarmSetType()`, `DMSwarmRestorePointsPerCell()`, `DMSwarmSortGetAccess()`, `DMSwarmSortGetNumberOfPointsPerCell()`
162: @*/
163: PetscErrorCode DMSwarmSortGetPointsPerCell(DM dm, PetscInt e, PetscInt *npoints, PetscInt **pidlist)
164: {
165:   DM_Swarm   *swarm = (DM_Swarm *)dm->data;
166:   PetscInt    p, pid, pid_unsorted;
167:   DMSwarmSort ctx;

169:   PetscFunctionBegin;
170:   ctx = swarm->sort_context;
171:   PetscCheck(ctx, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "The DMSwarmSort context has not been created. Must call DMSwarmSortGetAccess() first");
172:   PetscCall(DMSwarmSortGetNumberOfPointsPerCell(dm, e, npoints));
173:   PetscCall(DMGetWorkArray(dm, *npoints, MPIU_SCALAR, pidlist));
174:   for (p = 0; p < *npoints; p++) {
175:     pid           = ctx->pcell_offsets[e] + p;
176:     pid_unsorted  = ctx->list[pid].point_index;
177:     (*pidlist)[p] = pid_unsorted;
178:   }
179:   PetscFunctionReturn(PETSC_SUCCESS);
180: }

182: /*@C
183:   DMSwarmSortRestorePointsPerCell - Restores an array of point indices for all points in a cell

185:   Not Collective

187:   Input Parameters:
188: + dm      - a `DMSWARM` object
189: . e       - the index of the cell
190: . npoints - the number of points in the cell
191: - pidlist - array of the indices identifying all points in cell e

193:   Level: advanced

195:   Note:
196:   You must call `DMSwarmSortGetAccess()` and `DMSwarmSortGetPointsPerCell()` before you can call `DMSwarmSortRestorePointsPerCell()`

198: .seealso: `DMSWARM`, `DMSwarmSetType()`, `DMSwarmSortGetPointsPerCell()`, `DMSwarmSortGetAccess()`, `DMSwarmSortGetNumberOfPointsPerCell()`
199: @*/
200: PetscErrorCode DMSwarmSortRestorePointsPerCell(DM dm, PetscInt e, PetscInt *npoints, PetscInt **pidlist)
201: {
202:   PetscFunctionBegin;
203:   PetscCall(DMRestoreWorkArray(dm, *npoints, MPIU_SCALAR, pidlist));
204:   PetscFunctionReturn(PETSC_SUCCESS);
205: }

207: /*@
208:   DMSwarmSortGetAccess - Setups up a `DMSWARM` point sort context for efficient traversal of points within a cell

210:   Not Collective

212:   Input Parameter:
213: . dm - a `DMSWARM` object

215:   Level: advanced

217:   Notes:
218:   Calling `DMSwarmSortGetAccess()` creates a list which enables easy identification of all points contained in a
219:   given cell. This method does not explicitly sort the data within the `DMSWARM` based on the cell index associated
220:   with a `DMSWARM` point.

222:   The sort context is valid only for the `DMSWARM` points defined at the time when `DMSwarmSortGetAccess()` was called.
223:   For example, suppose the swarm contained NP points when `DMSwarmSortGetAccess()` was called. If the user subsequently
224:   adds 10 additional points to the swarm, the sort context is still valid, but only for the first NP points.
225:   The indices associated with the 10 new additional points will not be contained within the sort context.
226:   This means that the user can still safely perform queries via `DMSwarmSortGetPointsPerCell()` and
227:   `DMSwarmSortGetPointsPerCell()`, however the results return will be based on the first NP points.

229:   If any` DMSWARM` re-sizing method is called after `DMSwarmSortGetAccess()` which modifies any of the first NP entries
230:   in the `DMSWARM`, the sort context will become invalid. Currently there are no guards to prevent the user from
231:   invalidating the sort context. For this reason, we highly recommend you do not use `DMSwarmRemovePointAtIndex()` in
232:   between calls to `DMSwarmSortGetAccess()` and `DMSwarmSortRestoreAccess()`.

234:   To facilitate safe removal of points using the sort context, we suggest a "two pass" strategy in which the
235:   first pass "marks" points for removal, and the second pass actually removes the points from the `DMSWARM`

237:   You must call `DMSwarmSortGetAccess()` before you can call `DMSwarmSortGetPointsPerCell()` or `DMSwarmSortGetNumberOfPointsPerCell()`

239:   The sort context may become invalid if any re-sizing methods are applied which alter the first NP points
240:   within swarm at the time `DMSwarmSortGetAccess()` was called.

242:   You must call `DMSwarmSortRestoreAccess()` when you no longer need access to the sort context

244: .seealso: `DMSWARM`, `DMSwarmSetType()`, `DMSwarmSortRestoreAccess()`
245: @*/
246: PetscErrorCode DMSwarmSortGetAccess(DM dm)
247: {
248:   DM_Swarm *swarm = (DM_Swarm *)dm->data;
249:   PetscInt  ncells;
250:   DM        celldm;
251:   PetscBool isda, isplex, isshell;

253:   PetscFunctionBegin;
254:   if (!swarm->sort_context) PetscCall(DMSwarmSortCreate(&swarm->sort_context));

256:   /* get the number of cells */
257:   PetscCall(DMSwarmGetCellDM(dm, &celldm));
258:   PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMDA, &isda));
259:   PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMPLEX, &isplex));
260:   PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMSHELL, &isshell));
261:   ncells = 0;
262:   if (isda) {
263:     PetscInt        nel, npe;
264:     const PetscInt *element;

266:     PetscCall(DMDAGetElements(celldm, &nel, &npe, &element));
267:     ncells = nel;
268:     PetscCall(DMDARestoreElements(celldm, &nel, &npe, &element));
269:   } else if (isplex) {
270:     PetscInt ps, pe;

272:     PetscCall(DMPlexGetHeightStratum(celldm, 0, &ps, &pe));
273:     ncells = pe - ps;
274:   } else if (isshell) {
275:     PetscErrorCode (*method_DMShellGetNumberOfCells)(DM, PetscInt *);

277:     PetscCall(PetscObjectQueryFunction((PetscObject)celldm, "DMGetNumberOfCells_C", &method_DMShellGetNumberOfCells));
278:     if (method_DMShellGetNumberOfCells) {
279:       PetscCall(method_DMShellGetNumberOfCells(celldm, &ncells));
280:     } else
281:       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot determine the number of cells for the DMSHELL object. User must provide a method via PetscObjectComposeFunction( (PetscObject)shelldm, \"DMGetNumberOfCells_C\", your_function_to_compute_number_of_cells);");
282:   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot determine the number of cells for a DM not of type DA, PLEX or SHELL");

284:   /* setup */
285:   PetscCall(DMSwarmSortSetup(swarm->sort_context, dm, ncells));
286:   PetscFunctionReturn(PETSC_SUCCESS);
287: }

289: /*@
290:   DMSwarmSortRestoreAccess - Invalidates the `DMSWARM` point sorting context previously computed with `DMSwarmSortGetAccess()`

292:   Not Collective

294:   Input Parameter:
295: . dm - a `DMSWARM` object

297:   Level: advanced

299:   Note:
300:   You must call `DMSwarmSortGetAccess()` before calling `DMSwarmSortRestoreAccess()`

302: .seealso: `DMSWARM`, `DMSwarmSetType()`, `DMSwarmSortGetAccess()`
303: @*/
304: PetscErrorCode DMSwarmSortRestoreAccess(DM dm)
305: {
306:   DM_Swarm *swarm = (DM_Swarm *)dm->data;

308:   PetscFunctionBegin;
309:   if (!swarm->sort_context) PetscFunctionReturn(PETSC_SUCCESS);
310:   PetscCheck(swarm->sort_context->isvalid, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "You must call DMSwarmSortGetAccess() before calling DMSwarmSortRestoreAccess()");
311:   swarm->sort_context->isvalid = PETSC_FALSE;
312:   PetscFunctionReturn(PETSC_SUCCESS);
313: }

315: /*@
316:   DMSwarmSortGetIsValid - Gets the isvalid flag associated with a `DMSWARM` point sorting context

318:   Not Collective

320:   Input Parameter:
321: . dm - a `DMSWARM` object

323:   Output Parameter:
324: . isvalid - flag indicating whether the sort context is up-to-date

326:   Level: advanced

328: .seealso: `DMSWARM`, `DMSwarmSetType()`, `DMSwarmSortGetAccess()`
329: @*/
330: PetscErrorCode DMSwarmSortGetIsValid(DM dm, PetscBool *isvalid)
331: {
332:   DM_Swarm *swarm = (DM_Swarm *)dm->data;

334:   PetscFunctionBegin;
335:   if (!swarm->sort_context) {
336:     *isvalid = PETSC_FALSE;
337:     PetscFunctionReturn(PETSC_SUCCESS);
338:   }
339:   *isvalid = swarm->sort_context->isvalid;
340:   PetscFunctionReturn(PETSC_SUCCESS);
341: }

343: /*@
344:   DMSwarmSortGetSizes - Gets the sizes associated with a `DMSWARM` point sorting context

346:   Not Collective

348:   Input Parameter:
349: . dm - a `DMSWARM` object

351:   Output Parameters:
352: + ncells  - number of cells within the sort context (pass `NULL` to ignore)
353: - npoints - number of points used to create the sort context (pass `NULL` to ignore)

355:   Level: advanced

357: .seealso: `DMSWARM`, `DMSwarmSetType()`, `DMSwarmSortGetAccess()`
358: @*/
359: PetscErrorCode DMSwarmSortGetSizes(DM dm, PetscInt *ncells, PetscInt *npoints)
360: {
361:   DM_Swarm *swarm = (DM_Swarm *)dm->data;

363:   PetscFunctionBegin;
364:   if (!swarm->sort_context) {
365:     if (ncells) *ncells = 0;
366:     if (npoints) *npoints = 0;
367:     PetscFunctionReturn(PETSC_SUCCESS);
368:   }
369:   if (ncells) *ncells = swarm->sort_context->ncells;
370:   if (npoints) *npoints = swarm->sort_context->npoints;
371:   PetscFunctionReturn(PETSC_SUCCESS);
372: }