Actual source code: schurm.c
petsc-3.7.1 2016-05-15
Report Typos and Errors

      2: #include <petsc/private/matimpl.h>
      3: #include <petscksp.h>                 /*I "petscksp.h" I*/
      4: const char *const MatSchurComplementAinvTypes[] = {"DIAG","LUMP","MatSchurComplementAinvType","MAT_SCHUR_COMPLEMENT_AINV_",0};

      6: typedef struct {
      7:   Mat                        A,Ap,B,C,D;
      8:   KSP                        ksp;
      9:   Vec                        work1,work2;
     10:   MatSchurComplementAinvType ainvtype;
     11: } Mat_SchurComplement;

     17: PetscErrorCode MatCreateVecs_SchurComplement(Mat N,Vec *right,Vec *left)
     18: {
     19:   Mat_SchurComplement *Na = (Mat_SchurComplement*)N->data;
     20:   PetscErrorCode      ierr;

     23:   if (Na->D) {
     24:     MatCreateVecs(Na->D,right,left);
     25:     return(0);
     26:   }
     27:   if (right) {
     28:     MatCreateVecs(Na->B,right,NULL);
     29:   }
     30:   if (left) {
     31:     MatCreateVecs(Na->C,NULL,left);
     32:   }
     33:   return(0);
     34: }

     38: PetscErrorCode MatView_SchurComplement(Mat N,PetscViewer viewer)
     39: {
     40:   Mat_SchurComplement *Na = (Mat_SchurComplement*)N->data;
     41:   PetscErrorCode      ierr;

     44:   PetscViewerASCIIPrintf(viewer,"Schur complement A11 - A10 inv(A00) A01\n");
     45:   if (Na->D) {
     46:     PetscViewerASCIIPrintf(viewer,"A11\n");
     47:     PetscViewerASCIIPushTab(viewer);
     48:     MatView(Na->D,viewer);
     49:     PetscViewerASCIIPopTab(viewer);
     50:   } else {
     51:     PetscViewerASCIIPrintf(viewer,"A11 = 0\n");
     52:   }
     53:   PetscViewerASCIIPrintf(viewer,"A10\n");
     54:   PetscViewerASCIIPushTab(viewer);
     55:   MatView(Na->C,viewer);
     56:   PetscViewerASCIIPopTab(viewer);
     57:   PetscViewerASCIIPrintf(viewer,"KSP of A00\n");
     58:   PetscViewerASCIIPushTab(viewer);
     59:   KSPView(Na->ksp,viewer);
     60:   PetscViewerASCIIPopTab(viewer);
     61:   PetscViewerASCIIPrintf(viewer,"A01\n");
     62:   PetscViewerASCIIPushTab(viewer);
     63:   MatView(Na->B,viewer);
     64:   PetscViewerASCIIPopTab(viewer);
     65:   return(0);
     66: }

     68: /*
     69:            A11^T - A01^T ksptrans(A00,Ap00) A10^T
     70: */
     73: PetscErrorCode MatMultTranspose_SchurComplement(Mat N,Vec x,Vec y)
     74: {
     75:   Mat_SchurComplement *Na = (Mat_SchurComplement*)N->data;
     76:   PetscErrorCode      ierr;

     79:   if (!Na->work1) {MatCreateVecs(Na->A,&Na->work1,NULL);}
     80:   if (!Na->work2) {MatCreateVecs(Na->A,&Na->work2,NULL);}
     81:   MatMultTranspose(Na->C,x,Na->work1);
     82:   KSPSolveTranspose(Na->ksp,Na->work1,Na->work2);
     83:   MatMultTranspose(Na->B,Na->work2,y);
     84:   VecScale(y,-1.0);
     85:   if (Na->D) {
     86:     MatMultTransposeAdd(Na->D,x,y,y);
     87:   }
     88:   return(0);
     89: }

     91: /*
     92:            A11 - A10 ksp(A00,Ap00) A01
     93: */
     96: PetscErrorCode MatMult_SchurComplement(Mat N,Vec x,Vec y)
     97: {
     98:   Mat_SchurComplement *Na = (Mat_SchurComplement*)N->data;
     99:   PetscErrorCode      ierr;

    102:   if (!Na->work1) {MatCreateVecs(Na->A,&Na->work1,NULL);}
    103:   if (!Na->work2) {MatCreateVecs(Na->A,&Na->work2,NULL);}
    104:   MatMult(Na->B,x,Na->work1);
    105:   KSPSolve(Na->ksp,Na->work1,Na->work2);
    106:   MatMult(Na->C,Na->work2,y);
    107:   VecScale(y,-1.0);
    108:   if (Na->D) {
    109:     MatMultAdd(Na->D,x,y,y);
    110:   }
    111:   return(0);
    112: }

    114: /*
    115:            A11 - A10 ksp(A00,Ap00) A01
    116: */
    119: PetscErrorCode MatMultAdd_SchurComplement(Mat N,Vec x,Vec y,Vec z)
    120: {
    121:   Mat_SchurComplement *Na = (Mat_SchurComplement*)N->data;
    122:   PetscErrorCode      ierr;

    125:   if (!Na->work1) {MatCreateVecs(Na->A,&Na->work1,NULL);}
    126:   if (!Na->work2) {MatCreateVecs(Na->A,&Na->work2,NULL);}
    127:   MatMult(Na->B,x,Na->work1);
    128:   KSPSolve(Na->ksp,Na->work1,Na->work2);
    129:   if (y == z) {
    130:     VecScale(Na->work2,-1.0);
    131:     MatMultAdd(Na->C,Na->work2,z,z);
    132:   } else {
    133:     MatMult(Na->C,Na->work2,z);
    134:     VecAYPX(z,-1.0,y);
    135:   }
    136:   if (Na->D) {
    137:     MatMultAdd(Na->D,x,z,z);
    138:   }
    139:   return(0);
    140: }

    144: PetscErrorCode MatSetFromOptions_SchurComplement(PetscOptionItems *PetscOptionsObject,Mat N)
    145: {
    146:   Mat_SchurComplement *Na = (Mat_SchurComplement*)N->data;
    147:   PetscErrorCode      ierr;

    150:   PetscOptionsHead(PetscOptionsObject,"MatSchurComplementOptions");
    151:   Na->ainvtype = MAT_SCHUR_COMPLEMENT_AINV_DIAG;
    152:   PetscOptionsEnum("-mat_schur_complement_ainv_type","Type of approximation for inv(A00) used when assembling Sp = A11 - A10 inv(A00) A01","MatSchurComplementSetAinvType",MatSchurComplementAinvTypes,(PetscEnum)Na->ainvtype,(PetscEnum*)&Na->ainvtype,NULL);
    153:   PetscOptionsTail();
    154:   KSPSetFromOptions(Na->ksp);
    155:   return(0);
    156: }

    160: PetscErrorCode MatDestroy_SchurComplement(Mat N)
    161: {
    162:   Mat_SchurComplement *Na = (Mat_SchurComplement*)N->data;
    163:   PetscErrorCode      ierr;

    166:   MatDestroy(&Na->A);
    167:   MatDestroy(&Na->Ap);
    168:   MatDestroy(&Na->B);
    169:   MatDestroy(&Na->C);
    170:   MatDestroy(&Na->D);
    171:   VecDestroy(&Na->work1);
    172:   VecDestroy(&Na->work2);
    173:   KSPDestroy(&Na->ksp);
    174:   PetscFree(N->data);
    175:   return(0);
    176: }

    180: /*@
    181:       MatCreateSchurComplement - Creates a new matrix object that behaves like the Schur complement of a matrix

    183:    Collective on Mat

    185:    Input Parameters:
    186: +   A00,A01,A10,A11  - the four parts of the original matrix A = [A00 A01; A10 A11] (A11 is optional)
    187: -   Ap00             - preconditioning matrix for use in ksp(A00,Ap00) to approximate the action of A^{-1}

    189:    Output Parameter:
    190: .   S - the matrix that the Schur complement S = A11 - A10 ksp(A00,Ap00) A01

    192:    Level: intermediate

    194:    Notes: The Schur complement is NOT actually formed! Rather, this
    195:           object performs the matrix-vector product by using formula S = A11 - A10 A^{-1} A01
    196:           for Schur complement S and a KSP solver to approximate the action of A^{-1}.

    198:           All four matrices must have the same MPI communicator.

    200:           A00 and  A11 must be square matrices.

    202: .seealso: MatCreateNormal(), MatMult(), MatCreate(), MatSchurComplementGetKSP(), MatSchurComplementUpdateSubMatrices(), MatCreateTranspose(), MatGetSchurComplement()

    204: @*/
    205: PetscErrorCode  MatCreateSchurComplement(Mat A00,Mat Ap00,Mat A01,Mat A10,Mat A11,Mat *S)
    206: {

    210:   KSPInitializePackage();
    211:   MatCreate(((PetscObject)A00)->comm,S);
    212:   MatSetType(*S,MATSCHURCOMPLEMENT);
    213:   MatSchurComplementSetSubMatrices(*S,A00,Ap00,A01,A10,A11);
    214:   return(0);
    215: }

    219: /*@
    220:       MatSchurComplementSetSubMatrices - Sets the matrices that define the Schur complement

    222:    Collective on Mat

    224:    Input Parameter:
    225: +   S                - matrix obtained with MatCreateSchurComplement (or equivalent) and implementing the action of A11 - A10 ksp(A00,Ap00) A01
    226: .   A00,A01,A10,A11  - the four parts of A = [A00 A01; A10 A11] (A11 is optional)
    227: -   Ap00             - preconditioning matrix for use in ksp(A00,Ap00) to approximate the action of A^{-1}.

    229:    Level: intermediate

    231:    Notes: The Schur complement is NOT actually formed! Rather, this
    232:           object performs the matrix-vector product by using formula S = A11 - A10 A^{-1} A01
    233:           for Schur complement S and a KSP solver to approximate the action of A^{-1}.

    235:           All four matrices must have the same MPI communicator.

    237:           A00 and  A11 must be square matrices.

    239: .seealso: MatCreateNormal(), MatMult(), MatCreate(), MatSchurComplementGetKSP(), MatSchurComplementUpdateSubMatrices(), MatCreateTranspose(), MatCreateSchurComplement(), MatGetSchurComplement()

    241: @*/
    242: PetscErrorCode  MatSchurComplementSetSubMatrices(Mat S,Mat A00,Mat Ap00,Mat A01,Mat A10,Mat A11)
    243: {
    244:   PetscErrorCode      ierr;
    245:   PetscInt            m,n;
    246:   Mat_SchurComplement *Na = (Mat_SchurComplement*)S->data;

    249:   if (S->assembled) SETERRQ(PetscObjectComm((PetscObject)S),PETSC_ERR_ARG_WRONGSTATE,"Use MatSchurComplementUpdateSubMatrices() for already used matrix");
    257:   if (A00->rmap->n != A00->cmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of A00 %D do not equal local columns %D",A00->rmap->n,A00->cmap->n);
    258:   if (A00->rmap->n != Ap00->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of A00 %D do not equal local rows of Ap00 %D",A00->rmap->n,Ap00->rmap->n);
    259:   if (Ap00->rmap->n != Ap00->cmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of Ap00 %D do not equal local columns %D",Ap00->rmap->n,Ap00->cmap->n);
    260:   if (A00->cmap->n != A01->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local columns of A00 %D do not equal local rows of A01 %D",A00->cmap->n,A01->rmap->n);
    261:   if (A10->cmap->n != A00->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local columns of A10 %D do not equal local rows of A00 %D",A10->cmap->n,A00->rmap->n);
    262:   if (A11) {
    265:     if (A10->rmap->n != A11->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of A10 %D do not equal local rows A11 %D",A10->rmap->n,A11->rmap->n);
    266:   }

    268:   MatGetLocalSize(A01,NULL,&n);
    269:   MatGetLocalSize(A10,&m,NULL);
    270:   MatSetSizes(S,m,n,PETSC_DECIDE,PETSC_DECIDE);
    271:   PetscObjectReference((PetscObject)A00);
    272:   PetscObjectReference((PetscObject)Ap00);
    273:   PetscObjectReference((PetscObject)A01);
    274:   PetscObjectReference((PetscObject)A10);
    275:   Na->A  = A00;
    276:   Na->Ap = Ap00;
    277:   Na->B  = A01;
    278:   Na->C  = A10;
    279:   Na->D  = A11;
    280:   if (A11) {
    281:     PetscObjectReference((PetscObject)A11);
    282:   }
    283:   S->assembled    = PETSC_TRUE;
    284:   S->preallocated = PETSC_TRUE;

    286:   PetscLayoutSetUp((S)->rmap);
    287:   PetscLayoutSetUp((S)->cmap);
    288:   KSPSetOperators(Na->ksp,A00,Ap00);
    289:   return(0);
    290: }

    294: /*@
    295:   MatSchurComplementGetKSP - Gets the KSP object that is used to invert A00 in the Schur complement matrix S = A11 - A10 ksp(A00,Ap00) A01

    297:   Not Collective

    299:   Input Parameter:
    300: . S - matrix obtained with MatCreateSchurComplement() (or equivalent) and implementing the action of A11 - A10 ksp(A00,Ap00) A01

    302:   Output Parameter:
    303: . ksp - the linear solver object

    305:   Options Database:
    306: . -fieldsplit_<splitname_0>_XXX sets KSP and PC options for the 0-split solver inside the Schur complement used in PCFieldSplit; default <splitname_0> is 0.

    308:   Level: intermediate

    310: .seealso: MatSchurComplementSetKSP(), MatCreateSchurComplement(), MatCreateNormal(), MatMult(), MatCreate()
    311: @*/
    312: PetscErrorCode MatSchurComplementGetKSP(Mat S, KSP *ksp)
    313: {
    314:   Mat_SchurComplement *Na;

    319:   Na   = (Mat_SchurComplement*) S->data;
    320:   *ksp = Na->ksp;
    321:   return(0);
    322: }

    326: /*@
    327:   MatSchurComplementSetKSP - Sets the KSP object that is used to invert A00 in the Schur complement matrix S = A11 - A10 ksp(A00,Ap00) A01

    329:   Not Collective

    331:   Input Parameters:
    332: + S   - matrix created with MatCreateSchurComplement()
    333: - ksp - the linear solver object

    335:   Level: developer

    337:   Developer Notes:
    338:     This is used in PCFieldSplit to reuse the 0-split KSP to implement ksp(A00,Ap00) in S.

    340: .seealso: MatSchurComplementGetKSP(), MatCreateSchurComplement(), MatCreateNormal(), MatMult(), MatCreate(), MATSCHURCOMPLEMENT
    341: @*/
    342: PetscErrorCode MatSchurComplementSetKSP(Mat S, KSP ksp)
    343: {
    344:   Mat_SchurComplement *Na;
    345:   PetscErrorCode      ierr;

    350:   Na      = (Mat_SchurComplement*) S->data;
    351:   PetscObjectReference((PetscObject)ksp);
    352:   KSPDestroy(&Na->ksp);
    353:   Na->ksp = ksp;
    354:   KSPSetOperators(Na->ksp, Na->A, Na->Ap);
    355:   return(0);
    356: }

    360: /*@
    361:       MatSchurComplementUpdateSubMatrices - Updates the Schur complement matrix object with new submatrices

    363:    Collective on Mat

    365:    Input Parameters:
    366: +   S                - matrix obtained with MatCreateSchurComplement() (or equivalent) and implementing the action of A11 - A10 ksp(A00,Ap00) A01
    367: .   A00,A01,A10,A11  - the four parts of A = [A00 A01; A10 A11] (A11 is optional)
    368: -   Ap00             - preconditioning matrix for use in ksp(A00,Ap00) to approximate the action of A^{-1}.

    370:    Level: intermediate

    372:    Notes: All four matrices must have the same MPI communicator

    374:           A00 and  A11 must be square matrices

    376:           All of the matrices provided must have the same sizes as was used with MatCreateSchurComplement() or MatSchurComplementSetSubMatrices()
    377:           though they need not be the same matrices.

    379: .seealso: MatCreateNormal(), MatMult(), MatCreate(), MatSchurComplementGetKSP(), MatCreateSchurComplement()

    381: @*/
    382: PetscErrorCode  MatSchurComplementUpdateSubMatrices(Mat S,Mat A00,Mat Ap00,Mat A01,Mat A10,Mat A11)
    383: {
    384:   PetscErrorCode      ierr;
    385:   Mat_SchurComplement *Na = (Mat_SchurComplement*)S->data;

    388:   if (!S->assembled) SETERRQ(PetscObjectComm((PetscObject)S),PETSC_ERR_ARG_WRONGSTATE,"Use MatSchurComplementSetSubMatrices() for a new matrix");
    395:   if (A00->rmap->n != A00->cmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of A00 %D do not equal local columns %D",A00->rmap->n,A00->cmap->n);
    396:   if (A00->rmap->n != Ap00->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of A00 %D do not equal local rows of Ap00 %D",A00->rmap->n,Ap00->rmap->n);
    397:   if (Ap00->rmap->n != Ap00->cmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of Ap00 %D do not equal local columns %D",Ap00->rmap->n,Ap00->cmap->n);
    398:   if (A00->cmap->n != A01->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local columns of A00 %D do not equal local rows of A01 %D",A00->cmap->n,A01->rmap->n);
    399:   if (A10->cmap->n != A00->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local columns of A10 %D do not equal local rows of A00 %D",A10->cmap->n,A00->rmap->n);
    400:   if (A11) {
    403:     if (A10->rmap->n != A11->rmap->n) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_SIZ,"Local rows of A10 %D do not equal local rows A11 %D",A10->rmap->n,A11->rmap->n);
    404:   }

    406:   PetscObjectReference((PetscObject)A00);
    407:   PetscObjectReference((PetscObject)Ap00);
    408:   PetscObjectReference((PetscObject)A01);
    409:   PetscObjectReference((PetscObject)A10);
    410:   if (A11) {
    411:     PetscObjectReference((PetscObject)A11);
    412:   }

    414:   MatDestroy(&Na->A);
    415:   MatDestroy(&Na->Ap);
    416:   MatDestroy(&Na->B);
    417:   MatDestroy(&Na->C);
    418:   MatDestroy(&Na->D);

    420:   Na->A  = A00;
    421:   Na->Ap = Ap00;
    422:   Na->B  = A01;
    423:   Na->C  = A10;
    424:   Na->D  = A11;

    426:   KSPSetOperators(Na->ksp,A00,Ap00);
    427:   return(0);
    428: }

    433: /*@C
    434:   MatSchurComplementGetSubMatrices - Get the individual submatrices in the Schur complement

    436:   Collective on Mat

    438:   Input Parameter:
    439: . S                - matrix obtained with MatCreateSchurComplement() (or equivalent) and implementing the action of A11 - A10 ksp(A00,Ap00) A01

    441:   Output Paramters:
    442: + A00,A01,A10,A11  - the four parts of the original matrix A = [A00 A01; A10 A11] (A11 is optional)
    443: - Ap00             - preconditioning matrix for use in ksp(A00,Ap00) to approximate the action of A^{-1}.

    445:   Note: A11 is optional, and thus can be NULL.  The submatrices are not increfed before they are returned and should not be modified or destroyed.

    447:   Level: intermediate

    449: .seealso: MatCreateNormal(), MatMult(), MatCreate(), MatSchurComplementGetKSP(), MatCreateSchurComplement(), MatSchurComplementUpdateSubMatrices()
    450: @*/
    451: PetscErrorCode  MatSchurComplementGetSubMatrices(Mat S,Mat *A00,Mat *Ap00,Mat *A01,Mat *A10,Mat *A11)
    452: {
    453:   Mat_SchurComplement *Na = (Mat_SchurComplement*) S->data;
    454:   PetscErrorCode      ierr;
    455:   PetscBool           flg;

    459:   PetscObjectTypeCompare((PetscObject)S,MATSCHURCOMPLEMENT,&flg);
    460:   if (flg) {
    461:     if (A00) *A00 = Na->A;
    462:     if (Ap00) *Ap00 = Na->Ap;
    463:     if (A01) *A01 = Na->B;
    464:     if (A10) *A10 = Na->C;
    465:     if (A11) *A11 = Na->D;
    466:   } else {
    467:     if (A00) *A00 = 0;
    468:     if (Ap00) *Ap00 = 0;
    469:     if (A01) *A01 = 0;
    470:     if (A10) *A10 = 0;
    471:     if (A11) *A11 = 0;
    472:   }
    473:   return(0);
    474: }

    476: #include <petsc/private/kspimpl.h>

    480: /*@
    481:   MatSchurComplementComputeExplicitOperator - Compute the Schur complement matrix explicitly

    483:   Collective on Mat

    485:   Input Parameter:
    486: . M - the matrix obtained with MatCreateSchurComplement()

    488:   Output Parameter:
    489: . S - the Schur complement matrix

    491:   Note: This can be expensive, so it is mainly for testing

    493:   Level: advanced

    495: .seealso: MatCreateSchurComplement(), MatSchurComplementUpdate()
    496: @*/
    497: PetscErrorCode MatSchurComplementComputeExplicitOperator(Mat M, Mat *S)
    498: {
    499:   Mat            B, C, D;
    500:   KSP            ksp;
    501:   PC             pc;
    502:   PetscBool      isLU, isILU;
    503:   PetscReal      fill = 2.0;

    507:   MatSchurComplementGetSubMatrices(M, NULL, NULL, &B, &C, &D);
    508:   MatSchurComplementGetKSP(M, &ksp);
    509:   KSPGetPC(ksp, &pc);
    510:   PetscObjectTypeCompare((PetscObject) pc, PCLU, &isLU);
    511:   PetscObjectTypeCompare((PetscObject) pc, PCILU, &isILU);
    512:   if (isLU || isILU) {
    513:     Mat       fact, Bd, AinvB, AinvBd;
    514:     PetscReal eps = 1.0e-10;

    516:     /* This can be sped up for banded LU */
    517:     KSPSetUp(ksp);
    518:     PCFactorGetMatrix(pc, &fact);
    519:     MatConvert(B, MATDENSE, MAT_INITIAL_MATRIX, &Bd);
    520:     MatDuplicate(Bd, MAT_DO_NOT_COPY_VALUES, &AinvBd);
    521:     MatMatSolve(fact, Bd, AinvBd);
    522:     MatDestroy(&Bd);
    523:     MatChop(AinvBd, eps);
    524:     MatConvert(AinvBd, MATAIJ, MAT_INITIAL_MATRIX, &AinvB);
    525:     MatDestroy(&AinvBd);
    526:     MatMatMult(C, AinvB, MAT_INITIAL_MATRIX, fill, S);
    527:     MatDestroy(&AinvB);
    528:   } else {
    529:     Mat Ainvd, Ainv;

    531:     PCComputeExplicitOperator(pc, &Ainvd);
    532:     MatConvert(Ainvd, MATAIJ, MAT_INITIAL_MATRIX, &Ainv);
    533:     MatDestroy(&Ainvd);
    534: #if 0
    535:     /* Symmetric version */
    536:     MatPtAP(Ainv, B, MAT_INITIAL_MATRIX, fill, S);
    537: #else
    538:     /* Nonsymmetric version */
    539:     MatMatMatMult(C, Ainv, B, MAT_INITIAL_MATRIX, fill, S);
    540: #endif
    541:     MatDestroy(&Ainv);
    542:   }

    544:   PetscViewerPushFormat(PETSC_VIEWER_STDOUT_WORLD, PETSC_VIEWER_ASCII_INFO);
    545:   MatView(*S, PETSC_VIEWER_STDOUT_WORLD);
    546:   PetscViewerPopFormat(PETSC_VIEWER_STDOUT_WORLD);

    548:   if (D) {
    549:     MatInfo   info;
    550:     PetscReal norm;

    552:     MatGetInfo(D, MAT_GLOBAL_SUM, &info);
    553:     if (info.nz_used) {
    554:       MatNorm(D, NORM_INFINITY, &norm);
    555:       if (norm > PETSC_MACHINE_EPSILON) SETERRQ(PetscObjectComm((PetscObject) M), PETSC_ERR_SUP, "Not yet implemented for Schur complements with non-vanishing D");
    556:     }
    557:   }
    558:   return(0);
    559: }

    563: /* Developer Notes: This should be implemented with a MatCreate_SchurComplement() as that is the standard design for new Mat classes. */
    564: PetscErrorCode MatGetSchurComplement_Basic(Mat mat,IS isrow0,IS iscol0,IS isrow1,IS iscol1,MatReuse mreuse,Mat *newmat,MatSchurComplementAinvType ainvtype, MatReuse preuse,Mat *newpmat)
    565: {
    567:   Mat            A=0,Ap=0,B=0,C=0,D=0;
    568:   MatReuse       reuse;

    577:   if (mreuse == MAT_IGNORE_MATRIX && preuse == MAT_IGNORE_MATRIX) return(0);

    581:   if (mat->factortype) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");

    583:   reuse = MAT_INITIAL_MATRIX;
    584:   if (mreuse == MAT_REUSE_MATRIX) {
    585:     MatSchurComplementGetSubMatrices(*newmat,&A,&Ap,&B,&C,&D);
    586:     if (!A || !Ap || !B || !C) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ARG_WRONGSTATE,"Attempting to reuse matrix but Schur complement matrices unset");
    587:     if (A != Ap) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_ARG_WRONGSTATE,"Preconditioning matrix does not match operator");
    588:     MatDestroy(&Ap); /* get rid of extra reference */
    589:     reuse = MAT_REUSE_MATRIX;
    590:   }
    591:   MatGetSubMatrix(mat,isrow0,iscol0,reuse,&A);
    592:   MatGetSubMatrix(mat,isrow0,iscol1,reuse,&B);
    593:   MatGetSubMatrix(mat,isrow1,iscol0,reuse,&C);
    594:   MatGetSubMatrix(mat,isrow1,iscol1,reuse,&D);
    595:   switch (mreuse) {
    596:   case MAT_INITIAL_MATRIX:
    597:     MatCreateSchurComplement(A,A,B,C,D,newmat);
    598:     break;
    599:   case MAT_REUSE_MATRIX:
    600:     MatSchurComplementUpdateSubMatrices(*newmat,A,A,B,C,D);
    601:     break;
    602:   default:
    603:     if (mreuse != MAT_IGNORE_MATRIX) SETERRQ(PetscObjectComm((PetscObject)mat),PETSC_ERR_SUP,"Unrecognized value of mreuse");
    604:   }
    605:   if (preuse != MAT_IGNORE_MATRIX) {
    606:     MatCreateSchurComplementPmat(A,B,C,D,ainvtype,preuse,newpmat);
    607:   }
    608:   MatDestroy(&A);
    609:   MatDestroy(&B);
    610:   MatDestroy(&C);
    611:   MatDestroy(&D);
    612:   return(0);
    613: }

    617: /*@
    618:     MatGetSchurComplement - Obtain the Schur complement from eliminating part of the matrix in another part.

    620:     Collective on Mat

    622:     Input Parameters:
    623: +   A      - matrix in which the complement is to be taken
    624: .   isrow0 - rows to eliminate
    625: .   iscol0 - columns to eliminate, (isrow0,iscol0) should be square and nonsingular
    626: .   isrow1 - rows in which the Schur complement is formed
    627: .   iscol1 - columns in which the Schur complement is formed
    628: .   mreuse - MAT_INITIAL_MATRIX or MAT_REUSE_MATRIX, use MAT_IGNORE_MATRIX to put nothing in S
    629: .   plump  - the type of approximation used for the inverse of the (0,0) block used in forming Sp:
    630:                        MAT_SCHUR_COMPLEMENT_AINV_DIAG or MAT_SCHUR_COMPLEMENT_AINV_LUMP
    631: -   preuse - MAT_INITIAL_MATRIX or MAT_REUSE_MATRIX, use MAT_IGNORE_MATRIX to put nothing in Sp

    633:     Output Parameters:
    634: +   S      - exact Schur complement, often of type MATSCHURCOMPLEMENT which is difficult to use for preconditioning
    635: -   Sp     - approximate Schur complement suitable for preconditioning

    637:     Note:
    638:     Since the real Schur complement is usually dense, providing a good approximation to newpmat usually requires
    639:     application-specific information.  The default for assembled matrices is to use the inverse of the diagonal of
    640:     the (0,0) block A00 in place of A00^{-1}. This rarely produce a scalable algorithm. Optionally, A00 can be lumped
    641:     before forming inv(diag(A00)).

    643:     Sometimes users would like to provide problem-specific data in the Schur complement, usually only for special row
    644:     and column index sets.  In that case, the user should call PetscObjectComposeFunction() on the *S matrix and pass mreuse of MAT_REUSE_MATRIX to set
    645:     "MatGetSchurComplement_C" to their function.  If their function needs to fall back to the default implementation, it
    646:     should call MatGetSchurComplement_Basic().

    648:     Level: advanced

    650:     Concepts: matrices^submatrices

    652: .seealso: MatGetSubMatrix(), PCFIELDSPLIT, MatCreateSchurComplement(), MatSchurComplementAinvType
    653: @*/
    654: PetscErrorCode  MatGetSchurComplement(Mat A,IS isrow0,IS iscol0,IS isrow1,IS iscol1,MatReuse mreuse,Mat *S,MatSchurComplementAinvType ainvtype,MatReuse preuse,Mat *Sp)
    655: {
    656:   PetscErrorCode ierr,(*f)(Mat,IS,IS,IS,IS,MatReuse,Mat*,MatReuse,Mat*) = NULL;

    667:   if (A->factortype) SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");
    668:   f = NULL;
    669:   if (mreuse == MAT_REUSE_MATRIX) { /* This is the only situation, in which we can demand that the user pass a non-NULL pointer to non-garbage in S. */
    670:     PetscObjectQueryFunction((PetscObject)*S,"MatGetSchurComplement_C",&f);
    671:   }
    672:   if (f) {
    673:       (*f)(A,isrow0,iscol0,isrow1,iscol1,mreuse,S,preuse,Sp);
    674:   } else {
    675:     MatGetSchurComplement_Basic(A,isrow0,iscol0,isrow1,iscol1,mreuse,S,ainvtype,preuse,Sp);
    676:   }
    677:   return(0);
    678: }

    682: /*@
    683:     MatSchurComplementSetAinvType - set the type of approximation used for the inverse of the (0,0) block used in forming Sp in MatSchurComplementGetPmat()

    685:     Not collective.

    687:     Input Parameters:
    688: +   S        - matrix obtained with MatCreateSchurComplement() (or equivalent) and implementing the action of A11 - A10 ksp(A00,Ap00) A01
    689: -   ainvtype - type of approximation used to form A00inv from A00 when assembling Sp = A11 - A10 A00inv A01:
    690:                       MAT_SCHUR_COMPLEMENT_AINV_DIAG or MAT_SCHUR_COMPLEMENT_AINV_LUMP

    692:     Options database:
    693:     -mat_schur_complement_ainv_type diag | lump

    695:     Note:
    696:     Since the real Schur complement is usually dense, providing a good approximation to newpmat usually requires
    697:     application-specific information.  The default for assembled matrices is to use the inverse of the diagonal of
    698:     the (0,0) block A00 in place of A00^{-1}. This rarely produces a scalable algorithm. Optionally, A00 can be lumped
    699:     before forming inv(diag(A00)).

    701:     Level: advanced

    703:     Concepts: matrices^submatrices

    705: .seealso: MatSchurComplementAinvType, MatCreateSchurComplement(), MatGetSchurComplement(), MatSchurComplementGetPmat(), MatSchurComplementGetAinvType()
    706: @*/
    707: PetscErrorCode  MatSchurComplementSetAinvType(Mat S,MatSchurComplementAinvType ainvtype)
    708: {
    709:   PetscErrorCode      ierr;
    710:   const char*         t;
    711:   PetscBool           isschur;
    712:   Mat_SchurComplement *schur;

    716:   PetscObjectGetType((PetscObject)S,&t);
    717:   PetscStrcmp(t,MATSCHURCOMPLEMENT,&isschur);
    718:   if (!isschur) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Expected Mat of type MATSCHURCOMPLEMENT, got %s instead",t);
    719:   schur = (Mat_SchurComplement*)S->data;
    720:   if (ainvtype != MAT_SCHUR_COMPLEMENT_AINV_DIAG && ainvtype != MAT_SCHUR_COMPLEMENT_AINV_LUMP) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Unknown MatSchurComplementAinvType: %D",ainvtype);
    721:   schur->ainvtype = ainvtype;
    722:   return(0);
    723: }

    727: /*@
    728:     MatSchurComplementGetAinvType - get the type of approximation for the inverse of the (0,0) block used in forming Sp in MatSchurComplementGetPmat()

    730:     Not collective.

    732:     Input Parameter:
    733: .   S      - matrix obtained with MatCreateSchurComplement() (or equivalent) and implementing the action of A11 - A10 ksp(A00,Ap00) A01

    735:     Output Parameter:
    736: .   ainvtype - type of approximation used to form A00inv from A00 when assembling Sp = A11 - A10 A00inv A01:
    737:                       MAT_SCHUR_COMPLEMENT_AINV_DIAG or MAT_SCHUR_COMPLEMENT_AINV_LUMP

    739:     Note:
    740:     Since the real Schur complement is usually dense, providing a good approximation to newpmat usually requires
    741:     application-specific information.  The default for assembled matrices is to use the inverse of the diagonal of
    742:     the (0,0) block A00 in place of A00^{-1}. This rarely produce a scalable algorithm. Optionally, A00 can be lumped
    743:     before forming inv(diag(A00)).

    745:     Level: advanced

    747:     Concepts: matrices^submatrices

    749: .seealso: MatSchurComplementAinvType, MatCreateSchurComplement(), MatGetSchurComplement(), MatSchurComplementGetPmat(), MatSchurComplementSetAinvType()
    750: @*/
    751: PetscErrorCode  MatSchurComplementGetAinvType(Mat S,MatSchurComplementAinvType *ainvtype)
    752: {
    753:   PetscErrorCode      ierr;
    754:   const char*         t;
    755:   PetscBool           isschur;
    756:   Mat_SchurComplement *schur;

    760:   PetscObjectGetType((PetscObject)S,&t);
    761:   PetscStrcmp(t,MATSCHURCOMPLEMENT,&isschur);
    762:   if (!isschur) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Expected Mat of type MATSCHURCOMPLEMENT, got %s instead",t);
    763:   schur = (Mat_SchurComplement*)S->data;
    764:   if (ainvtype) *ainvtype = schur->ainvtype;
    765:   return(0);
    766: }

    770: /*@
    771:     MatCreateSchurComplementPmat - create a preconditioning matrix for the Schur complement by assembling Sp = A11 - A10 inv(diag(A00)) A01

    773:     Collective on Mat

    775:     Input Parameters:
    776: +   A00,A01,A10,A11      - the four parts of the original matrix A = [A00 A01; A10 A11] (A01,A10, and A11 are optional, implying zero matrices)
    777: .   ainvtype             - type of approximation for inv(A00) used when forming Sp = A11 - A10 inv(A00) A01
    778: -   preuse               - MAT_INITIAL_MATRIX for a new Sp, or MAT_REUSE_MATRIX to reuse an existing Sp, or MAT_IGNORE_MATRIX to put nothing in Sp

    780:     Output Parameter:
    781: -   Spmat                - approximate Schur complement suitable for preconditioning S = A11 - A10 inv(diag(A00)) A01

    783:     Note:
    784:     Since the real Schur complement is usually dense, providing a good approximation to newpmat usually requires
    785:     application-specific information.  The default for assembled matrices is to use the inverse of the diagonal of
    786:     the (0,0) block A00 in place of A00^{-1}. This rarely produce a scalable algorithm. Optionally, A00 can be lumped
    787:     before forming inv(diag(A00)).

    789:     Level: advanced

    791:     Concepts: matrices^submatrices

    793: .seealso: MatCreateSchurComplement(), MatGetSchurComplement(), MatSchurComplementGetPmat(), MatSchurComplementAinvType
    794: @*/
    795: PetscErrorCode  MatCreateSchurComplementPmat(Mat A00,Mat A01,Mat A10,Mat A11,MatSchurComplementAinvType ainvtype,MatReuse preuse,Mat *Spmat)
    796: {

    799:   PetscInt       N00;

    802:   /* Use an appropriate approximate inverse of A00 to form A11 - A10 inv(diag(A00)) A01; a NULL A01, A10 or A11 indicates a zero matrix. */
    803:   /* TODO: Perhaps should create an appropriately-sized zero matrix of the same type as A00? */
    804:   if ((!A01 || !A10) & !A11) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Cannot assemble Spmat: A01, A10 and A11 are all NULL.");

    806:   if (preuse == MAT_IGNORE_MATRIX) return(0);

    808:   /* A zero size A00 or empty A01 or A10 imply S = A11. */
    809:   MatGetSize(A00,&N00,NULL);
    810:   if (!A01 || !A10 || !N00) {
    811:     if (preuse == MAT_INITIAL_MATRIX) {
    812:       MatDuplicate(A11,MAT_COPY_VALUES,Spmat);
    813:     } else { /* MAT_REUSE_MATRIX */
    814:       /* TODO: when can we pass SAME_NONZERO_PATTERN? */
    815:       MatCopy(A11,*Spmat,DIFFERENT_NONZERO_PATTERN);
    816:     }

    818:   } else {
    819:     Mat         AdB;
    820:     Vec         diag;

    822:     MatCreateVecs(A00,&diag,NULL);
    823:     if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP) {
    824:       MatGetRowSum(A00,diag);
    825:     } else if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_DIAG) {
    826:       MatGetDiagonal(A00,diag);
    827:     } else SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Unknown MatSchurComplementAinvType: %D", ainvtype);
    828:     VecReciprocal(diag);
    829:     MatDuplicate(A01,MAT_COPY_VALUES,&AdB);
    830:     MatDiagonalScale(AdB,diag,NULL);
    831:     VecDestroy(&diag);
    832:     /* Cannot really reuse Spmat in MatMatMult() because of MatAYPX() -->
    833:          MatAXPY() --> MatHeaderReplace() --> MatDestroy_XXX_MatMatMult()  */
    834:     MatDestroy(Spmat);
    835:     MatMatMult(A10,AdB,MAT_INITIAL_MATRIX,PETSC_DEFAULT,Spmat);
    836:     if (!A11) {
    837:       MatScale(*Spmat,-1.0);
    838:     } else {
    839:       /* TODO: when can we pass SAME_NONZERO_PATTERN? */
    840:       MatAYPX(*Spmat,-1,A11,DIFFERENT_NONZERO_PATTERN);
    841:     }
    842:     MatDestroy(&AdB);
    843:   }
    844:   return(0);
    845: }

    849: PetscErrorCode  MatSchurComplementGetPmat_Basic(Mat S,MatReuse preuse,Mat *Spmat)
    850: {
    851:   Mat A,B,C,D;
    852:   Mat_SchurComplement *schur = (Mat_SchurComplement *)S->data;
    853:   PetscErrorCode      ierr;

    856:   if (preuse == MAT_IGNORE_MATRIX) return(0);

    858:   MatSchurComplementGetSubMatrices(S,&A,NULL,&B,&C,&D);
    859:   if (!A) SETERRQ(PetscObjectComm((PetscObject)S),PETSC_ERR_ARG_WRONGSTATE,"Schur complement component matrices unset");
    860:   MatCreateSchurComplementPmat(A,B,C,D,schur->ainvtype,preuse,Spmat);
    861:   return(0);
    862: }

    866: /*@
    867:     MatSchurComplementGetPmat - Obtain a preconditioning matrix for the Schur complement by assembling Sp = A11 - A10 inv(diag(A00)) A01

    869:     Collective on Mat

    871:     Input Parameters:
    872: +   S      - matrix obtained with MatCreateSchurComplement() (or equivalent) and implementing the action of A11 - A10 ksp(A00,Ap00) A01
    873: -   preuse - MAT_INITIAL_MATRIX for a new Sp, or MAT_REUSE_MATRIX to reuse an existing Sp, or MAT_IGNORE_MATRIX to put nothing in Sp

    875:     Output Parameter:
    876: -   Sp     - approximate Schur complement suitable for preconditioning S = A11 - A10 inv(diag(A00)) A01

    878:     Note:
    879:     Since the real Schur complement is usually dense, providing a good approximation to newpmat usually requires
    880:     application-specific information.  The default for assembled matrices is to use the inverse of the diagonal of
    881:     the (0,0) block A00 in place of A00^{-1}. This rarely produce a scalable algorithm. Optionally, A00 can be lumped
    882:     before forming inv(diag(A00)).

    884:     Sometimes users would like to provide problem-specific data in the Schur complement, usually only
    885:     for special row and column index sets.  In that case, the user should call PetscObjectComposeFunction() to set
    886:     "MatSchurComplementGetPmat_C" to their function.  If their function needs to fall back to the default implementation,
    887:     it should call MatSchurComplementGetPmat_Basic().

    889:     Level: advanced

    891:     Concepts: matrices^submatrices

    893: .seealso: MatGetSubMatrix(), PCFIELDSPLIT, MatGetSchurComplement(), MatCreateSchurComplement(), MatSchurComplementSetAinvType()
    894: @*/
    895: PetscErrorCode  MatSchurComplementGetPmat(Mat S,MatReuse preuse,Mat *Sp)
    896: {
    897:   PetscErrorCode ierr,(*f)(Mat,MatReuse,Mat*);

    903:   if (S->factortype) SETERRQ(PetscObjectComm((PetscObject)S),PETSC_ERR_ARG_WRONGSTATE,"Not for factored matrix");

    905:   PetscObjectQueryFunction((PetscObject)S,"MatSchurComplementGetPmat_C",&f);
    906:   if (f) {
    907:     (*f)(S,preuse,Sp);
    908:   } else {
    909:     MatSchurComplementGetPmat_Basic(S,preuse,Sp);
    910:   }
    911:   return(0);
    912: }

    916: PETSC_EXTERN PetscErrorCode MatCreate_SchurComplement(Mat N)
    917: {
    918:   PetscErrorCode      ierr;
    919:   Mat_SchurComplement *Na;

    922:   PetscNewLog(N,&Na);
    923:   N->data = (void*) Na;

    925:   N->ops->destroy        = MatDestroy_SchurComplement;
    926:   N->ops->getvecs        = MatCreateVecs_SchurComplement;
    927:   N->ops->view           = MatView_SchurComplement;
    928:   N->ops->mult           = MatMult_SchurComplement;
    929:   N->ops->multtranspose  = MatMultTranspose_SchurComplement;
    930:   N->ops->multadd        = MatMultAdd_SchurComplement;
    931:   N->ops->setfromoptions = MatSetFromOptions_SchurComplement;
    932:   N->assembled           = PETSC_FALSE;
    933:   N->preallocated        = PETSC_FALSE;

    935:   KSPCreate(PetscObjectComm((PetscObject)N),&Na->ksp);
    936:   PetscObjectChangeTypeName((PetscObject)N,MATSCHURCOMPLEMENT);
    937:   return(0);
    938: }

    940: static PetscBool KSPMatRegisterAllCalled;

    944: /*@C
    945:   KSPMatRegisterAll - Registers all matrix implementations in the KSP package.

    947:   Not Collective

    949:   Level: advanced

    951: .keywords: Mat, KSP, register, all

    953: .seealso: MatRegisterAll(),  KSPInitializePackage()
    954: @*/
    955: PetscErrorCode KSPMatRegisterAll()
    956: {

    960:   if (KSPMatRegisterAllCalled) return(0);
    961:   KSPMatRegisterAllCalled = PETSC_TRUE;
    962:   MatRegister(MATSCHURCOMPLEMENT,MatCreate_SchurComplement);
    963:   return(0);
    964: }