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