xref: /petsc/src/dm/impls/swarm/swarmpic_sort.c (revision d2522c19e8fa9bca20aaca277941d9a63e71db6a)
1 #include <petscdm.h>
2 #include <petscdmda.h>
3 #include <petscdmplex.h>
4 #include <petscdmswarm.h>
5 #include <petsc/private/dmswarmimpl.h>
6 
7 int sort_CompareSwarmPoint(const void *dataA, const void *dataB) {
8   SwarmPoint *pointA = (SwarmPoint *)dataA;
9   SwarmPoint *pointB = (SwarmPoint *)dataB;
10 
11   if (pointA->cell_index < pointB->cell_index) {
12     return -1;
13   } else if (pointA->cell_index > pointB->cell_index) {
14     return 1;
15   } else {
16     return 0;
17   }
18 }
19 
20 PetscErrorCode DMSwarmSortApplyCellIndexSort(DMSwarmSort ctx) {
21   PetscFunctionBegin;
22   qsort(ctx->list, ctx->npoints, sizeof(SwarmPoint), sort_CompareSwarmPoint);
23   PetscFunctionReturn(0);
24 }
25 
26 PetscErrorCode DMSwarmSortCreate(DMSwarmSort *_ctx) {
27   DMSwarmSort ctx;
28 
29   PetscFunctionBegin;
30   PetscCall(PetscNew(&ctx));
31   ctx->isvalid = PETSC_FALSE;
32   ctx->ncells  = 0;
33   ctx->npoints = 0;
34   PetscCall(PetscMalloc1(1, &ctx->pcell_offsets));
35   PetscCall(PetscMalloc1(1, &ctx->list));
36   *_ctx = ctx;
37   PetscFunctionReturn(0);
38 }
39 
40 PetscErrorCode DMSwarmSortSetup(DMSwarmSort ctx, DM dm, PetscInt ncells) {
41   PetscInt *swarm_cellid;
42   PetscInt  p, npoints;
43   PetscInt  tmp, c, count;
44 
45   PetscFunctionBegin;
46   if (!ctx) PetscFunctionReturn(0);
47   if (ctx->isvalid) PetscFunctionReturn(0);
48 
49   PetscCall(PetscLogEventBegin(DMSWARM_Sort, 0, 0, 0, 0));
50   /* check the number of cells */
51   if (ncells != ctx->ncells) {
52     PetscCall(PetscRealloc(sizeof(PetscInt) * (ncells + 1), &ctx->pcell_offsets));
53     ctx->ncells = ncells;
54   }
55   PetscCall(PetscArrayzero(ctx->pcell_offsets, ctx->ncells + 1));
56 
57   /* get the number of points */
58   PetscCall(DMSwarmGetLocalSize(dm, &npoints));
59   if (npoints != ctx->npoints) {
60     PetscCall(PetscRealloc(sizeof(SwarmPoint) * npoints, &ctx->list));
61     ctx->npoints = npoints;
62   }
63   PetscCall(PetscArrayzero(ctx->list, npoints));
64 
65   PetscCall(DMSwarmGetField(dm, DMSwarmPICField_cellid, NULL, NULL, (void **)&swarm_cellid));
66   for (p = 0; p < ctx->npoints; p++) {
67     ctx->list[p].point_index = p;
68     ctx->list[p].cell_index  = swarm_cellid[p];
69   }
70   PetscCall(DMSwarmRestoreField(dm, DMSwarmPICField_cellid, NULL, NULL, (void **)&swarm_cellid));
71   PetscCall(DMSwarmSortApplyCellIndexSort(ctx));
72 
73   /* sum points per cell */
74   for (p = 0; p < ctx->npoints; p++) { ctx->pcell_offsets[ctx->list[p].cell_index]++; }
75 
76   /* create offset list */
77   count = 0;
78   for (c = 0; c < ctx->ncells; c++) {
79     tmp                   = ctx->pcell_offsets[c];
80     ctx->pcell_offsets[c] = count;
81     count                 = count + tmp;
82   }
83   ctx->pcell_offsets[c] = count;
84 
85   ctx->isvalid = PETSC_TRUE;
86   PetscCall(PetscLogEventEnd(DMSWARM_Sort, 0, 0, 0, 0));
87   PetscFunctionReturn(0);
88 }
89 
90 PetscErrorCode DMSwarmSortDestroy(DMSwarmSort *_ctx) {
91   DMSwarmSort ctx;
92 
93   PetscFunctionBegin;
94   if (!_ctx) PetscFunctionReturn(0);
95   if (!*_ctx) PetscFunctionReturn(0);
96   ctx = *_ctx;
97   if (ctx->list) PetscCall(PetscFree(ctx->list));
98   if (ctx->pcell_offsets) PetscCall(PetscFree(ctx->pcell_offsets));
99   PetscCall(PetscFree(ctx));
100   *_ctx = NULL;
101   PetscFunctionReturn(0);
102 }
103 
104 /*@C
105    DMSwarmSortGetNumberOfPointsPerCell - Returns the number of points in a cell
106 
107    Not collective
108 
109    Input parameters:
110 +  dm - a DMSwarm objects
111 .  e - the index of the cell
112 -  npoints - the number of points in the cell
113 
114    Level: advanced
115 
116    Notes:
117    You must call DMSwarmSortGetAccess() before you can call DMSwarmSortGetNumberOfPointsPerCell()
118 
119 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()`, `DMSwarmSortGetPointsPerCell()`
120 @*/
121 PetscErrorCode DMSwarmSortGetNumberOfPointsPerCell(DM dm, PetscInt e, PetscInt *npoints) {
122   DM_Swarm   *swarm = (DM_Swarm *)dm->data;
123   PetscInt    points_per_cell;
124   DMSwarmSort ctx;
125 
126   PetscFunctionBegin;
127   ctx = swarm->sort_context;
128   PetscCheck(ctx, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "The DMSwarmSort context has not been created. Must call DMSwarmSortGetAccess() first");
129   PetscCheck(ctx->isvalid, PETSC_COMM_SELF, PETSC_ERR_USER, "SwarmPointSort container is not valid. Must call DMSwarmSortGetAccess() first");
130   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);
131   PetscCheck(e >= 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Cell index (%" PetscInt_FMT ") cannot be negative", e);
132   points_per_cell = ctx->pcell_offsets[e + 1] - ctx->pcell_offsets[e];
133   *npoints        = points_per_cell;
134   PetscFunctionReturn(0);
135 }
136 
137 /*@C
138    DMSwarmSortGetPointsPerCell - Creates an array of point indices for all points in a cell
139 
140    Not collective
141 
142    Input parameters:
143 +  dm - a DMSwarm object
144 .  e - the index of the cell
145 .  npoints - the number of points in the cell
146 -  pidlist - array of the indices indentifying all points in cell e
147 
148    Level: advanced
149 
150    Notes:
151      You must call DMSwarmSortGetAccess() before you can call DMSwarmSortGetPointsPerCell()
152 
153      The array pidlist is internally created and must be free'd by the user
154 
155 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()`, `DMSwarmSortGetNumberOfPointsPerCell()`
156 @*/
157 PETSC_EXTERN PetscErrorCode DMSwarmSortGetPointsPerCell(DM dm, PetscInt e, PetscInt *npoints, PetscInt **pidlist) {
158   DM_Swarm   *swarm = (DM_Swarm *)dm->data;
159   PetscInt    points_per_cell;
160   PetscInt    p, pid, pid_unsorted;
161   PetscInt   *plist;
162   DMSwarmSort ctx;
163 
164   PetscFunctionBegin;
165   ctx = swarm->sort_context;
166   PetscCheck(ctx, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "The DMSwarmSort context has not been created. Must call DMSwarmSortGetAccess() first");
167   PetscCall(DMSwarmSortGetNumberOfPointsPerCell(dm, e, &points_per_cell));
168   PetscCall(PetscMalloc1(points_per_cell, &plist));
169   for (p = 0; p < points_per_cell; p++) {
170     pid          = ctx->pcell_offsets[e] + p;
171     pid_unsorted = ctx->list[pid].point_index;
172     plist[p]     = pid_unsorted;
173   }
174   *npoints = points_per_cell;
175   *pidlist = plist;
176 
177   PetscFunctionReturn(0);
178 }
179 
180 /*@C
181    DMSwarmSortGetAccess - Setups up a DMSwarm point sort context for efficient traversal of points within a cell
182 
183    Not collective
184 
185    Input parameter:
186 .  dm - a DMSwarm object
187 
188    Calling DMSwarmSortGetAccess() creates a list which enables easy identification of all points contained in a
189    given cell. This method does not explicitly sort the data within the DMSwarm based on the cell index associated
190    with a DMSwarm point.
191 
192    The sort context is valid only for the DMSwarm points defined at the time when DMSwarmSortGetAccess() was called.
193    For example, suppose the swarm contained NP points when DMSwarmSortGetAccess() was called. If the user subsequently
194    adds 10 additional points to the swarm, the sort context is still valid, but only for the first NP points.
195    The indices associated with the 10 new additional points will not be contained within the sort context.
196    This means that the user can still safely perform queries via DMSwarmSortGetPointsPerCell() and
197    DMSwarmSortGetPointsPerCell(), however the results return will be based on the first NP points.
198 
199    If any DMSwam re-sizing method is called after DMSwarmSortGetAccess() which modifies any of the first NP entries
200    in the DMSwarm, the sort context will become invalid. Currently there are no guards to prevent the user from
201    invalidating the sort context. For this reason, we highly recommend you do not use DMSwarmRemovePointAtIndex() in
202    between calls to DMSwarmSortGetAccess() and DMSwarmSortRestoreAccess().
203 
204    To facilitate safe removal of points using the sort context, we suggest a "two pass" strategy in which the
205    first pass "marks" points for removal, and the second pass actually removes the points from the DMSwarm.
206 
207    Notes:
208      You must call DMSwarmSortGetAccess() before you can call DMSwarmSortGetPointsPerCell() or DMSwarmSortGetNumberOfPointsPerCell()
209 
210      The sort context may become invalid if any re-sizing methods are applied which alter the first NP points
211      within swarm at the time DMSwarmSortGetAccess() was called.
212 
213      You must call DMSwarmSortRestoreAccess() when you no longer need access to the sort context
214 
215    Level: advanced
216 
217 .seealso: `DMSwarmSetType()`, `DMSwarmSortRestoreAccess()`
218 @*/
219 PETSC_EXTERN PetscErrorCode DMSwarmSortGetAccess(DM dm) {
220   DM_Swarm *swarm = (DM_Swarm *)dm->data;
221   PetscInt  ncells;
222   DM        celldm;
223   PetscBool isda, isplex, isshell;
224 
225   PetscFunctionBegin;
226   if (!swarm->sort_context) { PetscCall(DMSwarmSortCreate(&swarm->sort_context)); }
227 
228   /* get the number of cells */
229   PetscCall(DMSwarmGetCellDM(dm, &celldm));
230   PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMDA, &isda));
231   PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMPLEX, &isplex));
232   PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMSHELL, &isshell));
233   ncells = 0;
234   if (isda) {
235     PetscInt        nel, npe;
236     const PetscInt *element;
237 
238     PetscCall(DMDAGetElements(celldm, &nel, &npe, &element));
239     ncells = nel;
240     PetscCall(DMDARestoreElements(celldm, &nel, &npe, &element));
241   } else if (isplex) {
242     PetscInt ps, pe;
243 
244     PetscCall(DMPlexGetHeightStratum(celldm, 0, &ps, &pe));
245     ncells = pe - ps;
246   } else if (isshell) {
247     PetscErrorCode (*method_DMShellGetNumberOfCells)(DM, PetscInt *);
248 
249     PetscCall(PetscObjectQueryFunction((PetscObject)celldm, "DMGetNumberOfCells_C", &method_DMShellGetNumberOfCells));
250     if (method_DMShellGetNumberOfCells) {
251       PetscCall(method_DMShellGetNumberOfCells(celldm, &ncells));
252     } else
253       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);");
254   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot determine the number of cells for a DM not of type DA, PLEX or SHELL");
255 
256   /* setup */
257   PetscCall(DMSwarmSortSetup(swarm->sort_context, dm, ncells));
258   PetscFunctionReturn(0);
259 }
260 
261 /*@C
262    DMSwarmSortRestoreAccess - Invalidates the DMSwarm point sorting context
263 
264    Not collective
265 
266    Input parameter:
267 .  dm - a DMSwarm object
268 
269    Level: advanced
270 
271    Note:
272    You must call DMSwarmSortGetAccess() before calling DMSwarmSortRestoreAccess()
273 
274 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()`
275 @*/
276 PETSC_EXTERN PetscErrorCode DMSwarmSortRestoreAccess(DM dm) {
277   DM_Swarm *swarm = (DM_Swarm *)dm->data;
278 
279   PetscFunctionBegin;
280   if (!swarm->sort_context) PetscFunctionReturn(0);
281   PetscCheck(swarm->sort_context->isvalid, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "You must call DMSwarmSortGetAccess() before calling DMSwarmSortRestoreAccess()");
282   swarm->sort_context->isvalid = PETSC_FALSE;
283   PetscFunctionReturn(0);
284 }
285 
286 /*@C
287    DMSwarmSortGetIsValid - Gets the isvalid flag associated with a DMSwarm point sorting context
288 
289    Not collective
290 
291    Input parameter:
292 .  dm - a DMSwarm object
293 
294    Output parameter:
295 .  isvalid - flag indicating whether the sort context is up-to-date
296 
297  Level: advanced
298 
299 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()`
300 @*/
301 PETSC_EXTERN PetscErrorCode DMSwarmSortGetIsValid(DM dm, PetscBool *isvalid) {
302   DM_Swarm *swarm = (DM_Swarm *)dm->data;
303 
304   PetscFunctionBegin;
305   if (!swarm->sort_context) {
306     *isvalid = PETSC_FALSE;
307     PetscFunctionReturn(0);
308   }
309   *isvalid = swarm->sort_context->isvalid;
310   PetscFunctionReturn(0);
311 }
312 
313 /*@C
314    DMSwarmSortGetSizes - Gets the sizes associated with a DMSwarm point sorting context
315 
316    Not collective
317 
318    Input parameter:
319 .  dm - a DMSwarm object
320 
321    Output parameters:
322 +  ncells - number of cells within the sort context (pass NULL to ignore)
323 -  npoints - number of points used to create the sort context (pass NULL to ignore)
324 
325    Level: advanced
326 
327 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()`
328 @*/
329 PETSC_EXTERN PetscErrorCode DMSwarmSortGetSizes(DM dm, PetscInt *ncells, PetscInt *npoints) {
330   DM_Swarm *swarm = (DM_Swarm *)dm->data;
331 
332   PetscFunctionBegin;
333   if (!swarm->sort_context) {
334     if (ncells) *ncells = 0;
335     if (npoints) *npoints = 0;
336     PetscFunctionReturn(0);
337   }
338   if (ncells) *ncells = swarm->sort_context->ncells;
339   if (npoints) *npoints = swarm->sort_context->npoints;
340   PetscFunctionReturn(0);
341 }
342