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