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 <private/matimpl.h> /*I "petscmat.h" I*/ 9 #include <private/vecimpl.h> 10 11 typedef struct { 12 PetscErrorCode (*destroy)(Mat); 13 PetscErrorCode (*mult)(Mat,Vec,Vec); 14 PetscErrorCode (*multtranspose)(Mat,Vec,Vec); 15 PetscErrorCode (*getdiagonal)(Mat,Vec); 16 PetscBool scale,shift; 17 PetscScalar vscale,vshift; 18 void *ctx; 19 } Mat_Shell; 20 21 #undef __FUNCT__ 22 #define __FUNCT__ "MatShellGetContext" 23 /*@C 24 MatShellGetContext - Returns the user-provided context associated with a shell matrix. 25 26 Not Collective 27 28 Input Parameter: 29 . mat - the matrix, should have been created with MatCreateShell() 30 31 Output Parameter: 32 . ctx - the user provided context 33 34 Level: advanced 35 36 Notes: 37 This routine is intended for use within various shell matrix routines, 38 as set with MatShellSetOperation(). 39 40 .keywords: matrix, shell, get, context 41 42 .seealso: MatCreateShell(), MatShellSetOperation(), MatShellSetContext() 43 @*/ 44 PetscErrorCode MatShellGetContext(Mat mat,void **ctx) 45 { 46 PetscErrorCode ierr; 47 PetscBool flg; 48 49 PetscFunctionBegin; 50 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 51 PetscValidPointer(ctx,2); 52 ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 53 if (!flg) *ctx = 0; 54 else *ctx = ((Mat_Shell*)(mat->data))->ctx; 55 PetscFunctionReturn(0); 56 } 57 58 #undef __FUNCT__ 59 #define __FUNCT__ "MatDestroy_Shell" 60 PetscErrorCode MatDestroy_Shell(Mat mat) 61 { 62 PetscErrorCode ierr; 63 Mat_Shell *shell; 64 65 PetscFunctionBegin; 66 shell = (Mat_Shell*)mat->data; 67 if (shell->destroy) {ierr = (*shell->destroy)(mat);CHKERRQ(ierr);} 68 ierr = PetscFree(shell);CHKERRQ(ierr); 69 PetscFunctionReturn(0); 70 } 71 72 #undef __FUNCT__ 73 #define __FUNCT__ "MatMult_Shell" 74 PetscErrorCode MatMult_Shell(Mat A,Vec x,Vec y) 75 { 76 Mat_Shell *shell = (Mat_Shell*)A->data; 77 PetscErrorCode ierr; 78 79 PetscFunctionBegin; 80 ierr = (*shell->mult)(A,x,y);CHKERRQ(ierr); 81 if (shell->shift && shell->scale) { 82 ierr = VecAXPBY(y,shell->vshift,shell->vscale,x);CHKERRQ(ierr); 83 } else if (shell->scale) { 84 ierr = VecScale(y,shell->vscale);CHKERRQ(ierr); 85 } else { 86 ierr = VecAXPY(y,shell->vshift,x);CHKERRQ(ierr); 87 } 88 PetscFunctionReturn(0); 89 } 90 91 #undef __FUNCT__ 92 #define __FUNCT__ "MatMultTranspose_Shell" 93 PetscErrorCode MatMultTranspose_Shell(Mat A,Vec x,Vec y) 94 { 95 Mat_Shell *shell = (Mat_Shell*)A->data; 96 PetscErrorCode ierr; 97 98 PetscFunctionBegin; 99 ierr = (*shell->multtranspose)(A,x,y);CHKERRQ(ierr); 100 if (shell->shift && shell->scale) { 101 ierr = VecAXPBY(y,shell->vshift,shell->vscale,x);CHKERRQ(ierr); 102 } else if (shell->scale) { 103 ierr = VecScale(y,shell->vscale);CHKERRQ(ierr); 104 } else { 105 ierr = VecAXPY(y,shell->vshift,x);CHKERRQ(ierr); 106 } 107 PetscFunctionReturn(0); 108 } 109 110 #undef __FUNCT__ 111 #define __FUNCT__ "MatGetDiagonal_Shell" 112 PetscErrorCode MatGetDiagonal_Shell(Mat A,Vec v) 113 { 114 Mat_Shell *shell = (Mat_Shell*)A->data; 115 PetscErrorCode ierr; 116 117 PetscFunctionBegin; 118 ierr = (*shell->getdiagonal)(A,v);CHKERRQ(ierr); 119 if (shell->scale) { 120 ierr = VecScale(v,shell->vscale);CHKERRQ(ierr); 121 } 122 if (shell->shift) { 123 ierr = VecShift(v,shell->vshift);CHKERRQ(ierr); 124 } 125 PetscFunctionReturn(0); 126 } 127 128 #undef __FUNCT__ 129 #define __FUNCT__ "MatShift_Shell" 130 PetscErrorCode MatShift_Shell(Mat Y,PetscScalar a) 131 { 132 Mat_Shell *shell = (Mat_Shell*)Y->data; 133 134 PetscFunctionBegin; 135 if (shell->scale || shell->shift) { 136 shell->vshift += a; 137 } else { 138 shell->mult = Y->ops->mult; 139 Y->ops->mult = MatMult_Shell; 140 if (Y->ops->multtranspose) { 141 shell->multtranspose = Y->ops->multtranspose; 142 Y->ops->multtranspose = MatMultTranspose_Shell; 143 } 144 if (Y->ops->getdiagonal) { 145 shell->getdiagonal = Y->ops->getdiagonal; 146 Y->ops->getdiagonal = MatGetDiagonal_Shell; 147 } 148 shell->vshift = a; 149 } 150 shell->shift = PETSC_TRUE; 151 PetscFunctionReturn(0); 152 } 153 154 #undef __FUNCT__ 155 #define __FUNCT__ "MatScale_Shell" 156 PetscErrorCode MatScale_Shell(Mat Y,PetscScalar a) 157 { 158 Mat_Shell *shell = (Mat_Shell*)Y->data; 159 160 PetscFunctionBegin; 161 if (shell->scale || shell->shift) { 162 shell->vscale *= a; 163 } else { 164 shell->mult = Y->ops->mult; 165 Y->ops->mult = MatMult_Shell; 166 if (Y->ops->multtranspose) { 167 shell->multtranspose = Y->ops->multtranspose; 168 Y->ops->multtranspose = MatMultTranspose_Shell; 169 } 170 if (Y->ops->getdiagonal) { 171 shell->getdiagonal = Y->ops->getdiagonal; 172 Y->ops->getdiagonal = MatGetDiagonal_Shell; 173 } 174 shell->vscale = a; 175 } 176 shell->scale = PETSC_TRUE; 177 PetscFunctionReturn(0); 178 } 179 180 #undef __FUNCT__ 181 #define __FUNCT__ "MatAssemblyEnd_Shell" 182 PetscErrorCode MatAssemblyEnd_Shell(Mat Y,MatAssemblyType t) 183 { 184 Mat_Shell *shell = (Mat_Shell*)Y->data; 185 186 PetscFunctionBegin; 187 if ((shell->shift || shell->scale) && t == MAT_FINAL_ASSEMBLY) { 188 shell->scale = PETSC_FALSE; 189 shell->shift = PETSC_FALSE; 190 shell->vshift = 0.0; 191 shell->vscale = 1.0; 192 Y->ops->mult = shell->mult; 193 Y->ops->multtranspose = shell->multtranspose; 194 Y->ops->getdiagonal = shell->getdiagonal; 195 } 196 PetscFunctionReturn(0); 197 } 198 199 extern PetscErrorCode MatConvert_Shell(Mat, const MatType,MatReuse,Mat*); 200 201 #undef __FUNCT__ 202 #define __FUNCT__ "MatSetBlockSize_Shell" 203 PetscErrorCode MatSetBlockSize_Shell(Mat A,PetscInt bs) 204 { 205 PetscErrorCode ierr; 206 207 PetscFunctionBegin; 208 ierr = PetscLayoutSetBlockSize(A->rmap,bs);CHKERRQ(ierr); 209 ierr = PetscLayoutSetBlockSize(A->cmap,bs);CHKERRQ(ierr); 210 PetscFunctionReturn(0); 211 } 212 213 static struct _MatOps MatOps_Values = {0, 214 0, 215 0, 216 0, 217 /* 4*/ 0, 218 0, 219 0, 220 0, 221 0, 222 0, 223 /*10*/ 0, 224 0, 225 0, 226 0, 227 0, 228 /*15*/ 0, 229 0, 230 0, 231 0, 232 0, 233 /*20*/ 0, 234 MatAssemblyEnd_Shell, 235 0, 236 0, 237 /*24*/ 0, 238 0, 239 0, 240 0, 241 0, 242 /*29*/ 0, 243 0, 244 0, 245 0, 246 0, 247 /*34*/ 0, 248 0, 249 0, 250 0, 251 0, 252 /*39*/ 0, 253 0, 254 0, 255 0, 256 0, 257 /*44*/ 0, 258 MatScale_Shell, 259 MatShift_Shell, 260 0, 261 0, 262 /*49*/ MatSetBlockSize_Shell, 263 0, 264 0, 265 0, 266 0, 267 /*54*/ 0, 268 0, 269 0, 270 0, 271 0, 272 /*59*/ 0, 273 MatDestroy_Shell, 274 0, 275 0, 276 0, 277 /*64*/ 0, 278 0, 279 0, 280 0, 281 0, 282 /*69*/ 0, 283 0, 284 MatConvert_Shell, 285 0, 286 0, 287 /*74*/ 0, 288 0, 289 0, 290 0, 291 0, 292 /*79*/ 0, 293 0, 294 0, 295 0, 296 0, 297 /*84*/ 0, 298 0, 299 0, 300 0, 301 0, 302 /*89*/ 0, 303 0, 304 0, 305 0, 306 0, 307 /*94*/ 0, 308 0, 309 0, 310 0}; 311 312 /*MC 313 MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free. 314 315 Level: advanced 316 317 .seealso: MatCreateShell 318 M*/ 319 320 EXTERN_C_BEGIN 321 #undef __FUNCT__ 322 #define __FUNCT__ "MatCreate_Shell" 323 PetscErrorCode MatCreate_Shell(Mat A) 324 { 325 Mat_Shell *b; 326 PetscErrorCode ierr; 327 328 PetscFunctionBegin; 329 ierr = PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));CHKERRQ(ierr); 330 331 ierr = PetscNewLog(A,Mat_Shell,&b);CHKERRQ(ierr); 332 A->data = (void*)b; 333 334 ierr = PetscLayoutSetBlockSize(A->rmap,1);CHKERRQ(ierr); 335 ierr = PetscLayoutSetBlockSize(A->cmap,1);CHKERRQ(ierr); 336 ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr); 337 ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr); 338 339 b->ctx = 0; 340 b->scale = PETSC_FALSE; 341 b->shift = PETSC_FALSE; 342 b->vshift = 0.0; 343 b->vscale = 1.0; 344 b->mult = 0; 345 b->multtranspose = 0; 346 b->getdiagonal = 0; 347 A->assembled = PETSC_TRUE; 348 A->preallocated = PETSC_FALSE; 349 ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr); 350 PetscFunctionReturn(0); 351 } 352 EXTERN_C_END 353 354 #undef __FUNCT__ 355 #define __FUNCT__ "MatCreateShell" 356 /*@C 357 MatCreateShell - Creates a new matrix class for use with a user-defined 358 private data storage format. 359 360 Collective on MPI_Comm 361 362 Input Parameters: 363 + comm - MPI communicator 364 . m - number of local rows (must be given) 365 . n - number of local columns (must be given) 366 . M - number of global rows (may be PETSC_DETERMINE) 367 . N - number of global columns (may be PETSC_DETERMINE) 368 - ctx - pointer to data needed by the shell matrix routines 369 370 Output Parameter: 371 . A - the matrix 372 373 Level: advanced 374 375 Usage: 376 $ extern int mult(Mat,Vec,Vec); 377 $ MatCreateShell(comm,m,n,M,N,ctx,&mat); 378 $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult); 379 $ [ Use matrix for operations that have been set ] 380 $ MatDestroy(mat); 381 382 Notes: 383 The shell matrix type is intended to provide a simple class to use 384 with KSP (such as, for use with matrix-free methods). You should not 385 use the shell type if you plan to define a complete matrix class. 386 387 Fortran Notes: The context can only be an integer or a PetscObject 388 unfortunately it cannot be a Fortran array or derived type. 389 390 PETSc requires that matrices and vectors being used for certain 391 operations are partitioned accordingly. For example, when 392 creating a shell matrix, A, that supports parallel matrix-vector 393 products using MatMult(A,x,y) the user should set the number 394 of local matrix rows to be the number of local elements of the 395 corresponding result vector, y. Note that this is information is 396 required for use of the matrix interface routines, even though 397 the shell matrix may not actually be physically partitioned. 398 For example, 399 400 $ 401 $ Vec x, y 402 $ extern int mult(Mat,Vec,Vec); 403 $ Mat A 404 $ 405 $ VecCreateMPI(comm,PETSC_DECIDE,M,&y); 406 $ VecCreateMPI(comm,PETSC_DECIDE,N,&x); 407 $ VecGetLocalSize(y,&m); 408 $ VecGetLocalSize(x,&n); 409 $ MatCreateShell(comm,m,n,M,N,ctx,&A); 410 $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult); 411 $ MatMult(A,x,y); 412 $ MatDestroy(A); 413 $ VecDestroy(y); VecDestroy(x); 414 $ 415 416 .keywords: matrix, shell, create 417 418 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext() 419 @*/ 420 PetscErrorCode MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A) 421 { 422 PetscErrorCode ierr; 423 424 PetscFunctionBegin; 425 ierr = MatCreate(comm,A);CHKERRQ(ierr); 426 ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr); 427 428 ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr); 429 ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr); 430 PetscFunctionReturn(0); 431 } 432 433 #undef __FUNCT__ 434 #define __FUNCT__ "MatShellSetContext" 435 /*@ 436 MatShellSetContext - sets the context for a shell matrix 437 438 Logically Collective on Mat 439 440 Input Parameters: 441 + mat - the shell matrix 442 - ctx - the context 443 444 Level: advanced 445 446 Fortran Notes: The context can only be an integer or a PetscObject 447 unfortunately it cannot be a Fortran array or derived type. 448 449 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation() 450 @*/ 451 PetscErrorCode MatShellSetContext(Mat mat,void *ctx) 452 { 453 Mat_Shell *shell = (Mat_Shell*)mat->data; 454 PetscErrorCode ierr; 455 PetscBool flg; 456 457 PetscFunctionBegin; 458 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 459 ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 460 if (flg) { 461 shell->ctx = ctx; 462 } 463 PetscFunctionReturn(0); 464 } 465 466 #undef __FUNCT__ 467 #define __FUNCT__ "MatShellSetOperation" 468 /*@C 469 MatShellSetOperation - Allows user to set a matrix operation for 470 a shell matrix. 471 472 Logically Collective on Mat 473 474 Input Parameters: 475 + mat - the shell matrix 476 . op - the name of the operation 477 - f - the function that provides the operation. 478 479 Level: advanced 480 481 Usage: 482 $ extern PetscErrorCode usermult(Mat,Vec,Vec); 483 $ ierr = MatCreateShell(comm,m,n,M,N,ctx,&A); 484 $ ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult); 485 486 Notes: 487 See the file include/petscmat.h for a complete list of matrix 488 operations, which all have the form MATOP_<OPERATION>, where 489 <OPERATION> is the name (in all capital letters) of the 490 user interface routine (e.g., MatMult() -> MATOP_MULT). 491 492 All user-provided functions should have the same calling 493 sequence as the usual matrix interface routines, since they 494 are intended to be accessed via the usual matrix interface 495 routines, e.g., 496 $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec) 497 498 In particular each function MUST return an error code of 0 on success and 499 nonzero on failure. 500 501 Within each user-defined routine, the user should call 502 MatShellGetContext() to obtain the user-defined context that was 503 set by MatCreateShell(). 504 505 Fortran Notes: For MatGetVecs() the user code should check if the input left or right matrix is -1 and in that case not 506 generate a matrix. See src/mat/examples/tests/ex120f.F 507 508 .keywords: matrix, shell, set, operation 509 510 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext() 511 @*/ 512 PetscErrorCode MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void)) 513 { 514 PetscErrorCode ierr; 515 PetscBool flg; 516 517 PetscFunctionBegin; 518 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 519 if (op == MATOP_DESTROY) { 520 ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 521 if (flg) { 522 Mat_Shell *shell = (Mat_Shell*)mat->data; 523 shell->destroy = (PetscErrorCode (*)(Mat)) f; 524 } else mat->ops->destroy = (PetscErrorCode (*)(Mat)) f; 525 } 526 else if (op == MATOP_VIEW) mat->ops->view = (PetscErrorCode (*)(Mat,PetscViewer)) f; 527 else (((void(**)(void))mat->ops)[op]) = f; 528 529 PetscFunctionReturn(0); 530 } 531 532 #undef __FUNCT__ 533 #define __FUNCT__ "MatShellGetOperation" 534 /*@C 535 MatShellGetOperation - Gets a matrix function for a shell matrix. 536 537 Not Collective 538 539 Input Parameters: 540 + mat - the shell matrix 541 - op - the name of the operation 542 543 Output Parameter: 544 . f - the function that provides the operation. 545 546 Level: advanced 547 548 Notes: 549 See the file include/petscmat.h for a complete list of matrix 550 operations, which all have the form MATOP_<OPERATION>, where 551 <OPERATION> is the name (in all capital letters) of the 552 user interface routine (e.g., MatMult() -> MATOP_MULT). 553 554 All user-provided functions have the same calling 555 sequence as the usual matrix interface routines, since they 556 are intended to be accessed via the usual matrix interface 557 routines, e.g., 558 $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec) 559 560 Within each user-defined routine, the user should call 561 MatShellGetContext() to obtain the user-defined context that was 562 set by MatCreateShell(). 563 564 .keywords: matrix, shell, set, operation 565 566 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext() 567 @*/ 568 PetscErrorCode MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void)) 569 { 570 PetscErrorCode ierr; 571 PetscBool flg; 572 573 PetscFunctionBegin; 574 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 575 if (op == MATOP_DESTROY) { 576 ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 577 if (flg) { 578 Mat_Shell *shell = (Mat_Shell*)mat->data; 579 *f = (void(*)(void))shell->destroy; 580 } else { 581 *f = (void(*)(void))mat->ops->destroy; 582 } 583 } else if (op == MATOP_VIEW) { 584 *f = (void(*)(void))mat->ops->view; 585 } else { 586 *f = (((void(**)(void))mat->ops)[op]); 587 } 588 589 PetscFunctionReturn(0); 590 } 591 592