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