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>
     73: #include <petsc/private/petscimpl.h>

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

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

     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;

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

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

     94:   d->instance = count;

     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;

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

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

    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;

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

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

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

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

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

    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]));

    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:   }

    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: }

    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: }

    180: /* === Phase A === */

    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: }

    197: PetscErrorCode DMSwarmDataExTopologyAddNeighbour(DMSwarmDataEx d, const PetscMPIInt proc_id)
    198: {
    199:   PetscMPIInt n, found;
    200:   PetscMPIInt size;

    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");

    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: }

    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

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

    233: 0 ==> 0         e=0
    234: 0 ==> 1         e=1
    235: 0 ==> 2         e=2
    236: 0 ==> 3         e=3

    238: 1 ==> 0         e=4
    239: 1 ==> 1         e=5
    240: 1 ==> 2         e=6
    241: 1 ==> 3         e=7

    243: 2 ==> 0         e=8
    244: 2 ==> 1         e=9
    245: 2 ==> 2         e=10
    246: 2 ==> 3         e=11

    248: 3 ==> 0         e=12
    249: 3 ==> 1         e=13
    250: 3 ==> 2         e=14
    251: 3 ==> 3         e=15

    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

    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;

    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: }

    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;

    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;

    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: }

    331: PetscErrorCode DMSwarmDataExTopologyFinalize(DMSwarmDataEx d)
    332: {
    333:   PetscMPIInt symm_nn = 0, *symm_procs = NULL, r0, n, st, rt, size, *maxtag, iflg;

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

    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];

    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: }

    371: /* === Phase B === */
    372: static PetscErrorCode _DMSwarmDataExConvertProcIdToLocalIndex(DMSwarmDataEx de, PetscMPIInt proc_id, PetscMPIInt *local)
    373: {
    374:   PetscMPIInt i, np;

    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: }

    388: PetscErrorCode DMSwarmDataExInitializeSendCount(DMSwarmDataEx de)
    389: {
    390:   PetscMPIInt i;

    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: }

    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;

    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");

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

    414:   de->messages_to_be_sent[local_val] = de->messages_to_be_sent[local_val] + count;
    415:   PetscFunctionReturn(PETSC_SUCCESS);
    416: }

    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");

    423:   de->message_lengths_status = DEOBJECT_FINALIZED;
    424:   PetscCall(PetscLogEventEnd(DMSWARM_DataExchangerSendCount, 0, 0, 0, 0));
    425:   PetscFunctionReturn(PETSC_SUCCESS);
    426: }

    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;

    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: }

    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;

    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: }

    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;

    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");

    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]);

    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: }

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

    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: }

    557: /* do the actual message passing */
    558: PetscErrorCode DMSwarmDataExBegin(DMSwarmDataEx de)
    559: {
    560:   PetscMPIInt i, np;
    561:   void       *dest;

    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: }

    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;

    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: }

    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: }

    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: }

    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: }