1 2 /* 3 This provides a simple shell for Fortran (and C programmers) to 4 create a very simple matrix class for use with KSP without coding 5 much of anything. 6 */ 7 8 #include <petsc/private/matimpl.h> /*I "petscmat.h" I*/ 9 10 struct _MatShellOps { 11 /* 0 */ 12 PetscErrorCode (*mult)(Mat,Vec,Vec); 13 /* 5 */ 14 PetscErrorCode (*multtranspose)(Mat,Vec,Vec); 15 /* 10 */ 16 /* 15 */ 17 PetscErrorCode (*getdiagonal)(Mat,Vec); 18 /* 20 */ 19 /* 24 */ 20 /* 29 */ 21 /* 34 */ 22 /* 39 */ 23 PetscErrorCode (*copy)(Mat,Mat,MatStructure); 24 /* 44 */ 25 PetscErrorCode (*diagonalset)(Mat,Vec,InsertMode); 26 /* 49 */ 27 /* 54 */ 28 /* 59 */ 29 PetscErrorCode (*destroy)(Mat); 30 /* 64 */ 31 /* 69 */ 32 /* 74 */ 33 /* 79 */ 34 /* 84 */ 35 /* 89 */ 36 /* 94 */ 37 /* 99 */ 38 /* 104 */ 39 /* 109 */ 40 /* 114 */ 41 /* 119 */ 42 /* 124 */ 43 /* 129 */ 44 /* 134 */ 45 /* 139 */ 46 /* 144 */ 47 }; 48 49 typedef struct { 50 struct _MatShellOps ops[1]; 51 52 PetscScalar vscale,vshift; 53 Vec dshift; 54 Vec left,right; 55 Vec dshift_owned,left_owned,right_owned; 56 Vec left_work,right_work; 57 Vec left_add_work,right_add_work; 58 PetscBool usingscaled; 59 void *ctx; 60 } Mat_Shell; 61 /* 62 The most general expression for the matrix is 63 64 S = L (a A + B) R 65 66 where 67 A is the matrix defined by the user's function 68 a is a scalar multiple 69 L is left scaling 70 R is right scaling 71 B is a diagonal shift defined by 72 diag(dshift) if the vector dshift is non-NULL 73 vscale*identity otherwise 74 75 The following identities apply: 76 77 Scale by c: 78 c [L (a A + B) R] = L [(a c) A + c B] R 79 80 Shift by c: 81 [L (a A + B) R] + c = L [a A + (B + c Linv Rinv)] R 82 83 Diagonal scaling is achieved by simply multiplying with existing L and R vectors 84 85 In the data structure: 86 87 vscale=1.0 means no special scaling will be applied 88 dshift=NULL means a constant diagonal shift (fall back to vshift) 89 vshift=0.0 means no constant diagonal shift, note that vshift is only used if dshift is NULL 90 */ 91 92 static PetscErrorCode MatMult_Shell(Mat,Vec,Vec); 93 static PetscErrorCode MatMultTranspose_Shell(Mat,Vec,Vec); 94 static PetscErrorCode MatGetDiagonal_Shell(Mat,Vec); 95 static PetscErrorCode MatCopy_Shell(Mat,Mat,MatStructure); 96 97 static PetscErrorCode MatShellUseScaledMethods(Mat Y) 98 { 99 Mat_Shell *shell = (Mat_Shell*)Y->data; 100 101 PetscFunctionBegin; 102 if (shell->usingscaled) PetscFunctionReturn(0); 103 shell->ops->mult = Y->ops->mult; 104 Y->ops->mult = MatMult_Shell; 105 if (Y->ops->multtranspose) { 106 shell->ops->multtranspose = Y->ops->multtranspose; 107 Y->ops->multtranspose = MatMultTranspose_Shell; 108 } 109 if (Y->ops->getdiagonal) { 110 shell->ops->getdiagonal = Y->ops->getdiagonal; 111 Y->ops->getdiagonal = MatGetDiagonal_Shell; 112 } 113 if (Y->ops->copy) { 114 shell->ops->copy = Y->ops->copy; 115 Y->ops->copy = MatCopy_Shell; 116 } 117 shell->usingscaled = PETSC_TRUE; 118 PetscFunctionReturn(0); 119 } 120 121 static PetscErrorCode MatShellPreScaleLeft(Mat A,Vec x,Vec *xx) 122 { 123 Mat_Shell *shell = (Mat_Shell*)A->data; 124 PetscErrorCode ierr; 125 126 PetscFunctionBegin; 127 *xx = NULL; 128 if (!shell->left) { 129 *xx = x; 130 } else { 131 if (!shell->left_work) {ierr = VecDuplicate(shell->left,&shell->left_work);CHKERRQ(ierr);} 132 ierr = VecPointwiseMult(shell->left_work,x,shell->left);CHKERRQ(ierr); 133 *xx = shell->left_work; 134 } 135 PetscFunctionReturn(0); 136 } 137 138 static PetscErrorCode MatShellPreScaleRight(Mat A,Vec x,Vec *xx) 139 { 140 Mat_Shell *shell = (Mat_Shell*)A->data; 141 PetscErrorCode ierr; 142 143 PetscFunctionBegin; 144 *xx = NULL; 145 if (!shell->right) { 146 *xx = x; 147 } else { 148 if (!shell->right_work) {ierr = VecDuplicate(shell->right,&shell->right_work);CHKERRQ(ierr);} 149 ierr = VecPointwiseMult(shell->right_work,x,shell->right);CHKERRQ(ierr); 150 *xx = shell->right_work; 151 } 152 PetscFunctionReturn(0); 153 } 154 155 static PetscErrorCode MatShellPostScaleLeft(Mat A,Vec x) 156 { 157 Mat_Shell *shell = (Mat_Shell*)A->data; 158 PetscErrorCode ierr; 159 160 PetscFunctionBegin; 161 if (shell->left) {ierr = VecPointwiseMult(x,x,shell->left);CHKERRQ(ierr);} 162 PetscFunctionReturn(0); 163 } 164 165 static PetscErrorCode MatShellPostScaleRight(Mat A,Vec x) 166 { 167 Mat_Shell *shell = (Mat_Shell*)A->data; 168 PetscErrorCode ierr; 169 170 PetscFunctionBegin; 171 if (shell->right) {ierr = VecPointwiseMult(x,x,shell->right);CHKERRQ(ierr);} 172 PetscFunctionReturn(0); 173 } 174 175 static PetscErrorCode MatShellShiftAndScale(Mat A,Vec X,Vec Y) 176 { 177 Mat_Shell *shell = (Mat_Shell*)A->data; 178 PetscErrorCode ierr; 179 180 PetscFunctionBegin; 181 if (shell->dshift) { /* get arrays because there is no VecPointwiseMultAdd() */ 182 PetscInt i,m; 183 const PetscScalar *x,*d; 184 PetscScalar *y; 185 ierr = VecGetLocalSize(X,&m);CHKERRQ(ierr); 186 ierr = VecGetArrayRead(shell->dshift,&d);CHKERRQ(ierr); 187 ierr = VecGetArrayRead(X,&x);CHKERRQ(ierr); 188 ierr = VecGetArray(Y,&y);CHKERRQ(ierr); 189 for (i=0; i<m; i++) y[i] = shell->vscale*y[i] + d[i]*x[i]; 190 ierr = VecRestoreArrayRead(shell->dshift,&d);CHKERRQ(ierr); 191 ierr = VecRestoreArrayRead(X,&x);CHKERRQ(ierr); 192 ierr = VecRestoreArray(Y,&y);CHKERRQ(ierr); 193 } else if (PetscAbsScalar(shell->vshift) != 0) { 194 ierr = VecAXPBY(Y,shell->vshift,shell->vscale,X);CHKERRQ(ierr); 195 } else if (shell->vscale != 1.0) { 196 ierr = VecScale(Y,shell->vscale);CHKERRQ(ierr); 197 } 198 PetscFunctionReturn(0); 199 } 200 201 /*@ 202 MatShellGetContext - Returns the user-provided context associated with a shell matrix. 203 204 Not Collective 205 206 Input Parameter: 207 . mat - the matrix, should have been created with MatCreateShell() 208 209 Output Parameter: 210 . ctx - the user provided context 211 212 Level: advanced 213 214 Fortran Notes: To use this from Fortran you must write a Fortran interface definition for this 215 function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument. 216 217 .keywords: matrix, shell, get, context 218 219 .seealso: MatCreateShell(), MatShellSetOperation(), MatShellSetContext() 220 @*/ 221 PetscErrorCode MatShellGetContext(Mat mat,void *ctx) 222 { 223 PetscErrorCode ierr; 224 PetscBool flg; 225 226 PetscFunctionBegin; 227 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 228 PetscValidPointer(ctx,2); 229 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 230 if (flg) *(void**)ctx = ((Mat_Shell*)(mat->data))->ctx; 231 else SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot get context from non-shell matrix"); 232 PetscFunctionReturn(0); 233 } 234 235 PetscErrorCode MatDestroy_Shell(Mat mat) 236 { 237 PetscErrorCode ierr; 238 Mat_Shell *shell = (Mat_Shell*)mat->data; 239 240 PetscFunctionBegin; 241 if (shell->ops->destroy) { 242 ierr = (*shell->ops->destroy)(mat);CHKERRQ(ierr); 243 } 244 ierr = VecDestroy(&shell->left_owned);CHKERRQ(ierr); 245 ierr = VecDestroy(&shell->right_owned);CHKERRQ(ierr); 246 ierr = VecDestroy(&shell->dshift_owned);CHKERRQ(ierr); 247 ierr = VecDestroy(&shell->left_work);CHKERRQ(ierr); 248 ierr = VecDestroy(&shell->right_work);CHKERRQ(ierr); 249 ierr = VecDestroy(&shell->left_add_work);CHKERRQ(ierr); 250 ierr = VecDestroy(&shell->right_add_work);CHKERRQ(ierr); 251 ierr = PetscFree(mat->data);CHKERRQ(ierr); 252 PetscFunctionReturn(0); 253 } 254 255 PetscErrorCode MatCopy_Shell(Mat A,Mat B,MatStructure str) 256 { 257 Mat_Shell *shellA = (Mat_Shell*)A->data,*shellB = (Mat_Shell*)B->data; 258 PetscErrorCode ierr; 259 PetscBool matflg; 260 261 PetscFunctionBegin; 262 ierr = PetscObjectTypeCompare((PetscObject)B,MATSHELL,&matflg);CHKERRQ(ierr); 263 if (!matflg) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_NOTSAMETYPE,"Output matrix must be a MATSHELL"); 264 265 ierr = MatShellUseScaledMethods(B);CHKERRQ(ierr); 266 ierr = PetscMemcpy(B->ops,A->ops,sizeof(struct _MatOps));CHKERRQ(ierr); 267 ierr = PetscMemcpy(shellB->ops,shellA->ops,sizeof(struct _MatShellOps));CHKERRQ(ierr); 268 269 if (shellA->ops->copy) { 270 ierr = (*shellA->ops->copy)(A,B,str);CHKERRQ(ierr); 271 } 272 shellB->vscale = shellA->vscale; 273 shellB->vshift = shellA->vshift; 274 if (shellA->dshift_owned) { 275 if (!shellB->dshift_owned) { 276 ierr = VecDuplicate(shellA->dshift_owned,&shellB->dshift_owned);CHKERRQ(ierr); 277 } 278 ierr = VecCopy(shellA->dshift_owned,shellB->dshift_owned);CHKERRQ(ierr); 279 shellB->dshift = shellB->dshift_owned; 280 } else { 281 shellB->dshift = NULL; 282 } 283 if (shellA->left_owned) { 284 if (!shellB->left_owned) { 285 ierr = VecDuplicate(shellA->left_owned,&shellB->left_owned);CHKERRQ(ierr); 286 } 287 ierr = VecCopy(shellA->left_owned,shellB->left_owned);CHKERRQ(ierr); 288 shellB->left = shellB->left_owned; 289 } else { 290 shellB->left = NULL; 291 } 292 if (shellA->right_owned) { 293 if (!shellB->right_owned) { 294 ierr = VecDuplicate(shellA->right_owned,&shellB->right_owned);CHKERRQ(ierr); 295 } 296 ierr = VecCopy(shellA->right_owned,shellB->right_owned);CHKERRQ(ierr); 297 shellB->right = shellB->right_owned; 298 } else { 299 shellB->right = NULL; 300 } 301 PetscFunctionReturn(0); 302 } 303 304 PetscErrorCode MatDuplicate_Shell(Mat mat,MatDuplicateOption op,Mat *M) 305 { 306 PetscErrorCode ierr; 307 void *ctx; 308 309 PetscFunctionBegin; 310 ierr = MatShellGetContext(mat,&ctx);CHKERRQ(ierr); 311 ierr = MatCreateShell(PetscObjectComm((PetscObject)mat),mat->rmap->n,mat->cmap->n,mat->rmap->N,mat->cmap->N,ctx,M);CHKERRQ(ierr); 312 ierr = MatCopy(mat,*M,SAME_NONZERO_PATTERN);CHKERRQ(ierr); 313 PetscFunctionReturn(0); 314 } 315 316 PetscErrorCode MatMult_Shell(Mat A,Vec x,Vec y) 317 { 318 Mat_Shell *shell = (Mat_Shell*)A->data; 319 PetscErrorCode ierr; 320 Vec xx; 321 PetscObjectState instate,outstate; 322 323 PetscFunctionBegin; 324 ierr = MatShellPreScaleRight(A,x,&xx);CHKERRQ(ierr); 325 ierr = PetscObjectStateGet((PetscObject)y, &instate);CHKERRQ(ierr); 326 ierr = (*shell->ops->mult)(A,xx,y);CHKERRQ(ierr); 327 ierr = PetscObjectStateGet((PetscObject)y, &outstate);CHKERRQ(ierr); 328 if (instate == outstate) { 329 /* increase the state of the output vector since the user did not update its state themself as should have been done */ 330 ierr = PetscObjectStateIncrease((PetscObject)y);CHKERRQ(ierr); 331 } 332 ierr = MatShellShiftAndScale(A,xx,y);CHKERRQ(ierr); 333 ierr = MatShellPostScaleLeft(A,y);CHKERRQ(ierr); 334 PetscFunctionReturn(0); 335 } 336 337 PetscErrorCode MatMultAdd_Shell(Mat A,Vec x,Vec y,Vec z) 338 { 339 Mat_Shell *shell = (Mat_Shell*)A->data; 340 PetscErrorCode ierr; 341 342 PetscFunctionBegin; 343 if (y == z) { 344 if (!shell->right_add_work) {ierr = VecDuplicate(z,&shell->right_add_work);CHKERRQ(ierr);} 345 ierr = MatMult(A,x,shell->right_add_work);CHKERRQ(ierr); 346 ierr = VecAXPY(z,1.0,shell->right_add_work);CHKERRQ(ierr); 347 } else { 348 ierr = MatMult(A,x,z);CHKERRQ(ierr); 349 ierr = VecAXPY(z,1.0,y);CHKERRQ(ierr); 350 } 351 PetscFunctionReturn(0); 352 } 353 354 PetscErrorCode MatMultTranspose_Shell(Mat A,Vec x,Vec y) 355 { 356 Mat_Shell *shell = (Mat_Shell*)A->data; 357 PetscErrorCode ierr; 358 Vec xx; 359 PetscObjectState instate,outstate; 360 361 PetscFunctionBegin; 362 ierr = MatShellPreScaleLeft(A,x,&xx);CHKERRQ(ierr); 363 ierr = PetscObjectStateGet((PetscObject)y, &instate);CHKERRQ(ierr); 364 ierr = (*shell->ops->multtranspose)(A,xx,y);CHKERRQ(ierr); 365 ierr = PetscObjectStateGet((PetscObject)y, &outstate);CHKERRQ(ierr); 366 if (instate == outstate) { 367 /* increase the state of the output vector since the user did not update its state themself as should have been done */ 368 ierr = PetscObjectStateIncrease((PetscObject)y);CHKERRQ(ierr); 369 } 370 ierr = MatShellShiftAndScale(A,xx,y);CHKERRQ(ierr); 371 ierr = MatShellPostScaleRight(A,y);CHKERRQ(ierr); 372 PetscFunctionReturn(0); 373 } 374 375 PetscErrorCode MatMultTransposeAdd_Shell(Mat A,Vec x,Vec y,Vec z) 376 { 377 Mat_Shell *shell = (Mat_Shell*)A->data; 378 PetscErrorCode ierr; 379 380 PetscFunctionBegin; 381 if (y == z) { 382 if (!shell->left_add_work) {ierr = VecDuplicate(z,&shell->left_add_work);CHKERRQ(ierr);} 383 ierr = MatMultTranspose(A,x,shell->left_add_work);CHKERRQ(ierr); 384 ierr = VecWAXPY(z,1.0,shell->left_add_work,y);CHKERRQ(ierr); 385 } else { 386 ierr = MatMultTranspose(A,x,z);CHKERRQ(ierr); 387 ierr = VecAXPY(z,1.0,y);CHKERRQ(ierr); 388 } 389 PetscFunctionReturn(0); 390 } 391 392 PetscErrorCode MatGetDiagonal_Shell(Mat A,Vec v) 393 { 394 Mat_Shell *shell = (Mat_Shell*)A->data; 395 PetscErrorCode ierr; 396 397 PetscFunctionBegin; 398 ierr = (*shell->ops->getdiagonal)(A,v);CHKERRQ(ierr); 399 ierr = VecScale(v,shell->vscale);CHKERRQ(ierr); 400 if (shell->dshift) { 401 ierr = VecPointwiseMult(v,v,shell->dshift);CHKERRQ(ierr); 402 } else { 403 ierr = VecShift(v,shell->vshift);CHKERRQ(ierr); 404 } 405 if (shell->left) {ierr = VecPointwiseMult(v,v,shell->left);CHKERRQ(ierr);} 406 if (shell->right) {ierr = VecPointwiseMult(v,v,shell->right);CHKERRQ(ierr);} 407 PetscFunctionReturn(0); 408 } 409 410 PetscErrorCode MatShift_Shell(Mat Y,PetscScalar a) 411 { 412 Mat_Shell *shell = (Mat_Shell*)Y->data; 413 PetscErrorCode ierr; 414 415 PetscFunctionBegin; 416 if (shell->left || shell->right || shell->dshift) { 417 if (!shell->dshift) { 418 if (!shell->dshift_owned) {ierr = VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift_owned);CHKERRQ(ierr);} 419 shell->dshift = shell->dshift_owned; 420 ierr = VecSet(shell->dshift,shell->vshift+a);CHKERRQ(ierr); 421 } else {ierr = VecScale(shell->dshift,a);CHKERRQ(ierr);} 422 if (shell->left) {ierr = VecPointwiseDivide(shell->dshift,shell->dshift,shell->left);CHKERRQ(ierr);} 423 if (shell->right) {ierr = VecPointwiseDivide(shell->dshift,shell->dshift,shell->right);CHKERRQ(ierr);} 424 } else shell->vshift += a; 425 ierr = MatShellUseScaledMethods(Y);CHKERRQ(ierr); 426 PetscFunctionReturn(0); 427 } 428 429 PetscErrorCode MatDiagonalSet_Shell(Mat A,Vec D,InsertMode ins) 430 { 431 Mat_Shell *shell = (Mat_Shell*)A->data; 432 PetscErrorCode ierr; 433 434 PetscFunctionBegin; 435 if (shell->vscale != 1.0 || shell->left || shell->right) { 436 SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE, "Operation not supported on scaled matrix"); 437 } 438 439 if (shell->ops->diagonalset) { 440 ierr = (*shell->ops->diagonalset)(A,D,ins);CHKERRQ(ierr); 441 } 442 shell->vshift = 0.0; 443 if (shell->dshift) { ierr = VecZeroEntries(shell->dshift);CHKERRQ(ierr); } 444 PetscFunctionReturn(0); 445 } 446 447 PetscErrorCode MatScale_Shell(Mat Y,PetscScalar a) 448 { 449 Mat_Shell *shell = (Mat_Shell*)Y->data; 450 PetscErrorCode ierr; 451 452 PetscFunctionBegin; 453 shell->vscale *= a; 454 if (shell->dshift) { 455 ierr = VecScale(shell->dshift,a);CHKERRQ(ierr); 456 } else shell->vshift *= a; 457 ierr = MatShellUseScaledMethods(Y);CHKERRQ(ierr); 458 PetscFunctionReturn(0); 459 } 460 461 static PetscErrorCode MatDiagonalScale_Shell(Mat Y,Vec left,Vec right) 462 { 463 Mat_Shell *shell = (Mat_Shell*)Y->data; 464 PetscErrorCode ierr; 465 466 PetscFunctionBegin; 467 if (left) { 468 if (!shell->left_owned) {ierr = VecDuplicate(left,&shell->left_owned);CHKERRQ(ierr);} 469 if (shell->left) { 470 ierr = VecPointwiseMult(shell->left,shell->left,left);CHKERRQ(ierr); 471 } else { 472 shell->left = shell->left_owned; 473 ierr = VecCopy(left,shell->left);CHKERRQ(ierr); 474 } 475 } 476 if (right) { 477 if (!shell->right_owned) {ierr = VecDuplicate(right,&shell->right_owned);CHKERRQ(ierr);} 478 if (shell->right) { 479 ierr = VecPointwiseMult(shell->right,shell->right,right);CHKERRQ(ierr); 480 } else { 481 shell->right = shell->right_owned; 482 ierr = VecCopy(right,shell->right);CHKERRQ(ierr); 483 } 484 } 485 ierr = MatShellUseScaledMethods(Y);CHKERRQ(ierr); 486 PetscFunctionReturn(0); 487 } 488 489 PetscErrorCode MatAssemblyEnd_Shell(Mat Y,MatAssemblyType t) 490 { 491 Mat_Shell *shell = (Mat_Shell*)Y->data; 492 493 PetscFunctionBegin; 494 if (t == MAT_FINAL_ASSEMBLY) { 495 shell->vshift = 0.0; 496 shell->vscale = 1.0; 497 shell->dshift = NULL; 498 shell->left = NULL; 499 shell->right = NULL; 500 if (shell->ops->mult) { 501 Y->ops->mult = shell->ops->mult; 502 shell->ops->mult = NULL; 503 } 504 if (shell->ops->multtranspose) { 505 Y->ops->multtranspose = shell->ops->multtranspose; 506 shell->ops->multtranspose = NULL; 507 } 508 if (shell->ops->getdiagonal) { 509 Y->ops->getdiagonal = shell->ops->getdiagonal; 510 shell->ops->getdiagonal = NULL; 511 } 512 if (shell->ops->copy) { 513 Y->ops->copy = shell->ops->copy; 514 shell->ops->copy = NULL; 515 } 516 shell->usingscaled = PETSC_FALSE; 517 } 518 PetscFunctionReturn(0); 519 } 520 521 PETSC_INTERN PetscErrorCode MatConvert_Shell(Mat, MatType,MatReuse,Mat*); 522 523 static PetscErrorCode MatMissingDiagonal_Shell(Mat A,PetscBool *missing,PetscInt *d) 524 { 525 PetscFunctionBegin; 526 *missing = PETSC_FALSE; 527 PetscFunctionReturn(0); 528 } 529 530 static struct _MatOps MatOps_Values = {0, 531 0, 532 0, 533 0, 534 /* 4*/ 0, 535 0, 536 0, 537 0, 538 0, 539 0, 540 /*10*/ 0, 541 0, 542 0, 543 0, 544 0, 545 /*15*/ 0, 546 0, 547 0, 548 MatDiagonalScale_Shell, 549 0, 550 /*20*/ 0, 551 MatAssemblyEnd_Shell, 552 0, 553 0, 554 /*24*/ 0, 555 0, 556 0, 557 0, 558 0, 559 /*29*/ 0, 560 0, 561 0, 562 0, 563 0, 564 /*34*/ MatDuplicate_Shell, 565 0, 566 0, 567 0, 568 0, 569 /*39*/ 0, 570 0, 571 0, 572 0, 573 MatCopy_Shell, 574 /*44*/ 0, 575 MatScale_Shell, 576 MatShift_Shell, 577 MatDiagonalSet_Shell, 578 0, 579 /*49*/ 0, 580 0, 581 0, 582 0, 583 0, 584 /*54*/ 0, 585 0, 586 0, 587 0, 588 0, 589 /*59*/ 0, 590 MatDestroy_Shell, 591 0, 592 0, 593 0, 594 /*64*/ 0, 595 0, 596 0, 597 0, 598 0, 599 /*69*/ 0, 600 0, 601 MatConvert_Shell, 602 0, 603 0, 604 /*74*/ 0, 605 0, 606 0, 607 0, 608 0, 609 /*79*/ 0, 610 0, 611 0, 612 0, 613 0, 614 /*84*/ 0, 615 0, 616 0, 617 0, 618 0, 619 /*89*/ 0, 620 0, 621 0, 622 0, 623 0, 624 /*94*/ 0, 625 0, 626 0, 627 0, 628 0, 629 /*99*/ 0, 630 0, 631 0, 632 0, 633 0, 634 /*104*/ 0, 635 0, 636 0, 637 0, 638 0, 639 /*109*/ 0, 640 0, 641 0, 642 0, 643 MatMissingDiagonal_Shell, 644 /*114*/ 0, 645 0, 646 0, 647 0, 648 0, 649 /*119*/ 0, 650 0, 651 0, 652 0, 653 0, 654 /*124*/ 0, 655 0, 656 0, 657 0, 658 0, 659 /*129*/ 0, 660 0, 661 0, 662 0, 663 0, 664 /*134*/ 0, 665 0, 666 0, 667 0, 668 0, 669 /*139*/ 0, 670 0, 671 0 672 }; 673 674 /*MC 675 MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free. 676 677 Level: advanced 678 679 .seealso: MatCreateShell 680 M*/ 681 682 PETSC_EXTERN PetscErrorCode MatCreate_Shell(Mat A) 683 { 684 Mat_Shell *b; 685 PetscErrorCode ierr; 686 687 PetscFunctionBegin; 688 ierr = PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));CHKERRQ(ierr); 689 690 ierr = PetscNewLog(A,&b);CHKERRQ(ierr); 691 A->data = (void*)b; 692 693 ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr); 694 ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr); 695 696 b->ctx = 0; 697 b->vshift = 0.0; 698 b->vscale = 1.0; 699 A->assembled = PETSC_TRUE; 700 A->preallocated = PETSC_FALSE; 701 702 ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr); 703 PetscFunctionReturn(0); 704 } 705 706 /*@C 707 MatCreateShell - Creates a new matrix class for use with a user-defined 708 private data storage format. 709 710 Collective on MPI_Comm 711 712 Input Parameters: 713 + comm - MPI communicator 714 . m - number of local rows (must be given) 715 . n - number of local columns (must be given) 716 . M - number of global rows (may be PETSC_DETERMINE) 717 . N - number of global columns (may be PETSC_DETERMINE) 718 - ctx - pointer to data needed by the shell matrix routines 719 720 Output Parameter: 721 . A - the matrix 722 723 Level: advanced 724 725 Usage: 726 $ extern int mult(Mat,Vec,Vec); 727 $ MatCreateShell(comm,m,n,M,N,ctx,&mat); 728 $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult); 729 $ [ Use matrix for operations that have been set ] 730 $ MatDestroy(mat); 731 732 Notes: 733 The shell matrix type is intended to provide a simple class to use 734 with KSP (such as, for use with matrix-free methods). You should not 735 use the shell type if you plan to define a complete matrix class. 736 737 Fortran Notes: To use this from Fortran with a ctx you must write an interface definition for this 738 function and for MatShellGetContext() that tells Fortran the Fortran derived data type you are passing 739 in as the ctx argument. 740 741 PETSc requires that matrices and vectors being used for certain 742 operations are partitioned accordingly. For example, when 743 creating a shell matrix, A, that supports parallel matrix-vector 744 products using MatMult(A,x,y) the user should set the number 745 of local matrix rows to be the number of local elements of the 746 corresponding result vector, y. Note that this is information is 747 required for use of the matrix interface routines, even though 748 the shell matrix may not actually be physically partitioned. 749 For example, 750 751 $ 752 $ Vec x, y 753 $ extern int mult(Mat,Vec,Vec); 754 $ Mat A 755 $ 756 $ VecCreateMPI(comm,PETSC_DECIDE,M,&y); 757 $ VecCreateMPI(comm,PETSC_DECIDE,N,&x); 758 $ VecGetLocalSize(y,&m); 759 $ VecGetLocalSize(x,&n); 760 $ MatCreateShell(comm,m,n,M,N,ctx,&A); 761 $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult); 762 $ MatMult(A,x,y); 763 $ MatDestroy(A); 764 $ VecDestroy(y); VecDestroy(x); 765 $ 766 767 .keywords: matrix, shell, create 768 769 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext() 770 @*/ 771 PetscErrorCode MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A) 772 { 773 PetscErrorCode ierr; 774 775 PetscFunctionBegin; 776 ierr = MatCreate(comm,A);CHKERRQ(ierr); 777 ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr); 778 ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr); 779 ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr); 780 ierr = MatSetUp(*A);CHKERRQ(ierr); 781 PetscFunctionReturn(0); 782 } 783 784 /*@ 785 MatShellSetContext - sets the context for a shell matrix 786 787 Logically Collective on Mat 788 789 Input Parameters: 790 + mat - the shell matrix 791 - ctx - the context 792 793 Level: advanced 794 795 Fortran Notes: To use this from Fortran you must write a Fortran interface definition for this 796 function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument. 797 798 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation() 799 @*/ 800 PetscErrorCode MatShellSetContext(Mat mat,void *ctx) 801 { 802 Mat_Shell *shell = (Mat_Shell*)mat->data; 803 PetscErrorCode ierr; 804 PetscBool flg; 805 806 PetscFunctionBegin; 807 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 808 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 809 if (flg) { 810 shell->ctx = ctx; 811 } else SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot attach context to non-shell matrix"); 812 PetscFunctionReturn(0); 813 } 814 815 /*@C 816 MatShellTestMult - Compares the multiply routine provided to the MATSHELL with differencing on a given function. 817 818 Logically Collective on Mat 819 820 Input Parameters: 821 + mat - the shell matrix 822 . f - the function 823 . base - differences are computed around this vector, see MatMFFDSetBase(), for Jacobians this is the point at which the Jacobian is being evaluated 824 - ctx - an optional context for the function 825 826 Output Parameter: 827 . flg - PETSC_TRUE if the multiply is likely correct 828 829 Options Database: 830 . -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference 831 832 Level: advanced 833 834 Fortran Notes: Not supported from Fortran 835 836 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellTestMultTranspose() 837 @*/ 838 PetscErrorCode MatShellTestMult(Mat mat,PetscErrorCode (*f)(void*,Vec,Vec),Vec base,void *ctx,PetscBool *flg) 839 { 840 PetscErrorCode ierr; 841 PetscInt m,n; 842 Mat mf,Dmf,Dmat,Ddiff; 843 PetscReal Diffnorm,Dmfnorm; 844 PetscBool v = PETSC_FALSE, flag = PETSC_TRUE; 845 846 PetscFunctionBegin; 847 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 848 ierr = PetscOptionsHasName(NULL,((PetscObject)mat)->prefix,"-mat_shell_test_mult_view",&v);CHKERRQ(ierr); 849 ierr = MatGetLocalSize(mat,&m,&n);CHKERRQ(ierr); 850 ierr = MatCreateMFFD(PetscObjectComm((PetscObject)mat),m,n,PETSC_DECIDE,PETSC_DECIDE,&mf);CHKERRQ(ierr); 851 ierr = MatMFFDSetFunction(mf,f,ctx);CHKERRQ(ierr); 852 ierr = MatMFFDSetBase(mf,base,NULL);CHKERRQ(ierr); 853 854 ierr = MatComputeExplicitOperator(mf,&Dmf);CHKERRQ(ierr); 855 ierr = MatComputeExplicitOperator(mat,&Dmat);CHKERRQ(ierr); 856 857 ierr = MatDuplicate(Dmat,MAT_COPY_VALUES,&Ddiff);CHKERRQ(ierr); 858 ierr = MatAXPY(Ddiff,-1.0,Dmf,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr); 859 ierr = MatNorm(Ddiff,NORM_FROBENIUS,&Diffnorm);CHKERRQ(ierr); 860 ierr = MatNorm(Dmf,NORM_FROBENIUS,&Dmfnorm);CHKERRQ(ierr); 861 if (Diffnorm/Dmfnorm > 10*PETSC_SQRT_MACHINE_EPSILON) { 862 flag = PETSC_FALSE; 863 if (v) { 864 ierr = PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL and matrix free multiple appear to produce different results.\n Norm Ratio %g Difference results followed by finite difference one\n",(double)(Diffnorm/Dmfnorm)); 865 ierr = MatViewFromOptions(Ddiff,(PetscObject)mat,"-mat_shell_test_mult_view");CHKERRQ(ierr); 866 ierr = MatViewFromOptions(Dmf,(PetscObject)mat,"-mat_shell_test_mult_view");CHKERRQ(ierr); 867 ierr = MatViewFromOptions(Dmat,(PetscObject)mat,"-mat_shell_test_mult_view");CHKERRQ(ierr); 868 } 869 } else if (v) { 870 ierr = PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL and matrix free multiple appear to produce the same results\n"); 871 } 872 if (flg) *flg = flag; 873 ierr = MatDestroy(&Ddiff);CHKERRQ(ierr); 874 ierr = MatDestroy(&mf);CHKERRQ(ierr); 875 ierr = MatDestroy(&Dmf);CHKERRQ(ierr); 876 ierr = MatDestroy(&Dmat);CHKERRQ(ierr); 877 PetscFunctionReturn(0); 878 } 879 880 /*@C 881 MatShellTestMultTranpose - Compares the multiply transpose routine provided to the MATSHELL with differencing on a given function. 882 883 Logically Collective on Mat 884 885 Input Parameters: 886 + mat - the shell matrix 887 . f - the function 888 . base - differences are computed around this vector, see MatMFFDSetBase(), for Jacobians this is the point at which the Jacobian is being evaluated 889 - ctx - an optional context for the function 890 891 Output Parameter: 892 . flg - PETSC_TRUE if the multiply is likely correct 893 894 Options Database: 895 . -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference 896 897 Level: advanced 898 899 Fortran Notes: Not supported from Fortran 900 901 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellTestMult() 902 @*/ 903 PetscErrorCode MatShellTestMultTranspose(Mat mat,PetscErrorCode (*f)(void*,Vec,Vec),Vec base,void *ctx,PetscBool *flg) 904 { 905 PetscErrorCode ierr; 906 Vec x,y,z; 907 PetscInt m,n,M,N; 908 Mat mf,Dmf,Dmat,Ddiff; 909 PetscReal Diffnorm,Dmfnorm; 910 PetscBool v = PETSC_FALSE, flag = PETSC_TRUE; 911 912 PetscFunctionBegin; 913 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 914 ierr = PetscOptionsHasName(NULL,((PetscObject)mat)->prefix,"-mat_shell_test_mult_transpose_view",&v);CHKERRQ(ierr); 915 ierr = MatCreateVecs(mat,&x,&y);CHKERRQ(ierr); 916 ierr = VecDuplicate(y,&z);CHKERRQ(ierr); 917 ierr = MatGetLocalSize(mat,&m,&n);CHKERRQ(ierr); 918 ierr = MatGetSize(mat,&M,&N);CHKERRQ(ierr); 919 ierr = MatCreateMFFD(PetscObjectComm((PetscObject)mat),m,n,M,N,&mf);CHKERRQ(ierr); 920 ierr = MatMFFDSetFunction(mf,f,ctx);CHKERRQ(ierr); 921 ierr = MatMFFDSetBase(mf,base,NULL);CHKERRQ(ierr); 922 ierr = MatComputeExplicitOperator(mf,&Dmf);CHKERRQ(ierr); 923 ierr = MatTranspose(Dmf,MAT_INPLACE_MATRIX,&Dmf);CHKERRQ(ierr); 924 ierr = MatComputeExplicitOperatorTranspose(mat,&Dmat);CHKERRQ(ierr); 925 926 ierr = MatDuplicate(Dmat,MAT_COPY_VALUES,&Ddiff);CHKERRQ(ierr); 927 ierr = MatAXPY(Ddiff,-1.0,Dmf,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr); 928 ierr = MatNorm(Ddiff,NORM_FROBENIUS,&Diffnorm);CHKERRQ(ierr); 929 ierr = MatNorm(Dmf,NORM_FROBENIUS,&Dmfnorm);CHKERRQ(ierr); 930 if (Diffnorm/Dmfnorm > 10*PETSC_SQRT_MACHINE_EPSILON) { 931 flag = PETSC_FALSE; 932 if (v) { 933 ierr = PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL and matrix free multiple appear to produce different results.\n Norm Ratio %g Difference results followed by finite difference one\n",(double)(Diffnorm/Dmfnorm)); 934 ierr = MatViewFromOptions(Ddiff,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");CHKERRQ(ierr); 935 ierr = MatViewFromOptions(Dmf,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");CHKERRQ(ierr); 936 ierr = MatViewFromOptions(Dmat,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");CHKERRQ(ierr); 937 } 938 } else if (v) { 939 ierr = PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL transpose and matrix free multiple appear to produce the same results\n"); 940 } 941 if (flg) *flg = flag; 942 ierr = MatDestroy(&mf);CHKERRQ(ierr); 943 ierr = MatDestroy(&Dmat);CHKERRQ(ierr); 944 ierr = MatDestroy(&Ddiff);CHKERRQ(ierr); 945 ierr = MatDestroy(&Dmf);CHKERRQ(ierr); 946 ierr = VecDestroy(&x);CHKERRQ(ierr); 947 ierr = VecDestroy(&y);CHKERRQ(ierr); 948 ierr = VecDestroy(&z);CHKERRQ(ierr); 949 PetscFunctionReturn(0); 950 } 951 952 /*@C 953 MatShellSetOperation - Allows user to set a matrix operation for 954 a shell matrix. 955 956 Logically Collective on Mat 957 958 Input Parameters: 959 + mat - the shell matrix 960 . op - the name of the operation 961 - f - the function that provides the operation. 962 963 Level: advanced 964 965 Usage: 966 $ extern PetscErrorCode usermult(Mat,Vec,Vec); 967 $ ierr = MatCreateShell(comm,m,n,M,N,ctx,&A); 968 $ ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult); 969 970 Notes: 971 See the file include/petscmat.h for a complete list of matrix 972 operations, which all have the form MATOP_<OPERATION>, where 973 <OPERATION> is the name (in all capital letters) of the 974 user interface routine (e.g., MatMult() -> MATOP_MULT). 975 976 All user-provided functions (except for MATOP_DESTROY) should have the same calling 977 sequence as the usual matrix interface routines, since they 978 are intended to be accessed via the usual matrix interface 979 routines, e.g., 980 $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec) 981 982 In particular each function MUST return an error code of 0 on success and 983 nonzero on failure. 984 985 Within each user-defined routine, the user should call 986 MatShellGetContext() to obtain the user-defined context that was 987 set by MatCreateShell(). 988 989 Fortran Notes: For MatCreateVecs() the user code should check if the input left or right matrix is -1 and in that case not 990 generate a matrix. See src/mat/examples/tests/ex120f.F 991 992 .keywords: matrix, shell, set, operation 993 994 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext() 995 @*/ 996 PetscErrorCode MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void)) 997 { 998 PetscErrorCode ierr; 999 PetscBool flg; 1000 1001 PetscFunctionBegin; 1002 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 1003 switch (op) { 1004 case MATOP_DESTROY: 1005 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 1006 if (flg) { 1007 Mat_Shell *shell = (Mat_Shell*)mat->data; 1008 shell->ops->destroy = (PetscErrorCode (*)(Mat))f; 1009 } else mat->ops->destroy = (PetscErrorCode (*)(Mat))f; 1010 break; 1011 case MATOP_DIAGONAL_SET: 1012 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 1013 if (flg) { 1014 Mat_Shell *shell = (Mat_Shell*)mat->data; 1015 shell->ops->diagonalset = (PetscErrorCode (*)(Mat,Vec,InsertMode))f; 1016 } else { 1017 mat->ops->diagonalset = (PetscErrorCode (*)(Mat,Vec,InsertMode))f; 1018 } 1019 break; 1020 case MATOP_VIEW: 1021 if (!mat->ops->viewnative) { 1022 mat->ops->viewnative = mat->ops->view; 1023 } 1024 mat->ops->view = (PetscErrorCode (*)(Mat,PetscViewer))f; 1025 break; 1026 case MATOP_MULT: 1027 mat->ops->mult = (PetscErrorCode (*)(Mat,Vec,Vec))f; 1028 if (!mat->ops->multadd) { 1029 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 1030 if (flg) mat->ops->multadd = MatMultAdd_Shell; 1031 } 1032 break; 1033 case MATOP_MULT_TRANSPOSE: 1034 mat->ops->multtranspose = (PetscErrorCode (*)(Mat,Vec,Vec))f; 1035 if (!mat->ops->multtransposeadd) { 1036 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 1037 if (flg) mat->ops->multtransposeadd = MatMultTransposeAdd_Shell; 1038 } 1039 break; 1040 default: 1041 (((void(**)(void))mat->ops)[op]) = f; 1042 } 1043 PetscFunctionReturn(0); 1044 } 1045 1046 /*@C 1047 MatShellGetOperation - Gets a matrix function for a shell matrix. 1048 1049 Not Collective 1050 1051 Input Parameters: 1052 + mat - the shell matrix 1053 - op - the name of the operation 1054 1055 Output Parameter: 1056 . f - the function that provides the operation. 1057 1058 Level: advanced 1059 1060 Notes: 1061 See the file include/petscmat.h for a complete list of matrix 1062 operations, which all have the form MATOP_<OPERATION>, where 1063 <OPERATION> is the name (in all capital letters) of the 1064 user interface routine (e.g., MatMult() -> MATOP_MULT). 1065 1066 All user-provided functions have the same calling 1067 sequence as the usual matrix interface routines, since they 1068 are intended to be accessed via the usual matrix interface 1069 routines, e.g., 1070 $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec) 1071 1072 Within each user-defined routine, the user should call 1073 MatShellGetContext() to obtain the user-defined context that was 1074 set by MatCreateShell(). 1075 1076 .keywords: matrix, shell, set, operation 1077 1078 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext() 1079 @*/ 1080 PetscErrorCode MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void)) 1081 { 1082 PetscErrorCode ierr; 1083 PetscBool flg; 1084 1085 PetscFunctionBegin; 1086 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 1087 switch (op) { 1088 case MATOP_DESTROY: 1089 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 1090 if (flg) { 1091 Mat_Shell *shell = (Mat_Shell*)mat->data; 1092 *f = (void (*)(void))shell->ops->destroy; 1093 } else { 1094 *f = (void (*)(void))mat->ops->destroy; 1095 } 1096 break; 1097 case MATOP_DIAGONAL_SET: 1098 ierr = PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 1099 if (flg) { 1100 Mat_Shell *shell = (Mat_Shell*)mat->data; 1101 *f = (void (*)(void))shell->ops->diagonalset; 1102 } else { 1103 *f = (void (*)(void))mat->ops->diagonalset; 1104 } 1105 break; 1106 case MATOP_VIEW: 1107 *f = (void (*)(void))mat->ops->view; 1108 break; 1109 default: 1110 *f = (((void (**)(void))mat->ops)[op]); 1111 } 1112 PetscFunctionReturn(0); 1113 } 1114 1115