Actual source code: schurm.c

      1: #include <../src/ksp/ksp/utils/schurm/schurm.h>

      3: const char *const MatSchurComplementAinvTypes[] = {"DIAG", "LUMP", "BLOCKDIAG", "FULL", "MatSchurComplementAinvType", "MAT_SCHUR_COMPLEMENT_AINV_", NULL};

      5: PetscErrorCode MatCreateVecs_SchurComplement(Mat N, Vec *right, Vec *left)
      6: {
      7:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

      9:   PetscFunctionBegin;
     10:   if (Na->D) {
     11:     PetscCall(MatCreateVecs(Na->D, right, left));
     12:     PetscFunctionReturn(PETSC_SUCCESS);
     13:   }
     14:   if (right) PetscCall(MatCreateVecs(Na->B, right, NULL));
     15:   if (left) PetscCall(MatCreateVecs(Na->C, NULL, left));
     16:   PetscFunctionReturn(PETSC_SUCCESS);
     17: }

     19: PetscErrorCode MatView_SchurComplement(Mat N, PetscViewer viewer)
     20: {
     21:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

     23:   PetscFunctionBegin;
     24:   PetscCall(PetscViewerASCIIPrintf(viewer, "Schur complement A11 - A10 inv(A00) A01\n"));
     25:   if (Na->D) {
     26:     PetscCall(PetscViewerASCIIPrintf(viewer, "A11\n"));
     27:     PetscCall(PetscViewerASCIIPushTab(viewer));
     28:     PetscCall(MatView(Na->D, viewer));
     29:     PetscCall(PetscViewerASCIIPopTab(viewer));
     30:   } else {
     31:     PetscCall(PetscViewerASCIIPrintf(viewer, "A11 = 0\n"));
     32:   }
     33:   PetscCall(PetscViewerASCIIPrintf(viewer, "A10\n"));
     34:   PetscCall(PetscViewerASCIIPushTab(viewer));
     35:   PetscCall(MatView(Na->C, viewer));
     36:   PetscCall(PetscViewerASCIIPopTab(viewer));
     37:   PetscCall(PetscViewerASCIIPrintf(viewer, "KSP solver for A00 block viewable with the additional option -%sksp_view\n", ((PetscObject)Na->ksp)->prefix ? ((PetscObject)Na->ksp)->prefix : NULL));
     38:   PetscCall(PetscViewerASCIIPrintf(viewer, "A01\n"));
     39:   PetscCall(PetscViewerASCIIPushTab(viewer));
     40:   PetscCall(MatView(Na->B, viewer));
     41:   PetscCall(PetscViewerASCIIPopTab(viewer));
     42:   PetscFunctionReturn(PETSC_SUCCESS);
     43: }

     45: /*
     46:            A11^T - A01^T ksptrans(A00,Ap00) A10^T
     47: */
     48: PetscErrorCode MatMultTranspose_SchurComplement(Mat N, Vec x, Vec y)
     49: {
     50:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

     52:   PetscFunctionBegin;
     53:   if (!Na->work1) PetscCall(MatCreateVecs(Na->A, &Na->work1, NULL));
     54:   if (!Na->work2) PetscCall(MatCreateVecs(Na->A, &Na->work2, NULL));
     55:   PetscCall(MatMultTranspose(Na->C, x, Na->work1));
     56:   PetscCall(KSPSolveTranspose(Na->ksp, Na->work1, Na->work2));
     57:   PetscCall(MatMultTranspose(Na->B, Na->work2, y));
     58:   PetscCall(VecScale(y, -1.0));
     59:   if (Na->D) PetscCall(MatMultTransposeAdd(Na->D, x, y, y));
     60:   PetscFunctionReturn(PETSC_SUCCESS);
     61: }

     63: /*
     64:            A11 - A10 ksp(A00,Ap00) A01
     65: */
     66: PetscErrorCode MatMult_SchurComplement(Mat N, Vec x, Vec y)
     67: {
     68:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

     70:   PetscFunctionBegin;
     71:   if (!Na->work1) PetscCall(MatCreateVecs(Na->A, &Na->work1, NULL));
     72:   if (!Na->work2) PetscCall(MatCreateVecs(Na->A, &Na->work2, NULL));
     73:   PetscCall(MatMult(Na->B, x, Na->work1));
     74:   PetscCall(KSPSolve(Na->ksp, Na->work1, Na->work2));
     75:   PetscCall(MatMult(Na->C, Na->work2, y));
     76:   PetscCall(VecScale(y, -1.0));
     77:   if (Na->D) PetscCall(MatMultAdd(Na->D, x, y, y));
     78:   PetscFunctionReturn(PETSC_SUCCESS);
     79: }

     81: /*
     82:            A11 - A10 ksp(A00,Ap00) A01
     83: */
     84: PetscErrorCode MatMultAdd_SchurComplement(Mat N, Vec x, Vec y, Vec z)
     85: {
     86:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

     88:   PetscFunctionBegin;
     89:   if (!Na->work1) PetscCall(MatCreateVecs(Na->A, &Na->work1, NULL));
     90:   if (!Na->work2) PetscCall(MatCreateVecs(Na->A, &Na->work2, NULL));
     91:   PetscCall(MatMult(Na->B, x, Na->work1));
     92:   PetscCall(KSPSolve(Na->ksp, Na->work1, Na->work2));
     93:   if (y == z) {
     94:     PetscCall(VecScale(Na->work2, -1.0));
     95:     PetscCall(MatMultAdd(Na->C, Na->work2, z, z));
     96:   } else {
     97:     PetscCall(MatMult(Na->C, Na->work2, z));
     98:     PetscCall(VecAYPX(z, -1.0, y));
     99:   }
    100:   if (Na->D) PetscCall(MatMultAdd(Na->D, x, z, z));
    101:   PetscFunctionReturn(PETSC_SUCCESS);
    102: }

    104: PetscErrorCode MatSetFromOptions_SchurComplement(Mat N, PetscOptionItems PetscOptionsObject)
    105: {
    106:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

    108:   PetscFunctionBegin;
    109:   PetscOptionsHeadBegin(PetscOptionsObject, "MatSchurComplementOptions");
    110:   Na->ainvtype = MAT_SCHUR_COMPLEMENT_AINV_DIAG;
    111:   PetscCall(PetscOptionsEnum("-mat_schur_complement_ainv_type", "Type of approximation for DIAGFORM(A00) used when assembling Sp = A11 - A10 inv(DIAGFORM(A00)) A01", "MatSchurComplementSetAinvType", MatSchurComplementAinvTypes, (PetscEnum)Na->ainvtype,
    112:                              (PetscEnum *)&Na->ainvtype, NULL));
    113:   PetscOptionsHeadEnd();
    114:   PetscCall(KSPSetFromOptions(Na->ksp));
    115:   PetscFunctionReturn(PETSC_SUCCESS);
    116: }

    118: PetscErrorCode MatDestroy_SchurComplement(Mat N)
    119: {
    120:   Mat_SchurComplement *Na = (Mat_SchurComplement *)N->data;

    122:   PetscFunctionBegin;
    123:   PetscCall(MatDestroy(&Na->A));
    124:   PetscCall(MatDestroy(&Na->Ap));
    125:   PetscCall(MatDestroy(&Na->B));
    126:   PetscCall(MatDestroy(&Na->C));
    127:   PetscCall(MatDestroy(&Na->D));
    128:   PetscCall(VecDestroy(&Na->work1));
    129:   PetscCall(VecDestroy(&Na->work2));
    130:   PetscCall(KSPDestroy(&Na->ksp));
    131:   PetscCall(PetscFree(N->data));
    132:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_seqdense_C", NULL));
    133:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_mpidense_C", NULL));
    134:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_anytype_C", NULL));
    135:   PetscFunctionReturn(PETSC_SUCCESS);
    136: }

    138: /*@
    139:   MatCreateSchurComplement - Creates a new `Mat` that behaves like the Schur complement of a matrix

    141:   Collective

    143:   Input Parameters:
    144: + A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
    145: . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A00^{-1}$
    146: . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
    147: . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
    148: - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$

    150:   Output Parameter:
    151: . S - the matrix that behaves as the Schur complement $S = A11 - A10 ksp(A00,Ap00) A01$

    153:   Level: intermediate

    155:   Notes:
    156:   The Schur complement is NOT explicitly formed! Rather, this function returns a virtual Schur complement
    157:   that can compute the matrix-vector product by using formula $S = A11 - A10 A^{-1} A01$
    158:   for Schur complement `S` and a `KSP` solver to approximate the action of $A^{-1}$.

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

    162:   `A00` and  `A11` must be square matrices.

    164:   `MatGetSchurComplement()` takes as arguments the index sets for the submatrices and returns both the virtual Schur complement (what this returns) plus
    165:   a sparse approximation to the Schur complement (useful for building a preconditioner for the Schur complement) which can be obtained from this
    166:   matrix with `MatSchurComplementGetPmat()`

    168:   Developer Notes:
    169:   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
    170:   remove redundancy and be clearer and simpler.

    172: .seealso: [](ch_ksp), `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatSchurComplementUpdateSubMatrices()`, `MatCreateTranspose()`, `MatGetSchurComplement()`,
    173:           `MatSchurComplementGetPmat()`, `MatSchurComplementSetSubMatrices()`
    174: @*/
    175: PetscErrorCode MatCreateSchurComplement(Mat A00, Mat Ap00, Mat A01, Mat A10, Mat A11, Mat *S)
    176: {
    177:   PetscFunctionBegin;
    178:   PetscCall(KSPInitializePackage());
    179:   PetscCall(MatCreate(PetscObjectComm((PetscObject)A00), S));
    180:   PetscCall(MatSetType(*S, MATSCHURCOMPLEMENT));
    181:   PetscCall(MatSchurComplementSetSubMatrices(*S, A00, Ap00, A01, A10, A11));
    182:   PetscFunctionReturn(PETSC_SUCCESS);
    183: }

    185: /*@
    186:   MatSchurComplementSetSubMatrices - Sets the matrices that define the Schur complement

    188:   Collective

    190:   Input Parameters:
    191: + S    - matrix obtained with `MatSetType`(S,`MATSCHURCOMPLEMENT`)
    192: . A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
    193: . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A00^{-1}$
    194: . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
    195: . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
    196: - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$

    198:   Level: intermediate

    200:   Notes:
    201:   The Schur complement is NOT explicitly formed! Rather, this
    202:   object performs the matrix-vector product of the Schur complement by using formula $S = A11 - A10 ksp(A00,Ap00) A01$

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

    206:   `A00` and `A11` must be square matrices.

    208:   This is to be used in the context of code such as
    209: .vb
    210:      MatSetType(S,MATSCHURCOMPLEMENT);
    211:      MatSchurComplementSetSubMatrices(S,...);
    212: .ve
    213:   while `MatSchurComplementUpdateSubMatrices()` should only be called after `MatCreateSchurComplement()` or `MatSchurComplementSetSubMatrices()`

    215: .seealso: [](ch_ksp), `Mat`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatSchurComplementUpdateSubMatrices()`, `MatCreateTranspose()`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`
    216: @*/
    217: PetscErrorCode MatSchurComplementSetSubMatrices(Mat S, Mat A00, Mat Ap00, Mat A01, Mat A10, Mat A11)
    218: {
    219:   Mat_SchurComplement *Na = (Mat_SchurComplement *)S->data;
    220:   PetscBool            isschur;

    222:   PetscFunctionBegin;
    223:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
    224:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
    225:   PetscCheck(!S->assembled, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Use MatSchurComplementUpdateSubMatrices() for already used matrix");
    230:   PetscCheckSameComm(A00, 2, Ap00, 3);
    231:   PetscCheckSameComm(A00, 2, A01, 4);
    232:   PetscCheckSameComm(A00, 2, A10, 5);
    233:   PetscCheck(A00->rmap->n == A00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, A00->rmap->n, A00->cmap->n);
    234:   PetscCheck(A00->rmap->n == Ap00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local rows of Ap00 %" PetscInt_FMT, A00->rmap->n, Ap00->rmap->n);
    235:   PetscCheck(Ap00->rmap->n == Ap00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of Ap00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, Ap00->rmap->n, Ap00->cmap->n);
    236:   PetscCheck(A00->cmap->n == A01->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A00 %" PetscInt_FMT " do not equal local rows of A01 %" PetscInt_FMT, A00->cmap->n, A01->rmap->n);
    237:   PetscCheck(A10->cmap->n == A00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A10 %" PetscInt_FMT " do not equal local rows of A00 %" PetscInt_FMT, A10->cmap->n, A00->rmap->n);
    238:   if (A11) {
    240:     PetscCheckSameComm(A00, 2, A11, 6);
    241:     PetscCheck(A10->rmap->n == A11->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A10 %" PetscInt_FMT " do not equal local rows A11 %" PetscInt_FMT, A10->rmap->n, A11->rmap->n);
    242:   }

    244:   PetscCall(MatSetSizes(S, A10->rmap->n, A01->cmap->n, A10->rmap->N, A01->cmap->N));
    245:   PetscCall(PetscObjectReference((PetscObject)A00));
    246:   PetscCall(PetscObjectReference((PetscObject)Ap00));
    247:   PetscCall(PetscObjectReference((PetscObject)A01));
    248:   PetscCall(PetscObjectReference((PetscObject)A10));
    249:   PetscCall(PetscObjectReference((PetscObject)A11));
    250:   Na->A  = A00;
    251:   Na->Ap = Ap00;
    252:   Na->B  = A01;
    253:   Na->C  = A10;
    254:   Na->D  = A11;
    255:   PetscCall(MatSetUp(S));
    256:   PetscCall(KSPSetOperators(Na->ksp, A00, Ap00));
    257:   S->assembled = PETSC_TRUE;
    258:   PetscFunctionReturn(PETSC_SUCCESS);
    259: }

    261: /*@
    262:   MatSchurComplementGetKSP - Gets the `KSP` object that is used to solve with `A00` in the Schur complement matrix $S = A11 - A10 ksp(A00,Ap00) A01$

    264:   Not Collective

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

    269:   Output Parameter:
    270: . ksp - the linear solver object

    272:   Options Database Key:
    273: . -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.

    275:   Level: intermediate

    277: .seealso: [](ch_ksp), `Mat`, `MatSchurComplementSetKSP()`, `MatCreateSchurComplement()`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`
    278: @*/
    279: PetscErrorCode MatSchurComplementGetKSP(Mat S, KSP *ksp)
    280: {
    281:   Mat_SchurComplement *Na;
    282:   PetscBool            isschur;

    284:   PetscFunctionBegin;
    286:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
    287:   PetscCheck(isschur, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)S)->type_name);
    288:   PetscAssertPointer(ksp, 2);
    289:   Na   = (Mat_SchurComplement *)S->data;
    290:   *ksp = Na->ksp;
    291:   PetscFunctionReturn(PETSC_SUCCESS);
    292: }

    294: /*@
    295:   MatSchurComplementSetKSP - Sets the `KSP` object that is used to solve with `A00` in the Schur complement matrix $ S = A11 - A10 ksp(A00,Ap00) A01$

    297:   Not Collective

    299:   Input Parameters:
    300: + S   - matrix created with `MatCreateSchurComplement()`
    301: - ksp - the linear solver object

    303:   Level: developer

    305:   Developer Notes:
    306:   This is used in `PCFIELDSPLIT` to reuse the 0-split `KSP` to implement $ksp(A00,Ap00)$ in `S`.
    307:   The `KSP` operators are overwritten with `A00` and `Ap00` currently set in `S`.

    309: .seealso: [](ch_ksp), `Mat`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MATSCHURCOMPLEMENT`
    310: @*/
    311: PetscErrorCode MatSchurComplementSetKSP(Mat S, KSP ksp)
    312: {
    313:   Mat_SchurComplement *Na;
    314:   PetscBool            isschur;

    316:   PetscFunctionBegin;
    318:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
    319:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
    321:   Na = (Mat_SchurComplement *)S->data;
    322:   PetscCall(PetscObjectReference((PetscObject)ksp));
    323:   PetscCall(KSPDestroy(&Na->ksp));
    324:   Na->ksp = ksp;
    325:   PetscCall(KSPSetOperators(Na->ksp, Na->A, Na->Ap));
    326:   PetscFunctionReturn(PETSC_SUCCESS);
    327: }

    329: /*@
    330:   MatSchurComplementUpdateSubMatrices - Updates the Schur complement matrix object with new submatrices

    332:   Collective

    334:   Input Parameters:
    335: + S    - matrix obtained with `MatCreateSchurComplement()` (or `MatSchurSetSubMatrices()`) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$
    336: . A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
    337: . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A00^{-1}$
    338: . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
    339: . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
    340: - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$

    342:   Level: intermediate

    344:   Notes:
    345:   All four matrices must have the same MPI communicator

    347:   `A00` and  `A11` must be square matrices

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

    352:   This can only be called after `MatCreateSchurComplement()` or `MatSchurComplementSetSubMatrices()`, it cannot be called immediately after `MatSetType`(S,`MATSCHURCOMPLEMENT`);

    354:   Developer Notes:
    355:   This code is almost identical to `MatSchurComplementSetSubMatrices()`. The API should be refactored.

    357: .seealso: [](ch_ksp), `Mat`, `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`
    358: @*/
    359: PetscErrorCode MatSchurComplementUpdateSubMatrices(Mat S, Mat A00, Mat Ap00, Mat A01, Mat A10, Mat A11)
    360: {
    361:   Mat_SchurComplement *Na = (Mat_SchurComplement *)S->data;
    362:   PetscBool            isschur;

    364:   PetscFunctionBegin;
    366:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
    367:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
    368:   PetscCheck(S->assembled, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Use MatSchurComplementSetSubMatrices() for a new matrix");
    373:   PetscCheckSameComm(A00, 2, Ap00, 3);
    374:   PetscCheckSameComm(A00, 2, A01, 4);
    375:   PetscCheckSameComm(A00, 2, A10, 5);
    376:   PetscCheck(A00->rmap->n == A00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, A00->rmap->n, A00->cmap->n);
    377:   PetscCheck(A00->rmap->n == Ap00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A00 %" PetscInt_FMT " do not equal local rows of Ap00 %" PetscInt_FMT, A00->rmap->n, Ap00->rmap->n);
    378:   PetscCheck(Ap00->rmap->n == Ap00->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of Ap00 %" PetscInt_FMT " do not equal local columns %" PetscInt_FMT, Ap00->rmap->n, Ap00->cmap->n);
    379:   PetscCheck(A00->cmap->n == A01->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A00 %" PetscInt_FMT " do not equal local rows of A01 %" PetscInt_FMT, A00->cmap->n, A01->rmap->n);
    380:   PetscCheck(A10->cmap->n == A00->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local columns of A10 %" PetscInt_FMT " do not equal local rows of A00 %" PetscInt_FMT, A10->cmap->n, A00->rmap->n);
    381:   if (A11) {
    383:     PetscCheckSameComm(A00, 2, A11, 6);
    384:     PetscCheck(A10->rmap->n == A11->rmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Local rows of A10 %" PetscInt_FMT " do not equal local rows A11 %" PetscInt_FMT, A10->rmap->n, A11->rmap->n);
    385:   }

    387:   PetscCall(PetscObjectReference((PetscObject)A00));
    388:   PetscCall(PetscObjectReference((PetscObject)Ap00));
    389:   PetscCall(PetscObjectReference((PetscObject)A01));
    390:   PetscCall(PetscObjectReference((PetscObject)A10));
    391:   if (A11) PetscCall(PetscObjectReference((PetscObject)A11));

    393:   PetscCall(MatDestroy(&Na->A));
    394:   PetscCall(MatDestroy(&Na->Ap));
    395:   PetscCall(MatDestroy(&Na->B));
    396:   PetscCall(MatDestroy(&Na->C));
    397:   PetscCall(MatDestroy(&Na->D));

    399:   Na->A  = A00;
    400:   Na->Ap = Ap00;
    401:   Na->B  = A01;
    402:   Na->C  = A10;
    403:   Na->D  = A11;

    405:   PetscCall(KSPSetOperators(Na->ksp, A00, Ap00));
    406:   PetscFunctionReturn(PETSC_SUCCESS);
    407: }

    409: /*@
    410:   MatSchurComplementGetSubMatrices - Get the individual submatrices in the Schur complement

    412:   Collective

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

    417:   Output Parameters:
    418: + A00  - the upper-left block of the original matrix $A = [A00 A01; A10 A11]$
    419: . Ap00 - matrix from which the preconditioner is constructed for use in $ksp(A00,Ap00)$ to approximate the action of $A^{-1}$
    420: . A01  - the upper-right block of the original matrix $A = [A00 A01; A10 A11]$
    421: . A10  - the lower-left block of the original matrix $A = [A00 A01; A10 A11]$
    422: - A11  - (optional) the lower-right block of the original matrix $A = [A00 A01; A10 A11]$

    424:   Level: intermediate

    426:   Note:
    427:   Use `NULL` for any unneeded output argument.

    429:   The reference counts of the submatrices are not increased before they are returned and the matrices should not be modified or destroyed.

    431: .seealso: [](ch_ksp), `MatCreateNormal()`, `MatMult()`, `MatCreate()`, `MatSchurComplementGetKSP()`, `MatCreateSchurComplement()`, `MatSchurComplementUpdateSubMatrices()`
    432: @*/
    433: PetscErrorCode MatSchurComplementGetSubMatrices(Mat S, Mat *A00, Mat *Ap00, Mat *A01, Mat *A10, Mat *A11)
    434: {
    435:   Mat_SchurComplement *Na = (Mat_SchurComplement *)S->data;
    436:   PetscBool            flg;

    438:   PetscFunctionBegin;
    440:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &flg));
    441:   PetscCheck(flg, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)S)->type_name);
    442:   if (A00) *A00 = Na->A;
    443:   if (Ap00) *Ap00 = Na->Ap;
    444:   if (A01) *A01 = Na->B;
    445:   if (A10) *A10 = Na->C;
    446:   if (A11) *A11 = Na->D;
    447:   PetscFunctionReturn(PETSC_SUCCESS);
    448: }

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

    452: /*@
    453:   MatSchurComplementComputeExplicitOperator - Compute the Schur complement matrix explicitly

    455:   Collective

    457:   Input Parameter:
    458: . A - the matrix obtained with `MatCreateSchurComplement()`

    460:   Output Parameter:
    461: . S - the Schur complement matrix

    463:   Level: advanced

    465:   Notes:
    466:   This can be expensive when `S` is large, so it is mainly for testing

    468:   Use `MatSchurComplementGetPmat()` to get a sparse approximation for the Schur complement suitable for use in building a preconditioner

    470:   `S` will automatically have the same prefix as `A` appended by `explicit_operator_`,
    471:   there are three options available: `-fieldsplit_1_explicit_operator_mat_type`,
    472:   `-fieldsplit_1_explicit_operator_mat_symmetric`, and `-fieldsplit_1_explicit_operator_mat_hermitian`

    474:   Developer Note:
    475:   The three aforementioned should not be parsed and used in this routine, but rather in `MatSetFromOptions()`

    477: .seealso: [](ch_ksp), `MatCreateSchurComplement()`, `MatSchurComplementUpdateSubMatrices()`, `MatSchurComplementGetPmat()`
    478: @*/
    479: PetscErrorCode MatSchurComplementComputeExplicitOperator(Mat A, Mat *S)
    480: {
    481:   Mat       P, B, C, D, E = NULL, Bd, AinvBd, sub = NULL;
    482:   MatType   mtype;
    483:   VecType   vtype;
    484:   KSP       ksp;
    485:   PetscInt  n, N, m, M;
    486:   PetscBool flg = PETSC_FALSE, set, symm;
    487:   char      prefix[256], type[256];

    489:   PetscFunctionBegin;
    490:   PetscAssertPointer(S, 2);
    492:   PetscCall(PetscObjectQuery((PetscObject)A, "AinvB", (PetscObject *)&AinvBd));
    493:   set = (PetscBool)(AinvBd != NULL);
    494:   if (set && AinvBd->cmap->N == -1) PetscFunctionReturn(PETSC_SUCCESS); // early bail out if composed Mat is uninitialized
    495:   PetscCall(MatSchurComplementGetSubMatrices(A, &P, NULL, &B, &C, &D));
    496:   PetscCall(MatGetVecType(B, &vtype));
    497:   PetscCall(MatGetLocalSize(B, &m, &n));
    498:   PetscCall(MatSchurComplementGetKSP(A, &ksp));
    499:   PetscCall(KSPSetUp(ksp));
    500:   if (set) {
    501:     PetscCheck(AinvBd->cmap->N >= A->cmap->N, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_SIZ, "Composed Mat should have at least as many columns as the Schur complement (%" PetscInt_FMT " >= %" PetscInt_FMT ")", AinvBd->cmap->N, A->cmap->N);
    502:     PetscCall(MatGetType(AinvBd, &mtype));
    503:     if (AinvBd->cmap->N > A->cmap->N) {
    504:       Mat s[2];

    506:       PetscCall(MatDuplicate(AinvBd, MAT_DO_NOT_COPY_VALUES, &Bd));
    507:       PetscCall(MatDenseGetSubMatrix(Bd, PETSC_DECIDE, PETSC_DECIDE, A->cmap->N, AinvBd->cmap->N, s));
    508:       PetscCall(MatDenseGetSubMatrix(AinvBd, PETSC_DECIDE, PETSC_DECIDE, A->cmap->N, AinvBd->cmap->N, s + 1));
    509:       PetscCall(MatCopy(s[1], s[0], SAME_NONZERO_PATTERN)); // copy the last columns of the composed Mat, which are likely the input columns of PCApply_FieldSplit_Schur()
    510:       PetscCall(MatDenseRestoreSubMatrix(AinvBd, s + 1));
    511:       PetscCall(MatDenseRestoreSubMatrix(Bd, s));
    512:       PetscCall(MatDenseGetSubMatrix(Bd, PETSC_DECIDE, PETSC_DECIDE, 0, A->cmap->N, &sub));
    513:       PetscCall(MatConvert(B, mtype, MAT_REUSE_MATRIX, &sub)); // copy A01 into the first columns of the block of RHS of KSPMatSolve()
    514:       PetscCall(MatDenseRestoreSubMatrix(Bd, &sub));
    515:     } else PetscCall(MatConvert(B, mtype, MAT_INITIAL_MATRIX, &Bd));
    516:   } else {
    517:     PetscCall(MatGetSize(B, &M, &N));
    518:     PetscCall(MatCreateDenseFromVecType(PetscObjectComm((PetscObject)A), vtype, m, n, M, N, -1, NULL, &AinvBd));
    519:     PetscCall(MatGetType(AinvBd, &mtype));
    520:     PetscCall(MatConvert(B, mtype, MAT_INITIAL_MATRIX, &Bd));
    521:   }
    522:   PetscCall(KSPMatSolve(ksp, Bd, AinvBd));
    523:   if (set && AinvBd->cmap->N > A->cmap->N) {
    524:     Mat          AinvB;
    525:     PetscScalar *v;
    526:     PetscBool    match;

    528:     PetscCall(PetscObjectTypeCompareAny((PetscObject)AinvBd, &match, MATSEQDENSECUDA, MATMPIDENSECUDA, ""));
    529:     if (match) {
    530: #if PetscDefined(HAVE_CUDA)
    531:       PetscCall(MatDenseCUDAGetArrayWrite(AinvBd, &v));
    532:       PetscCall(MatCreateDenseCUDA(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
    533:       PetscCall(MatDenseCUDAReplaceArray(AinvB, v));
    534:       PetscCall(MatDenseCUDARestoreArrayWrite(AinvBd, &v));
    535: #endif
    536:     } else {
    537:       PetscCall(PetscObjectTypeCompareAny((PetscObject)AinvBd, &match, MATSEQDENSEHIP, MATMPIDENSEHIP, ""));
    538:       if (match) {
    539: #if PetscDefined(HAVE_HIP)
    540:         PetscCall(MatDenseHIPGetArrayWrite(AinvBd, &v));
    541:         PetscCall(MatCreateDenseHIP(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
    542:         PetscCall(MatDenseHIPReplaceArray(AinvB, v));
    543:         PetscCall(MatDenseHIPRestoreArrayWrite(AinvBd, &v));
    544: #endif
    545:       } else {
    546:         PetscCall(MatDenseGetArrayWrite(AinvBd, &v)); // no easy way to resize a Mat, so create a new one with the same data pointer
    547:         PetscCall(MatCreateDense(PetscObjectComm((PetscObject)A), AinvBd->rmap->n, A->cmap->n, AinvBd->rmap->N, A->cmap->N, v, &AinvB));
    548:         PetscCall(MatDenseReplaceArray(AinvB, v)); // let MatDestroy() free the data pointer
    549:         PetscCall(MatDenseRestoreArrayWrite(AinvBd, &v));
    550:       }
    551:     }
    552:     PetscCall(MatHeaderReplace(AinvBd, &AinvB)); // replace the input composed Mat with just A00^-1 A01 (trailing columns are removed)
    553:   }
    554:   PetscCall(MatDestroy(&Bd));
    555:   if (!set) PetscCall(MatFilter(AinvBd, PETSC_SMALL, PETSC_FALSE, PETSC_FALSE));
    556:   if (D && !*S) {
    557:     PetscCall(MatGetLocalSize(D, &m, &n));
    558:     PetscCall(MatGetSize(D, &M, &N));
    559:     PetscCall(MatCreateDenseFromVecType(PetscObjectComm((PetscObject)A), vtype, m, n, M, N, -1, NULL, S));
    560:   } else if (*S) {
    561:     PetscCall(MatGetType(AinvBd, &mtype));
    562:     PetscCall(MatSetType(*S, mtype));
    563:   }
    564:   PetscCall(MatMatMult(C, AinvBd, *S ? MAT_REUSE_MATRIX : MAT_INITIAL_MATRIX, PETSC_DETERMINE, S));
    565:   if (!set) PetscCall(MatDestroy(&AinvBd));
    566:   else {
    567:     PetscCall(MatScale(AinvBd, -1.0));
    568:     PetscCall(MatFilter(AinvBd, PETSC_MACHINE_EPSILON, PETSC_FALSE, PETSC_FALSE));
    569:     PetscCall(MatFilter(*S, PETSC_MACHINE_EPSILON, PETSC_FALSE, PETSC_FALSE));
    570:   }
    571:   if (D) {
    572:     PetscCall(PetscObjectTypeCompareAny((PetscObject)D, &flg, MATSEQSBAIJ, MATMPISBAIJ, ""));
    573:     if (flg) {
    574:       PetscCall(MatIsSymmetricKnown(A, &set, &symm));
    575:       if (!set || !symm) PetscCall(MatConvert(D, MATBAIJ, MAT_INITIAL_MATRIX, &E)); /* convert the (1,1) block to nonsymmetric storage for MatAXPY() */
    576:     }
    577:     PetscCall(MatAXPY(*S, -1.0, E ? E : D, DIFFERENT_NONZERO_PATTERN));        /* calls Mat[Get|Restore]RowUpperTriangular(), so only the upper triangular part is valid with symmetric storage */
    578:     if (!E && flg) PetscCall(MatConvert(*S, MATSBAIJ, MAT_INPLACE_MATRIX, S)); /* if A is symmetric and the (1,1) block is a MatSBAIJ, return S as a MatSBAIJ since the lower triangular part is invalid */
    579:   }
    580:   PetscCall(MatDestroy(&E));
    581:   PetscCall(MatScale(*S, -1.0));
    582:   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%sexplicit_operator_", ((PetscObject)A)->prefix ? ((PetscObject)A)->prefix : ""));
    583:   PetscCall(MatSetOptionsPrefix(*S, prefix));
    584:   PetscObjectOptionsBegin((PetscObject)*S);
    585:   PetscCall(PetscOptionsFList("-mat_type", "Matrix type", "MatSetType", MatList, !E && flg ? MATSBAIJ : mtype, type, 256, &set));
    586:   if (set) PetscCall(MatConvert(*S, type, MAT_INPLACE_MATRIX, S));
    587:   flg = PETSC_FALSE;
    588:   PetscCall(PetscOptionsBool("-mat_symmetric", "Sets the MAT_SYMMETRIC option", "MatSetOption", flg, &flg, &set));
    589:   if (set) PetscCall(MatSetOption(*S, MAT_SYMMETRIC, flg));
    590:   if (PetscDefined(USE_COMPLEX)) {
    591:     flg = PETSC_FALSE;
    592:     PetscCall(PetscOptionsBool("-mat_hermitian", "Sets the MAT_HERMITIAN option", "MatSetOption", flg, &flg, &set));
    593:     if (set) PetscCall(MatSetOption(*S, MAT_HERMITIAN, flg));
    594:   }
    595:   PetscOptionsEnd();
    596:   PetscFunctionReturn(PETSC_SUCCESS);
    597: }

    599: /* Developer Notes:
    600:     This should be implemented with a MatCreate_SchurComplement() as that is the standard design for new Mat classes. */
    601: PetscErrorCode MatGetSchurComplement_Basic(Mat mat, IS isrow0, IS iscol0, IS isrow1, IS iscol1, MatReuse mreuse, Mat *S, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
    602: {
    603:   Mat      A = NULL, Ap = NULL, B = NULL, C = NULL, D = NULL;
    604:   MatReuse reuse;

    606:   PetscFunctionBegin;
    616:   if (mreuse == MAT_IGNORE_MATRIX && preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);

    620:   PetscCheck(!mat->factortype, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");

    622:   reuse = MAT_INITIAL_MATRIX;
    623:   if (mreuse == MAT_REUSE_MATRIX) {
    624:     PetscCall(MatSchurComplementGetSubMatrices(*S, &A, &Ap, &B, &C, &D));
    625:     PetscCheck(A && Ap && B && C, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Attempting to reuse matrix but Schur complement matrices unset");
    626:     PetscCheck(A == Ap, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Matrix for constructing the preconditioner does not match operator");
    627:     PetscCall(MatDestroy(&Ap)); /* get rid of extra reference */
    628:     reuse = MAT_REUSE_MATRIX;
    629:   }
    630:   PetscCall(MatCreateSubMatrix(mat, isrow0, iscol0, reuse, &A));
    631:   PetscCall(MatCreateSubMatrix(mat, isrow0, iscol1, reuse, &B));
    632:   PetscCall(MatCreateSubMatrix(mat, isrow1, iscol0, reuse, &C));
    633:   PetscCall(MatCreateSubMatrix(mat, isrow1, iscol1, reuse, &D));
    634:   switch (mreuse) {
    635:   case MAT_INITIAL_MATRIX:
    636:     PetscCall(MatCreateSchurComplement(A, A, B, C, D, S));
    637:     break;
    638:   case MAT_REUSE_MATRIX:
    639:     PetscCall(MatSchurComplementUpdateSubMatrices(*S, A, A, B, C, D));
    640:     break;
    641:   default:
    642:     PetscCheck(mreuse == MAT_IGNORE_MATRIX, PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Unrecognized value of mreuse %d", (int)mreuse);
    643:   }
    644:   if (preuse != MAT_IGNORE_MATRIX) PetscCall(MatCreateSchurComplementPmat(A, B, C, D, ainvtype, preuse, Sp));
    645:   PetscCall(MatDestroy(&A));
    646:   PetscCall(MatDestroy(&B));
    647:   PetscCall(MatDestroy(&C));
    648:   PetscCall(MatDestroy(&D));
    649:   PetscFunctionReturn(PETSC_SUCCESS);
    650: }

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

    655:   Collective

    657:   Input Parameters:
    658: + A        - matrix in which the complement is to be taken
    659: . isrow0   - rows to eliminate
    660: . iscol0   - columns to eliminate, (isrow0,iscol0) should be square and nonsingular
    661: . isrow1   - rows in which the Schur complement is formed
    662: . iscol1   - columns in which the Schur complement is formed
    663: . mreuse   - `MAT_INITIAL_MATRIX` or `MAT_REUSE_MATRIX`, use `MAT_IGNORE_MATRIX` to put nothing in `S`
    664: . ainvtype - the type of approximation used for the inverse of the (0,0) block used in forming `Sp`:
    665:              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
    666: - preuse   - `MAT_INITIAL_MATRIX` or `MAT_REUSE_MATRIX`, use `MAT_IGNORE_MATRIX` to put nothing in `Sp`

    668:   Output Parameters:
    669: + S  - exact Schur complement, often of type `MATSCHURCOMPLEMENT` which is difficult to use for preconditioning
    670: - Sp - approximate Schur complement from which a preconditioner can be built $A11 - A10 inv(DIAGFORM(A00)) A01$

    672:   Level: advanced

    674:   Notes:
    675:   Since the real Schur complement is usually dense, providing a good approximation to `Sp` usually requires
    676:   application-specific information.

    678:   Sometimes users would like to provide problem-specific data in the Schur complement, usually only for special row
    679:   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
    680:   "MatGetSchurComplement_C" to their function.  If their function needs to fall back to the default implementation, it
    681:   should call `MatGetSchurComplement_Basic()`.

    683:   `MatCreateSchurComplement()` takes as arguments the four submatrices and returns the virtual Schur complement (what this function returns in S).

    685:   `MatSchurComplementGetPmat()` takes the virtual Schur complement and returns an explicit approximate Schur complement (what this returns in Sp).

    687:   In other words calling `MatCreateSchurComplement()` followed by `MatSchurComplementGetPmat()` produces the same output as this function but with slightly different
    688:   inputs. The actually submatrices of the original block matrix instead of index sets to the submatrices.

    690:   Developer Notes:
    691:   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
    692:   remove redundancy and be clearer and simpler.

    694: .seealso: [](ch_ksp), `MatCreateSubMatrix()`, `PCFIELDSPLIT`, `MatCreateSchurComplement()`, `MatSchurComplementAinvType`
    695: @*/
    696: PetscErrorCode MatGetSchurComplement(Mat A, IS isrow0, IS iscol0, IS isrow1, IS iscol1, MatReuse mreuse, Mat *S, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
    697: {
    698:   PetscErrorCode (*f)(Mat, IS, IS, IS, IS, MatReuse, Mat *, MatReuse, Mat *) = NULL;

    700:   PetscFunctionBegin;
    712:   PetscCheck(!A->factortype, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");
    713:   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. */
    714:     PetscCall(PetscObjectQueryFunction((PetscObject)*S, "MatGetSchurComplement_C", &f));
    715:   }
    716:   if (f) PetscCall((*f)(A, isrow0, iscol0, isrow1, iscol1, mreuse, S, preuse, Sp));
    717:   else PetscCall(MatGetSchurComplement_Basic(A, isrow0, iscol0, isrow1, iscol1, mreuse, S, ainvtype, preuse, Sp));
    718:   PetscFunctionReturn(PETSC_SUCCESS);
    719: }

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

    724:   Not Collective

    726:   Input Parameters:
    727: + S        - matrix obtained with `MatCreateSchurComplement()` (or equivalent) and implementing the action of $A11 - A10 ksp(A00,Ap00) A01$
    728: - ainvtype - type of approximation to be used to form approximate Schur complement $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$:
    729:              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`

    731:   Options Database Key:
    732: . -mat_schur_complement_ainv_type diag | lump | blockdiag | full - set schur complement type

    734:   Level: advanced

    736: .seealso: [](ch_ksp), `MatSchurComplementAinvType`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementGetAinvType()`
    737: @*/
    738: PetscErrorCode MatSchurComplementSetAinvType(Mat S, MatSchurComplementAinvType ainvtype)
    739: {
    740:   PetscBool            isschur;
    741:   Mat_SchurComplement *schur;

    743:   PetscFunctionBegin;
    745:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
    746:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);
    748:   schur = (Mat_SchurComplement *)S->data;
    749:   PetscCheck(ainvtype == MAT_SCHUR_COMPLEMENT_AINV_DIAG || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_FULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown MatSchurComplementAinvType: %d", (int)ainvtype);
    750:   schur->ainvtype = ainvtype;
    751:   PetscFunctionReturn(PETSC_SUCCESS);
    752: }

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

    757:   Not Collective

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

    762:   Output Parameter:
    763: . ainvtype - type of approximation used to form approximate Schur complement Sp = A11 - A10 inv(DIAGFORM(A00)) A01:
    764:              `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`

    766:   Level: advanced

    768: .seealso: [](ch_ksp), `MatSchurComplementAinvType`, `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementSetAinvType()`
    769: @*/
    770: PetscErrorCode MatSchurComplementGetAinvType(Mat S, MatSchurComplementAinvType *ainvtype)
    771: {
    772:   PetscBool            isschur;
    773:   Mat_SchurComplement *schur;

    775:   PetscFunctionBegin;
    777:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
    778:   PetscCheck(isschur, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONG, "Not for type %s", ((PetscObject)S)->type_name);
    779:   schur = (Mat_SchurComplement *)S->data;
    780:   if (ainvtype) *ainvtype = schur->ainvtype;
    781:   PetscFunctionReturn(PETSC_SUCCESS);
    782: }

    784: /*@
    785:   MatCreateSchurComplementPmat - create a matrix for preconditioning the Schur complement by explicitly assembling the sparse matrix
    786:   $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$

    788:   Collective

    790:   Input Parameters:
    791: + A00      - the upper-left part of the original matrix $A = [A00 A01; A10 A11]$
    792: . A01      - (optional) the upper-right part of the original matrix $A = [A00 A01; A10 A11]$
    793: . A10      - (optional) the lower-left part of the original matrix $A = [A00 A01; A10 A11]$
    794: . A11      - (optional) the lower-right part of the original matrix $A = [A00 A01; A10 A11]$
    795: . ainvtype - type of approximation for DIAGFORM(A00) used when forming $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$. See `MatSchurComplementAinvType`.
    796: - 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`

    798:   Output Parameter:
    799: . Sp - approximate Schur complement suitable for constructing a preconditioner for the true Schur complement $S = A11 - A10 inv(A00) A01$

    801:   Level: advanced

    803: .seealso: [](ch_ksp), `MatCreateSchurComplement()`, `MatGetSchurComplement()`, `MatSchurComplementGetPmat()`, `MatSchurComplementAinvType`
    804: @*/
    805: PetscErrorCode MatCreateSchurComplementPmat(Mat A00, Mat A01, Mat A10, Mat A11, MatSchurComplementAinvType ainvtype, MatReuse preuse, Mat *Sp)
    806: {
    807:   PetscInt N00;

    809:   PetscFunctionBegin;
    810:   /* Use an appropriate approximate inverse of A00 to form A11 - A10 inv(DIAGFORM(A00)) A01; a NULL A01, A10 or A11 indicates a zero matrix. */
    811:   /* TODO: Perhaps should create an appropriately-sized zero matrix of the same type as A00? */
    813:   if (preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);

    815:   /* A zero size A00 or empty A01 or A10 imply S = A11. */
    816:   PetscCall(MatGetSize(A00, &N00, NULL));
    817:   if (!A01 || !A10 || !N00) {
    818:     if (preuse == MAT_INITIAL_MATRIX) {
    819:       PetscCall(MatDuplicate(A11, MAT_COPY_VALUES, Sp));
    820:     } else { /* MAT_REUSE_MATRIX */
    821:       /* TODO: when can we pass SAME_NONZERO_PATTERN? */
    822:       PetscCall(MatCopy(A11, *Sp, DIFFERENT_NONZERO_PATTERN));
    823:     }
    824:   } else {
    825:     Mat       AdB, T;
    826:     Vec       diag;
    827:     PetscBool flg;

    829:     if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP || ainvtype == MAT_SCHUR_COMPLEMENT_AINV_DIAG) {
    830:       PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATTRANSPOSEVIRTUAL, &flg));
    831:       if (flg) {
    832:         PetscCall(MatTransposeGetMat(A01, &T));
    833:         PetscCall(MatTranspose(T, MAT_INITIAL_MATRIX, &AdB));
    834:       } else {
    835:         PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATHERMITIANTRANSPOSEVIRTUAL, &flg));
    836:         if (flg) {
    837:           PetscCall(MatHermitianTransposeGetMat(A01, &T));
    838:           PetscCall(MatHermitianTranspose(T, MAT_INITIAL_MATRIX, &AdB));
    839:         }
    840:       }
    841:       if (!flg) PetscCall(MatDuplicate(A01, MAT_COPY_VALUES, &AdB));
    842:       else {
    843:         PetscScalar shift, scale;

    845:         PetscCall(MatShellGetScalingShifts(A01, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
    846:         PetscCall(MatShift(AdB, shift));
    847:         PetscCall(MatScale(AdB, scale));
    848:       }
    849:       PetscCall(MatCreateVecs(A00, &diag, NULL));
    850:       if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_LUMP) {
    851:         PetscCall(MatGetRowSum(A00, diag));
    852:       } else {
    853:         PetscCall(MatGetDiagonal(A00, diag));
    854:       }
    855:       PetscCall(VecReciprocal(diag));
    856:       PetscCall(MatDiagonalScale(AdB, diag, NULL));
    857:       PetscCall(VecDestroy(&diag));
    858:     } else if (ainvtype == MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG) {
    859:       Mat      A00_inv;
    860:       MatType  type;
    861:       MPI_Comm comm;

    863:       PetscCall(PetscObjectGetComm((PetscObject)A00, &comm));
    864:       PetscCall(MatGetType(A00, &type));
    865:       PetscCall(MatCreate(comm, &A00_inv));
    866:       PetscCall(MatSetType(A00_inv, type));
    867:       PetscCall(MatInvertBlockDiagonalMat(A00, A00_inv));
    868:       PetscCall(MatMatMult(A00_inv, A01, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &AdB));
    869:       PetscCall(MatDestroy(&A00_inv));
    870:     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown MatSchurComplementAinvType: %d", ainvtype);
    871:     /* Cannot really reuse Sp in MatMatMult() because of MatAYPX() -->
    872:          MatAXPY() --> MatHeaderReplace() --> MatDestroy_XXX_MatMatMult()  */
    873:     if (preuse == MAT_REUSE_MATRIX) PetscCall(MatDestroy(Sp));
    874:     PetscCall(MatMatMult(A10, AdB, MAT_INITIAL_MATRIX, PETSC_DETERMINE, Sp));
    875:     PetscCall(MatScale(*Sp, -1.0));
    876:     if (A11) { /* TODO: when can we pass SAME_NONZERO_PATTERN? */
    877:       PetscCall(MatAXPY(*Sp, 1.0, A11, DIFFERENT_NONZERO_PATTERN));
    878:     }
    879:     PetscCall(MatDestroy(&AdB));
    880:   }
    881:   PetscFunctionReturn(PETSC_SUCCESS);
    882: }

    884: static PetscErrorCode MatSchurComplementGetPmat_Basic(Mat S, MatReuse preuse, Mat *Sp)
    885: {
    886:   Mat                  A, B, C, D;
    887:   Mat_SchurComplement *schur = (Mat_SchurComplement *)S->data;
    888:   MatNullSpace         sp;

    890:   PetscFunctionBegin;
    891:   if (preuse == MAT_IGNORE_MATRIX) PetscFunctionReturn(PETSC_SUCCESS);
    892:   PetscCall(MatSchurComplementGetSubMatrices(S, &A, NULL, &B, &C, &D));
    893:   PetscCheck(A, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Schur complement component matrices unset");
    894:   if (schur->ainvtype != MAT_SCHUR_COMPLEMENT_AINV_FULL) PetscCall(MatCreateSchurComplementPmat(A, B, C, D, schur->ainvtype, preuse, Sp));
    895:   else {
    896:     if (preuse == MAT_REUSE_MATRIX) PetscCall(MatDestroy(Sp));
    897:     PetscCall(MatSchurComplementComputeExplicitOperator(S, Sp));
    898:   }
    899:   /* If the Schur complement has a nullspace, then Sp nullspace contains it, independently of the ainv type */
    900:   PetscCall(MatGetNullSpace(S, &sp));
    901:   if (sp) PetscCall(MatSetNullSpace(*Sp, sp));
    902:   PetscCall(MatGetTransposeNullSpace(S, &sp));
    903:   if (sp) PetscCall(MatSetTransposeNullSpace(*Sp, sp));
    904:   PetscFunctionReturn(PETSC_SUCCESS);
    905: }

    907: /*@
    908:   MatSchurComplementGetPmat - Obtain a matrix for preconditioning the Schur complement by assembling $Sp = A11 - A10 inv(DIAGFORM(A00)) A01$

    910:   Collective

    912:   Input Parameters:
    913: + S      - matrix obtained with MatCreateSchurComplement() (or equivalent) that implements the action of $A11 - A10 ksp(A00,Ap00) A01$
    914: - 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`

    916:   Output Parameter:
    917: . Sp - approximate Schur complement suitable for preconditioning the exact Schur complement $S = A11 - A10 inv(A00) A01$

    919:   Level: advanced

    921:   Notes:
    922:   The approximation of `Sp` depends on the argument passed to `MatSchurComplementSetAinvType()`
    923:   `MAT_SCHUR_COMPLEMENT_AINV_DIAG`, `MAT_SCHUR_COMPLEMENT_AINV_LUMP`, `MAT_SCHUR_COMPLEMENT_AINV_BLOCK_DIAG`, or `MAT_SCHUR_COMPLEMENT_AINV_FULL`
    924:   -mat_schur_complement_ainv_type <diag,lump,blockdiag,full>

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

    931:   Developer Notes:
    932:   The API that includes `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementGetPmat()` should be refactored to
    933:   remove redundancy and be clearer and simpler.

    935:   This routine should be called `MatSchurComplementCreatePmat()`

    937: .seealso: [](ch_ksp), `MatCreateSubMatrix()`, `PCFIELDSPLIT`, `MatGetSchurComplement()`, `MatCreateSchurComplement()`, `MatSchurComplementSetAinvType()`
    938: @*/
    939: PetscErrorCode MatSchurComplementGetPmat(Mat S, MatReuse preuse, Mat *Sp)
    940: {
    941:   PetscErrorCode (*f)(Mat, MatReuse, Mat *);

    943:   PetscFunctionBegin;
    947:   if (preuse != MAT_IGNORE_MATRIX) {
    948:     PetscAssertPointer(Sp, 3);
    949:     if (preuse == MAT_INITIAL_MATRIX) *Sp = NULL;
    951:   }
    952:   PetscCheck(!S->factortype, PetscObjectComm((PetscObject)S), PETSC_ERR_ARG_WRONGSTATE, "Not for factored matrix");

    954:   PetscCall(PetscObjectQueryFunction((PetscObject)S, "MatSchurComplementGetPmat_C", &f));
    955:   if (f) PetscCall((*f)(S, preuse, Sp));
    956:   else PetscCall(MatSchurComplementGetPmat_Basic(S, preuse, Sp));
    957:   PetscFunctionReturn(PETSC_SUCCESS);
    958: }

    960: static PetscErrorCode MatProductNumeric_SchurComplement_Dense(Mat C)
    961: {
    962:   Mat_Product         *product = C->product;
    963:   Mat_SchurComplement *Na      = (Mat_SchurComplement *)product->A->data;
    964:   Mat                  work1, work2;
    965:   PetscScalar         *v;
    966:   PetscInt             lda;

    968:   PetscFunctionBegin;
    969:   PetscCall(MatMatMult(Na->B, product->B, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &work1));
    970:   PetscCall(MatDuplicate(work1, MAT_DO_NOT_COPY_VALUES, &work2));
    971:   PetscCall(KSPMatSolve(Na->ksp, work1, work2));
    972:   PetscCall(MatDestroy(&work1));
    973:   PetscCall(MatDenseGetArrayWrite(C, &v));
    974:   PetscCall(MatDenseGetLDA(C, &lda));
    975:   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)C), C->rmap->n, C->cmap->n, C->rmap->N, C->cmap->N, v, &work1));
    976:   PetscCall(MatDenseSetLDA(work1, lda));
    977:   PetscCall(MatMatMult(Na->C, work2, MAT_REUSE_MATRIX, PETSC_DETERMINE, &work1));
    978:   PetscCall(MatDenseRestoreArrayWrite(C, &v));
    979:   PetscCall(MatDestroy(&work2));
    980:   PetscCall(MatDestroy(&work1));
    981:   if (Na->D) {
    982:     PetscCall(MatMatMult(Na->D, product->B, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &work1));
    983:     PetscCall(MatAYPX(C, -1.0, work1, SAME_NONZERO_PATTERN));
    984:     PetscCall(MatDestroy(&work1));
    985:   } else PetscCall(MatScale(C, -1.0));
    986:   PetscFunctionReturn(PETSC_SUCCESS);
    987: }

    989: static PetscErrorCode MatProductSymbolic_SchurComplement_Dense(Mat C)
    990: {
    991:   Mat_Product *product = C->product;
    992:   Mat          A = product->A, B = product->B;
    993:   PetscInt     m = A->rmap->n, n = B->cmap->n, M = A->rmap->N, N = B->cmap->N;
    994:   PetscBool    flg;

    996:   PetscFunctionBegin;
    997:   PetscCall(MatSetSizes(C, m, n, M, N));
    998:   PetscCall(PetscObjectBaseTypeCompareAny((PetscObject)C, &flg, MATSEQDENSE, MATMPIDENSE, ""));
    999:   if (!flg) {
    1000:     PetscCall(MatSetType(C, ((PetscObject)B)->type_name));
    1001:     C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Dense;
    1002:   }
    1003:   PetscCall(MatSetUp(C));
    1004:   C->ops->productnumeric = MatProductNumeric_SchurComplement_Dense;
    1005:   PetscFunctionReturn(PETSC_SUCCESS);
    1006: }

    1008: static PetscErrorCode MatProductSetFromOptions_SchurComplement_Dense(Mat C)
    1009: {
    1010:   Mat_Product *product = C->product;

    1012:   PetscFunctionBegin;
    1013:   if (product->type != MATPRODUCT_AB) PetscFunctionReturn(PETSC_SUCCESS);
    1014:   C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Dense;
    1015:   PetscFunctionReturn(PETSC_SUCCESS);
    1016: }

    1018: static PetscErrorCode MatProductNumeric_SchurComplement_Any(Mat C)
    1019: {
    1020:   Mat_SchurComplement *Na = (Mat_SchurComplement *)C->data;

    1022:   PetscFunctionBegin;
    1023:   if (Na->D && Na->D->product) PetscCall(MatProductNumeric(Na->D));
    1024:   if (Na->B->product) PetscCall(MatProductNumeric(Na->B));
    1025:   if (Na->C->product) PetscCall(MatProductNumeric(Na->C));
    1026:   C->assembled = PETSC_TRUE;
    1027:   PetscFunctionReturn(PETSC_SUCCESS);
    1028: }

    1030: static PetscErrorCode MatProductSymbolic_SchurComplement_Any(Mat C)
    1031: {
    1032:   Mat_SchurComplement *Na = (Mat_SchurComplement *)C->data;

    1034:   PetscFunctionBegin;
    1035:   if (Na->D && Na->D->product) PetscCall(MatProductSymbolic(Na->D));
    1036:   if (Na->B->product) PetscCall(MatProductSymbolic(Na->B));
    1037:   if (Na->C->product) PetscCall(MatProductSymbolic(Na->C));
    1038:   C->ops->productnumeric = MatProductNumeric_SchurComplement_Any;
    1039:   C->preallocated        = PETSC_TRUE;
    1040:   C->assembled           = PETSC_FALSE;
    1041:   PetscFunctionReturn(PETSC_SUCCESS);
    1042: }

    1044: static PetscErrorCode MatProductSetFromOptions_SchurComplement_Any(Mat C)
    1045: {
    1046:   Mat_Product         *product = C->product;
    1047:   Mat_SchurComplement *Na, *Ca;
    1048:   Mat                  B = product->B, S = product->A, pB = NULL, pC = NULL, pD = NULL;
    1049:   KSP                  ksp;
    1050:   PetscInt             m = PETSC_DECIDE, n = PETSC_DECIDE, M = PETSC_DECIDE, N = PETSC_DECIDE;
    1051:   MatProductType       pbtype = MATPRODUCT_UNSPECIFIED, pctype = MATPRODUCT_UNSPECIFIED;
    1052:   PetscBool            isschur;

    1054:   PetscFunctionBegin;
    1055:   if (product->type == MATPRODUCT_ABC || product->type == MATPRODUCT_AtB) PetscFunctionReturn(PETSC_SUCCESS);
    1056:   /* A * S not yet supported (should be easy though) */
    1057:   PetscCall(PetscObjectTypeCompare((PetscObject)S, MATSCHURCOMPLEMENT, &isschur));
    1058:   if (!isschur) PetscFunctionReturn(PETSC_SUCCESS);

    1060:   Na = (Mat_SchurComplement *)S->data;
    1061:   if (Na->D) {
    1062:     PetscCall(MatProductCreate(Na->D, B, NULL, &pD));
    1063:     PetscCall(MatProductSetType(pD, product->type));
    1064:     PetscCall(MatProductSetFromOptions(pD));
    1065:   }
    1066:   if (pD && !pD->ops->productsymbolic) {
    1067:     PetscCall(MatDestroy(&pD));
    1068:     PetscFunctionReturn(PETSC_SUCCESS);
    1069:   }

    1071:   /* S = A11 - A10 M A01 */
    1072:   switch (product->type) {
    1073:   case MATPRODUCT_AB: /* A11 B - A10 * M * A01 * B */
    1074:     pbtype = product->type;
    1075:     PetscCall(PetscObjectReference((PetscObject)Na->C));
    1076:     pC = Na->C;
    1077:     m  = S->rmap->n;
    1078:     M  = S->rmap->N;
    1079:     n  = B->cmap->n;
    1080:     N  = B->cmap->N;
    1081:     break;
    1082:   case MATPRODUCT_ABt: /* A11 B^t - A10 * M * A01 * B^t */
    1083:     pbtype = product->type;
    1084:     PetscCall(PetscObjectReference((PetscObject)Na->C));
    1085:     pC = Na->C;
    1086:     m  = S->rmap->n;
    1087:     M  = S->rmap->N;
    1088:     n  = B->rmap->n;
    1089:     N  = B->rmap->N;
    1090:     break;
    1091:   case MATPRODUCT_PtAP: /* Pt A11 P - Pt * A10 * M * A01 * P */
    1092:     pbtype = MATPRODUCT_AB;
    1093:     pctype = MATPRODUCT_AtB;
    1094:     m      = B->cmap->n;
    1095:     M      = B->cmap->N;
    1096:     n      = B->cmap->n;
    1097:     N      = B->cmap->N;
    1098:     break;
    1099:   case MATPRODUCT_RARt: /* R A11 Rt - R * A10 * M * A01 * Rt */
    1100:     pbtype = MATPRODUCT_ABt;
    1101:     pctype = MATPRODUCT_AB;
    1102:     m      = B->rmap->n;
    1103:     M      = B->rmap->N;
    1104:     n      = B->rmap->n;
    1105:     N      = B->rmap->N;
    1106:     break;
    1107:   default:
    1108:     break;
    1109:   }
    1110:   PetscCall(MatProductCreate(Na->B, B, NULL, &pB));
    1111:   PetscCall(MatProductSetType(pB, pbtype));
    1112:   PetscCall(MatProductSetFromOptions(pB));
    1113:   if (!pB->ops->productsymbolic) {
    1114:     PetscCall(MatDestroy(&pB));
    1115:     PetscFunctionReturn(PETSC_SUCCESS);
    1116:   }
    1117:   if (pC == NULL) { /* Some work can in principle be saved here if we recognize symmetry */
    1118:     PetscCall(MatProductCreate(B, Na->C, NULL, &pC));
    1119:     PetscCall(MatProductSetType(pC, pctype));
    1120:     PetscCall(MatProductSetFromOptions(pC));
    1121:     if (!pC->ops->productsymbolic) {
    1122:       PetscCall(MatDestroy(&pC));
    1123:       PetscFunctionReturn(PETSC_SUCCESS);
    1124:     }
    1125:   }
    1126:   PetscCall(MatSetType(C, MATSCHURCOMPLEMENT));
    1127:   PetscCall(MatSetSizes(C, m, n, M, N));
    1128:   PetscCall(PetscLayoutSetUp(C->rmap));
    1129:   PetscCall(PetscLayoutSetUp(C->cmap));
    1130:   PetscCall(PetscObjectReference((PetscObject)Na->A));
    1131:   PetscCall(PetscObjectReference((PetscObject)Na->Ap));
    1132:   Ca                      = (Mat_SchurComplement *)C->data;
    1133:   Ca->A                   = Na->A;
    1134:   Ca->Ap                  = Na->Ap;
    1135:   Ca->B                   = pB;
    1136:   Ca->C                   = pC;
    1137:   Ca->D                   = pD;
    1138:   C->ops->productsymbolic = MatProductSymbolic_SchurComplement_Any;
    1139:   PetscCall(MatSchurComplementGetKSP(S, &ksp));
    1140:   PetscCall(MatSchurComplementSetKSP(C, ksp));
    1141:   PetscFunctionReturn(PETSC_SUCCESS);
    1142: }

    1144: /*MC
    1145:   MATSCHURCOMPLEMENT -  "schurcomplement" - Matrix type that behaves like the Schur complement of a matrix.

    1147:   Level: intermediate

    1149: .seealso: [](ch_matrices), `Mat`, `MatCreate()`, `MatType`, `MatCreateSchurComplement()`, `MatSchurComplementComputeExplicitOperator()`,
    1150:           `MatSchurComplementGetSubMatrices()`, `MatSchurComplementGetKSP()`
    1151: M*/
    1152: PETSC_EXTERN PetscErrorCode MatCreate_SchurComplement(Mat N)
    1153: {
    1154:   Mat_SchurComplement *Na;

    1156:   PetscFunctionBegin;
    1157:   PetscCall(PetscNew(&Na));
    1158:   N->data = (void *)Na;

    1160:   N->ops->destroy        = MatDestroy_SchurComplement;
    1161:   N->ops->getvecs        = MatCreateVecs_SchurComplement;
    1162:   N->ops->view           = MatView_SchurComplement;
    1163:   N->ops->mult           = MatMult_SchurComplement;
    1164:   N->ops->multtranspose  = MatMultTranspose_SchurComplement;
    1165:   N->ops->multadd        = MatMultAdd_SchurComplement;
    1166:   N->ops->setfromoptions = MatSetFromOptions_SchurComplement;
    1167:   N->assembled           = PETSC_FALSE;
    1168:   N->preallocated        = PETSC_FALSE;

    1170:   PetscCall(KSPCreate(PetscObjectComm((PetscObject)N), &Na->ksp));
    1171:   PetscCall(PetscObjectChangeTypeName((PetscObject)N, MATSCHURCOMPLEMENT));
    1172:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_seqdense_C", MatProductSetFromOptions_SchurComplement_Dense));
    1173:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_schurcomplement_mpidense_C", MatProductSetFromOptions_SchurComplement_Dense));
    1174:   PetscCall(PetscObjectComposeFunction((PetscObject)N, "MatProductSetFromOptions_anytype_C", MatProductSetFromOptions_SchurComplement_Any));
    1175:   PetscFunctionReturn(PETSC_SUCCESS);
    1176: }