1 /*$Id: matstash.c,v 1.42 2000/04/12 04:24:13 bsmith Exp bsmith $*/ 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 __FUNC__ 26 #define __FUNC__ /*<a name=""></a>*/"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 ierr = PetscCommDuplicate_Private(comm,&stash->comm,&stash->tag1);CHKERRQ(ierr); 35 ierr = PetscCommGetNewTag(stash->comm,&stash->tag2);CHKERRQ(ierr); 36 ierr = MPI_Comm_size(stash->comm,&stash->size);CHKERRQ(ierr); 37 ierr = MPI_Comm_rank(stash->comm,&stash->rank);CHKERRQ(ierr); 38 39 nopt = stash->size; 40 opt = (int*)PetscMalloc(nopt*sizeof(int));CHKPTRQ(opt); 41 ierr = OptionsGetIntArray(PETSC_NULL,"-vecstash_initial_size",opt,&nopt,&flg);CHKERRQ(ierr); 42 if (flg) { 43 if (nopt == 1) max = opt[0]; 44 else if (nopt == stash->size) max = opt[stash->rank]; 45 else if (stash->rank < nopt) max = opt[stash->rank]; 46 else max = 0; /* Use default */ 47 stash->umax = max; 48 } else { 49 stash->umax = 0; 50 } 51 ierr = PetscFree(opt);CHKERRQ(ierr); 52 if (bs <= 0) bs = 1; 53 54 stash->bs = bs; 55 stash->nmax = 0; 56 stash->oldnmax = 0; 57 stash->n = 0; 58 stash->reallocs = -1; 59 stash->idx = 0; 60 stash->idy = 0; 61 stash->array = 0; 62 63 stash->send_waits = 0; 64 stash->recv_waits = 0; 65 stash->send_status = 0; 66 stash->nsends = 0; 67 stash->nrecvs = 0; 68 stash->svalues = 0; 69 stash->rvalues = 0; 70 stash->rmax = 0; 71 stash->nprocs = 0; 72 stash->nprocessed = 0; 73 PetscFunctionReturn(0); 74 } 75 76 /* 77 MatStashDestroy_Private - Destroy the stash 78 */ 79 #undef __FUNC__ 80 #define __FUNC__ /*<a name=""></a>*/"MatStashDestroy_Private" 81 int MatStashDestroy_Private(MatStash *stash) 82 { 83 int ierr; 84 85 PetscFunctionBegin; 86 ierr = PetscCommDestroy_Private(&stash->comm);CHKERRQ(ierr); 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 __FUNC__ 103 #define __FUNC__ /*<a name=""></a>*/"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 send_status = (MPI_Status *)PetscMalloc(2*nsends*sizeof(MPI_Status));CHKPTRQ(send_status); 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 bs2 = stash->bs*stash->bs; 121 oldnmax = ((int)(stash->n * 1.1) + 5)*bs2; 122 if (oldnmax > stash->oldnmax) stash->oldnmax = oldnmax; 123 124 stash->nmax = 0; 125 stash->n = 0; 126 stash->reallocs = -1; 127 stash->rmax = 0; 128 stash->nprocessed = 0; 129 130 if (stash->array) { 131 ierr = PetscFree(stash->array);CHKERRQ(ierr); 132 stash->array = 0; 133 stash->idx = 0; 134 stash->idy = 0; 135 } 136 if (stash->send_waits) { 137 ierr = PetscFree(stash->send_waits);CHKERRQ(ierr); 138 stash->send_waits = 0; 139 } 140 if (stash->recv_waits) { 141 ierr = PetscFree(stash->recv_waits);CHKERRQ(ierr); 142 stash->recv_waits = 0; 143 } 144 if (stash->svalues) { 145 ierr = PetscFree(stash->svalues);CHKERRQ(ierr); 146 stash->svalues = 0; 147 } 148 if (stash->rvalues) { 149 ierr = PetscFree(stash->rvalues);CHKERRQ(ierr); 150 stash->rvalues = 0; 151 } 152 if (stash->nprocs) { 153 ierr = PetscFree(stash->nprocs); 154 stash->nprocs = 0; 155 } 156 157 PetscFunctionReturn(0); 158 } 159 160 /* 161 MatStashGetInfo_Private - Gets the relavant statistics of the stash 162 163 Input Parameters: 164 stash - the stash 165 nstash - the size of the stash. Indicates the number of values stored. 166 reallocs - the number of additional mallocs incurred. 167 168 */ 169 #undef __FUNC__ 170 #define __FUNC__ /*<a name=""></a>*/"MatStashGetInfo_Private" 171 int MatStashGetInfo_Private(MatStash *stash,int *nstash,int *reallocs) 172 { 173 int bs2 = stash->bs*stash->bs; 174 175 PetscFunctionBegin; 176 *nstash = stash->n*bs2; 177 if (stash->reallocs < 0) *reallocs = 0; 178 else *reallocs = stash->reallocs; 179 PetscFunctionReturn(0); 180 } 181 182 183 /* 184 MatStashSetInitialSize_Private - Sets the initial size of the stash 185 186 Input Parameters: 187 stash - the stash 188 max - the value that is used as the max size of the stash. 189 this value is used while allocating memory. 190 */ 191 #undef __FUNC__ 192 #define __FUNC__ /*<a name=""></a>*/"MatStashSetInitialSize_Private" 193 int MatStashSetInitialSize_Private(MatStash *stash,int max) 194 { 195 PetscFunctionBegin; 196 stash->umax = max; 197 PetscFunctionReturn(0); 198 } 199 200 /* MatStashExpand_Private - Expand the stash. This function is called 201 when the space in the stash is not sufficient to add the new values 202 being inserted into the stash. 203 204 Input Parameters: 205 stash - the stash 206 incr - the minimum increase requested 207 208 Notes: 209 This routine doubles the currently used memory. 210 */ 211 #undef __FUNC__ 212 #define __FUNC__ /*<a name=""></a>*/"MatStashExpand_Private" 213 static int MatStashExpand_Private(MatStash *stash,int incr) 214 { 215 int *n_idx,*n_idy,newnmax,bs2,ierr; 216 MatScalar *n_array; 217 218 PetscFunctionBegin; 219 /* allocate a larger stash */ 220 bs2 = stash->bs*stash->bs; 221 if (!stash->oldnmax && !stash->nmax) { /* new stash */ 222 if (stash->umax) newnmax = stash->umax/bs2; 223 else newnmax = DEFAULT_STASH_SIZE/bs2; 224 } else if (!stash->nmax) { /* resuing stash */ 225 if (stash->umax > stash->oldnmax) newnmax = stash->umax/bs2; 226 else newnmax = stash->oldnmax/bs2; 227 } else newnmax = stash->nmax*2; 228 if (newnmax < (stash->nmax + incr)) newnmax += 2*incr; 229 230 n_array = (MatScalar*)PetscMalloc((newnmax)*(2*sizeof(int)+bs2*sizeof(MatScalar)));CHKPTRQ(n_array); 231 n_idx = (int*)(n_array + bs2*newnmax); 232 n_idy = (int*)(n_idx + newnmax); 233 ierr = PetscMemcpy(n_array,stash->array,bs2*stash->nmax*sizeof(MatScalar));CHKERRQ(ierr); 234 ierr = PetscMemcpy(n_idx,stash->idx,stash->nmax*sizeof(int));CHKERRQ(ierr); 235 ierr = PetscMemcpy(n_idy,stash->idy,stash->nmax*sizeof(int));CHKERRQ(ierr); 236 if (stash->array) {ierr = PetscFree(stash->array);CHKERRQ(ierr);} 237 stash->array = n_array; 238 stash->idx = n_idx; 239 stash->idy = n_idy; 240 stash->nmax = newnmax; 241 stash->reallocs++; 242 PetscFunctionReturn(0); 243 } 244 /* 245 MatStashValuesRow_Private - inserts values into the stash. This function 246 expects the values to be roworiented. Multiple columns belong to the same row 247 can be inserted with a single call to this function. 248 249 Input Parameters: 250 stash - the stash 251 row - the global row correspoiding to the values 252 n - the number of elements inserted. All elements belong to the above row. 253 idxn - the global column indices corresponding to each of the values. 254 values - the values inserted 255 */ 256 #undef __FUNC__ 257 #define __FUNC__ /*<a name=""></a>*/"MatStashValuesRow_Private" 258 int MatStashValuesRow_Private(MatStash *stash,int row,int n,int *idxn,MatScalar *values) 259 { 260 int ierr,i; 261 262 PetscFunctionBegin; 263 /* Check and see if we have sufficient memory */ 264 if ((stash->n + n) > stash->nmax) { 265 ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr); 266 } 267 for (i=0; i<n; i++) { 268 stash->idx[stash->n] = row; 269 stash->idy[stash->n] = idxn[i]; 270 stash->array[stash->n] = values[i]; 271 stash->n++; 272 } 273 PetscFunctionReturn(0); 274 } 275 /* 276 MatStashValuesCol_Private - inserts values into the stash. This function 277 expects the values to be columnoriented. Multiple columns belong to the same row 278 can be inserted with a single call to this function. 279 280 Input Parameters: 281 stash - the stash 282 row - the global row correspoiding to the values 283 n - the number of elements inserted. All elements belong to the above row. 284 idxn - the global column indices corresponding to each of the values. 285 values - the values inserted 286 stepval - the consecutive values are sepated by a distance of stepval. 287 this happens because the input is columnoriented. 288 */ 289 #undef __FUNC__ 290 #define __FUNC__ /*<a name=""></a>*/"MatStashValuesCol_Private" 291 int MatStashValuesCol_Private(MatStash *stash,int row,int n,int *idxn,MatScalar *values,int stepval) 292 { 293 int ierr,i; 294 295 PetscFunctionBegin; 296 /* Check and see if we have sufficient memory */ 297 if ((stash->n + n) > stash->nmax) { 298 ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr); 299 } 300 for (i=0; i<n; i++) { 301 stash->idx[stash->n] = row; 302 stash->idy[stash->n] = idxn[i]; 303 stash->array[stash->n] = values[i*stepval]; 304 stash->n++; 305 } 306 PetscFunctionReturn(0); 307 } 308 309 /* 310 MatStashValuesRowBlocked_Private - inserts blocks of values into the stash. 311 This function expects the values to be roworiented. Multiple columns belong 312 to the same block-row can be inserted with a single call to this function. 313 This function extracts the sub-block of values based on the dimensions of 314 the original input block, and the row,col values corresponding to the blocks. 315 316 Input Parameters: 317 stash - the stash 318 row - the global block-row correspoiding to the values 319 n - the number of elements inserted. All elements belong to the above row. 320 idxn - the global block-column indices corresponding to each of the blocks of 321 values. Each block is of size bs*bs. 322 values - the values inserted 323 rmax - the number of block-rows in the original block. 324 cmax - the number of block-columsn on the original block. 325 idx - the index of the current block-row in the original block. 326 */ 327 #undef __FUNC__ 328 #define __FUNC__ /*<a name=""></a>*/"MatStashValuesRowBlocked_Private" 329 int MatStashValuesRowBlocked_Private(MatStash *stash,int row,int n,int *idxn,MatScalar *values,int rmax,int cmax,int idx) 330 { 331 int ierr,i,j,k,bs2,bs=stash->bs; 332 MatScalar *vals,*array; 333 334 PetscFunctionBegin; 335 bs2 = bs*bs; 336 if ((stash->n+n) > stash->nmax) { 337 ierr = MatStashExpand_Private(stash,n);CHKERRQ(ierr); 338 } 339 for (i=0; i<n; i++) { 340 stash->idx[stash->n] = row; 341 stash->idy[stash->n] = idxn[i]; 342 /* Now copy over the block of values. Store the values column oriented. 343 This enables inserting multiple blocks belonging to a row with a single 344 funtion call */ 345 array = stash->array + bs2*stash->n; 346 vals = values + idx*bs2*n + bs*i; 347 for (j=0; j<bs; j++) { 348 for (k=0; k<bs; k++) {array[k*bs] = vals[k];} 349 array += 1; 350 vals += cmax*bs; 351 } 352 stash->n++; 353 } 354 PetscFunctionReturn(0); 355 } 356 357 /* 358 MatStashValuesColBlocked_Private - inserts blocks of values into the stash. 359 This function expects the values to be roworiented. Multiple columns belong 360 to the same block-row can be inserted with a single call to this function. 361 This function extracts the sub-block of values based on the dimensions of 362 the original input block, and the row,col values corresponding to the blocks. 363 364 Input Parameters: 365 stash - the stash 366 row - the global block-row correspoiding to the values 367 n - the number of elements inserted. All elements belong to the above row. 368 idxn - the global block-column indices corresponding to each of the blocks of 369 values. Each block is of size bs*bs. 370 values - the values inserted 371 rmax - the number of block-rows in the original block. 372 cmax - the number of block-columsn on the original block. 373 idx - the index of the current block-row in the original block. 374 */ 375 #undef __FUNC__ 376 #define __FUNC__ /*<a name=""></a>*/"MatStashValuesColBlocked_Private" 377 int MatStashValuesColBlocked_Private(MatStash *stash,int row,int n,int *idxn,MatScalar *values,int rmax,int cmax,int idx) 378 { 379 int ierr,i,j,k,bs2,bs=stash->bs; 380 MatScalar *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__ /*<a name=""></a>*/"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 MatScalar *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 = (MatScalar*)PetscMalloc((nreceives+1)*(nmax+1)*(bs2*sizeof(MatScalar)+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_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 svalues = (MatScalar*)PetscMalloc((stash->n+1)*(bs2*sizeof(MatScalar)+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 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[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_MATSCALAR,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__ /*<a name=""></a>*/"MatStashScatterGetMesg_Private" 543 int MatStashScatterGetMesg_Private(MatStash *stash,int *nvals,int **rows,int** cols,MatScalar **vals,int *flg) 544 { 545 int i,ierr,size=stash->size,*flg_v,*flg_i,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_MATSCALAR,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