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 PetscTruth 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 PetscTruth 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 if (A->rmap->n == PETSC_DECIDE || A->cmap->n == PETSC_DECIDE) { 336 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Must give local row and column count for matrix"); 337 } 338 339 ierr = PetscLayoutSetBlockSize(A->rmap,1);CHKERRQ(ierr); 340 ierr = PetscLayoutSetBlockSize(A->cmap,1);CHKERRQ(ierr); 341 ierr = PetscLayoutSetUp(A->rmap);CHKERRQ(ierr); 342 ierr = PetscLayoutSetUp(A->cmap);CHKERRQ(ierr); 343 344 b->ctx = 0; 345 b->scale = PETSC_FALSE; 346 b->shift = PETSC_FALSE; 347 b->vshift = 0.0; 348 b->vscale = 1.0; 349 b->mult = 0; 350 b->multtranspose = 0; 351 b->getdiagonal = 0; 352 A->assembled = PETSC_TRUE; 353 A->preallocated = PETSC_FALSE; 354 ierr = PetscObjectChangeTypeName((PetscObject)A,MATSHELL);CHKERRQ(ierr); 355 PetscFunctionReturn(0); 356 } 357 EXTERN_C_END 358 359 #undef __FUNCT__ 360 #define __FUNCT__ "MatCreateShell" 361 /*@C 362 MatCreateShell - Creates a new matrix class for use with a user-defined 363 private data storage format. 364 365 Collective on MPI_Comm 366 367 Input Parameters: 368 + comm - MPI communicator 369 . m - number of local rows (must be given) 370 . n - number of local columns (must be given) 371 . M - number of global rows (may be PETSC_DETERMINE) 372 . N - number of global columns (may be PETSC_DETERMINE) 373 - ctx - pointer to data needed by the shell matrix routines 374 375 Output Parameter: 376 . A - the matrix 377 378 Level: advanced 379 380 Usage: 381 $ extern int mult(Mat,Vec,Vec); 382 $ MatCreateShell(comm,m,n,M,N,ctx,&mat); 383 $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult); 384 $ [ Use matrix for operations that have been set ] 385 $ MatDestroy(mat); 386 387 Notes: 388 The shell matrix type is intended to provide a simple class to use 389 with KSP (such as, for use with matrix-free methods). You should not 390 use the shell type if you plan to define a complete matrix class. 391 392 Fortran Notes: The context can only be an integer or a PetscObject 393 unfortunately it cannot be a Fortran array or derived type. 394 395 PETSc requires that matrices and vectors being used for certain 396 operations are partitioned accordingly. For example, when 397 creating a shell matrix, A, that supports parallel matrix-vector 398 products using MatMult(A,x,y) the user should set the number 399 of local matrix rows to be the number of local elements of the 400 corresponding result vector, y. Note that this is information is 401 required for use of the matrix interface routines, even though 402 the shell matrix may not actually be physically partitioned. 403 For example, 404 405 $ 406 $ Vec x, y 407 $ extern int mult(Mat,Vec,Vec); 408 $ Mat A 409 $ 410 $ VecCreateMPI(comm,PETSC_DECIDE,M,&y); 411 $ VecCreateMPI(comm,PETSC_DECIDE,N,&x); 412 $ VecGetLocalSize(y,&m); 413 $ VecGetLocalSize(x,&n); 414 $ MatCreateShell(comm,m,n,M,N,ctx,&A); 415 $ MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult); 416 $ MatMult(A,x,y); 417 $ MatDestroy(A); 418 $ VecDestroy(y); VecDestroy(x); 419 $ 420 421 .keywords: matrix, shell, create 422 423 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext() 424 @*/ 425 PetscErrorCode PETSCMAT_DLLEXPORT MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A) 426 { 427 PetscErrorCode ierr; 428 429 PetscFunctionBegin; 430 ierr = MatCreate(comm,A);CHKERRQ(ierr); 431 ierr = MatSetSizes(*A,m,n,M,N);CHKERRQ(ierr); 432 433 ierr = MatSetType(*A,MATSHELL);CHKERRQ(ierr); 434 ierr = MatShellSetContext(*A,ctx);CHKERRQ(ierr); 435 PetscFunctionReturn(0); 436 } 437 438 #undef __FUNCT__ 439 #define __FUNCT__ "MatShellSetContext" 440 /*@ 441 MatShellSetContext - sets the context for a shell matrix 442 443 Collective on Mat 444 445 Input Parameters: 446 + mat - the shell matrix 447 - ctx - the context 448 449 Level: advanced 450 451 Fortran Notes: The context can only be an integer or a PetscObject 452 unfortunately it cannot be a Fortran array or derived type. 453 454 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation() 455 @*/ 456 PetscErrorCode PETSCMAT_DLLEXPORT MatShellSetContext(Mat mat,void *ctx) 457 { 458 Mat_Shell *shell = (Mat_Shell*)mat->data; 459 PetscErrorCode ierr; 460 PetscTruth flg; 461 462 PetscFunctionBegin; 463 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 464 ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 465 if (flg) { 466 shell->ctx = ctx; 467 } 468 PetscFunctionReturn(0); 469 } 470 471 #undef __FUNCT__ 472 #define __FUNCT__ "MatShellSetOperation" 473 /*@C 474 MatShellSetOperation - Allows user to set a matrix operation for 475 a shell matrix. 476 477 Collective on Mat 478 479 Input Parameters: 480 + mat - the shell matrix 481 . op - the name of the operation 482 - f - the function that provides the operation. 483 484 Level: advanced 485 486 Usage: 487 $ extern PetscErrorCode usermult(Mat,Vec,Vec); 488 $ ierr = MatCreateShell(comm,m,n,M,N,ctx,&A); 489 $ ierr = MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult); 490 491 Notes: 492 See the file include/petscmat.h for a complete list of matrix 493 operations, which all have the form MATOP_<OPERATION>, where 494 <OPERATION> is the name (in all capital letters) of the 495 user interface routine (e.g., MatMult() -> MATOP_MULT). 496 497 All user-provided functions should have the same calling 498 sequence as the usual matrix interface routines, since they 499 are intended to be accessed via the usual matrix interface 500 routines, e.g., 501 $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec) 502 503 In particular each function MUST return an error code of 0 on success and 504 nonzero on failure. 505 506 Within each user-defined routine, the user should call 507 MatShellGetContext() to obtain the user-defined context that was 508 set by MatCreateShell(). 509 510 Fortran Notes: For MatGetVecs() the user code should check if the input left or right matrix is -1 and in that case not 511 generate a matrix. See src/mat/examples/tests/ex120f.F 512 513 .keywords: matrix, shell, set, operation 514 515 .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext() 516 @*/ 517 PetscErrorCode PETSCMAT_DLLEXPORT MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void)) 518 { 519 PetscErrorCode ierr; 520 PetscTruth flg; 521 522 PetscFunctionBegin; 523 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 524 if (op == MATOP_DESTROY) { 525 ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 526 if (flg) { 527 Mat_Shell *shell = (Mat_Shell*)mat->data; 528 shell->destroy = (PetscErrorCode (*)(Mat)) f; 529 } else mat->ops->destroy = (PetscErrorCode (*)(Mat)) f; 530 } 531 else if (op == MATOP_VIEW) mat->ops->view = (PetscErrorCode (*)(Mat,PetscViewer)) f; 532 else (((void(**)(void))mat->ops)[op]) = f; 533 534 PetscFunctionReturn(0); 535 } 536 537 #undef __FUNCT__ 538 #define __FUNCT__ "MatShellGetOperation" 539 /*@C 540 MatShellGetOperation - Gets a matrix function for a shell matrix. 541 542 Not Collective 543 544 Input Parameters: 545 + mat - the shell matrix 546 - op - the name of the operation 547 548 Output Parameter: 549 . f - the function that provides the operation. 550 551 Level: advanced 552 553 Notes: 554 See the file include/petscmat.h for a complete list of matrix 555 operations, which all have the form MATOP_<OPERATION>, where 556 <OPERATION> is the name (in all capital letters) of the 557 user interface routine (e.g., MatMult() -> MATOP_MULT). 558 559 All user-provided functions have the same calling 560 sequence as the usual matrix interface routines, since they 561 are intended to be accessed via the usual matrix interface 562 routines, e.g., 563 $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec) 564 565 Within each user-defined routine, the user should call 566 MatShellGetContext() to obtain the user-defined context that was 567 set by MatCreateShell(). 568 569 .keywords: matrix, shell, set, operation 570 571 .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext() 572 @*/ 573 PetscErrorCode PETSCMAT_DLLEXPORT MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void)) 574 { 575 PetscErrorCode ierr; 576 PetscTruth flg; 577 578 PetscFunctionBegin; 579 PetscValidHeaderSpecific(mat,MAT_CLASSID,1); 580 if (op == MATOP_DESTROY) { 581 ierr = PetscTypeCompare((PetscObject)mat,MATSHELL,&flg);CHKERRQ(ierr); 582 if (flg) { 583 Mat_Shell *shell = (Mat_Shell*)mat->data; 584 *f = (void(*)(void))shell->destroy; 585 } else { 586 *f = (void(*)(void))mat->ops->destroy; 587 } 588 } else if (op == MATOP_VIEW) { 589 *f = (void(*)(void))mat->ops->view; 590 } else { 591 *f = (((void(**)(void))mat->ops)[op]); 592 } 593 594 PetscFunctionReturn(0); 595 } 596 597