xref: /petsc/src/dm/impls/swarm/data_ex.c (revision 57d508425293f0bb93f59574d14951d8faac9af8)
1 /*
2 Build a few basic tools to help with partitioned domains.
3 
4 1)
5 On each processor, have a DomainExchangerTopology.
6 This is a doubly-connected edge list which enumerates the
7 communication paths between connected processors. By numbering
8 these paths we can always uniquely assign message identifiers.
9 
10         edge
11          10
12 proc  --------->  proc
13  0    <--------    1
14          11
15         twin
16 
17 Eg: Proc 0 send to proc 1 with message id is 10. To receive the correct
18 message, proc 1 looks for the edge connected to proc 0, and then the
19 message id comes from the twin of that edge
20 
21 2)
22 A DomainExchangerArrayPacker.
23 A little function which given a piece of data, will memcpy the data into
24 an array (which will be sent to procs) into the correct place.
25 
26 On Proc 1 we sent data to procs 0,2,3. The data is on different lengths.
27 All data gets jammed into single array. Need to "jam" data into correct locations
28 The Packer knows how much is to going to each processor and keeps track of the inserts
29 so as to avoid ever packing TOO much into one slot, and inevatbly corrupting some memory
30 
31 data to 0    data to 2       data to 3
32 
33 |--------|-----------------|--|
34 
35 User has to unpack message themselves. I can get you the pointer for each i
36 entry, but you'll have to cast it to the appropriate data type.
37 
38 Phase A: Build topology
39 
40 Phase B: Define message lengths
41 
42 Phase C: Pack data
43 
44 Phase D: Send data
45 
46 + Constructor
47 DMSwarmDataExCreate()
48 + Phase A
49 DMSwarmDataExTopologyInitialize()
50 DMSwarmDataExTopologyAddNeighbour()
51 DMSwarmDataExTopologyAddNeighbour()
52 DMSwarmDataExTopologyFinalize()
53 + Phase B
54 DMSwarmDataExZeroAllSendCount()
55 DMSwarmDataExAddToSendCount()
56 DMSwarmDataExAddToSendCount()
57 DMSwarmDataExAddToSendCount()
58 + Phase C
59 DMSwarmDataExPackInitialize()
60 DMSwarmDataExPackData()
61 DMSwarmDataExPackData()
62 DMSwarmDataExPackFinalize()
63 +Phase D
64 DMSwarmDataExBegin()
65  ... perform any calculations ...
66 DMSwarmDataExEnd()
67 
68 ... user calls any getters here ...
69 
70 */
71 #include <petscvec.h>
72 #include <petscmat.h>
73 #include <petsc/private/petscimpl.h>
74 
75 #include "../src/dm/impls/swarm/data_ex.h"
76 
77 const char *status_names[] = {"initialized", "finalized", "unknown"};
78 
79 PETSC_EXTERN PetscLogEvent DMSWARM_DataExchangerTopologySetup;
80 PETSC_EXTERN PetscLogEvent DMSWARM_DataExchangerBegin;
81 PETSC_EXTERN PetscLogEvent DMSWARM_DataExchangerEnd;
82 PETSC_EXTERN PetscLogEvent DMSWARM_DataExchangerSendCount;
83 PETSC_EXTERN PetscLogEvent DMSWARM_DataExchangerPack;
84 
85 PetscErrorCode DMSwarmDataExCreate(MPI_Comm comm, const PetscInt count, DMSwarmDataEx *ex)
86 {
87   DMSwarmDataEx d;
88 
89   PetscFunctionBegin;
90   PetscCall(PetscNew(&d));
91   PetscCallMPI(MPI_Comm_dup(comm, &d->comm));
92   PetscCallMPI(MPI_Comm_rank(d->comm, &d->rank));
93 
94   d->instance = count;
95 
96   d->topology_status        = DEOBJECT_STATE_UNKNOWN;
97   d->message_lengths_status = DEOBJECT_STATE_UNKNOWN;
98   d->packer_status          = DEOBJECT_STATE_UNKNOWN;
99   d->communication_status   = DEOBJECT_STATE_UNKNOWN;
100 
101   d->n_neighbour_procs = -1;
102   d->neighbour_procs   = NULL;
103 
104   d->messages_to_be_sent      = NULL;
105   d->message_offsets          = NULL;
106   d->messages_to_be_recvieved = NULL;
107 
108   d->unit_message_size   = (size_t)-1;
109   d->send_message        = NULL;
110   d->send_message_length = -1;
111   d->recv_message        = NULL;
112   d->recv_message_length = -1;
113   d->total_pack_cnt      = -1;
114   d->pack_cnt            = NULL;
115 
116   d->send_tags = NULL;
117   d->recv_tags = NULL;
118 
119   d->_stats    = NULL;
120   d->_requests = NULL;
121   *ex          = d;
122   PetscFunctionReturn(PETSC_SUCCESS);
123 }
124 
125 /*
126     This code is horrible, who let it get into main.
127 
128     Should be printing to a viewer, should not be using PETSC_COMM_WORLD
129 
130 */
131 PetscErrorCode DMSwarmDataExView(DMSwarmDataEx d)
132 {
133   PetscMPIInt p;
134 
135   PetscFunctionBegin;
136   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "DMSwarmDataEx: instance=%" PetscInt_FMT "\n", d->instance));
137   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  topology status:        %s \n", status_names[d->topology_status]));
138   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  message lengths status: %s \n", status_names[d->message_lengths_status]));
139   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  packer status status:   %s \n", status_names[d->packer_status]));
140   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  communication status:   %s \n", status_names[d->communication_status]));
141 
142   if (d->topology_status == DEOBJECT_FINALIZED) {
143     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  Topology:\n"));
144     PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "    [%d] neighbours: %d \n", d->rank, d->n_neighbour_procs));
145     for (p = 0; p < d->n_neighbour_procs; p++) PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "    [%d]   neighbour[%d] = %d \n", d->rank, p, d->neighbour_procs[p]));
146     PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, stdout));
147   }
148 
149   if (d->message_lengths_status == DEOBJECT_FINALIZED) {
150     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  Message lengths:\n"));
151     PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "    [%d] atomic size: %ld \n", d->rank, (long int)d->unit_message_size));
152     for (p = 0; p < d->n_neighbour_procs; p++) PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "    [%d] >>>>> ( %" PetscInt_FMT " units :: tag = %d) >>>>> [%d] \n", d->rank, d->messages_to_be_sent[p], d->send_tags[p], d->neighbour_procs[p]));
153     for (p = 0; p < d->n_neighbour_procs; p++) {
154       PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "    [%d] <<<<< ( %" PetscInt_FMT " units :: tag = %d) <<<<< [%d] \n", d->rank, d->messages_to_be_recvieved[p], d->recv_tags[p], d->neighbour_procs[p]));
155     }
156     PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, stdout));
157   }
158   if (d->packer_status == DEOBJECT_FINALIZED) { }
159   if (d->communication_status == DEOBJECT_FINALIZED) { }
160   PetscFunctionReturn(PETSC_SUCCESS);
161 }
162 
163 PetscErrorCode DMSwarmDataExDestroy(DMSwarmDataEx d)
164 {
165   PetscFunctionBegin;
166   PetscCallMPI(MPI_Comm_free(&d->comm));
167   if (d->neighbour_procs) PetscCall(PetscFree(d->neighbour_procs));
168   if (d->messages_to_be_sent) PetscCall(PetscFree(d->messages_to_be_sent));
169   if (d->message_offsets) PetscCall(PetscFree(d->message_offsets));
170   if (d->messages_to_be_recvieved) PetscCall(PetscFree(d->messages_to_be_recvieved));
171   if (d->send_message) PetscCall(PetscFree(d->send_message));
172   if (d->recv_message) PetscCall(PetscFree(d->recv_message));
173   if (d->pack_cnt) PetscCall(PetscFree(d->pack_cnt));
174   if (d->send_tags) PetscCall(PetscFree(d->send_tags));
175   if (d->recv_tags) PetscCall(PetscFree(d->recv_tags));
176   if (d->_stats) PetscCall(PetscFree(d->_stats));
177   if (d->_requests) PetscCall(PetscFree(d->_requests));
178   PetscCall(PetscFree(d));
179   PetscFunctionReturn(PETSC_SUCCESS);
180 }
181 
182 /* === Phase A === */
183 
184 PetscErrorCode DMSwarmDataExTopologyInitialize(DMSwarmDataEx d)
185 {
186   PetscFunctionBegin;
187   d->topology_status   = DEOBJECT_INITIALIZED;
188   d->n_neighbour_procs = 0;
189   PetscCall(PetscFree(d->neighbour_procs));
190   PetscCall(PetscFree(d->messages_to_be_sent));
191   PetscCall(PetscFree(d->message_offsets));
192   PetscCall(PetscFree(d->messages_to_be_recvieved));
193   PetscCall(PetscFree(d->pack_cnt));
194   PetscCall(PetscFree(d->send_tags));
195   PetscCall(PetscFree(d->recv_tags));
196   PetscFunctionReturn(PETSC_SUCCESS);
197 }
198 
199 PetscErrorCode DMSwarmDataExTopologyAddNeighbour(DMSwarmDataEx d, const PetscMPIInt proc_id)
200 {
201   PetscMPIInt n, found;
202   PetscMPIInt size;
203 
204   PetscFunctionBegin;
205   PetscCheck(d->topology_status != DEOBJECT_FINALIZED, d->comm, PETSC_ERR_ARG_WRONGSTATE, "Topology has been finalized. To modify or update call DMSwarmDataExTopologyInitialize() first");
206   PetscCheck(d->topology_status == DEOBJECT_INITIALIZED, d->comm, PETSC_ERR_ARG_WRONGSTATE, "Topology must be initialised. Call DMSwarmDataExTopologyInitialize() first");
207 
208   /* error on negative entries */
209   PetscCheck(proc_id >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Trying to set proc neighbour with a rank < 0");
210   /* error on ranks larger than number of procs in communicator */
211   PetscCallMPI(MPI_Comm_size(d->comm, &size));
212   PetscCheck(proc_id < size, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Trying to set proc neighbour %d with a rank >= size %d", proc_id, size);
213   if (d->n_neighbour_procs == 0) PetscCall(PetscMalloc1(1, &d->neighbour_procs));
214   /* check for proc_id */
215   found = 0;
216   for (n = 0; n < d->n_neighbour_procs; n++) {
217     if (d->neighbour_procs[n] == proc_id) found = 1;
218   }
219   if (found == 0) { /* add it to list */
220     PetscCall(PetscRealloc(sizeof(PetscMPIInt) * (d->n_neighbour_procs + 1), &d->neighbour_procs));
221     d->neighbour_procs[d->n_neighbour_procs] = proc_id;
222     d->n_neighbour_procs++;
223   }
224   PetscFunctionReturn(PETSC_SUCCESS);
225 }
226 
227 /*
228 counter: the index of the communication object
229 N: the number of processors
230 r0: rank of sender
231 r1: rank of receiver
232 
233 procs = { 0, 1, 2, 3 }
234 
235 0 ==> 0         e=0
236 0 ==> 1         e=1
237 0 ==> 2         e=2
238 0 ==> 3         e=3
239 
240 1 ==> 0         e=4
241 1 ==> 1         e=5
242 1 ==> 2         e=6
243 1 ==> 3         e=7
244 
245 2 ==> 0         e=8
246 2 ==> 1         e=9
247 2 ==> 2         e=10
248 2 ==> 3         e=11
249 
250 3 ==> 0         e=12
251 3 ==> 1         e=13
252 3 ==> 2         e=14
253 3 ==> 3         e=15
254 
255 If we require that proc A sends to proc B, then the SEND tag index will be given by
256   N * rank(A) + rank(B) + offset
257 If we require that proc A will receive from proc B, then the RECV tag index will be given by
258   N * rank(B) + rank(A) + offset
259 
260 */
261 static void _get_tags(PetscInt counter, PetscMPIInt N, PetscMPIInt r0, PetscMPIInt r1, PetscMPIInt maxtag, PetscMPIInt *_st, PetscMPIInt *_rt)
262 {
263   PetscMPIInt st, rt;
264 
265   st   = (N * r0 + r1 + N * N * counter) % maxtag;
266   rt   = (N * r1 + r0 + N * N * counter) % maxtag;
267   *_st = st;
268   *_rt = rt;
269 }
270 
271 /*
272 Makes the communication map symmetric
273 */
274 static PetscErrorCode DMSwarmDataExCompleteCommunicationMap_Private(MPI_Comm comm, PetscMPIInt n, const PetscMPIInt proc_neighbours[], PetscMPIInt *n_new, PetscMPIInt **proc_neighbours_new)
275 {
276   Mat                A;
277   PetscInt           i, j, nc;
278   PetscInt           n_, *proc_neighbours_;
279   PetscInt           rank_;
280   PetscMPIInt        size, rank;
281   PetscScalar       *vals;
282   const PetscInt    *cols;
283   const PetscScalar *red_vals;
284   PetscMPIInt        _n_new, *_proc_neighbours_new;
285 
286   PetscFunctionBegin;
287   n_ = n;
288   PetscCall(PetscMalloc1(n_, &proc_neighbours_));
289   for (i = 0; i < n_; ++i) proc_neighbours_[i] = proc_neighbours[i];
290   PetscCallMPI(MPI_Comm_size(comm, &size));
291   PetscCallMPI(MPI_Comm_rank(comm, &rank));
292   rank_ = rank;
293 
294   PetscCall(MatCreate(comm, &A));
295   PetscCall(MatSetSizes(A, PETSC_DECIDE, PETSC_DECIDE, size, size));
296   PetscCall(MatSetType(A, MATAIJ));
297   PetscCall(MatSeqAIJSetPreallocation(A, 1, NULL));
298   PetscCall(MatMPIAIJSetPreallocation(A, n_, NULL, n_, NULL));
299   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
300   /* Build original map */
301   PetscCall(PetscMalloc1(n_, &vals));
302   for (i = 0; i < n_; ++i) vals[i] = 1.0;
303   PetscCall(MatSetValues(A, 1, &rank_, n_, proc_neighbours_, vals, INSERT_VALUES));
304   PetscCall(MatAssemblyBegin(A, MAT_FLUSH_ASSEMBLY));
305   PetscCall(MatAssemblyEnd(A, MAT_FLUSH_ASSEMBLY));
306   /* Now force all other connections if they are not already there */
307   /* It's more efficient to do them all at once */
308   for (i = 0; i < n_; ++i) vals[i] = 2.0;
309   PetscCall(MatSetValues(A, n_, proc_neighbours_, 1, &rank_, vals, INSERT_VALUES));
310   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
311   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
312   /*
313   PetscCall(PetscViewerPushFormat(PETSC_VIEWER_STDOUT_WORLD,PETSC_VIEWER_ASCII_INFO));
314   PetscCall(MatView(A,PETSC_VIEWER_STDOUT_WORLD));
315   PetscCall(PetscViewerPopFormat(PETSC_VIEWER_STDOUT_WORLD));
316 */
317   if ((n_new != NULL) && (proc_neighbours_new != NULL)) {
318     PetscCall(MatGetRow(A, rank_, &nc, &cols, &red_vals));
319     PetscCall(PetscMPIIntCast(nc, &_n_new));
320     PetscCall(PetscMalloc1(_n_new, &_proc_neighbours_new));
321     for (j = 0; j < nc; ++j) PetscCall(PetscMPIIntCast(cols[j], &_proc_neighbours_new[j]));
322     PetscCall(MatRestoreRow(A, rank_, &nc, &cols, &red_vals));
323     *n_new               = _n_new;
324     *proc_neighbours_new = _proc_neighbours_new;
325   }
326   PetscCall(MatDestroy(&A));
327   PetscCall(PetscFree(vals));
328   PetscCall(PetscFree(proc_neighbours_));
329   PetscCallMPI(MPI_Barrier(comm));
330   PetscFunctionReturn(PETSC_SUCCESS);
331 }
332 
333 PetscErrorCode DMSwarmDataExTopologyFinalize(DMSwarmDataEx d)
334 {
335   PetscMPIInt symm_nn = 0, *symm_procs = NULL, r0, n, st, rt, size, *maxtag, iflg;
336 
337   PetscFunctionBegin;
338   PetscCheck(d->topology_status == DEOBJECT_INITIALIZED, d->comm, PETSC_ERR_ARG_WRONGSTATE, "Topology must be initialised. Call DMSwarmDataExTopologyInitialize() first");
339 
340   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerTopologySetup, 0, 0, 0, 0));
341   /* given information about all my neighbours, make map symmetric */
342   PetscCall(DMSwarmDataExCompleteCommunicationMap_Private(d->comm, d->n_neighbour_procs, d->neighbour_procs, &symm_nn, &symm_procs));
343   /* update my arrays */
344   PetscCall(PetscFree(d->neighbour_procs));
345   d->n_neighbour_procs = symm_nn;
346   d->neighbour_procs   = symm_procs;
347   /* allocates memory */
348   if (!d->messages_to_be_sent) PetscCall(PetscMalloc1(d->n_neighbour_procs + 1, &d->messages_to_be_sent));
349   if (!d->message_offsets) PetscCall(PetscMalloc1(d->n_neighbour_procs + 1, &d->message_offsets));
350   if (!d->messages_to_be_recvieved) PetscCall(PetscMalloc1(d->n_neighbour_procs + 1, &d->messages_to_be_recvieved));
351   if (!d->pack_cnt) PetscCall(PetscMalloc(sizeof(PetscInt) * d->n_neighbour_procs, &d->pack_cnt));
352   if (!d->_stats) PetscCall(PetscMalloc(sizeof(MPI_Status) * 2 * d->n_neighbour_procs, &d->_stats));
353   if (!d->_requests) PetscCall(PetscMalloc(sizeof(MPI_Request) * 2 * d->n_neighbour_procs, &d->_requests));
354   if (!d->send_tags) PetscCall(PetscMalloc(sizeof(int) * d->n_neighbour_procs, &d->send_tags));
355   if (!d->recv_tags) PetscCall(PetscMalloc(sizeof(int) * d->n_neighbour_procs, &d->recv_tags));
356   /* compute message tags */
357   PetscCallMPI(MPI_Comm_size(d->comm, &size));
358   PetscCallMPI(MPI_Comm_get_attr(MPI_COMM_WORLD, MPI_TAG_UB, &maxtag, &iflg));
359   PetscCheck(iflg, d->comm, PETSC_ERR_LIB, "MPI error: MPI_Comm_get_attr() is not returning a MPI_TAG_UB");
360   r0 = d->rank;
361   for (n = 0; n < d->n_neighbour_procs; ++n) {
362     PetscMPIInt r1 = d->neighbour_procs[n];
363 
364     _get_tags(d->instance, size, r0, r1, *maxtag, &st, &rt);
365     d->send_tags[n] = st;
366     d->recv_tags[n] = rt;
367   }
368   d->topology_status = DEOBJECT_FINALIZED;
369   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerTopologySetup, 0, 0, 0, 0));
370   PetscFunctionReturn(PETSC_SUCCESS);
371 }
372 
373 /* === Phase B === */
374 static PetscErrorCode _DMSwarmDataExConvertProcIdToLocalIndex(DMSwarmDataEx de, PetscMPIInt proc_id, PetscMPIInt *local)
375 {
376   PetscMPIInt i, np;
377 
378   PetscFunctionBegin;
379   np     = de->n_neighbour_procs;
380   *local = -1;
381   for (i = 0; i < np; ++i) {
382     if (proc_id == de->neighbour_procs[i]) {
383       *local = i;
384       break;
385     }
386   }
387   PetscFunctionReturn(PETSC_SUCCESS);
388 }
389 
390 PetscErrorCode DMSwarmDataExInitializeSendCount(DMSwarmDataEx de)
391 {
392   PetscMPIInt i;
393 
394   PetscFunctionBegin;
395   PetscCheck(de->topology_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Topology not finalized");
396   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerSendCount, 0, 0, 0, 0));
397   de->message_lengths_status = DEOBJECT_INITIALIZED;
398   for (i = 0; i < de->n_neighbour_procs; ++i) de->messages_to_be_sent[i] = 0;
399   PetscFunctionReturn(PETSC_SUCCESS);
400 }
401 
402 /*
403 1) only allows counters to be set on neighbouring cpus
404 */
405 PetscErrorCode DMSwarmDataExAddToSendCount(DMSwarmDataEx de, const PetscMPIInt proc_id, const PetscInt count)
406 {
407   PetscMPIInt local_val;
408 
409   PetscFunctionBegin;
410   PetscCheck(de->message_lengths_status != DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths have been defined. To modify these call DMSwarmDataExInitializeSendCount() first");
411   PetscCheck(de->message_lengths_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths must be defined. Call DMSwarmDataExInitializeSendCount() first");
412 
413   PetscCall(_DMSwarmDataExConvertProcIdToLocalIndex(de, proc_id, &local_val));
414   PetscCheck(local_val != -1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Proc %d is not a valid neighbour rank", (int)proc_id);
415 
416   de->messages_to_be_sent[local_val] = de->messages_to_be_sent[local_val] + count;
417   PetscFunctionReturn(PETSC_SUCCESS);
418 }
419 
420 PetscErrorCode DMSwarmDataExFinalizeSendCount(DMSwarmDataEx de)
421 {
422   PetscFunctionBegin;
423   PetscCheck(de->message_lengths_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths must be defined. Call DMSwarmDataExInitializeSendCount() first");
424 
425   de->message_lengths_status = DEOBJECT_FINALIZED;
426   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerSendCount, 0, 0, 0, 0));
427   PetscFunctionReturn(PETSC_SUCCESS);
428 }
429 
430 /* === Phase C === */
431 /*
432   zero out all send counts
433   free send and recv buffers
434   zeros out message length
435   zeros out all counters
436   zero out packed data counters
437 */
438 static PetscErrorCode _DMSwarmDataExInitializeTmpStorage(DMSwarmDataEx de)
439 {
440   PetscMPIInt i, np;
441 
442   PetscFunctionBegin;
443   np = de->n_neighbour_procs;
444   for (i = 0; i < np; ++i) {
445     /*  de->messages_to_be_sent[i] = -1; */
446     de->messages_to_be_recvieved[i] = -1;
447   }
448   PetscCall(PetscFree(de->send_message));
449   PetscCall(PetscFree(de->recv_message));
450   PetscFunctionReturn(PETSC_SUCCESS);
451 }
452 
453 /*
454    Zeros out pack data counters
455    Ensures mesaage length is set
456    Checks send counts properly initialized
457    allocates space for pack data
458 */
459 PetscErrorCode DMSwarmDataExPackInitialize(DMSwarmDataEx de, size_t unit_message_size)
460 {
461   PetscMPIInt i, np;
462   PetscInt    total;
463 
464   PetscFunctionBegin;
465   PetscCheck(de->topology_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Topology not finalized");
466   PetscCheck(de->message_lengths_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths not finalized");
467   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerPack, 0, 0, 0, 0));
468   de->packer_status = DEOBJECT_INITIALIZED;
469   PetscCall(_DMSwarmDataExInitializeTmpStorage(de));
470   np                    = de->n_neighbour_procs;
471   de->unit_message_size = unit_message_size;
472   total                 = 0;
473   for (i = 0; i < np; ++i) {
474     if (de->messages_to_be_sent[i] == -1) {
475       PetscMPIInt proc_neighour = de->neighbour_procs[i];
476       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "Messages_to_be_sent[neighbour_proc=%d] is un-initialised. Call DMSwarmDataExSetSendCount() first", proc_neighour);
477     }
478     total = total + de->messages_to_be_sent[i];
479   }
480   /* create space for the data to be sent */
481   PetscCall(PetscMalloc(unit_message_size * (total + 1), &de->send_message));
482   /* initialize memory */
483   PetscCall(PetscMemzero(de->send_message, unit_message_size * (total + 1)));
484   /* set total items to send */
485   de->send_message_length = total;
486   de->message_offsets[0]  = 0;
487   total                   = de->messages_to_be_sent[0];
488   for (i = 1; i < np; ++i) {
489     de->message_offsets[i] = total;
490     total                  = total + de->messages_to_be_sent[i];
491   }
492   /* init the packer counters */
493   de->total_pack_cnt = 0;
494   for (i = 0; i < np; ++i) de->pack_cnt[i] = 0;
495   PetscFunctionReturn(PETSC_SUCCESS);
496 }
497 
498 /*
499     Ensures data gets been packed appropriately and no overlaps occur
500 */
501 PetscErrorCode DMSwarmDataExPackData(DMSwarmDataEx de, PetscMPIInt proc_id, PetscInt n, void *data)
502 {
503   PetscMPIInt local;
504   PetscInt    insert_location;
505   void       *dest;
506 
507   PetscFunctionBegin;
508   PetscCheck(de->packer_status != DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Packed data have been defined. To modify these call DMSwarmDataExInitializeSendCount(), DMSwarmDataExAddToSendCount(), DMSwarmDataExPackInitialize() first");
509   PetscCheck(de->packer_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Packed data must be defined. Call DMSwarmDataExInitializeSendCount(), DMSwarmDataExAddToSendCount(), DMSwarmDataExPackInitialize() first");
510 
511   PetscCheck(de->send_message, de->comm, PETSC_ERR_ORDER, "send_message is not initialized. Call DMSwarmDataExPackInitialize() first");
512   PetscCall(_DMSwarmDataExConvertProcIdToLocalIndex(de, proc_id, &local));
513   PetscCheck(local != -1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "proc_id %d is not registered neighbour", proc_id);
514   PetscCheck(n + de->pack_cnt[local] <= de->messages_to_be_sent[local], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Trying to pack too many entries to be sent to proc %d. Space requested = %" PetscInt_FMT ": Attempt to insert %" PetscInt_FMT, proc_id, de->messages_to_be_sent[local], n + de->pack_cnt[local]);
515 
516   /* copy memory */
517   insert_location = de->message_offsets[local] + de->pack_cnt[local];
518   dest            = ((char *)de->send_message) + de->unit_message_size * insert_location;
519   PetscCall(PetscMemcpy(dest, data, de->unit_message_size * n));
520   /* increment counter */
521   de->pack_cnt[local] = de->pack_cnt[local] + n;
522   PetscFunctionReturn(PETSC_SUCCESS);
523 }
524 
525 /*
526 *) Ensures all data has been packed
527 */
528 PetscErrorCode DMSwarmDataExPackFinalize(DMSwarmDataEx de)
529 {
530   PetscMPIInt i, np;
531   PetscInt    total;
532 
533   PetscFunctionBegin;
534   PetscCheck(de->packer_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Packer has not been initialized. Must call DMSwarmDataExPackInitialize() first.");
535   np = de->n_neighbour_procs;
536   for (i = 0; i < np; ++i) {
537     PetscCheck(de->pack_cnt[i] == de->messages_to_be_sent[i], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not all messages for neighbour[%d] have been packed. Expected %" PetscInt_FMT " : Inserted %" PetscInt_FMT, de->neighbour_procs[i], de->messages_to_be_sent[i], de->pack_cnt[i]);
538   }
539   /* init */
540   for (i = 0; i < np; ++i) de->messages_to_be_recvieved[i] = -1;
541   /* figure out the recv counts here */
542   for (i = 0; i < np; ++i) PetscCallMPI(MPIU_Isend(&de->messages_to_be_sent[i], 1, MPIU_INT, de->neighbour_procs[i], de->send_tags[i], de->comm, &de->_requests[i]));
543   for (i = 0; i < np; ++i) PetscCallMPI(MPIU_Irecv(&de->messages_to_be_recvieved[i], 1, MPIU_INT, de->neighbour_procs[i], de->recv_tags[i], de->comm, &de->_requests[np + i]));
544   PetscCallMPI(MPI_Waitall(2 * np, de->_requests, de->_stats));
545   /* create space for the data to be recvieved */
546   total = 0;
547   for (i = 0; i < np; ++i) total = total + de->messages_to_be_recvieved[i];
548   PetscCall(PetscMalloc(de->unit_message_size * (total + 1), &de->recv_message));
549   /* initialize memory */
550   PetscCall(PetscMemzero(de->recv_message, de->unit_message_size * (total + 1)));
551   /* set total items to receive */
552   de->recv_message_length  = total;
553   de->packer_status        = DEOBJECT_FINALIZED;
554   de->communication_status = DEOBJECT_INITIALIZED;
555   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerPack, 0, 0, 0, 0));
556   PetscFunctionReturn(PETSC_SUCCESS);
557 }
558 
559 /* do the actual message passing */
560 PetscErrorCode DMSwarmDataExBegin(DMSwarmDataEx de)
561 {
562   PetscMPIInt i, np;
563   void       *dest;
564 
565   PetscFunctionBegin;
566   PetscCheck(de->topology_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Topology not finalized");
567   PetscCheck(de->message_lengths_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths not finalized");
568   PetscCheck(de->packer_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Packer not finalized");
569   PetscCheck(de->communication_status != DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Communication has already been finalized. Must call DMSwarmDataExInitialize() first.");
570   PetscCheck(de->recv_message, de->comm, PETSC_ERR_ORDER, "recv_message has not been initialized. Must call DMSwarmDataExPackFinalize() first");
571   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerBegin, 0, 0, 0, 0));
572   np = de->n_neighbour_procs;
573   /* == NON BLOCKING == */
574   for (i = 0; i < np; ++i) {
575     dest = ((char *)de->send_message) + de->unit_message_size * de->message_offsets[i];
576     PetscCallMPI(MPIU_Isend(dest, de->messages_to_be_sent[i] * de->unit_message_size, MPI_CHAR, de->neighbour_procs[i], de->send_tags[i], de->comm, &de->_requests[i]));
577   }
578   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerBegin, 0, 0, 0, 0));
579   PetscFunctionReturn(PETSC_SUCCESS);
580 }
581 
582 /* do the actual message passing now */
583 PetscErrorCode DMSwarmDataExEnd(DMSwarmDataEx de)
584 {
585   PetscMPIInt i, np;
586   PetscInt    total;
587   PetscInt   *message_recv_offsets;
588   void       *dest;
589 
590   PetscFunctionBegin;
591   PetscCheck(de->communication_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Communication has not been initialized. Must call DMSwarmDataExInitialize() first.");
592   PetscCheck(de->recv_message, de->comm, PETSC_ERR_ORDER, "recv_message has not been initialized. Must call DMSwarmDataExPackFinalize() first");
593   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerEnd, 0, 0, 0, 0));
594   np = de->n_neighbour_procs;
595   PetscCall(PetscMalloc1(np + 1, &message_recv_offsets));
596   message_recv_offsets[0] = 0;
597   total                   = de->messages_to_be_recvieved[0];
598   for (i = 1; i < np; ++i) {
599     message_recv_offsets[i] = total;
600     total                   = total + de->messages_to_be_recvieved[i];
601   }
602   /* == NON BLOCKING == */
603   for (i = 0; i < np; ++i) {
604     dest = ((char *)de->recv_message) + de->unit_message_size * message_recv_offsets[i];
605     PetscCallMPI(MPIU_Irecv(dest, de->messages_to_be_recvieved[i] * de->unit_message_size, MPI_CHAR, de->neighbour_procs[i], de->recv_tags[i], de->comm, &de->_requests[np + i]));
606   }
607   PetscCallMPI(MPI_Waitall(2 * np, de->_requests, de->_stats));
608   PetscCall(PetscFree(message_recv_offsets));
609   de->communication_status = DEOBJECT_FINALIZED;
610   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerEnd, 0, 0, 0, 0));
611   PetscFunctionReturn(PETSC_SUCCESS);
612 }
613 
614 PetscErrorCode DMSwarmDataExGetSendData(DMSwarmDataEx de, PetscInt *length, void **send)
615 {
616   PetscFunctionBegin;
617   PetscCheck(de->packer_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ARG_WRONGSTATE, "Data has not finished being packed.");
618   *length = de->send_message_length;
619   *send   = de->send_message;
620   PetscFunctionReturn(PETSC_SUCCESS);
621 }
622 
623 PetscErrorCode DMSwarmDataExGetRecvData(DMSwarmDataEx de, PetscInt *length, void **recv)
624 {
625   PetscFunctionBegin;
626   PetscCheck(de->communication_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ARG_WRONGSTATE, "Data has not finished being sent.");
627   *length = de->recv_message_length;
628   *recv   = de->recv_message;
629   PetscFunctionReturn(PETSC_SUCCESS);
630 }
631 
632 PetscErrorCode DMSwarmDataExTopologyGetNeighbours(DMSwarmDataEx de, PetscMPIInt *n, PetscMPIInt *neigh[])
633 {
634   PetscFunctionBegin;
635   if (n) *n = de->n_neighbour_procs;
636   if (neigh) *neigh = de->neighbour_procs;
637   PetscFunctionReturn(PETSC_SUCCESS);
638 }
639