xref: /petsc/src/dm/impls/swarm/swarm_migrate.c (revision e91c04dfc8a52dee1965211bb1cc8e5bf775178f)
1 #include <petscsf.h>
2 #include <petscdmswarm.h>
3 #include <petscdmda.h>
4 #include <petsc/private/dmswarmimpl.h> /*I   "petscdmswarm.h"   I*/
5 #include "../src/dm/impls/swarm/data_bucket.h"
6 #include "../src/dm/impls/swarm/data_ex.h"
7 
8 /*
9  User loads desired location (MPI rank) into field DMSwarm_rank
10 
11  It should be storing the rank information as MPIInt not Int
12 */
13 PetscErrorCode DMSwarmMigrate_Push_Basic(DM dm, PetscBool remove_sent_points)
14 {
15   DM_Swarm     *swarm = (DM_Swarm *)dm->data;
16   DMSwarmDataEx de;
17   PetscInt      p, npoints, *rankval, n_points_recv;
18   PetscMPIInt   rank, nrank;
19   void         *point_buffer, *recv_points;
20   size_t        sizeof_dmswarm_point;
21   PetscBool     debug = PETSC_FALSE;
22 
23   PetscFunctionBegin;
24   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
25 
26   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
27   PetscCall(DMSwarmGetField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
28   PetscCall(DMSwarmDataExCreate(PetscObjectComm((PetscObject)dm), 0, &de));
29   PetscCall(DMSwarmDataExTopologyInitialize(de));
30   for (p = 0; p < npoints; ++p) {
31     PetscCall(PetscMPIIntCast(rankval[p], &nrank));
32     if (nrank != rank) PetscCall(DMSwarmDataExTopologyAddNeighbour(de, nrank));
33   }
34   PetscCall(DMSwarmDataExTopologyFinalize(de));
35   PetscCall(DMSwarmDataExInitializeSendCount(de));
36   for (p = 0; p < npoints; p++) {
37     PetscCall(PetscMPIIntCast(rankval[p], &nrank));
38     if (nrank != rank) PetscCall(DMSwarmDataExAddToSendCount(de, nrank, 1));
39   }
40   PetscCall(DMSwarmDataExFinalizeSendCount(de));
41   PetscCall(DMSwarmDataBucketCreatePackedArray(swarm->db, &sizeof_dmswarm_point, &point_buffer));
42   PetscCall(DMSwarmDataExPackInitialize(de, sizeof_dmswarm_point));
43   for (p = 0; p < npoints; p++) {
44     PetscCall(PetscMPIIntCast(rankval[p], &nrank));
45     if (nrank != rank) {
46       /* copy point into buffer */
47       PetscCall(DMSwarmDataBucketFillPackedArray(swarm->db, p, point_buffer));
48       /* insert point buffer into DMSwarmDataExchanger */
49       PetscCall(DMSwarmDataExPackData(de, nrank, 1, point_buffer));
50     }
51   }
52   PetscCall(DMSwarmDataExPackFinalize(de));
53   PetscCall(DMSwarmRestoreField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
54 
55   if (remove_sent_points) {
56     DMSwarmDataField gfield;
57 
58     PetscCall(DMSwarmDataBucketGetDMSwarmDataFieldByName(swarm->db, DMSwarmField_rank, &gfield));
59     PetscCall(DMSwarmDataFieldGetAccess(gfield));
60     PetscCall(DMSwarmDataFieldGetEntries(gfield, (void **)&rankval));
61 
62     /* remove points which left processor */
63     PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
64     for (p = 0; p < npoints; p++) {
65       PetscCall(PetscMPIIntCast(rankval[p], &nrank));
66       if (nrank != rank) {
67         /* kill point */
68         PetscCall(DMSwarmDataFieldRestoreAccess(gfield));
69 
70         PetscCall(DMSwarmDataBucketRemovePointAtIndex(swarm->db, p));
71 
72         PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL)); /* you need to update npoints as the list size decreases! */
73         PetscCall(DMSwarmDataFieldGetAccess(gfield));
74         PetscCall(DMSwarmDataFieldGetEntries(gfield, (void **)&rankval));
75         p--; /* check replacement point */
76       }
77     }
78     PetscCall(DMSwarmDataFieldRestoreEntries(gfield, (void **)&rankval));
79     PetscCall(DMSwarmDataFieldRestoreAccess(gfield));
80   }
81   PetscCall(DMSwarmDataExBegin(de));
82   PetscCall(DMSwarmDataExEnd(de));
83   PetscCall(DMSwarmDataExGetRecvData(de, &n_points_recv, &recv_points));
84   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
85   PetscCall(DMSwarmDataBucketSetSizes(swarm->db, npoints + n_points_recv, DMSWARM_DATA_BUCKET_BUFFER_DEFAULT));
86   for (p = 0; p < n_points_recv; p++) {
87     void *data_p = (void *)((char *)recv_points + p * sizeof_dmswarm_point);
88 
89     PetscCall(DMSwarmDataBucketInsertPackedArray(swarm->db, npoints + p, data_p));
90   }
91   if (debug) PetscCall(DMSwarmDataExView(de));
92   PetscCall(DMSwarmDataBucketDestroyPackedArray(swarm->db, &point_buffer));
93   PetscCall(DMSwarmDataExDestroy(de));
94   PetscFunctionReturn(PETSC_SUCCESS);
95 }
96 
97 static PetscErrorCode DMSwarmMigrate_DMNeighborScatter(DM dm, DM dmcell, PetscBool remove_sent_points, PetscInt *npoints_prior_migration)
98 {
99   DM_Swarm          *swarm = (DM_Swarm *)dm->data;
100   DMSwarmCellDM      celldm;
101   DMSwarmDataEx      de;
102   PetscInt           r, p, npoints, *p_cellid, n_points_recv;
103   PetscMPIInt        rank, _rank;
104   const PetscMPIInt *neighbourranks;
105   void              *point_buffer, *recv_points;
106   size_t             sizeof_dmswarm_point;
107   PetscInt           nneighbors;
108   PetscMPIInt        mynneigh, *myneigh;
109   const char        *cellid;
110 
111   PetscFunctionBegin;
112   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
113   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
114   PetscCall(DMSwarmGetCellDMActive(dm, &celldm));
115   PetscCall(DMSwarmCellDMGetCellID(celldm, &cellid));
116   PetscCall(DMSwarmGetField(dm, cellid, NULL, NULL, (void **)&p_cellid));
117   PetscCall(DMSwarmDataExCreate(PetscObjectComm((PetscObject)dm), 0, &de));
118   PetscCall(DMGetNeighbors(dmcell, &nneighbors, &neighbourranks));
119   PetscCall(DMSwarmDataExTopologyInitialize(de));
120   for (r = 0; r < nneighbors; r++) {
121     _rank = neighbourranks[r];
122     if ((_rank != rank) && (_rank > 0)) PetscCall(DMSwarmDataExTopologyAddNeighbour(de, _rank));
123   }
124   PetscCall(DMSwarmDataExTopologyFinalize(de));
125   PetscCall(DMSwarmDataExTopologyGetNeighbours(de, &mynneigh, &myneigh));
126   PetscCall(DMSwarmDataExInitializeSendCount(de));
127   for (p = 0; p < npoints; p++) {
128     if (p_cellid[p] == DMLOCATEPOINT_POINT_NOT_FOUND) {
129       for (r = 0; r < mynneigh; r++) {
130         _rank = myneigh[r];
131         PetscCall(DMSwarmDataExAddToSendCount(de, _rank, 1));
132       }
133     }
134   }
135   PetscCall(DMSwarmDataExFinalizeSendCount(de));
136   PetscCall(DMSwarmDataBucketCreatePackedArray(swarm->db, &sizeof_dmswarm_point, &point_buffer));
137   PetscCall(DMSwarmDataExPackInitialize(de, sizeof_dmswarm_point));
138   for (p = 0; p < npoints; p++) {
139     if (p_cellid[p] == DMLOCATEPOINT_POINT_NOT_FOUND) {
140       for (r = 0; r < mynneigh; r++) {
141         _rank = myneigh[r];
142         /* copy point into buffer */
143         PetscCall(DMSwarmDataBucketFillPackedArray(swarm->db, p, point_buffer));
144         /* insert point buffer into DMSwarmDataExchanger */
145         PetscCall(DMSwarmDataExPackData(de, _rank, 1, point_buffer));
146       }
147     }
148   }
149   PetscCall(DMSwarmDataExPackFinalize(de));
150   PetscCall(DMSwarmRestoreField(dm, cellid, NULL, NULL, (void **)&p_cellid));
151   if (remove_sent_points) {
152     DMSwarmDataField PField;
153 
154     PetscCall(DMSwarmDataBucketGetDMSwarmDataFieldByName(swarm->db, cellid, &PField));
155     PetscCall(DMSwarmDataFieldGetEntries(PField, (void **)&p_cellid));
156     /* remove points which left processor */
157     PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
158     for (p = 0; p < npoints; p++) {
159       if (p_cellid[p] == DMLOCATEPOINT_POINT_NOT_FOUND) {
160         /* kill point */
161         PetscCall(DMSwarmDataBucketRemovePointAtIndex(swarm->db, p));
162         PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL)); /* you need to update npoints as the list size decreases! */
163         PetscCall(DMSwarmDataFieldGetEntries(PField, (void **)&p_cellid));     /* update date point increase realloc performed */
164         p--;                                                                   /* check replacement point */
165       }
166     }
167   }
168   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, npoints_prior_migration, NULL, NULL));
169   PetscCall(DMSwarmDataExBegin(de));
170   PetscCall(DMSwarmDataExEnd(de));
171   PetscCall(DMSwarmDataExGetRecvData(de, &n_points_recv, &recv_points));
172   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
173   PetscCall(DMSwarmDataBucketSetSizes(swarm->db, npoints + n_points_recv, DMSWARM_DATA_BUCKET_BUFFER_DEFAULT));
174   for (p = 0; p < n_points_recv; p++) {
175     void *data_p = (void *)((char *)recv_points + p * sizeof_dmswarm_point);
176 
177     PetscCall(DMSwarmDataBucketInsertPackedArray(swarm->db, npoints + p, data_p));
178   }
179   PetscCall(DMSwarmDataBucketDestroyPackedArray(swarm->db, &point_buffer));
180   PetscCall(DMSwarmDataExDestroy(de));
181   PetscFunctionReturn(PETSC_SUCCESS);
182 }
183 
184 PetscErrorCode DMSwarmMigrate_CellDMScatter(DM dm, PetscBool remove_sent_points)
185 {
186   DM_Swarm          *swarm = (DM_Swarm *)dm->data;
187   DMSwarmCellDM      celldm;
188   PetscInt           p, npoints, npointsg = 0, npoints2, npoints2g, *rankval, *p_cellid, npoints_prior_migration, Nfc;
189   PetscSF            sfcell = NULL;
190   const PetscSFNode *LA_sfcell;
191   DM                 dmcell;
192   Vec                pos;
193   PetscBool          error_check = swarm->migrate_error_on_missing_point;
194   const char       **coordFields;
195   PetscMPIInt        size, rank;
196   const char        *cellid;
197 
198   PetscFunctionBegin;
199   PetscCall(DMSwarmGetCellDM(dm, &dmcell));
200   PetscCheck(dmcell, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Only valid if cell DM provided");
201   PetscCall(DMSwarmGetCellDMActive(dm, &celldm));
202   PetscCall(DMSwarmCellDMGetCellID(celldm, &cellid));
203 
204   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
205   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
206 
207 #if 1
208   {
209     PetscInt     npoints_curr, range = 0;
210     PetscSFNode *sf_cells;
211 
212     PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints_curr, NULL, NULL));
213     PetscCall(PetscMalloc1(npoints_curr, &sf_cells));
214 
215     PetscCall(DMSwarmGetField(dm, cellid, NULL, NULL, (void **)&p_cellid));
216     for (p = 0; p < npoints_curr; p++) {
217       sf_cells[p].rank  = 0;
218       sf_cells[p].index = p_cellid[p];
219       if (p_cellid[p] > range) range = p_cellid[p];
220     }
221     PetscCall(DMSwarmRestoreField(dm, cellid, NULL, NULL, (void **)&p_cellid));
222 
223     /* PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)dm),&sfcell)); */
224     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sfcell));
225     PetscCall(PetscSFSetGraph(sfcell, range, npoints_curr, NULL, PETSC_OWN_POINTER, sf_cells, PETSC_OWN_POINTER));
226   }
227 #endif
228 
229   PetscCall(DMSwarmCellDMGetCoordinateFields(celldm, &Nfc, &coordFields));
230   PetscCall(DMSwarmCreateLocalVectorFromFields(dm, Nfc, coordFields, &pos));
231   PetscCall(DMLocatePoints(dmcell, pos, DM_POINTLOCATION_NONE, &sfcell));
232   PetscCall(DMSwarmDestroyLocalVectorFromFields(dm, Nfc, coordFields, &pos));
233 
234   if (error_check) PetscCall(DMSwarmGetSize(dm, &npointsg));
235   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
236   PetscCall(DMSwarmGetField(dm, cellid, NULL, NULL, (void **)&p_cellid));
237   PetscCall(DMSwarmGetField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
238   PetscCall(PetscSFGetGraph(sfcell, NULL, NULL, NULL, &LA_sfcell));
239 
240   for (p = 0; p < npoints; p++) {
241     p_cellid[p] = LA_sfcell[p].index;
242     rankval[p]  = rank;
243   }
244   PetscCall(DMSwarmRestoreField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
245   PetscCall(DMSwarmRestoreField(dm, cellid, NULL, NULL, (void **)&p_cellid));
246   PetscCall(PetscSFDestroy(&sfcell));
247 
248   if (size > 1) {
249     PetscCall(DMSwarmMigrate_DMNeighborScatter(dm, dmcell, remove_sent_points, &npoints_prior_migration));
250   } else {
251     DMSwarmDataField PField;
252     PetscInt         npoints_curr;
253 
254     /* remove points which the domain */
255     PetscCall(DMSwarmDataBucketGetDMSwarmDataFieldByName(swarm->db, cellid, &PField));
256     PetscCall(DMSwarmDataFieldGetEntries(PField, (void **)&p_cellid));
257 
258     PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints_curr, NULL, NULL));
259     for (p = 0; p < npoints_curr; p++) {
260       if (p_cellid[p] == DMLOCATEPOINT_POINT_NOT_FOUND) {
261         /* kill point */
262         PetscCall(DMSwarmDataBucketRemovePointAtIndex(swarm->db, p));
263         PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints_curr, NULL, NULL)); /* you need to update npoints as the list size decreases! */
264         PetscCall(DMSwarmDataFieldGetEntries(PField, (void **)&p_cellid));          /* update date point in case realloc performed */
265         p--;                                                                        /* check replacement point */
266       }
267     }
268     PetscCall(DMSwarmGetLocalSize(dm, &npoints_prior_migration));
269   }
270 
271   /* locate points newly received */
272   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints2, NULL, NULL));
273 
274 #if 0
275   { /* safe alternative - however this performs two point locations on: (i) the initial points set and; (ii) the (initial + received) point set */
276     PetscScalar *LA_coor;
277     PetscInt bs;
278     DMSwarmDataField PField;
279 
280     PetscCall(DMSwarmGetField(dm,coordname,&bs,NULL,(void**)&LA_coor));
281     PetscCall(VecCreateSeqWithArray(PETSC_COMM_SELF,bs,bs*npoints2,(const PetscScalar*)LA_coor,&pos));
282     PetscCall(DMLocatePoints(dmcell,pos,DM_POINTLOCATION_NONE,&sfcell));
283 
284     PetscCall(VecDestroy(&pos));
285     PetscCall(DMSwarmRestoreField(dm,coordname,&bs,NULL,(void**)&LA_coor));
286 
287     PetscCall(PetscSFGetGraph(sfcell, NULL, NULL, NULL, &LA_sfcell));
288     PetscCall(DMSwarmGetField(dm,DMSwarmField_rank,NULL,NULL,(void**)&rankval));
289     for (p=0; p<npoints2; p++) {
290       rankval[p] = LA_sfcell[p].index;
291     }
292     PetscCall(PetscSFDestroy(&sfcell));
293     PetscCall(DMSwarmRestoreField(dm,DMSwarmField_rank,NULL,NULL,(void**)&rankval));
294 
295     /* remove points which left processor */
296     PetscCall(DMSwarmDataBucketGetDMSwarmDataFieldByName(swarm->db,DMSwarmField_rank,&PField));
297     PetscCall(DMSwarmDataFieldGetEntries(PField,(void**)&rankval));
298 
299     PetscCall(DMSwarmDataBucketGetSizes(swarm->db,&npoints2,NULL,NULL));
300     for (p=0; p<npoints2; p++) {
301       if (rankval[p] == DMLOCATEPOINT_POINT_NOT_FOUND) {
302         /* kill point */
303         PetscCall(DMSwarmDataBucketRemovePointAtIndex(swarm->db,p));
304         PetscCall(DMSwarmDataBucketGetSizes(swarm->db,&npoints2,NULL,NULL)); /* you need to update npoints as the list size decreases! */
305         PetscCall(DMSwarmDataFieldGetEntries(PField,(void**)&rankval)); /* update date point increase realloc performed */
306         p--; /* check replacement point */
307       }
308     }
309   }
310 #endif
311 
312   { /* perform two point locations: (i) on the initial points set prior to communication; and (ii) on the new (received) points */
313     Vec              npos;
314     IS               nis;
315     PetscInt         npoints_from_neighbours, bs;
316     DMSwarmDataField PField;
317 
318     npoints_from_neighbours = npoints2 - npoints_prior_migration;
319 
320     PetscCall(DMSwarmCreateLocalVectorFromFields(dm, Nfc, coordFields, &pos));
321     PetscCall(VecGetBlockSize(pos, &bs));
322     PetscCall(ISCreateStride(PETSC_COMM_SELF, npoints_from_neighbours * bs, npoints_prior_migration * bs, 1, &nis));
323     PetscCall(VecGetSubVector(pos, nis, &npos));
324     PetscCall(DMLocatePoints(dmcell, npos, DM_POINTLOCATION_NONE, &sfcell));
325     PetscCall(VecRestoreSubVector(pos, nis, &npos));
326     PetscCall(ISDestroy(&nis));
327     PetscCall(DMSwarmDestroyLocalVectorFromFields(dm, Nfc, coordFields, &pos));
328 
329     PetscCall(PetscSFGetGraph(sfcell, NULL, NULL, NULL, &LA_sfcell));
330     PetscCall(DMSwarmGetField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
331     PetscCall(DMSwarmGetField(dm, cellid, NULL, NULL, (void **)&p_cellid));
332     for (p = 0; p < npoints_from_neighbours; p++) {
333       rankval[npoints_prior_migration + p]  = rank;
334       p_cellid[npoints_prior_migration + p] = LA_sfcell[p].index;
335     }
336 
337     PetscCall(DMSwarmRestoreField(dm, cellid, NULL, NULL, (void **)&p_cellid));
338     PetscCall(DMSwarmRestoreField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
339     PetscCall(PetscSFDestroy(&sfcell));
340 
341     /* remove points which left processor */
342     PetscCall(DMSwarmDataBucketGetDMSwarmDataFieldByName(swarm->db, cellid, &PField));
343     PetscCall(DMSwarmDataFieldGetEntries(PField, (void **)&p_cellid));
344 
345     PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints2, NULL, NULL));
346     for (p = npoints_prior_migration; p < npoints2; p++) {
347       if (p_cellid[p] == DMLOCATEPOINT_POINT_NOT_FOUND) {
348         /* kill point */
349         PetscCall(DMSwarmDataBucketRemovePointAtIndex(swarm->db, p));
350         PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints2, NULL, NULL)); /* you need to update npoints as the list size decreases! */
351         PetscCall(DMSwarmDataFieldGetEntries(PField, (void **)&p_cellid));      /* update date point in case realloc performed */
352         p--;                                                                    /* check replacement point */
353       }
354     }
355   }
356 
357   /* check for error on removed points */
358   if (error_check) {
359     PetscCall(DMSwarmGetSize(dm, &npoints2g));
360     PetscCheck(npointsg == npoints2g, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "Points from the DMSwarm must remain constant during migration (initial %" PetscInt_FMT " - final %" PetscInt_FMT ")", npointsg, npoints2g);
361   }
362   PetscFunctionReturn(PETSC_SUCCESS);
363 }
364 
365 PetscErrorCode DMSwarmMigrate_CellDMExact(DM dm, PetscBool remove_sent_points)
366 {
367   PetscFunctionBegin;
368   PetscFunctionReturn(PETSC_SUCCESS);
369 }
370 
371 /*
372  Redundant as this assumes points can only be sent to a single rank
373 */
374 PetscErrorCode DMSwarmMigrate_GlobalToLocal_Basic(DM dm, PetscInt *globalsize)
375 {
376   DM_Swarm     *swarm = (DM_Swarm *)dm->data;
377   DMSwarmDataEx de;
378   PetscInt      p, npoints, *rankval, n_points_recv;
379   PetscMPIInt   rank, nrank, negrank;
380   void         *point_buffer, *recv_points;
381   size_t        sizeof_dmswarm_point;
382 
383   PetscFunctionBegin;
384   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
385   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
386   *globalsize = npoints;
387   PetscCall(DMSwarmGetField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
388   PetscCall(DMSwarmDataExCreate(PetscObjectComm((PetscObject)dm), 0, &de));
389   PetscCall(DMSwarmDataExTopologyInitialize(de));
390   for (p = 0; p < npoints; p++) {
391     PetscCall(PetscMPIIntCast(rankval[p], &negrank));
392     if (negrank < 0) {
393       nrank = -negrank - 1;
394       PetscCall(DMSwarmDataExTopologyAddNeighbour(de, nrank));
395     }
396   }
397   PetscCall(DMSwarmDataExTopologyFinalize(de));
398   PetscCall(DMSwarmDataExInitializeSendCount(de));
399   for (p = 0; p < npoints; p++) {
400     PetscCall(PetscMPIIntCast(rankval[p], &negrank));
401     if (negrank < 0) {
402       nrank = -negrank - 1;
403       PetscCall(DMSwarmDataExAddToSendCount(de, nrank, 1));
404     }
405   }
406   PetscCall(DMSwarmDataExFinalizeSendCount(de));
407   PetscCall(DMSwarmDataBucketCreatePackedArray(swarm->db, &sizeof_dmswarm_point, &point_buffer));
408   PetscCall(DMSwarmDataExPackInitialize(de, sizeof_dmswarm_point));
409   for (p = 0; p < npoints; p++) {
410     PetscCall(PetscMPIIntCast(rankval[p], &negrank));
411     if (negrank < 0) {
412       nrank      = -negrank - 1;
413       rankval[p] = nrank;
414       /* copy point into buffer */
415       PetscCall(DMSwarmDataBucketFillPackedArray(swarm->db, p, point_buffer));
416       /* insert point buffer into DMSwarmDataExchanger */
417       PetscCall(DMSwarmDataExPackData(de, nrank, 1, point_buffer));
418       rankval[p] = negrank;
419     }
420   }
421   PetscCall(DMSwarmDataExPackFinalize(de));
422   PetscCall(DMSwarmRestoreField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
423   PetscCall(DMSwarmDataExBegin(de));
424   PetscCall(DMSwarmDataExEnd(de));
425   PetscCall(DMSwarmDataExGetRecvData(de, &n_points_recv, &recv_points));
426   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
427   PetscCall(DMSwarmDataBucketSetSizes(swarm->db, npoints + n_points_recv, DMSWARM_DATA_BUCKET_BUFFER_DEFAULT));
428   for (p = 0; p < n_points_recv; p++) {
429     void *data_p = (void *)((char *)recv_points + p * sizeof_dmswarm_point);
430 
431     PetscCall(DMSwarmDataBucketInsertPackedArray(swarm->db, npoints + p, data_p));
432   }
433   PetscCall(DMSwarmDataExView(de));
434   PetscCall(DMSwarmDataBucketDestroyPackedArray(swarm->db, &point_buffer));
435   PetscCall(DMSwarmDataExDestroy(de));
436   PetscFunctionReturn(PETSC_SUCCESS);
437 }
438 
439 typedef struct {
440   PetscMPIInt owner_rank;
441   PetscReal   min[3], max[3];
442 } CollectBBox;
443 
444 PETSC_EXTERN PetscErrorCode DMSwarmCollect_DMDABoundingBox(DM dm, PetscInt *globalsize)
445 {
446   DM_Swarm          *swarm = (DM_Swarm *)dm->data;
447   DMSwarmDataEx      de;
448   PetscInt           p, pk, npoints, *rankval, n_points_recv, n_bbox_recv, dim, neighbour_cells;
449   PetscMPIInt        rank, nrank;
450   void              *point_buffer, *recv_points;
451   size_t             sizeof_dmswarm_point, sizeof_bbox_ctx;
452   PetscBool          isdmda;
453   CollectBBox       *bbox, *recv_bbox;
454   const PetscMPIInt *dmneighborranks;
455   DM                 dmcell;
456 
457   PetscFunctionBegin;
458   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
459 
460   PetscCall(DMSwarmGetCellDM(dm, &dmcell));
461   PetscCheck(dmcell, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Only valid if cell DM provided");
462   isdmda = PETSC_FALSE;
463   PetscCall(PetscObjectTypeCompare((PetscObject)dmcell, DMDA, &isdmda));
464   PetscCheck(isdmda, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Only DMDA support for CollectBoundingBox");
465 
466   PetscCall(DMGetDimension(dm, &dim));
467   sizeof_bbox_ctx = sizeof(CollectBBox);
468   PetscCall(PetscMalloc1(1, &bbox));
469   bbox->owner_rank = rank;
470 
471   /* compute the bounding box based on the overlapping / stenctil size */
472   {
473     Vec lcoor;
474 
475     PetscCall(DMGetCoordinatesLocal(dmcell, &lcoor));
476     if (dim >= 1) {
477       PetscCall(VecStrideMin(lcoor, 0, NULL, &bbox->min[0]));
478       PetscCall(VecStrideMax(lcoor, 0, NULL, &bbox->max[0]));
479     }
480     if (dim >= 2) {
481       PetscCall(VecStrideMin(lcoor, 1, NULL, &bbox->min[1]));
482       PetscCall(VecStrideMax(lcoor, 1, NULL, &bbox->max[1]));
483     }
484     if (dim == 3) {
485       PetscCall(VecStrideMin(lcoor, 2, NULL, &bbox->min[2]));
486       PetscCall(VecStrideMax(lcoor, 2, NULL, &bbox->max[2]));
487     }
488   }
489   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
490   *globalsize = npoints;
491   PetscCall(DMSwarmGetField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
492   PetscCall(DMSwarmDataExCreate(PetscObjectComm((PetscObject)dm), 0, &de));
493   /* use DMDA neighbours */
494   PetscCall(DMDAGetNeighbors(dmcell, &dmneighborranks));
495   if (dim == 1) {
496     neighbour_cells = 3;
497   } else if (dim == 2) {
498     neighbour_cells = 9;
499   } else {
500     neighbour_cells = 27;
501   }
502   PetscCall(DMSwarmDataExTopologyInitialize(de));
503   for (p = 0; p < neighbour_cells; p++) {
504     if ((dmneighborranks[p] >= 0) && (dmneighborranks[p] != rank)) PetscCall(DMSwarmDataExTopologyAddNeighbour(de, dmneighborranks[p]));
505   }
506   PetscCall(DMSwarmDataExTopologyFinalize(de));
507   PetscCall(DMSwarmDataExInitializeSendCount(de));
508   for (p = 0; p < neighbour_cells; p++) {
509     if ((dmneighborranks[p] >= 0) && (dmneighborranks[p] != rank)) PetscCall(DMSwarmDataExAddToSendCount(de, dmneighborranks[p], 1));
510   }
511   PetscCall(DMSwarmDataExFinalizeSendCount(de));
512   /* send bounding boxes */
513   PetscCall(DMSwarmDataExPackInitialize(de, sizeof_bbox_ctx));
514   for (p = 0; p < neighbour_cells; p++) {
515     nrank = dmneighborranks[p];
516     if ((nrank >= 0) && (nrank != rank)) {
517       /* insert bbox buffer into DMSwarmDataExchanger */
518       PetscCall(DMSwarmDataExPackData(de, nrank, 1, bbox));
519     }
520   }
521   PetscCall(DMSwarmDataExPackFinalize(de));
522   /* recv bounding boxes */
523   PetscCall(DMSwarmDataExBegin(de));
524   PetscCall(DMSwarmDataExEnd(de));
525   PetscCall(DMSwarmDataExGetRecvData(de, &n_bbox_recv, (void **)&recv_bbox));
526   /*  Wrong, should not be using PETSC_COMM_WORLD */
527   for (p = 0; p < n_bbox_recv; p++) {
528     PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[rank %d]: box from %d : range[%+1.4e,%+1.4e]x[%+1.4e,%+1.4e]\n", rank, recv_bbox[p].owner_rank, (double)recv_bbox[p].min[0], (double)recv_bbox[p].max[0], (double)recv_bbox[p].min[1],
529                                       (double)recv_bbox[p].max[1]));
530   }
531   PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, stdout));
532   /* of course this is stupid as this "generic" function should have a better way to know what the coordinates are called */
533   PetscCall(DMSwarmDataExInitializeSendCount(de));
534   for (pk = 0; pk < n_bbox_recv; pk++) {
535     PetscReal *array_x, *array_y;
536 
537     PetscCall(DMSwarmGetField(dm, "coorx", NULL, NULL, (void **)&array_x));
538     PetscCall(DMSwarmGetField(dm, "coory", NULL, NULL, (void **)&array_y));
539     for (p = 0; p < npoints; p++) {
540       if ((array_x[p] >= recv_bbox[pk].min[0]) && (array_x[p] <= recv_bbox[pk].max[0])) {
541         if ((array_y[p] >= recv_bbox[pk].min[1]) && (array_y[p] <= recv_bbox[pk].max[1])) PetscCall(DMSwarmDataExAddToSendCount(de, recv_bbox[pk].owner_rank, 1));
542       }
543     }
544     PetscCall(DMSwarmRestoreField(dm, "coory", NULL, NULL, (void **)&array_y));
545     PetscCall(DMSwarmRestoreField(dm, "coorx", NULL, NULL, (void **)&array_x));
546   }
547   PetscCall(DMSwarmDataExFinalizeSendCount(de));
548   PetscCall(DMSwarmDataBucketCreatePackedArray(swarm->db, &sizeof_dmswarm_point, &point_buffer));
549   PetscCall(DMSwarmDataExPackInitialize(de, sizeof_dmswarm_point));
550   for (pk = 0; pk < n_bbox_recv; pk++) {
551     PetscReal *array_x, *array_y;
552 
553     PetscCall(DMSwarmGetField(dm, "coorx", NULL, NULL, (void **)&array_x));
554     PetscCall(DMSwarmGetField(dm, "coory", NULL, NULL, (void **)&array_y));
555     for (p = 0; p < npoints; p++) {
556       if ((array_x[p] >= recv_bbox[pk].min[0]) && (array_x[p] <= recv_bbox[pk].max[0])) {
557         if ((array_y[p] >= recv_bbox[pk].min[1]) && (array_y[p] <= recv_bbox[pk].max[1])) {
558           /* copy point into buffer */
559           PetscCall(DMSwarmDataBucketFillPackedArray(swarm->db, p, point_buffer));
560           /* insert point buffer into DMSwarmDataExchanger */
561           PetscCall(DMSwarmDataExPackData(de, recv_bbox[pk].owner_rank, 1, point_buffer));
562         }
563       }
564     }
565     PetscCall(DMSwarmRestoreField(dm, "coory", NULL, NULL, (void **)&array_y));
566     PetscCall(DMSwarmRestoreField(dm, "coorx", NULL, NULL, (void **)&array_x));
567   }
568   PetscCall(DMSwarmDataExPackFinalize(de));
569   PetscCall(DMSwarmRestoreField(dm, DMSwarmField_rank, NULL, NULL, (void **)&rankval));
570   PetscCall(DMSwarmDataExBegin(de));
571   PetscCall(DMSwarmDataExEnd(de));
572   PetscCall(DMSwarmDataExGetRecvData(de, &n_points_recv, &recv_points));
573   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
574   PetscCall(DMSwarmDataBucketSetSizes(swarm->db, npoints + n_points_recv, DMSWARM_DATA_BUCKET_BUFFER_DEFAULT));
575   for (p = 0; p < n_points_recv; p++) {
576     void *data_p = (void *)((char *)recv_points + p * sizeof_dmswarm_point);
577 
578     PetscCall(DMSwarmDataBucketInsertPackedArray(swarm->db, npoints + p, data_p));
579   }
580   PetscCall(DMSwarmDataBucketDestroyPackedArray(swarm->db, &point_buffer));
581   PetscCall(PetscFree(bbox));
582   PetscCall(DMSwarmDataExView(de));
583   PetscCall(DMSwarmDataExDestroy(de));
584   PetscFunctionReturn(PETSC_SUCCESS);
585 }
586 
587 /* General collection when no order, or neighbour information is provided */
588 /*
589  User provides context and collect() method
590  Broadcast user context
591 
592  for each context / rank {
593    collect(swarm,context,n,list)
594  }
595 */
596 PETSC_EXTERN PetscErrorCode DMSwarmCollect_General(DM dm, PetscErrorCode (*collect)(DM, void *, PetscInt *, PetscInt **), size_t ctx_size, void *ctx, PetscInt *globalsize)
597 {
598   DM_Swarm     *swarm = (DM_Swarm *)dm->data;
599   DMSwarmDataEx de;
600   PetscInt      p, npoints, n_points_recv;
601   PetscMPIInt   size, rank, len;
602   void         *point_buffer, *recv_points;
603   void         *ctxlist;
604   PetscInt     *n2collect, **collectlist;
605   size_t        sizeof_dmswarm_point;
606 
607   PetscFunctionBegin;
608   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
609   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
610   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
611   *globalsize = npoints;
612   /* Broadcast user context */
613   PetscCall(PetscMPIIntCast(ctx_size, &len));
614   PetscCall(PetscMalloc(ctx_size * size, &ctxlist));
615   PetscCallMPI(MPI_Allgather(ctx, len, MPI_CHAR, ctxlist, len, MPI_CHAR, PetscObjectComm((PetscObject)dm)));
616   PetscCall(PetscMalloc1(size, &n2collect));
617   PetscCall(PetscMalloc1(size, &collectlist));
618   for (PetscMPIInt r = 0; r < size; r++) {
619     PetscInt  _n2collect;
620     PetscInt *_collectlist;
621     void     *_ctx_r;
622 
623     _n2collect   = 0;
624     _collectlist = NULL;
625     if (r != rank) { /* don't collect data from yourself */
626       _ctx_r = (void *)((char *)ctxlist + r * ctx_size);
627       PetscCall(collect(dm, _ctx_r, &_n2collect, &_collectlist));
628     }
629     n2collect[r]   = _n2collect;
630     collectlist[r] = _collectlist;
631   }
632   PetscCall(DMSwarmDataExCreate(PetscObjectComm((PetscObject)dm), 0, &de));
633   /* Define topology */
634   PetscCall(DMSwarmDataExTopologyInitialize(de));
635   for (PetscMPIInt r = 0; r < size; r++) {
636     if (n2collect[r] > 0) PetscCall(DMSwarmDataExTopologyAddNeighbour(de, r));
637   }
638   PetscCall(DMSwarmDataExTopologyFinalize(de));
639   /* Define send counts */
640   PetscCall(DMSwarmDataExInitializeSendCount(de));
641   for (PetscMPIInt r = 0; r < size; r++) {
642     if (n2collect[r] > 0) PetscCall(DMSwarmDataExAddToSendCount(de, r, n2collect[r]));
643   }
644   PetscCall(DMSwarmDataExFinalizeSendCount(de));
645   /* Pack data */
646   PetscCall(DMSwarmDataBucketCreatePackedArray(swarm->db, &sizeof_dmswarm_point, &point_buffer));
647   PetscCall(DMSwarmDataExPackInitialize(de, sizeof_dmswarm_point));
648   for (PetscMPIInt r = 0; r < size; r++) {
649     for (p = 0; p < n2collect[r]; p++) {
650       PetscCall(DMSwarmDataBucketFillPackedArray(swarm->db, collectlist[r][p], point_buffer));
651       /* insert point buffer into the data exchanger */
652       PetscCall(DMSwarmDataExPackData(de, r, 1, point_buffer));
653     }
654   }
655   PetscCall(DMSwarmDataExPackFinalize(de));
656   /* Scatter */
657   PetscCall(DMSwarmDataExBegin(de));
658   PetscCall(DMSwarmDataExEnd(de));
659   /* Collect data in DMSwarm container */
660   PetscCall(DMSwarmDataExGetRecvData(de, &n_points_recv, &recv_points));
661   PetscCall(DMSwarmDataBucketGetSizes(swarm->db, &npoints, NULL, NULL));
662   PetscCall(DMSwarmDataBucketSetSizes(swarm->db, npoints + n_points_recv, DMSWARM_DATA_BUCKET_BUFFER_DEFAULT));
663   for (p = 0; p < n_points_recv; p++) {
664     void *data_p = (void *)((char *)recv_points + p * sizeof_dmswarm_point);
665 
666     PetscCall(DMSwarmDataBucketInsertPackedArray(swarm->db, npoints + p, data_p));
667   }
668   /* Release memory */
669   for (PetscMPIInt r = 0; r < size; r++) {
670     if (collectlist[r]) PetscCall(PetscFree(collectlist[r]));
671   }
672   PetscCall(PetscFree(collectlist));
673   PetscCall(PetscFree(n2collect));
674   PetscCall(PetscFree(ctxlist));
675   PetscCall(DMSwarmDataBucketDestroyPackedArray(swarm->db, &point_buffer));
676   PetscCall(DMSwarmDataExView(de));
677   PetscCall(DMSwarmDataExDestroy(de));
678   PetscFunctionReturn(PETSC_SUCCESS);
679 }
680 
681 /*@
682   DMSwarmGetMigrateType - Get the style of point migration
683 
684   Logically Collective
685 
686   Input Parameter:
687 . dm - the `DMSWARM`
688 
689   Output Parameter:
690 . mtype - The migration type, see `DMSwarmMigrateType`
691 
692   Level: intermediate
693 
694 .seealso: `DM`, `DMSWARM`, `DMSwarmMigrateType`, `DMSwarmMigrate()`
695 @*/
696 PetscErrorCode DMSwarmGetMigrateType(DM dm, DMSwarmMigrateType *mtype)
697 {
698   PetscFunctionBegin;
699   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
700   PetscAssertPointer(mtype, 2);
701   *mtype = ((DM_Swarm *)dm->data)->migrate_type;
702   PetscFunctionReturn(PETSC_SUCCESS);
703 }
704 
705 /*@
706   DMSwarmSetMigrateType - Set the style of point migration
707 
708   Logically Collective
709 
710   Input Parameters:
711 + dm    - the `DMSWARM`
712 - mtype - The migration type, see `DMSwarmMigrateType`
713 
714   Level: intermediate
715 
716 .seealso: `DM`, `DMSWARM`, `DMSwarmMigrateType`, `DMSwarmGetMigrateType()`, `DMSwarmMigrate()`
717 @*/
718 PetscErrorCode DMSwarmSetMigrateType(DM dm, DMSwarmMigrateType mtype)
719 {
720   PetscFunctionBegin;
721   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
722   PetscValidLogicalCollectiveInt(dm, mtype, 2);
723   ((DM_Swarm *)dm->data)->migrate_type = mtype;
724   PetscFunctionReturn(PETSC_SUCCESS);
725 }
726