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