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