xref: /petsc/src/mat/utils/matstash.c (revision 25922d2b4d5ad70bed352f4782abcd1a61dd4c80)
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   *nstash   = stash->n*bs2;
179   if (stash->reallocs < 0) *reallocs = 0;
180   else                     *reallocs = stash->reallocs;
181   PetscFunctionReturn(0);
182 }
183 
184 
185 /*
186    MatStashSetInitialSize_Private - Sets the initial size of the stash
187 
188    Input Parameters:
189    stash  - the stash
190    max    - the value that is used as the max size of the stash.
191             this value is used while allocating memory.
192 */
193 #undef __FUNCT__
194 #define __FUNCT__ "MatStashSetInitialSize_Private"
195 int MatStashSetInitialSize_Private(MatStash *stash,int max)
196 {
197   PetscFunctionBegin;
198   stash->umax = max;
199   PetscFunctionReturn(0);
200 }
201 
202 /* MatStashExpand_Private - Expand the stash. This function is called
203    when the space in the stash is not sufficient to add the new values
204    being inserted into the stash.
205 
206    Input Parameters:
207    stash - the stash
208    incr  - the minimum increase requested
209 
210    Notes:
211    This routine doubles the currently used memory.
212  */
213 #undef __FUNCT__
214 #define __FUNCT__ "MatStashExpand_Private"
215 static int MatStashExpand_Private(MatStash *stash,int incr)
216 {
217   int       *n_idx,*n_idy,newnmax,bs2,ierr;
218   MatScalar *n_array;
219 
220   PetscFunctionBegin;
221   /* allocate a larger stash */
222   bs2     = stash->bs*stash->bs;
223   if (!stash->oldnmax && !stash->nmax) { /* new stash */
224     if (stash->umax)                  newnmax = stash->umax/bs2;
225     else                              newnmax = DEFAULT_STASH_SIZE/bs2;
226   } else if (!stash->nmax) { /* resuing stash */
227     if (stash->umax > stash->oldnmax) newnmax = stash->umax/bs2;
228     else                              newnmax = stash->oldnmax/bs2;
229   } else                              newnmax = stash->nmax*2;
230   if (newnmax  < (stash->nmax + incr)) newnmax += 2*incr;
231 
232   ierr  = PetscMalloc((newnmax)*(2*sizeof(int)+bs2*sizeof(MatScalar)),&n_array);CHKERRQ(ierr);
233   n_idx = (int*)(n_array + bs2*newnmax);
234   n_idy = (int*)(n_idx + newnmax);
235   ierr  = PetscMemcpy(n_array,stash->array,bs2*stash->nmax*sizeof(MatScalar));CHKERRQ(ierr);
236   ierr  = PetscMemcpy(n_idx,stash->idx,stash->nmax*sizeof(int));CHKERRQ(ierr);
237   ierr  = PetscMemcpy(n_idy,stash->idy,stash->nmax*sizeof(int));CHKERRQ(ierr);
238   if (stash->array) {ierr = PetscFree(stash->array);CHKERRQ(ierr);}
239   stash->array   = n_array;
240   stash->idx     = n_idx;
241   stash->idy     = n_idy;
242   stash->nmax    = newnmax;
243   stash->reallocs++;
244   PetscFunctionReturn(0);
245 }
246 /*
247   MatStashValuesRow_Private - inserts values into the stash. This function
248   expects the values to be roworiented. Multiple columns belong to the same row
249   can be inserted with a single call to this function.
250 
251   Input Parameters:
252   stash  - the stash
253   row    - the global row correspoiding to the values
254   n      - the number of elements inserted. All elements belong to the above row.
255   idxn   - the global column indices corresponding to each of the values.
256   values - the values inserted
257 */
258 #undef __FUNCT__
259 #define __FUNCT__ "MatStashValuesRow_Private"
260 int MatStashValuesRow_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[])
261 {
262   int    ierr,i;
263 
264   PetscFunctionBegin;
265   /* Check and see if we have sufficient memory */
266   if ((stash->n + n) > stash->nmax) {
267     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
268   }
269   for (i=0; i<n; i++) {
270     stash->idx[stash->n]   = row;
271     stash->idy[stash->n]   = idxn[i];
272     stash->array[stash->n] = values[i];
273     stash->n++;
274   }
275   PetscFunctionReturn(0);
276 }
277 /*
278   MatStashValuesCol_Private - inserts values into the stash. This function
279   expects the values to be columnoriented. Multiple columns belong to the same row
280   can be inserted with a single call to this function.
281 
282   Input Parameters:
283   stash   - the stash
284   row     - the global row correspoiding to the values
285   n       - the number of elements inserted. All elements belong to the above row.
286   idxn    - the global column indices corresponding to each of the values.
287   values  - the values inserted
288   stepval - the consecutive values are sepated by a distance of stepval.
289             this happens because the input is columnoriented.
290 */
291 #undef __FUNCT__
292 #define __FUNCT__ "MatStashValuesCol_Private"
293 int MatStashValuesCol_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int stepval)
294 {
295   int    ierr,i;
296 
297   PetscFunctionBegin;
298   /* Check and see if we have sufficient memory */
299   if ((stash->n + n) > stash->nmax) {
300     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
301   }
302   for (i=0; i<n; i++) {
303     stash->idx[stash->n]   = row;
304     stash->idy[stash->n]   = idxn[i];
305     stash->array[stash->n] = values[i*stepval];
306     stash->n++;
307   }
308   PetscFunctionReturn(0);
309 }
310 
311 /*
312   MatStashValuesRowBlocked_Private - inserts blocks of values into the stash.
313   This function expects the values to be roworiented. Multiple columns belong
314   to the same block-row can be inserted with a single call to this function.
315   This function extracts the sub-block of values based on the dimensions of
316   the original input block, and the row,col values corresponding to the blocks.
317 
318   Input Parameters:
319   stash  - the stash
320   row    - the global block-row correspoiding to the values
321   n      - the number of elements inserted. All elements belong to the above row.
322   idxn   - the global block-column indices corresponding to each of the blocks of
323            values. Each block is of size bs*bs.
324   values - the values inserted
325   rmax   - the number of block-rows in the original block.
326   cmax   - the number of block-columsn on the original block.
327   idx    - the index of the current block-row in the original block.
328 */
329 #undef __FUNCT__
330 #define __FUNCT__ "MatStashValuesRowBlocked_Private"
331 int MatStashValuesRowBlocked_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int rmax,int cmax,int idx)
332 {
333   int             ierr,i,j,k,bs2,bs=stash->bs;
334   const MatScalar *vals;
335   MatScalar       *array;
336 
337   PetscFunctionBegin;
338   bs2 = bs*bs;
339   if ((stash->n+n) > stash->nmax) {
340     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
341   }
342   for (i=0; i<n; i++) {
343     stash->idx[stash->n]   = row;
344     stash->idy[stash->n] = idxn[i];
345     /* Now copy over the block of values. Store the values column oriented.
346        This enables inserting multiple blocks belonging to a row with a single
347        funtion call */
348     array = stash->array + bs2*stash->n;
349     vals  = values + idx*bs2*n + bs*i;
350     for (j=0; j<bs; j++) {
351       for (k=0; k<bs; k++) {array[k*bs] = vals[k];}
352       array += 1;
353       vals  += cmax*bs;
354     }
355     stash->n++;
356   }
357   PetscFunctionReturn(0);
358 }
359 
360 /*
361   MatStashValuesColBlocked_Private - inserts blocks of values into the stash.
362   This function expects the values to be roworiented. Multiple columns belong
363   to the same block-row can be inserted with a single call to this function.
364   This function extracts the sub-block of values based on the dimensions of
365   the original input block, and the row,col values corresponding to the blocks.
366 
367   Input Parameters:
368   stash  - the stash
369   row    - the global block-row correspoiding to the values
370   n      - the number of elements inserted. All elements belong to the above row.
371   idxn   - the global block-column indices corresponding to each of the blocks of
372            values. Each block is of size bs*bs.
373   values - the values inserted
374   rmax   - the number of block-rows in the original block.
375   cmax   - the number of block-columsn on the original block.
376   idx    - the index of the current block-row in the original block.
377 */
378 #undef __FUNCT__
379 #define __FUNCT__ "MatStashValuesColBlocked_Private"
380 int MatStashValuesColBlocked_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int rmax,int cmax,int idx)
381 {
382   int             ierr,i,j,k,bs2,bs=stash->bs;
383   const MatScalar *vals;
384   MatScalar       *array;
385 
386   PetscFunctionBegin;
387   bs2 = bs*bs;
388   if ((stash->n+n) > stash->nmax) {
389     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
390   }
391   for (i=0; i<n; i++) {
392     stash->idx[stash->n]   = row;
393     stash->idy[stash->n] = idxn[i];
394     /* Now copy over the block of values. Store the values column oriented.
395      This enables inserting multiple blocks belonging to a row with a single
396      funtion call */
397     array = stash->array + bs2*stash->n;
398     vals  = values + idx*bs + bs2*rmax*i;
399     for (j=0; j<bs; j++) {
400       for (k=0; k<bs; k++) {array[k] = vals[k];}
401       array += bs;
402       vals  += rmax*bs;
403     }
404     stash->n++;
405   }
406   PetscFunctionReturn(0);
407 }
408 /*
409   MatStashScatterBegin_Private - Initiates the transfer of values to the
410   correct owners. This function goes through the stash, and check the
411   owners of each stashed value, and sends the values off to the owner
412   processors.
413 
414   Input Parameters:
415   stash  - the stash
416   owners - an array of size 'no-of-procs' which gives the ownership range
417            for each node.
418 
419   Notes: The 'owners' array in the cased of the blocked-stash has the
420   ranges specified blocked global indices, and for the regular stash in
421   the proper global indices.
422 */
423 #undef __FUNCT__
424 #define __FUNCT__ "MatStashScatterBegin_Private"
425 int MatStashScatterBegin_Private(MatStash *stash,int *owners)
426 {
427   int         *owner,*startv,*starti,tag1=stash->tag1,tag2=stash->tag2,bs2;
428   int         size=stash->size,*nprocs,nsends,nreceives;
429   int         nmax,count,ierr,*sindices,*rindices,i,j,idx;
430   MatScalar   *rvalues,*svalues;
431   MPI_Comm    comm = stash->comm;
432   MPI_Request *send_waits,*recv_waits;
433 
434   PetscFunctionBegin;
435 
436   bs2   = stash->bs*stash->bs;
437   /*  first count number of contributors to each processor */
438   ierr  = PetscMalloc(2*size*sizeof(int),&nprocs);CHKERRQ(ierr);
439   ierr  = PetscMemzero(nprocs,2*size*sizeof(int));CHKERRQ(ierr);
440   ierr  = PetscMalloc((stash->n+1)*sizeof(int),&owner);CHKERRQ(ierr);
441 
442   for (i=0; i<stash->n; i++) {
443     idx = stash->idx[i];
444     for (j=0; j<size; j++) {
445       if (idx >= owners[j] && idx < owners[j+1]) {
446         nprocs[2*j]++; nprocs[2*j+1] = 1; owner[i] = j; break;
447       }
448     }
449   }
450   nsends = 0;  for (i=0; i<size; i++) { nsends += nprocs[2*i+1];}
451 
452   /* inform other processors of number of messages and max length*/
453   ierr = PetscMaxSum(comm,nprocs,&nmax,&nreceives);CHKERRQ(ierr);
454 
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   ierr     = PetscMalloc((nreceives+1)*(nmax+1)*(bs2*sizeof(MatScalar)+2*sizeof(int)),&rvalues);CHKERRQ(ierr);
461   rindices = (int*)(rvalues + bs2*nreceives*nmax);
462   ierr     = PetscMalloc((nreceives+1)*2*sizeof(MPI_Request),&recv_waits);CHKERRQ(ierr);
463   for (i=0,count=0; i<nreceives; i++) {
464     ierr = MPI_Irecv(rvalues+bs2*nmax*i,bs2*nmax,MPIU_MATSCALAR,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,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   ierr     = PetscMalloc((stash->n+1)*(bs2*sizeof(MatScalar)+2*sizeof(int)),&svalues);CHKERRQ(ierr);
474   sindices = (int*)(svalues + bs2*stash->n);
475   ierr     = PetscMalloc(2*(nsends+1)*sizeof(MPI_Request),&send_waits);CHKERRQ(ierr);
476   ierr     = PetscMalloc(2*size*sizeof(int),&startv);CHKERRQ(ierr);
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[2*i-2];
482     starti[i] = starti[i-1] + nprocs[2*i-2]*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       MatScalar *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[2*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[2*i-2];}
502   for (i=0,count=0; i<size; i++) {
503     if (nprocs[2*i+1]) {
504       ierr = MPI_Isend(svalues+bs2*startv[i],bs2*nprocs[2*i],MPIU_MATSCALAR,i,tag1,comm,
505                        send_waits+count++);CHKERRQ(ierr);
506       ierr = MPI_Isend(sindices+2*startv[i],2*nprocs[2*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 __FUNCT__
542 #define __FUNCT__ "MatStashScatterGetMesg_Private"
543 int MatStashScatterGetMesg_Private(MatStash *stash,int *nvals,int **rows,int** cols,MatScalar **vals,int *flg)
544 {
545   int         i,ierr,*flg_v,i1,i2,*rindices,bs2;
546   MPI_Status  recv_status;
547   PetscTruth  match_found = PETSC_FALSE;
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   bs2   = stash->bs*stash->bs;
557   /* If a matching pair of receieves are found, process them, and return the data to
558      the calling function. Until then keep receiving messages */
559   while (!match_found) {
560     ierr = MPI_Waitany(2*stash->nrecvs,stash->recv_waits,&i,&recv_status);CHKERRQ(ierr);
561     /* Now pack the received message into a structure which is useable by others */
562     if (i % 2) {
563       ierr = MPI_Get_count(&recv_status,MPI_INT,nvals);CHKERRQ(ierr);
564       flg_v[2*recv_status.MPI_SOURCE+1] = i/2;
565       *nvals = *nvals/2; /* This message has both row indices and col indices */
566     } else {
567       ierr = MPI_Get_count(&recv_status,MPIU_MATSCALAR,nvals);CHKERRQ(ierr);
568       flg_v[2*recv_status.MPI_SOURCE] = i/2;
569       *nvals = *nvals/bs2;
570     }
571 
572     /* Check if we have both the messages from this proc */
573     i1 = flg_v[2*recv_status.MPI_SOURCE];
574     i2 = flg_v[2*recv_status.MPI_SOURCE+1];
575     if (i1 != -1 && i2 != -1) {
576       rindices    = (int*)(stash->rvalues + bs2*stash->rmax*stash->nrecvs);
577       *rows       = rindices + 2*i2*stash->rmax;
578       *cols       = *rows + *nvals;
579       *vals       = stash->rvalues + i1*bs2*stash->rmax;
580       *flg        = 1;
581       stash->nprocessed ++;
582       match_found = PETSC_TRUE;
583     }
584   }
585   PetscFunctionReturn(0);
586 }
587