xref: /petsc/src/mat/utils/matstash.c (revision ac2a4f0d24b3b6a4ee93edbcad41f4bb9e923944)
1 /*$Id: matstash.c,v 1.35 1999/06/30 23:52:35 balay Exp bsmith $*/
2 
3 #include "src/mat/matimpl.h"
4 
5 #define DEFAULT_STASH_SIZE   10000
6 
7 /*
8   MatStashCreate_Private - Creates a stash ,currently used for all the parallel
9   matrix implementations. The stash is where elements of a matrix destined
10   to be stored on other processors are kept until matrix assembly is done.
11 
12   This is a simple minded stash. Simply adds entries to end of stash.
13 
14   Input Parameters:
15   comm - communicator, required for scatters.
16   bs   - stash block size. used when stashing blocks of values
17 
18   Output Parameters:
19   stash    - the newly created stash
20 */
21 #undef __FUNC__
22 #define __FUNC__ "MatStashCreate_Private"
23 int MatStashCreate_Private(MPI_Comm comm,int bs, MatStash *stash)
24 {
25   int ierr,flg,max,*opt,nopt;
26 
27   PetscFunctionBegin;
28   /* Require 2 tags, get the second using PetscCommGetNewTag() */
29   ierr = PetscCommDuplicate_Private(comm,&stash->comm,&stash->tag1);CHKERRQ(ierr);
30   ierr = PetscCommGetNewTag(stash->comm,&stash->tag2);CHKERRQ(ierr);
31   ierr = MPI_Comm_size(stash->comm,&stash->size);CHKERRQ(ierr);
32   ierr = MPI_Comm_rank(stash->comm,&stash->rank);CHKERRQ(ierr);
33 
34   nopt = stash->size;
35   opt  = (int*) PetscMalloc(nopt*sizeof(int));CHKPTRQ(opt);
36   ierr = OptionsGetIntArray(PETSC_NULL,"-vecstash_initial_size",opt,&nopt,&flg);CHKERRQ(ierr);
37   if (flg) {
38     if (nopt == 1)                max = opt[0];
39     else if (nopt == stash->size) max = opt[stash->rank];
40     else if (stash->rank < nopt)  max = opt[stash->rank];
41     else                          max = 0; /* Use default */
42     stash->umax = max;
43   } else {
44     stash->umax = 0;
45   }
46   ierr = PetscFree(opt);CHKERRQ(ierr);
47   if (bs <= 0) bs = 1;
48 
49   stash->bs       = bs;
50   stash->nmax     = 0;
51   stash->oldnmax  = 0;
52   stash->n        = 0;
53   stash->reallocs = -1;
54   stash->idx      = 0;
55   stash->idy      = 0;
56   stash->array    = 0;
57 
58   stash->send_waits  = 0;
59   stash->recv_waits  = 0;
60   stash->send_status = 0;
61   stash->nsends      = 0;
62   stash->nrecvs      = 0;
63   stash->svalues     = 0;
64   stash->rvalues     = 0;
65   stash->rmax        = 0;
66   stash->nprocs      = 0;
67   stash->nprocessed  = 0;
68   PetscFunctionReturn(0);
69 }
70 
71 /*
72    MatStashDestroy_Private - Destroy the stash
73 */
74 #undef __FUNC__
75 #define __FUNC__ "MatStashDestroy_Private"
76 int MatStashDestroy_Private(MatStash *stash)
77 {
78   int ierr;
79 
80   PetscFunctionBegin;
81   ierr = PetscCommDestroy_Private(&stash->comm);CHKERRQ(ierr);
82   if (stash->array) {
83     ierr = PetscFree(stash->array);CHKERRQ(ierr);
84     stash->array = 0;
85   }
86   PetscFunctionReturn(0);
87 }
88 
89 /*
90    MatStashScatterEnd_Private - This is called as the fial stage of
91    scatter. The final stages of messagepassing is done here, and
92    all the memory used for messagepassing is cleanedu up. This
93    routine also resets the stash, and deallocates the memory used
94    for the stash. It also keeps track of the current memory usage
95    so that the same value can be used the next time through.
96 */
97 #undef __FUNC__
98 #define __FUNC__ "MatStashScatterEnd_Private"
99 int MatStashScatterEnd_Private(MatStash *stash)
100 {
101   int         nsends=stash->nsends,ierr,bs2,oldnmax;
102   MPI_Status  *send_status;
103 
104   PetscFunctionBegin;
105   /* wait on sends */
106   if (nsends) {
107     send_status = (MPI_Status *)PetscMalloc(2*nsends*sizeof(MPI_Status));CHKPTRQ(send_status);
108     ierr        = MPI_Waitall(2*nsends,stash->send_waits,send_status);CHKERRQ(ierr);
109     ierr        = PetscFree(send_status);CHKERRQ(ierr);
110   }
111 
112   /* Now update nmaxold to be app 10% more than max n used, this way the
113      wastage of space is reduced the next time this stash is used.
114      Also update the oldmax, only if it increases */
115   bs2      = stash->bs*stash->bs;
116   oldnmax  = ((int)(stash->n * 1.1) + 5)*bs2;
117   if (oldnmax > stash->oldnmax) stash->oldnmax = oldnmax;
118 
119   stash->nmax       = 0;
120   stash->n          = 0;
121   stash->reallocs   = -1;
122   stash->rmax       = 0;
123   stash->nprocessed = 0;
124 
125   if (stash->array) {
126     ierr         = PetscFree(stash->array);CHKERRQ(ierr);
127     stash->array = 0;
128     stash->idx   = 0;
129     stash->idy   = 0;
130   }
131   if (stash->send_waits) {
132     ierr = PetscFree(stash->send_waits);CHKERRQ(ierr);
133     stash->send_waits = 0;
134   }
135   if (stash->recv_waits) {
136     ierr = PetscFree(stash->recv_waits);CHKERRQ(ierr);
137     stash->recv_waits = 0;
138   }
139   if (stash->svalues) {
140     ierr = PetscFree(stash->svalues);CHKERRQ(ierr);
141     stash->svalues = 0;
142   }
143   if (stash->rvalues) {
144     ierr = PetscFree(stash->rvalues);CHKERRQ(ierr);
145     stash->rvalues = 0;
146   }
147   if (stash->nprocs) {
148     ierr = PetscFree(stash->nprocs);
149     stash->nprocs = 0;
150   }
151 
152   PetscFunctionReturn(0);
153 }
154 
155 /*
156    MatStashGetInfo_Private - Gets the relavant statistics of the stash
157 
158    Input Parameters:
159    stash    - the stash
160    nstash   - the size of the stash. Indicates the number of values stored.
161    reallocs - the number of additional mallocs incurred.
162 
163 */
164 #undef __FUNC__
165 #define __FUNC__ "MatStashGetInfo_Private"
166 int MatStashGetInfo_Private(MatStash *stash,int *nstash, int *reallocs)
167 {
168   int bs2 = stash->bs*stash->bs;
169 
170   PetscFunctionBegin;
171   *nstash   = stash->n*bs2;
172   if (stash->reallocs < 0) *reallocs = 0;
173   else                     *reallocs = stash->reallocs;
174   PetscFunctionReturn(0);
175 }
176 
177 
178 /*
179    MatStashSetInitialSize_Private - Sets the initial size of the stash
180 
181    Input Parameters:
182    stash  - the stash
183    max    - the value that is used as the max size of the stash.
184             this value is used while allocating memory.
185 */
186 #undef __FUNC__
187 #define __FUNC__ "MatStashSetInitialSize_Private"
188 int MatStashSetInitialSize_Private(MatStash *stash,int max)
189 {
190   PetscFunctionBegin;
191   stash->umax = max;
192   PetscFunctionReturn(0);
193 }
194 
195 /* MatStashExpand_Private - Expand the stash. This function is called
196    when the space in the stash is not sufficient to add the new values
197    being inserted into the stash.
198 
199    Input Parameters:
200    stash - the stash
201    incr  - the minimum increase requested
202 
203    Notes:
204    This routine doubles the currently used memory.
205  */
206 #undef __FUNC__
207 #define __FUNC__ "MatStashExpand_Private"
208 static int MatStashExpand_Private(MatStash *stash,int incr)
209 {
210   int    *n_idx,*n_idy,newnmax,bs2,ierr;
211   Scalar *n_array;
212 
213   PetscFunctionBegin;
214   /* allocate a larger stash */
215   bs2     = stash->bs*stash->bs;
216   if (!stash->oldnmax && !stash->nmax) { /* new stash */
217     if (stash->umax)                  newnmax = stash->umax/bs2;
218     else                              newnmax = DEFAULT_STASH_SIZE/bs2;
219   } else if (!stash->nmax) { /* resuing stash */
220     if (stash->umax > stash->oldnmax) newnmax = stash->umax/bs2;
221     else                              newnmax = stash->oldnmax/bs2;
222   } else                              newnmax = stash->nmax*2;
223   if (newnmax  < (stash->nmax + incr)) newnmax += 2*incr;
224 
225   n_array = (Scalar *)PetscMalloc((newnmax)*(2*sizeof(int)+bs2*sizeof(Scalar)));CHKPTRQ(n_array);
226   n_idx   = (int *) (n_array + bs2*newnmax);
227   n_idy   = (int *) (n_idx + newnmax);
228   ierr = PetscMemcpy(n_array,stash->array,bs2*stash->nmax*sizeof(Scalar));CHKERRQ(ierr);
229   ierr = PetscMemcpy(n_idx,stash->idx,stash->nmax*sizeof(int));CHKERRQ(ierr);
230   ierr = PetscMemcpy(n_idy,stash->idy,stash->nmax*sizeof(int));CHKERRQ(ierr);
231   if (stash->array) {ierr = PetscFree(stash->array);CHKERRQ(ierr);}
232   stash->array   = n_array;
233   stash->idx     = n_idx;
234   stash->idy     = n_idy;
235   stash->nmax    = newnmax;
236   stash->reallocs++;
237   PetscFunctionReturn(0);
238 }
239 /*
240   MatStashValuesRow_Private - inserts values into the stash. This function
241   expects the values to be roworiented. Multiple columns belong to the same row
242   can be inserted with a single call to this function.
243 
244   Input Parameters:
245   stash  - the stash
246   row    - the global row correspoiding to the values
247   n      - the number of elements inserted. All elements belong to the above row.
248   idxn   - the global column indices corresponding to each of the values.
249   values - the values inserted
250 */
251 #undef __FUNC__
252 #define __FUNC__ "MatStashValuesRow_Private"
253 int MatStashValuesRow_Private(MatStash *stash,int row,int n, int *idxn,Scalar *values)
254 {
255   int    ierr,i;
256 
257   PetscFunctionBegin;
258   /* Check and see if we have sufficient memory */
259   if ((stash->n + n) > stash->nmax) {
260     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
261   }
262   for ( i=0; i<n; i++ ) {
263     stash->idx[stash->n]   = row;
264     stash->idy[stash->n]   = idxn[i];
265     stash->array[stash->n] = values[i];
266     stash->n++;
267   }
268   PetscFunctionReturn(0);
269 }
270 /*
271   MatStashValuesCol_Private - inserts values into the stash. This function
272   expects the values to be columnoriented. Multiple columns belong to the same row
273   can be inserted with a single call to this function.
274 
275   Input Parameters:
276   stash   - the stash
277   row     - the global row correspoiding to the values
278   n       - the number of elements inserted. All elements belong to the above row.
279   idxn    - the global column indices corresponding to each of the values.
280   values  - the values inserted
281   stepval - the consecutive values are sepated by a distance of stepval.
282             this happens because the input is columnoriented.
283 */
284 #undef __FUNC__
285 #define __FUNC__ "MatStashValuesCol_Private"
286 int MatStashValuesCol_Private(MatStash *stash,int row,int n, int *idxn,
287                                       Scalar *values,int stepval)
288 {
289   int    ierr,i;
290 
291   PetscFunctionBegin;
292   /* Check and see if we have sufficient memory */
293   if ((stash->n + n) > stash->nmax) {
294     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
295   }
296   for ( i=0; i<n; i++ ) {
297     stash->idx[stash->n]   = row;
298     stash->idy[stash->n]   = idxn[i];
299     stash->array[stash->n] = values[i*stepval];
300     stash->n++;
301   }
302   PetscFunctionReturn(0);
303 }
304 
305 /*
306   MatStashValuesRowBlocked_Private - inserts blocks of values into the stash.
307   This function expects the values to be roworiented. Multiple columns belong
308   to the same block-row can be inserted with a single call to this function.
309   This function extracts the sub-block of values based on the dimensions of
310   the original input block, and the row,col values corresponding to the blocks.
311 
312   Input Parameters:
313   stash  - the stash
314   row    - the global block-row correspoiding to the values
315   n      - the number of elements inserted. All elements belong to the above row.
316   idxn   - the global block-column indices corresponding to each of the blocks of
317            values. Each block is of size bs*bs.
318   values - the values inserted
319   rmax   - the number of block-rows in the original block.
320   cmax   - the number of block-columsn on the original block.
321   idx    - the index of the current block-row in the original block.
322 */
323 #undef __FUNC__
324 #define __FUNC__ "MatStashValuesRowBlocked_Private"
325 int MatStashValuesRowBlocked_Private(MatStash *stash,int row,int n,int *idxn,Scalar *values,
326                                int rmax,int cmax,int idx)
327 {
328   int    ierr,i,j,k,bs2,bs=stash->bs;
329   Scalar *vals,*array;
330 
331   PetscFunctionBegin;
332   bs2 = bs*bs;
333   if ((stash->n+n) > stash->nmax) {
334     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
335   }
336   for ( i=0; i<n; i++ ) {
337     stash->idx[stash->n]   = row;
338     stash->idy[stash->n] = idxn[i];
339     /* Now copy over the block of values. Store the values column oriented.
340        This enables inserting multiple blocks belonging to a row with a single
341        funtion call */
342     array = stash->array + bs2*stash->n;
343     vals  = values + idx*bs2*n + bs*i;
344     for ( j=0; j<bs; j++ ) {
345       for ( k=0; k<bs; k++ ) {array[k*bs] = vals[k];}
346       array += 1;
347       vals  += cmax*bs;
348     }
349     stash->n++;
350   }
351   PetscFunctionReturn(0);
352 }
353 
354 /*
355   MatStashValuesColBlocked_Private - inserts blocks of values into the stash.
356   This function expects the values to be roworiented. Multiple columns belong
357   to the same block-row can be inserted with a single call to this function.
358   This function extracts the sub-block of values based on the dimensions of
359   the original input block, and the row,col values corresponding to the blocks.
360 
361   Input Parameters:
362   stash  - the stash
363   row    - the global block-row correspoiding to the values
364   n      - the number of elements inserted. All elements belong to the above row.
365   idxn   - the global block-column indices corresponding to each of the blocks of
366            values. Each block is of size bs*bs.
367   values - the values inserted
368   rmax   - the number of block-rows in the original block.
369   cmax   - the number of block-columsn on the original block.
370   idx    - the index of the current block-row in the original block.
371 */
372 #undef __FUNC__
373 #define __FUNC__ "MatStashValuesColBlocked_Private"
374 int MatStashValuesColBlocked_Private(MatStash *stash,int row,int n,int *idxn,
375                                              Scalar *values,int rmax,int cmax,int idx)
376 {
377   int    ierr,i,j,k,bs2,bs=stash->bs;
378   Scalar *vals,*array;
379 
380   PetscFunctionBegin;
381   bs2 = bs*bs;
382   if ((stash->n+n) > stash->nmax) {
383     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
384   }
385   for ( i=0; i<n; i++ ) {
386     stash->idx[stash->n]   = row;
387     stash->idy[stash->n] = idxn[i];
388     /* Now copy over the block of values. Store the values column oriented.
389      This enables inserting multiple blocks belonging to a row with a single
390      funtion call */
391     array = stash->array + bs2*stash->n;
392     vals  = values + idx*bs + bs2*rmax*i;
393     for ( j=0; j<bs; j++ ) {
394       for ( k=0; k<bs; k++ ) {array[k] = vals[k];}
395       array += bs;
396       vals  += rmax*bs;
397     }
398     stash->n++;
399   }
400   PetscFunctionReturn(0);
401 }
402 /*
403   MatStashScatterBegin_Private - Initiates the transfer of values to the
404   correct owners. This function goes through the stash, and check the
405   owners of each stashed value, and sends the values off to the owner
406   processors.
407 
408   Input Parameters:
409   stash  - the stash
410   owners - an array of size 'no-of-procs' which gives the ownership range
411            for each node.
412 
413   Notes: The 'owners' array in the cased of the blocked-stash has the
414   ranges specified blocked global indices, and for the regular stash in
415   the proper global indices.
416 */
417 #undef __FUNC__
418 #define __FUNC__ "MatStashScatterBegin_Private"
419 int MatStashScatterBegin_Private(MatStash *stash,int *owners)
420 {
421   int         *owner,*startv,*starti,tag1=stash->tag1,tag2=stash->tag2,bs2;
422   int         rank=stash->rank,size=stash->size,*nprocs,*procs,nsends,nreceives;
423   int         nmax,*work,count,ierr,*sindices,*rindices,i,j,idx;
424   Scalar      *rvalues,*svalues;
425   MPI_Comm    comm = stash->comm;
426   MPI_Request *send_waits,*recv_waits;
427 
428   PetscFunctionBegin;
429 
430   bs2 = stash->bs*stash->bs;
431   /*  first count number of contributors to each processor */
432   nprocs = (int *) PetscMalloc( 2*size*sizeof(int) );CHKPTRQ(nprocs);
433   ierr   = PetscMemzero(nprocs,2*size*sizeof(int));CHKERRQ(ierr);
434   procs  = nprocs + size;
435   owner  = (int *) PetscMalloc( (stash->n+1)*sizeof(int) );CHKPTRQ(owner);
436 
437   for ( i=0; i<stash->n; i++ ) {
438     idx = stash->idx[i];
439     for ( j=0; j<size; j++ ) {
440       if (idx >= owners[j] && idx < owners[j+1]) {
441         nprocs[j]++; procs[j] = 1; owner[i] = j; break;
442       }
443     }
444   }
445   nsends = 0;  for ( i=0; i<size; i++ ) { nsends += procs[i];}
446 
447   /* inform other processors of number of messages and max length*/
448   work      = (int *)PetscMalloc(2*size*sizeof(int));CHKPTRQ(work);
449   ierr      = MPI_Allreduce(nprocs,work,2*size,MPI_INT,PetscMaxSum_Op,comm);CHKERRQ(ierr);
450   nmax      = work[rank];
451   nreceives = work[size+rank];
452   ierr      = PetscFree(work);CHKERRQ(ierr);
453   /* post receives:
454      since we don't know how long each individual message is we
455      allocate the largest needed buffer for each receive. Potentially
456      this is a lot of wasted space.
457   */
458   rvalues    = (Scalar *)PetscMalloc((nreceives+1)*(nmax+1)*(bs2*sizeof(Scalar)+2*sizeof(int)));CHKPTRQ(rvalues);
459   rindices   = (int *) (rvalues + bs2*nreceives*nmax);
460   recv_waits = (MPI_Request *)PetscMalloc((nreceives+1)*2*sizeof(MPI_Request));CHKPTRQ(recv_waits);
461   for ( i=0,count=0; i<nreceives; i++ ) {
462     ierr = MPI_Irecv(rvalues+bs2*nmax*i,bs2*nmax,MPIU_SCALAR,MPI_ANY_SOURCE,tag1,comm,
463                      recv_waits+count++);CHKERRQ(ierr);
464     ierr = MPI_Irecv(rindices+2*nmax*i,2*nmax,MPI_INT,MPI_ANY_SOURCE,tag2,comm,
465                      recv_waits+count++);CHKERRQ(ierr);
466   }
467 
468   /* do sends:
469       1) starts[i] gives the starting index in svalues for stuff going to
470          the ith processor
471   */
472   svalues    = (Scalar *)PetscMalloc((stash->n+1)*(bs2*sizeof(Scalar)+2*sizeof(int)));CHKPTRQ(svalues);
473   sindices   = (int *) (svalues + bs2*stash->n);
474   send_waits = (MPI_Request *) PetscMalloc(2*(nsends+1)*sizeof(MPI_Request));CHKPTRQ(send_waits);
475   startv     = (int *) PetscMalloc(2*size*sizeof(int) );CHKPTRQ(startv);
476   starti     = startv + size;
477   /* use 2 sends the first with all_a, the next with all_i and all_j */
478   startv[0]  = 0; starti[0] = 0;
479   for ( i=1; i<size; i++ ) {
480     startv[i] = startv[i-1] + nprocs[i-1];
481     starti[i] = starti[i-1] + nprocs[i-1]*2;
482   }
483   for ( i=0; i<stash->n; i++ ) {
484     j = owner[i];
485     if (bs2 == 1) {
486       svalues[startv[j]]              = stash->array[i];
487     } else {
488       int    k;
489       Scalar *buf1,*buf2;
490       buf1 = svalues+bs2*startv[j];
491       buf2 = stash->array+bs2*i;
492       for ( k=0; k<bs2; k++ ){ buf1[k] = buf2[k]; }
493     }
494     sindices[starti[j]]             = stash->idx[i];
495     sindices[starti[j]+nprocs[j]]   = stash->idy[i];
496     startv[j]++;
497     starti[j]++;
498   }
499   startv[0] = 0;
500   for ( i=1; i<size; i++ ) { startv[i] = startv[i-1] + nprocs[i-1];}
501   for ( i=0,count=0; i<size; i++ ) {
502     if (procs[i]) {
503       ierr = MPI_Isend(svalues+bs2*startv[i],bs2*nprocs[i],MPIU_SCALAR,i,tag1,comm,
504                        send_waits+count++);CHKERRQ(ierr);
505       ierr = MPI_Isend(sindices+2*startv[i],2*nprocs[i],MPI_INT,i,tag2,comm,
506                        send_waits+count++);CHKERRQ(ierr);
507     }
508   }
509   ierr = PetscFree(owner);CHKERRQ(ierr);
510   ierr = PetscFree(startv);CHKERRQ(ierr);
511   /* This memory is reused in scatter end  for a different purpose*/
512   for (i=0; i<2*size; i++ ) nprocs[i] = -1;
513   stash->nprocs      = nprocs;
514 
515   stash->svalues    = svalues;    stash->rvalues    = rvalues;
516   stash->nsends     = nsends;     stash->nrecvs     = nreceives;
517   stash->send_waits = send_waits; stash->recv_waits = recv_waits;
518   stash->rmax       = nmax;
519   PetscFunctionReturn(0);
520 }
521 
522 /*
523    MatStashScatterGetMesg_Private - This function waits on the receives posted
524    in the function MatStashScatterBegin_Private() and returns one message at
525    a time to the calling function. If no messages are left, it indicates this
526    by setting flg = 0, else it sets flg = 1.
527 
528    Input Parameters:
529    stash - the stash
530 
531    Output Parameters:
532    nvals - the number of entries in the current message.
533    rows  - an array of row indices (or blocked indices) corresponding to the values
534    cols  - an array of columnindices (or blocked indices) corresponding to the values
535    vals  - the values
536    flg   - 0 indicates no more message left, and the current call has no values associated.
537            1 indicates that the current call successfully received a message, and the
538              other output parameters nvals,rows,cols,vals are set appropriately.
539 */
540 #undef __FUNC__
541 #define __FUNC__ "MatStashScatterGetMesg_Private"
542 int MatStashScatterGetMesg_Private(MatStash *stash,int *nvals,int **rows,int** cols,Scalar **vals,int *flg)
543 {
544   int         i,ierr,size=stash->size,*flg_v,*flg_i;
545   int         i1,i2,*rindices,match_found=0,bs2;
546   MPI_Status  recv_status;
547 
548   PetscFunctionBegin;
549 
550   *flg = 0; /* When a message is discovered this is reset to 1 */
551   /* Return if no more messages to process */
552   if (stash->nprocessed == stash->nrecvs) { PetscFunctionReturn(0); }
553 
554   flg_v = stash->nprocs;
555   flg_i = flg_v + size;
556   bs2   = stash->bs*stash->bs;
557   /* If a matching pair of receieves are found, process them, and return the data to
558      the calling function. Until then keep receiving messages */
559   while (!match_found) {
560     ierr = MPI_Waitany(2*stash->nrecvs,stash->recv_waits,&i,&recv_status);CHKERRQ(ierr);
561     /* Now pack the received message into a structure which is useable by others */
562     if (i % 2) {
563       ierr = MPI_Get_count(&recv_status,MPI_INT,nvals);CHKERRQ(ierr);
564       flg_i[recv_status.MPI_SOURCE] = i/2;
565       *nvals = *nvals/2; /* This message has both row indices and col indices */
566     } else {
567       ierr = MPI_Get_count(&recv_status,MPIU_SCALAR,nvals);CHKERRQ(ierr);
568       flg_v[recv_status.MPI_SOURCE] = i/2;
569       *nvals = *nvals/bs2;
570     }
571 
572     /* Check if we have both the messages from this proc */
573     i1 = flg_v[recv_status.MPI_SOURCE];
574     i2 = flg_i[recv_status.MPI_SOURCE];
575     if (i1 != -1 && i2 != -1) {
576       rindices    = (int *) (stash->rvalues + bs2*stash->rmax*stash->nrecvs);
577       *rows       = rindices + 2*i2*stash->rmax;
578       *cols       = *rows + *nvals;
579       *vals       = stash->rvalues + i1*bs2*stash->rmax;
580       *flg        = 1;
581       stash->nprocessed ++;
582       match_found = 1;
583     }
584   }
585   PetscFunctionReturn(0);
586 }
587