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