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