xref: /petsc/src/ksp/pc/impls/ksp/pcksp.c (revision 3220ff8572602716d60bdda8b51773ebceb3c8ea)
1 #include <petsc/private/pcimpl.h>
2 #include <petsc/private/kspimpl.h>
3 #include <petscksp.h> /*I "petscksp.h" I*/
4 
5 typedef struct {
6   KSP      ksp;
7   PetscInt its; /* total number of iterations KSP uses */
8 } PC_KSP;
9 
PCKSPCreateKSP_KSP(PC pc)10 static PetscErrorCode PCKSPCreateKSP_KSP(PC pc)
11 {
12   const char *prefix;
13   PC_KSP     *jac = (PC_KSP *)pc->data;
14   DM          dm;
15 
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 }
31 
PCApply_KSP(PC pc,Vec x,Vec y)32 static PetscErrorCode PCApply_KSP(PC pc, Vec x, Vec y)
33 {
34   PetscInt its;
35   PC_KSP  *jac = (PC_KSP *)pc->data;
36 
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 }
49 
PCMatApply_KSP(PC pc,Mat X,Mat Y)50 static PetscErrorCode PCMatApply_KSP(PC pc, Mat X, Mat Y)
51 {
52   PetscInt its;
53   PC_KSP  *jac = (PC_KSP *)pc->data;
54 
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 }
67 
PCApplyTranspose_KSP(PC pc,Vec x,Vec y)68 static PetscErrorCode PCApplyTranspose_KSP(PC pc, Vec x, Vec y)
69 {
70   PetscInt its;
71   PC_KSP  *jac = (PC_KSP *)pc->data;
72 
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 }
85 
PCMatApplyTranspose_KSP(PC pc,Mat X,Mat Y)86 static PetscErrorCode PCMatApplyTranspose_KSP(PC pc, Mat X, Mat Y)
87 {
88   PetscInt its;
89   PC_KSP  *jac = (PC_KSP *)pc->data;
90 
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 }
103 
PCSetUp_KSP(PC pc)104 static PetscErrorCode PCSetUp_KSP(PC pc)
105 {
106   PC_KSP *jac = (PC_KSP *)pc->data;
107   Mat     mat;
108 
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 }
120 
121 /* Default destroy, if it has never been setup */
PCReset_KSP(PC pc)122 static PetscErrorCode PCReset_KSP(PC pc)
123 {
124   PC_KSP *jac = (PC_KSP *)pc->data;
125 
126   PetscFunctionBegin;
127   PetscCall(KSPDestroy(&jac->ksp));
128   PetscFunctionReturn(PETSC_SUCCESS);
129 }
130 
PCDestroy_KSP(PC pc)131 static PetscErrorCode PCDestroy_KSP(PC pc)
132 {
133   PC_KSP *jac = (PC_KSP *)pc->data;
134 
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 }
142 
PCView_KSP(PC pc,PetscViewer viewer)143 static PetscErrorCode PCView_KSP(PC pc, PetscViewer viewer)
144 {
145   PC_KSP   *jac = (PC_KSP *)pc->data;
146   PetscBool isascii;
147 
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 }
162 
PCKSPSetKSP_KSP(PC pc,KSP ksp)163 static PetscErrorCode PCKSPSetKSP_KSP(PC pc, KSP ksp)
164 {
165   PC_KSP *jac = (PC_KSP *)pc->data;
166 
167   PetscFunctionBegin;
168   PetscCall(PetscObjectReference((PetscObject)ksp));
169   PetscCall(KSPDestroy(&jac->ksp));
170   jac->ksp = ksp;
171   PetscFunctionReturn(PETSC_SUCCESS);
172 }
173 
174 /*@
175   PCKSPSetKSP - Sets the `KSP` context for a `PCKSP`.
176 
177   Collective
178 
179   Input Parameters:
180 + pc  - the preconditioner context
181 - ksp - the `KSP` solver
182 
183   Level: advanced
184 
185   Notes:
186   The `PC` and the `KSP` must have the same communicator
187 
188   This would rarely be used, the standard usage is to call `PCKSPGetKSP()` and then change options on that `KSP`
189 
190 .seealso: [](ch_ksp), `PCKSP`, `PCKSPGetKSP()`
191 @*/
PCKSPSetKSP(PC pc,KSP ksp)192 PetscErrorCode PCKSPSetKSP(PC pc, KSP ksp)
193 {
194   PetscFunctionBegin;
195   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
196   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 2);
197   PetscCheckSameComm(pc, 1, ksp, 2);
198   PetscTryMethod(pc, "PCKSPSetKSP_C", (PC, KSP), (pc, ksp));
199   PetscFunctionReturn(PETSC_SUCCESS);
200 }
201 
PCKSPGetKSP_KSP(PC pc,KSP * ksp)202 static PetscErrorCode PCKSPGetKSP_KSP(PC pc, KSP *ksp)
203 {
204   PC_KSP *jac = (PC_KSP *)pc->data;
205 
206   PetscFunctionBegin;
207   if (!jac->ksp) PetscCall(PCKSPCreateKSP_KSP(pc));
208   *ksp = jac->ksp;
209   PetscFunctionReturn(PETSC_SUCCESS);
210 }
211 
212 /*@
213   PCKSPGetKSP - Gets the `KSP` context for a `PCKSP`.
214 
215   Not Collective but ksp returned is parallel if pc was parallel
216 
217   Input Parameter:
218 . pc - the preconditioner context
219 
220   Output Parameter:
221 . ksp - the `KSP` solver
222 
223   Note:
224   If the `PC` is not a `PCKSP` object it raises an error
225 
226   Level: advanced
227 
228 .seealso: [](ch_ksp), `PCKSP`, `PCKSPSetKSP()`
229 @*/
PCKSPGetKSP(PC pc,KSP * ksp)230 PetscErrorCode PCKSPGetKSP(PC pc, KSP *ksp)
231 {
232   PetscFunctionBegin;
233   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
234   PetscAssertPointer(ksp, 2);
235   PetscUseMethod(pc, "PCKSPGetKSP_C", (PC, KSP *), (pc, ksp));
236   PetscFunctionReturn(PETSC_SUCCESS);
237 }
238 
PCSetFromOptions_KSP(PC pc,PetscOptionItems PetscOptionsObject)239 static PetscErrorCode PCSetFromOptions_KSP(PC pc, PetscOptionItems PetscOptionsObject)
240 {
241   PC_KSP *jac = (PC_KSP *)pc->data;
242 
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 }
250 
251 /*MC
252    PCKSP - Defines a preconditioner as any `KSP` solver. This allows, for example, embedding a Krylov method inside a preconditioner.
253 
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()`)
258 
259    Level: intermediate
260 
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.
265 
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.
275 
276 .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PC`,
277           `PCSHELL`, `PCCOMPOSITE`, `PCSetUseAmat()`, `PCKSPGetKSP()`, `KSPFGMRES`, `KSPGCR`, `KSPFCG`
278 M*/
279 
PCCreate_KSP(PC pc)280 PETSC_EXTERN PetscErrorCode PCCreate_KSP(PC pc)
281 {
282   PC_KSP *jac;
283 
284   PetscFunctionBegin;
285   PetscCall(PetscNew(&jac));
286   pc->data = (void *)jac;
287 
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;
298 
299   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCKSPGetKSP_C", PCKSPGetKSP_KSP));
300   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCKSPSetKSP_C", PCKSPSetKSP_KSP));
301   PetscFunctionReturn(PETSC_SUCCESS);
302 }
303