Actual source code: data_ex.c

  1: /*
  2: Build a few basic tools to help with partitioned domains.

  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.

 10:         edge
 11:          10
 12: proc  --------->  proc
 13:  0    <--------    1
 14:          11
 15:         twin

 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

 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.

 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

 31: data to 0    data to 2       data to 3

 33: |--------|-----------------|--|

 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.

 38: Phase A: Build topology

 40: Phase B: Define message lengths

 42: Phase C: Pack data

 44: Phase D: Send data

 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()

 68: ... user calls any getters here ...

 70: */
 71: #include <petscvec.h>
 72: #include <petscmat.h>

 74: #include "../src/dm/impls/swarm/data_ex.h"

 76: const char *status_names[] = {"initialized", "finalized", "unknown"};

 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;

 84: PetscErrorCode DMSwarmDataExCreate(MPI_Comm comm, const PetscInt count, DMSwarmDataEx *ex)
 85: {
 86:   DMSwarmDataEx d;

 88:   PetscFunctionBegin;
 89:   PetscCall(PetscNew(&d));
 90:   PetscCallMPI(MPI_Comm_dup(comm, &d->comm));
 91:   PetscCallMPI(MPI_Comm_rank(d->comm, &d->rank));

 93:   d->instance = count;

 95:   d->topology_status        = DEOBJECT_STATE_UNKNOWN;
 96:   d->message_lengths_status = DEOBJECT_STATE_UNKNOWN;
 97:   d->packer_status          = DEOBJECT_STATE_UNKNOWN;
 98:   d->communication_status   = DEOBJECT_STATE_UNKNOWN;

100:   d->n_neighbour_procs = -1;
101:   d->neighbour_procs   = NULL;

103:   d->messages_to_be_sent      = NULL;
104:   d->message_offsets          = NULL;
105:   d->messages_to_be_recvieved = NULL;

107:   d->unit_message_size   = (size_t)-1;
108:   d->send_message        = NULL;
109:   d->send_message_length = -1;
110:   d->recv_message        = NULL;
111:   d->recv_message_length = -1;
112:   d->total_pack_cnt      = -1;
113:   d->pack_cnt            = NULL;

115:   d->send_tags = NULL;
116:   d->recv_tags = NULL;

118:   d->_stats    = NULL;
119:   d->_requests = NULL;
120:   *ex          = d;
121:   PetscFunctionReturn(PETSC_SUCCESS);
122: }

124: /*
125:     This code is horrible, who let it get into main.

127:     Should be printing to a viewer, should not be using PETSC_COMM_WORLD

129: */
130: PetscErrorCode DMSwarmDataExView(DMSwarmDataEx d)
131: {
132:   PetscMPIInt p;

134:   PetscFunctionBegin;
135:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "DMSwarmDataEx: instance=%" PetscInt_FMT "\n", d->instance));
136:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  topology status:        %s \n", status_names[d->topology_status]));
137:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  message lengths status: %s \n", status_names[d->message_lengths_status]));
138:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  packer status status:   %s \n", status_names[d->packer_status]));
139:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  communication status:   %s \n", status_names[d->communication_status]));

141:   if (d->topology_status == DEOBJECT_FINALIZED) {
142:     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  Topology:\n"));
143:     PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "    [%d] neighbours: %d \n", d->rank, d->n_neighbour_procs));
144:     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]));
145:     PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, stdout));
146:   }

148:   if (d->message_lengths_status == DEOBJECT_FINALIZED) {
149:     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "  Message lengths:\n"));
150:     PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "    [%d] atomic size: %ld \n", d->rank, (long int)d->unit_message_size));
151:     for (p = 0; p < d->n_neighbour_procs; p++) {
152:       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:     }
154:     for (p = 0; p < d->n_neighbour_procs; p++) {
155:       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]));
156:     }
157:     PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, stdout));
158:   }
159:   if (d->packer_status == DEOBJECT_FINALIZED) { }
160:   if (d->communication_status == DEOBJECT_FINALIZED) { }
161:   PetscFunctionReturn(PETSC_SUCCESS);
162: }

164: PetscErrorCode DMSwarmDataExDestroy(DMSwarmDataEx d)
165: {
166:   PetscFunctionBegin;
167:   PetscCallMPI(MPI_Comm_free(&d->comm));
168:   if (d->neighbour_procs) PetscCall(PetscFree(d->neighbour_procs));
169:   if (d->messages_to_be_sent) PetscCall(PetscFree(d->messages_to_be_sent));
170:   if (d->message_offsets) PetscCall(PetscFree(d->message_offsets));
171:   if (d->messages_to_be_recvieved) PetscCall(PetscFree(d->messages_to_be_recvieved));
172:   if (d->send_message) PetscCall(PetscFree(d->send_message));
173:   if (d->recv_message) PetscCall(PetscFree(d->recv_message));
174:   if (d->pack_cnt) PetscCall(PetscFree(d->pack_cnt));
175:   if (d->send_tags) PetscCall(PetscFree(d->send_tags));
176:   if (d->recv_tags) PetscCall(PetscFree(d->recv_tags));
177:   if (d->_stats) PetscCall(PetscFree(d->_stats));
178:   if (d->_requests) PetscCall(PetscFree(d->_requests));
179:   PetscCall(PetscFree(d));
180:   PetscFunctionReturn(PETSC_SUCCESS);
181: }

183: /* === Phase A === */

185: PetscErrorCode DMSwarmDataExTopologyInitialize(DMSwarmDataEx d)
186: {
187:   PetscFunctionBegin;
188:   d->topology_status   = DEOBJECT_INITIALIZED;
189:   d->n_neighbour_procs = 0;
190:   PetscCall(PetscFree(d->neighbour_procs));
191:   PetscCall(PetscFree(d->messages_to_be_sent));
192:   PetscCall(PetscFree(d->message_offsets));
193:   PetscCall(PetscFree(d->messages_to_be_recvieved));
194:   PetscCall(PetscFree(d->pack_cnt));
195:   PetscCall(PetscFree(d->send_tags));
196:   PetscCall(PetscFree(d->recv_tags));
197:   PetscFunctionReturn(PETSC_SUCCESS);
198: }

200: PetscErrorCode DMSwarmDataExTopologyAddNeighbour(DMSwarmDataEx d, const PetscMPIInt proc_id)
201: {
202:   PetscMPIInt n, found;
203:   PetscMPIInt size;

205:   PetscFunctionBegin;
206:   PetscCheck(d->topology_status != DEOBJECT_FINALIZED, d->comm, PETSC_ERR_ARG_WRONGSTATE, "Topology has been finalized. To modify or update call DMSwarmDataExTopologyInitialize() first");
207:   PetscCheck(d->topology_status == DEOBJECT_INITIALIZED, d->comm, PETSC_ERR_ARG_WRONGSTATE, "Topology must be initialised. Call DMSwarmDataExTopologyInitialize() first");

209:   /* error on negative entries */
210:   PetscCheck(proc_id >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Trying to set proc neighbour with a rank < 0");
211:   /* error on ranks larger than number of procs in communicator */
212:   PetscCallMPI(MPI_Comm_size(d->comm, &size));
213:   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);
214:   if (d->n_neighbour_procs == 0) PetscCall(PetscMalloc1(1, &d->neighbour_procs));
215:   /* check for proc_id */
216:   found = 0;
217:   for (n = 0; n < d->n_neighbour_procs; n++) {
218:     if (d->neighbour_procs[n] == proc_id) found = 1;
219:   }
220:   if (found == 0) { /* add it to list */
221:     PetscCall(PetscRealloc(sizeof(PetscMPIInt) * (d->n_neighbour_procs + 1), &d->neighbour_procs));
222:     d->neighbour_procs[d->n_neighbour_procs] = proc_id;
223:     d->n_neighbour_procs++;
224:   }
225:   PetscFunctionReturn(PETSC_SUCCESS);
226: }

228: /*
229: counter: the index of the communication object
230: N: the number of processors
231: r0: rank of sender
232: r1: rank of receiver

234: procs = { 0, 1, 2, 3 }

236: 0 ==> 0         e=0
237: 0 ==> 1         e=1
238: 0 ==> 2         e=2
239: 0 ==> 3         e=3

241: 1 ==> 0         e=4
242: 1 ==> 1         e=5
243: 1 ==> 2         e=6
244: 1 ==> 3         e=7

246: 2 ==> 0         e=8
247: 2 ==> 1         e=9
248: 2 ==> 2         e=10
249: 2 ==> 3         e=11

251: 3 ==> 0         e=12
252: 3 ==> 1         e=13
253: 3 ==> 2         e=14
254: 3 ==> 3         e=15

256: If we require that proc A sends to proc B, then the SEND tag index will be given by
257:   N * rank(A) + rank(B) + offset
258: If we require that proc A will receive from proc B, then the RECV tag index will be given by
259:   N * rank(B) + rank(A) + offset

261: */
262: static void _get_tags(PetscInt counter, PetscMPIInt N, PetscMPIInt r0, PetscMPIInt r1, PetscMPIInt maxtag, PetscMPIInt *_st, PetscMPIInt *_rt)
263: {
264:   PetscMPIInt st, rt;

266:   st   = (N * r0 + r1 + N * N * counter) % maxtag;
267:   rt   = (N * r1 + r0 + N * N * counter) % maxtag;
268:   *_st = st;
269:   *_rt = rt;
270: }

272: /*
273: Makes the communication map symmetric
274: */
275: static PetscErrorCode DMSwarmDataExCompleteCommunicationMap_Private(MPI_Comm comm, PetscMPIInt n, const PetscMPIInt proc_neighbours[], PetscMPIInt *n_new, PetscMPIInt **proc_neighbours_new)
276: {
277:   Mat                A;
278:   PetscInt           i, j, nc;
279:   PetscInt           n_, *proc_neighbours_;
280:   PetscInt           rank_;
281:   PetscMPIInt        size, rank;
282:   PetscScalar       *vals;
283:   const PetscInt    *cols;
284:   const PetscScalar *red_vals;
285:   PetscMPIInt        _n_new, *_proc_neighbours_new;

287:   PetscFunctionBegin;
288:   n_ = n;
289:   PetscCall(PetscMalloc1(n_, &proc_neighbours_));
290:   for (i = 0; i < n_; ++i) proc_neighbours_[i] = proc_neighbours[i];
291:   PetscCallMPI(MPI_Comm_size(comm, &size));
292:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
293:   rank_ = rank;

295:   PetscCall(MatCreate(comm, &A));
296:   PetscCall(MatSetSizes(A, PETSC_DECIDE, PETSC_DECIDE, size, size));
297:   PetscCall(MatSetType(A, MATAIJ));
298:   PetscCall(MatSeqAIJSetPreallocation(A, 1, NULL));
299:   PetscCall(MatMPIAIJSetPreallocation(A, n_, NULL, n_, NULL));
300:   PetscCall(MatSetOption(A, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
301:   /* Build original map */
302:   PetscCall(PetscMalloc1(n_, &vals));
303:   for (i = 0; i < n_; ++i) vals[i] = 1.0;
304:   PetscCall(MatSetValues(A, 1, &rank_, n_, proc_neighbours_, vals, INSERT_VALUES));
305:   PetscCall(MatAssemblyBegin(A, MAT_FLUSH_ASSEMBLY));
306:   PetscCall(MatAssemblyEnd(A, MAT_FLUSH_ASSEMBLY));
307:   /* Now force all other connections if they are not already there */
308:   /* It's more efficient to do them all at once */
309:   for (i = 0; i < n_; ++i) vals[i] = 2.0;
310:   PetscCall(MatSetValues(A, n_, proc_neighbours_, 1, &rank_, vals, INSERT_VALUES));
311:   PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
312:   PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
313:   /*
314:   PetscCall(PetscViewerPushFormat(PETSC_VIEWER_STDOUT_WORLD,PETSC_VIEWER_ASCII_INFO));
315:   PetscCall(MatView(A,PETSC_VIEWER_STDOUT_WORLD));
316:   PetscCall(PetscViewerPopFormat(PETSC_VIEWER_STDOUT_WORLD));
317: */
318:   if ((n_new != NULL) && (proc_neighbours_new != NULL)) {
319:     PetscCall(MatGetRow(A, rank_, &nc, &cols, &red_vals));
320:     _n_new = (PetscMPIInt)nc;
321:     PetscCall(PetscMalloc1(_n_new, &_proc_neighbours_new));
322:     for (j = 0; j < nc; ++j) _proc_neighbours_new[j] = (PetscMPIInt)cols[j];
323:     PetscCall(MatRestoreRow(A, rank_, &nc, &cols, &red_vals));
324:     *n_new               = (PetscMPIInt)_n_new;
325:     *proc_neighbours_new = (PetscMPIInt *)_proc_neighbours_new;
326:   }
327:   PetscCall(MatDestroy(&A));
328:   PetscCall(PetscFree(vals));
329:   PetscCall(PetscFree(proc_neighbours_));
330:   PetscCallMPI(MPI_Barrier(comm));
331:   PetscFunctionReturn(PETSC_SUCCESS);
332: }

334: PetscErrorCode DMSwarmDataExTopologyFinalize(DMSwarmDataEx d)
335: {
336:   PetscMPIInt symm_nn = 0, *symm_procs = NULL, r0, n, st, rt, size, *maxtag, flg;

338:   PetscFunctionBegin;
339:   PetscCheck(d->topology_status == DEOBJECT_INITIALIZED, d->comm, PETSC_ERR_ARG_WRONGSTATE, "Topology must be initialised. Call DMSwarmDataExTopologyInitialize() first");

341:   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerTopologySetup, 0, 0, 0, 0));
342:   /* given information about all my neighbours, make map symmetric */
343:   PetscCall(DMSwarmDataExCompleteCommunicationMap_Private(d->comm, d->n_neighbour_procs, d->neighbour_procs, &symm_nn, &symm_procs));
344:   /* update my arrays */
345:   PetscCall(PetscFree(d->neighbour_procs));
346:   d->n_neighbour_procs = symm_nn;
347:   d->neighbour_procs   = symm_procs;
348:   /* allocates memory */
349:   if (!d->messages_to_be_sent) PetscCall(PetscMalloc1(d->n_neighbour_procs + 1, &d->messages_to_be_sent));
350:   if (!d->message_offsets) PetscCall(PetscMalloc1(d->n_neighbour_procs + 1, &d->message_offsets));
351:   if (!d->messages_to_be_recvieved) PetscCall(PetscMalloc1(d->n_neighbour_procs + 1, &d->messages_to_be_recvieved));
352:   if (!d->pack_cnt) PetscCall(PetscMalloc(sizeof(PetscInt) * d->n_neighbour_procs, &d->pack_cnt));
353:   if (!d->_stats) PetscCall(PetscMalloc(sizeof(MPI_Status) * 2 * d->n_neighbour_procs, &d->_stats));
354:   if (!d->_requests) PetscCall(PetscMalloc(sizeof(MPI_Request) * 2 * d->n_neighbour_procs, &d->_requests));
355:   if (!d->send_tags) PetscCall(PetscMalloc(sizeof(int) * d->n_neighbour_procs, &d->send_tags));
356:   if (!d->recv_tags) PetscCall(PetscMalloc(sizeof(int) * d->n_neighbour_procs, &d->recv_tags));
357:   /* compute message tags */
358:   PetscCallMPI(MPI_Comm_size(d->comm, &size));
359:   PetscCallMPI(MPI_Comm_get_attr(MPI_COMM_WORLD, MPI_TAG_UB, &maxtag, &flg));
360:   PetscCheck(flg, d->comm, PETSC_ERR_LIB, "MPI error: MPI_Comm_get_attr() is not returning a MPI_TAG_UB");
361:   r0 = d->rank;
362:   for (n = 0; n < d->n_neighbour_procs; ++n) {
363:     PetscMPIInt r1 = d->neighbour_procs[n];

365:     _get_tags(d->instance, size, r0, r1, *maxtag, &st, &rt);
366:     d->send_tags[n] = (int)st;
367:     d->recv_tags[n] = (int)rt;
368:   }
369:   d->topology_status = DEOBJECT_FINALIZED;
370:   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerTopologySetup, 0, 0, 0, 0));
371:   PetscFunctionReturn(PETSC_SUCCESS);
372: }

374: /* === Phase B === */
375: static PetscErrorCode _DMSwarmDataExConvertProcIdToLocalIndex(DMSwarmDataEx de, PetscMPIInt proc_id, PetscMPIInt *local)
376: {
377:   PetscMPIInt i, np;

379:   PetscFunctionBegin;
380:   np     = de->n_neighbour_procs;
381:   *local = -1;
382:   for (i = 0; i < np; ++i) {
383:     if (proc_id == de->neighbour_procs[i]) {
384:       *local = i;
385:       break;
386:     }
387:   }
388:   PetscFunctionReturn(PETSC_SUCCESS);
389: }

391: PetscErrorCode DMSwarmDataExInitializeSendCount(DMSwarmDataEx de)
392: {
393:   PetscMPIInt i;

395:   PetscFunctionBegin;
396:   PetscCheck(de->topology_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Topology not finalized");
397:   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerSendCount, 0, 0, 0, 0));
398:   de->message_lengths_status = DEOBJECT_INITIALIZED;
399:   for (i = 0; i < de->n_neighbour_procs; ++i) de->messages_to_be_sent[i] = 0;
400:   PetscFunctionReturn(PETSC_SUCCESS);
401: }

403: /*
404: 1) only allows counters to be set on neighbouring cpus
405: */
406: PetscErrorCode DMSwarmDataExAddToSendCount(DMSwarmDataEx de, const PetscMPIInt proc_id, const PetscInt count)
407: {
408:   PetscMPIInt local_val;

410:   PetscFunctionBegin;
411:   PetscCheck(de->message_lengths_status != DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths have been defined. To modify these call DMSwarmDataExInitializeSendCount() first");
412:   PetscCheck(de->message_lengths_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths must be defined. Call DMSwarmDataExInitializeSendCount() first");

414:   PetscCall(_DMSwarmDataExConvertProcIdToLocalIndex(de, proc_id, &local_val));
415:   PetscCheck(local_val != -1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Proc %d is not a valid neighbour rank", (int)proc_id);

417:   de->messages_to_be_sent[local_val] = de->messages_to_be_sent[local_val] + count;
418:   PetscFunctionReturn(PETSC_SUCCESS);
419: }

421: PetscErrorCode DMSwarmDataExFinalizeSendCount(DMSwarmDataEx de)
422: {
423:   PetscFunctionBegin;
424:   PetscCheck(de->message_lengths_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths must be defined. Call DMSwarmDataExInitializeSendCount() first");

426:   de->message_lengths_status = DEOBJECT_FINALIZED;
427:   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerSendCount, 0, 0, 0, 0));
428:   PetscFunctionReturn(PETSC_SUCCESS);
429: }

431: /* === Phase C === */
432: /*
433:   zero out all send counts
434:   free send and recv buffers
435:   zeros out message length
436:   zeros out all counters
437:   zero out packed data counters
438: */
439: static PetscErrorCode _DMSwarmDataExInitializeTmpStorage(DMSwarmDataEx de)
440: {
441:   PetscMPIInt i, np;

443:   PetscFunctionBegin;
444:   np = de->n_neighbour_procs;
445:   for (i = 0; i < np; ++i) {
446:     /*  de->messages_to_be_sent[i] = -1; */
447:     de->messages_to_be_recvieved[i] = -1;
448:   }
449:   PetscCall(PetscFree(de->send_message));
450:   PetscCall(PetscFree(de->recv_message));
451:   PetscFunctionReturn(PETSC_SUCCESS);
452: }

454: /*
455:    Zeros out pack data counters
456:    Ensures mesaage length is set
457:    Checks send counts properly initialized
458:    allocates space for pack data
459: */
460: PetscErrorCode DMSwarmDataExPackInitialize(DMSwarmDataEx de, size_t unit_message_size)
461: {
462:   PetscMPIInt i, np;
463:   PetscInt    total;

465:   PetscFunctionBegin;
466:   PetscCheck(de->topology_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Topology not finalized");
467:   PetscCheck(de->message_lengths_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths not finalized");
468:   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerPack, 0, 0, 0, 0));
469:   de->packer_status = DEOBJECT_INITIALIZED;
470:   PetscCall(_DMSwarmDataExInitializeTmpStorage(de));
471:   np                    = de->n_neighbour_procs;
472:   de->unit_message_size = unit_message_size;
473:   total                 = 0;
474:   for (i = 0; i < np; ++i) {
475:     if (de->messages_to_be_sent[i] == -1) {
476:       PetscMPIInt proc_neighour = de->neighbour_procs[i];
477:       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ORDER, "Messages_to_be_sent[neighbour_proc=%d] is un-initialised. Call DMSwarmDataExSetSendCount() first", (int)proc_neighour);
478:     }
479:     total = total + de->messages_to_be_sent[i];
480:   }
481:   /* create space for the data to be sent */
482:   PetscCall(PetscMalloc(unit_message_size * (total + 1), &de->send_message));
483:   /* initialize memory */
484:   PetscCall(PetscMemzero(de->send_message, unit_message_size * (total + 1)));
485:   /* set total items to send */
486:   de->send_message_length = total;
487:   de->message_offsets[0]  = 0;
488:   total                   = de->messages_to_be_sent[0];
489:   for (i = 1; i < np; ++i) {
490:     de->message_offsets[i] = total;
491:     total                  = total + de->messages_to_be_sent[i];
492:   }
493:   /* init the packer counters */
494:   de->total_pack_cnt = 0;
495:   for (i = 0; i < np; ++i) de->pack_cnt[i] = 0;
496:   PetscFunctionReturn(PETSC_SUCCESS);
497: }

499: /*
500:     Ensures data gets been packed appropriately and no overlaps occur
501: */
502: PetscErrorCode DMSwarmDataExPackData(DMSwarmDataEx de, PetscMPIInt proc_id, PetscInt n, void *data)
503: {
504:   PetscMPIInt local;
505:   PetscInt    insert_location;
506:   void       *dest;

508:   PetscFunctionBegin;
509:   PetscCheck(de->packer_status != DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Packed data have been defined. To modify these call DMSwarmDataExInitializeSendCount(), DMSwarmDataExAddToSendCount(), DMSwarmDataExPackInitialize() first");
510:   PetscCheck(de->packer_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Packed data must be defined. Call DMSwarmDataExInitializeSendCount(), DMSwarmDataExAddToSendCount(), DMSwarmDataExPackInitialize() first");

512:   PetscCheck(de->send_message, de->comm, PETSC_ERR_ORDER, "send_message is not initialized. Call DMSwarmDataExPackInitialize() first");
513:   PetscCall(_DMSwarmDataExConvertProcIdToLocalIndex(de, proc_id, &local));
514:   PetscCheck(local != -1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "proc_id %d is not registered neighbour", (int)proc_id);
515:   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]);

517:   /* copy memory */
518:   insert_location = de->message_offsets[local] + de->pack_cnt[local];
519:   dest            = ((char *)de->send_message) + de->unit_message_size * insert_location;
520:   PetscCall(PetscMemcpy(dest, data, de->unit_message_size * n));
521:   /* increment counter */
522:   de->pack_cnt[local] = de->pack_cnt[local] + n;
523:   PetscFunctionReturn(PETSC_SUCCESS);
524: }

526: /*
527: *) Ensures all data has been packed
528: */
529: PetscErrorCode DMSwarmDataExPackFinalize(DMSwarmDataEx de)
530: {
531:   PetscMPIInt i, np;
532:   PetscInt    total;

534:   PetscFunctionBegin;
535:   PetscCheck(de->packer_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Packer has not been initialized. Must call DMSwarmDataExPackInitialize() first.");
536:   np = de->n_neighbour_procs;
537:   for (i = 0; i < np; ++i) {
538:     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]);
539:   }
540:   /* init */
541:   for (i = 0; i < np; ++i) de->messages_to_be_recvieved[i] = -1;
542:   /* figure out the recv counts here */
543:   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]));
544:   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]));
545:   PetscCallMPI(MPI_Waitall(2 * np, de->_requests, de->_stats));
546:   /* create space for the data to be recvieved */
547:   total = 0;
548:   for (i = 0; i < np; ++i) total = total + de->messages_to_be_recvieved[i];
549:   PetscCall(PetscMalloc(de->unit_message_size * (total + 1), &de->recv_message));
550:   /* initialize memory */
551:   PetscCall(PetscMemzero(de->recv_message, de->unit_message_size * (total + 1)));
552:   /* set total items to receive */
553:   de->recv_message_length  = total;
554:   de->packer_status        = DEOBJECT_FINALIZED;
555:   de->communication_status = DEOBJECT_INITIALIZED;
556:   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerPack, 0, 0, 0, 0));
557:   PetscFunctionReturn(PETSC_SUCCESS);
558: }

560: /* do the actual message passing */
561: PetscErrorCode DMSwarmDataExBegin(DMSwarmDataEx de)
562: {
563:   PetscMPIInt i, np;
564:   void       *dest;
565:   PetscInt    length;

567:   PetscFunctionBegin;
568:   PetscCheck(de->topology_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Topology not finalized");
569:   PetscCheck(de->message_lengths_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Message lengths not finalized");
570:   PetscCheck(de->packer_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Packer not finalized");
571:   PetscCheck(de->communication_status != DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ORDER, "Communication has already been finalized. Must call DMSwarmDataExInitialize() first.");
572:   PetscCheck(de->recv_message, de->comm, PETSC_ERR_ORDER, "recv_message has not been initialized. Must call DMSwarmDataExPackFinalize() first");
573:   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerBegin, 0, 0, 0, 0));
574:   np = de->n_neighbour_procs;
575:   /* == NON BLOCKING == */
576:   for (i = 0; i < np; ++i) {
577:     length = de->messages_to_be_sent[i] * de->unit_message_size;
578:     dest   = ((char *)de->send_message) + de->unit_message_size * de->message_offsets[i];
579:     PetscCallMPI(MPI_Isend(dest, length, MPI_CHAR, de->neighbour_procs[i], de->send_tags[i], de->comm, &de->_requests[i]));
580:   }
581:   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerBegin, 0, 0, 0, 0));
582:   PetscFunctionReturn(PETSC_SUCCESS);
583: }

585: /* do the actual message passing now */
586: PetscErrorCode DMSwarmDataExEnd(DMSwarmDataEx de)
587: {
588:   PetscMPIInt i, np;
589:   PetscInt    total;
590:   PetscInt   *message_recv_offsets;
591:   void       *dest;
592:   PetscInt    length;

594:   PetscFunctionBegin;
595:   PetscCheck(de->communication_status == DEOBJECT_INITIALIZED, de->comm, PETSC_ERR_ORDER, "Communication has not been initialized. Must call DMSwarmDataExInitialize() first.");
596:   PetscCheck(de->recv_message, de->comm, PETSC_ERR_ORDER, "recv_message has not been initialized. Must call DMSwarmDataExPackFinalize() first");
597:   PetscCall(PetscLogEventBegin(DMSWARM_DataExchangerEnd, 0, 0, 0, 0));
598:   np = de->n_neighbour_procs;
599:   PetscCall(PetscMalloc1(np + 1, &message_recv_offsets));
600:   message_recv_offsets[0] = 0;
601:   total                   = de->messages_to_be_recvieved[0];
602:   for (i = 1; i < np; ++i) {
603:     message_recv_offsets[i] = total;
604:     total                   = total + de->messages_to_be_recvieved[i];
605:   }
606:   /* == NON BLOCKING == */
607:   for (i = 0; i < np; ++i) {
608:     length = de->messages_to_be_recvieved[i] * de->unit_message_size;
609:     dest   = ((char *)de->recv_message) + de->unit_message_size * message_recv_offsets[i];
610:     PetscCallMPI(MPI_Irecv(dest, length, MPI_CHAR, de->neighbour_procs[i], de->recv_tags[i], de->comm, &de->_requests[np + i]));
611:   }
612:   PetscCallMPI(MPI_Waitall(2 * np, de->_requests, de->_stats));
613:   PetscCall(PetscFree(message_recv_offsets));
614:   de->communication_status = DEOBJECT_FINALIZED;
615:   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerEnd, 0, 0, 0, 0));
616:   PetscFunctionReturn(PETSC_SUCCESS);
617: }

619: PetscErrorCode DMSwarmDataExGetSendData(DMSwarmDataEx de, PetscInt *length, void **send)
620: {
621:   PetscFunctionBegin;
622:   PetscCheck(de->packer_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ARG_WRONGSTATE, "Data has not finished being packed.");
623:   *length = de->send_message_length;
624:   *send   = de->send_message;
625:   PetscFunctionReturn(PETSC_SUCCESS);
626: }

628: PetscErrorCode DMSwarmDataExGetRecvData(DMSwarmDataEx de, PetscInt *length, void **recv)
629: {
630:   PetscFunctionBegin;
631:   PetscCheck(de->communication_status == DEOBJECT_FINALIZED, de->comm, PETSC_ERR_ARG_WRONGSTATE, "Data has not finished being sent.");
632:   *length = de->recv_message_length;
633:   *recv   = de->recv_message;
634:   PetscFunctionReturn(PETSC_SUCCESS);
635: }

637: PetscErrorCode DMSwarmDataExTopologyGetNeighbours(DMSwarmDataEx de, PetscMPIInt *n, PetscMPIInt *neigh[])
638: {
639:   PetscFunctionBegin;
640:   if (n) *n = de->n_neighbour_procs;
641:   if (neigh) *neigh = de->neighbour_procs;
642:   PetscFunctionReturn(PETSC_SUCCESS);
643: }