xref: /petsc/src/mat/utils/matstash.c (revision 72ce74bd30d5d09d1371a3eb387704f4af395d1a)
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 PetscErrorCode MatStashCreate_Private(MPI_Comm comm,int bs,MatStash *stash)
27 {
28   PetscErrorCode ierr;
29   int 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 PetscErrorCode MatStashDestroy_Private(MatStash *stash)
83 {
84   PetscErrorCode 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 PetscErrorCode 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 PetscErrorCode 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 PetscErrorCode 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 PetscErrorCode MatStashValuesRow_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[])
263 {
264   PetscErrorCode ierr;
265   int i;
266 
267   PetscFunctionBegin;
268   /* Check and see if we have sufficient memory */
269   if ((stash->n + n) > stash->nmax) {
270     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
271   }
272   for (i=0; i<n; i++) {
273     stash->idx[stash->n]   = row;
274     stash->idy[stash->n]   = idxn[i];
275     stash->array[stash->n] = values[i];
276     stash->n++;
277   }
278   PetscFunctionReturn(0);
279 }
280 /*
281   MatStashValuesCol_Private - inserts values into the stash. This function
282   expects the values to be columnoriented. Multiple columns belong to the same row
283   can be inserted with a single call to this function.
284 
285   Input Parameters:
286   stash   - the stash
287   row     - the global row correspoiding to the values
288   n       - the number of elements inserted. All elements belong to the above row.
289   idxn    - the global column indices corresponding to each of the values.
290   values  - the values inserted
291   stepval - the consecutive values are sepated by a distance of stepval.
292             this happens because the input is columnoriented.
293 */
294 #undef __FUNCT__
295 #define __FUNCT__ "MatStashValuesCol_Private"
296 PetscErrorCode MatStashValuesCol_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int stepval)
297 {
298   PetscErrorCode ierr;
299   int i;
300 
301   PetscFunctionBegin;
302   /* Check and see if we have sufficient memory */
303   if ((stash->n + n) > stash->nmax) {
304     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
305   }
306   for (i=0; i<n; i++) {
307     stash->idx[stash->n]   = row;
308     stash->idy[stash->n]   = idxn[i];
309     stash->array[stash->n] = values[i*stepval];
310     stash->n++;
311   }
312   PetscFunctionReturn(0);
313 }
314 
315 /*
316   MatStashValuesRowBlocked_Private - inserts blocks of values into the stash.
317   This function expects the values to be roworiented. Multiple columns belong
318   to the same block-row can be inserted with a single call to this function.
319   This function extracts the sub-block of values based on the dimensions of
320   the original input block, and the row,col values corresponding to the blocks.
321 
322   Input Parameters:
323   stash  - the stash
324   row    - the global block-row correspoiding to the values
325   n      - the number of elements inserted. All elements belong to the above row.
326   idxn   - the global block-column indices corresponding to each of the blocks of
327            values. Each block is of size bs*bs.
328   values - the values inserted
329   rmax   - the number of block-rows in the original block.
330   cmax   - the number of block-columsn on the original block.
331   idx    - the index of the current block-row in the original block.
332 */
333 #undef __FUNCT__
334 #define __FUNCT__ "MatStashValuesRowBlocked_Private"
335 PetscErrorCode MatStashValuesRowBlocked_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int rmax,int cmax,int idx)
336 {
337   PetscErrorCode ierr;
338   int i,j,k,bs2,bs=stash->bs;
339   const MatScalar *vals;
340   MatScalar       *array;
341 
342   PetscFunctionBegin;
343   bs2 = bs*bs;
344   if ((stash->n+n) > stash->nmax) {
345     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
346   }
347   for (i=0; i<n; i++) {
348     stash->idx[stash->n]   = row;
349     stash->idy[stash->n] = idxn[i];
350     /* Now copy over the block of values. Store the values column oriented.
351        This enables inserting multiple blocks belonging to a row with a single
352        funtion call */
353     array = stash->array + bs2*stash->n;
354     vals  = values + idx*bs2*n + bs*i;
355     for (j=0; j<bs; j++) {
356       for (k=0; k<bs; k++) {array[k*bs] = vals[k];}
357       array += 1;
358       vals  += cmax*bs;
359     }
360     stash->n++;
361   }
362   PetscFunctionReturn(0);
363 }
364 
365 /*
366   MatStashValuesColBlocked_Private - inserts blocks of values into the stash.
367   This function expects the values to be roworiented. Multiple columns belong
368   to the same block-row can be inserted with a single call to this function.
369   This function extracts the sub-block of values based on the dimensions of
370   the original input block, and the row,col values corresponding to the blocks.
371 
372   Input Parameters:
373   stash  - the stash
374   row    - the global block-row correspoiding to the values
375   n      - the number of elements inserted. All elements belong to the above row.
376   idxn   - the global block-column indices corresponding to each of the blocks of
377            values. Each block is of size bs*bs.
378   values - the values inserted
379   rmax   - the number of block-rows in the original block.
380   cmax   - the number of block-columsn on the original block.
381   idx    - the index of the current block-row in the original block.
382 */
383 #undef __FUNCT__
384 #define __FUNCT__ "MatStashValuesColBlocked_Private"
385 PetscErrorCode MatStashValuesColBlocked_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int rmax,int cmax,int idx)
386 {
387   PetscErrorCode ierr;
388   int i,j,k,bs2,bs=stash->bs;
389   const MatScalar *vals;
390   MatScalar       *array;
391 
392   PetscFunctionBegin;
393   bs2 = bs*bs;
394   if ((stash->n+n) > stash->nmax) {
395     ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr);
396   }
397   for (i=0; i<n; i++) {
398     stash->idx[stash->n]   = row;
399     stash->idy[stash->n] = idxn[i];
400     /* Now copy over the block of values. Store the values column oriented.
401      This enables inserting multiple blocks belonging to a row with a single
402      funtion call */
403     array = stash->array + bs2*stash->n;
404     vals  = values + idx*bs + bs2*rmax*i;
405     for (j=0; j<bs; j++) {
406       for (k=0; k<bs; k++) {array[k] = vals[k];}
407       array += bs;
408       vals  += rmax*bs;
409     }
410     stash->n++;
411   }
412   PetscFunctionReturn(0);
413 }
414 /*
415   MatStashScatterBegin_Private - Initiates the transfer of values to the
416   correct owners. This function goes through the stash, and check the
417   owners of each stashed value, and sends the values off to the owner
418   processors.
419 
420   Input Parameters:
421   stash  - the stash
422   owners - an array of size 'no-of-procs' which gives the ownership range
423            for each node.
424 
425   Notes: The 'owners' array in the cased of the blocked-stash has the
426   ranges specified blocked global indices, and for the regular stash in
427   the proper global indices.
428 */
429 #undef __FUNCT__
430 #define __FUNCT__ "MatStashScatterBegin_Private"
431 PetscErrorCode MatStashScatterBegin_Private(MatStash *stash,int *owners)
432 {
433   int         *owner,*startv,*starti,tag1=stash->tag1,tag2=stash->tag2,bs2;
434   int         size=stash->size,*nprocs,nsends,nreceives;
435   int         nmax,count,ierr,*sindices,*rindices,i,j,idx;
436   MatScalar   *rvalues,*svalues;
437   MPI_Comm    comm = stash->comm;
438   MPI_Request *send_waits,*recv_waits;
439 
440   PetscFunctionBegin;
441 
442   bs2   = stash->bs*stash->bs;
443   /*  first count number of contributors to each processor */
444   ierr  = PetscMalloc(2*size*sizeof(int),&nprocs);CHKERRQ(ierr);
445   ierr  = PetscMemzero(nprocs,2*size*sizeof(int));CHKERRQ(ierr);
446   ierr  = PetscMalloc((stash->n+1)*sizeof(int),&owner);CHKERRQ(ierr);
447 
448   for (i=0; i<stash->n; i++) {
449     idx = stash->idx[i];
450     for (j=0; j<size; j++) {
451       if (idx >= owners[j] && idx < owners[j+1]) {
452         nprocs[2*j]++; nprocs[2*j+1] = 1; owner[i] = j; break;
453       }
454     }
455   }
456   nsends = 0;  for (i=0; i<size; i++) { nsends += nprocs[2*i+1];}
457 
458   /* inform other processors of number of messages and max length*/
459   ierr = PetscMaxSum(comm,nprocs,&nmax,&nreceives);CHKERRQ(ierr);
460 
461   /* post receives:
462      since we don't know how long each individual message is we
463      allocate the largest needed buffer for each receive. Potentially
464      this is a lot of wasted space.
465   */
466   ierr     = PetscMalloc((nreceives+1)*(nmax+1)*(bs2*sizeof(MatScalar)+2*sizeof(int)),&rvalues);CHKERRQ(ierr);
467   rindices = (int*)(rvalues + bs2*nreceives*nmax);
468   ierr     = PetscMalloc((nreceives+1)*2*sizeof(MPI_Request),&recv_waits);CHKERRQ(ierr);
469   for (i=0,count=0; i<nreceives; i++) {
470     ierr = MPI_Irecv(rvalues+bs2*nmax*i,bs2*nmax,MPIU_MATSCALAR,MPI_ANY_SOURCE,tag1,comm,
471                      recv_waits+count++);CHKERRQ(ierr);
472     ierr = MPI_Irecv(rindices+2*nmax*i,2*nmax,MPI_INT,MPI_ANY_SOURCE,tag2,comm,recv_waits+count++);CHKERRQ(ierr);
473   }
474 
475   /* do sends:
476       1) starts[i] gives the starting index in svalues for stuff going to
477          the ith processor
478   */
479   ierr     = PetscMalloc((stash->n+1)*(bs2*sizeof(MatScalar)+2*sizeof(int)),&svalues);CHKERRQ(ierr);
480   sindices = (int*)(svalues + bs2*stash->n);
481   ierr     = PetscMalloc(2*(nsends+1)*sizeof(MPI_Request),&send_waits);CHKERRQ(ierr);
482   ierr     = PetscMalloc(2*size*sizeof(int),&startv);CHKERRQ(ierr);
483   starti   = startv + size;
484   /* use 2 sends the first with all_a, the next with all_i and all_j */
485   startv[0]  = 0; starti[0] = 0;
486   for (i=1; i<size; i++) {
487     startv[i] = startv[i-1] + nprocs[2*i-2];
488     starti[i] = starti[i-1] + nprocs[2*i-2]*2;
489   }
490   for (i=0; i<stash->n; i++) {
491     j = owner[i];
492     if (bs2 == 1) {
493       svalues[startv[j]]              = stash->array[i];
494     } else {
495       int       k;
496       MatScalar *buf1,*buf2;
497       buf1 = svalues+bs2*startv[j];
498       buf2 = stash->array+bs2*i;
499       for (k=0; k<bs2; k++){ buf1[k] = buf2[k]; }
500     }
501     sindices[starti[j]]               = stash->idx[i];
502     sindices[starti[j]+nprocs[2*j]]   = stash->idy[i];
503     startv[j]++;
504     starti[j]++;
505   }
506   startv[0] = 0;
507   for (i=1; i<size; i++) { startv[i] = startv[i-1] + nprocs[2*i-2];}
508   for (i=0,count=0; i<size; i++) {
509     if (nprocs[2*i+1]) {
510       ierr = MPI_Isend(svalues+bs2*startv[i],bs2*nprocs[2*i],MPIU_MATSCALAR,i,tag1,comm,
511                        send_waits+count++);CHKERRQ(ierr);
512       ierr = MPI_Isend(sindices+2*startv[i],2*nprocs[2*i],MPI_INT,i,tag2,comm,
513                        send_waits+count++);CHKERRQ(ierr);
514     }
515   }
516   ierr = PetscFree(owner);CHKERRQ(ierr);
517   ierr = PetscFree(startv);CHKERRQ(ierr);
518   /* This memory is reused in scatter end  for a different purpose*/
519   for (i=0; i<2*size; i++) nprocs[i] = -1;
520   stash->nprocs      = nprocs;
521 
522   stash->svalues    = svalues;    stash->rvalues    = rvalues;
523   stash->nsends     = nsends;     stash->nrecvs     = nreceives;
524   stash->send_waits = send_waits; stash->recv_waits = recv_waits;
525   stash->rmax       = nmax;
526   PetscFunctionReturn(0);
527 }
528 
529 /*
530    MatStashScatterGetMesg_Private - This function waits on the receives posted
531    in the function MatStashScatterBegin_Private() and returns one message at
532    a time to the calling function. If no messages are left, it indicates this
533    by setting flg = 0, else it sets flg = 1.
534 
535    Input Parameters:
536    stash - the stash
537 
538    Output Parameters:
539    nvals - the number of entries in the current message.
540    rows  - an array of row indices (or blocked indices) corresponding to the values
541    cols  - an array of columnindices (or blocked indices) corresponding to the values
542    vals  - the values
543    flg   - 0 indicates no more message left, and the current call has no values associated.
544            1 indicates that the current call successfully received a message, and the
545              other output parameters nvals,rows,cols,vals are set appropriately.
546 */
547 #undef __FUNCT__
548 #define __FUNCT__ "MatStashScatterGetMesg_Private"
549 PetscErrorCode MatStashScatterGetMesg_Private(MatStash *stash,int *nvals,int **rows,int** cols,MatScalar **vals,int *flg)
550 {
551   int         i,ierr,*flg_v,i1,i2,*rindices,bs2;
552   MPI_Status  recv_status;
553   PetscTruth  match_found = PETSC_FALSE;
554 
555   PetscFunctionBegin;
556 
557   *flg = 0; /* When a message is discovered this is reset to 1 */
558   /* Return if no more messages to process */
559   if (stash->nprocessed == stash->nrecvs) { PetscFunctionReturn(0); }
560 
561   flg_v = stash->nprocs;
562   bs2   = stash->bs*stash->bs;
563   /* If a matching pair of receieves are found, process them, and return the data to
564      the calling function. Until then keep receiving messages */
565   while (!match_found) {
566     ierr = MPI_Waitany(2*stash->nrecvs,stash->recv_waits,&i,&recv_status);CHKERRQ(ierr);
567     /* Now pack the received message into a structure which is useable by others */
568     if (i % 2) {
569       ierr = MPI_Get_count(&recv_status,MPI_INT,nvals);CHKERRQ(ierr);
570       flg_v[2*recv_status.MPI_SOURCE+1] = i/2;
571       *nvals = *nvals/2; /* This message has both row indices and col indices */
572     } else {
573       ierr = MPI_Get_count(&recv_status,MPIU_MATSCALAR,nvals);CHKERRQ(ierr);
574       flg_v[2*recv_status.MPI_SOURCE] = i/2;
575       *nvals = *nvals/bs2;
576     }
577 
578     /* Check if we have both the messages from this proc */
579     i1 = flg_v[2*recv_status.MPI_SOURCE];
580     i2 = flg_v[2*recv_status.MPI_SOURCE+1];
581     if (i1 != -1 && i2 != -1) {
582       rindices    = (int*)(stash->rvalues + bs2*stash->rmax*stash->nrecvs);
583       *rows       = rindices + 2*i2*stash->rmax;
584       *cols       = *rows + *nvals;
585       *vals       = stash->rvalues + i1*bs2*stash->rmax;
586       *flg        = 1;
587       stash->nprocessed ++;
588       match_found = PETSC_TRUE;
589     }
590   }
591   PetscFunctionReturn(0);
592 }
593