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