Date: 2026-07-30T21:14:11+00:00

Actual source code: pcksp.c

      1: #include <petsc/private/pcimpl.h>
      2: #include <petsc/private/kspimpl.h>
      3: #include <petscksp.h>

      5: typedef struct {
      6:   KSP      ksp;
      7:   PetscInt its; /* total number of iterations KSP uses */
      8: } PC_KSP;

     10: static PetscErrorCode PCKSPCreateKSP_KSP(PC pc)
     11: {
     12:   const char *prefix;
     13:   PC_KSP     *jac = (PC_KSP *)pc->data;
     14:   DM          dm;

     16:   PetscFunctionBegin;
     17:   PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &jac->ksp));
     18:   PetscCall(KSPSetNestLevel(jac->ksp, pc->kspnestlevel));
     19:   PetscCall(KSPSetErrorIfNotConverged(jac->ksp, pc->erroriffailure));
     20:   PetscCall(PetscObjectIncrementTabLevel((PetscObject)jac->ksp, (PetscObject)pc, 1));
     21:   PetscCall(PCGetOptionsPrefix(pc, &prefix));
     22:   PetscCall(KSPSetOptionsPrefix(jac->ksp, prefix));
     23:   PetscCall(KSPAppendOptionsPrefix(jac->ksp, "ksp_"));
     24:   PetscCall(PCGetDM(pc, &dm));
     25:   if (dm) {
     26:     PetscCall(KSPSetDM(jac->ksp, dm));
     27:     PetscCall(KSPSetDMActive(jac->ksp, KSP_DMACTIVE_ALL, PETSC_FALSE));
     28:   }
     29:   PetscFunctionReturn(PETSC_SUCCESS);
     30: }

     32: static PetscErrorCode PCApply_KSP(PC pc, Vec x, Vec y)
     33: {
     34:   PetscInt its;
     35:   PC_KSP  *jac = (PC_KSP *)pc->data;

     37:   PetscFunctionBegin;
     38:   if (jac->ksp->presolve) {
     39:     PetscCall(VecCopy(x, y));
     40:     PetscCall(KSPSolve(jac->ksp, y, y));
     41:   } else {
     42:     PetscCall(KSPSolve(jac->ksp, x, y));
     43:   }
     44:   PetscCall(KSPCheckSolve(jac->ksp, pc, y));
     45:   PetscCall(KSPGetIterationNumber(jac->ksp, &its));
     46:   jac->its += its;
     47:   PetscFunctionReturn(PETSC_SUCCESS);
     48: }

     50: static PetscErrorCode PCMatApply_KSP(PC pc, Mat X, Mat Y)
     51: {
     52:   PetscInt its;
     53:   PC_KSP  *jac = (PC_KSP *)pc->data;

     55:   PetscFunctionBegin;
     56:   if (jac->ksp->presolve) {
     57:     PetscCall(MatCopy(X, Y, SAME_NONZERO_PATTERN));
     58:     PetscCall(KSPMatSolve(jac->ksp, Y, Y)); /* TODO FIXME: this will fail since KSPMatSolve() does not allow inplace solve yet */
     59:   } else {
     60:     PetscCall(KSPMatSolve(jac->ksp, X, Y));
     61:   }
     62:   PetscCall(KSPCheckSolve(jac->ksp, pc, NULL));
     63:   PetscCall(KSPGetIterationNumber(jac->ksp, &its));
     64:   jac->its += its;
     65:   PetscFunctionReturn(PETSC_SUCCESS);
     66: }

     68: static PetscErrorCode PCApplyTranspose_KSP(PC pc, Vec x, Vec y)
     69: {
     70:   PetscInt its;
     71:   PC_KSP  *jac = (PC_KSP *)pc->data;

     73:   PetscFunctionBegin;
     74:   if (jac->ksp->presolve) {
     75:     PetscCall(VecCopy(x, y));
     76:     PetscCall(KSPSolveTranspose(jac->ksp, y, y));
     77:   } else {
     78:     PetscCall(KSPSolveTranspose(jac->ksp, x, y));
     79:   }
     80:   PetscCall(KSPCheckSolve(jac->ksp, pc, y));
     81:   PetscCall(KSPGetIterationNumber(jac->ksp, &its));
     82:   jac->its += its;
     83:   PetscFunctionReturn(PETSC_SUCCESS);
     84: }

     86: static PetscErrorCode PCMatApplyTranspose_KSP(PC pc, Mat X, Mat Y)
     87: {
     88:   PetscInt its;
     89:   PC_KSP  *jac = (PC_KSP *)pc->data;

     91:   PetscFunctionBegin;
     92:   if (jac->ksp->presolve) {
     93:     PetscCall(MatCopy(X, Y, SAME_NONZERO_PATTERN));
     94:     PetscCall(KSPMatSolveTranspose(jac->ksp, Y, Y)); /* TODO FIXME: this will fail since KSPMatSolveTranspose() does not allow inplace solve yet */
     95:   } else {
     96:     PetscCall(KSPMatSolveTranspose(jac->ksp, X, Y));
     97:   }
     98:   PetscCall(KSPCheckSolve(jac->ksp, pc, NULL));
     99:   PetscCall(KSPGetIterationNumber(jac->ksp, &its));
    100:   jac->its += its;
    101:   PetscFunctionReturn(PETSC_SUCCESS);
    102: }

    104: static PetscErrorCode PCSetUp_KSP(PC pc)
    105: {
    106:   PC_KSP *jac = (PC_KSP *)pc->data;
    107:   Mat     mat;

    109:   PetscFunctionBegin;
    110:   if (!jac->ksp) {
    111:     PetscCall(PCKSPCreateKSP_KSP(pc));
    112:     PetscCall(KSPSetFromOptions(jac->ksp));
    113:   }
    114:   if (pc->useAmat) mat = pc->mat;
    115:   else mat = pc->pmat;
    116:   PetscCall(KSPSetOperators(jac->ksp, mat, pc->pmat));
    117:   PetscCall(KSPSetUp(jac->ksp));
    118:   PetscFunctionReturn(PETSC_SUCCESS);
    119: }

    121: /* Default destroy, if it has never been setup */
    122: static PetscErrorCode PCReset_KSP(PC pc)
    123: {
    124:   PC_KSP *jac = (PC_KSP *)pc->data;

    126:   PetscFunctionBegin;
    127:   PetscCall(KSPDestroy(&jac->ksp));
    128:   PetscFunctionReturn(PETSC_SUCCESS);
    129: }

    131: static PetscErrorCode PCDestroy_KSP(PC pc)
    132: {
    133:   PC_KSP *jac = (PC_KSP *)pc->data;

    135:   PetscFunctionBegin;
    136:   PetscCall(KSPDestroy(&jac->ksp));
    137:   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCKSPGetKSP_C", NULL));
    138:   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCKSPSetKSP_C", NULL));
    139:   PetscCall(PetscFree(pc->data));
    140:   PetscFunctionReturn(PETSC_SUCCESS);
    141: }

    143: static PetscErrorCode PCView_KSP(PC pc, PetscViewer viewer)
    144: {
    145:   PC_KSP   *jac = (PC_KSP *)pc->data;
    146:   PetscBool isascii;

    148:   PetscFunctionBegin;
    149:   if (!jac->ksp) PetscCall(PCKSPCreateKSP_KSP(pc));
    150:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
    151:   if (isascii) {
    152:     if (pc->useAmat) PetscCall(PetscViewerASCIIPrintf(viewer, "  Using Amat (not Pmat) as operator on inner solve\n"));
    153:     PetscCall(PetscViewerASCIIPrintf(viewer, "  KSP and PC on KSP preconditioner follow\n"));
    154:     PetscCall(PetscViewerASCIIPrintf(viewer, "  ---------------------------------\n"));
    155:   }
    156:   PetscCall(PetscViewerASCIIPushTab(viewer));
    157:   PetscCall(KSPView(jac->ksp, viewer));
    158:   PetscCall(PetscViewerASCIIPopTab(viewer));
    159:   if (isascii) PetscCall(PetscViewerASCIIPrintf(viewer, "  ---------------------------------\n"));
    160:   PetscFunctionReturn(PETSC_SUCCESS);
    161: }

    163: static PetscErrorCode PCKSPSetKSP_KSP(PC pc, KSP ksp)
    164: {
    165:   PC_KSP *jac = (PC_KSP *)pc->data;

    167:   PetscFunctionBegin;
    168:   PetscCall(PetscObjectReference((PetscObject)ksp));
    169:   PetscCall(KSPDestroy(&jac->ksp));
    170:   jac->ksp = ksp;
    171:   PetscFunctionReturn(PETSC_SUCCESS);
    172: }

    174: /*@
    175:   PCKSPSetKSP - Sets the `KSP` context for a `PCKSP`.

    177:   Collective

    179:   Input Parameters:
    180: + pc  - the preconditioner context
    181: - ksp - the `KSP` solver

    183:   Level: advanced

    185:   Notes:
    186:   The `PC` and the `KSP` must have the same communicator

    188:   This would rarely be used, the standard usage is to call `PCKSPGetKSP()` and then change options on that `KSP`

    190: .seealso: [](ch_ksp), `PCKSP`, `PCKSPGetKSP()`
    191: @*/
    192: PetscErrorCode PCKSPSetKSP(PC pc, KSP ksp)
    193: {
    194:   PetscFunctionBegin;
    197:   PetscCheckSameComm(pc, 1, ksp, 2);
    198:   PetscTryMethod(pc, "PCKSPSetKSP_C", (PC, KSP), (pc, ksp));
    199:   PetscFunctionReturn(PETSC_SUCCESS);
    200: }

    202: static PetscErrorCode PCKSPGetKSP_KSP(PC pc, KSP *ksp)
    203: {
    204:   PC_KSP *jac = (PC_KSP *)pc->data;

    206:   PetscFunctionBegin;
    207:   if (!jac->ksp) PetscCall(PCKSPCreateKSP_KSP(pc));
    208:   *ksp = jac->ksp;
    209:   PetscFunctionReturn(PETSC_SUCCESS);
    210: }

    212: /*@
    213:   PCKSPGetKSP - Gets the `KSP` context for a `PCKSP`.

    215:   Not Collective but ksp returned is parallel if pc was parallel

    217:   Input Parameter:
    218: . pc - the preconditioner context

    220:   Output Parameter:
    221: . ksp - the `KSP` solver

    223:   Note:
    224:   If the `PC` is not a `PCKSP` object it raises an error

    226:   Level: advanced

    228: .seealso: [](ch_ksp), `PCKSP`, `PCKSPSetKSP()`
    229: @*/
    230: PetscErrorCode PCKSPGetKSP(PC pc, KSP *ksp)
    231: {
    232:   PetscFunctionBegin;
    234:   PetscAssertPointer(ksp, 2);
    235:   PetscUseMethod(pc, "PCKSPGetKSP_C", (PC, KSP *), (pc, ksp));
    236:   PetscFunctionReturn(PETSC_SUCCESS);
    237: }

    239: static PetscErrorCode PCSetFromOptions_KSP(PC pc, PetscOptionItems PetscOptionsObject)
    240: {
    241:   PC_KSP *jac = (PC_KSP *)pc->data;

    243:   PetscFunctionBegin;
    244:   PetscOptionsHeadBegin(PetscOptionsObject, "PC KSP options");
    245:   if (!jac->ksp) PetscCall(PCKSPCreateKSP_KSP(pc));
    246:   PetscCall(KSPSetFromOptions(jac->ksp));
    247:   PetscOptionsHeadEnd();
    248:   PetscFunctionReturn(PETSC_SUCCESS);
    249: }

    251: /*MC
    252:    PCKSP - Defines a preconditioner as any `KSP` solver. This allows, for example, embedding a Krylov method inside a preconditioner.

    254:    Options Database Key:
    255: .  -pc_use_amat - use the matrix that defines the linear system, Amat as the matrix for the
    256:                   inner solver, otherwise by default it uses the matrix used to construct
    257:                   the preconditioner, Pmat (see `PCSetOperators()`)

    259:    Level: intermediate

    261:    Note:
    262:    The application of an inexact Krylov solve is a nonlinear operation. Thus, performing a solve with `KSP` is,
    263:    in general, a nonlinear operation, so `PCKSP` is in general a nonlinear preconditioner.
    264:    Thus, one can see divergence or an incorrect answer unless using a flexible Krylov method (e.g. `KSPFGMRES`, `KSPGCR`, or `KSPFCG`) for the outer Krylov solve.

    266:    Developer Note:
    267:    If the outer Krylov method has a nonzero initial guess it will compute a new residual based on that initial guess
    268:    and pass that as the right-hand side into this `KSP` (and hence this `KSP` will always have a zero initial guess). For all outer Krylov methods
    269:    except Richardson this is necessary since Krylov methods, even the flexible ones, need to "see" the result of the action of the preconditioner on the
    270:    input (current residual) vector, the action of the preconditioner cannot depend also on some other vector (the "initial guess"). For
    271:    `KSPRICHARDSON` it is possible to provide a `PCApplyRichardson_PCKSP()` that short circuits returning to the `KSP` object at each iteration to compute the
    272:    residual, see for example `PCApplyRichardson_SOR()`. We do not implement a `PCApplyRichardson_PCKSP()`  because (1) using a `KSP` directly inside a Richardson
    273:    is not an efficient algorithm anyways and (2) implementing it for its > 1 would essentially require that we implement Richardson (reimplementing the
    274:    Richardson code) inside the `PCApplyRichardson_PCKSP()` leading to duplicate code.

    276: .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PC`,
    277:           `PCSHELL`, `PCCOMPOSITE`, `PCSetUseAmat()`, `PCKSPGetKSP()`, `KSPFGMRES`, `KSPGCR`, `KSPFCG`
    278: M*/

    280: PETSC_EXTERN PetscErrorCode PCCreate_KSP(PC pc)
    281: {
    282:   PC_KSP *jac;

    284:   PetscFunctionBegin;
    285:   PetscCall(PetscNew(&jac));
    286:   pc->data = (void *)jac;

    288:   PetscCall(PetscMemzero(pc->ops, sizeof(struct _PCOps)));
    289:   pc->ops->apply             = PCApply_KSP;
    290:   pc->ops->matapply          = PCMatApply_KSP;
    291:   pc->ops->applytranspose    = PCApplyTranspose_KSP;
    292:   pc->ops->matapplytranspose = PCMatApplyTranspose_KSP;
    293:   pc->ops->setup             = PCSetUp_KSP;
    294:   pc->ops->reset             = PCReset_KSP;
    295:   pc->ops->destroy           = PCDestroy_KSP;
    296:   pc->ops->setfromoptions    = PCSetFromOptions_KSP;
    297:   pc->ops->view              = PCView_KSP;

    299:   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCKSPGetKSP_C", PCKSPGetKSP_KSP));
    300:   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCKSPSetKSP_C", PCKSPSetKSP_KSP));
    301:   PetscFunctionReturn(PETSC_SUCCESS);
    302: }