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