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