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