xref: /petsc/src/ksp/pc/impls/galerkin/galerkin.c (revision 6d5076fa512d0d8e4e12a3685978afdbfee2b1a4)
1 
2 /*
3       Defines a preconditioner defined by R^T S R
4 */
5 #include <petsc/private/pcimpl.h>
6 #include <petscksp.h> /*I "petscksp.h" I*/
7 
8 typedef struct {
9   KSP ksp;
10   Mat R, P;
11   Vec b, x;
12   PetscErrorCode (*computeasub)(PC, Mat, Mat, Mat *, void *);
13   void *computeasub_ctx;
14 } PC_Galerkin;
15 
16 static PetscErrorCode PCApply_Galerkin(PC pc, Vec x, Vec y)
17 {
18   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
19 
20   PetscFunctionBegin;
21   if (jac->R) {
22     PetscCall(MatRestrict(jac->R, x, jac->b));
23   } else {
24     PetscCall(MatRestrict(jac->P, x, jac->b));
25   }
26   PetscCall(KSPSolve(jac->ksp, jac->b, jac->x));
27   PetscCall(KSPCheckSolve(jac->ksp, pc, jac->x));
28   if (jac->P) {
29     PetscCall(MatInterpolate(jac->P, jac->x, y));
30   } else {
31     PetscCall(MatInterpolate(jac->R, jac->x, y));
32   }
33   PetscFunctionReturn(PETSC_SUCCESS);
34 }
35 
36 static PetscErrorCode PCSetUp_Galerkin(PC pc)
37 {
38   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
39   PetscBool    a;
40   Vec         *xx, *yy;
41 
42   PetscFunctionBegin;
43   if (jac->computeasub) {
44     Mat Ap;
45     if (!pc->setupcalled) {
46       PetscCall((*jac->computeasub)(pc, pc->pmat, NULL, &Ap, jac->computeasub_ctx));
47       PetscCall(KSPSetOperators(jac->ksp, Ap, Ap));
48       PetscCall(MatDestroy(&Ap));
49     } else {
50       PetscCall(KSPGetOperators(jac->ksp, NULL, &Ap));
51       PetscCall((*jac->computeasub)(pc, pc->pmat, Ap, NULL, jac->computeasub_ctx));
52     }
53   }
54 
55   if (!jac->x) {
56     PetscCall(KSPGetOperatorsSet(jac->ksp, &a, NULL));
57     PetscCheck(a, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Must set operator of PCGALERKIN KSP with PCGalerkinGetKSP()/KSPSetOperators()");
58     PetscCall(KSPCreateVecs(jac->ksp, 1, &xx, 1, &yy));
59     jac->x = *xx;
60     jac->b = *yy;
61     PetscCall(PetscFree(xx));
62     PetscCall(PetscFree(yy));
63   }
64   PetscCheck(jac->R || jac->P, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Must set restriction or interpolation of PCGALERKIN with PCGalerkinSetRestriction()/Interpolation()");
65   /* should check here that sizes of R/P match size of a */
66 
67   PetscFunctionReturn(PETSC_SUCCESS);
68 }
69 
70 static PetscErrorCode PCReset_Galerkin(PC pc)
71 {
72   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
73 
74   PetscFunctionBegin;
75   PetscCall(MatDestroy(&jac->R));
76   PetscCall(MatDestroy(&jac->P));
77   PetscCall(VecDestroy(&jac->x));
78   PetscCall(VecDestroy(&jac->b));
79   PetscCall(KSPReset(jac->ksp));
80   PetscFunctionReturn(PETSC_SUCCESS);
81 }
82 
83 static PetscErrorCode PCDestroy_Galerkin(PC pc)
84 {
85   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
86 
87   PetscFunctionBegin;
88   PetscCall(PCReset_Galerkin(pc));
89   PetscCall(KSPDestroy(&jac->ksp));
90   PetscCall(PetscFree(pc->data));
91   PetscFunctionReturn(PETSC_SUCCESS);
92 }
93 
94 static PetscErrorCode PCView_Galerkin(PC pc, PetscViewer viewer)
95 {
96   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
97   PetscBool    iascii;
98 
99   PetscFunctionBegin;
100   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
101   if (iascii) {
102     PetscCall(PetscViewerASCIIPrintf(viewer, "  KSP on Galerkin follow\n"));
103     PetscCall(PetscViewerASCIIPrintf(viewer, "  ---------------------------------\n"));
104   }
105   PetscCall(KSPView(jac->ksp, viewer));
106   PetscFunctionReturn(PETSC_SUCCESS);
107 }
108 
109 static PetscErrorCode PCGalerkinGetKSP_Galerkin(PC pc, KSP *ksp)
110 {
111   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
112 
113   PetscFunctionBegin;
114   *ksp = jac->ksp;
115   PetscFunctionReturn(PETSC_SUCCESS);
116 }
117 
118 static PetscErrorCode PCGalerkinSetRestriction_Galerkin(PC pc, Mat R)
119 {
120   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
121 
122   PetscFunctionBegin;
123   PetscCall(PetscObjectReference((PetscObject)R));
124   PetscCall(MatDestroy(&jac->R));
125   jac->R = R;
126   PetscFunctionReturn(PETSC_SUCCESS);
127 }
128 
129 static PetscErrorCode PCGalerkinSetInterpolation_Galerkin(PC pc, Mat P)
130 {
131   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
132 
133   PetscFunctionBegin;
134   PetscCall(PetscObjectReference((PetscObject)P));
135   PetscCall(MatDestroy(&jac->P));
136   jac->P = P;
137   PetscFunctionReturn(PETSC_SUCCESS);
138 }
139 
140 static PetscErrorCode PCGalerkinSetComputeSubmatrix_Galerkin(PC pc, PetscErrorCode (*computeAsub)(PC, Mat, Mat, Mat *, void *), void *ctx)
141 {
142   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
143 
144   PetscFunctionBegin;
145   jac->computeasub     = computeAsub;
146   jac->computeasub_ctx = ctx;
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151    PCGalerkinSetRestriction - Sets the restriction operator for the `PCGALERKIN` preconditioner
152 
153    Logically Collective
154 
155    Input Parameters:
156 +  pc - the preconditioner context
157 -  R - the restriction operator
158 
159    Level: Intermediate
160 
161    Note:
162    Either this or `PCGalerkinSetInterpolation()` or both must be called
163 
164 .seealso: `PC`, `PCCreate()`, `PCSetType()`, `PCType`, `PCGALERKIN`,
165           `PCGalerkinSetInterpolation()`, `PCGalerkinGetKSP()`
166 @*/
167 PetscErrorCode PCGalerkinSetRestriction(PC pc, Mat R)
168 {
169   PetscFunctionBegin;
170   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
171   PetscTryMethod(pc, "PCGalerkinSetRestriction_C", (PC, Mat), (pc, R));
172   PetscFunctionReturn(PETSC_SUCCESS);
173 }
174 
175 /*@
176    PCGalerkinSetInterpolation - Sets the interpolation operator for the `PCGALERKIN` preconditioner
177 
178    Logically Collective
179 
180    Input Parameters:
181 +  pc - the preconditioner context
182 -  R - the interpolation operator
183 
184    Level: Intermediate
185 
186    Note:
187    Either this or `PCGalerkinSetRestriction()` or both must be called
188 
189 .seealso: `PC`, `PCCreate()`, `PCSetType()`, `PCType`, `PCGALERKIN`,
190           `PCGalerkinSetRestriction()`, `PCGalerkinGetKSP()`
191 @*/
192 PetscErrorCode PCGalerkinSetInterpolation(PC pc, Mat P)
193 {
194   PetscFunctionBegin;
195   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
196   PetscTryMethod(pc, "PCGalerkinSetInterpolation_C", (PC, Mat), (pc, P));
197   PetscFunctionReturn(PETSC_SUCCESS);
198 }
199 
200 /*@
201    PCGalerkinSetComputeSubmatrix - Provide a routine that will be called to compute the Galerkin submatrix
202 
203    Logically Collective
204 
205    Input Parameters:
206 +  pc - the preconditioner context
207 .  computeAsub - routine that computes the submatrix from the global matrix
208 -  ctx - context used by the routine, or `NULL`
209 
210    Calling sequence of `computeAsub`:
211 $  PetscErrorCode computeAsub(PC pc, Mat A, Mat Ap, Mat *cAP, void *ctx);
212 +  PC - the `PCGALERKIN`
213 .  A - the matrix in the `PCGALERKIN`
214 .  Ap - the computed submatrix from any previous computation, if `NULL` it has not previously been computed
215 .  cAp - the submatrix computed by this routine
216 -  ctx - optional user-defined function context
217 
218    Level: Intermediate
219 
220    Notes:
221    Instead of providing this routine you can call `PCGalerkinGetKSP()` and then `KSPSetOperators()` to provide the submatrix,
222    but that will not work for multiple `KSPSolve()`s with different matrices unless you call it for each solve.
223 
224    This routine is called each time the outer matrix is changed. In the first call the Ap argument is `NULL` and the routine should create the
225    matrix and computes its values in cAp. On each subsequent call the routine should up the Ap matrix.
226 
227    Developer Note:
228    If the user does not call this routine nor call `PCGalerkinGetKSP()` and `KSPSetOperators()` then `PCGALERKIN`
229    could automatically compute the submatrix via calls to `MatGalerkin()` or `MatRARt()`
230 
231 .seealso: `PC`, `PCCreate()`, `PCSetType()`, `PCType`, `PCGALERKIN`,
232           `PCGalerkinSetRestriction()`, `PCGalerkinSetInterpolation()`, `PCGalerkinGetKSP()`
233 @*/
234 PetscErrorCode PCGalerkinSetComputeSubmatrix(PC pc, PetscErrorCode (*computeAsub)(PC, Mat, Mat, Mat *, void *), void *ctx)
235 {
236   PetscFunctionBegin;
237   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
238   PetscTryMethod(pc, "PCGalerkinSetComputeSubmatrix_C", (PC, PetscErrorCode(*)(PC, Mat, Mat, Mat *, void *), void *), (pc, computeAsub, ctx));
239   PetscFunctionReturn(PETSC_SUCCESS);
240 }
241 
242 /*@
243    PCGalerkinGetKSP - Gets the `KSP` object in the `PCGALERKIN`
244 
245    Not Collective
246 
247    Input Parameter:
248 .  pc - the preconditioner context
249 
250    Output Parameter:
251 .  ksp - the `KSP` object
252 
253    Level: Intermediate
254 
255    Note:
256    Once you have called this routine you can call `KSPSetOperators()` on the resulting ksp to provide the operator for the Galerkin problem,
257    an alternative is to use `PCGalerkinSetComputeSubmatrix()` to provide a routine that computes the submatrix as needed.
258 
259 .seealso: `PC`, `PCCreate()`, `PCSetType()`, `PCType`, `PCGALERKIN`,
260           `PCGalerkinSetRestriction()`, `PCGalerkinSetInterpolation()`, `PCGalerkinSetComputeSubmatrix()`
261 @*/
262 PetscErrorCode PCGalerkinGetKSP(PC pc, KSP *ksp)
263 {
264   PetscFunctionBegin;
265   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
266   PetscValidPointer(ksp, 2);
267   PetscUseMethod(pc, "PCGalerkinGetKSP_C", (PC, KSP *), (pc, ksp));
268   PetscFunctionReturn(PETSC_SUCCESS);
269 }
270 
271 static PetscErrorCode PCSetFromOptions_Galerkin(PC pc, PetscOptionItems *PetscOptionsObject)
272 {
273   PC_Galerkin *jac = (PC_Galerkin *)pc->data;
274   const char  *prefix;
275   PetscBool    flg;
276 
277   PetscFunctionBegin;
278   PetscCall(KSPGetOptionsPrefix(jac->ksp, &prefix));
279   PetscCall(PetscStrendswith(prefix, "galerkin_", &flg));
280   if (!flg) {
281     PetscCall(PCGetOptionsPrefix(pc, &prefix));
282     PetscCall(KSPSetOptionsPrefix(jac->ksp, prefix));
283     PetscCall(KSPAppendOptionsPrefix(jac->ksp, "galerkin_"));
284   }
285 
286   PetscOptionsHeadBegin(PetscOptionsObject, "Galerkin options");
287   if (jac->ksp) PetscCall(KSPSetFromOptions(jac->ksp));
288   PetscOptionsHeadEnd();
289   PetscFunctionReturn(PETSC_SUCCESS);
290 }
291 
292 /*MC
293      PCGALERKIN - Build (part of) a preconditioner by P S R (where P is often R^T)
294 
295     Level: intermediate
296 
297     Note:
298     Use
299 .vb
300      `PCGalerkinSetRestriction`(pc,R) and/or `PCGalerkinSetInterpolation`(pc,P)
301      `PCGalerkinGetKSP`(pc,&ksp);
302      `KSPSetOperators`(ksp,A,....)
303      ...
304 .ve
305 
306     Developer Notes:
307     If `KSPSetOperators()` has not been called on the inner `KSP` then `PCGALERKIN` could use `MatRARt()` or `MatPtAP()` to compute
308     the operators automatically.
309 
310     Should there be a prefix for the inner `KSP`?
311 
312     There is no `KSPSetFromOptions_Galerkin()` that calls `KSPSetFromOptions()` on the inner `KSP`
313 
314 .seealso: `PCCreate()`, `PCSetType()`, `PCType`, `PC`,
315           `PCSHELL`, `PCKSP`, `PCGalerkinSetRestriction()`, `PCGalerkinSetInterpolation()`, `PCGalerkinGetKSP()`
316 M*/
317 
318 PETSC_EXTERN PetscErrorCode PCCreate_Galerkin(PC pc)
319 {
320   PC_Galerkin *jac;
321 
322   PetscFunctionBegin;
323   PetscCall(PetscNew(&jac));
324 
325   pc->ops->apply           = PCApply_Galerkin;
326   pc->ops->setup           = PCSetUp_Galerkin;
327   pc->ops->reset           = PCReset_Galerkin;
328   pc->ops->destroy         = PCDestroy_Galerkin;
329   pc->ops->view            = PCView_Galerkin;
330   pc->ops->setfromoptions  = PCSetFromOptions_Galerkin;
331   pc->ops->applyrichardson = NULL;
332 
333   PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &jac->ksp));
334   PetscCall(KSPSetErrorIfNotConverged(jac->ksp, pc->erroriffailure));
335   PetscCall(PetscObjectIncrementTabLevel((PetscObject)jac->ksp, (PetscObject)pc, 1));
336 
337   pc->data = (void *)jac;
338 
339   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGalerkinSetRestriction_C", PCGalerkinSetRestriction_Galerkin));
340   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGalerkinSetInterpolation_C", PCGalerkinSetInterpolation_Galerkin));
341   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGalerkinGetKSP_C", PCGalerkinGetKSP_Galerkin));
342   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGalerkinSetComputeSubmatrix_C", PCGalerkinSetComputeSubmatrix_Galerkin));
343   PetscFunctionReturn(PETSC_SUCCESS);
344 }
345