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