1 2 #include "src/mat/matimpl.h" 3 4 /* 5 The input to the stash is ALWAYS in MatScalar precision, and the 6 internal storage and output is also in MatScalar. 7 */ 8 #define DEFAULT_STASH_SIZE 10000 9 10 /* 11 MatStashCreate_Private - Creates a stash,currently used for all the parallel 12 matrix implementations. The stash is where elements of a matrix destined 13 to be stored on other processors are kept until matrix assembly is done. 14 15 This is a simple minded stash. Simply adds entries to end of stash. 16 17 Input Parameters: 18 comm - communicator, required for scatters. 19 bs - stash block size. used when stashing blocks of values 20 21 Output Parameters: 22 stash - the newly created stash 23 */ 24 #undef __FUNCT__ 25 #define __FUNCT__ "MatStashCreate_Private" 26 PetscErrorCode MatStashCreate_Private(MPI_Comm comm,int bs,MatStash *stash) 27 { 28 PetscErrorCode ierr; 29 int 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 PetscErrorCode MatStashDestroy_Private(MatStash *stash) 83 { 84 PetscErrorCode 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 PetscErrorCode 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 PetscErrorCode MatStashGetInfo_Private(MatStash *stash,int *nstash,int *reallocs) 174 { 175 int bs2 = stash->bs*stash->bs; 176 177 PetscFunctionBegin; 178 if (nstash) *nstash = stash->n*bs2; 179 if (reallocs) { 180 if (stash->reallocs < 0) *reallocs = 0; 181 else *reallocs = stash->reallocs; 182 } 183 PetscFunctionReturn(0); 184 } 185 186 187 /* 188 MatStashSetInitialSize_Private - Sets the initial size of the stash 189 190 Input Parameters: 191 stash - the stash 192 max - the value that is used as the max size of the stash. 193 this value is used while allocating memory. 194 */ 195 #undef __FUNCT__ 196 #define __FUNCT__ "MatStashSetInitialSize_Private" 197 PetscErrorCode MatStashSetInitialSize_Private(MatStash *stash,int max) 198 { 199 PetscFunctionBegin; 200 stash->umax = max; 201 PetscFunctionReturn(0); 202 } 203 204 /* MatStashExpand_Private - Expand the stash. This function is called 205 when the space in the stash is not sufficient to add the new values 206 being inserted into the stash. 207 208 Input Parameters: 209 stash - the stash 210 incr - the minimum increase requested 211 212 Notes: 213 This routine doubles the currently used memory. 214 */ 215 #undef __FUNCT__ 216 #define __FUNCT__ "MatStashExpand_Private" 217 static int MatStashExpand_Private(MatStash *stash,int incr) 218 { 219 int *n_idx,*n_idy,newnmax,bs2,ierr; 220 MatScalar *n_array; 221 222 PetscFunctionBegin; 223 /* allocate a larger stash */ 224 bs2 = stash->bs*stash->bs; 225 if (!stash->oldnmax && !stash->nmax) { /* new stash */ 226 if (stash->umax) newnmax = stash->umax/bs2; 227 else newnmax = DEFAULT_STASH_SIZE/bs2; 228 } else if (!stash->nmax) { /* resuing stash */ 229 if (stash->umax > stash->oldnmax) newnmax = stash->umax/bs2; 230 else newnmax = stash->oldnmax/bs2; 231 } else newnmax = stash->nmax*2; 232 if (newnmax < (stash->nmax + incr)) newnmax += 2*incr; 233 234 ierr = PetscMalloc((newnmax)*(2*sizeof(int)+bs2*sizeof(MatScalar)),&n_array);CHKERRQ(ierr); 235 n_idx = (int*)(n_array + bs2*newnmax); 236 n_idy = (int*)(n_idx + newnmax); 237 ierr = PetscMemcpy(n_array,stash->array,bs2*stash->nmax*sizeof(MatScalar));CHKERRQ(ierr); 238 ierr = PetscMemcpy(n_idx,stash->idx,stash->nmax*sizeof(int));CHKERRQ(ierr); 239 ierr = PetscMemcpy(n_idy,stash->idy,stash->nmax*sizeof(int));CHKERRQ(ierr); 240 if (stash->array) {ierr = PetscFree(stash->array);CHKERRQ(ierr);} 241 stash->array = n_array; 242 stash->idx = n_idx; 243 stash->idy = n_idy; 244 stash->nmax = newnmax; 245 stash->reallocs++; 246 PetscFunctionReturn(0); 247 } 248 /* 249 MatStashValuesRow_Private - inserts values into the stash. This function 250 expects the values to be roworiented. Multiple columns belong to the same row 251 can be inserted with a single call to this function. 252 253 Input Parameters: 254 stash - the stash 255 row - the global row correspoiding to the values 256 n - the number of elements inserted. All elements belong to the above row. 257 idxn - the global column indices corresponding to each of the values. 258 values - the values inserted 259 */ 260 #undef __FUNCT__ 261 #define __FUNCT__ "MatStashValuesRow_Private" 262 PetscErrorCode MatStashValuesRow_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[]) 263 { 264 PetscErrorCode ierr; 265 int i; 266 267 PetscFunctionBegin; 268 /* Check and see if we have sufficient memory */ 269 if ((stash->n + n) > stash->nmax) { 270 ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr); 271 } 272 for (i=0; i<n; i++) { 273 stash->idx[stash->n] = row; 274 stash->idy[stash->n] = idxn[i]; 275 stash->array[stash->n] = values[i]; 276 stash->n++; 277 } 278 PetscFunctionReturn(0); 279 } 280 /* 281 MatStashValuesCol_Private - inserts values into the stash. This function 282 expects the values to be columnoriented. Multiple columns belong to the same row 283 can be inserted with a single call to this function. 284 285 Input Parameters: 286 stash - the stash 287 row - the global row correspoiding to the values 288 n - the number of elements inserted. All elements belong to the above row. 289 idxn - the global column indices corresponding to each of the values. 290 values - the values inserted 291 stepval - the consecutive values are sepated by a distance of stepval. 292 this happens because the input is columnoriented. 293 */ 294 #undef __FUNCT__ 295 #define __FUNCT__ "MatStashValuesCol_Private" 296 PetscErrorCode MatStashValuesCol_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int stepval) 297 { 298 PetscErrorCode ierr; 299 int i; 300 301 PetscFunctionBegin; 302 /* Check and see if we have sufficient memory */ 303 if ((stash->n + n) > stash->nmax) { 304 ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr); 305 } 306 for (i=0; i<n; i++) { 307 stash->idx[stash->n] = row; 308 stash->idy[stash->n] = idxn[i]; 309 stash->array[stash->n] = values[i*stepval]; 310 stash->n++; 311 } 312 PetscFunctionReturn(0); 313 } 314 315 /* 316 MatStashValuesRowBlocked_Private - inserts blocks of values into the stash. 317 This function expects the values to be roworiented. Multiple columns belong 318 to the same block-row can be inserted with a single call to this function. 319 This function extracts the sub-block of values based on the dimensions of 320 the original input block, and the row,col values corresponding to the blocks. 321 322 Input Parameters: 323 stash - the stash 324 row - the global block-row correspoiding to the values 325 n - the number of elements inserted. All elements belong to the above row. 326 idxn - the global block-column indices corresponding to each of the blocks of 327 values. Each block is of size bs*bs. 328 values - the values inserted 329 rmax - the number of block-rows in the original block. 330 cmax - the number of block-columsn on the original block. 331 idx - the index of the current block-row in the original block. 332 */ 333 #undef __FUNCT__ 334 #define __FUNCT__ "MatStashValuesRowBlocked_Private" 335 PetscErrorCode MatStashValuesRowBlocked_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int rmax,int cmax,int idx) 336 { 337 PetscErrorCode ierr; 338 int i,j,k,bs2,bs=stash->bs; 339 const MatScalar *vals; 340 MatScalar *array; 341 342 PetscFunctionBegin; 343 bs2 = bs*bs; 344 if ((stash->n+n) > stash->nmax) { 345 ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr); 346 } 347 for (i=0; i<n; i++) { 348 stash->idx[stash->n] = row; 349 stash->idy[stash->n] = idxn[i]; 350 /* Now copy over the block of values. Store the values column oriented. 351 This enables inserting multiple blocks belonging to a row with a single 352 funtion call */ 353 array = stash->array + bs2*stash->n; 354 vals = values + idx*bs2*n + bs*i; 355 for (j=0; j<bs; j++) { 356 for (k=0; k<bs; k++) {array[k*bs] = vals[k];} 357 array += 1; 358 vals += cmax*bs; 359 } 360 stash->n++; 361 } 362 PetscFunctionReturn(0); 363 } 364 365 /* 366 MatStashValuesColBlocked_Private - inserts blocks of values into the stash. 367 This function expects the values to be roworiented. Multiple columns belong 368 to the same block-row can be inserted with a single call to this function. 369 This function extracts the sub-block of values based on the dimensions of 370 the original input block, and the row,col values corresponding to the blocks. 371 372 Input Parameters: 373 stash - the stash 374 row - the global block-row correspoiding to the values 375 n - the number of elements inserted. All elements belong to the above row. 376 idxn - the global block-column indices corresponding to each of the blocks of 377 values. Each block is of size bs*bs. 378 values - the values inserted 379 rmax - the number of block-rows in the original block. 380 cmax - the number of block-columsn on the original block. 381 idx - the index of the current block-row in the original block. 382 */ 383 #undef __FUNCT__ 384 #define __FUNCT__ "MatStashValuesColBlocked_Private" 385 PetscErrorCode MatStashValuesColBlocked_Private(MatStash *stash,int row,int n,const int idxn[],const MatScalar values[],int rmax,int cmax,int idx) 386 { 387 PetscErrorCode ierr; 388 int i,j,k,bs2,bs=stash->bs; 389 const MatScalar *vals; 390 MatScalar *array; 391 392 PetscFunctionBegin; 393 bs2 = bs*bs; 394 if ((stash->n+n) > stash->nmax) { 395 ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr); 396 } 397 for (i=0; i<n; i++) { 398 stash->idx[stash->n] = row; 399 stash->idy[stash->n] = idxn[i]; 400 /* Now copy over the block of values. Store the values column oriented. 401 This enables inserting multiple blocks belonging to a row with a single 402 funtion call */ 403 array = stash->array + bs2*stash->n; 404 vals = values + idx*bs + bs2*rmax*i; 405 for (j=0; j<bs; j++) { 406 for (k=0; k<bs; k++) {array[k] = vals[k];} 407 array += bs; 408 vals += rmax*bs; 409 } 410 stash->n++; 411 } 412 PetscFunctionReturn(0); 413 } 414 /* 415 MatStashScatterBegin_Private - Initiates the transfer of values to the 416 correct owners. This function goes through the stash, and check the 417 owners of each stashed value, and sends the values off to the owner 418 processors. 419 420 Input Parameters: 421 stash - the stash 422 owners - an array of size 'no-of-procs' which gives the ownership range 423 for each node. 424 425 Notes: The 'owners' array in the cased of the blocked-stash has the 426 ranges specified blocked global indices, and for the regular stash in 427 the proper global indices. 428 */ 429 #undef __FUNCT__ 430 #define __FUNCT__ "MatStashScatterBegin_Private" 431 PetscErrorCode MatStashScatterBegin_Private(MatStash *stash,int *owners) 432 { 433 int *owner,*startv,*starti,tag1=stash->tag1,tag2=stash->tag2,bs2; 434 int size=stash->size,*nprocs,nsends,nreceives; 435 int nmax,count,ierr,*sindices,*rindices,i,j,idx; 436 MatScalar *rvalues,*svalues; 437 MPI_Comm comm = stash->comm; 438 MPI_Request *send_waits,*recv_waits; 439 440 PetscFunctionBegin; 441 442 bs2 = stash->bs*stash->bs; 443 /* first count number of contributors to each processor */ 444 ierr = PetscMalloc(2*size*sizeof(int),&nprocs);CHKERRQ(ierr); 445 ierr = PetscMemzero(nprocs,2*size*sizeof(int));CHKERRQ(ierr); 446 ierr = PetscMalloc((stash->n+1)*sizeof(int),&owner);CHKERRQ(ierr); 447 448 for (i=0; i<stash->n; i++) { 449 idx = stash->idx[i]; 450 for (j=0; j<size; j++) { 451 if (idx >= owners[j] && idx < owners[j+1]) { 452 nprocs[2*j]++; nprocs[2*j+1] = 1; owner[i] = j; break; 453 } 454 } 455 } 456 nsends = 0; for (i=0; i<size; i++) { nsends += nprocs[2*i+1];} 457 458 /* inform other processors of number of messages and max length*/ 459 ierr = PetscMaxSum(comm,nprocs,&nmax,&nreceives);CHKERRQ(ierr); 460 461 /* post receives: 462 since we don't know how long each individual message is we 463 allocate the largest needed buffer for each receive. Potentially 464 this is a lot of wasted space. 465 */ 466 ierr = PetscMalloc((nreceives+1)*(nmax+1)*(bs2*sizeof(MatScalar)+2*sizeof(int)),&rvalues);CHKERRQ(ierr); 467 rindices = (int*)(rvalues + bs2*nreceives*nmax); 468 ierr = PetscMalloc((nreceives+1)*2*sizeof(MPI_Request),&recv_waits);CHKERRQ(ierr); 469 for (i=0,count=0; i<nreceives; i++) { 470 ierr = MPI_Irecv(rvalues+bs2*nmax*i,bs2*nmax,MPIU_MATSCALAR,MPI_ANY_SOURCE,tag1,comm, 471 recv_waits+count++);CHKERRQ(ierr); 472 ierr = MPI_Irecv(rindices+2*nmax*i,2*nmax,MPI_INT,MPI_ANY_SOURCE,tag2,comm,recv_waits+count++);CHKERRQ(ierr); 473 } 474 475 /* do sends: 476 1) starts[i] gives the starting index in svalues for stuff going to 477 the ith processor 478 */ 479 ierr = PetscMalloc((stash->n+1)*(bs2*sizeof(MatScalar)+2*sizeof(int)),&svalues);CHKERRQ(ierr); 480 sindices = (int*)(svalues + bs2*stash->n); 481 ierr = PetscMalloc(2*(nsends+1)*sizeof(MPI_Request),&send_waits);CHKERRQ(ierr); 482 ierr = PetscMalloc(2*size*sizeof(int),&startv);CHKERRQ(ierr); 483 starti = startv + size; 484 /* use 2 sends the first with all_a, the next with all_i and all_j */ 485 startv[0] = 0; starti[0] = 0; 486 for (i=1; i<size; i++) { 487 startv[i] = startv[i-1] + nprocs[2*i-2]; 488 starti[i] = starti[i-1] + nprocs[2*i-2]*2; 489 } 490 for (i=0; i<stash->n; i++) { 491 j = owner[i]; 492 if (bs2 == 1) { 493 svalues[startv[j]] = stash->array[i]; 494 } else { 495 int k; 496 MatScalar *buf1,*buf2; 497 buf1 = svalues+bs2*startv[j]; 498 buf2 = stash->array+bs2*i; 499 for (k=0; k<bs2; k++){ buf1[k] = buf2[k]; } 500 } 501 sindices[starti[j]] = stash->idx[i]; 502 sindices[starti[j]+nprocs[2*j]] = stash->idy[i]; 503 startv[j]++; 504 starti[j]++; 505 } 506 startv[0] = 0; 507 for (i=1; i<size; i++) { startv[i] = startv[i-1] + nprocs[2*i-2];} 508 for (i=0,count=0; i<size; i++) { 509 if (nprocs[2*i+1]) { 510 ierr = MPI_Isend(svalues+bs2*startv[i],bs2*nprocs[2*i],MPIU_MATSCALAR,i,tag1,comm, 511 send_waits+count++);CHKERRQ(ierr); 512 ierr = MPI_Isend(sindices+2*startv[i],2*nprocs[2*i],MPI_INT,i,tag2,comm, 513 send_waits+count++);CHKERRQ(ierr); 514 } 515 } 516 ierr = PetscFree(owner);CHKERRQ(ierr); 517 ierr = PetscFree(startv);CHKERRQ(ierr); 518 /* This memory is reused in scatter end for a different purpose*/ 519 for (i=0; i<2*size; i++) nprocs[i] = -1; 520 stash->nprocs = nprocs; 521 522 stash->svalues = svalues; stash->rvalues = rvalues; 523 stash->nsends = nsends; stash->nrecvs = nreceives; 524 stash->send_waits = send_waits; stash->recv_waits = recv_waits; 525 stash->rmax = nmax; 526 PetscFunctionReturn(0); 527 } 528 529 /* 530 MatStashScatterGetMesg_Private - This function waits on the receives posted 531 in the function MatStashScatterBegin_Private() and returns one message at 532 a time to the calling function. If no messages are left, it indicates this 533 by setting flg = 0, else it sets flg = 1. 534 535 Input Parameters: 536 stash - the stash 537 538 Output Parameters: 539 nvals - the number of entries in the current message. 540 rows - an array of row indices (or blocked indices) corresponding to the values 541 cols - an array of columnindices (or blocked indices) corresponding to the values 542 vals - the values 543 flg - 0 indicates no more message left, and the current call has no values associated. 544 1 indicates that the current call successfully received a message, and the 545 other output parameters nvals,rows,cols,vals are set appropriately. 546 */ 547 #undef __FUNCT__ 548 #define __FUNCT__ "MatStashScatterGetMesg_Private" 549 PetscErrorCode MatStashScatterGetMesg_Private(MatStash *stash,int *nvals,int **rows,int** cols,MatScalar **vals,int *flg) 550 { 551 int i,ierr,*flg_v,i1,i2,*rindices,bs2; 552 MPI_Status recv_status; 553 PetscTruth match_found = PETSC_FALSE; 554 555 PetscFunctionBegin; 556 557 *flg = 0; /* When a message is discovered this is reset to 1 */ 558 /* Return if no more messages to process */ 559 if (stash->nprocessed == stash->nrecvs) { PetscFunctionReturn(0); } 560 561 flg_v = stash->nprocs; 562 bs2 = stash->bs*stash->bs; 563 /* If a matching pair of receieves are found, process them, and return the data to 564 the calling function. Until then keep receiving messages */ 565 while (!match_found) { 566 ierr = MPI_Waitany(2*stash->nrecvs,stash->recv_waits,&i,&recv_status);CHKERRQ(ierr); 567 /* Now pack the received message into a structure which is useable by others */ 568 if (i % 2) { 569 ierr = MPI_Get_count(&recv_status,MPI_INT,nvals);CHKERRQ(ierr); 570 flg_v[2*recv_status.MPI_SOURCE+1] = i/2; 571 *nvals = *nvals/2; /* This message has both row indices and col indices */ 572 } else { 573 ierr = MPI_Get_count(&recv_status,MPIU_MATSCALAR,nvals);CHKERRQ(ierr); 574 flg_v[2*recv_status.MPI_SOURCE] = i/2; 575 *nvals = *nvals/bs2; 576 } 577 578 /* Check if we have both the messages from this proc */ 579 i1 = flg_v[2*recv_status.MPI_SOURCE]; 580 i2 = flg_v[2*recv_status.MPI_SOURCE+1]; 581 if (i1 != -1 && i2 != -1) { 582 rindices = (int*)(stash->rvalues + bs2*stash->rmax*stash->nrecvs); 583 *rows = rindices + 2*i2*stash->rmax; 584 *cols = *rows + *nvals; 585 *vals = stash->rvalues + i1*bs2*stash->rmax; 586 *flg = 1; 587 stash->nprocessed ++; 588 match_found = PETSC_TRUE; 589 } 590 } 591 PetscFunctionReturn(0); 592 } 593