1 #ifndef lint 2 static char vcid[] = "$Id: mpidense.c,v 1.9 1995/11/03 02:49:51 bsmith Exp bsmith $"; 3 #endif 4 5 /* 6 Basic functions for basic parallel dense matrices. 7 */ 8 9 #include "mpidense.h" 10 #include "vec/vecimpl.h" 11 #include "inline/spops.h" 12 13 static int MatSetValues_MPIDense(Mat mat,int m,int *idxm,int n, 14 int *idxn,Scalar *v,InsertMode addv) 15 { 16 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 17 int ierr, i, j, rstart = mdn->rstart, rend = mdn->rend, row; 18 19 if (mdn->insertmode != NOT_SET_VALUES && mdn->insertmode != addv) { 20 SETERRQ(1,"MatSetValues_MPIDense:Cannot mix inserts and adds"); 21 } 22 mdn->insertmode = addv; 23 for ( i=0; i<m; i++ ) { 24 if (idxm[i] < 0) SETERRQ(1,"MatSetValues_MPIDense:Negative row"); 25 if (idxm[i] >= mdn->M) SETERRQ(1,"MatSetValues_MPIDense:Row too large"); 26 if (idxm[i] >= rstart && idxm[i] < rend) { 27 row = idxm[i] - rstart; 28 for ( j=0; j<n; j++ ) { 29 if (idxn[j] < 0) SETERRQ(1,"MatSetValues_MPIDense:Negative column"); 30 if (idxn[j] >= mdn->N) 31 SETERRQ(1,"MatSetValues_MPIDense:Column too large"); 32 ierr = MatSetValues(mdn->A,1,&row,1,&idxn[j],v+i*n+j,addv); 33 CHKERRQ(ierr); 34 } 35 } 36 else { 37 ierr = StashValues_Private(&mdn->stash,idxm[i],n,idxn,v+i*n,addv); 38 CHKERRQ(ierr); 39 } 40 } 41 return 0; 42 } 43 44 static int MatAssemblyBegin_MPIDense(Mat mat,MatAssemblyType mode) 45 { 46 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 47 MPI_Comm comm = mat->comm; 48 int size = mdn->size, *owners = mdn->rowners, rank = mdn->rank; 49 int *nprocs,i,j,idx,*procs,nsends,nreceives,nmax,*work; 50 int tag = mat->tag, *owner,*starts,count,ierr; 51 InsertMode addv; 52 MPI_Request *send_waits,*recv_waits; 53 Scalar *rvalues,*svalues; 54 55 /* make sure all processors are either in INSERTMODE or ADDMODE */ 56 MPI_Allreduce((void *) &mdn->insertmode,(void *) &addv,1,MPI_INT, 57 MPI_BOR,comm); 58 if (addv == (ADD_VALUES|INSERT_VALUES)) { SETERRQ(1, 59 "MatAssemblyBegin_MPIDense:Cannot mix adds/inserts on different procs"); 60 } 61 mdn->insertmode = addv; /* in case this processor had no cache */ 62 63 /* first count number of contributors to each processor */ 64 nprocs = (int *) PetscMalloc( 2*size*sizeof(int) ); CHKPTRQ(nprocs); 65 PetscMemzero(nprocs,2*size*sizeof(int)); procs = nprocs + size; 66 owner = (int *) PetscMalloc( (mdn->stash.n+1)*sizeof(int) ); CHKPTRQ(owner); 67 for ( i=0; i<mdn->stash.n; i++ ) { 68 idx = mdn->stash.idx[i]; 69 for ( j=0; j<size; j++ ) { 70 if (idx >= owners[j] && idx < owners[j+1]) { 71 nprocs[j]++; procs[j] = 1; owner[i] = j; break; 72 } 73 } 74 } 75 nsends = 0; for ( i=0; i<size; i++ ) { nsends += procs[i];} 76 77 /* inform other processors of number of messages and max length*/ 78 work = (int *) PetscMalloc( size*sizeof(int) ); CHKPTRQ(work); 79 MPI_Allreduce((void *) procs,(void *) work,size,MPI_INT,MPI_SUM,comm); 80 nreceives = work[rank]; 81 MPI_Allreduce((void *) nprocs,(void *) work,size,MPI_INT,MPI_MAX,comm); 82 nmax = work[rank]; 83 PetscFree(work); 84 85 /* post receives: 86 1) each message will consist of ordered pairs 87 (global index,value) we store the global index as a double 88 to simplify the message passing. 89 2) since we don't know how long each individual message is we 90 allocate the largest needed buffer for each receive. Potentially 91 this is a lot of wasted space. 92 93 This could be done better. 94 */ 95 rvalues = (Scalar *) PetscMalloc(3*(nreceives+1)*(nmax+1)*sizeof(Scalar)); 96 CHKPTRQ(rvalues); 97 recv_waits = (MPI_Request *) PetscMalloc((nreceives+1)*sizeof(MPI_Request)); 98 CHKPTRQ(recv_waits); 99 for ( i=0; i<nreceives; i++ ) { 100 MPI_Irecv((void *)(rvalues+3*nmax*i),3*nmax,MPIU_SCALAR,MPI_ANY_SOURCE,tag, 101 comm,recv_waits+i); 102 } 103 104 /* do sends: 105 1) starts[i] gives the starting index in svalues for stuff going to 106 the ith processor 107 */ 108 svalues = (Scalar *) PetscMalloc( 3*(mdn->stash.n+1)*sizeof(Scalar) ); 109 CHKPTRQ(svalues); 110 send_waits = (MPI_Request *) PetscMalloc( (nsends+1)*sizeof(MPI_Request)); 111 CHKPTRQ(send_waits); 112 starts = (int *) PetscMalloc( size*sizeof(int) ); CHKPTRQ(starts); 113 starts[0] = 0; 114 for ( i=1; i<size; i++ ) { starts[i] = starts[i-1] + nprocs[i-1];} 115 for ( i=0; i<mdn->stash.n; i++ ) { 116 svalues[3*starts[owner[i]]] = (Scalar) mdn->stash.idx[i]; 117 svalues[3*starts[owner[i]]+1] = (Scalar) mdn->stash.idy[i]; 118 svalues[3*(starts[owner[i]]++)+2] = mdn->stash.array[i]; 119 } 120 PetscFree(owner); 121 starts[0] = 0; 122 for ( i=1; i<size; i++ ) { starts[i] = starts[i-1] + nprocs[i-1];} 123 count = 0; 124 for ( i=0; i<size; i++ ) { 125 if (procs[i]) { 126 MPI_Isend((void*)(svalues+3*starts[i]),3*nprocs[i],MPIU_SCALAR,i,tag, 127 comm,send_waits+count++); 128 } 129 } 130 PetscFree(starts); PetscFree(nprocs); 131 132 /* Free cache space */ 133 ierr = StashDestroy_Private(&mdn->stash); CHKERRQ(ierr); 134 135 mdn->svalues = svalues; mdn->rvalues = rvalues; 136 mdn->nsends = nsends; mdn->nrecvs = nreceives; 137 mdn->send_waits = send_waits; mdn->recv_waits = recv_waits; 138 mdn->rmax = nmax; 139 140 return 0; 141 } 142 extern int MatSetUpMultiply_MPIDense(Mat); 143 144 static int MatAssemblyEnd_MPIDense(Mat mat,MatAssemblyType mode) 145 { 146 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 147 MPI_Status *send_status,recv_status; 148 int imdex, nrecvs=mdn->nrecvs, count=nrecvs, i, n, ierr, row, col; 149 Scalar *values,val; 150 InsertMode addv = mdn->insertmode; 151 152 /* wait on receives */ 153 while (count) { 154 MPI_Waitany(nrecvs,mdn->recv_waits,&imdex,&recv_status); 155 /* unpack receives into our local space */ 156 values = mdn->rvalues + 3*imdex*mdn->rmax; 157 MPI_Get_count(&recv_status,MPIU_SCALAR,&n); 158 n = n/3; 159 for ( i=0; i<n; i++ ) { 160 row = (int) PETSCREAL(values[3*i]) - mdn->rstart; 161 col = (int) PETSCREAL(values[3*i+1]); 162 val = values[3*i+2]; 163 if (col >= 0 && col < mdn->N) { 164 MatSetValues(mdn->A,1,&row,1,&col,&val,addv); 165 } 166 else {SETERRQ(1,"MatAssemblyEnd_MPIDense:Invalid column");} 167 } 168 count--; 169 } 170 PetscFree(mdn->recv_waits); PetscFree(mdn->rvalues); 171 172 /* wait on sends */ 173 if (mdn->nsends) { 174 send_status = (MPI_Status *) PetscMalloc( mdn->nsends*sizeof(MPI_Status) ); 175 CHKPTRQ(send_status); 176 MPI_Waitall(mdn->nsends,mdn->send_waits,send_status); 177 PetscFree(send_status); 178 } 179 PetscFree(mdn->send_waits); PetscFree(mdn->svalues); 180 181 mdn->insertmode = NOT_SET_VALUES; 182 ierr = MatAssemblyBegin(mdn->A,mode); CHKERRQ(ierr); 183 ierr = MatAssemblyEnd(mdn->A,mode); CHKERRQ(ierr); 184 185 if (!mdn->assembled && mode == FINAL_ASSEMBLY) { 186 ierr = MatSetUpMultiply_MPIDense(mat); CHKERRQ(ierr); 187 } 188 mdn->assembled = 1; 189 return 0; 190 } 191 192 static int MatZeroEntries_MPIDense(Mat A) 193 { 194 Mat_MPIDense *l = (Mat_MPIDense *) A->data; 195 return MatZeroEntries(l->A); 196 } 197 198 /* the code does not do the diagonal entries correctly unless the 199 matrix is square and the column and row owerships are identical. 200 This is a BUG. The only way to fix it seems to be to access 201 mdn->A and mdn->B directly and not through the MatZeroRows() 202 routine. 203 */ 204 static int MatZeroRows_MPIDense(Mat A,IS is,Scalar *diag) 205 { 206 Mat_MPIDense *l = (Mat_MPIDense *) A->data; 207 int i,ierr,N, *rows,*owners = l->rowners,size = l->size; 208 int *procs,*nprocs,j,found,idx,nsends,*work; 209 int nmax,*svalues,*starts,*owner,nrecvs,rank = l->rank; 210 int *rvalues,tag = A->tag,count,base,slen,n,*source; 211 int *lens,imdex,*lrows,*values; 212 MPI_Comm comm = A->comm; 213 MPI_Request *send_waits,*recv_waits; 214 MPI_Status recv_status,*send_status; 215 IS istmp; 216 217 if (!l->assembled) SETERRQ(1,"MatZeroRows_MPIDense:Must assemble matrix"); 218 ierr = ISGetLocalSize(is,&N); CHKERRQ(ierr); 219 ierr = ISGetIndices(is,&rows); CHKERRQ(ierr); 220 221 /* first count number of contributors to each processor */ 222 nprocs = (int *) PetscMalloc( 2*size*sizeof(int) ); CHKPTRQ(nprocs); 223 PetscMemzero(nprocs,2*size*sizeof(int)); procs = nprocs + size; 224 owner = (int *) PetscMalloc((N+1)*sizeof(int)); CHKPTRQ(owner); /* see note*/ 225 for ( i=0; i<N; i++ ) { 226 idx = rows[i]; 227 found = 0; 228 for ( j=0; j<size; j++ ) { 229 if (idx >= owners[j] && idx < owners[j+1]) { 230 nprocs[j]++; procs[j] = 1; owner[i] = j; found = 1; break; 231 } 232 } 233 if (!found) SETERRQ(1,"MatZeroRows_MPIDense:Index out of range"); 234 } 235 nsends = 0; for ( i=0; i<size; i++ ) { nsends += procs[i];} 236 237 /* inform other processors of number of messages and max length*/ 238 work = (int *) PetscMalloc( size*sizeof(int) ); CHKPTRQ(work); 239 MPI_Allreduce( procs, work,size,MPI_INT,MPI_SUM,comm); 240 nrecvs = work[rank]; 241 MPI_Allreduce( nprocs, work,size,MPI_INT,MPI_MAX,comm); 242 nmax = work[rank]; 243 PetscFree(work); 244 245 /* post receives: */ 246 rvalues = (int *) PetscMalloc((nrecvs+1)*(nmax+1)*sizeof(int)); /*see note */ 247 CHKPTRQ(rvalues); 248 recv_waits = (MPI_Request *) PetscMalloc((nrecvs+1)*sizeof(MPI_Request)); 249 CHKPTRQ(recv_waits); 250 for ( i=0; i<nrecvs; i++ ) { 251 MPI_Irecv(rvalues+nmax*i,nmax,MPI_INT,MPI_ANY_SOURCE,tag,comm,recv_waits+i); 252 } 253 254 /* do sends: 255 1) starts[i] gives the starting index in svalues for stuff going to 256 the ith processor 257 */ 258 svalues = (int *) PetscMalloc( (N+1)*sizeof(int) ); CHKPTRQ(svalues); 259 send_waits = (MPI_Request *) PetscMalloc( (nsends+1)*sizeof(MPI_Request)); 260 CHKPTRQ(send_waits); 261 starts = (int *) PetscMalloc( (size+1)*sizeof(int) ); CHKPTRQ(starts); 262 starts[0] = 0; 263 for ( i=1; i<size; i++ ) { starts[i] = starts[i-1] + nprocs[i-1];} 264 for ( i=0; i<N; i++ ) { 265 svalues[starts[owner[i]]++] = rows[i]; 266 } 267 ISRestoreIndices(is,&rows); 268 269 starts[0] = 0; 270 for ( i=1; i<size+1; i++ ) { starts[i] = starts[i-1] + nprocs[i-1];} 271 count = 0; 272 for ( i=0; i<size; i++ ) { 273 if (procs[i]) { 274 MPI_Isend(svalues+starts[i],nprocs[i],MPI_INT,i,tag,comm,send_waits+count++); 275 } 276 } 277 PetscFree(starts); 278 279 base = owners[rank]; 280 281 /* wait on receives */ 282 lens = (int *) PetscMalloc( 2*(nrecvs+1)*sizeof(int) ); CHKPTRQ(lens); 283 source = lens + nrecvs; 284 count = nrecvs; slen = 0; 285 while (count) { 286 MPI_Waitany(nrecvs,recv_waits,&imdex,&recv_status); 287 /* unpack receives into our local space */ 288 MPI_Get_count(&recv_status,MPI_INT,&n); 289 source[imdex] = recv_status.MPI_SOURCE; 290 lens[imdex] = n; 291 slen += n; 292 count--; 293 } 294 PetscFree(recv_waits); 295 296 /* move the data into the send scatter */ 297 lrows = (int *) PetscMalloc( (slen+1)*sizeof(int) ); CHKPTRQ(lrows); 298 count = 0; 299 for ( i=0; i<nrecvs; i++ ) { 300 values = rvalues + i*nmax; 301 for ( j=0; j<lens[i]; j++ ) { 302 lrows[count++] = values[j] - base; 303 } 304 } 305 PetscFree(rvalues); PetscFree(lens); 306 PetscFree(owner); PetscFree(nprocs); 307 308 /* actually zap the local rows */ 309 ierr = ISCreateSeq(MPI_COMM_SELF,slen,lrows,&istmp);CHKERRQ(ierr); 310 PLogObjectParent(A,istmp); 311 PetscFree(lrows); 312 ierr = MatZeroRows(l->A,istmp,diag); CHKERRQ(ierr); 313 ierr = ISDestroy(istmp); CHKERRQ(ierr); 314 315 /* wait on sends */ 316 if (nsends) { 317 send_status = (MPI_Status *) PetscMalloc(nsends*sizeof(MPI_Status)); 318 CHKPTRQ(send_status); 319 MPI_Waitall(nsends,send_waits,send_status); 320 PetscFree(send_status); 321 } 322 PetscFree(send_waits); PetscFree(svalues); 323 324 return 0; 325 } 326 327 static int MatMult_MPIDense(Mat mat,Vec xx,Vec yy) 328 { 329 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 330 int ierr; 331 if (!mdn->assembled) 332 SETERRQ(1,"MatMult_MPIDense:Must assemble matrix first"); 333 ierr = VecScatterBegin(xx,mdn->lvec,INSERT_VALUES,SCATTER_ALL,mdn->Mvctx); 334 CHKERRQ(ierr); 335 ierr = VecScatterEnd(xx,mdn->lvec,INSERT_VALUES,SCATTER_ALL,mdn->Mvctx); 336 CHKERRQ(ierr); 337 ierr = MatMult(mdn->A,mdn->lvec,yy); CHKERRQ(ierr); 338 return 0; 339 } 340 341 static int MatMultAdd_MPIDense(Mat mat,Vec xx,Vec yy,Vec zz) 342 { 343 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 344 int ierr; 345 if (!mdn->assembled) 346 SETERRQ(1,"MatMultAdd_MPIDense:Must assemble matrix first"); 347 ierr = VecScatterBegin(xx,mdn->lvec,INSERT_VALUES,SCATTER_ALL,mdn->Mvctx); 348 CHKERRQ(ierr); 349 ierr = VecScatterEnd(xx,mdn->lvec,INSERT_VALUES,SCATTER_ALL,mdn->Mvctx); 350 CHKERRQ(ierr); 351 ierr = MatMultAdd(mdn->A,mdn->lvec,yy,zz); CHKERRQ(ierr); 352 return 0; 353 } 354 355 static int MatMultTrans_MPIDense(Mat A,Vec xx,Vec yy) 356 { 357 Mat_MPIDense *a = (Mat_MPIDense *) A->data; 358 int ierr; 359 Scalar zero = 0.0; 360 361 if (!a->assembled) SETERRQ(1,"MatMulTrans_MPIDense:must assemble matrix"); 362 ierr = VecSet(&zero,yy); CHKERRQ(ierr); 363 ierr = MatMultTrans(a->A,xx,a->lvec); CHKERRQ(ierr); 364 ierr = VecScatterBegin(a->lvec,yy,ADD_VALUES, 365 (ScatterMode)(SCATTER_ALL|SCATTER_REVERSE),a->Mvctx); CHKERRQ(ierr); 366 ierr = VecScatterEnd(a->lvec,yy,ADD_VALUES, 367 (ScatterMode)(SCATTER_ALL|SCATTER_REVERSE),a->Mvctx); CHKERRQ(ierr); 368 return 0; 369 } 370 371 static int MatMultTransAdd_MPIDense(Mat A,Vec xx,Vec yy,Vec zz) 372 { 373 Mat_MPIDense *a = (Mat_MPIDense *) A->data; 374 int ierr; 375 376 if (!a->assembled) SETERRQ(1,"MatMulTransAdd_MPIDense:must assemble matrix"); 377 ierr = VecCopy(yy,zz); CHKERRQ(ierr); 378 ierr = MatMultTrans(a->A,xx,a->lvec); CHKERRQ(ierr); 379 ierr = VecScatterBegin(a->lvec,zz,ADD_VALUES, 380 (ScatterMode)(SCATTER_ALL|SCATTER_REVERSE),a->Mvctx); CHKERRQ(ierr); 381 ierr = VecScatterEnd(a->lvec,zz,ADD_VALUES, 382 (ScatterMode)(SCATTER_ALL|SCATTER_REVERSE),a->Mvctx); CHKERRQ(ierr); 383 return 0; 384 } 385 386 static int MatGetDiagonal_MPIDense(Mat A,Vec v) 387 { 388 Mat_MPIDense *a = (Mat_MPIDense *) A->data; 389 Mat_SeqDense *aloc = (Mat_SeqDense *) a->A->data; 390 int ierr, i, n, m = a->m, radd; 391 Scalar *x; 392 393 if (!a->assembled) SETERRQ(1,"MatGetDiag_MPIDense:must assemble matrix"); 394 ierr = VecGetArray(v,&x); CHKERRQ(ierr); 395 ierr = VecGetSize(v,&n); CHKERRQ(ierr); 396 if (n != a->M) SETERRQ(1,"MatGetDiagonal_SeqDense:Nonconforming mat and vec"); 397 radd = a->rstart*m*m; 398 for ( i=0; i<m; i++ ) { 399 x[i] = aloc->v[radd + i*m + i]; 400 } 401 return 0; 402 } 403 404 static int MatDestroy_MPIDense(PetscObject obj) 405 { 406 Mat mat = (Mat) obj; 407 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 408 int ierr; 409 410 #if defined(PETSC_LOG) 411 PLogObjectState(obj,"Rows=%d, Cols=%d",mdn->M,mdn->N); 412 #endif 413 PetscFree(mdn->rowners); 414 ierr = MatDestroy(mdn->A); CHKERRQ(ierr); 415 if (mdn->lvec) VecDestroy(mdn->lvec); 416 if (mdn->Mvctx) VecScatterDestroy(mdn->Mvctx); 417 PetscFree(mdn); 418 PLogObjectDestroy(mat); 419 PetscHeaderDestroy(mat); 420 return 0; 421 } 422 423 #include "pinclude/pviewer.h" 424 425 static int MatView_MPIDense_Binary(Mat mat,Viewer viewer) 426 { 427 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 428 int ierr; 429 if (mdn->size == 1) { 430 ierr = MatView(mdn->A,viewer); CHKERRQ(ierr); 431 } 432 else SETERRQ(1,"MatView_MPIDense_Binary:Only uniprocessor output supported"); 433 return 0; 434 } 435 436 static int MatView_MPIDense_ASCII(Mat mat,Viewer viewer) 437 { 438 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 439 int ierr, format; 440 PetscObject vobj = (PetscObject) viewer; 441 FILE *fd; 442 443 ierr = ViewerFileGetPointer_Private(viewer,&fd); CHKERRQ(ierr); 444 if (vobj->type == ASCII_FILE_VIEWER || vobj->type == ASCII_FILES_VIEWER) { 445 ierr = ViewerFileGetFormat_Private(viewer,&format); 446 if (format == FILE_FORMAT_INFO_DETAILED) { 447 int nz, nzalloc, mem, rank; 448 MPI_Comm_rank(mat->comm,&rank); 449 ierr = MatGetInfo(mat,MAT_LOCAL,&nz,&nzalloc,&mem); 450 MPIU_Seq_begin(mat->comm,1); 451 fprintf(fd," [%d] local rows %d nz %d nz alloced %d mem %d \n", 452 rank,mdn->m,nz,nzalloc,mem); 453 fflush(fd); 454 MPIU_Seq_end(mat->comm,1); 455 ierr = VecScatterView(mdn->Mvctx,viewer); CHKERRQ(ierr); 456 return 0; 457 } 458 else if (format == FILE_FORMAT_INFO) { 459 return 0; 460 } 461 } 462 if (vobj->type == ASCII_FILE_VIEWER) { 463 MPIU_Seq_begin(mat->comm,1); 464 fprintf(fd,"[%d] rows %d starts %d ends %d cols %d\n", 465 mdn->rank,mdn->m,mdn->rstart,mdn->rend,mdn->n); 466 ierr = MatView(mdn->A,viewer); CHKERRQ(ierr); 467 fflush(fd); 468 MPIU_Seq_end(mat->comm,1); 469 } 470 else { 471 int size = mdn->size, rank = mdn->rank; 472 if (size == 1) { 473 ierr = MatView(mdn->A,viewer); CHKERRQ(ierr); 474 } 475 else { 476 /* assemble the entire matrix onto first processor. */ 477 Mat A; 478 int M = mdn->M, N = mdn->N,m,row,i, nz, *cols; 479 Scalar *vals; 480 Mat_SeqDense *Amdn = (Mat_SeqDense*) mdn->A->data; 481 482 if (!rank) { 483 ierr = MatCreateMPIDense(mat->comm,M,M,N,N,&A); CHKERRQ(ierr); 484 } 485 else { 486 ierr = MatCreateMPIDense(mat->comm,0,M,N,N,&A); CHKERRQ(ierr); 487 } 488 PLogObjectParent(mat,A); 489 490 /* Copy the matrix ... This isn't the most efficient means, 491 but it's quick for now */ 492 row = mdn->rstart; m = Amdn->m; 493 for ( i=0; i<m; i++ ) { 494 ierr = MatGetRow(mat,row,&nz,&cols,&vals); CHKERRQ(ierr); 495 ierr = MatSetValues(A,1,&row,nz,cols,vals,INSERT_VALUES); CHKERRQ(ierr); 496 ierr = MatRestoreRow(mat,row,&nz,&cols,&vals); CHKERRQ(ierr); 497 row++; 498 } 499 500 ierr = MatAssemblyBegin(A,FINAL_ASSEMBLY); CHKERRQ(ierr); 501 ierr = MatAssemblyEnd(A,FINAL_ASSEMBLY); CHKERRQ(ierr); 502 if (!rank) { 503 ierr = MatView(((Mat_MPIDense*)(A->data))->A,viewer); CHKERRQ(ierr); 504 } 505 ierr = MatDestroy(A); CHKERRQ(ierr); 506 } 507 } 508 return 0; 509 } 510 511 static int MatView_MPIDense(PetscObject obj,Viewer viewer) 512 { 513 Mat mat = (Mat) obj; 514 Mat_MPIDense *mdn = (Mat_MPIDense *) mat->data; 515 PetscObject vobj = (PetscObject) viewer; 516 int ierr; 517 518 if (!mdn->assembled) SETERRQ(1,"MatView_MPIDense:must assemble matrix"); 519 if (!viewer) { 520 viewer = STDOUT_VIEWER_SELF; vobj = (PetscObject) viewer; 521 } 522 if (vobj->cookie == VIEWER_COOKIE && vobj->type == ASCII_FILE_VIEWER) { 523 ierr = MatView_MPIDense_ASCII(mat,viewer); CHKERRQ(ierr); 524 } 525 else if (vobj->cookie == VIEWER_COOKIE && vobj->type == ASCII_FILES_VIEWER) { 526 ierr = MatView_MPIDense_ASCII(mat,viewer); CHKERRQ(ierr); 527 } 528 else if (vobj->type == BINARY_FILE_VIEWER) { 529 return MatView_MPIDense_Binary(mat,viewer); 530 } 531 return 0; 532 } 533 534 static int MatGetInfo_MPIDense(Mat A,MatInfoType flag,int *nz, 535 int *nzalloc,int *mem) 536 { 537 Mat_MPIDense *mat = (Mat_MPIDense *) A->data; 538 Mat mdn = mat->A; 539 int ierr, isend[3], irecv[3]; 540 541 ierr = MatGetInfo(mdn,MAT_LOCAL,&isend[0],&isend[1],&isend[2]); CHKERRQ(ierr); 542 if (flag == MAT_LOCAL) { 543 *nz = isend[0]; *nzalloc = isend[1]; *mem = isend[2]; 544 } else if (flag == MAT_GLOBAL_MAX) { 545 MPI_Allreduce(isend,irecv,3,MPI_INT,MPI_MAX,A->comm); 546 *nz = irecv[0]; *nzalloc = irecv[1]; *mem = irecv[2]; 547 } else if (flag == MAT_GLOBAL_SUM) { 548 MPI_Allreduce(isend,irecv,3,MPI_INT,MPI_SUM,A->comm); 549 *nz = irecv[0]; *nzalloc = irecv[1]; *mem = irecv[2]; 550 } 551 return 0; 552 } 553 554 static int MatSetOption_MPIDense(Mat A,MatOption op) 555 { 556 Mat_MPIDense *a = (Mat_MPIDense *) A->data; 557 558 if (op == NO_NEW_NONZERO_LOCATIONS || 559 op == YES_NEW_NONZERO_LOCATIONS || 560 op == COLUMNS_SORTED || 561 op == ROW_ORIENTED) { 562 MatSetOption(a->A,op); 563 } 564 else if (op == ROWS_SORTED || 565 op == SYMMETRIC_MATRIX || 566 op == STRUCTURALLY_SYMMETRIC_MATRIX || 567 op == YES_NEW_DIAGONALS) 568 PLogInfo((PetscObject)A,"Info:MatSetOption_MPIDense:Option ignored\n"); 569 else if (op == COLUMN_ORIENTED) 570 {SETERRQ(PETSC_ERR_SUP,"MatSetOption_MPIDense:COLUMN_ORIENTED");} 571 else if (op == NO_NEW_DIAGONALS) 572 {SETERRQ(PETSC_ERR_SUP,"MatSetOption_MPIDense:NO_NEW_DIAGONALS");} 573 else 574 {SETERRQ(PETSC_ERR_SUP,"MatSetOption_MPIDense:unknown option");} 575 return 0; 576 } 577 578 static int MatGetSize_MPIDense(Mat A,int *m,int *n) 579 { 580 Mat_MPIDense *mat = (Mat_MPIDense *) A->data; 581 *m = mat->M; *n = mat->N; 582 return 0; 583 } 584 585 static int MatGetLocalSize_MPIDense(Mat A,int *m,int *n) 586 { 587 Mat_MPIDense *mat = (Mat_MPIDense *) A->data; 588 *m = mat->m; *n = mat->N; 589 return 0; 590 } 591 592 static int MatGetOwnershipRange_MPIDense(Mat A,int *m,int *n) 593 { 594 Mat_MPIDense *mat = (Mat_MPIDense *) A->data; 595 *m = mat->rstart; *n = mat->rend; 596 return 0; 597 } 598 599 static int MatGetRow_MPIDense(Mat A,int row,int *nz,int **idx,Scalar **v) 600 { 601 Mat_MPIDense *mat = (Mat_MPIDense *) A->data; 602 int lrow, rstart = mat->rstart, rend = mat->rend; 603 604 if (row < rstart || row >= rend) SETERRQ(1,"MatGetRow_MPIDense:only local rows") 605 lrow = row - rstart; 606 return MatGetRow(mat->A,lrow,nz,idx,v); 607 } 608 609 static int MatRestoreRow_MPIDense(Mat mat,int row,int *nz,int **idx,Scalar **v) 610 { 611 if (idx) PetscFree(*idx); 612 if (v) PetscFree(*v); 613 return 0; 614 } 615 616 static int MatNorm_MPIDense(Mat A,NormType type,double *norm) 617 { 618 Mat_MPIDense *mdn = (Mat_MPIDense *) A->data; 619 Mat_SeqDense *mat = (Mat_SeqDense*) mdn->A->data; 620 int ierr, i, j; 621 double sum = 0.0; 622 Scalar *v = mat->v; 623 624 if (!mdn->assembled) SETERRQ(1,"MatNorm_MPIDense:Must assemble matrix"); 625 if (mdn->size == 1) { 626 ierr = MatNorm(mdn->A,type,norm); CHKERRQ(ierr); 627 } else { 628 if (type == NORM_FROBENIUS) { 629 for (i=0; i<mat->n*mat->m; i++ ) { 630 #if defined(PETSC_COMPLEX) 631 sum += real(conj(*v)*(*v)); v++; 632 #else 633 sum += (*v)*(*v); v++; 634 #endif 635 } 636 MPI_Allreduce((void*)&sum,(void*)norm,1,MPI_DOUBLE,MPI_SUM,A->comm); 637 *norm = sqrt(*norm); 638 PLogFlops(2*mat->n*mat->m); 639 } 640 else if (type == NORM_1) { 641 double *tmp, *tmp2; 642 tmp = (double *) PetscMalloc( 2*mdn->N*sizeof(double) ); CHKPTRQ(tmp); 643 tmp2 = tmp + mdn->N; 644 PetscMemzero(tmp,2*mdn->N*sizeof(double)); 645 *norm = 0.0; 646 v = mat->v; 647 for ( j=0; j<mat->n; j++ ) { 648 for ( i=0; i<mat->m; i++ ) { 649 tmp[j] += PetscAbsScalar(*v); v++; 650 } 651 } 652 MPI_Allreduce((void*)tmp,(void*)tmp2,mdn->N,MPI_DOUBLE,MPI_SUM,A->comm); 653 for ( j=0; j<mdn->N; j++ ) { 654 if (tmp2[j] > *norm) *norm = tmp2[j]; 655 } 656 PetscFree(tmp); 657 PLogFlops(mat->n*mat->m); 658 } 659 else if (type == NORM_INFINITY) { /* max row norm */ 660 double ntemp; 661 ierr = MatNorm(mdn->A,type,&ntemp); CHKERRQ(ierr); 662 MPI_Allreduce((void*)&ntemp,(void*)norm,1,MPI_DOUBLE,MPI_MAX,A->comm); 663 } 664 else { 665 SETERRQ(1,"MatNorm_MPIDense:No support for two norm"); 666 } 667 } 668 return 0; 669 } 670 671 static int MatTranspose_MPIDense(Mat A,Mat *matout) 672 { 673 Mat_MPIDense *a = (Mat_MPIDense *) A->data; 674 Mat_SeqDense *Aloc = (Mat_SeqDense *) a->A->data; 675 Mat B; 676 int M = a->M, N = a->N, m, n, *rwork, rstart = a->rstart; 677 int j, i, ierr; 678 Scalar *v; 679 680 if (!matout && M != N) 681 SETERRQ(1,"MatTranspose_MPIDense:Supports square matrix only in-place"); 682 ierr = MatCreateMPIDense(A->comm,PETSC_DECIDE,PETSC_DECIDE,N,M,&B); CHKERRQ(ierr); 683 684 m = Aloc->m; n = Aloc->n; v = Aloc->v; 685 rwork = (int *) PetscMalloc(n*sizeof(int)); CHKPTRQ(rwork); 686 for ( j=0; j<n; j++ ) { 687 for (i=0; i<m; i++) rwork[i] = rstart + i; 688 ierr = MatSetValues(B,1,&j,m,rwork,v,INSERT_VALUES); CHKERRQ(ierr); 689 v += m; 690 } 691 PetscFree(rwork); 692 ierr = MatAssemblyBegin(B,FINAL_ASSEMBLY); CHKERRQ(ierr); 693 ierr = MatAssemblyEnd(B,FINAL_ASSEMBLY); CHKERRQ(ierr); 694 if (matout) { 695 *matout = B; 696 } else { 697 /* This isn't really an in-place transpose, but free data struct from a */ 698 PetscFree(a->rowners); 699 ierr = MatDestroy(a->A); CHKERRQ(ierr); 700 if (a->lvec) VecDestroy(a->lvec); 701 if (a->Mvctx) VecScatterDestroy(a->Mvctx); 702 PetscFree(a); 703 PetscMemcpy(A,B,sizeof(struct _Mat)); 704 PetscHeaderDestroy(B); 705 } 706 return 0; 707 } 708 709 static int MatCopyPrivate_MPIDense(Mat,Mat *,int); 710 711 /* -------------------------------------------------------------------*/ 712 static struct _MatOps MatOps = {MatSetValues_MPIDense, 713 MatGetRow_MPIDense,MatRestoreRow_MPIDense, 714 MatMult_MPIDense,MatMultAdd_MPIDense, 715 MatMultTrans_MPIDense,MatMultTransAdd_MPIDense, 716 0,0, 717 0,0,0, 718 0,0,MatTranspose_MPIDense, 719 MatGetInfo_MPIDense,0, 720 MatGetDiagonal_MPIDense,0,MatNorm_MPIDense, 721 MatAssemblyBegin_MPIDense,MatAssemblyEnd_MPIDense, 722 0, 723 MatSetOption_MPIDense,MatZeroEntries_MPIDense,MatZeroRows_MPIDense, 724 0, 725 0,0,0,0, 726 MatGetSize_MPIDense,MatGetLocalSize_MPIDense, 727 MatGetOwnershipRange_MPIDense, 728 0,0, 729 0,0,0,0,0,MatCopyPrivate_MPIDense}; 730 731 /*@C 732 MatCreateMPIDense - Creates a sparse parallel matrix in dense format. 733 734 Input Parameters: 735 . comm - MPI communicator 736 . m - number of local rows (or PETSC_DECIDE to have calculated if M is given) 737 . n - number of local columns (or PETSC_DECIDE to have calculated 738 if N is given) 739 . M - number of global rows (or PETSC_DECIDE to have calculated if m is given) 740 . N - number of global columns (or PETSC_DECIDE to have calculated 741 if n is given) 742 743 Output Parameter: 744 . newmat - the matrix 745 746 Notes: 747 The dense format is fully compatible with standard Fortran 77 748 storage by columns. 749 750 The user MUST specify either the local or global matrix dimensions 751 (possibly both). 752 753 Currently, the only parallel dense matrix decomposition is by rows, 754 so that n=N and each submatrix owns all of the global columns. 755 756 .keywords: matrix, dense, parallel 757 758 .seealso: MatCreate(), MatCreateSeqDense(), MatSetValues() 759 @*/ 760 int MatCreateMPIDense(MPI_Comm comm,int m,int n,int M,int N,Mat *newmat) 761 { 762 Mat mat; 763 Mat_MPIDense *a; 764 int ierr, i; 765 766 *newmat = 0; 767 PetscHeaderCreate(mat,_Mat,MAT_COOKIE,MATMPIDENSE,comm); 768 PLogObjectCreate(mat); 769 mat->data = (void *) (a = PetscNew(Mat_MPIDense)); CHKPTRQ(a); 770 PetscMemcpy(&mat->ops,&MatOps,sizeof(struct _MatOps)); 771 mat->destroy = MatDestroy_MPIDense; 772 mat->view = MatView_MPIDense; 773 mat->factor = 0; 774 775 a->insertmode = NOT_SET_VALUES; 776 MPI_Comm_rank(comm,&a->rank); 777 MPI_Comm_size(comm,&a->size); 778 779 if (M == PETSC_DECIDE) MPI_Allreduce(&m,&M,1,MPI_INT,MPI_SUM,comm); 780 if (m == PETSC_DECIDE) {m = M/a->size + ((M % a->size) > a->rank);} 781 782 /* each row stores all columns */ 783 if (N == PETSC_DECIDE) N = n; 784 if (n == PETSC_DECIDE) n = N; 785 if (n != N) SETERRQ(1,"MatCreateMPIDense:For now, only n=N is supported"); 786 a->N = N; 787 a->M = M; 788 a->m = m; 789 a->n = n; 790 791 /* build local table of row and column ownerships */ 792 a->rowners = (int *) PetscMalloc((a->size+2)*sizeof(int)); CHKPTRQ(a->rowners); 793 PLogObjectMemory(mat,(a->size+2)*sizeof(int)+sizeof(struct _Mat)+ 794 sizeof(Mat_MPIDense)); 795 MPI_Allgather(&m,1,MPI_INT,a->rowners+1,1,MPI_INT,comm); 796 a->rowners[0] = 0; 797 for ( i=2; i<=a->size; i++ ) { 798 a->rowners[i] += a->rowners[i-1]; 799 } 800 a->rstart = a->rowners[a->rank]; 801 a->rend = a->rowners[a->rank+1]; 802 803 ierr = MatCreateSeqDense(MPI_COMM_SELF,m,N,&a->A); CHKERRQ(ierr); 804 PLogObjectParent(mat,a->A); 805 806 /* build cache for off array entries formed */ 807 ierr = StashBuild_Private(&a->stash); CHKERRQ(ierr); 808 809 /* stuff used for matrix vector multiply */ 810 a->lvec = 0; 811 a->Mvctx = 0; 812 a->assembled = 0; 813 814 *newmat = mat; 815 return 0; 816 } 817 818 static int MatCopyPrivate_MPIDense(Mat A,Mat *newmat,int cpvalues) 819 { 820 Mat mat; 821 Mat_MPIDense *a,*oldmat = (Mat_MPIDense *) A->data; 822 int ierr; 823 824 if (!oldmat->assembled) SETERRQ(1,"MatCopyPrivate_MPIDense:Must assemble matrix"); 825 *newmat = 0; 826 PetscHeaderCreate(mat,_Mat,MAT_COOKIE,MATMPIDENSE,A->comm); 827 PLogObjectCreate(mat); 828 mat->data = (void *) (a = PetscNew(Mat_MPIDense)); CHKPTRQ(a); 829 PetscMemcpy(&mat->ops,&MatOps,sizeof(struct _MatOps)); 830 mat->destroy = MatDestroy_MPIDense; 831 mat->view = MatView_MPIDense; 832 mat->factor = A->factor; 833 834 a->m = oldmat->m; 835 a->n = oldmat->n; 836 a->M = oldmat->M; 837 a->N = oldmat->N; 838 839 a->assembled = 1; 840 a->rstart = oldmat->rstart; 841 a->rend = oldmat->rend; 842 a->size = oldmat->size; 843 a->rank = oldmat->rank; 844 a->insertmode = NOT_SET_VALUES; 845 846 a->rowners = (int *) PetscMalloc((a->size+1)*sizeof(int)); CHKPTRQ(a->rowners); 847 PLogObjectMemory(mat,(a->size+1)*sizeof(int)+sizeof(struct _Mat)+sizeof(Mat_MPIDense)); 848 PetscMemcpy(a->rowners,oldmat->rowners,(a->size+1)*sizeof(int)); 849 ierr = StashInitialize_Private(&a->stash); CHKERRQ(ierr); 850 851 ierr = VecDuplicate(oldmat->lvec,&a->lvec); CHKERRQ(ierr); 852 PLogObjectParent(mat,a->lvec); 853 ierr = VecScatterCopy(oldmat->Mvctx,&a->Mvctx); CHKERRQ(ierr); 854 PLogObjectParent(mat,a->Mvctx); 855 ierr = MatConvert(oldmat->A,MATSAME,&a->A); CHKERRQ(ierr); 856 PLogObjectParent(mat,a->A); 857 *newmat = mat; 858 return 0; 859 } 860 861 #include "sysio.h" 862 863 int MatLoad_MPIDense(Viewer bview,MatType type,Mat *newmat) 864 { 865 Mat A; 866 int i, nz, ierr, j,rstart, rend, fd; 867 Scalar *vals,*svals; 868 PetscObject vobj = (PetscObject) bview; 869 MPI_Comm comm = vobj->comm; 870 MPI_Status status; 871 int header[4],rank,size,*rowlengths = 0,M,N,m,*rowners,maxnz,*cols; 872 int *ourlens,*sndcounts = 0,*procsnz = 0, *offlens,jj,*mycols,*smycols; 873 int tag = ((PetscObject)bview)->tag; 874 875 MPI_Comm_size(comm,&size); MPI_Comm_rank(comm,&rank); 876 if (!rank) { 877 ierr = ViewerFileGetDescriptor_Private(bview,&fd); CHKERRQ(ierr); 878 ierr = SYRead(fd,(char *)header,4,SYINT); CHKERRQ(ierr); 879 if (header[0] != MAT_COOKIE) SETERRQ(1,"MatLoad_MPIDenseorMPIRow:not matrix object"); 880 } 881 882 MPI_Bcast(header+1,3,MPI_INT,0,comm); 883 M = header[1]; N = header[2]; 884 /* determine ownership of all rows */ 885 m = M/size + ((M % size) > rank); 886 rowners = (int *) PetscMalloc((size+2)*sizeof(int)); CHKPTRQ(rowners); 887 MPI_Allgather(&m,1,MPI_INT,rowners+1,1,MPI_INT,comm); 888 rowners[0] = 0; 889 for ( i=2; i<=size; i++ ) { 890 rowners[i] += rowners[i-1]; 891 } 892 rstart = rowners[rank]; 893 rend = rowners[rank+1]; 894 895 /* distribute row lengths to all processors */ 896 ourlens = (int*) PetscMalloc( 2*(rend-rstart)*sizeof(int) ); CHKPTRQ(ourlens); 897 offlens = ourlens + (rend-rstart); 898 if (!rank) { 899 rowlengths = (int*) PetscMalloc( M*sizeof(int) ); CHKPTRQ(rowlengths); 900 ierr = SYRead(fd,rowlengths,M,SYINT); CHKERRQ(ierr); 901 sndcounts = (int*) PetscMalloc( size*sizeof(int) ); CHKPTRQ(sndcounts); 902 for ( i=0; i<size; i++ ) sndcounts[i] = rowners[i+1] - rowners[i]; 903 MPI_Scatterv(rowlengths,sndcounts,rowners,MPI_INT,ourlens,rend-rstart,MPI_INT,0,comm); 904 PetscFree(sndcounts); 905 } 906 else { 907 MPI_Scatterv(0,0,0,MPI_INT,ourlens,rend-rstart,MPI_INT, 0,comm); 908 } 909 910 if (!rank) { 911 /* calculate the number of nonzeros on each processor */ 912 procsnz = (int*) PetscMalloc( size*sizeof(int) ); CHKPTRQ(procsnz); 913 PetscMemzero(procsnz,size*sizeof(int)); 914 for ( i=0; i<size; i++ ) { 915 for ( j=rowners[i]; j< rowners[i+1]; j++ ) { 916 procsnz[i] += rowlengths[j]; 917 } 918 } 919 PetscFree(rowlengths); 920 921 /* determine max buffer needed and allocate it */ 922 maxnz = 0; 923 for ( i=0; i<size; i++ ) { 924 maxnz = PetscMax(maxnz,procsnz[i]); 925 } 926 cols = (int *) PetscMalloc( maxnz*sizeof(int) ); CHKPTRQ(cols); 927 928 /* read in my part of the matrix column indices */ 929 nz = procsnz[0]; 930 mycols = (int *) PetscMalloc( nz*sizeof(int) ); CHKPTRQ(mycols); 931 ierr = SYRead(fd,mycols,nz,SYINT); CHKERRQ(ierr); 932 933 /* read in every one elses and ship off */ 934 for ( i=1; i<size; i++ ) { 935 nz = procsnz[i]; 936 ierr = SYRead(fd,cols,nz,SYINT); CHKERRQ(ierr); 937 MPI_Send(cols,nz,MPI_INT,i,tag,comm); 938 } 939 PetscFree(cols); 940 } 941 else { 942 /* determine buffer space needed for message */ 943 nz = 0; 944 for ( i=0; i<m; i++ ) { 945 nz += ourlens[i]; 946 } 947 mycols = (int*) PetscMalloc( nz*sizeof(int) ); CHKPTRQ(mycols); 948 949 /* receive message of column indices*/ 950 MPI_Recv(mycols,nz,MPI_INT,0,tag,comm,&status); 951 MPI_Get_count(&status,MPI_INT,&maxnz); 952 if (maxnz != nz) SETERRQ(1,"MatLoad_MPIDenseorMPIRow:something is wrong with file"); 953 } 954 955 /* loop over local rows, determining number of off diagonal entries */ 956 PetscMemzero(offlens,m*sizeof(int)); 957 jj = 0; 958 for ( i=0; i<m; i++ ) { 959 for ( j=0; j<ourlens[i]; j++ ) { 960 if (mycols[jj] < rstart || mycols[jj] >= rend) offlens[i]++; 961 jj++; 962 } 963 } 964 965 /* create our matrix */ 966 for ( i=0; i<m; i++ ) { 967 ourlens[i] -= offlens[i]; 968 } 969 if (type == MATMPIDENSE) { 970 ierr = MatCreateMPIDense(comm,m,PETSC_DECIDE,M,N,newmat);CHKERRQ(ierr); 971 } 972 A = *newmat; 973 for ( i=0; i<m; i++ ) { 974 ourlens[i] += offlens[i]; 975 } 976 977 if (!rank) { 978 vals = (Scalar *) PetscMalloc( maxnz*sizeof(Scalar) ); CHKPTRQ(vals); 979 980 /* read in my part of the matrix numerical values */ 981 nz = procsnz[0]; 982 ierr = SYRead(fd,vals,nz,SYSCALAR); CHKERRQ(ierr); 983 984 /* insert into matrix */ 985 jj = rstart; 986 smycols = mycols; 987 svals = vals; 988 for ( i=0; i<m; i++ ) { 989 ierr = MatSetValues(A,1,&jj,ourlens[i],smycols,svals,INSERT_VALUES);CHKERRQ(ierr); 990 smycols += ourlens[i]; 991 svals += ourlens[i]; 992 jj++; 993 } 994 995 /* read in other processors and ship out */ 996 for ( i=1; i<size; i++ ) { 997 nz = procsnz[i]; 998 ierr = SYRead(fd,vals,nz,SYSCALAR); CHKERRQ(ierr); 999 MPI_Send(vals,nz,MPIU_SCALAR,i,A->tag,comm); 1000 } 1001 PetscFree(procsnz); 1002 } 1003 else { 1004 /* receive numeric values */ 1005 vals = (Scalar*) PetscMalloc( nz*sizeof(Scalar) ); CHKPTRQ(vals); 1006 1007 /* receive message of values*/ 1008 MPI_Recv(vals,nz,MPIU_SCALAR,0,A->tag,comm,&status); 1009 MPI_Get_count(&status,MPIU_SCALAR,&maxnz); 1010 if (maxnz != nz) SETERRQ(1,"MatLoad_MPIDenseorMPIRow:something is wrong with file"); 1011 1012 /* insert into matrix */ 1013 jj = rstart; 1014 smycols = mycols; 1015 svals = vals; 1016 for ( i=0; i<m; i++ ) { 1017 ierr = MatSetValues(A,1,&jj,ourlens[i],smycols,svals,INSERT_VALUES);CHKERRQ(ierr); 1018 smycols += ourlens[i]; 1019 svals += ourlens[i]; 1020 jj++; 1021 } 1022 } 1023 PetscFree(ourlens); PetscFree(vals); PetscFree(mycols); PetscFree(rowners); 1024 1025 ierr = MatAssemblyBegin(A,FINAL_ASSEMBLY); CHKERRQ(ierr); 1026 ierr = MatAssemblyEnd(A,FINAL_ASSEMBLY); CHKERRQ(ierr); 1027 return 0; 1028 } 1029