xref: /petsc/src/mat/utils/matstash.c (revision 606d414c6e034e02e67059b83ebaefc3ebe99698)
1 #ifdef PETSC_RCS_HEADER
2 static char vcid[] = "$Id: matstash.c,v 1.34 1999/05/04 20:33:40 balay Exp balay $";
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(size*sizeof(int));CHKPTRQ(work);
451   ierr = MPI_Allreduce(procs,work,size,MPI_INT,MPI_SUM,comm);CHKERRQ(ierr);
452   nreceives = work[rank];
453   ierr = MPI_Allreduce(nprocs,work,size,MPI_INT,MPI_MAX,comm);CHKERRQ(ierr);
454   nmax = work[rank];
455   ierr = PetscFree(work);CHKERRQ(ierr);
456   /* post receives:
457      since we don't know how long each individual message is we
458      allocate the largest needed buffer for each receive. Potentially
459      this is a lot of wasted space.
460   */
461   rvalues    = (Scalar *)PetscMalloc((nreceives+1)*(nmax+1)*(bs2*sizeof(Scalar)+2*sizeof(int)));CHKPTRQ(rvalues);
462   rindices   = (int *) (rvalues + bs2*nreceives*nmax);
463   recv_waits = (MPI_Request *)PetscMalloc((nreceives+1)*2*sizeof(MPI_Request));CHKPTRQ(recv_waits);
464   for ( i=0,count=0; i<nreceives; i++ ) {
465     ierr = MPI_Irecv(rvalues+bs2*nmax*i,bs2*nmax,MPIU_SCALAR,MPI_ANY_SOURCE,tag1,comm,
466                      recv_waits+count++);CHKERRQ(ierr);
467     ierr = MPI_Irecv(rindices+2*nmax*i,2*nmax,MPI_INT,MPI_ANY_SOURCE,tag2,comm,
468                      recv_waits+count++);CHKERRQ(ierr);
469   }
470 
471   /* do sends:
472       1) starts[i] gives the starting index in svalues for stuff going to
473          the ith processor
474   */
475   svalues    = (Scalar *)PetscMalloc((stash->n+1)*(bs2*sizeof(Scalar)+2*sizeof(int)));CHKPTRQ(svalues);
476   sindices   = (int *) (svalues + bs2*stash->n);
477   send_waits = (MPI_Request *) PetscMalloc(2*(nsends+1)*sizeof(MPI_Request));CHKPTRQ(send_waits);
478   startv     = (int *) PetscMalloc(2*size*sizeof(int) );CHKPTRQ(startv);
479   starti     = startv + size;
480   /* use 2 sends the first with all_a, the next with all_i and all_j */
481   startv[0]  = 0; starti[0] = 0;
482   for ( i=1; i<size; i++ ) {
483     startv[i] = startv[i-1] + nprocs[i-1];
484     starti[i] = starti[i-1] + nprocs[i-1]*2;
485   }
486   for ( i=0; i<stash->n; i++ ) {
487     j = owner[i];
488     if (bs2 == 1) {
489       svalues[startv[j]]              = stash->array[i];
490     } else {
491       int    k;
492       Scalar *buf1,*buf2;
493       buf1 = svalues+bs2*startv[j];
494       buf2 = stash->array+bs2*i;
495       for ( k=0; k<bs2; k++ ){ buf1[k] = buf2[k]; }
496     }
497     sindices[starti[j]]             = stash->idx[i];
498     sindices[starti[j]+nprocs[j]]   = stash->idy[i];
499     startv[j]++;
500     starti[j]++;
501   }
502   startv[0] = 0;
503   for ( i=1; i<size; i++ ) { startv[i] = startv[i-1] + nprocs[i-1];}
504   for ( i=0,count=0; i<size; i++ ) {
505     if (procs[i]) {
506       ierr = MPI_Isend(svalues+bs2*startv[i],bs2*nprocs[i],MPIU_SCALAR,i,tag1,comm,
507                        send_waits+count++);CHKERRQ(ierr);
508       ierr = MPI_Isend(sindices+2*startv[i],2*nprocs[i],MPI_INT,i,tag2,comm,
509                        send_waits+count++);CHKERRQ(ierr);
510     }
511   }
512   ierr = PetscFree(owner);CHKERRQ(ierr);
513   ierr = PetscFree(startv);CHKERRQ(ierr);
514   /* This memory is reused in scatter end  for a different purpose*/
515   for (i=0; i<2*size; i++ ) nprocs[i] = -1;
516   stash->nprocs      = nprocs;
517 
518   stash->svalues    = svalues;    stash->rvalues    = rvalues;
519   stash->nsends     = nsends;     stash->nrecvs     = nreceives;
520   stash->send_waits = send_waits; stash->recv_waits = recv_waits;
521   stash->rmax       = nmax;
522   PetscFunctionReturn(0);
523 }
524 
525 /*
526    MatStashScatterGetMesg_Private - This function waits on the receives posted
527    in the function MatStashScatterBegin_Private() and returns one message at
528    a time to the calling function. If no messages are left, it indicates this
529    by setting flg = 0, else it sets flg = 1.
530 
531    Input Parameters:
532    stash - the stash
533 
534    Output Parameters:
535    nvals - the number of entries in the current message.
536    rows  - an array of row indices (or blocked indices) corresponding to the values
537    cols  - an array of columnindices (or blocked indices) corresponding to the values
538    vals  - the values
539    flg   - 0 indicates no more message left, and the current call has no values associated.
540            1 indicates that the current call successfully received a message, and the
541              other output parameters nvals,rows,cols,vals are set appropriately.
542 */
543 #undef __FUNC__
544 #define __FUNC__ "MatStashScatterGetMesg_Private"
545 int MatStashScatterGetMesg_Private(MatStash *stash,int *nvals,int **rows,int** cols,Scalar **vals,int *flg)
546 {
547   int         i,ierr,size=stash->size,*flg_v,*flg_i;
548   int         i1,i2,*rindices,match_found=0,bs2;
549   MPI_Status  recv_status;
550 
551   PetscFunctionBegin;
552 
553   *flg = 0; /* When a message is discovered this is reset to 1 */
554   /* Return if no more messages to process */
555   if (stash->nprocessed == stash->nrecvs) { PetscFunctionReturn(0); }
556 
557   flg_v = stash->nprocs;
558   flg_i = flg_v + size;
559   bs2   = stash->bs*stash->bs;
560   /* If a matching pair of receieves are found, process them, and return the data to
561      the calling function. Until then keep receiving messages */
562   while (!match_found) {
563     ierr = MPI_Waitany(2*stash->nrecvs,stash->recv_waits,&i,&recv_status);CHKERRQ(ierr);
564     /* Now pack the received message into a structure which is useable by others */
565     if (i % 2) {
566       ierr = MPI_Get_count(&recv_status,MPI_INT,nvals);CHKERRQ(ierr);
567       flg_i[recv_status.MPI_SOURCE] = i/2;
568       *nvals = *nvals/2; /* This message has both row indices and col indices */
569     } else {
570       ierr = MPI_Get_count(&recv_status,MPIU_SCALAR,nvals);CHKERRQ(ierr);
571       flg_v[recv_status.MPI_SOURCE] = i/2;
572       *nvals = *nvals/bs2;
573     }
574 
575     /* Check if we have both the messages from this proc */
576     i1 = flg_v[recv_status.MPI_SOURCE];
577     i2 = flg_i[recv_status.MPI_SOURCE];
578     if (i1 != -1 && i2 != -1) {
579       rindices    = (int *) (stash->rvalues + bs2*stash->rmax*stash->nrecvs);
580       *rows       = rindices + 2*i2*stash->rmax;
581       *cols       = *rows + *nvals;
582       *vals       = stash->rvalues + i1*bs2*stash->rmax;
583       *flg        = 1;
584       stash->nprocessed ++;
585       match_found = 1;
586     }
587   }
588   PetscFunctionReturn(0);
589 }
590