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