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