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