Actual source code: shell.c
petsc-3.10.5 2019-03-28
Report Typos and Errors

      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: */

      8:  #include <petsc/private/matimpl.h>

     10: struct _MatShellOps {
     11:   /*  3 */ PetscErrorCode (*mult)(Mat,Vec,Vec);
     12:   /*  5 */ PetscErrorCode (*multtranspose)(Mat,Vec,Vec);
     13:   /* 17 */ PetscErrorCode (*getdiagonal)(Mat,Vec);
     14:   /* 43 */ PetscErrorCode (*copy)(Mat,Mat,MatStructure);
     15:   /* 60 */ PetscErrorCode (*destroy)(Mat);
     16: };

     18: typedef struct {
     19:   struct _MatShellOps ops[1];

     21:   PetscScalar vscale,vshift;
     22:   Vec         dshift;
     23:   Vec         left,right;
     24:   Vec         left_work,right_work;
     25:   Vec         left_add_work,right_add_work;
     26:   Mat         axpy;
     27:   PetscScalar axpy_vscale;
     28:   PetscBool   managescalingshifts;                   /* The user will manage the scaling and shifts for the MATSHELL, not the default */
     29:   void        *ctx;
     30: } Mat_Shell;

     32: /*
     33:       xx = diag(left)*x
     34: */
     35: static PetscErrorCode MatShellPreScaleLeft(Mat A,Vec x,Vec *xx)
     36: {
     37:   Mat_Shell      *shell = (Mat_Shell*)A->data;

     41:   *xx = NULL;
     42:   if (!shell->left) {
     43:     *xx = x;
     44:   } else {
     45:     if (!shell->left_work) {VecDuplicate(shell->left,&shell->left_work);}
     46:     VecPointwiseMult(shell->left_work,x,shell->left);
     47:     *xx  = shell->left_work;
     48:   }
     49:   return(0);
     50: }

     52: /*
     53:      xx = diag(right)*x
     54: */
     55: static PetscErrorCode MatShellPreScaleRight(Mat A,Vec x,Vec *xx)
     56: {
     57:   Mat_Shell      *shell = (Mat_Shell*)A->data;

     61:   *xx = NULL;
     62:   if (!shell->right) {
     63:     *xx = x;
     64:   } else {
     65:     if (!shell->right_work) {VecDuplicate(shell->right,&shell->right_work);}
     66:     VecPointwiseMult(shell->right_work,x,shell->right);
     67:     *xx  = shell->right_work;
     68:   }
     69:   return(0);
     70: }

     72: /*
     73:     x = diag(left)*x
     74: */
     75: static PetscErrorCode MatShellPostScaleLeft(Mat A,Vec x)
     76: {
     77:   Mat_Shell      *shell = (Mat_Shell*)A->data;

     81:   if (shell->left) {VecPointwiseMult(x,x,shell->left);}
     82:   return(0);
     83: }

     85: /*
     86:     x = diag(right)*x
     87: */
     88: static PetscErrorCode MatShellPostScaleRight(Mat A,Vec x)
     89: {
     90:   Mat_Shell      *shell = (Mat_Shell*)A->data;

     94:   if (shell->right) {VecPointwiseMult(x,x,shell->right);}
     95:   return(0);
     96: }

     98: /*
     99:          Y = vscale*Y + diag(dshift)*X + vshift*X

    101:          On input Y already contains A*x
    102: */
    103: static PetscErrorCode MatShellShiftAndScale(Mat A,Vec X,Vec Y)
    104: {
    105:   Mat_Shell      *shell = (Mat_Shell*)A->data;

    109:   if (shell->dshift) {          /* get arrays because there is no VecPointwiseMultAdd() */
    110:     PetscInt          i,m;
    111:     const PetscScalar *x,*d;
    112:     PetscScalar       *y;
    113:     VecGetLocalSize(X,&m);
    114:     VecGetArrayRead(shell->dshift,&d);
    115:     VecGetArrayRead(X,&x);
    116:     VecGetArray(Y,&y);
    117:     for (i=0; i<m; i++) y[i] = shell->vscale*y[i] + d[i]*x[i];
    118:     VecRestoreArrayRead(shell->dshift,&d);
    119:     VecRestoreArrayRead(X,&x);
    120:     VecRestoreArray(Y,&y);
    121:   } else {
    122:     VecScale(Y,shell->vscale);
    123:   }
    124:   if (shell->vshift != 0.0) {VecAXPY(Y,shell->vshift,X);} /* if test is for non-square matrices */
    125:   return(0);
    126: }

    128: /*@
    129:     MatShellGetContext - Returns the user-provided context associated with a shell matrix.

    131:     Not Collective

    133:     Input Parameter:
    134: .   mat - the matrix, should have been created with MatCreateShell()

    136:     Output Parameter:
    137: .   ctx - the user provided context

    139:     Level: advanced

    141:    Fortran Notes:
    142:     To use this from Fortran you must write a Fortran interface definition for this
    143:     function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument.

    145: .keywords: matrix, shell, get, context

    147: .seealso: MatCreateShell(), MatShellSetOperation(), MatShellSetContext()
    148: @*/
    149: PetscErrorCode  MatShellGetContext(Mat mat,void *ctx)
    150: {
    152:   PetscBool      flg;

    157:   PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
    158:   if (flg) *(void**)ctx = ((Mat_Shell*)(mat->data))->ctx;
    159:   else SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot get context from non-shell matrix");
    160:   return(0);
    161: }

    163: PetscErrorCode MatDestroy_Shell(Mat mat)
    164: {
    166:   Mat_Shell      *shell = (Mat_Shell*)mat->data;

    169:   if (shell->ops->destroy) {
    170:     (*shell->ops->destroy)(mat);
    171:   }
    172:   VecDestroy(&shell->left);
    173:   VecDestroy(&shell->right);
    174:   VecDestroy(&shell->dshift);
    175:   VecDestroy(&shell->left_work);
    176:   VecDestroy(&shell->right_work);
    177:   VecDestroy(&shell->left_add_work);
    178:   VecDestroy(&shell->right_add_work);
    179:   MatDestroy(&shell->axpy);
    180:   PetscFree(mat->data);
    181:   return(0);
    182: }

    184: PetscErrorCode MatCopy_Shell(Mat A,Mat B,MatStructure str)
    185: {
    186:   Mat_Shell       *shellA = (Mat_Shell*)A->data,*shellB = (Mat_Shell*)B->data;
    187:   PetscErrorCode  ierr;
    188:   PetscBool       matflg;

    191:   PetscObjectTypeCompare((PetscObject)B,MATSHELL,&matflg);
    192:   if (!matflg) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_NOTSAMETYPE,"Output matrix must be a MATSHELL");

    194:   PetscMemcpy(B->ops,A->ops,sizeof(struct _MatOps));
    195:   PetscMemcpy(shellB->ops,shellA->ops,sizeof(struct _MatShellOps));

    197:   if (shellA->ops->copy) {
    198:     (*shellA->ops->copy)(A,B,str);
    199:   }
    200:   shellB->vscale = shellA->vscale;
    201:   shellB->vshift = shellA->vshift;
    202:   if (shellA->dshift) {
    203:     if (!shellB->dshift) {
    204:       VecDuplicate(shellA->dshift,&shellB->dshift);
    205:     }
    206:     VecCopy(shellA->dshift,shellB->dshift);
    207:   } else {
    208:     VecDestroy(&shellB->dshift);
    209:   }
    210:   if (shellA->left) {
    211:     if (!shellB->left) {
    212:       VecDuplicate(shellA->left,&shellB->left);
    213:     }
    214:     VecCopy(shellA->left,shellB->left);
    215:   } else {
    216:     VecDestroy(&shellB->left);
    217:   }
    218:   if (shellA->right) {
    219:     if (!shellB->right) {
    220:       VecDuplicate(shellA->right,&shellB->right);
    221:     }
    222:     VecCopy(shellA->right,shellB->right);
    223:   } else {
    224:     VecDestroy(&shellB->right);
    225:   }
    226:   MatDestroy(&shellB->axpy);
    227:   if (shellA->axpy) {
    228:     PetscObjectReference((PetscObject)shellA->axpy);
    229:     shellB->axpy        = shellA->axpy;
    230:     shellB->axpy_vscale = shellA->axpy_vscale;
    231:   }
    232:   return(0);
    233: }

    235: PetscErrorCode MatDuplicate_Shell(Mat mat,MatDuplicateOption op,Mat *M)
    236: {
    238:   void           *ctx;

    241:   MatShellGetContext(mat,&ctx);
    242:   MatCreateShell(PetscObjectComm((PetscObject)mat),mat->rmap->n,mat->cmap->n,mat->rmap->N,mat->cmap->N,ctx,M);
    243:   MatCopy(mat,*M,SAME_NONZERO_PATTERN);
    244:   return(0);
    245: }

    247: PetscErrorCode MatMult_Shell(Mat A,Vec x,Vec y)
    248: {
    249:   Mat_Shell        *shell = (Mat_Shell*)A->data;
    250:   PetscErrorCode   ierr;
    251:   Vec              xx;
    252:   PetscObjectState instate,outstate;

    255:   MatShellPreScaleRight(A,x,&xx);
    256:   PetscObjectStateGet((PetscObject)y, &instate);
    257:   if (!shell->ops->mult) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Have not provided a MatMult() for this MATSHELL");
    258:   (*shell->ops->mult)(A,xx,y);
    259:   PetscObjectStateGet((PetscObject)y, &outstate);
    260:   if (instate == outstate) {
    261:     /* increase the state of the output vector since the user did not update its state themself as should have been done */
    262:     PetscObjectStateIncrease((PetscObject)y);
    263:   }
    264:   MatShellShiftAndScale(A,xx,y);
    265:   MatShellPostScaleLeft(A,y);

    267:   if (shell->axpy) {
    268:     if (!shell->left_work) {MatCreateVecs(A,&shell->left_work,NULL);}
    269:     MatMult(shell->axpy,x,shell->left_work);
    270:     VecAXPY(y,shell->axpy_vscale,shell->left_work);
    271:   }
    272:   return(0);
    273: }

    275: PetscErrorCode MatMultAdd_Shell(Mat A,Vec x,Vec y,Vec z)
    276: {
    277:   Mat_Shell      *shell = (Mat_Shell*)A->data;

    281:   if (y == z) {
    282:     if (!shell->right_add_work) {VecDuplicate(z,&shell->right_add_work);}
    283:     MatMult(A,x,shell->right_add_work);
    284:     VecAXPY(z,1.0,shell->right_add_work);
    285:   } else {
    286:     MatMult(A,x,z);
    287:     VecAXPY(z,1.0,y);
    288:   }
    289:   return(0);
    290: }

    292: PetscErrorCode MatMultTranspose_Shell(Mat A,Vec x,Vec y)
    293: {
    294:   Mat_Shell        *shell = (Mat_Shell*)A->data;
    295:   PetscErrorCode   ierr;
    296:   Vec              xx;
    297:   PetscObjectState instate,outstate;

    300:   MatShellPreScaleLeft(A,x,&xx);
    301:   PetscObjectStateGet((PetscObject)y, &instate);
    302:   if (!shell->ops->multtranspose) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Have not provided a MatMultTranspose() for this MATSHELL");
    303:   (*shell->ops->multtranspose)(A,xx,y);
    304:   PetscObjectStateGet((PetscObject)y, &outstate);
    305:   if (instate == outstate) {
    306:     /* increase the state of the output vector since the user did not update its state themself as should have been done */
    307:     PetscObjectStateIncrease((PetscObject)y);
    308:   }
    309:   MatShellShiftAndScale(A,xx,y);
    310:   MatShellPostScaleRight(A,y);
    311:   return(0);
    312: }

    314: PetscErrorCode MatMultTransposeAdd_Shell(Mat A,Vec x,Vec y,Vec z)
    315: {
    316:   Mat_Shell      *shell = (Mat_Shell*)A->data;

    320:   if (y == z) {
    321:     if (!shell->left_add_work) {VecDuplicate(z,&shell->left_add_work);}
    322:     MatMultTranspose(A,x,shell->left_add_work);
    323:     VecAXPY(z,1.0,shell->left_add_work);
    324:   } else {
    325:     MatMultTranspose(A,x,z);
    326:     VecAXPY(z,1.0,y);
    327:   }
    328:   return(0);
    329: }

    331: /*
    332:           diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)
    333: */
    334: PetscErrorCode MatGetDiagonal_Shell(Mat A,Vec v)
    335: {
    336:   Mat_Shell      *shell = (Mat_Shell*)A->data;

    340:   if (shell->ops->getdiagonal) {
    341:     (*shell->ops->getdiagonal)(A,v);
    342:   } else SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Must provide shell matrix with routine to return diagonal using\nMatShellSetOperation(S,MATOP_GET_DIAGONAL,...)");
    343:   VecScale(v,shell->vscale);
    344:   if (shell->dshift) {
    345:     VecAXPY(v,1.0,shell->dshift);
    346:   }
    347:   VecShift(v,shell->vshift);
    348:   if (shell->left)  {VecPointwiseMult(v,v,shell->left);}
    349:   if (shell->right) {VecPointwiseMult(v,v,shell->right);}
    350:   if (shell->axpy) {
    351:     if (!shell->left_work) {VecDuplicate(v,&shell->left_work);}
    352:     MatGetDiagonal(shell->axpy,shell->left_work);
    353:     VecAXPY(v,shell->axpy_vscale,shell->left_work);
    354:   }
    355:   return(0);
    356: }

    358: PetscErrorCode MatShift_Shell(Mat Y,PetscScalar a)
    359: {
    360:   Mat_Shell      *shell = (Mat_Shell*)Y->data;

    364:   if (shell->left || shell->right) {
    365:     if (!shell->dshift) {
    366:       VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift);
    367:       VecSet(shell->dshift,a);
    368:     } else {
    369:       if (shell->left)  {VecPointwiseMult(shell->dshift,shell->dshift,shell->left);}
    370:       if (shell->right) {VecPointwiseMult(shell->dshift,shell->dshift,shell->right);}
    371:       VecShift(shell->dshift,a);
    372:     }
    373:     if (shell->left)  {VecPointwiseDivide(shell->dshift,shell->dshift,shell->left);}
    374:     if (shell->right) {VecPointwiseDivide(shell->dshift,shell->dshift,shell->right);}
    375:   } else shell->vshift += a;
    376:   return(0);
    377: }

    379: PetscErrorCode MatDiagonalSet_Shell(Mat A,Vec D,InsertMode ins)
    380: {
    381:   Mat_Shell      *shell = (Mat_Shell*)A->data;

    385:   if (ins == INSERT_VALUES) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE, "Operation not supported with INSERT_VALUES");
    386:   if (!shell->dshift) {VecDuplicate(D,&shell->dshift);}
    387:   if (shell->left || shell->right) {
    388:     if (!shell->right_work) {VecDuplicate(shell->left ? shell->left : shell->right, &shell->right_work);}
    389:     if (shell->left && shell->right)  {
    390:       VecPointwiseDivide(shell->right_work,D,shell->left);
    391:       VecPointwiseDivide(shell->right_work,shell->right_work,shell->right);
    392:     } else if (shell->left) {
    393:       VecPointwiseDivide(shell->right_work,D,shell->left);
    394:     } else {
    395:       VecPointwiseDivide(shell->right_work,D,shell->right);
    396:     }
    397:     if (!shell->dshift) {
    398:       VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift);
    399:       VecCopy(shell->dshift,shell->right_work);
    400:     } else {
    401:       VecAXPY(shell->dshift,1.0,shell->right_work);
    402:     }
    403:   } else {
    404:     VecAXPY(shell->dshift,1.0,D);
    405:   }
    406:   return(0);
    407: }

    409: PetscErrorCode MatScale_Shell(Mat Y,PetscScalar a)
    410: {
    411:   Mat_Shell      *shell = (Mat_Shell*)Y->data;

    415:   shell->vscale *= a;
    416:   shell->vshift *= a;
    417:   if (shell->dshift) {
    418:     VecScale(shell->dshift,a);
    419:   }
    420:   shell->axpy_vscale *= a;
    421:   return(0);
    422: }

    424: static PetscErrorCode MatDiagonalScale_Shell(Mat Y,Vec left,Vec right)
    425: {
    426:   Mat_Shell      *shell = (Mat_Shell*)Y->data;

    430:   if (shell->axpy) SETERRQ(PetscObjectComm((PetscObject)Y),PETSC_ERR_SUP,"Cannot diagonal scale MATSHELL after MatAXPY operation");
    431:   if (left) {
    432:     if (!shell->left) {
    433:       VecDuplicate(left,&shell->left);
    434:       VecCopy(left,shell->left);
    435:     } else {
    436:       VecPointwiseMult(shell->left,shell->left,left);
    437:     }
    438:   }
    439:   if (right) {
    440:     if (!shell->right) {
    441:       VecDuplicate(right,&shell->right);
    442:       VecCopy(right,shell->right);
    443:     } else {
    444:       VecPointwiseMult(shell->right,shell->right,right);
    445:     }
    446:   }
    447:   return(0);
    448: }

    450: PetscErrorCode MatAssemblyEnd_Shell(Mat Y,MatAssemblyType t)
    451: {
    452:   Mat_Shell      *shell = (Mat_Shell*)Y->data;

    456:   if (t == MAT_FINAL_ASSEMBLY) {
    457:     shell->vshift = 0.0;
    458:     shell->vscale = 1.0;
    459:     VecDestroy(&shell->dshift);
    460:     VecDestroy(&shell->left);
    461:     VecDestroy(&shell->right);
    462:     MatDestroy(&shell->axpy);
    463:   }
    464:   return(0);
    465: }

    467: static PetscErrorCode MatMissingDiagonal_Shell(Mat A,PetscBool  *missing,PetscInt *d)
    468: {
    470:   *missing = PETSC_FALSE;
    471:   return(0);
    472: }

    474: PetscErrorCode MatAXPY_Shell(Mat Y,PetscScalar a,Mat X,MatStructure str)
    475: {
    476:   Mat_Shell      *shell = (Mat_Shell*)Y->data;

    480:   PetscObjectReference((PetscObject)X);
    481:   MatDestroy(&shell->axpy);
    482:   shell->axpy        = X;
    483:   shell->axpy_vscale = a;
    484:   return(0);
    485: }

    487: static struct _MatOps MatOps_Values = {0,
    488:                                        0,
    489:                                        0,
    490:                                        0,
    491:                                 /* 4*/ MatMultAdd_Shell,
    492:                                        0,
    493:                                        MatMultTransposeAdd_Shell,
    494:                                        0,
    495:                                        0,
    496:                                        0,
    497:                                 /*10*/ 0,
    498:                                        0,
    499:                                        0,
    500:                                        0,
    501:                                        0,
    502:                                 /*15*/ 0,
    503:                                        0,
    504:                                        0,
    505:                                        MatDiagonalScale_Shell,
    506:                                        0,
    507:                                 /*20*/ 0,
    508:                                        MatAssemblyEnd_Shell,
    509:                                        0,
    510:                                        0,
    511:                                 /*24*/ 0,
    512:                                        0,
    513:                                        0,
    514:                                        0,
    515:                                        0,
    516:                                 /*29*/ 0,
    517:                                        0,
    518:                                        0,
    519:                                        0,
    520:                                        0,
    521:                                 /*34*/ MatDuplicate_Shell,
    522:                                        0,
    523:                                        0,
    524:                                        0,
    525:                                        0,
    526:                                 /*39*/ MatAXPY_Shell,
    527:                                        0,
    528:                                        0,
    529:                                        0,
    530:                                        MatCopy_Shell,
    531:                                 /*44*/ 0,
    532:                                        MatScale_Shell,
    533:                                        MatShift_Shell,
    534:                                        MatDiagonalSet_Shell,
    535:                                        0,
    536:                                 /*49*/ 0,
    537:                                        0,
    538:                                        0,
    539:                                        0,
    540:                                        0,
    541:                                 /*54*/ 0,
    542:                                        0,
    543:                                        0,
    544:                                        0,
    545:                                        0,
    546:                                 /*59*/ 0,
    547:                                        MatDestroy_Shell,
    548:                                        0,
    549:                                        0,
    550:                                        0,
    551:                                 /*64*/ 0,
    552:                                        0,
    553:                                        0,
    554:                                        0,
    555:                                        0,
    556:                                 /*69*/ 0,
    557:                                        0,
    558:                                        MatConvert_Shell,
    559:                                        0,
    560:                                        0,
    561:                                 /*74*/ 0,
    562:                                        0,
    563:                                        0,
    564:                                        0,
    565:                                        0,
    566:                                 /*79*/ 0,
    567:                                        0,
    568:                                        0,
    569:                                        0,
    570:                                        0,
    571:                                 /*84*/ 0,
    572:                                        0,
    573:                                        0,
    574:                                        0,
    575:                                        0,
    576:                                 /*89*/ 0,
    577:                                        0,
    578:                                        0,
    579:                                        0,
    580:                                        0,
    581:                                 /*94*/ 0,
    582:                                        0,
    583:                                        0,
    584:                                        0,
    585:                                        0,
    586:                                 /*99*/ 0,
    587:                                        0,
    588:                                        0,
    589:                                        0,
    590:                                        0,
    591:                                /*104*/ 0,
    592:                                        0,
    593:                                        0,
    594:                                        0,
    595:                                        0,
    596:                                /*109*/ 0,
    597:                                        0,
    598:                                        0,
    599:                                        0,
    600:                                        MatMissingDiagonal_Shell,
    601:                                /*114*/ 0,
    602:                                        0,
    603:                                        0,
    604:                                        0,
    605:                                        0,
    606:                                /*119*/ 0,
    607:                                        0,
    608:                                        0,
    609:                                        0,
    610:                                        0,
    611:                                /*124*/ 0,
    612:                                        0,
    613:                                        0,
    614:                                        0,
    615:                                        0,
    616:                                /*129*/ 0,
    617:                                        0,
    618:                                        0,
    619:                                        0,
    620:                                        0,
    621:                                /*134*/ 0,
    622:                                        0,
    623:                                        0,
    624:                                        0,
    625:                                        0,
    626:                                /*139*/ 0,
    627:                                        0,
    628:                                        0
    629: };

    631: /*MC
    632:    MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type -- perhaps matrix free.

    634:   Level: advanced

    636: .seealso: MatCreateShell()
    637: M*/

    639: PETSC_EXTERN PetscErrorCode MatCreate_Shell(Mat A)
    640: {
    641:   Mat_Shell      *b;

    645:   PetscMemcpy(A->ops,&MatOps_Values,sizeof(struct _MatOps));

    647:   PetscNewLog(A,&b);
    648:   A->data = (void*)b;

    650:   PetscLayoutSetUp(A->rmap);
    651:   PetscLayoutSetUp(A->cmap);

    653:   b->ctx                 = 0;
    654:   b->vshift              = 0.0;
    655:   b->vscale              = 1.0;
    656:   b->managescalingshifts = PETSC_TRUE;
    657:   A->assembled           = PETSC_TRUE;
    658:   A->preallocated        = PETSC_FALSE;

    660:   PetscObjectChangeTypeName((PetscObject)A,MATSHELL);
    661:   return(0);
    662: }

    664: /*@C
    665:    MatCreateShell - Creates a new matrix class for use with a user-defined
    666:    private data storage format.

    668:   Collective on MPI_Comm

    670:    Input Parameters:
    671: +  comm - MPI communicator
    672: .  m - number of local rows (must be given)
    673: .  n - number of local columns (must be given)
    674: .  M - number of global rows (may be PETSC_DETERMINE)
    675: .  N - number of global columns (may be PETSC_DETERMINE)
    676: -  ctx - pointer to data needed by the shell matrix routines

    678:    Output Parameter:
    679: .  A - the matrix

    681:    Level: advanced

    683:   Usage:
    684: $    extern int mult(Mat,Vec,Vec);
    685: $    MatCreateShell(comm,m,n,M,N,ctx,&mat);
    686: $    MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
    687: $    [ Use matrix for operations that have been set ]
    688: $    MatDestroy(mat);

    690:    Notes:
    691:    The shell matrix type is intended to provide a simple class to use
    692:    with KSP (such as, for use with matrix-free methods). You should not
    693:    use the shell type if you plan to define a complete matrix class.

    695:    Fortran Notes:
    696:     To use this from Fortran with a ctx you must write an interface definition for this
    697:     function and for MatShellGetContext() that tells Fortran the Fortran derived data type you are passing
    698:     in as the ctx argument.

    700:    PETSc requires that matrices and vectors being used for certain
    701:    operations are partitioned accordingly.  For example, when
    702:    creating a shell matrix, A, that supports parallel matrix-vector
    703:    products using MatMult(A,x,y) the user should set the number
    704:    of local matrix rows to be the number of local elements of the
    705:    corresponding result vector, y. Note that this is information is
    706:    required for use of the matrix interface routines, even though
    707:    the shell matrix may not actually be physically partitioned.
    708:    For example,

    710: $
    711: $     Vec x, y
    712: $     extern int mult(Mat,Vec,Vec);
    713: $     Mat A
    714: $
    715: $     VecCreateMPI(comm,PETSC_DECIDE,M,&y);
    716: $     VecCreateMPI(comm,PETSC_DECIDE,N,&x);
    717: $     VecGetLocalSize(y,&m);
    718: $     VecGetLocalSize(x,&n);
    719: $     MatCreateShell(comm,m,n,M,N,ctx,&A);
    720: $     MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult);
    721: $     MatMult(A,x,y);
    722: $     MatDestroy(A);
    723: $     VecDestroy(y); VecDestroy(x);
    724: $

    727:    MATSHELL handles MatShift(), MatDiagonalSet(), MatDiagonalScale(), MatAXPY(), and MatScale() internally so these
    728:    operations cannot be overwritten unless MatShellSetManageScalingShifts() is called.

    731:     For rectangular matrices do all the scalings and shifts make sense?

    733:     Developers Notes:
    734:     Regarding shifting and scaling. The general form is

    736:           diag(left)(vscale*A + diag(dshift) + vshift I)diag(right)

    738:       The order you apply the operations is important. For example if you have a dshift then
    739:       apply a MatScale(s) you get s*vscale*A + s*diag(shift). But if you first scale and then shift
    740:       you get s*vscale*A + diag(shift)

    742:           A is the user provided function.

    744: .keywords: matrix, shell, create

    746: .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext(), MatShellSetContext(), MATSHELL, MatShellSetManageScalingShifts()
    747: @*/
    748: PetscErrorCode  MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *A)
    749: {

    753:   MatCreate(comm,A);
    754:   MatSetSizes(*A,m,n,M,N);
    755:   MatSetType(*A,MATSHELL);
    756:   MatShellSetContext(*A,ctx);
    757:   MatSetUp(*A);
    758:   return(0);
    759: }

    761: /*@
    762:     MatShellSetContext - sets the context for a shell matrix

    764:    Logically Collective on Mat

    766:     Input Parameters:
    767: +   mat - the shell matrix
    768: -   ctx - the context

    770:    Level: advanced

    772:    Fortran Notes:
    773:     To use this from Fortran you must write a Fortran interface definition for this
    774:     function that tells Fortran the Fortran derived data type that you are passing in as the ctx argument.

    776: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation()
    777: @*/
    778: PetscErrorCode  MatShellSetContext(Mat mat,void *ctx)
    779: {
    780:   Mat_Shell      *shell = (Mat_Shell*)mat->data;
    782:   PetscBool      flg;

    786:   PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
    787:   if (flg) {
    788:     shell->ctx = ctx;
    789:   } else SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Cannot attach context to non-shell matrix");
    790:   return(0);
    791: }

    793: /*@
    794:     MatShellSetManageScalingShifts - Allows the user to control the scaling and shift operations of the MATSHELL. Must be called immediately
    795:           after MatCreateShell()

    797:    Logically Collective on Mat

    799:     Input Parameter:
    800: .   mat - the shell matrix

    802:   Level: advanced

    804: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext(), MatShellSetOperation()
    805: @*/
    806: PetscErrorCode MatShellSetManageScalingShifts(Mat A)
    807: {
    809:   Mat_Shell      *shell;
    810:   PetscBool      flg;

    814:   PetscObjectTypeCompare((PetscObject)A,MATSHELL,&flg);
    815:   if (!flg) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_SUP,"Can only use with MATSHELL matrices");
    816:   shell = (Mat_Shell*)A->data;
    817:   shell->managescalingshifts = PETSC_FALSE;
    818:   A->ops->diagonalset   = NULL;
    819:   A->ops->diagonalscale = NULL;
    820:   A->ops->scale         = NULL;
    821:   A->ops->shift         = NULL;
    822:   A->ops->axpy          = NULL;
    823:   return(0);
    824: }

    826: /*@C
    827:     MatShellTestMult - Compares the multiply routine provided to the MATSHELL with differencing on a given function.

    829:    Logically Collective on Mat

    831:     Input Parameters:
    832: +   mat - the shell matrix
    833: .   f - the function
    834: .   base - differences are computed around this vector, see MatMFFDSetBase(), for Jacobians this is the point at which the Jacobian is being evaluated
    835: -   ctx - an optional context for the function

    837:    Output Parameter:
    838: .   flg - PETSC_TRUE if the multiply is likely correct

    840:    Options Database:
    841: .   -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference

    843:    Level: advanced

    845:    Fortran Notes:
    846:     Not supported from Fortran

    848: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellTestMultTranspose()
    849: @*/
    850: PetscErrorCode  MatShellTestMult(Mat mat,PetscErrorCode (*f)(void*,Vec,Vec),Vec base,void *ctx,PetscBool *flg)
    851: {
    853:   PetscInt       m,n;
    854:   Mat            mf,Dmf,Dmat,Ddiff;
    855:   PetscReal      Diffnorm,Dmfnorm;
    856:   PetscBool      v = PETSC_FALSE, flag = PETSC_TRUE;

    860:   PetscOptionsHasName(NULL,((PetscObject)mat)->prefix,"-mat_shell_test_mult_view",&v);
    861:   MatGetLocalSize(mat,&m,&n);
    862:   MatCreateMFFD(PetscObjectComm((PetscObject)mat),m,n,PETSC_DECIDE,PETSC_DECIDE,&mf);
    863:   MatMFFDSetFunction(mf,f,ctx);
    864:   MatMFFDSetBase(mf,base,NULL);

    866:   MatComputeExplicitOperator(mf,&Dmf);
    867:   MatComputeExplicitOperator(mat,&Dmat);

    869:   MatDuplicate(Dmat,MAT_COPY_VALUES,&Ddiff);
    870:   MatAXPY(Ddiff,-1.0,Dmf,DIFFERENT_NONZERO_PATTERN);
    871:   MatNorm(Ddiff,NORM_FROBENIUS,&Diffnorm);
    872:   MatNorm(Dmf,NORM_FROBENIUS,&Dmfnorm);
    873:   if (Diffnorm/Dmfnorm > 10*PETSC_SQRT_MACHINE_EPSILON) {
    874:     flag = PETSC_FALSE;
    875:     if (v) {
    876:       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));
    877:       MatViewFromOptions(Ddiff,(PetscObject)mat,"-mat_shell_test_mult_view");
    878:       MatViewFromOptions(Dmf,(PetscObject)mat,"-mat_shell_test_mult_view");
    879:       MatViewFromOptions(Dmat,(PetscObject)mat,"-mat_shell_test_mult_view");
    880:     }
    881:   } else if (v) {
    882:     PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL and matrix free multiple appear to produce the same results\n");
    883:   }
    884:   if (flg) *flg = flag;
    885:   MatDestroy(&Ddiff);
    886:   MatDestroy(&mf);
    887:   MatDestroy(&Dmf);
    888:   MatDestroy(&Dmat);
    889:   return(0);
    890: }

    892: /*@C
    893:     MatShellTestMultTranpose - Compares the multiply transpose routine provided to the MATSHELL with differencing on a given function.

    895:    Logically Collective on Mat

    897:     Input Parameters:
    898: +   mat - the shell matrix
    899: .   f - the function
    900: .   base - differences are computed around this vector, see MatMFFDSetBase(), for Jacobians this is the point at which the Jacobian is being evaluated
    901: -   ctx - an optional context for the function

    903:    Output Parameter:
    904: .   flg - PETSC_TRUE if the multiply is likely correct

    906:    Options Database:
    907: .   -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference

    909:    Level: advanced

    911:    Fortran Notes:
    912:     Not supported from Fortran

    914: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellTestMult()
    915: @*/
    916: PetscErrorCode  MatShellTestMultTranspose(Mat mat,PetscErrorCode (*f)(void*,Vec,Vec),Vec base,void *ctx,PetscBool *flg)
    917: {
    919:   Vec            x,y,z;
    920:   PetscInt       m,n,M,N;
    921:   Mat            mf,Dmf,Dmat,Ddiff;
    922:   PetscReal      Diffnorm,Dmfnorm;
    923:   PetscBool      v = PETSC_FALSE, flag = PETSC_TRUE;

    927:   PetscOptionsHasName(NULL,((PetscObject)mat)->prefix,"-mat_shell_test_mult_transpose_view",&v);
    928:   MatCreateVecs(mat,&x,&y);
    929:   VecDuplicate(y,&z);
    930:   MatGetLocalSize(mat,&m,&n);
    931:   MatGetSize(mat,&M,&N);
    932:   MatCreateMFFD(PetscObjectComm((PetscObject)mat),m,n,M,N,&mf);
    933:   MatMFFDSetFunction(mf,f,ctx);
    934:   MatMFFDSetBase(mf,base,NULL);
    935:   MatComputeExplicitOperator(mf,&Dmf);
    936:   MatTranspose(Dmf,MAT_INPLACE_MATRIX,&Dmf);
    937:   MatComputeExplicitOperatorTranspose(mat,&Dmat);

    939:   MatDuplicate(Dmat,MAT_COPY_VALUES,&Ddiff);
    940:   MatAXPY(Ddiff,-1.0,Dmf,DIFFERENT_NONZERO_PATTERN);
    941:   MatNorm(Ddiff,NORM_FROBENIUS,&Diffnorm);
    942:   MatNorm(Dmf,NORM_FROBENIUS,&Dmfnorm);
    943:   if (Diffnorm/Dmfnorm > 10*PETSC_SQRT_MACHINE_EPSILON) {
    944:     flag = PETSC_FALSE;
    945:     if (v) {
    946:       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));
    947:       MatViewFromOptions(Ddiff,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");
    948:       MatViewFromOptions(Dmf,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");
    949:       MatViewFromOptions(Dmat,(PetscObject)mat,"-mat_shell_test_mult_transpose_view");
    950:     }
    951:   } else if (v) {
    952:     PetscPrintf(PetscObjectComm((PetscObject)mat),"MATSHELL transpose and matrix free multiple appear to produce the same results\n");
    953:   }
    954:   if (flg) *flg = flag;
    955:   MatDestroy(&mf);
    956:   MatDestroy(&Dmat);
    957:   MatDestroy(&Ddiff);
    958:   MatDestroy(&Dmf);
    959:   VecDestroy(&x);
    960:   VecDestroy(&y);
    961:   VecDestroy(&z);
    962:   return(0);
    963: }

    965: /*@C
    966:     MatShellSetOperation - Allows user to set a matrix operation for a shell matrix.

    968:    Logically Collective on Mat

    970:     Input Parameters:
    971: +   mat - the shell matrix
    972: .   op - the name of the operation
    973: -   f - the function that provides the operation.

    975:    Level: advanced

    977:     Usage:
    978: $      extern PetscErrorCode usermult(Mat,Vec,Vec);
    979: $      MatCreateShell(comm,m,n,M,N,ctx,&A);
    980: $      MatShellSetOperation(A,MATOP_MULT,(void(*)(void))usermult);

    982:     Notes:
    983:     See the file include/petscmat.h for a complete list of matrix
    984:     operations, which all have the form MATOP_<OPERATION>, where
    985:     <OPERATION> is the name (in all capital letters) of the
    986:     user interface routine (e.g., MatMult() -> MATOP_MULT).

    988:     All user-provided functions (except for MATOP_DESTROY) should have the same calling
    989:     sequence as the usual matrix interface routines, since they
    990:     are intended to be accessed via the usual matrix interface
    991:     routines, e.g.,
    992: $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)

    994:     In particular each function MUST return an error code of 0 on success and
    995:     nonzero on failure.

    997:     Within each user-defined routine, the user should call
    998:     MatShellGetContext() to obtain the user-defined context that was
    999:     set by MatCreateShell().

    1001:     Fortran Notes:
    1002:     For MatCreateVecs() the user code should check if the input left or right matrix is -1 and in that case not
    1003:        generate a matrix. See src/mat/examples/tests/ex120f.F

    1005:     Use MatSetOperation() to set an operation for any matrix type

    1007: .keywords: matrix, shell, set, operation

    1009: .seealso: MatCreateShell(), MatShellGetContext(), MatShellGetOperation(), MatShellSetContext(), MatSetOperation(), MatShellSetManageScalingShifts()
    1010: @*/
    1011: PetscErrorCode MatShellSetOperation(Mat mat,MatOperation op,void (*f)(void))
    1012: {
    1013:   PetscBool      flg;
    1014:   Mat_Shell      *shell;

    1019:   PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
    1020:   if (!flg) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Can only use with MATSHELL matrices");
    1021:   shell = (Mat_Shell*)mat->data;

    1023:   switch (op) {
    1024:   case MATOP_DESTROY:
    1025:     shell->ops->destroy = (PetscErrorCode (*)(Mat))f;
    1026:     break;
    1027:   case MATOP_VIEW:
    1028:     if (!mat->ops->viewnative) {
    1029:       mat->ops->viewnative = mat->ops->view;
    1030:     }
    1031:     mat->ops->view = (PetscErrorCode (*)(Mat,PetscViewer))f;
    1032:     break;
    1033:   case MATOP_COPY:
    1034:     shell->ops->copy = (PetscErrorCode (*)(Mat,Mat,MatStructure))f;
    1035:     break;
    1036:   case MATOP_DIAGONAL_SET:
    1037:   case MATOP_DIAGONAL_SCALE:
    1038:   case MATOP_SHIFT:
    1039:   case MATOP_SCALE:
    1040:   case MATOP_AXPY:
    1041:     if (shell->managescalingshifts) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ARG_WRONGSTATE,"MATSHELL is managing scalings and shifts, see MatShellSetManageScalingShifts()");
    1042:     (((void(**)(void))mat->ops)[op]) = f;
    1043:     break;
    1044:   case MATOP_GET_DIAGONAL:
    1045:     if (shell->managescalingshifts) {
    1046:       shell->ops->getdiagonal = (PetscErrorCode (*)(Mat,Vec))f;
    1047:       mat->ops->getdiagonal   = MatGetDiagonal_Shell;
    1048:     } else {
    1049:       shell->ops->getdiagonal = NULL;
    1050:       mat->ops->getdiagonal   = (PetscErrorCode (*)(Mat,Vec))f;
    1051:     }
    1052:     break;
    1053:   case MATOP_MULT:
    1054:     if (shell->managescalingshifts) {
    1055:       shell->ops->mult = (PetscErrorCode (*)(Mat,Vec,Vec))f;
    1056:       mat->ops->mult   = MatMult_Shell;
    1057:     } else {
    1058:       shell->ops->mult = NULL;
    1059:       mat->ops->mult   = (PetscErrorCode (*)(Mat,Vec,Vec))f;
    1060:     }
    1061:     break;
    1062:   case MATOP_MULT_TRANSPOSE:
    1063:     if (shell->managescalingshifts) {
    1064:       shell->ops->multtranspose = (PetscErrorCode (*)(Mat,Vec,Vec))f;
    1065:       mat->ops->multtranspose   = MatMultTranspose_Shell;
    1066:     } else {
    1067:       shell->ops->multtranspose = NULL;
    1068:       mat->ops->multtranspose   = (PetscErrorCode (*)(Mat,Vec,Vec))f;
    1069:     }
    1070:     break;
    1071:   default:
    1072:     (((void(**)(void))mat->ops)[op]) = f;
    1073:     break;
    1074:   }
    1075:   return(0);
    1076: }

    1078: /*@C
    1079:     MatShellGetOperation - Gets a matrix function for a shell matrix.

    1081:     Not Collective

    1083:     Input Parameters:
    1084: +   mat - the shell matrix
    1085: -   op - the name of the operation

    1087:     Output Parameter:
    1088: .   f - the function that provides the operation.

    1090:     Level: advanced

    1092:     Notes:
    1093:     See the file include/petscmat.h for a complete list of matrix
    1094:     operations, which all have the form MATOP_<OPERATION>, where
    1095:     <OPERATION> is the name (in all capital letters) of the
    1096:     user interface routine (e.g., MatMult() -> MATOP_MULT).

    1098:     All user-provided functions have the same calling
    1099:     sequence as the usual matrix interface routines, since they
    1100:     are intended to be accessed via the usual matrix interface
    1101:     routines, e.g.,
    1102: $       MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec)

    1104:     Within each user-defined routine, the user should call
    1105:     MatShellGetContext() to obtain the user-defined context that was
    1106:     set by MatCreateShell().

    1108: .keywords: matrix, shell, set, operation

    1110: .seealso: MatCreateShell(), MatShellGetContext(), MatShellSetOperation(), MatShellSetContext()
    1111: @*/
    1112: PetscErrorCode MatShellGetOperation(Mat mat,MatOperation op,void(**f)(void))
    1113: {
    1114:   PetscBool      flg;
    1115:   Mat_Shell      *shell;

    1120:   PetscObjectTypeCompare((PetscObject)mat,MATSHELL,&flg);
    1121:   if (!flg) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Can only use with MATSHELL matrices");
    1122:   shell = (Mat_Shell*)mat->data;

    1124:   switch (op) {
    1125:   case MATOP_DESTROY:
    1126:     *f = (void (*)(void))shell->ops->destroy;
    1127:     break;
    1128:   case MATOP_VIEW:
    1129:     *f = (void (*)(void))mat->ops->view;
    1130:     break;
    1131:   case MATOP_COPY:
    1132:     *f = (void (*)(void))shell->ops->copy;
    1133:     break;
    1134:   case MATOP_DIAGONAL_SET:
    1135:   case MATOP_DIAGONAL_SCALE:
    1136:   case MATOP_SHIFT:
    1137:   case MATOP_SCALE:
    1138:   case MATOP_AXPY:
    1139:     *f = (((void (**)(void))mat->ops)[op]);
    1140:     break;
    1141:   case MATOP_GET_DIAGONAL:
    1142:     if (shell->ops->getdiagonal)
    1143:       *f = (void (*)(void))shell->ops->getdiagonal;
    1144:     else
    1145:       *f = (((void (**)(void))mat->ops)[op]);
    1146:     break;
    1147:   case MATOP_MULT:
    1148:     if (shell->ops->mult)
    1149:       *f = (void (*)(void))shell->ops->mult;
    1150:     else
    1151:       *f = (((void (**)(void))mat->ops)[op]);
    1152:     break;
    1153:   case MATOP_MULT_TRANSPOSE:
    1154:     if (shell->ops->multtranspose)
    1155:       *f = (void (*)(void))shell->ops->multtranspose;
    1156:     else
    1157:       *f = (((void (**)(void))mat->ops)[op]);
    1158:     break;
    1159:   default:
    1160:     *f = (((void (**)(void))mat->ops)[op]);
    1161:   }
    1162:   return(0);
    1163: }